命令注入漏洞
注: 命令注入漏洞的分析, 及含有命令注入漏洞的函数解析
含有命令注入漏洞的函数: system(),exec(),passthru(),shell_exec(),``(与 shell_exec()功能相同)
一, 基于 DVWA 环境的命令注入漏洞(shell_exec)
1, 函数用法
String shell_exec(string command)
command 要执行的命令
2, 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>";
- }
- ?>
源码分析:
函数首先判断环境下的系统, 如果是 win 则执行第一个命令, 若是 linux 执行的命令加上 - c 选项, 以为 linux 中 ping 命令是一直执行的. 只有加了 - c 指定发送的跳数才能停止.
可以看到在接收用户输入的地方, 对用户的输入没有做任何的处理. 不难看出这就是一个典型的命令注入漏洞. 而且孩子是最轻易.
我们正常测试一下:
可以看到, 正常返回的是 ping 返回的数据.
我们通过这个命令执行漏洞进行测试一下:
构造我们的语句: 10.39.1.4 | net user
解释: | 的意思是前面命令的输出结果作为后面命令的输入.
net user 查看当前系统中存在哪些用户
测试:
可以看到当前系统中存在三个用户. 如果作为 ××× 去利用的话就可以使用命令去创建一个用户. 就不在演示
漏洞分析: 不处理用户的任何输入就直接执行函数中的命令.
知识扩展:
; - 分号在 linux 命令执行的时候, 可以直接执行几条命令, 命令与命令之间用分号隔开.
& 前面的命令执行后接着执行候命的命令
&& 前面的命令执行成功后才可以执行下面的命令
| 前面的命令输出结果最为后面命令输入的内容
|| 前面的命令执行失败后才会执行后面的命令
3,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>";
- }
- ?>
源码分析:
Str_replace()函数, 以其他字符替换字符串中的一些字符(区分大小写).
$target = str_replace( array_keys( $substitutions ), $substitutions, $target ); 将用户输入的内容, 含有 && 或; 的替换为空.
其他的部分基本和 low 相差不大.
这里的源码对用户的输入进行了初步的过滤, 过滤掉了一些能够同时执行命令的符号, 但是我们知道, 拥有同样作用的符号不止 && 和;. 所以依然可以进行命令注入.
命令注入测试:
构造语句: 10.39.1.4 & net user
& 前面命令执行后接着执行后面的命令
测试:
依然获得了执行的结果
漏洞分析: 此级别下的源码虽然对用户的输入设置了过滤, 但是没有将特殊符号过滤完全, 仅仅设置黑名单是不够的, 你不知道用户会输入什么, 造成有心者亦可以利用此漏洞.
4, 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 级别的源码相差不大, 只是将更多的符号加入黑名单.
通过这样的确实能够有效的防御之前的诸多思路.
测试:
输入 10.39.1.4 | net user
已经不能用那些方法了. 具体的利用我也没有找到合适的方法.
漏洞分析: 只是做黑名单的话, 总是不够安全的, 只要黑名单不够完整, 就不是很安全. 即使你认为名单已经很完整了. 可能还有你不知道的存在可以利用.
5,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();
源码分析:
Explode()函数, 将字符串变为数组. 这里就是将我们输入的 ip, 变成数组
然后判断数组的前四组数是否为数字, 并且数组中有四个对象. 不满足就会报错提醒. 也就是说这里只允许你输入四组数字.
若果判断为 true 的话, 就会再将这四组数通过点连接起来再就行 ping 命令.
就不测试了, 这样的源码已经杜绝了你的所有命令注入 ×××.
二, eval()函数造成的命令注入漏洞
1, 函数用法
eval(phpcode)
Phpcode 规定要计算的 php 代码. 通常用分号结束每句代码的执行.
2, 环境源码:
- <?php
- $var = "var";
- if(isset($_GET["name"])){
- $arg = $_GET["name"];
- eval("\$var=$arg;");
- echo "\$var =".$var;
- }
- ?>
构造语句:
name=phpinfo()
测试效果:
3, ctf 题目实例
题目地址 bugku 中的本地包含: http://120.24.86.145:8003/
题目解析:
源码:
- <?php
- include "flag.php";
- $a = @$_REQUEST['hello'];
- eval( "var_dump($a);");
- show_source(__FILE__);
- ?>
构造语句:
Hello = file('flag.php')
解析, 当参数接收到的构造的语句的时候, 代码就会变为
Eval(var_dump(file('flag.php')))
Eval 函数, 执行函数体没的 php 代码; file()函数把整个文件读到一个数组中. Var_dump()函数 输出.
所以执行结果就是将 flag.php 中的内容以数组的形式输出出来.
得到 flag
三, system()函数造成的漏洞
1, 函数用法:
System(string command,int &return_var)
Command 要执行的命令
Return_var 存放命令的执行后的状态值
2, 环境源码:
- <?php
- $cmd = $_GET['cmd'];
- if(isset($cmd)){
- echo system("dir".$cmd);
- }
- ?>
构造语句:
Cmd=| net user
测试:
通过漏洞我们获得了系统中存在哪些用户, 同样的我们也可以通过这样的方法在系统中创建我们自己的用户. 并可以加入到管理员组中. 这里就不在说了.
四, shell_exec()函数造成的漏洞
1, 函数用法:
shell_exec(string command)
command 要执行的命令
2, 环境源码:
- <?php
- $cmd = $_GET['cmd'];
- if(isset($cmd)){
- echo "<h3>";
- echo shell_exec("dir".$cmd);
- echo "<h3>";
- }
- ?>
4, 测试:
构造语句: | net user
实现方法和上一个函数是一样的. 同样的函数还有 exec()和符号
五, passthru()函数造成的漏洞
1, 函数用法:
void passthru (string command, int &return_var)
command 要执行的命令
return_var 存放执行命令后的状态值
同 exec() 函数类似, passthru() 函数 也是用来执行外部命令 (command) 的. 当所执行的 Unix 命令输出二进制数据, 并且需要直接传送到浏览器的时候, 需要用此函数来替代 exec() 或 system() 函数.
2, 环境源码:
- <?php
- $cmd = $_GET['cmd'];
- if(isset($cmd)){
- echo passthru($cmd);
- }
- ?>
3, 测试
构造语句 cmd=net user
获得用户列表
六, 总结
总结以上所有函数漏洞造成的命令注入漏洞, 每一个例子都是因为没有对用户的输入进行处理. 在防御漏洞的时候, 一定明白一个道理, 所有用户的输入都是有害的. 所有的输入都是不值得相信的.
来源: http://blog.51cto.com/12332766/2151859