DEV Community

Cover image for PHP: Return true to win - WriteUp (Part 2)
Antony Garand
Antony Garand

Posted on

PHP: Return true to win - WriteUp (Part 2)

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;
}
Enter fullscreen mode Exit fullscreen mode

Solution

foo(x);
Enter fullscreen mode Exit fullscreen mode

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();
}
Enter fullscreen mode Exit fullscreen mode

Solution

foo(function(&$a){$a=new$a(1);});
Enter fullscreen mode Exit fullscreen mode

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);
});
Enter fullscreen mode Exit fullscreen mode

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 of true in the constructor, which works as the Bar 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;
}
Enter fullscreen mode Exit fullscreen mode

Solution

foo([]);
Enter fullscreen mode Exit fullscreen mode

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];
}
Enter fullscreen mode Exit fullscreen mode

Solution

foo(x);
Enter fullscreen mode Exit fullscreen mode

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];
}
Enter fullscreen mode Exit fullscreen mode

Solution

foo("\00Test\00foo");
Enter fullscreen mode Exit fullscreen mode

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.

As foo is a private property of Test, its array key is Testfoo with nullbytes before Test and foo.

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());
}
Enter fullscreen mode Exit fullscreen mode

Solution

foo(function(){yield;});
Enter fullscreen mode Exit fullscreen mode

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;
}
Enter fullscreen mode Exit fullscreen mode

Solution

foo(NAN);
Enter fullscreen mode Exit fullscreen mode

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];
}
Enter fullscreen mode Exit fullscreen mode

Solution

foo([123456=>1,1]);
Enter fullscreen mode Exit fullscreen mode

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:

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

Discussion (1)

Collapse
vikkio88 profile image
Vincenzo

You are not using psr2, your argument is invalid