Reputation: 29
A friend of mine brought to my attention a piece of JavaScript code that gives you unrealistic time scores on the flashcard website Quizlet's match game. It somehow stops the game's timer at the user specified time.
document.getElementsByClassName("UIButton UIButton--hero")[0].click();
setTimeout(function(){for(var F = setTimeout(";"), i = 0; i < F; i++) clearTimeout(i)}, 5100); //Change 5100
Using it is simple, you get on the match game (for example https://quizlet.com/187478162/match) and you simply enter this code in the console in the inspect menu. You then need to complete the game but it doesn't matter how long you take.
I don't know JavaScript (I am very knowledgeable with Python) but I have figured out so far that the first line clicks the start button and the first setTimeout function waits until the specified time to execute the function inside of it. It is the function inside that confuses me. It seems to just create and clear a bunch of Timeouts. I have no Idea how this stops the game timer though.
If someone could explain how it works that would be very much appreciated
Upvotes: 0
Views: 188
Reputation: 13279
Ah, that's a very interesting piece of code, very clever. Here's what's happening.
The inner function basically has the following (expanded for readability):
var F = setTimeout(";")
for(i = 0; i < F; i++) clearTimeout(i)
setTimeout can accept functions or strings that get evaluated after a timeout. They're just passing in the no-op ';', and they're not even passing in a delay. What they care about is the timer-id "F". If you go to the dev-tools (on a non-busy page), and put setTimeout(';') in the console, you'll see that it'll first return the id 0, then 1, then 2, and so on. The ids counts up.
So, you can imagine this timeline:
Upvotes: 1
Reputation: 1248
Before I get to the answer part, you should know a couple of things about setTimeout( ):
The setTimeout( ) function, can accept a callback function as it's first parameter, or, it can also accept code in a 'string'.
Whenever the setTimeout( ) function is called, it returns a timeout id which can be used in order to pass into the clearTimeout( ) function when you want to clear a particular timeout.
Now, please run the below code snippet and see what happens:
a) In the very first setTimeout call, I'm passing a callback function, storing the returned id in the const variable 'id1'.
b) In the second setTimeout call, I'm passing a code string, storing the returned id in a const variable 'id2'.
const id1 = setTimeout(() => console.log('setTimeout with callback fn'), 1000, '_');
const id2 = setTimeout('console.log("setTimeout with code string")', 2000);
console.log('id from 1st setTimeout( ) is :', id1);
console.log('id from 2nd setTimeout( ) is :', id2);
Both the setTimeout calls work without an issue, but the more interesting thing is when you look at the 'id' values for both function, for the first call the id is '1', for the second call the id is '2'.
This means two things: One these IDs are unique and secondly, these IDs are in the same ID pool, think of it like an ID list or an array where for each setTimeout call, a new ID is created by incrementing over the last existing setTimeout ID from the ID Pool.
Now, let's look at the code in question:
In the first line of code, the getElementsByClassName returns a nodelist of all the elements with the class passed in, and with the click( ), you are just simulating a buttonclick for the 0th element from the nodelist that is returned. You can use other functions like querySelector('UIButton UIButton--hero') or querySelecorAll('UIButton UIButton--hero') in this case and it will not make a difference in the way this works.
document.getElementsByClassName("UIButton UIButton--hero")[0].click();
Now, when we look at the below setTimeout( ) function calls with the knowledge we have about how the setTimeout function and setTimeout IDs work, it will be way easier to understand how it stops the actual timer:
setTimeout(function () {
for (var F = setTimeout("console.log(';')"), i = 0; i < F; i++)
clearTimeout(i);
}, 5100); //Change 5100
We are making an outer setTimeout call which takes in a callback function, and the timer for this setTimeout is set to '5100' milliseconds, which is basically a 5 second timer.
Looking at the callback function itself:
function () {
for (var F = setTimeout("console.log(';')"), i = 0; i < F; i++)
clearTimeout(i);
}
This callback function, runs a for loop, in the initialization block of this for loop, two variables are initialized, 'F' and 'i', value of 'F' is the ID returned by the current setTimeout( ) function, 'i' is set to 0.
Now from what we know from the code snippet I posted above is that the ID pool for all the IDs is the same, which means the ID 'F' is definitely going to be greater the setTimeout ID of the original timer that is present on the webpage.
Answer & Conclusion:
Now the loop itself iterates basing on the value of 'i', as long as it is less than 'F', and it clears the interval using clearInterval(i) for each value of i from 0 to F, which means it stops all timers on the page from ID = 0 to ID = 'F'.
And that is the reason this code can be used in order to exploit the website and stop it's timer so users can cheat and complete the quiz. Please let me know if you have any further queries.
Upvotes: 1