命令注入
简介:
命令注入是一种攻击,其目标是通过易受攻击的应用程序在主机操作系统上执行任意命令。当应用程序将不安全的用户提供的数据(表单、Cookie、HTTP表头等)传递给系统shell时,可能会产生命令注入漏洞。在进行攻击时,攻击者提供的操作系统命令通常以易受攻击的应用程序的权限执行。
产生原因:
- web服务器没有对用户提交的参数进行有效的检测过滤
- 操作系统允许一条语句在使用连接符和管道符后执行多条命令
Low级别
- 登陆DVWA平台,选择Low级别然后进入Command Injection模块。
- 通过页面我们可以看出这是让你输入一个IP地址然后对其进行Ping测试,因此我们先只输入一个地址观察一下。
这里我们可以看到在下方返回了对127.0.0.1进行Ping检测的内容,因此我们可以猜测这里有一个可以执行系统命令的函数,则这里可能存在命令注入漏洞,且执行的命令为Ping 输入的内容。
- 然后我们进行测试,我们利用windows命令的连接符&&进行测试。这里我们输入127.0.0.1&&dir
可以看见这里不仅仅返回了127.0.0.1的Ping检测信息,还返回了刚刚拼接dir命令的结果即当前目录下的文件和子目录的信息。然后我们就可以通过拼接其他命令进行攻击,达到我们的目的。
- 然后我们查看一下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 命令后面。
在这里运用了两个函数stristr(string,search,before_search)和php_uname(mode)函数,第一个函数的作用是搜索字符串在另一字符串中的第一次出现。before_search为可选参数默认值为 "false" 的布尔值。如果设置为 "true",它将返回 search 参数第一次出现之前的字符串部分。该函数的返回值为返回字符串的剩余部分(从匹配点),如果未找到所搜索的字符串,则返回 FALSE。
第二个函数的作用是返回运行php的操作系统的相关描述,参数mode可取值
”a” (此为默认,包含序列”s n r v m”里的所有模式)
”s ”(返回操作系统名称)
”n”(返回主机名)
”r”(返回版本名称)
”v”(返回版本信息)
”m”(返回机器类型)
因此这段代码的含义为判断该操作系统为哪种操作系统并按操作系统的不同执行其对应的Ping命令。
Medium级别
- 我们同样先试一下Low级别的做法,输入127.0.0.1&&dir进行测试,发现失败了。
- 不过windows下的管道符或者连接符不只有&&,还有以下几种:
command1&command2 无论command1是否执行成功都要执行command2
command1&&command2 只有command1执行成功后才执行command2
command1||command2 只有command1执行失败后才执行command2
command1|command2 将command1的执行结果传递给command2
- 既然用&&连接测试失败了,我们试一下利用&进行连接,我们输入127.0.0.1进行测试,发现下方成功显示出了命令结果,则表示注入成功。
- 那么我们来看一下代码是什么导致了这个结果。
<?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>";
}
?>
这里我们可以看见先对于Low级别而言这里多了一些代码,这段代码的作用就是对输入的字符进行判断若出现‘&&’或‘;’字符则用‘’代替。这样就在一定程度上隔绝了用此类连接符连接的命令注入。这是明显的黑名单做法,可还是不能有效的阻挡命令注入。
High级别
我们在依次使用不同的连接符或管道符进行测试,我们发现只有管道符 ‘|’ 对命令进行连接才能实现测试。通过刚刚Medium级别,我们同样猜测这里应该同样的采用了黑名单的做法,不过相对于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>";
}
?>
果然如同Medium级别一样采用的是黑名单方法,并扩大了限制的字符,在一定程度上限制了命令注入,但并没有起到实际作用。
Impossible级别
我们在依次进行输入,发现任何连接符和管道符都实现不了命令注入。并下面提示
ERROR: You have entered an invalid IP.
跟之前几个级别提示的
Ping 请求找不到主机 127.0.0.1dir。请检查该名称,然后重试。
这些无法判断服务器是怎样处理输入的数据,所以这里我们就直接看一下代码。
?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();
?>
我们发现这里还添加了token参数的检验,杜绝了CSRF漏洞的利用,并且这里我们发现在代码中,其限制了输入数据的格式,也就是说这里采用了白名单的做法,只有输入的格式跟白名单的一样才能通过验证,其他的都不行。这样就基本上杜绝了命令注入。