Command Injection简介
概念
- 命令执行漏洞是指可以随意执行系统命令,属于高危漏洞之一,也属于代码执行范围内;PHP命令注入攻击漏洞是PHP应用程序中常见的脚本漏洞之一;就好比说一句话木马<?php @eval($_POST['cmd']);?>
类型
-
代码过滤不严或无过滤;
-
系统漏洞造成的命令执行,bash破壳漏洞,该漏洞可以构造环境变量的值来执行具有攻击力的脚本代码,会影响到bash交互的多种应用,例如:http,ssh,dhcp;
-
调用第三方组件,例如:php(system(),shell_exec(),exec(),eval()),Java(struts2),thinkphp(老牌的PHP框架);
&& , & 和 |
-
&&:command1 && command2
第一个命令执行成功后才会执行第二个命令,否则不执行;
-
& :command1 & command2
不管前面的命令是否执行成功,后面的命令都会执行;
-
| : command1 | command2
只执行后面的命令,不执行前面的命令,而且是前面的命令执行成功后才会去执行后面的命令
安全级别:Low
查看源代码
<?php
if( isset( $_POST[ 'Submit' ] ) ) {
// Get input
$target = $_REQUEST[ 'ip' ];
// Determine OS and execute the ping command.
if( stristr( php_uname( 's' ), 'Windows NT' ) ) {
// Windows
$cmd = shell_exec( 'ping ' . $target );
}
else {
// *nix
$cmd = shell_exec( 'ping -c 4 ' . $target );
}
// Feedback for the end user
echo "<pre>{$cmd}</pre>";
}
?>
- stristr(string,search,before_search)
string 必需。规定被搜索的字符串。 search 必需。规定要搜索的字符串。
如果该参数是数字,则搜索匹配该数字对应的 ASCII 值的字符。
before_search 可选。默认值为 “false” 的布尔值。
如果设置为 “true”,它将返回 search 参数第一次出现之前的字符串部分。
- php_uname(mode)
这个函数会返回运行php的操作系统的相关描述,参数mode可取值”a” (此为默认,包含序列”s n r v m”里的所有模式),”s
”(返回操作系统名称),”n”(返回主机名),” r”(返回版本名称),”v”(返回版本信息), ”m”(返回机器类型)。
对ip参数没有做任何的过滤,从而导致了严重的命令注入漏洞
测试
127.0.0.1
127.0.0.1&&ipconfig
127.0.0.1&ipconfig
127.0.0.1| calc
127.0.0.1| netstat -ano
安全级别:Medium
<?php
if( isset( $_POST[ 'Submit' ] ) ) {
// Get input
$target = $_REQUEST[ 'ip' ];
// Set blacklist
$substitutions = array(
'&&' => '',
';' => '',
);
// Remove any of the charactars in the array (blacklist).
$target = str_replace( array_keys( $substitutions ), $substitutions, $target );
// Determine OS and execute the ping command.
if( stristr( php_uname( 's' ), 'Windows NT' ) ) {
// Windows
$cmd = shell_exec( 'ping ' . $target );
}
else {
// *nix
$cmd = shell_exec( 'ping -c 4 ' . $target );
}
// Feedback for the end user
echo "<pre>{$cmd}</pre>";
}
?>
分析
服务器端对ip参数做了一定过滤,把“&&”、“;”替换为空;
Medium级别的过滤方式本质上采用的是黑名单机制,相对白名单依旧存在安全问题;
尝试使用&&
,报错
但是将&&
改成&
,就没有问题了
而且也没有过滤|
而且&&&
,&|&
都可以绕过这个限制
总结
当将
&&
,;
替换成空格后,
可以用&&&
,&;&
,|
,&
绕过
安全级别:High
源代码
<?php
if( isset( $_POST[ 'Submit' ] ) ) {
// Get input
$target = trim($_REQUEST[ 'ip' ]);
// Set blacklist
$substitutions = array(
'&' => '',
';' => '',
'| ' => '',
'-' => '',
'$' => '',
'(' => '',
')' => '',
'`' => '',
'||' => '',
);
// Remove any of the charactars in the array (blacklist).
$target = str_replace( array_keys( $substitutions ), $substitutions, $target );
// Determine OS and execute the ping command.
if( stristr( php_uname( 's' ), 'Windows NT' ) ) {
// Windows
$cmd = shell_exec( 'ping ' . $target );
}
else {
// *nix
$cmd = shell_exec( 'ping -c 4 ' . $target );
}
// Feedback for the end user
echo "<pre>{$cmd}</pre>";
}
?>
代码分析
在源码中可以看到
‘&’ => ‘’,
‘;’ => ‘’,
'| ’ => ‘’,
‘-’ => ‘’,
‘$’ => ‘’,
‘(’ => ‘’,
‘)’ => ‘’,
‘`’ => ‘’,
‘||’ => ‘’,
都被过滤了,不过|
值得注意一下,它后面是有个空格的,所以说|
是可以绕过的。
安全级别:Impossible
源代码
<?php
if( isset( $_POST[ 'Submit' ] ) ) {
// Check Anti-CSRF token
checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );
// Get input
$target = $_REQUEST[ 'ip' ];
$target = stripslashes( $target );
// Split the IP into 4 octects
$octet = explode( ".", $target );
// Check IF each octet is an integer
if( ( is_numeric( $octet[0] ) ) && ( is_numeric( $octet[1] ) ) && ( is_numeric( $octet[2] ) ) && ( is_numeric( $octet[3] ) ) && ( sizeof( $octet ) == 4 ) ) {
// If all 4 octets are int's put the IP back together.
$target = $octet[0] . '.' . $octet[1] . '.' . $octet[2] . '.' . $octet[3];
// Determine OS and execute the ping command.
if( stristr( php_uname( 's' ), 'Windows NT' ) ) {
// Windows
$cmd = shell_exec( 'ping ' . $target );
}
else {
// *nix
$cmd = shell_exec( 'ping -c 4 ' . $target );
}
// Feedback for the end user
echo "<pre>{$cmd}</pre>";
}
else {
// Ops. Let the user name theres a mistake
echo '<pre>ERROR: You have entered an invalid IP.</pre>';
}
}
// Generate Anti-CSRF token
generateSessionToken();
?>
源码分析
在Impossible级别中,代码加入了Anti-CSRF token,同时对参数ip进行了严格的限制,只有诸如“数字.数字.数字.数字”的输入才会被接收执行,杜绝了命令注入漏洞;