1-Web安全——命令执行注入攻击
目录
1. 系统命令执行
通常在程序开发过程中,应用程序在调用一些第三方程序时会用到一些系统命令相关函数。
例如在linux系统中,shell解释器就是用户和系统进行交互的一个接口,对用户的输入进行解析并在系统中执行。而shell脚本语言就是linux系统由各种shell命令组成的程序,具有其他普通编程的很多特点。
2. Web命令执行
常用的ASP,PHP,JSP等web脚本解释语言支持动态执行在运行时生成的代码这种特点,可以帮助开发者根据各种数据和条件动态修改程序代码,这对于开发人员来说是有利的,但这也隐藏着巨大的风险。
命令注入攻击(即Command Injection)是web应用程序对用户的输入提交的数据过滤不严格,导致攻击者构造恶意的特殊命令字符串的方式将数据提交到web应用程序中,从而执行外部程序或系统命令来进行攻击,非法获取目标服务器的数据资源。
命令注入攻击是挪威一名程序员在1997年意外发现的,他通过构造命令字符串的方式从一个网站上删除网页,就像从硬盘中删除一个文件这么简单。
3. 什么是PHP命令注入漏洞
php命令注入攻击是php语言中很常见的一种漏洞,通常web应用程序在调用php语言中的一些系统命令执行相关函数时,对用户的提交的数据没有进行合理的过滤或安全校验就作为函数参数执行产生的攻击。例如攻击者对网站写入一个php文件时,就可以通过php命令注入来实现向网站写入一个webshell文件,进一步实施渗透攻击。
4. PHP命令执行的函数
php中可以实现执行外部程序或系统命令的函数有:
- system
- exec
- passthru
- shell_exec
- popen
- proc_popen
4.1 system函数
在php中,命令执行函数主要的作用就是通过web应用程序执行外部程序或系统命令,例如web应用程序想要获取当前服务器系统的用户的话,就可以通过system函数来获取用户信息,构造代码如下:
<?php
$cmd = $_GET["cmd"];
if(isset($cmd))
{
echo "<pre>";
system("net user".$cmd);
echo "<pre>";
}
?>
以上代码中变量cmd会动态接收用户输入的命令,作为system函数的命令参数并执行,然后将结果输出。由于web应用程序可以通过变量cmd接收用户的不同命令并执行,而攻击者则可能利用变量cmd提交恶意数据进行命令注入攻击获取服务器的数据信息。
例如攻击者想要查看当前服务器的用户和ip地址等信息,那么可以在URL地址中构造系统命令,如下所示:
http://www.dvwa.com/cmd/test.php?cmd=||%20ipconfig
system函数执行结果如下所示:
攻击者通过cmd变量构造|| ipconfig命令提交数据后,在后台进行拼接后其实就是一个“net user || ipconfig”这样的字符串,并将这个字符串作为参数传入到system函数中执行,并且system函数会先执行net user系统命令获取当前服务器的用户,接着在执行ipconfig命令查看服务器的ip地址信息,并将命令执行后的结果输出。
4.2 exec函数
exec函数跟system函数的作用类似,唯一不同的就是exec函数不会将执行后的结果输出,其函数原型如下:
string exec ( string $command [, array &$output [, int &$return_var ]] )
参数说明:
Command:表示要执行的命令
Output:这是一个数组,用于接收exec函数执行后返回的字符串结果
return_var:记录exec函数执行后返回的状态
exec函数测试代码如下:
<?php
$cmd = $_GET["cmd"];
if(isset($cmd)){
echo "<pre>";
//output用于接收exec函数执行后的结果
$output = array();
//执行命令
exec($cmd , $output);
echo "<pre>";
//将output中的结果输出
while(list($key , $value)=each($output))
{
echo $value."<br/>";
}
}
?>
构造测试URL:http://www.dvwa.com/cmd/test.php?cmd=dir c:\ ,读取目标服务器的C盘文件,执行结果如下:
exec函数执行完后,会将执行后的结果放入到output数组中,需要在代码中将结果展示出来。
4.3 passthru函数
passthru函数也是用于执行系统命令的,并且会将执行后的结果进行输出,需要注意的是该函数主要是应用在unix系统下。当unix系统的命令输出的结果是二进制数据时,并且需要将数据结果返回给浏览器,就需要用到passthru函数来代替system和exec函数。
函数原型:
void passthru( string $command[, int &$return_var] )
参数的说明可以参考之前的system,exec函数。
passthru函数的php代码如下:
<?php
$cmd = $_GET["cmd"];
echo "<pre>";
//执行命令
passthru($cmd);
echo "<pre>";
?>
4.4 shell_exec函数
用于执行shell命令并将执行的结果以字符串的形式返回,但是不会将结果进行输出。
函数原型如下:
string shell_exec ( string $cmd )
shell_exec函数的测试代码如下:
<?php
$cmd = $_GET["cmd"];
echo "<pre>";
print shell_exec($cmd);
echo "<pre>";
?>
利用shell_exec函数查看当前服务器的系统配置信息,执行结果如下:
4.5 popen函数
popen函数会将执行后的系统命令结果用一个文件指针的形式返回。
函数原型:
resource popen ( string $command , string $mode )
参数mode表示打开的文件的读写方式。
popen函数的利用代码:
<?php
$cmd = $_GET["cmd"];
if(isset($cmd)){
echo "<pre>";
//将命令写入到文本
$cmd = $_GET["cmd"].">> 1.txt";
//执行系统命令
popen($cmd , "r");
echo "<pre>";
//打开并读写文本文件
$fp = fopen("1.txt" , "r");
if($fp){
while(!feof($fp)){
$content = fgets($fp);
echo $content;
}
}
fclose($fp);
}
?>
popen函数的执行结果如下:
5. 特殊符号——反引号
在php中``反引号里的字符串内容会解析为系统命令并执行。
反引号利用代码:
<?php
$cmd = $_GET["cmd"];
if(isset($cmd)){
echo "<pre>";
//会将反引号里的内容解析为系统命令并执行
print `$cmd`;
echo "<pre>";
}
?>
6. 命令执行用到的运算符
在进行命令注入攻击,特别是绕过WAF的时候可以使用 && ,& ,| ,||等符号分隔命令:
1. “&&”符号:Command1&&Command2表示先执行Command1命令,执行成功后再执行Command2命令,否则就不执行Command2。
以net user&&whoami命令为例,结果如下:
前面的net user命令执行过程中出错了,那“&&”符号后面的whoami命令就不会再执行了。
2. “&”符号: Command1&Command2表示先执行命令1,无论成功与否,都执行命令2。
以whoami%26net user命令为例,URL地址栏中不能直接使用“&”符号,只能使用“&”符号的URL编码%26,执行结果如下:
虽然执行net user命令出错了,但后面的whoami命令依然能得到执行。
3. “||”符号: Command1||Command2表示使用“||”符号进行连接实现逻辑或功能,只有在Command1命令执行失败后才会执行Command2关于其用法参考system函数。
4. “|”符号:|符号表示管道,把上一条命令的输出作为下一条命令的参数,可参考system函数中的用法。
7. DVWA靶场——命令注入
打开DVWA靶场再把难度调成low级别,选择Command Injection,如下所示:
dvwa靶场提示输入一个ip地址,但是如果想要执行其他命令的话就可以结合命令执行用到的运算符进行配合,例如获取服务器系统的当前用户信息,就可以构造127.0.0.1|net user命令进行注入。
分析后台代码:
<?php
if( isset( $_POST[ 'Submit' ] ) ) {
//接收ip地址
$target = $_REQUEST[ 'ip' ];
//确定操作系统并执行命令
if( stristr( php_uname( 's' ), 'Windows NT' ) ) {
$cmd = shell_exec( 'ping ' . $target );
} else {
//linux系统发送4个ping包
$cmd = shell_exec( 'ping -c 4 ' . $target );
}
//返回命令执行结果
echo "<pre>{$cmd}</pre>";
}
?>
后台对前台用户输入的命令没有做任何的过滤,直接把前端的输入作为命令执行函数的参数,并将执行后的结果返回。
把难度修改成medium级别,分析后台代码:
<?php
if( isset( $_POST[ 'Submit' ] ) ) {
//获取输入的命令
$target = $_REQUEST[ 'ip' ];
//过滤&& , ;
$substitutions = array(
'&&' => '',
';' => '',
);
//通过str_replace进行过滤
$target = str_replace( array_keys( $substitutions ), $substitutions, $target );
//确定操作系统并执行命令
if( stristr( php_uname( 's' ), 'Windows NT' ) ) {
//windows
$cmd = shell_exec( 'ping ' . $target );
} else {
//linux or unix
$cmd = shell_exec( 'ping -c 4 ' . $target );
}
echo "<pre>{$cmd}</pre>";
}
?>
medium级别的dvwa靶场对“$$”和“;”符号进行了过滤,str_replace函数会对这两个符号进行匹配,如果匹配到则把该字符串用空字符串进行替换,那么我们在进行命令执行注入时就不能使用“$$”和“;”符号了。
因此可以使用“||”,“|”,“$”符号对目标进行命令注入,分别构造127.0.301.1||net user和127.0.0.1|net user命令进行注入:
把难度调成high级别,分析代码:
<?php
if( isset( $_POST[ 'Submit' ] ) ) {
$target = trim($_REQUEST[ 'ip' ]);
//过滤列表
$substitutions = array(
'&' => '',
';' => '',
'| ' => '',
'-' => '',
'$' => '',
'(' => '',
')' => '',
'`' => '',
'||' => '',
);
$target = str_replace( array_keys( $substitutions ), $substitutions, $target );
if( stristr( php_uname( 's' ), 'Windows NT' ) ) {
$cmd = shell_exec( 'ping ' . $target );
}else {
$cmd = shell_exec( 'ping -c 4 ' . $target );
}
echo "<pre>{$cmd}</pre>";
}
?>
后台对于所有的系统命令符号都进行了过滤,但是仔细分析发现,“|”符号后面对了一个空格,也就是说后台对于“|”符号的过滤还是不严格,因此我们可以利用这一点进行绕过。
由于“|”符号前面的命令必须执行失败出错,后面的ipconfig命令才会得到执行,那么需要保证192.168.1.100这个ip地址是ping不通的。
DVWA靶场安全设置为impossible级别:
php
if( isset( $_POST[ 'Submit' ] ) ) {
//校验tooken防止CSRF
checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );
$target = $_REQUEST[ 'ip' ];
//去除反斜杠
$target = stripslashes( $target );
//对输入的ip进行分割
$octet = explode( ".", $target );
//判断用户提交的数据是否为数字
if( ( is_numeric( $octet[0] ) ) && ( is_numeric( $octet[1] ) ) && ( is_numeric( $octet[2] ) ) && ( is_numeric( $octet[3] ) ) && ( sizeof( $octet ) == 4 ) ) {
$target = $octet[0] . '.' . $octet[1] . '.' . $octet[2] . '.' . $octet[3];
if( stristr( php_uname( 's' ), 'Windows NT' ) ) {
$cmd = shell_exec( 'ping ' . $target );
}
else {
$cmd = shell_exec( 'ping -c 4 ' . $target );
}
echo "<pre>{$cmd}</pre>";
}
else {
echo '<pre>ERROR: You have entered an invalid IP.</pre>';
}
}
generateSessionToken();
?>
后台代码经过一系列的过滤,对于提交的数据格式只有为ip地址格式的才会被接收并执行,因此这里是不存在命令注入漏洞的。
8. eval注入攻击利用
php中的eval注入也可以实现命令注入,eval函数会将参数字符串作为php程序代码来执行,用户可以将php代码保存成字符串形式然后传递到eval函数执行。
函数原型:
mixed eval( string $code)
eval函数的参数code就是php代码字符串,攻击者可以通过eval的参数构造全部或部分字符串的内容实现命令注入。
测试代码:
<?php
$cmd = $_GET["cmd"];
if(isset($cmd)){
echo "<pre>";
eval($cmd);
echo "<pre>";
}
?>
在URL链接中http://www.dvwa.com/cmd/test.php?cmd=phpinfo(); 提交构造好的php字符串:
我们在URL中提交的“phpinfo();”字符串经过eval函数的处理,可以按照php代码来执行并返回结果。
9. 系统命令执行防御思路
1. 尽量减少命令执行函数的使用,可以在disable_functions中禁用掉,在php.ini文件中找到该选项,输入禁用的命令执行函数,以system函数为例:
禁用system函数之后,再次执行php代码就会报错:
Warning: system() has been disabled for security reasons in C:\wamp\www\dvwa.com\cmd\test.php on line 5
Call Stack
2. 命令执行的函数或方法的参数进行合理的过滤或校验
3. 参数的值尽量使用引号包裹,并在拼接前调用addslashes进行转义,该函数的用法可参考之前的文章。
更多推荐
所有评论(0)