DVWA命令注入(Command Injection)漏洞代码审计
代码审计(Code audit)是一种以发现程序错误,安全漏洞和违反程序规范为目标的源代码分析。软件代码审计是对编程项目中源代码的全面分析,旨在发现错误,安全漏洞或违反编程约定。 它是防御性编程范例的一个组成部分,它试图在软件发布之前减少错误。
DVWA是一个基于PHP/MySQL开发的存在糟糕漏洞的web应用,旨在为专业的安全人员提供一个合法的环境,来测试他们的工具和技能。帮助web开发人员理解web应用保护的过程。
下面将以DVWA靶场中的命令注入漏洞练习为例,熟悉人工代码审计的简单流程与方法,从中发现存在的漏洞并加以简单利用。代码中不熟悉的函数,可以通过PHP手册进行查询了解。
工具:PHP手册(下载链接:https://pan.baidu.com/s/1xSH__T1iy7zDD-opgkrw_Q?pwd=e2ht 提取码:e2ht)
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>";
}
?>
1.判断是否传入参数,如果是,将传入的参数赋值给$target
变量。
if( isset( $_POST[ 'Submit' ] ) ) {
$target = $_REQUEST[ 'ip' ];
……
isset() — 检测变量是否已声明并且其值不为
null
。
2.判断操作系统类型,若为Windows操作系统,直接执行ping
命令,其他操作系统则加入ping
4次的限制(Windows默认ping
4次)。
……
if( stristr( php_uname( 's' ), 'Windows NT' ) ) {
$cmd = shell_exec( 'ping ' . $target );
}
else {
$cmd = shell_exec( 'ping -c 4 ' . $target );
}
……
stristr() — 查找字符串的首次出现(忽略大小写)。将
php_uname
返回的操作系统字段与Windows NT
进行匹配,若匹配成功,则为Windows操作系统。php_uname() — 返回运行 PHP 的系统的有关信息。
shell_exec() — 通过 shell 执行命令并将完整的输出以字符串的方式返回。
3.将cmd执行ping
命令的结果在前端输出。后续出现相同的代码将不再做出解释。
……
echo "<pre>{$cmd}</pre>";
}
存在漏洞:
shell_exec()
函数是 PHP 中一个强大的功能,允许运行系统命令。然而,如果使用不当,这个函数可能会导致安全问题。在这个例子中,我们看到 shell_exec()
被用来执行ping
命令,这是一个常见的安全功能,用于检测主机是否在线。但是,问题在于,该脚本并没有对输入进行任何形式的验证和过滤,这可能会导致恶意用户提交的数据被直接用于执行系统命令,从而可能导致安全问题。
漏洞利用:
在输入框输入ip地址 && 系统命令
并执行,便可轻松获取想获取的系统信息,例如:
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>";
}
?>
分析增加的代码,Medium男的从用户输入中移除部分特殊字符,例如 '&&'
和 ';'
。
……
$substitutions = array(
'&&' => '',
';' => '',
);
$target = str_replace( array_keys( $substitutions ), $substitutions, $target );
……
str_replace() — 子字符串替换。使用该函数将用户输入中的特殊字符替换为空字符串。
array_keys() — 返回数组中部分的或所有的键名。
存在漏洞:
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>";
}
?>
1.当 $_POST['Submit']
被调用时,变量 $target
将被设置为用户输入的 IP 地址。
if( isset( $_POST[ 'Submit' ] ) ) {
$target = trim($_REQUEST[ 'ip' ]);
……
trim() — 去除字符串首尾处的空白字符(或者其他字符)
2.从用户输入中移除特殊字符,例如 '&'
、';'
、'|'
、'-'
、'$'
、 '('
、 ')'
、 和 '||'
。
……
$substitutions = array(
'&' => '',
';' => '',
'| ' => '',
'-' => '',
'$' => '',
'(' => '',
')' => '',
'`' => '',
'||' => '',
);
$target = str_replace( array_keys( $substitutions ), $substitutions, $target );
……
存在漏洞:
High难度的命令注入漏洞源代码中曾加了替换特殊字符的数量,但我们仍能从中找到黑名单以外的字符来实现命令注入。
漏洞利用:
不难发现特殊符号没有对 '|'(无空格)
进行替换,在输入框输入 ip地址|系统命令
并执行,便可轻松获取想获取的系统信息,例如:
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();
?>
1.检查用户 token
和 session token
是否匹配。
……
checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );
……
checkdnsrr() — 给指定的主机(域名)或者IP地址做DNS通信检查,可以防止请求伪造。
2.去除 IP 地址中的反斜杠。
……
$target = stripslashes( $target );
……
stripslashes() — 反引用一个引用字符串。将反斜杠
'\'
转换为正斜杠'/'
。
3.将 IP 地址拆分成四个部分。
……
$octet = explode( ".", $target );
……
explode() — 使用一个字符串分割另一个字符串。这里用
'.'
将 IP 地址进行了分割。
4.检查 IP 地址分割后的各部分是否为单纯的数字,如果是,则将拆分的 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];
……
is_numeric() — 检测变量是否为数字或数字字符串。
不难看出该方法对输入的 IP 地址做了合法性验证,只有输入符合 IPv4 格式的内容才能被正常执行,排除了一切可以注入其他命令的可能,安全性方面得到大幅提升。