#FdW Facepalm der Woche: WP Authenticity Checker

Ohne Frage, ich mag meinen Job. Sonst würde ich ihn nicht machen – logisch, oder? Dennoch begegnen mir immer mal wieder Sachen, die mir Kommentare wie “So, das reicht jetzt aber wirklich! Ich steig’ um und mach jetzt irgendwas mit Blumen!” entlocken. Dieses Mal fing alles mit der Anfrage eines Kunden an, der eines seiner Lieblings-Plugins nicht mehr im WordPress Plugin Repository finden konnte. Sowas kommt ab und zu vor, und oft sind dann Sicherheitsprobleme die Ursache. Ein weiterer häufig vorkommender Grund wäre das Alter des Plugins gewesen, in diesem Fall lag das letzte Update aber noch nicht lange zurück. Also hieß es mal wieder: “Das ist ja interessant, das schaue ich mir an.”

Mit ein wenig Google-Fu konnte ich ein Statement des Entwicklers zur aktuellen Situation finden, der schrieb:

Michael Simpson (@msimpson)
— Yes, WP wants me to fix a potential security vulnerability, so it is temporarily delisted.

Kurz darauf stieß ich dann auf das Plugin WP Authenticity Checker, dessen Scan anscheinend die zitierte Lücke entdeckt hatte. Die Beschreibung des Plugins klang vollmundig, also schaute ich mal unter die Haube:

WP Authenticity Checker scans all of your wordpress themes and plugins for potentially malicious or unwanted code, find the malicious code in seconds

Allerdings hält das Plugin bei weitem nicht, was die Beschreibung verspricht. Der Autor bezeichnet sich selbst als “Passionate Developer and Security Researcher”, angesichts der vorliegenden Leistung (Plugin-Version 1.0 vom 16.12.2016) muss ich mich dabei aber ausgiebig am Kopf kratzen. Das Plugin erkennt Malware anhand einer internen Liste von Definitionen, die bislang ohne Unterstützung für reguläre Ausdrücke oder andere fortgeschrittene Methoden auskommt. Die Signaturen liegen als Klartext in Stringform vor, deshalb überspringt der Scanner auch vorsorglich das eigene Verzeichnis. Die Liste der Signaturen sieht mit Version 1.0 so aus:

//Malware definations expression and syntax, add more itme in array to update definations
$this->malware_definations = 'base64';

Kein Witz. Im Klartext: Das Plugin ist nicht nur irreführend betitelt, da es keinerlei Echtheitsprüfung (beispielsweise durch Abgleich der Datei-Hashes mit dem Repository bei wordpress.org) durchführt, sondern es stellt sich auch beim Versuch, Malware erkennen zu wollen, äußerst … naiv an. Im Supportforum für das Plugin gibt es einen exemplarischen Test, bei dessen Ergebnissen mir das Lachen im Halse stecken bleibt. Unter den Treffern ist kein einziger echter Malwarefund, es handelt sich durchweg um legitimen Code. Amüsanterweise wird sogar ein Kommentar im ähnlich funktionierenden Plugin AntiVirus gefunden. 🙂

Außer der reinen Volltextsuche nach einer viel zu kurzen Zeichenkette, die zudem in real vorkommender Malware seit Ewigkeiten nicht mehr im Klartext auftaucht, hat das Plugin also zumindest zum jetzigen Zeitpunkt nichts zu bieten, stattdessen sorgt es für Irritationen nicht nur bei den Anwendern, die sich auf die Qualität der Plugins im WordPress Repository verlassen. Der Kollateralschaden ist durchaus vorhanden, wenn aufgrund solcher Fehlalarme sogar harmlose Plugins Dritter erst einmal auf die Ersatzbank geschickt werden.

Vom Einsatz des Plugins kann ich derzeit wirklich nur abraten. Es bringt keinerlei Nutzen sondern kann lediglich ein falsches Gefühl von Sicherheit vermitteln – oder verunsichert den Anwender durch zahllose Meldungen, die praktisch nie tatsächlichen Malwarebefall als Ursache haben. Ein trauriges Bild. 🙁

