各位小伙伴,今天我们继续学习Command Injection,翻译为中文就是命令行注入。是指通过提交恶意构造的参数破坏命令语句结构,从而达到执行恶意命令的目的。在OWASP TOP 10中一种存在注入漏洞,最常见的就是SQL和命令行注入。PHP开发的系统中存在命令注入漏洞,也是PHP应用程序中常见的脚本漏洞之一,国内著名的Web应用程序Discuz!、DedeCMS等都曾经存在过该类型漏洞。
命令执行漏洞的产生原因一般就是将用户输入未经过滤或者过滤不严就直接当作系统命令进行执行,我们可以通过批处理中的一些技巧来一次执行多条命令,这样就可以执行任意命令。在命令执行中,常用的命令连接符号有五个:&&、&、||、|和;
&&:前一个指令执行成功,后面的指令才继续执行,就像进行与操作一样
||:前一个命令执行失败,后面的才继续执行,类似于或操作
&:直接连接多个命令
|:管道符,将前一个命令的输出作为下一个命令的输入
;:直接连接多个命令
1、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 {
// unix/Linux
$cmd = shell_exec( 'ping -c 4 ' . $target );
}
// Feedback for the end user
$html .= "<pre>{$cmd}</pre>";
}
?>
分析:
服务器端首先获取用户提交的ip,接着获取服务端的系统信息,根据不同操作系统选择不同的ping命令格式,但是后续并没有对用户的输入做任何的过滤,就会导致严重的漏洞
stristr()函数定义和用法:
stristr() 函数搜索字符串在另一字符串中的第一次出现。
注释:该函数是二进制安全的。
注释:该函数是不区分大小写的。如需进行区分大小写的搜索,请使用 strstr() 函数。
语法:stristr(string,search,before_search)
php_uname(mode)函数定义和用法:
这个函数会返回运行php的操作系统的相关描述,参数mode可取值:
”a”(此为默认,包含序列”s n r v m”里的所有模式),
”s”(返回操作系统名称),
”n”(返回主机名),
“r”(返回版本名称),
”v”(返回版本信息),
”m”(返回机器类型)。
漏洞利用
无论是linux系统还是windows系统,我们都可以用&&来执行多条命令语句。所以我们执行的payload可以是127.0.0.1&&ipconfig可以发现返回了执行结果,ip详细信息,见下面截图:
当然,这里的127.0.0.1可以换成任何IP地址,而&&后面可以接各种命令,例如127.0.0.1&&del c:windowsinfo.log, 则删除windows目录下info.log文件,del文件不会显示任何信息。
2、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
$html .= "<pre>{$cmd}</pre>";
}
?>
分析:
通过源码我们可以看出,相对比于low级别,medium级别设置了黑名单,将&&和;做了过滤处理,&&不行,我们可以改用&
这里说明下几个符号的区别:
command1&command2&command3 三个命令同时执行
command1;command2;command3 不管前面命令执行成功没有,后面的命令继续执行
command1&&command2 只有前面命令执行成功,后面命令才继续执行
漏洞利用
我们就改用&,所以payload就变成了127.0.0.1&ipconfig
可以看到payload执行成功
3、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
$html .= "<pre>{$cmd}</pre>";
}
?>
分析:
还以为会是多么高端的过滤方式,只不过是把黑名单稍微细化了一下,但是依然可以使用|
Command 1 | Command 2
“|”是管道符,表示将Command 1的输出作为Command 2的输入,并且只打印Command 2执行的结果。
漏洞利用
127.0.0.1|ipconfig
依旧可以执行。
4、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
$html .= "<pre>{$cmd}</pre>";
}
else {
// Ops. Let the user name theres a mistake
$html .= '<pre>ERROR: You have entered an invalid IP.</pre>';
}
}
// Generate Anti-CSRF token
generateSessionToken();
?>
分析:
通过源代码分析我们可以看到:
1、该模块中加入了Anti-CSRF token来防范CSRF攻击,同时每次随机生成了一个token,当用户提交的时候,在服务器端比对一下token值是否正确,不正确就丢弃掉,正确就验证通过。
// Check Anti-CSRF token
checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );
2、通过加入stripslashes函数来对输入的ip信息进行删除字符串中的反斜杠操作。
$target = stripslashes( $target );
3、对输入的信息通过“.”号进行分割,分割成多个数组。
// Split the IP into 4 octects
$octet = explode( ".", $target );
4、然后采用白名单机制来验证输入的信息是不是都是数字组成,然后把所有的数字通过“.”进行拼接,这样就保证了输入的信息只能是以“数字.数字.数字.数字”的形式限定了只能输入IP地址,避免了命令执行漏洞。
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];
(完)
---------------------------------------------------------------------------
关注安全 关注作者