java代码审计8之命令执行

本文详细介绍了Java中执行系统命令的Runtime、ProcessBuilder和ProcessImpl方法,以及SpEL表达式在Spring框架中的使用,展示了潜在的安全漏洞,并提供了修复建议。
摘要由CSDN通过智能技术生成

之前的文章,
php代码审计12之命令执行

1、Java命令执行的函数

在Java中可用于执行系统命令的函数有三个:

	java.lang.Runtime
	
	java.lang.ProcessBuilder

	java.lang.ProcessImpl

三个方法的调用关系如下,可以看到,

其实是Runtime方法封装了ProcessBuilder方法,

而ProcessBuilder方式是封装了ProcessImpl方法

在这里插入图片描述

1.1、Runtime类

Runtime类是私有的,

类的对象无法通过这种方式注册:  Runtime r = new Runtime() ;

只能通过静态方法(getRuntime)获取: Runtime r = Runtime.getRuntime();

rce1Servlet.java

package com.example.demo;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

@WebServlet("/rce1")
public class rce1Servlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.setCharacterEncoding("utf-8");
        resp.setCharacterEncoding("utf-8");
        resp.setContentType("text/html; charset=utf-8");
        String cmd = req.getParameter("cmd");
        StringBuffer sb = new StringBuffer();
        BufferedReader br = new BufferedReader(new InputStreamReader(Runtime.getRuntime().exec(cmd).getInputStream()));
        String line;
        while ((line=br.readLine())!=null){
            sb.append(line).append("</br>");
        }
        br.close();
        resp.getWriter().write(sb.toString());
    }
}

访问,

http://localhost:8080/rce1?cmd=calc

在这里插入图片描述

1.2、追踪三个函数的关系

直接跟进exec函数,一直“ctrl+点击exec”函数,

在这里插入图片描述

最终可以跟到,是调用的ProcessBuilder类的start函数,

在这里插入图片描述

继续跟进start函数,看到调用的是ProcessImpl.start

与上边的图一致,

在这里插入图片描述

1.3、ProcessBuilder类

这种复现本质意义不大,重点是记一下敏感函数,

rce2Servlet.java
package com.example.demo;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

@WebServlet("/rce2")
public class rce2Servlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.setCharacterEncoding("utf-8");
        resp.setCharacterEncoding("utf-8");
        resp.setContentType("text/html; charset=utf-8");
        String cmd = req.getParameter("cmd");
        String[] arrcmd={"cmd.exe","/c",cmd};
        StringBuffer sb = new StringBuffer();
        ProcessBuilder processBuilder= new ProcessBuilder(arrcmd);
        Process process = processBuilder.start();
        BufferedReader br = new BufferedReader(new InputStreamReader(process.getInputStream()));
        String line;
        while ((line=br.readLine())!=null){
            sb.append(line).append("</br>");
        }
        br.close();
        resp.getWriter().write(sb.toString());
    }
}

访问,
http://localhost:8080/rce1?cmd=calc
在这里插入图片描述

1.4、ProcessImpl类

ProcessImpl类通常是为ProcessBuilder.start()创建新进程服务的,不能直接去调用。

看到ProcessImpl类构造器私有,所以不能直接对其进行实例化,为了演示可以用反射进行调用。

在获取到一个静态方法后,必须用setAccessible修改它的作用域,否则不能调用。 

在这里插入图片描述

2、SpEL表达式

SpEL(Spring Expression Language),即Spring表达式语言,是比JSP的EL更强大的一种表达式语言。

从Spring 3开始引入了Spring表达式语言,支持在运行时查询和操作对象图,

可以与基于XML和基于注解的Spring配置还有bean定义一起使用。

SpEL的用法有三种形式,

一种是在注解@Value中;

一种是XML配置;

一种是在代码块中使用Expression。

各种Spring CVE漏洞都是基于Expression形式的SpEL表达式注入

2.1、正常使用的场景

@RequestMapping("/spel")
    @ResponseBody
    public String spel(String input){
        SpelExpressionParser parser = new SpelExpressionParser();
		Expression expression = parser.parseExpression(input);
		return expression.getValue().toString();
}
直接将用户的输入当作表达式内容进行解析。

输入一个简单的乘法运算2*2,可以看到返回的值是经过解析后的4

在这里插入图片描述
我们来模拟下,

        String  input = "2*2";
        SpelExpressionParser parser = new SpelExpressionParser();
        Expression expression = parser.parseExpression(input);
        System.out.println(expression.getValue().toString());

在这里插入图片描述

2.2、造成漏洞

上边的input是用户可控的,

当用户传入恶意参数,就会执行命令
        //String  input = "2*2";
        String input = "T(java.lang.Runtime).getRuntime().exec('calc')";
        SpelExpressionParser parser = new SpelExpressionParser();
        Expression expression = parser.parseExpression(input);
        System.out.println(expression.getValue().toString());

在这里插入图片描述
**Spel漏洞的原理:**

SpEL表达式是可以操作类及其方法的,可以通过类型表达式T(Type)来调用任意类方法。

T(Type) 运算符会调用类的作用域和方法;SpEL内置了 java.lang 包下的类声明


在不指定 EvaluationContext 的情况下默认采用的是 StandardEvaluationContext,

而它包含了SpEL的所有功能,允许用户在可以控制输入的情况下成功造成任意代码执行。

