java的命令执行漏洞揭秘

0x01 前言

在Java中可用于执行系统命令常见的方式有两种,API为:java.lang.Runtimejava.lang.ProcessBuilder

0x02 java.lang.Runtime

java 复制代码
@GetMapping("/runtime/exec")
    public String CommandExec(String cmd) {
        Runtime run = Runtime.getRuntime(); //Runtime.getRuntime.exec
        StringBuilder sb = new StringBuilder(); //ProcessBuilder.start()

        try {
            Process p = run.exec(cmd);
            //只是调用了对应进程没有回显,需要从流中读取
            BufferedInputStream in = new BufferedInputStream(p.getInputStream());
            BufferedReader inBr = new BufferedReader(new InputStreamReader(in));
            String tmpStr;

            while ((tmpStr = inBr.readLine()) != null) {
                sb.append(tmpStr);
            }

            if (p.waitFor() != 0) {
                if (p.exitValue() == 1)
                    return "Command exec failed!!";
            }

            inBr.close();
            in.close();
        } catch (Exception e) {
            return e.toString();
        }
        return sb.toString();
    }

最基础的Runtime.getRuntime().exec(cmd),直接传入命令即可执行

0x03 java.lang.ProcessBuilder

1. 直接传入参数导致命令执行

java 复制代码
 @GetMapping("/ProcessBuilder")
    public String processBuilder(String cmd) {

        StringBuilder sb = new StringBuilder();

        try {
           // String[] arrCmd = {"/bin/sh", "-c", cmd};  //linux
            String[] arrCmd = {cmd};                 //windows,windows下无需指定
            ProcessBuilder processBuilder = new ProcessBuilder(arrCmd);
            Process p = processBuilder.start();
            BufferedInputStream in = new BufferedInputStream(p.getInputStream());
            BufferedReader inBr = new BufferedReader(new InputStreamReader(in));
            String tmpStr;

            while ((tmpStr = inBr.readLine()) != null) {
                sb.append(tmpStr);
            }
        } catch (Exception e) {
            return e.toString();
        }

        return sb.toString();
    }

同样也是直接执行命令,不同的是使用的是ProcessBuilder来执行命令。ProcessBuilder传入参数为列表,第一个参数为可执行命令程序,后面的参数为执行的命令程序的参数

2. 传入参数拼接导致命令执行

java 复制代码
 @GetMapping("/codeinject")
    public String codeInject(String filepath) throws IOException {

        String[] cmdList = new String[]{"sh", "-c", "ls -la " + filepath};
        //String[] cmdList = new String[]{"cmd.exe", "-c", "dir " + filepath};
        ProcessBuilder builder = new ProcessBuilder(cmdList);
        builder.redirectErrorStream(true);
        Process process = builder.start();
        return WebUtils.convertStreamToString(process.getInputStream());
    }

获取一个参数filepath,然后通过ProcessBuilder将数组cmdList中 的字符串拼接起来执行命令,由于没有对输入filepath进行过滤,原本用作查看目录下文件的一个功能就会被执行恶意命令。通过Java执行系统命令,与cmd中或者终端上一样执行shell命令,最典型的用法就是使用Runtime.getRuntime().exec(command)new ProcessBuilder(cmdArray).start()

拼接后paylaod:http://localhost/codeinject?filepath=/tmp;%20id

3. Host头未过滤命令执行

java 复制代码
    @GetMapping("/codeinject/host")
    public String codeInjectHost(HttpServletRequest request) throws IOException {

        String host = request.getHeader("host");
        logger.info(host);
        String[] cmdList = new String[]{"sh", "-c", "curl " + host};
        ProcessBuilder builder = new ProcessBuilder(cmdList);
        builder.redirectErrorStream(true);
        Process process = builder.start();
        return WebUtils.convertStreamToString(process.getInputStream());
    }

代码中在header中的host中设置参数且未过滤拼接后传入ProcessBuilder导致命令执行

payload:Host: 192.168.1.4;id && pwd

修复方式:

利用SecurityUtil.cmdFilter来对传入的参数进行过滤,严格限制用户输入只能包含a-zA-Z0-9_-.字符

java 复制代码
   public static String cmdFilter(String input) {
        if (!FILTER_PATTERN.matcher(input).matches()) {
            return null;
        }

        return input;
    }

0x04 其它方式

ScriptEngineManager

java 复制代码
    @GetMapping("/jscmd")
    public void jsEngine(String jsurl) throws Exception{
        // js nashorn javascript ecmascript
        ScriptEngine engine = new ScriptEngineManager().getEngineByName("js");
        Bindings bindings = engine.getBindings(ScriptContext.ENGINE_SCOPE);//启动javascript引擎
        String cmd = String.format("load(\"%s\")", jsurl);
        engine.eval(cmd, bindings);
    }

yml

java 复制代码
    @GetMapping("/vuln/yarm")
    public void yarm(String content) {
        Yaml y = new Yaml();
        y.load(content);
    }

groovy

java 复制代码
 @GetMapping("groovy")
    public void groovyshell(String content) {
        GroovyShell groovyShell = new GroovyShell();
        groovyShell.evaluate(content);
    }

0x05 总结

注意:java的Runtime.getRuntime.exec和ProcessBuilder.start,都是直接启动传入参数对应的进程,如果只是命令执行的部分参数可控,想在java中通过;、|、&等实现命令注入是行不通的, 例如这样传入id && whoami命令是无法执行的

存在问题的关键词:

java 复制代码
Runtime.getRuntime().exec()、new ProcessBuilder().start()、cmd
ScriptEngineManager、Yaml、GroovyShell
相关推荐
晚夜微雨问海棠呀5 分钟前
长沙景区数据分析项目实现
开发语言·python·信息可视化
graceyun5 分钟前
C语言初阶习题【9】数9的个数
c语言·开发语言
hanbarger7 分钟前
mybatis框架——缓存,分页
java·spring·mybatis
cdut_suye15 分钟前
Linux工具使用指南:从apt管理、gcc编译到makefile构建与gdb调试
java·linux·运维·服务器·c++·人工智能·python
苹果醋327 分钟前
2020重新出发,MySql基础,MySql表数据操作
java·运维·spring boot·mysql·nginx
小蜗牛慢慢爬行28 分钟前
如何在 Spring Boot 微服务中设置和管理多个数据库
java·数据库·spring boot·后端·微服务·架构·hibernate
azhou的代码园31 分钟前
基于JAVA+SpringBoot+Vue的制造装备物联及生产管理ERP系统
java·spring boot·制造
波音彬要多做39 分钟前
41 stack类与queue类
开发语言·数据结构·c++·学习·算法
Swift社区1 小时前
Excel 列名称转换问题 Swift 解答
开发语言·excel·swift
一道微光1 小时前
Mac的M2芯片运行lightgbm报错,其他python包可用,x86_x64架构运行
开发语言·python·macos