Unserializing user-supplied data, a bad idea | Drupal Sun

Upgrade Your Drupal Skills

We trained 1,000+ Drupal Developers over the last decade.

See Advanced Courses NAH, I know Enough

Unserializing user-supplied data, a bad idea

Parent Feed: 

Apart from PHP bugs and Denial of Service attacks, there's another reason why calling unserialize on user-supplied data (cookies, hidden form fields) is a bad idea.

When a serialized object is unserialized, its __wakeup() member function will be called if it exists. Lesser known is the fact that another magic method will also be invoked. This is the __destruct() member function that will be invoked when either 1) all references to the object are removed, 2) when the object is explicitly destroyed or 3) when the script ends.

You can see for yourself with the following example code:

class someClass {
  public $foo;

  public function __destruct() {
    echo "__destruct(): Exterminate! Exterminate!\n";
  }
}

demo();
echo "Back in main.\n";

function demo() {
  echo "Demo starts.\n";
  $s = serialize(new SomeClass());
  var_dump($s);

  $u = unserialize($s);
  var_dump($u);
  echo "Demo's over.\n";
}

This will output:

Demo starts.
__destruct(): Exterminate! Exterminate!        <-- no refs to the new result, object destroyed.
string(32) "O:9:"someClass":1:{s:3:"foo";N;}"
object(someClass)#1 (1) {
  ["foo"]=>
  NULL
}
Demo's over.
__destruct(): Exterminate! Exterminate!        <-- $u out of scope, object has no refs, destroyed.
Back in main.

The first destruct happens when the result of new SomeClass() is destroyed, the second one when the $u variable goes out of scope and the last reference to the object is removed.

As all the member variables can be controlled by the person supplying the string, he (m/f/o) can cause a __destruct() member function to operate on unexpected data. Stefan Esser has used this to great effect; See Shocking news in PHP exploitation and the Piwik cookie unserialize vulnerability for how he was able to exploit this to run arbitrary code.

Drupal 7 also contains a __destruct() that, combined with the autoloader, can be used to delete arbitrary files from the system, provided the server running PHP has sufficient privileges to do so.

The __destruct() in question is a member function of Archive_Tar:

function __destruct()
{
  $this->_close();
  //...
}

function _close() {
  //...  
  if ($this->_temp_tarname != '') {
    @drupal_unlink($this->_temp_tarname);
    //...
  }
  //...
}

So, if a module uses unserialize on user-supplied data, one can simply provide the following string (adapting _temp_tarname) to do damage:

O:11:"Archive_Tar":6:{s:8:"_tarname";N;s:9:"_compress";b:0;s:14:"_compress_type";s:4:"none";s:10:"_separator";s:1:" ";s:5:"_file";i:0;s:13:"_temp_tarname";s:0:"";}

Take away message: do not call unserialize on user-supplied data.

Author: 
Original Post: 

About Drupal Sun

Drupal Sun is an Evolving Web project. It allows you to:

  • Do full-text search on all the articles in Drupal Planet (thanks to Apache Solr)
  • Facet based on tags, author, or feed
  • Flip through articles quickly (with j/k or arrow keys) to find what you're interested in
  • View the entire article text inline, or in the context of the site where it was created

See the blog post at Evolving Web

Evolving Web