#GISI heute: Telefonnummer im Theme DAQ

WordPress bietet eine enorme Menge an Möglichkeiten, Eingaben des Anwenders zu validieren. Das ist an sich eine gute Sache, und ich freue mich, wenn ich Themes und Plugins sehe, die davon auch Gebrauch machen, statt einfach nur stumpf die Usereingaben zu übernehmen. Allerdings kann das auch ins Auge gehen, denn WordPress kann nur formale Vorgaben testen, aber nicht bewerten, ob der Test an sich sinnvoll ist.

Ein klassisches Beispiel dafür, wie man es nicht machen sollte, habe ich vor einiger Zeit in einem Theme namens DAQ gefunden. In der functions.php findet sich folgende Funktion:

function daq_check_number( $value ) {
    if ( preg_match( “/^\+[0-9]{2} [0-9]{5} [0-9]{5}$/”|”/^(\([0-9]{3}\)|[0-9]{3}-)[0-9]{3}-[0-9]{4}$/”, $value ) ) {
        return $value;
    }
    else
    {
        return “”;
    }
}

Verwendet wird dieser Code im Customizer (Im Dashboard unter Design, früher hieß der Menüpunkt “Anpassen”). Hier klinkt das Theme einige Eingabefelder für Kontaktinformationen ein, deren Inhalt vom Theme-Entwickler gewissenhaft validiert wird. Die Implementation an sich ist wirklich nach dem Lehrbuch, da gibt es wenig auszusetzen. Der Teufel steckt aber im Detail: Akzeptiert werden durch den Code in obiger Funktion nur Telefonnummern die entweder dem Format +nn nnnnn nnnnn oder (nnn)nnn-nnnn entsprechen.

Ersteres trifft man beispielsweise in Indien an, letzteres nennt sich North American Numbering Plan und wird wie der Name schon andeutet, unter anderem in den USA und Kanada verwendet.

Viele deutsche Telefonnummern passen in keines der beiden Muster, und das war auch der Grund, warum ich mir das Theme anschaute – ein Kunde hatte sich das Theme der Optik wegen ausgesucht und war nun schon der Verzweiflung nahe, weil sich seine Telefonnummer einfach nicht speichern ließ, sondern immer durch die Voreinstellung +99 99999 99999 ersetzt wurde.

Da nun die Ursache klar war, konnte ich dem Kunden auch schnell mittels Child Theme weiterhelfen. Das Theme selbst wollte ich nicht anfassen, um bei zukünftigen Updates nicht jedes Mal die Änderungen wiederholen zu müssen. Also flugs einen Ordner angelegt, eine entsprechende style.css erzeugt und das ganze mit einer functions.php abgerundet, die das Eingabefeld aus DAQ durch ein eigenes ersetzt.

Fazit: Mission erfüllt, Kunde glücklich. 🙂

Aus der Reihe “gute Idee, schlechte Idee” heute: Backups

Es klingelt das rote Telefon – dran ist ein potentieller Neukunde, der sich nicht mehr in sein WordPress einloggen kann. Klingt auf den ersten Blick nach einem einfachen Job – allerdings machen mich die Details dann doch stutzig, denn die “Passwort vergessen”-Funktion bringt auch keine Lösung. Also lasse ich mir die Zugangsdaten geben und schaue mir das mal im Detail an. Der Webspace-Scan zeigt keine Auffälligkeiten, in der Datenbank stolpere ich dann allerdings über die Einträge in der wp_users-Tabelle: Usernamen wie “admin”, “admin1”, “admin2” sind definitiv nicht normal, die zugehörigen Passworthashes in MD5(!) und die Mailadressen erst recht nicht.

