Disable Functions Bypass - dl Function

Tip

Impara e pratica il hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica il hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Impara e pratica il hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Supporta 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.

Why this bypass is uncommon today

The main blockers are:

  • dl() must exist and must not be disabled
  • enable_dl must still allow dynamic loading
  • The target SAPI must support dl()
  • The payload must be a valid PHP extension compiled for the same target ABI
  • The extension must be reachable from the configured extension_dir

The official PHP manual is the most important reality check here: dl() is only available for CLI and embed SAPIs, and for the CGI SAPI when run from the command line. That means the technique is usually not available in normal PHP-FPM/mod_php web requests, so check the SAPI before spending time building a payload.

Also note that enable_dl is an INI_SYSTEM setting and, as of PHP 8.3.0, PHP documents it as deprecated, so you usually cannot flip it at runtime from attacker-controlled PHP code.

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

Triage rapido da una foothold

Prima di costruire qualsiasi cosa, raccogli i parametri esatti che il modulo deve soddisfare:

<?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: se questo è fpm-fcgi o apache2handler, dl() è solitamente una strada morta per lo sfruttamento web
  • extension_dir: il payload deve essere caricato da qui
  • PHP Version, architettura, debug/non-debug, e ZTS/non-ZTS: il tuo module deve corrispondere a questi
  • disable_functions: conferma se dl è assente perché è disabilitato o perché la SAPI non lo supporta

Practical exploitation constraints

1. You normally need write access to extension_dir

Questo è il collo di bottiglia principale.

dl() prende il nome del file dell’estensione, e PHP lo carica da extension_dir. In pratica, questo significa che un normale upload arbitrario di file in /var/www/html/uploads non è sufficiente. Hai ancora bisogno di un percorso per posizionare un .so/.dll dove PHP effettivamente caricherà le estensioni.

Situazioni realistiche in cui questo diventa sfruttabile:

  • CTFs o lab intenzionalmente deboli dove extension_dir è scrivibile
  • Errori di shared-hosting o container che espongono un percorso di estensioni scrivibile
  • Un diverso primitive di scrittura arbitraria che raggiunge già extension_dir
  • Scenari di post-exploitation dove hai già scalato abbastanza per posizionare file lì

2. The module must match the target build

Corrispondere solo a PHP_VERSION non basta. L’estensione deve anche corrispondere a:

  • OS e architettura CPU
  • aspettative di libc/toolchain
  • ZEND_MODULE_API_NO
  • build debug vs non-debug
  • ZTS vs NTS

Se questi non corrispondono, dl() fallirà o causerà il crash del processo.

3. open_basedir is not the main defense here

Una volta che puoi posizionare il modulo in extension_dir e chiamare dl(), il codice dell’estensione viene eseguito all’interno del processo PHP. A quel punto la barriera rilevante non era open_basedir, ma la capacità di far atterrare un oggetto condiviso valido nel percorso di caricamento delle estensioni.

Building the malicious extension

La strada classica è ancora valida:

  1. Ricrea la build della vittima il più fedelmente possibile
  2. Usa phpize, ./configure, e make per costruire un’estensione condivisa
  3. Esporta una funzione PHP come bypass_exec($cmd) che incapsula l’esecuzione nativa di comandi
  4. Carica il modulo compilato in extension_dir
  5. Caricalo con dl() e chiama la funzione esportata

L’attacco è vecchio, ma ancora rilevante perché i changelog di PHP 8.x continuano a includere fix per crash specifici di dl(). Il primitive esiste ancora; la parte difficile è trovare un deployment dove è raggiungibile e dove puoi far atterrare un modulo corrispondente.

Minimal workflow

On the attacker box

mkdir bypass && cd bypass
phpize
./configure
make

Il file shared object risultante si troverà solitamente in modules/.

Se stai compilando in un ambiente diverso da quello del target, considera il file prodotto come una bozza finché non verifichi che l’ABI corrisponda a quello della vittima.

Caricamento e utilizzo dell’estensione

Se il target supporta realmente dl() e il modulo si trova dentro extension_dir, il lato runtime è semplice:

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

Su Windows il nome del file sarà tipicamente .dll, mentre su target Unix-like sarà solitamente .so.

Attacker notes

  • Non dare per scontato che questo funzioni da remoto solo perché function_exists("dl") restituisce true in qualche documentazione o vecchio writeup; verifica la SAPI in esecuzione
  • Un tentativo fallito di dl() può terminare il PHP worker se il modulo è incompatibile
  • Da PHP 8 in poi, disabled functions vengono rimosse dalla function table, quindi la userland enumeration potrebbe differire rispetto ai post più vecchi
  • Se non puoi scrivere in extension_dir, questa tecnica è solitamente meno pratica rispetto a FPM/FastCGI, LD_PRELOAD, o bypass specifici per moduli già trattati in questa sezione

Riferimenti

Tip

Impara e pratica il hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica il hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Impara e pratica il hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Supporta HackTricks