Disable Functions Bypass - dl Function

Tip

Lernen & üben Sie AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Lernen & üben Sie Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Unterstützen Sie HackTricks

dl() lets PHP load a shared extension at runtime. If you can make it load an attacker-controlled module, you can register a new PHP function that internally calls execve, system, or any other native primitive and therefore bypass disable_functions.

This is a real primitive, but on modern targets it is far less common than older writeups suggest.

Warum dieser bypass heute unüblich ist

Die Hauptgründe sind:

  • dl() muss existieren und darf nicht deaktiviert sein
  • enable_dl muss dynamisches Laden weiterhin erlauben
  • Die Ziel-SAPI muss dl() unterstützen
  • Die Payload muss eine gültige PHP-Extension sein, die für dasselbe Ziel-ABI kompiliert wurde
  • Die Extension muss vom konfigurierten extension_dir erreichbar sein

Das offizielle PHP-Handbuch ist hier die wichtigste Realitätprüfung: dl() ist nur für CLI- und embed-SAPIs verfügbar, sowie für die CGI-SAPI, wenn sie von der Kommandozeile ausgeführt wird. Das bedeutet, die Technik ist in der Regel nicht in normalen PHP-FPM/mod_php-Webanfragen verfügbar, also prüfe die SAPI, bevor du Zeit in den Bau einer Payload investierst.

Beachte außerdem, dass enable_dl eine INI_SYSTEM-Einstellung ist und, ab PHP 8.3.0, von PHP als deprecated dokumentiert wird, daher kannst du sie in der Regel nicht zur Laufzeit aus angreiferkontrolliertem PHP-Code umschalten.

If dl() is not viable, go back to the broader list of module/version dependent bypasses.

Schnelle Triage von einem foothold aus

Bevor du irgendetwas baust, sammle die genauen Parameter, die das Modul erfüllen muss:

<?php
phpinfo();
echo "PHP_VERSION=" . PHP_VERSION . PHP_EOL;
echo "PHP_SAPI=" . php_sapi_name() . PHP_EOL;
echo "ZTS=" . (PHP_ZTS ? "yes" : "no") . PHP_EOL;
echo "INT_BITS=" . (PHP_INT_SIZE * 8) . PHP_EOL;
echo "enable_dl=" . ini_get("enable_dl") . PHP_EOL;
echo "extension_dir=" . ini_get("extension_dir") . PHP_EOL;
echo "disabled=" . ini_get("disable_functions") . PHP_EOL;
?>

What you care about:

  • PHP_SAPI: wenn dies fpm-fcgi oder apache2handler ist, ist dl() normalerweise eine Sackgasse für Web-Exploitation
  • extension_dir: das payload muss von hier geladen werden
  • PHP Version, Architektur, debug/non-debug, und ZTS/non-ZTS: dein Modul muss damit übereinstimmen
  • disable_functions: bestätige, ob dl fehlt, weil es deaktiviert ist oder weil die SAPI es nicht unterstützt

Practical exploitation constraints

1. You normally need write access to extension_dir

Das ist der größte Engpass.

dl() nimmt den extension filename, und PHP lädt ihn aus extension_dir. In der Praxis bedeutet das, dass ein normaler beliebiger Upload nach /var/www/html/uploads nicht ausreicht. Du brauchst einen Pfad, um eine .so/.dll abzulegen, von dem PHP tatsächlich Extensions lädt.

Realistische Situationen, in denen das ausnutzbar wird:

  • CTFs oder absichtlich schwache Labs, in denen extension_dir beschreibbar ist
  • Shared-Hosting- oder Container-Fehler, die einen beschreibbaren Extension-Pfad offenlegen
  • Ein separates arbitrary file write primitive, das bereits extension_dir erreicht
  • Post-exploitation-Szenarien, in denen du bereits genug eskaliert hast, um dort Dateien abzulegen

2. The module must match the target build

Nur PHP_VERSION abzugleichen reicht nicht. Die Extension muss außerdem mitfolgendem übereinstimmen:

  • OS und CPU-Architektur
  • libc/toolchain-Erwartungen
  • ZEND_MODULE_API_NO
  • debug vs non-debug build
  • ZTS vs NTS

Wenn diese nicht übereinstimmen, wird dl() fehlschlagen oder den Prozess zum Absturz bringen.

3. open_basedir is not the main defense here

Sobald du das Modul in extension_dir ablegen und dl() aufrufen kannst, wird der Extension-Code im PHP-Prozess ausgeführt. Zu diesem Zeitpunkt war die relevante Barriere nicht open_basedir, sondern die Fähigkeit, ein gültiges Shared Object in den Extension-Ladepfad zu bringen.

Building the malicious extension

Der klassische Weg ist weiterhin gültig:

  1. Rekonstruiere den Ziel-Build so genau wie möglich
  2. Verwende phpize, ./configure und make, um eine shared extension zu bauen
  3. Exportiere eine PHP-Funktion wie bypass_exec($cmd), die native Kommandoausführung kapselt
  4. Lade das kompilierte Modul in extension_dir hoch
  5. Lade es mit dl() und rufe die exportierte Funktion auf

Der Angriff ist alt, aber weiterhin relevant, weil PHP 8.x Changelogs weiterhin dl()-spezifische Crash-Fixes enthalten. Das primitive Exists weiterhin; die Schwierigkeit ist, eine Deployment zu finden, in dem es erreichbar ist und in dem du ein passendes Modul ablegen kannst.

Minimal workflow

On the attacker box

mkdir bypass && cd bypass
phpize
./configure
make

Das resultierende Shared-Object befindet sich normalerweise unter modules/.

Wenn Sie auf einer anderen Umgebung als dem target bauen, behandeln Sie die erzeugte Datei als Entwurf, bis Sie verifiziert haben, dass das ABI mit dem victim übereinstimmt.

Laden und Verwenden der Erweiterung

Wenn das target tatsächlich dl() unterstützt und das Modul sich im extension_dir befindet, ist die Laufzeit einfach:

<?php
if (!extension_loaded('bypass')) {
dl('bypass.so'); // use the correct filename for the target platform
}
echo bypass_exec($_GET['cmd']);
?>

Auf Windows ist der Dateiname typischerweise .dll, während er bei Unix-ähnlichen Zielen normalerweise .so lautet.

Hinweise für Angreifer

  • Nehmen Sie nicht automatisch an, dass dies remote funktioniert, nur weil function_exists("dl") in einigen Dokumentationen oder alten writeups true zurückgibt; validieren Sie die laufende SAPI
  • Ein fehlgeschlagener dl()-Versuch kann den PHP-Worker beenden, wenn das Modul inkompatibel ist
  • Ab PHP 8 werden deaktivierte Funktionen aus der Funktionstabelle entfernt, sodass userland enumeration von älteren posts abweichen kann
  • Wenn Sie nicht in extension_dir schreiben können, ist diese Technik normalerweise weniger praktikabel als FPM/FastCGI, LD_PRELOAD oder in diesem Abschnitt bereits behandelte module-specific bypasses

Referenzen

Tip

Lernen & üben Sie AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Lernen & üben Sie Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Unterstützen Sie HackTricks