Als Sofortmaßnahme ändere ich also erst einmal die Zugangsdaten für die Datenbank, stelle die alte wp_users-Tabelle aus einem sauberen Backup wieder her und ändere dann für alle User vorsorglich nochmals das Passwort.

Danach geht es an den forensischen Teil der Arbeit. Da es sich um ein 08/15-Webhostingpaket handelt, sind die Logfiles nicht allzu ergiebig, außer erheblichem Grundrauschen (die üblichen Aufrufe von xmlrpc.php und wp-login.php aus allen möglichen Winkeln der Welt inklusive diverser ec2-Locations…) findet sich keines der gängigen Muster.

Eine Sache finde ich allerdings doch in den Logfiles: Zwei Tage bevor der Login nicht mehr funktionierte, gab es einen Abruf der wp-config.php.bak – Moment, was? Richtig. Vor geraumer Zeit wurde die wp-config.php bearbeitet und dabei vorsichtshalber ein Backup angelegt. Was in vielen Fällen löblich und sinnvoll ist, wird hier zur Zeitbombe: Für den Webserver ist die Datei wp-config.php.bak eine ganz normale Datei wie jede andere auch – und damit lässt sie sich wie ein Bild oder PDF auch herunterladen.

Damit stehen dem Angreifer dann gleich mehrere Vektoren für einen erfolgreichen Angriff zur Verfügung: Zum einen erhält er so die Salts, die WordPress verwendet, und zum anderen bekommt er die Zugangsdaten zur Datenbank auf dem Silbertablett präsentiert. Das verschafft im konkreten Fall dem Angreifer zwar noch keinen direkten Zugriff auf die Datenbank von außen, aber da als Datenbankhost “localhost” eingetragen ist, reicht eine einzige infizierte Website auf dem Server, um über die Backdoor dort auch Zugriff auf “meine” Datenbank zu bekommen. Da es sich um einen großen Hoster handelt, ist diese Theorie durchaus plausibel – beweisen lässt sie sich von außen nur schlecht. Allerdings passt sie sehr gut zur Faktenlage, da vor allem Auffälligkeiten in den Logfiles der betroffenen Domain fehlen und auch keine Infektion auf dem Webspace selbst zu finden war. Natürlich könnte es immer noch eine unbekannte Lücke in WordPress, dem Theme oder einem Plugin sein, aber auch das würde in den Logfiles deutliche Spuren hinterlassen.

Vorsichtshalber lösche ich also die wp-config.php.bak und ändere auch noch die Salts in der wp-config.php mit dem Salt-Generator von wordpress.org. Als Nachsorgemaßnahme schaue ich in den folgenden Tagen wiederholt in die Datenbank und in die Logfiles, allerdings bleibt es vorerst bei diesem einen Angriff. Zwar ärgert mich die unvollständige Erklärung zur Art und Weise des Angriffs schon etwas, aber mehr lässt sich aus den vorhandenen Informationen kaum machen.

Mein Fazit bleibt: Egal welche Lücke man versehentlich schafft, ausgenutzt wird sie früher oder später auf jeden Fall. Bei “Standardlücken” geht es schneller, bei anderen etwas langsamer. Im vorliegenden Fall lag die Backup-Datei schon seit über einem halben Jahr auf dem Webspace, aber in den Logfiles war nur ein einziger Abruf zu finden – dafür aber unzählige Versuche, bekannte Lücken zu nutzen. In dieser Hinsicht gleichen die Logfiles praktisch der Liste entdeckter Lücken, selbst schon vor Jahren behobene Exploits werden immer noch automatisiert durchgetestet.

Aus welchen Gründen auch immer Updates nicht einzuspielen, kann also keine Lösung auf Dauer sein. Man muss nicht gleich ins andere Extrem verfallen und alles ohne Rücksicht auf Verluste aktualisieren, im Zweifelsfall sollte die Sicherheit der eigenen Website aber immer Vorrang haben. Wenn es beim Sicherheitsupdate zu Problemen kommt, lassen die sich in aller Regel vom Experten leicht beheben.