应用程序有时需要调用一些执行系统命令的函数、如在PHP中,以下函数:
system:system()—执行shell命令也就是向dos发送一条指令。
exec:exec—方便的PHP函数发送一个字符串供操作系统的命令行处理。
shell_exec:shell_exec— 通过 shell 环境执行命令,并且将完整的输出以字符串的方式返回。
passthru:passthru ()只调用命令,不返回任何结果,但把命令的运行结果原样地直接输出到标准输出设备上。输入/输出—
ppopen:popen ()函数打开一个进程管道来执行给定的命令,返回一个文件句柄。
proc_popen:类似popen() 函数, 但是 proc_open() 提供了更加强大的控制程序执行的能力。
以上函数可以执行系统命令。当黑客能控制这些函数中的参数时,就可以将恶意的系统命令拼接到正常命令中,从而造成命令执行攻击,这就是命令执行漏洞。
换句话说就是**代码调用系统命令的时候,过滤没做好,**从而导致攻击者可以使用命令来干非法的事情。
利用系统函数实现远程命令执行
在PHP,允许命令执行的函数有:
eval()
assert()
preg_replace()
call_user_func()
…
如果页面中存在这些函数并且对于用户的输入没有做严格的过滤,那么就可能造成远程命令执行漏洞。
eval()函数
1.定义和用法
eval() 函数把字符串按照 PHP 代码来计算。
该字符串必须是合法的 PHP 代码,且必须以分号结尾。
如果没有在代码字符串中调用 return 语句,则返回 NULL。如果代码中存在解析错误,则 eval() 函数返
回 false。
2.语法
eval(phpcode)
phpcode 必需。规定要计算的 PHP 代码。
3.例子
$a = $_GET['a'];
eval($a);
?>
4.访问测试地址
http://127.0.0.1/1.php?a=phpinfo();
assert()函数
1.定义和用法
检查一个断言是否为 FALSE
2.语法
PHP 5
bool assert ( mixed assertion [, stringdescription ] )
PHP 7
bool assert ( mixed assertion [, Throwableexception ] )
assert() 会检查指定的 assertion 并在结果为 FALSE 时采取适当的行动
3.例子
$a = $_GET['a'];
assert($a);
?>
4.访问测试
http://127.0.0.1/oscommand/1.php?a=phpinfo();
ps:eval()和assert()区别
eval()函数正确执行需要满足php的代码规范,而assert()函数则不存在这个问题,对于php的代码规范
要求不高
preg_replace()函数
1.定义和语法
preg_replace 函数执行一个正则表达式的搜索和替换。
2.语法
mixed preg_replace ( mixed pattern , mixedreplacement , mixed subject [, intlimit = -1 [, int
&$count ]] )
搜索 subject 中匹配 pattern 的部分, 以 replacement 进行替换。
3.参数说明:
pattern: 要搜索的模式,可以是字符串或一个字符串数组。当pattern处存在一个"/e"修饰符时,
$replacement的值会被当成php代码来执行。
$replacement: 用于替换的字符串或字符串数组。
$subject: 要搜索替换的目标字符串或字符串数组。
$limit: 可选,对于每个模式用于每个 subject 字符串的最大可替换次数。 默认是-1(无限制)。
$count: 可选,为替换执行的次数。
4.例子
$a = $_GET['a'];
echo preg_replace("/test/e", $a, "just test!")
?>
http://127.0.0.1/oscommand/1.php?a=phpinfo()
ps: 在php5.4及以下版本中,preg_replace()可正常执行代码,而在php5.5及后续版本中会提醒"/e"修
饰符已被弃用,要求用preg_replace_callback()函数来代替。
call_user_func()函数
1.定义和用法
call_user_func — 把第一个参数作为回调函数调用
2.语法
mixed call_user_func ( callable callback [, mixedparameter [, mixed $… ]] )
第一个参数 callback 是被调用的回调函数,其余参数是回调函数的参数。
3.例子
call_user_func($GET['a'],$GET['b']);
?>
http://127.0.0.1/oscommand/1.php?a=assert&b=phpinfo()
其他函数
ob_start()、unserialize()、creat_function()
usort()、uasort()、uksort()
array_filter()
array_reduce()
array_map()
系统命令执行的函数
system()
exec()
shell_exec()
passthru()
pcntl_exec()
popen()
proc_open()
反引号
…
环境分析
if (isset($_POST['submit'])){
$target = $_REQUEST['ip'];
if(isset(php_uname('s'), 'Windows NT')) {
$cmd = shell_exec('ping ' . $taeget);
echo '
'.$cmd.'
';
} else {
$cmd = shell_exec('ping -c 3 ' . $target);
echo '
'.$cmd.'
'
}
}
页面通过request获取传入的ip参数,并获取当前系统类型之后拼接相应命令"ping + target IP"并执行,在此过程中IP参数可控,所以在IP可拼接命令。
127.0.0.1&&whoami
127.0.0.1;whoami
127.0.0.1||whoami
案例讲解
http://127.0.0.1/web/MLZX/rce.php?ip=127.0.0.1
页面rce.php提供了ping的功能,当给参数IP输入127.0.0.1时,程序会执行ping127.0.0.1,然后将ping
的结果返回到页面上。
而如果将参数IP设置为127.0.0.1|dir,然后再次访问,从返回结果可以看到,程序直接将目录结构返回
到页面上了,这里就利用了管道符 | 让系统执行了命令dir
如:dir D:
Linux系列支持的管道如下所示:
“;”:执行完前面的语句再执行后面的。如:ping 127.0.0.1;whoami
“|”:显示后面语句的执行结果。如:ping 127.0.0.1 | whoami
“||”:当前面的语句执行出错时,执行后面的语句。例如:ping 1 || whoami
“&”:如果前面的语句为假则直接执行后面的语句,前面的语句可真可假。如: Ping 127.0.0.1 & whoami
“&&”:如果前面的语句为假则直接出错,也不执行后面的,前面的语句只能为真。如:ping 127.0.0.1 && whoami
Windows 系列支持的管道如下所示:
“|”:直接执行后面的语句,如:ping 127.0.0.1 | whoami
“||:如果前面执行的语句执行出错,则执行后面的语句,前面的语句只能为假。例如:ping 2 || whoami
“&”:如果前面的语句为真,则直接执行前面语句。否则都不执行
“&&”:如果前面的语句为假则直接出错,也不执行后面的语句,前面的语句只能为真。例如:ping
127.0.0.1 && whoami
命令执行漏洞代码分析
服务端处理ping的代码。程序获取GET参数IP,然后拼接到system()函数中,利用system()函数执行
ping的功能,但是此处没有对参数IP做过滤和检测,导致可以利用管道执行其他的系统命令,代码如
下:
防范措施
在PHP下禁用高危系统函数
找到php.ini,查找到disable_functions,添加禁用的函数名
严格过滤关键字符
$substitutions = array(
'&&' => '',
';' => '',
'||' => '',
);
$target = str_replace(array_keys($substitutions), $substitution, $target);
严格限制允许的参数类型
利用正则表达