PHP代码执行

1、描述

eval()和assert()函数导致的代码执行漏洞大多是因为载入缓存或者模板以及对变量的处理不严格导致,比如直接把一个外部可控的参数拼接到模板里面,然后调用这两个函数去当成PHP代码执行。
preg_replace()函数的代码执行需要存在/e参数,这个函数原本是用来处理字符串的,因此漏洞出现最多的是在对字符串的处理,比如URL、HTML标签以及文章内容等过滤功能。
call_user_func()和call_user_func_array()函数的功能是调用函数,多用在框架里面动态调用函数,所以一般比较小的程序出现这种方式的代码执行会比较少。array_map()函数的作用是调用函数并且除第一个参数外其他参数为数组,通常会写死第一个参数,即调用的函数。

2、函数介绍

(1)eval()和assert()

eval ( string $code ) : mixed
• 把字符串 code 作为PHP代码执行。
assert ( mixed $assertion [, string $description ] ) : bool (PHP5)
assert ( mixed $assertion [, Throwable $exception ] ) : bool (PHP7)
assert() 会检查指定的 assertion 并在结果为 FALSE 时采取适当的行动。 如果 assertion 是字符串,它将会被assert() 当做 PHP 代码来执行。

  1. //eval函数
  2. <?php
  3. if(isset($_GET['cmd'])){
  4. $cmd = $_GET['cmd'];
  5. eval("$cmd");
  6. echo "success!";
  7. }else{
  8. echo "fail!";
  9. }
  10. ?>
  11. //assert函数
  12. <?php
  13. if(isset($_GET['cmd'])){
  14. $cmd = $_GET['cmd'];
  15. assert("$cmd");
  16. echo "success!";
  17. }else{
  18. echo "fail!";
  19. }
  20. ?>
  21. //回调函数
  22. //危险函数回调
  23. <?php
  24. function callBack(){
  25. $b = $_GET['cmd'];
  26. eval($b);
  27. }
  28. call_user_func("callBack");
  29. //eval.php?cmd=phpinfo()
  30. //定义危险函数调用
  31. <?php
  32. $b = "phpinfo()";
  33. call_user_func($_GET['cmd'],$b);
  34. //eval.php?cmd=assert

(2)动态函数preg_replace()

preg_replace ( mixed $pattern , mixed $replacement , mixed $subject [, int $limit = -1 [, int &$count ]] ) : mixed
• 搜索subject中匹配pattern的部分, 以replacement进行替换。
• /e模式下,preg_replace 函数在匹配到符号正则的字符串时,会将替换字符串(preg_replace 函数的第二个参数)当做代码来执行。
• 5.5.0 \e修饰符被弃用,用preg_replace_callback() 替代

<?php
//1、第一个数变化
//echo $cmd = $_GET['cmd'];
//$str = "<php>phpinfo()</php>";
//preg_replace("/<php>(.*)$cmd","\\1",$str);
////?cmd=<\/php>/e

//2、第二个数变化
//echo $cmd = $_GET['cmd'];
//preg_replace("/php/e",$cmd,"php");
////?cmd=phpinfo()

//3、第三个数变化
//echo $cmd = $_GET['cmd'];
//preg_replace("/<php>(.*)<\/php>/e","\\1",$cmd);
////?cmd=<php>phpinfo()</php>

(3)调用函数过滤不严

call_user_func()和array_map()等数十个函数有调用其他函数的功能,其中的一个参数作为要调用的函数名,那如果这个传入的函数名可控,那就可以调用意外的函数来执行我们想知道的代码,也就是存在代码执行漏洞。
call_user_func ( callable $callback [, mixed $parameter [, mixed $... ]] ) : mixed
第一个参数 callback 是被调用的回调函数,其余参数是回调函数的参数。

<?php
$b="phpinfo()"
call_user_func($_GET['a'],$b)?>
//payload:1.php?a=assert

类似函数:
call_user_func ()、 call_user_func_array ()、 array_map ()
usort ()、 uasort ()、 uksort ()、 array_filter ()
array_reduce ()、 array_diff_uassoc ()、 array_diff_ukey ()
array_udiff ()、 array_udiff_assoc ()、 array_udiff_uassoc ()
array_intersect_assoc ()、 array_intersect_uassoc ()
array_uintersect ()、 array_uintersect_assoc ()
array_uintersect_uassoc ()、 array_walk ()、 array_walk_recursive ()
xml_set_character_data_handler ()、 xml_set_default_handler ()
xml_set_element_handler ()、 xml_set_end_namespace_decl_handler ()
xml_set_external_entity_ref_handler ()、 xml_set_notation_decl_handler ()
xml_set_processing_instruction_handler ()
xml_set_start_namespace_decl_handler ()
xml_set_unparsed_entity_decl_handler ()、 stream_filter_register ()
set_error_handler ()、 register_shutdown_function ()、 register_tick_function ()

3、修复方案

• 尽量不要执行外部的应用程序或命令
• 使用自定义函数或函数库来替代外部应用程序或命令的功能
• 使用escappeshellarg函数来处理命令的参数
• 使用safe_mode_exec_dir来指定可执行的文件路径
• 将执行函数的参数做白名单限制,在代码或配置文件中限制某些参数
参考资料:https://xz.aliyun.com/t/2557