DEV Community

loading...

JavaScript: Equality insanity, or where x === 1 && x === 2

antogarand profile image Antony Garand ・5 min read

JavaScript can be used to make even the best of ourselves doubt what is currently happening.

In this post, I will show you different methods which can be used to make the following statement return true:

x === 1 && x === 2

Let me start with a challenge, for those among you who wish to attempt it yourselves first.
If you only want the answer, skip to the writeup section!

Challenge

There are three difficulty levels for this challenge, and many available solutions!

Your objective is to give X the required value for Flag! to be printed.

Place the following snippet somewhere in order to print Flag!

Level 1

// Your code here

if(x == 1 && x == 2 && x == 3) {
    console.log('Flag!');
} else {
    console.log('Wrong flag!');
}

// Your code here

Level 2

Let's make things a bit harder by using the strict equal operator!

// Your code here

if(x === 1 && x === 2 && x === 3) {
    console.log('Flag!');
} else {
    console.log('Wrong flag!');
}

// Your code here

Level 3

Finally, let's print the flag within the current scope!

This means this statement should not be within a class or function, but by itself within the script.

// Your code here

if(x === 1 && x === 2 && x === 3) {
    console.log('Flag!');
} else {
    console.log('Wrong flag!');
}

Writeup

Did you manage to print Flag!?

Part 1

Here is the first part of the previous challenge:

if(x == 1 && x == 2 && x == 3) {

The key to solving this part of the challenge is to know how JavaScript compares two objects.

Using the Equality Operator == instead of the Strict Equality Operator === means that the engine will try to convert both of the objects to primitives before comparing them.

You can find out more about the comparisons on MDN's Comparison Operators page.

This means that if we're comparing an object with a string, myObject.toString()'s result will be used for the comparison instead of failing.

Example:

const myObject = {
    toString() {
        return 'My Object!';
    }
}
console.log(myObject == 'My Object!');

returns true

In our scenario, as we're comparing x to the primitive type Number, the following steps will take place in the background:

If Type(x) is Object and Type(y) is either String or Number,

return the result of the comparison ToPrimitive(x) == y.

This behavior is documented in EcmaScript: The Abstract Equality Comparison Algorithm

Converting an object to a primitive is achieved by calling the object's toString or valueOf methods, as documented here: Object [[DefaultValue]]

In ES6, we can also directly override Symbol.toPrimitive to return our own custom value.
We can therefore create an object with the toString or valueOf function returning an incrementing number!

Solution

let i = 1,
  x = {
    valueOf() { // Default conversion for numbers
      return i++;
    },
    toString() { // Default conversion for strings
      return i++;
    },
    [Symbol.toPrimitive]() { // toPrimitive override
        return i++;
    }
  }

if(x == 1 && x == 2 && x == 3) {
    console.log('Flag!');
} else {
    console.log('Wrong flag!');
}

Note that Symbol.toPrimitive is the first attempted call, then valueOfand finally toString, should the order be important in your future challenges.

Part 2

The first part of this challenge could be solved using an Object and a non-strict comparison, but this will not work here.

As we are using the strict equal operator, x needs to be 1, then 2 and finally 3.

Two tricks are needed to solve this problem:
Getters and an obscure with statement.

The first part of this solution requires creating an object, myObject, which has the x property set to a getter function:


let i = 1,
  myObject = {
    get x() {
        return i++;
    }
 }

We can now access myObject.x and it will return an incrementing value!

This is still not enough to solve the challenge, as the if statement does not have our myObject prefix in the comparison.

Thankfully or not, there is an obscure statement in JavaScript which lets us set our scope to the properties of an object: with

Don't you like it when the MDN page for the operator starts with this big warning?

MDN Warning

The MDN documentation describes with as the following:

The 'with' statement adds the given object to the head of this scope chain during the evaluation of its statement body. If an unqualified name used in the body matches a property in the scope chain, then the name is bound to the property and the object containing the property.

The second part of this solution is therefore wrapping the comparison in a with statement, which will let x be accessed like a native property.

Solution

let i = 1,
  myObject = {
    get x() {
        return i++;
    }
 }

with(myObject) {

  if(x === 1 && x === 2 && x === 3) {
    console.log('Flag!');
  } else {
    console.log('Wrong flag!');
  }

}

Part 3

The previous solution only works out if you can control the context of the if statement, which is rarely the case when you're looking for XSS's.

As such, we can adapt our solution to require a single entry point, before the if statement, to print out Flag!.

Note: If you only have an entry point below the comparison, you might want to check out my previous post: Watch out for unwanted hoisting!

As we are still using a strict equality check, we still need to use a getter to generate X.

The difference with this solution is to add the accessor directly on current scope, the this object.

In a browser, this would refer to the window object, as defined by the DOM model.

In NodeJS, this would refer to the global object.

To modify the property of the current object property, we will use Object.defineProperty

Solution

let a = 1;
Object.defineProperty(
  window, // Object to assign the new property to: this, window, or global depending on the context 
  'x', // Name of the property to assign
  {  // Properties of the object, the getter function in our case
    get: function() { 
      return a++; 
    } 
  }
);

if(x === 1 && x === 2 && x === 3) {
  console.log('Flag!');
} else {
  console.log('Wrong flag!');
}

Conclusion

Thanks to its dynamic nature, JavaScript can make a sane developer understand why x === 1 && x === 2 && x !== x works!

Hopefully no one will depend on such features in actual code, but I would love to see a real world use case of such characteristics.

On another note, this had me thinking about comparisons which may only be false in JavaScript.

As you may know, NaN always returns false in a comparison, including with itself.

The only other value this may be true I know of would be Symbol(), as each symbol is unique.

Now, we can create a dynamic value for x where x !== x is indeed true.

Know other interesting features of JS you think would be worthy of a post?

Leave out a comment or reach out to me on Twitter!

References

MDN: Comparison Operators

EcmaScript: The Abstract Equality Comparison Algorithm

EcmaScript: Object [[DefaultValue]]

MDN: Getters

MDN: With

MDN: Object.defineProperty

Discussion

pic
Editor guide
Collapse
pranay_rauthu profile image
pranay rauthu

I couldn't resist myself sharing this stackoverflow post

Collapse
antogarand profile image
Antony Garand Author

So many great horrible examples! I Love it

Collapse
maxart2501 profile image
Massimo Artizzu

Very nice article.

Have you heard of this challenge? return true to win

Collapse
ycmjason profile image
Jason Yu

I got the second answer!! 😎😎