command Injection命令注入

command Injection命令注入

命令注入,是指在某些需要输入数据的位置,构造恶意代码破环原有的语句结构,而系统缺少有效的过滤,许多内容管理cms存在命令注入漏洞。

产生原因:

web前端传递参数到后台服务器上执行(特别是一些网络设备的web管理界面),由于开发者没有对输入进行严格的过滤,导致攻击者可以构造一些带有非法目的的一些命令,欺骗后台服务器执行,最终达到破坏数据,信息泄露,甚至掌控电脑的目的。

危害:
如果web应用是root权限,可以导致攻击者可以在该服务器上执行任意命令。

命令构造四种符号(windows,linux均可)

&&:代表首先执行命令a在执行命令b,但是前提条件是命令a执行正确才会执行命令b,在a执行失败的情况下不会执行b命令。所以又被称为短路运算符。

&:代表首先执行命令a在执行命令b,如果a执行失败,还是会继续执行命令b。也就是说命令b的执行不会受到命令a的干扰,在执行效率上来说“&&”更加高效。

||:代表首先执行a命令在执行b命令,如果a命令执行成功,就不会执行b命令,相反,如果a命令执行不成功,就会执行b命令。

|:直接执行后面的b命令

linux还可以:

linux系统还可以使用分号(;)同时执行多条命令
还可以使用重定向(>)在服务器中生成文件
使用(<)从准备好的文件读取命令等
127.0.0.1 & echo test >c:\1.txt 输出test,重定向保存到c盘下的文件中
(可以在服务器中写一句话木马)

low源码分析

 对输入的ip没有任何的过滤,直接拼接就可以执行命令
     
<?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
//php_uname( 's' )根据参数不同获取系统的不同信息
        $cmd = shell_exec( 'ping  ' . $target );
    }
    else {
        // *nix
        $cmd = shell_exec( 'ping  -c 4 ' . $target );
    }

    // Feedback for the end user
    echo "<pre>{$cmd}</pre>";
}

?>

根据源代码我们可以看到,对输入的ip没有作任何过滤,直接将它拼接即可执行命令,
php_uname('s')根据参数不同可以获取系统的不同信息,
    
stristr(string, search_string, bool)可以在string中搜索search_string 并将第一次匹配处及以后的字符串列出,如果没找到则返回false,在php中非空字符串为真,则可以判断系统类型。
    
没有任何过滤,直接使用 && & | || 就能完成注入
    
接收用户输入的ip,然后根据服务器是否是windows NT系统,对目标进行不同的ping测试
    
    除了ping之外让他执行我的一些别的命令
    
php_uname 获取web操作系统(服务器)的类型,返回运行php的操作系统的相关描述,参数mode可取值"a"(此为默认,包含序列“s,n,r,v,m"里的所有模式),"s" (返回操作系统的名称),"n" (返回主机名),"r"(返回版本名称),”r"(返回版本信息),"m"(返回机器类型)


stristr()在一个字符串里找是否存在指定的字符串

stristr( php_uname( 's' ), 'Windows NT' ) 
就是检查web服务器是不是windows服务器

 $cmd = shell_exec( 'ping  ' . $target );
如果判断正确,是windows服务器,就给cmd赋值
shell_exec()命令执行函数,引起命令执行漏洞的原因
    这个函数让php的网页程序,去执行操作系统的命令,对命令也没有过滤
    ping是操作系统的命令
    执行 ping 空格 我们输入的ip
    结果存储在变量cmd中,echo输出结果

    如果判断错误,就是linux,linux和windows的ping命令不同,
    windows是发4个包,linux是一直发包
    ping  -c 4 指定发包次数                                               

检查web服务器操作系统类型,根据不同情况,执行不同操作系统命令、
拼接之后就是,

ping 127.0.0.1 | net user

不需要闭合什么的,能够同时执行多条命令就可以

利用(用命令行获取管理员权限)

127.0.0.1 | net user
127.0.0.1 | net user test 123/add 
 添加账号
127.0.0.1 | net localgroup administrators test /add
 添加管理员权限

ping 192.168.225.1 主机

image

192.168.225.1&dir

image

dir命令类似于ls
显示一个磁盘上全部或部分文件目录(文件或文件夹),并显示文件信息(文件名,拓展名,文件长度,文件建立,最好一次修改的日期和时间等,不显示文件具体内容)

127.0.0.1&ipconfig

image

127.0.0.1 & net user xie /add 创建用户

127.0.0.1 & net 查看系统用户

medium级别命令注入漏洞

 $substitutions = array(
        '&&' => '',
        ';'  => '',
    );
//数组定义一堆变量,变量&&,值为空

 $target = str_replace( array_keys( $substitutions ), $substitutions, $target ); 
	array_keys( $substitutions )要替换的内容
     array_keys()获取数组(变量$substitutions)的键值,也就是&& ; 获取变量的key
     获取之后,替换成$substiutions,也就是括号中第二个,变量的值''
	$substitutions替换之后的内容
	$target我们输入的内容

就是用一个数组做的,两个str_replace

应对方法
使用其他连接符,比如 &
利用黑名单漏洞 127.0.0.1&;&net user

high

$target = trim($_REQUEST[ 'ip' ]); 
去掉字符串头尾空格

$substitutions = array(
        '&'  => '',
        ';'  => '',
        '| ' => '',
        '-'  => '',
        '$'  => '',
        '('  => '',
        ')'  => '',
        '`'  => '',
        '||' => '',
    ); 

image

把target的ip地址,用点来分割,分割之后变成四个变量,放入一个数组中

is_number($octet[0])判断octet这个数组第一个变量是不是数字
image

这四个变量都要是数字
还要判断
sizeof($octet)==4 判断这个变量数是不是4
image

四个变量重新拼接

high级别黑名单范围很全,但还是遗漏了一些连接符,可以利用 空格+|(管道符前一个命令的输出作为后一个命令的输入,且只能打印后面命令的结果)

源代码是过滤了 |+空格

也可以使用aaaa |net user
使用一个不会执行的命令和|来执行命令

漏洞分析

不具有通用性,只能防止ping命令
定义白名单,只允许输入ip地址
image

linux可以用第二种方法,给php建立独立的账号

漏洞防御:

image

在low的源代码中添加,escapeshellcmd()函数,对一些能引起命令执行漏洞的符号进行转义

image

escapeshellarg()把输入的命令变成一个整体,去ping

image

impossible

源码改动如下,可以发现加入了Anti-CSRF token,同时他将输入的命令通过stripslashes去掉反斜杠,由于规则是要输入ip地址,所以根据.来将输入的命令分成四个数组,之后通过is_numeric判断每个数组是否为数字且是否只有四个数组,判断成功后,按固定的ip格式重新组合地址,达到严格的参数限制,只有num.num.num.num才能执行。利用参数化的方式很好的避免了黑名单考虑不全和容易绕过的缺陷,防止了命令注入。

stripslashes(string) : 该函数会删除字符串string中的反斜杠,返回已剥离反斜杠的字符串。

explode(separator,string,limit): 该函数把字符串打散为数组,返回字符串的数组。参数separator规定在哪里分割字符串,参数string是要分割的字符串,可选参数limit规定所返回的数组元素的数目。

is_numeric(string): 该检测string是否为数字或数字字符串,如果是返回TRUE,否则返回FALSE。

可以看到,Impossible级别的代码加入了Anti-CSRF token,同时对参数ip进行了严格的限制,只有诸如“数字.数字.数字.数字”的输入才会被接收执行,因此不存在命令注入漏洞。

posted @ 2022-10-05 13:51  759518  阅读(1141)  评论(0编辑  收藏  举报