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

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

开发小鸡娃 安全随心录 2024-07-17 21:27

命令注入

命令执行漏洞是有些代码中需要调用到系统命令,当系统命令代码未对用户可控参数做过滤,则当用户能控制相和谐函数的参数的时候就可以把恶意命令把拼接正常命令中造成命令执行攻击。在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。如下  
![](https://mmbiz.qpic.cn/sz_mmbiz_png/9MnpyqibuMRZscq5aCWNOHq8wTyzlB6XkKYsSibx0ComM4M5G1no7cm5biae4MwhaCejWJkyP939d2qic20UUxUAMg/640?wx_fmt=png&from=appmsg "")  


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