Java代码审计-命令执行

前言

今天来学一下java代码审计中的命令执行漏洞相关知识,浅浅记录一下。

一、漏洞简介

命令执行漏洞是指应用有时需要调用一些执行系统命令的函数,如果系统命令代码未对用户可控参数做过滤,则当用户能控制这些函数中的参数时,就可以将恶意系统命令拼接到正常命令中,从而造成命令执行攻击。
命令执行攻击主要存在以下几个危害:继承 Web 服务程序的权限去执行系统命令或读/写文件,反弹 shell,控制整个网站甚至控制服务器,进一步实现内网渗透。
在 PHP 开发语言中有 system()、exec()、shell_exec()、eval()、passthru()等函数可以执行系统命令。在 Java 开发语言中可以执行系统命令的函数有 Runtime.getRuntime.execProcessBuilder.start

二、命令连接符

Windows与Linux均支持:

  • cmd1 | cmd2 只执行cmd2

  • cmd1 || cmd2 只有当cmd1执行失败后,cmd2才被执行

  • cmd1 & cmd2 先执行cmd1,不管是否成功,都会执行cmd2

  • cmd1 && cmd2 先执行cmd1,cmd1执行成功后才执行cmd2,否则不执行cmd2

Linux单独支持:

  • cmd1;cmd2 依次顺序执行命令

三、ProcessBuilder命令执行

1.ProcessBuilder简介

ProcessBuilder类是java.lang包下的基础类,在使用时无需导入,可以直接使用。它主要用于创建和运行各类外部程序。其start() 方法利用这些属性创建一个新的 Process 实例,可以利用 ProcessBuilder 执行命令。

如下示例,ping本地

public static void main(String[] args) throws IOException {
        try {
            // 创建 ProcessBuilder 实例
            ProcessBuilder processBuilder = new ProcessBuilder("cmd","/c","ping", "127.0.0.1");
            System.out.println(processBuilder.command());//要执行的指令打印
            // 启动进程
            Process process = processBuilder.start();
            // 获取进程的输入流
            InputStream inputStream = process.getInputStream();
            BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));

            String line;
            // 读取进程的输出
            while ((line = reader.readLine()) != null) {
                System.out.println(line);
            }
            // 等待进程结束
            process.waitFor();
        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
        }
    }

输出如图所示。

输出结果

2.漏洞利用

如上述代码,如果直接输入的命令为127.0.0.1|dir,则执行效果如下。

输出结果

四、Runtime exec 命令执行

1.Runtime exec简介

java.lang.Runtime 公共类中的 exec()方法同样也可以执行系统命令,exec()方法的使用 方式有以下 6 种:

//在单独的进程中执行指定的字符串命令
public Process exec(String command) 
//在单独的进程中执行指定的命令和参数
public Process exec(String[] cmdarray) 
//在具有指定环境的单独进程中执行指定的命令和参数
public Process exec(String[] cmdarray, String[] envp) 
//在具有指定环境和工作目录的单独进程中执行指定的命令和参数
public Process exec(String[] cmdarray, String[] envp, File dir) 
//在具有指定环境的单独进程中执行指定的字符串命令
public Process exec(String command, String[] envp) 
//在具有指定环境和工作目录的单独进程中执行指定的字符串命令
public Process exec(String command, String[] envp, File dir)

比如直接拼接字符串,然后exec()方法执行命令,结果如下

public static void main(String[] args) throws IOException {
        // 从用户输入获取要 ping 的目标 IP 地址
        System.out.print("请输入要 ping 的 IP 地址或域名: ");
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        String userInput = reader.readLine();

        // 不安全的命令执行
        String command = "cmd /c"+ "ping " + userInput;  // 直接使用用户输入构建命令
        Process process = Runtime.getRuntime().exec(command);

        // 打印结果
        BufferedReader processInput = new BufferedReader(new InputStreamReader(process.getInputStream()));
        String line;
        while ((line = processInput.readLine()) != null) {
            System.out.println(line);
        }
    }

输入如下图所示。

输出结果

2.漏洞利用

和第二部分一样,输入的参数未经过过滤,随意拼接造成漏洞执行。

输出结果

五、探索

1.代码底层原理

探索一下,Runtime.getRuntime().exec(command)执行命令的底层原理。

String command = "cmd /c"+ "ping " + userInput;  // 直接使用用户输入构建命令
Process process = Runtime.getRuntime().exec(command);

进入exec方法

输出结果

发现调用了exec(String command, String[] envp, File dir)方法,默认envp和dir为空。

输出结果

发现这个函数 是借助StringTokenizer工具 对字符串进行分割,存入数组,再执行。

接下来,调试具体看下情况。

输出结果

然后,再次调用了exec(cmdarry,encp,dir),

输出结果

原来,在这里最终调用的还是ProcessBuilder()方法 。

2.疑问

还是有个小疑问,在写代码进行测试的时候,命令语句,拼接了cmd /c 。如果没有这个,也能执行ping命令,但是加入|dir注入并没有成功。那么,为什么会没有cmd /c 注入不会成功呢?另外为什么要加cmd /c 呢?

个人之见解如下:

  • 首先对于没有使用cmd /c 的方式,不会造成命令注入,上述看代码原理已经探索过,分割字符串时按照空格分割,所以会造成"ping" “127.0.0.1|dir” 两个字符串。"127.0.0.1|dir"被看成一个整体字符串,而命令不被解析,导致注入失败。

  • 对于使用 cmd /c的方式,更加灵活,可以使用管道、重定向等命令,所以对"127.0.0.1|dir"进行了解析,但是也造成了命令注入的风险。

3.小结

  • 使用 cmd /c:

    • 适合执行包含多个命令、管道、重定向等复杂命令的情况。

    • 所有在命令行中可用的功能和语法都可以使用。

    • 需要防范命令注入,需对输入参数进行过滤

  • 不使用 cmd /c:

    • 只适合执行简单的原生命令。

    • 不能利用命令解释器的特性,如管道和其他命令的结合。

    • 经个人简单测试,命令注入失败。

六、总结

本文浅显探索了Java中命令执行漏洞的相应代码和产生原理,另发现,看底层代码的具体实现逻辑还是挺有意思的!

参考

  • 网络安全:Java代码审计实战
  • Java代码审计(入门篇)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值