Bypass de disable_functions - fonction dl
Tip
Apprenez et pratiquez le hacking AWS :
HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP :HackTricks Training GCP Red Team Expert (GRTE)
Apprenez et pratiquez le hacking Azure :
HackTricks Training Azure Red Team Expert (AzRTE)
Soutenir HackTricks
- Vérifiez les plans d’abonnement !
- Rejoignez le 💬 groupe Discord ou le groupe telegram ou suivez-nous sur Twitter 🐦 @hacktricks_live.
- Partagez des astuces de hacking en soumettant des PR au HackTricks et HackTricks Cloud dépôts github.
dl() permet à PHP de charger une extension partagée à l’exécution. Si vous pouvez la faire charger un module contrôlé par un attaquant, vous pouvez enregistrer une nouvelle fonction PHP qui appelle en interne execve, system, ou toute autre primitive native et ainsi contourner disable_functions.
Ceci est une primitive réelle, mais sur les cibles modernes elle est bien moins courante que ne le suggèrent d’anciens writeups.
Pourquoi ce bypass est peu courant aujourd’hui
Les principaux obstacles sont :
dl()doit exister et ne doit pas être désactivéeenable_dldoit encore permettre le chargement dynamique- Le SAPI cible doit supporter
dl() - Le payload doit être une extension PHP valide compilée pour la même ABI cible
- L’extension doit être accessible depuis le
extension_dirconfiguré
Le manuel officiel de PHP est le contrôle de réalité le plus important ici : dl() n’est disponible que pour les CLI et embed SAPIs, et pour le SAPI CGI lorsqu’il est exécuté depuis la ligne de commande. Cela signifie que la technique n’est généralement pas disponible dans des requêtes web normales PHP-FPM/mod_php, donc vérifiez le SAPI avant de passer du temps à construire un payload.
Notez aussi que enable_dl est un réglage INI_SYSTEM et, depuis PHP 8.3.0, PHP le documente comme déprécié, donc vous ne pouvez généralement pas le modifier à l’exécution depuis du code PHP contrôlé par un attaquant.
Si dl() n’est pas viable, revenez à la liste plus large des contournements dépendants du module/version.
Triage rapide depuis un foothold
Avant de construire quoi que ce soit, rassemblez les paramètres exacts que le module doit respecter :
<?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: if this isfpm-fcgiorapache2handler,dl()is usually a dead end for web exploitationextension_dir: the payload must be loaded from here- PHP Version, architecture, debug/non-debug, and ZTS/non-ZTS: your module must match them
disable_functions: confirm whetherdlis absent because it is disabled or because the SAPI does not support it
Contraintes pratiques d’exploitation
1. Vous avez normalement besoin d’un accès en écriture à extension_dir
C’est le principal goulot d’étranglement.
dl() prend le nom de fichier de l’extension, et PHP le charge depuis extension_dir. En pratique, cela signifie qu’un simple upload de fichier arbitraire vers /var/www/html/uploads ne suffit pas. Vous avez toujours besoin d’un chemin pour placer un .so/.dll là où PHP charge effectivement les extensions.
Situations réalistes où cela devient exploitable :
- CTFs or intentionally weak labs where
extension_diris writable - Erreurs d’hébergement mutualisé ou de conteneurs qui exposent un chemin d’extension accessible en écriture
- Un vecteur distinct d’écriture de fichier arbitraire qui atteint déjà
extension_dir - Scénarios post-exploitation où vous avez déjà escaladé suffisamment pour déposer des fichiers là-bas
2. Le module doit correspondre à la build cible
Ne correspondre qu’à PHP_VERSION n’est pas suffisant. L’extension doit aussi correspondre à :
- Système d’exploitation et architecture CPU
- attentes de libc/toolchain
ZEND_MODULE_API_NO- build debug vs non-debug
- ZTS vs NTS
Si ceux-ci ne correspondent pas, dl() échouera ou fera planter le processus.
3. open_basedir n’est pas la principale défense ici
Une fois que vous pouvez placer le module dans extension_dir et appeler dl(), le code de l’extension s’exécute dans le processus PHP. À ce stade, la barrière pertinente n’était pas open_basedir, mais la capacité à déposer un shared object valide (.so/.dll) dans le chemin de chargement des extensions.
Construction de l’extension malveillante
La méthode classique reste valide :
- Recréez la build de la cible aussi fidèlement que possible
- Utilisez
phpize,./configure, etmakepour compiler une extension partagée - Exportez une fonction PHP telle que
bypass_exec($cmd)qui encapsule l’exécution de commandes natives - Téléversez le module compilé dans
extension_dir - Chargez-le avec
dl()et appelez la fonction exportée
L’attaque est ancienne, mais toujours pertinente car les changelogs de PHP 8.x continuent d’inclure des correctifs de crash spécifiques à dl(). Le mécanisme existe toujours ; la difficulté est de trouver un déploiement où il est accessible et où vous pouvez déposer un module correspondant.
Workflow minimal
On the attacker box
mkdir bypass && cd bypass
phpize
./configure
make
Le fichier objet partagé résultant se trouvera généralement dans modules/.
Si vous compilez dans un environnement différent de la cible, considérez le fichier produit comme une ébauche jusqu’à ce que vous vérifiiez que l’ABI correspond à celle de la victime.
Chargement et utilisation de l’extension
Si la cible prend réellement en charge dl() et que le module se trouve dans extension_dir, la partie runtime est simple :
<?php
if (!extension_loaded('bypass')) {
dl('bypass.so'); // use the correct filename for the target platform
}
echo bypass_exec($_GET['cmd']);
?>
Sur Windows le nom de fichier sera typiquement une .dll, tandis que sur les targets Unix-like ce sera généralement une .so.
Notes de l’attaquant
- Ne supposez pas que cela fonctionne à distance simplement parce que
function_exists("dl")renvoie true dans une documentation ou un ancien writeup ; validez le SAPI actif - Une tentative
dl()échouée peut tuer le worker PHP si le module est incompatible - À partir de PHP 8, les fonctions désactivées sont retirées de la table des fonctions, donc l’énumération userland peut différer des anciens posts
- Si vous ne pouvez pas écrire dans
extension_dir, cette technique est généralement moins pratique que FPM/FastCGI,LD_PRELOAD, ou les contournements spécifiques aux modules déjà couverts dans cette section
Références
Tip
Apprenez et pratiquez le hacking AWS :
HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP :HackTricks Training GCP Red Team Expert (GRTE)
Apprenez et pratiquez le hacking Azure :
HackTricks Training Azure Red Team Expert (AzRTE)
Soutenir HackTricks
- Vérifiez les plans d’abonnement !
- Rejoignez le 💬 groupe Discord ou le groupe telegram ou suivez-nous sur Twitter 🐦 @hacktricks_live.
- Partagez des astuces de hacking en soumettant des PR au HackTricks et HackTricks Cloud dépôts github.


