Webbeveiliging

Bestandsinclusie (LFI / RFI)

Definitie

Bestandsinclusie is een PHP-kwetsbaarheid waarbij een aanvaller het pad bepaalt dat aan include of require wordt doorgegeven. Bij LFI (Local File Inclusion) laat hij een bestand laden dat al op de server staat, bij RFI (Remote File Inclusion) een bestand dat ergens anders wordt gehost. Het leidt tot het uitlezen van geheimen of het uitvoeren van code.

Bestandsinclusie (LFI / RFI), Webbeveiliging

In PHP laden de instructies include en require (en hun varianten _once) een bestand en voeren ze de code daarin uit. Ze worden overal gebruikt, bijvoorbeeld om een template van een thema te laden. Het gevaar ontstaat wanneer het pad van het bestand afhangt van gegevens die een bezoeker zonder voldoende controle meestuurt. De MITRE-catalogus deelt deze zwakte in onder CWE-98, met twee gangbare namen: RFI voor inclusie op afstand, LFI voor lokale inclusie.

Hoe de aanval werkt

Code zoals deze laat de bezoeker kiezen welk bestand er wordt geladen:

// Niet doen
include( __DIR__ . '/templates/' . $_GET['weergave'] . '.php' );
  • LFI (Local File Inclusion): met reeksen als ../ klimt de aanvaller omhoog in de mappenstructuur en laat hij een bestand laden dat al op de server staat. In het beste geval leest hij een bestand dat hij niet hoort te zien; lukt het hem een bestand te laten includen waarvan hij de inhoud bepaalt (een logbestand, een geüpload bestand), dan krijgt hij remote code execution.
  • RFI (Remote File Inclusion): de aanvaller geeft een URL naar zijn eigen server op en PHP voegt zijn code direct in. Dat is de meest directe vorm, maar er is een optie voor nodig die zelden aanstaat.
Vergelijking in drie kolommen: LFI (inclusie van een bestand op de server, wp-config.php uitgelezen of uitgevoerd via ../-paden, wrappers php://filter en phar://, werkt met de standaardconfiguratie), RFI (inclusie van een http- of ftp-URL met de code van de aanvaller, vereist dat allow_url_include aanstaat, een optie die standaard uitstaat en sinds PHP 7.4 verouderd is) en verweer (vaste lijst toegestane bestanden, nooit een pad uit het verzoek, open_basedir en allow_url_include uit, updates)
RFI hangt af van een optie die standaard uitstaat; LFI werkt gewoon met een standaardconfiguratie.

De rol van de PHP-configuratie

De PHP-documentatie is duidelijk over twee instellingen:

  • allow_url_include staat URL's toe in include en require. De standaardwaarde is 0, je kunt hem alleen in de systeemconfiguratie (php.ini) wijzigen en hij is verouderd sinds PHP 7.4.0. Hij vereist ook dat allow_url_fopen aanstaat.
  • allow_url_fopen maakt het mogelijk URL's te openen alsof het bestanden zijn; deze optie staat standaard aan, omdat veel scripts haar nodig hebben.

RFI uitschakelen lost niet alles op: met de wrappers van PHP, zoals php://filter, kun je op een standaardconfiguratie sommige LFI's omzetten in het uitvoeren van code, en phar:// werd lange tijd gebruikt om een onveilige deserialisatie te starten. Ons artikel over de wrappers php://filter en phar beschrijft die technieken.

Video: “Web Hacker Basics 04 (Local and Remote File Inclusion)” (Netsec Explained, in het Engels)

Hoe je je ertegen beschermt

Het beste verweer, dat ook MITRE aanraadt, is nooit een pad op te bouwen uit ontvangen gegevens, maar vaste waarden te koppelen aan bekende bestanden:

$weergaven = array( 'lijst' => 'lijst.php', 'raster' => 'raster.php' );
$weergave  = isset( $weergaven[ $_GET['weergave'] ?? '' ] ) ? $weergaven[ $_GET['weergave'] ] : 'lijst.php';
include __DIR__ . '/templates/' . $weergave;
  • Houd bestanden die worden ingevoegd waar mogelijk buiten de webroot.
  • Beperk met open_basedir wat PHP mag lezen: een misbruikte inclusie kan dan niet buiten de toegestane mappen komen. PHP harden bij je hostingprovider legt deze instelling en de gevolgen uit.
  • Een web application firewall blokkeert typische patronen (../, php://), maar alleen als aanvulling.

Voor WordPress

De WordPress-core bouwt zijn inclusies niet op uit parameters van bezoekers: het risico komt van plugins en thema's die een template, een taal of een module laden op basis van een URL- of AJAX-parameter. Het favoriete doelwit van een LFI is wp-config.php, met de toegangsgegevens van de database en de beveiligingssleutels; een vergeten gevoelig bestand in de root, zoals een .env of een back-up, is een ander makkelijk doelwit, zoals ons artikel over blootgestelde gevoelige bestanden laat zien. Controleer ten slotte of je hostingprovider allow_url_include uitgeschakeld laat.

Bronnen

  1. MITRE CWE-98: PHP Remote File Inclusion (externe link, opent in een nieuw tabblad)
  2. PHP Manual: Filesystem and Streams Configuration Options (externe link, opent in een nieuw tabblad)
  3. PHP Manual: Backward Incompatible Changes in PHP 8.0 (Phar) (externe link, opent in een nieuw tabblad)

Alle definities

WordPress-beveiliging, zonder ruis

Eén e-mail wanneer het ertoe doet: nieuw waargenomen aanvallen, updates van de plugin en praktische gidsen. Geen spam, afmelden met één klik.

Begin gratis, stap over op Pro wanneer je eraan toe bent

De gratis versie verbergt WordPress al en verplaatst de inlogpagina. Pro voegt aliassen, tweestapsverificatie en bewaking toe.