Command Injection(命令注入),是指通过提交恶意构造的参数破坏命令语句结构,从而达到执行恶意命令的目的。PHP命令注入攻击漏洞是PHP应用程序中常见的脚本漏洞之一
DVWA-1.9系列一共分为10个功能模块:
Brute Force(暴力破解)
Command Injection(命令行注入)
CSRF(跨站请求伪造)
File Inclusion(文件包含)
File Upload(文件上传)
Insecure CAPTCHA(不安全的验证码)
SQL Injection(SQL注入)
SQL Injection(Blind)(SQL盲注)
XSS(Reflected)(反射型跨站脚本)
XSS(Stored)(存储型跨站脚本)
好久没刷web了,先从Command Injection开刀吧
该模块主要实现的是一个ping 某个ip的功能,但由于对输入的内容验证不完善导致命令执行漏洞
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>";
}
?>
我们可以看到,服务器通过判断操作系统执行不同ping命令,但是对ip参数并未做任何的过滤,导致了严重的命令注入漏洞。
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&&whoami
可以发现返回了执行结果,故可以执行任意命令了哦
刚开始我还试了;号,没有执行和老版本的有点不同。
Medium级源代码如下
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>";
}
?>
通过源代码我们可以看到,代码中的主要过滤原则是采用黑名单的方式:将&&和;替换为空了
所以,low级别的payload就不能用了,但是该级别的缺点是只过滤了上述两种字符,这里我们还可以用&号、管道符| ||等直接绕过。
区别:
command1&command2&command3 三个命令同时执行
command1;command2;command3 不管前面命令执行成功没有,后面的命令继续执行
command1&&command2 只有前面命令执行成功,后面命令才继续执行
command1|command2 管道符的限制是只显示后面那条命令的执行结果
command1||command2是前面的命令执行失败,后面命令才继续执行和&&号的相反
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>";
}
?>
通过源代码可以看出,high级别把黑名单机制更加扩大了。我们可以看到代码中把'| '替换成了空白符,'|'却没有被过滤掉,可以构造payload 127.0.0.1|whoami
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();
?>
通过源代码分析我们可以看到:
该模块中加入了Anti-CSRF token来防范CSRF攻击,同时每次随机生成了一个token,当用户提交的时候,在服务器端比对一下token值是否正确,不正确就丢弃掉,正确就验证通过。
// Check Anti-CSRF token
checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );
通过加入stripslashes函数来对输入的ip信息进行删除字符串中的反斜杠操作。
$target = stripslashes( $target );
对输入的信息通过“.”号进行分割,分割成多个数组。
// Split the IP into 4 octects
$octet = explode( ".", $target );
然后采用白名单机制来验证输入的信息是不是都是数字组成,然后把所有的数字通过“.”进行拼接,这样就保证了输入的信息只能是以数字.数字.数字.数字的形式,避免了命令执行漏洞。
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];
所以几乎不可能单纯的命令执行,写站可以借鉴下~~~~