命令/代码执行漏洞基础概念
在编写PHP代码的过程中经常会有一些额外的需求,比如调动一个在系统中已经存在的命令,这时就需要调用一些函数来执行这些命令。
在开发中经常灵活的插入一些代码,使代码生效:
- 系统主题模板的调用;
- 灵活地逻辑判断。
当这些代码的参数可控时,恶意用户就可以插入自己想要执行的代码、命令,这些代码被执行之后就造成了漏洞。
常见命令/代码执行函数详解
PHP中造成命令执行漏洞的常用函数
string system(string $command[, int &$return_var])
函数执行 command 参数所指定的命令,并且输出执行结果
string exec(string $command[, array &$output[, int &$return_var]])
exec() 执行 command 参数所指定的命令。
string shell_exec(string $cmd)
通过 shell 环境执行命令,并且将完整的输出以字符串的方式返回。
void passthru(string $command[, int &$return_var])
执行外部程序并且显示原始输出。
``
例如`ls`
,反引号的内容就会被当做系统命令执行,内部就是执行了shell_exec()
进行处理。
void pcntl_exec(string $path[, array $args[, array $envs]])
pcntl是php的多进程处理进展,在处理大量任务的情况下会用到,pcntl需要额外安装。$path为可执行程序路径(/bin/bash)。$args表示传递给$path程序的参数。例如pcntl_exec("/bin/bash", array("whoami"));
resource popen(string $command, string $mode)
打开一个指向进程的管道,该进程由派生给定的 command 命令执行而产生。 例如 popen('whoami >> 123.txt', 'r');
resource proc_open(string $cmd, array $descriptorspec, array &$pipes[, string $cwd[, array $env[, array $other_options]]])
执行一个命令,并且打开用来输入/输出的文件指针。类似 popen()
函数,但是 proc_open()
提供了更加强大的控制程序执行的能力。
eval与assert
mixed eval(string $code)
把字符串 code 作为 PHP 代码执行。
- 代码不能包含打开/关闭 PHP tags。比如不能传入:
'<?php echo "Hi!"; ?>'
但仍然可以使用合适的PHP tag 来离开、重新进入 PHP模式。比如'echo "In PHP mode!"; ?>In HTML mode!<?php echo "Back in PHP mode!";'
- 传入的代码必须是有效的PHP代码。所有的语句必须以分号结尾。比如
'echo "Hi!"'
会导致parse error,加上分号后正常运行。
bool assert(mixed $assertion[, string $description])
PHP 语言中用来判断哪个表达式是否成立,返回 true or false。
如果 assertion 是字符串,它将会被 assert()
当做PHP代码来执行。
调用函数过滤不当
call_user_func()
、call_user_func_array ()
、 array_map ()
等几十个函数都可以调用其他函数的功能。其中一个参数为调用的函数名,如果这个传入的函数名可控,那就可以调用意外的函数来执行我们想执行的代码。
mixed call_user_func(callable $callback[, mixed $parameter[, mixed $...]])
第一个参数 callback 是被调用的回调函数,其余参数是回调函数的参数。
该类函数的功能是调用函数,多用在框架中动态调用函数。所以一些小的程序出现这种方式的代码执行会很少。
array array_map(callable $callback, array $array1[, array $...])
返回数组,是为 array1 每个元素应用 callback 函数之后的数组。 callback 函数形参的数量和传给 array_map()
数组数量,两者必须一样。
动态函数执行
PHP的特性原因,PHP的函数名称可以由字符串进行拼接。
//例1
$a = 'a'.'s'.'s'.'e'.'r'.'t';
$a('phpinfo()');
//例2
$_GET['a']($_POST[_]);