Heartbleed

Hier mal eine kurze Zusammenfassung zum Thema Heartbleed, auch um mich selbst etwas zu sortieren. Unterm Strich mehr eine kleine Linksammlung.

Das Problem

Durch einen Programmierfehler ist es möglich 64K Speicher aus dem OpenSSL Adressebereich auszulesen. Hier können alle möglichen Daten liegen, die dann von Angreifern abgegriffen und genutzt werden können. Da der Fehler bereits seit zwei Jahren besteht, sollte geprüft werden, ob eine betroffene Version installiert war und v.a. ist.

Wie der Fehler zustande kommt kann hier, hier und hier nachgelesen werden.

 

Die Serverseite

Um Server zu testen gibt es diverse Webseiten:

– http://filippo.io/Heartbleed/ 

– http://possible.lv/tools/hb/

Und Scripte:

– https://github.com/FiloSottile/Heartbleed (in go geschrieben)

– https://github.com/noxxi/p5-scripts/blob/master/check-ssl-heartbleed.pl

Betroffene Daten sind z.B.:

– Cookies

– Zugangsdaten von Benutzern, wie bei der Login Seite von Yahoo

– private Keys, siehe hier

Was natürlich aufgrund der Natur der Schwachstelle nicht heisst, dass ein Serverdienst der betroffen ist auch tatsächlich wichtige Daten preisgibt.

Neben HTTPS sind auch POPS, IMAPS usw. betroffen.

Clients

Da auch Clients begroffen sein können (Browser sind bisher nicht betroffen) hier zu Prüfung noch ein paar Tools:

Pacemaker simuliert einen Server, um Clients auf die Schwachstelle zu überprüfen

Heartbleed Detector prüft Android Geräte

Bisher habe ich nur gesehen, das curl in manchen Linux Distributionen betroffen war.

Allerdings: Serverbetreiber die betroffen waren sollten Ihre Zertifikate getauscht und alte Zertifikate für ungültig erklärt haben. Clients sind hier zum teil nicht korrekt eingerichtet, z.B. wenn die entsprechenden Sperrlisten nicht heruntergeladen werden können, wird das alte Zertifikate im manchen Fällen weiter akzeptiert.

 

Betroffen Produkte und Webseiten

Hier eine ganz gute Liste mit betroffenen Seiten.

Cisco, Juniper und andere Hersteller haben Listen mit betroffenen Produkten zusammengestellt.

 

Fazit

Betroffene Dienste wie wie z.B. Freemail Dienste haben es wohl bisher zumindest teilweise Ihre Kunden nicht informiert oder aber nur allgemein empfohlen, Passwörter zu ändern, da will man wohl keine Kunden vergräzen. Auch liefen betroffene Dienste z.T. auch noch lange nach bekanntwerden der Lücke weiter, Stundenlang war es Angreifern möglich, teilweise sensible Daten von Benutzern abzugreifen. Meiner Meinung nach ein Unding.

Auf der Seite der Hersteller, die die betroffenen OpenSSL Version einsetzen, muss schnell gehandelt werden. Wenn sensible Daten, wie Session Cookies oder Klartext Passwörter ausgelesen werden können sollte der Dienst in meinen Augen durch die Administratoren abgestellt werden, bis ein Patch bereit steht.

Extrem wichtig ist das Thema Systemhärtung. Administratoren, die die Heartbeat Funktion nicht aktiviert haben, waren auch nicht betroffen.

Mir stellen sich noch folgende Fragen, die zum Teil auch allgemeiner Natur sind:

– Sollte in der Ausbildung und im Studium das Thema Sicherheit nicht eine größere Rolle spielen?

– Inwieweit ist es sinnvoll für technische Analysen Netzwerkverkehr mitgeschnitten werden? Ist ja auch Datenschutztechnisch eine interessante Frage. Wie sieht das aus mit Aufzeichnung zur Analyse ob Daten weg sind und im Gegensatz dem Datenschutz der ggf. verletzt wird, wenn man Netzwerkverkehr mitschneidet?

– Wie hätte man hier loggen müssen?

Ich bin mal gespannt, welche Effekte sich in nächster Zeit noch durch die Heartbleed Schwachstelle ergeben.

Update 15.04.2014: Inzwischen gibt es auch was, um den Private Key zu extrahieren, sofern überhaupt möglich: https://github.com/robertdavidgraham/heartleech
Außerdem gibts natürlich von den meisten Pentestiing Tools entsprechende Plugins, z.B. von Nessus, nmap, metasploit usw..

PHP-Object-Injection in Contao 2.11.13

Dies ist ein Gastbeitrag von Ruben Rögels.

Disclaimer

Der Artikel beschreibt einen möglichen Angriffsvektor für eine potentielle PHP-Object-Injection in Contao 2.11.13 und früher.
Er dient in keiner Weise als Aufforderung oder Anleitung.

Die Sicherheitslücke wurde darüber hinaus auch sehr schnell behoben:

Anlass

