第四课-系统学习代码审计:命令执行漏洞讲解

admin 2024年7月18日13:50:21评论14 views字数 5522阅读18分24秒阅读模式
命令注入
    命令执行漏洞是有些代码中需要调用到系统命令,当系统命令代码未对用户可控参数做过滤,则当用户能控制相和谐函数的参数的时候就可以把恶意命令把拼接正常命令中造成命令执行攻击。在Java中,命令执行漏洞主要包含两个主要方法,ProcessBuilder.start()、Runtime.getRuntime.exe()。
ProcessBuilder命令执行漏洞
ProcessBuilder 是 Java 中的一个类,用于创建和管理操作系统进程。它允许你在 Java 应用程序中执行系统命令。然而,如果使用不当,ProcessBuilder 可能导致命令注入漏洞。这种漏洞使得攻击者能够通过不受信任的输入执行任意命令,进而控制目标系统。
    其中每个ProcessBuilder管理一个进程属性,start()方法会创建一个新的Process实例。

ProcessBuilder使用方法
public static void main(String[] args) throws IOException {//        ProcessBuilder pb = new ProcessBuilder("cmd", "/c", "calc");        ProcessBuilder pb = new ProcessBuilder("cmd", "/c", "dir && calc");        Process p = pb.start();        // 输出结果        // 命令正常执行的输出        InputStream inputStream = p.getInputStream();        // 命令执行失败的输出        InputStream errorStream = p.getErrorStream();        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream,"GBK"));        String line = null;        // 如果命令正确,输出结果        if (inputStream != null){            while ((line = bufferedReader.readLine()) != null) {                System.out.println(line);            }        }        // 如果命令失败,输出结果        if (errorStream != null){            BufferedReader errorReader = new BufferedReader(new InputStreamReader(errorStream,"GBK"));            while ((line = errorReader.readLine()) != null) {                System.out.println(line);            }        }        bufferedReader.close();        inputStream.close();        p.destroy();        System.out.println("执行完毕");    }

ProcessBuilder命令注入案例

@GetMapping("/ping")    public Map ping(String ip) throws Exception {//        ProcessBuilder pb = new ProcessBuilder("ping" ,ip);        ProcessBuilder pb = new ProcessBuilder("cmd","/c","ping" ,ip);        Process p = pb.start();        InputStream inputStream = p.getInputStream();        // 命令执行失败的输出        InputStream errorStream = p.getErrorStream();        // 封装返回结果        HashMap hashMap = new HashMap();        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream,"GBK"));        String line = null;        StringBuilder result = new StringBuilder();        // 如果命令正确,输出结果        if (inputStream != null){            while ((line = bufferedReader.readLine()) != null) {                System.out.println(line);                result.append(line + "<hr/>");            }        }        // 如果命令失败,输出结果        if (errorStream != null){            BufferedReader errorReader = new BufferedReader(new InputStreamReader(errorStream,"GBK"));            while ((line = errorReader.readLine()) != null) {                System.out.println(line);                result.append(line + "<hr/>");            }        }        bufferedReader.close();        inputStream.close();//        p.destroy();        System.out.println("执行完毕");        hashMap.put("data",result);        return hashMap;    }

    现在由于IP没有做任何过滤,因此可以进行命令拼接,但是进行命令拼接还要分为不同的操作系统。

| 操作系统 | 符号  | 示例               | 说明                                               ||----------|-------|--------------------|----------------------------------------------------|| Windows  | `&`   | `cmd1 & cmd2`      | 先执行`cmd1`,再执行`cmd2`|| Windows  | `&&`  | `cmd1 && cmd2`     | 先执行`cmd1`,如果成功则执行`cmd2`。|| Windows  | `||`  | `cmd1 || cmd2`     | 先执行`cmd1`,如果失败则执行`cmd2`|| Windows  | `|`   | `cmd1 | cmd2`      |`cmd1`的输出作为`cmd2`的输入。|| Linux    | `;`   | `cmd1; cmd2`       | 先执行`cmd1`,再执行`cmd2`。|| Linux    | `&&`  | `cmd1 && cmd2`     | 先执行`cmd1`,如果成功则执行`cmd2`|| Linux    | `||`  | `cmd1 || cmd2`     | 先执行`cmd1`,如果失败则执行`cmd2`。|| Linux    | `|`   | `cmd1 | cmd2`      | 将`cmd1`的输出作为`cmd2`的输入。|| Linux    | `&`   | `cmd1 & cmd2`      | 将`cmd1`放入后台执行,然后立即执行`cmd2`。|

 

    但是,当在输入框进行命令拼接的时候,会发现报错。这是因为在执行系统命令的时候,实际并没有获取Windows或Linux的shell,因此如果想要拼接,需要先调用一个shell。如下

第四课-系统学习代码审计:命令执行漏洞讲解

 

Runtime Exec命令执行漏洞

//执行指定的字符串命令。exec(String command)//执行由字符串数组指定的命令和参数。exec(String[] cmdArray)//执行指定的字符串命令,并在指定的环境变量下。exec(String command, String[] envp)//执行由字符串数组指定的命令和参数,并在指定的环境变量下。exec(String[] cmdArray, String[] envp)//在指定的工作目录下执行指定的字符串命令,并在指定的环境变量下。exec(String command, String[] envp, File dir)//在指定的工作目录下执行由字符串数组指定的命令和参数,并在指定的环境变量下。exec(String[] cmdArray, String[] envp, File dir)

ScriptEngineManager
在Java中,ScriptEngine 是用于执行脚本代码的 API。它是 Java Scripting API 的一部分,通常用于在 Java 应用程序中嵌入脚本语言代码。

// ====== 获取执行引擎// 创建一个 ScriptEngineManager 实例ScriptEngineManager manager = new ScriptEngineManager();// 通过 ScriptEngineManager 获取一个 JavaScript 引擎实例ScriptEngine engine = manager.getEngineByName("JavaScript");// 检查是否成功获取引擎if (engine != null) {    System.out.println("JavaScript 引擎已成功加载。");} else {    System.out.println("未能加载 JavaScript 引擎。");}// ==================== 执行JS代码===========ScriptEngineManager manager = new ScriptEngineManager();    ScriptEngine engine = manager.getEngineByName("JavaScript");    try {        // 执行简单的 JavaScript 表达式        engine.eval("print('Hello, World!');");        // 执行带有变量的 JavaScript 代码        engine.eval("var x = 10; var y = 20; var sum = x + y; print('Sum: ' + sum);");        // 从 Java 调用 JavaScript 函数        String script = "function greet(name) { return 'Hello, ' + name; }";        engine.eval(script);        Object result = engine.eval("greet('Alice');");        System.out.println(result); // 输出:Hello, Alice    } catch (ScriptException e) {        e.printStackTrace();    }}// ========================= 传递 Java 对象到 JavaScript    ==================ScriptEngineManager manager = new ScriptEngineManager();    ScriptEngine engine = manager.getEngineByName("JavaScript");    try {        // 创建一个 Java 对象        Person person = new Person("John", 30);        // 将 Java 对象绑定到脚本引擎的上下文中        engine.put("person", person);        // 在 JavaScript 中访问和修改 Java 对象的属性        engine.eval("print('Name: ' + person.getName());");        engine.eval("person.setName('Jane');");        engine.eval("print('New Name: ' + person.getName());");    } catch (ScriptException e) {        e.printStackTrace();    }}// ================= 执行外部的 JavaScript 文件============ScriptEngineManager manager = new ScriptEngineManager();ScriptEngine engine = manager.getEngineByName("JavaScript");try (FileReader reader = new FileReader("path/to/your/script.js")) {    // 执行外部 JavaScript 文件    engine.eval(reader);} catch (ScriptException | IOException e) {    e.printStackTrace();}

    因为scriptEngine的相关特性,可以执行java代码,例如下面代码会执行弹窗。

try {    String test = "var a = mainOutput(); function mainOutput() { var x=java.lang.Runtime.getRuntime().exec("calc"); };";    engine.eval(test);}catch (Exception e){}

 

总结:

除了以上三种,还有很多可以执行系统命令的方式,主要分的话就是两大类,一类是原生的就是Runtime和ProcessBuilder,另一类是组件,包括不限于:Groovy代码注入、scriptengine任意命令执行、JMX等等

参考链接:

    Groovy代码注入
https://xz.aliyun.com/t/8231?time__1311=n4%2BxnD0Dc7eYuxmqGNnmDUx%2FRKDtKD9ACoD

scriptengine任意命令执行

https://github.com/yzddmr6/Java-Js-Engine-Payloads?tab=readme-ov-file#js%E5%8A%A0%E8%BD%BD%E4%BB%BB%E6%84%8F%E5%AD%97%E8%8A%82%E7%A0%81

 

原文始发于微信公众号(安全随心录):第四课-系统学习代码审计:命令执行漏洞讲解

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年7月18日13:50:21
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   第四课-系统学习代码审计:命令执行漏洞讲解https://cn-sec.com/archives/2968252.html

发表评论

匿名网友 填写信息