2.3、漏洞修复

EvaluationContext:这是一个通用的上下文接口,用于在SpEL表达式中传递变量和方法调用。

它包含了一些基本的属性访问器,


如getProperty()和setProperty(),以及一些方法,如setVariable()和getVariable()。

通过实现这个接口,可以在SpEL表达式中使用任意类型的变量和方法。


SimpleEvaluationContext和StandardEvaluationContext是SpEL提供的两个EvaluationContext,

SimpleEvaluationContext是一个简化版的EvaluationContext,
	
		仅支持SpEL语言语法的一个子集,不包括Java类型引用、构造函数和 bean引用。
		
		不会造成漏洞(一般用来修复造成的漏洞)

StandardEvaluationContext是完整版的EvaluationContext
		
		支持全部SpEL语法,在不指定EvaluationContext的情况下,默认使用此方法。
		
		会造成漏洞

所以我们在上边造成漏洞的代码内,没有任何和“StandardEvaluationContext”相关的代码,

但是仍然造成了漏洞,
//没有出现“StandardEvaluationContext”

		String input = "T(java.lang.Runtime).getRuntime().exec('calc')";
        SpelExpressionParser parser = new SpelExpressionParser();
        Expression expression = parser.parseExpression(input);
        System.out.println(expression.getValue().toString());

这段代码的功能和下边一致,

        //String  input = "2*2";
        String input = "T(java.lang.Runtime).getRuntime().exec('calc')";
        SpelExpressionParser parser = new SpelExpressionParser();
        EvaluationContext context = new StandardEvaluationContext();
        Expression expression = parser.parseExpression(input);
        System.out.println(expression.getValue(context).toString());

在这里插入图片描述

了解上述之后,修复就比较简单了,正常功能没问题,
        String  input = "2*2";
        //String input = "T(java.lang.Runtime).getRuntime().exec('calc')";
        SpelExpressionParser parser = new SpelExpressionParser();

        // 创建一个安全的EvaluationContext
        SimpleEvaluationContext simpleContext = SimpleEvaluationContext.forReadOnlyDataBinding().build();
        
        Expression expression = parser.parseExpression(input);
        System.out.println(expression.getValue(simpleContext).toString());

在这里插入图片描述

运行恶意代码就会报错,

在这里插入图片描述

比如 CVE-2018-1273 的修复方式就和我们上边几乎一致,

将StandardEvaluationContext换为SimpleEvaluationContext

在这里插入图片描述

2.4、多种产生漏洞的情况

第一种,

T(java.lang.Runtime).getRuntime().exec("calc")

完整漏洞代码,

package com.example.controller;

import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.expression.Expression;
import org.springframework.expression.common.TemplateParserContext;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
@EnableAutoConfiguration
public class Index {
    @ResponseBody
    @RequestMapping(value = "/spel", method = {RequestMethod.GET, RequestMethod.POST})
    public String spel(String cmd){
        SpelExpressionParser parser = new SpelExpressionParser(); 创建ExpressionParser解析表达式
//        TemplateParserContext templateParserContext = new TemplateParserContext();
//       Expression expression = parser.parseExpression(cmd,templateParserContext);
        Expression expression = parser.parseExpression(cmd);
        return expression.getValue().toString();

    }
}

在这里插入图片描述
第二种,

new java.lang.ProcessBuilder("calc").start()
代码和上边一致,

在这里插入图片描述
第三种,

cmd=#{2*2}
这种直接传参是会报错的,需要一个模板引擎,

在这里插入图片描述

这种需要post传参,测试get会报错,即使url编码“#”

在这里插入图片描述
一个问题是这种如何传递前两种方式呢,直接传就回显了
在这里插入图片描述
放到括号就ok了,

#{T(java.lang.Runtime).getRuntime().exec("calc")}

#{new java.lang.ProcessBuilder("calc").start()}

在这里插入图片描述

但是上边的两种么有回显,稍微改下就行了,
#{new java.util.Scanner(new java.lang.ProcessBuilder("cmd", "/c", "ipconfig /all").start().getInputStream(), "GBK").useDelimiter("asfsfsdfsf").next()}

小结,

、、正常无回显
T(java.lang.Runtime).getRuntime().exec("calc")
new java.lang.ProcessBuilder("calc").start()

、、正常有回显
new java.util.Scanner(new java.lang.ProcessBuilder("cmd", "/c", "ipconfig /all").start().getInputStream(), "GBK").useDelimiter("asfsfsdfsf").next()


、、模板的无回显,就将上面的payload放到“ #{} ”
#{T(java.lang.Runtime).getRuntime().exec("calc")}
#{new java.lang.ProcessBuilder("calc").start()}

、、模板有回显,就将上面的payload放到“ #{} ”
#{new java.util.Scanner(new java.lang.ProcessBuilder("cmd", "/c", "ipconfig /all").start().getInputStream(), "GBK").useDelimiter("asfsfsdfsf").next()}

2.5、发现漏洞,重要关注两点:

存在new SpelExpressionParser()		、、表示创建了spel的解析器(使用spel技术)

parseExpression(spel)				、、参数spel可控( 解析表达式 )
String spel = "new java.util.Date()";

ExpressionParser parser = new SpelExpressionParser();

Expression expression = parser.parseExpression(spel);

System.out.println(expression.getValue());
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

划水的小白白

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值