Am 03. Februar wurde Contao 2.11.14 veröffentlich (https://contao.org/de/news/contao_2-11-14.html).
In diesem Release wurde eine potentielle PHP-Object-Injection behoben, heißt es in der Meldung. Entdeckt wurde die Lücke von Pedro Ribeiro. Hier kann sein PoC eingesehen werden: https://github.com/pedrib/PoC/blob/master/contao-3.2.4.txt

Da ich selbst Contao verwende, habe ich mich mit der Problematik beschäftigt und dabei unabhängig von Pedro ein PoC implementiert.

serialize() und unserialize()

Bevor ich den konkreten Fall in Contao erläutere, möchte ich den Angriffsverktor generell kurz beschreiben.

PHP bietet die Möglichkeit Daten, und damit auch Objekte, in speicherbare Form zu überführen. Hierzu dient die serialize()-Funktion.

Beispiel

Klasse “User”

class User {
   public $name = '';

   public function setName($name) {
      $this->name = $name;
   }

   public function getName() {
      return $this->name;
   }
}

System “A”:

$user = new User();
$user->setName('Pedro');
$serialized = serialize($user);
file_put_contents('saved_object.txt',$serialized);

Um aus der serialisierten Repräsentation des Objekts wieder ein instanziertes Objekt zu erhalten, muss die unserialize()-Funktion genutzt werden:

System “B”:

$serialized = file_get_contents('saved_object.txt);
$user = unserialize($serialized);
echo $user->getName();

Und nun erscheint “Pedro”. Wir haben das Objekt also auf einem System A serialisiert, auf ein System B übertragen und können dort nach der Deserialisierung per getName() die gespeicherten Daten abrufen.

Schlussfolgerung

Die Datei “saved_object.txt” kann also zwischen zwei Applikationen ausgetauscht werden. Die Einzige Voraussetzungen ist, dass in beiden Applikationen die gleiche Klasse “User” existiert, da diese von der unserialize()-Funktion geladen werden muss.

Am Rande: PHP ist es egal, ob es tatsächlich die gleiche Klasse ist, wenn der Klassen-Name auf dem Ziel-System existiert, wird die Klasse geladen und mit den Daten der serialisierten Repräsentation befüllt.

Nun steht die Frage im Raum: Was geschieht, wenn eine Benutzereingabe Argument der unserialize()-Funktion ist.

Mechanik in Contao

Genau diese Konstellation existiert in Contao 2.11.13 (und vermutlich auch davor).
Bevor ich ins Detail gehe, beschreibe ich die Mechanik, die in Contao problematisch ist.

Contao besitzt eine Config-Klasse.
Diese ermöglicht das Lesen und Schreiben der System-Konfiguration, welche in der Datei “localconfig.php” liegt.
Diese Datei wird per __destruct()-Methode jedes mal geschrieben, wenn die Daten in der Config-Klasse verändert wurden. Dies wird mit der Eigenschaft “blnIsModified” überprüft.

Voraussetzung 1: Die Config-Klasse bietet also prinzipiell eine Möglichkeit Dinge in eine ausführbare PHP-Datei zu schreiben.

Eine weitere Wichtige Rolle spielt die Input-Klasse.
Diese säubert Benutzereingabe. Hierzu werden u.a. die Inhalte der Superglobals $_POST und $_GET in die Klasse kopiert und anschließend bereinigt (XSS, Tags, Entitäten, etc.).
Dies geschieht aber nur vollständig beim Aufruf von Input::post() oder Input::get(). Wird aber Input::postRaw() verwendet, finden viele der Bereinigungen nicht statt.

An dieser Stelle sei auch angemerkt, dass die serialisierte Repräsentation eines Objekts Nullbyte-Stings enthalten kann, die einen HTTP-Request ohne Maskierung in meinen Versuchen nicht überlebt haben.

Voraussetzung 2: Die serialisierte Repräsentation des Objekts überlebt die Input Sanitization.

Zudem besitzt Contao eine Widget-Basisklasse, von der alle Widgets, also Eingabefelder, abgeleitet werden. Die Widget-Klasse implementiert Validierungen der Eingaben. Im Zuge der Validierung wird die Eingabe durch die unserialize()-Funktion geschleift.

Voraussetzung 3: unserialize() wird auf eine Benutzereingabe angewendet

Hürden

Die einzigen Hürden waren für mich die Nullbyte-Strings und Zeilenumbrüche.
Diese entstehen beim Serialisieren, wenn das zu serialisierende Objekt “protected” oder “private” Eigenschaften besitzt.

Serialisierte Config-Objekte

Da sich die unserialize()-Funktion aber, wie bereits vorher beschrieben, nicht daran stört, dass in der serialisierten Repräsentation Eigenschaften public sind, die in der Klasse tatsächlich aber protected sind, können wir in unsere Config-Klasse die zu störenden Nullbyte-Strings führenden protected Deklarationen entfernen.

Anschließend wird der Config noch der eigentliche Schadcode hinzugefügt:

$cfg = Config::getInstance();
$cfg->add("throw new Exception('Opps!'); $dummy",null);

$serialized = serialize($cfg);
file_put_contents('evil_config.txt',$serialized);

Nun müssen aus der “evil_config.txt” noch die Zeilenumbrüche entfernt werden. Dabei ist zu beachten, dass die Längenangabe der Strings entsprechend angepasst wird:

Dieser String lässt sich nun in einem Eingabefeld abschicken:

<!DOCTYPE html>
<html>
   <head>
      <title>PoC</title>
   </head>
   <body>
      <form method="post">
         <textarea name="name"></textarea>
         <input name="submit" value="los" type="submit" />
      </form>
   </body>
</html>

<?php
define('TL_MODE','FE');
define('BYPASS_TOKEN_CHECK',true);

require_once('../../initialize.php');

$input = Input::getInstance();
if($input->post('submit')) {
   $w = new TextArea();
   $w->value = $input->postRaw('name');
   $w->validate();
}

In meinen Versuchen konnte ich unter Benutzung von Input::postRaw() die localconfig.php beliebig verändern und mit Input::post() zumindest leeren.