Introduction
Welcome to the second part of the Returntrue.win writeup!
Hopefully you already did read and understand the first half of those challenge. While not required, it is recommended to read it Here first!
Now is the time to stop reading and try to solve the challenges yourself before reading on the solutions!
Website: https://returntrue.win
Level 9
Challenge
function foo($x)
{
$y = $x + 1;
return ++$x != $y;
}
Solution
foo(x);
Explanation
The solution here is lies in how PHP implemented the increment operator.
While you might expect ++$x
to convert $x
to a number before converting it, it actually will return the next character in the alphabet.
In our case, ++$x
is equal to y
as it is the next letter following x
,
$y
will be a number, aka. 1
, as it is the result of an addition.
The final comparison therefore is 'y' != 1
, which is true!
Level 10
Challenge
class Bar
{
private $a;
public function __construct($a)
{
$this->a = (bool) $a;
}
public function a()
{
return $this->a;
}
}
function foo(callable $x)
{
$object = new Bar(false);
$x($object);
return $object->a();
}
Solution
foo(function(&$a){$a=new$a(1);});
Explanation
This one is tricky, and fun!
Here is how the long, unminified, version of the answer might look like:
foo(function(&$barInstance) {
$barInstance = new Bar(true);
});
As we are given $object
as argument in our function, using it as a reference lets us modify it.
We can't easily modify the a
property of this object as it is private, but we can reassign the variable to be a new instance of Bar!
The minified version uses few tricks to shorten the payload:
- Use
1
instead oftrue
in the constructor, which works as theBar
constructor casts the element to a boolean - Create a new instance of a class from an instance, as documented here
- Remove all unnecessary spaces and indentation
The most interesting part of the shortest payload here is the creation of a new Bar
instance using new $a(1)
, which I didn't know about before reading the solutions!
Level 11
Challenge
function foo(array $x)
{
return $x[0] === null;
}
Solution
foo([]);
Explanation
Arrays undefined offset will return null if accessed, therefore an empty array will pass this check.
Level 12
Challenge
function foo($x)
{
return $x[123456] !== null
&& $x[123456] === $x[123457];
}
Solution
foo(x);
Explanation
This may seem weird at first, but the answer is to pass any string.
Accessing an invalid char index on a string will return an empty string ''
, which passes all conditions.
It is equivalent to the following array, with additional notices printed out:
[123456 => '', '']
Reference:
StackOverflow: Finding the next numeric index of an array
Level 13
Challenge
class Test
{
private $foo = true;
}
function foo($x)
{
$o = (array) new Test;
return $o[$x];
}
Solution
foo("\00Test\00foo");
Explanation
The solution resides in knowing how array casting works.
The keys are the member variable names, with a few notable exceptions:
[...] private variables have the class name prepended to the variable name;
[...] These prepended values have null bytes on either side.
Asfoo
is a private property ofTest
, its array key isTestfoo
with nullbytes beforeTest
andfoo
.
To obtain the shortest score of 11, you need to replace both escaped nullbytes \00
with a real nullbyte in the POSTed request.
I'll leave this as a challenge to the reader!
Level 14
Challenge
function foo($x)
{
return is_object($x) && is_object($x());
}
Solution
foo(function(){yield;});
Explanation
The solution is simple once explained, but really tricky to find out.
The first required knowledge is that anonymous functions are represented by the Closure class in PHP.
This lets us pass the first is_object($x)
using less characters than creating a callable object.
The second required knowledge is PHP's generators.
The yield
keyword returns a Generator, which can be used to continue the execution later on.
This leads us to this very short 18 characters solution!
Level 15
Challenge
function foo($value)
{
return $value != $value;
}
Solution
foo(NAN);
Explanation
The NAN constant is not equal to itself due the IEEE754 spec.
More details on this stackoverflow question.
Level 16
Challenge
function foo($x)
{
// This level is slightly different than level 12
// the comparison is != instead of !== ;)
return $x[123456] != null
&& $x[123456] === $x[123457];
}
Solution
foo([123456=>1,1]);
Explanation
Unlike challenge 12, the loose equality comparison does not let us use a string to pass the first condition.
The reason why this solution works is that PHP increments the last inserted numeric key to create its next values.
In this case, as the first inserted key is 123456
, the second one will be 123457
, both having the value of 1.
Conclusion
Those were lots of original solutions to the challenge!
Most of those solutions did produce warnings and notices, therefore most of the odd behavior should be detected in a proper development environment.
If you like learning about weird behaviors and challenges, make sure to follow me on Twitter where I post about this kind of stuff
Like on my previous blog post, I would like to thank:
- @matthieunapoli for making this awesome website
- @MarcS0h for solving the easiest challenges while I was tackling the hard ones
- @rpkamp82, for his Writeup on returntrue.win
- @dopitz for his gist containing many short solutions
If you do know similar oddities or challenge websites, please do send them my way so I can solve and write about them!
References
https://returntrue.win
https://gist.github.com/odan/0799dfa59d40acdb18e8a1fa9611a996
https://www.rpkamp.com/2018/02/15/all-answers-to-returntrue.win-with-explanations/
https://alf.nu/ReturnTrue - Similar website for JavaScript
Top comments (1)
You are not using psr2, your argument is invalid