Posts PBCTF Unfinished Chat Writeup
Post
Cancel

PBCTF Unfinished Chat Writeup

Preview Image

Part Zero: Unpacking the source code and finding the initial sink (TheGrandPew)


After downloading the challenge and running the installer on my windows machine, I headed over to the installation path C:\Users\%username%\AppData\Local\Programs\pbchat and found that this electron app was using asar to bundle the electron code. So I moved the app.asar over to my working directory and used 7zip to extract the contents of the asar file.

1
2
3
4
5
6
7
8
9
10
11
12
app.unpacked/
  node_modules/*
  assets/*
  build/
    js/
      app.js -> The client side source code
      app.js.map
  index.html -> Main window html
  package.json
  main.js -> The main electron process code
  preload-chat.js -> Chat window preload
  preload.js -> Main window preload

Does setup

After setting up the firebase and all needed to run the electron app, I decided I would open the electron app for the first time.

pbchat_pic

I then opened up a chat window and had a play around with the messages, tried some simple html injection payloads such as pew but they did not work because proper escaping was in place. From there I decided to open chrome devtools and have a good look at the app.js file which turned out to be webpacked but a map file was also provided so chrome devtools auto provided a webpack directory in the sources with all the app.js source. First look at the code told me this was a ReactJS + Firebase application so I started to use the devtools->sources find feature to search through the codebase fast. My Initial searches where dangerouslySetInnerHTML, innerHTML, outerHTML, replace, … , eval, Function(), setTimeout, setInterval but nothing of interest came out of that so I went on to look over the React Message component and surely enough I found that the chat supports youtube embedes when isPalantirEnabled is set on the returned firebase message object. My first Idea to attack this code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import React from "react";
import Arg from "@vunamhung/arg.js";

const URL_REGEX = /((?:[\w]+):(?:\/\/)?(?:(?:[^/\s]+\.?)+)(?:[^#?\s]+)?(?:\?[^#\s]+)?(?:#[^\s]+)?)/gi;

export default function Linkify({ text }) {
  const addTracking = (url) => {
    const u = new URL(url);
    u.search = Arg.stringify({
      ...Arg.parse(u.search),
      utm_source: "pbchat",
      utm_content: "link",
    });
    return u.toString();
  };

  const parse = (t) => {
    return t.split(URL_REGEX).map((part, i) => {
      if (part.match(URL_REGEX)) {
        const url = addTracking(part);
        let checkurl = new URL(url);

        if (checkurl.protocol === "https:" && checkurl.hostname.endsWith("www.youtube.com")){
            checkurl = "https://www.youtube.com/embed/" + checkurl.searchParams.get("v");
            return (
              <iframe sandbox="allow-scripts allow-same-origin allow-presentation" key={i} src={checkurl} className="preview mt-3">
              </iframe>

            );
        }
        else {
          return (
            <a key={i} href={url}>
              {part}
            </a>
          );
        }
      } else {
        return <span key={i}>{part}</span>;
      }
    });
  };

  return <span>{parse(text || "")}</span>;
}
  • was to abuse the lack of validation on the v url parameter to preform a directory traversal on the iframe src to iframe https://www.youtube.com/redirect?u=https://attacker.com/. However the redirect on youtube.com turned into a dead-end because it requires user interaction to procced with the redirect. My Second Idea was to look into the use of Arg.js to add “tracking parameters”, I searched up “@vunamhung/arg.js” on https://snyk.io/vuln and found that the version they where using was vulnerable to prototype pollution so I guess we found our first sink (the iframe) + vulnerability (prototype pollution).

Part One: The search for a prototype pollution gadget (s1r1us)


Ok so now that I have prototype pollution, I need to find a gadget so that I can turn this into a XSS. I firstly had a look for DOM XSS sinks again using the find feature but this time paying close atention to the librarys with the sinks. After looking through the results of those searches I concluded that I must find a prototype pollution in ReactJS, first I tried searching google for a known ReactJS gadget but couldn’t find one :(. The second thing I did was to setup an enviroment where I could easyly change Object.prototype and insert a simple iframe into the document using React, I ended up using jsbin for this.

With that done I did a console.log(<iframe id=pew>pew</iframe>) to dump how React structures these html Objects before ReactDom.Render is called on them. I did because I thought the easyiest way to get xss would be to inject a property into these Objects.

1
2
3
4
5
6
7
8
9
10
11
12
13
{
  "$$typeof": Symbol(react.element),
  "key":null,
  "props": {
    "id":"pew"
  },
  "ref":null,
  "type":"iframe",
  "_owner":null,
  "_store":{
    "validated":false
  }
}
This post is licensed under CC BY 4.0 by the author.