SpringKill的0day|在Mybatis环境下绕过Ognl防护RCE

admin 2024年3月23日21:48:49评论14 views字数 5053阅读16分50秒阅读模式

此文章由SpringKiller安全研究师傅产出,这位佬是一个能独立开发一款企业级IAST,伸手0day伸脚1day,能手搓操作系统用脚逆向的师傅,还是OWASP的代码贡献者之一。哦,差点忘了,这个师傅能书会画,上厅堂下厨房无所不能,现在是甲方大爹。呆哥建议爱学习的可以找他击剑一下。

SpringKill师傅的Github地址:https://github.com/springkill

sdfd

01

本文简述

本文是SpringKill近期审计Mybatis绕过Ognl的RCE trick。此文较长,简述一下行文思路:表达式注入原理->审计Mybatis绕过Ognl的过程->某云案例/Mybatis官方修复。

02

OGNL表达式

01

什么是OGNL表达式

OGNL 是 Object-Graph Navigation Language(对象图导航语言)OGNL 最初是作为 WebWork 框架的一部分开发的,现在已成为Apache Struts2 的一个关键组件(这也就是为什么Struts2中的OGNL表达式漏洞这么多),并被用于其他各种 Java 框架。

02

对象图导航

OGNL的核心就在于对象图导航这个概念,其实和数据结构中的图和很相似,再OGNL或者面向对象编程语言中:以引用作为边,对象作为节点,对象可以包含其他对象(组合)或与其他对象产生关联(聚合),从而在更大的对象图中形成子图。

以一段简单的代码作为解释:

public class ObjectGraphDemo {    class People{        Car car;    }    class Car{        String carName = "BMW";        House house;    }    class House{        String houseName = "MyHouse";    }    public void printPeopleInfo(){        People people = new People();        System.out.println(people.car.carName);        System.out.println(people.car.house.houseName);    }}

我们设定好了三个类,分别叫PeopleCar还有House,通过这三个类的引用,我们就可以体会到以引用作为边,对象作为节点的这么一种设计理念。

03

OGNL中都有什么

想知道OGNL都有什么,不如直接点进去看看,我们都知道再ognl.Ognl.getValue()方法处会触发RCE漏洞,那么也就是看在这里getValue方法接受了什么参数(其实更简单的办法是去问问GPT):

这里我将所有的getValue全都拷贝过来了,看起来很多其实全都是重载方法进行互相调用,通过看这些代码发现必不可少的三样东西是expressioncontextroot,这也就是我们说的OGNL的三要素,其实这些东西网上很多人都分析过了,之所以这么啰嗦还是为了自己能够更好地理解。

public static Object getValue(Object tree, Map context, Object root) throws OgnlException {    return getValue((Object)tree, (Map)context, root, (Class)null);}public static Object getValue(Object tree, Map context, Object root, Class resultType) throws OgnlException {    OgnlContext ognlContext = (OgnlContext)addDefaultContext(root, context);    Node node = (Node)tree;    Object result;    if (node.getAccessor() != null) {        result = node.getAccessor().get(ognlContext, root);    } else {        result = node.getValue(ognlContext, root);    }    if (resultType != null) {        result = getTypeConverter(context).convertValue(context, root, (Member)null, (String)null, result, resultType);    }    return result;}public static Object getValue(ExpressionAccessor expression, OgnlContext context, Object root) {    return expression.get(context, root);}public static Object getValue(ExpressionAccessor expression, OgnlContext context, Object root, Class resultType) {    return getTypeConverter(context).convertValue(context, root, (Member)null, (String)null, expression.get(context, root), resultType);}public static Object getValue(String expression, Map context, Object root) throws OgnlException {    return getValue((String)expression, (Map)context, root, (Class)null);}public static Object getValue(String expression, Map context, Object root, Class resultType) throws OgnlException {    return getValue(parseExpression(expression), context, root, resultType);}public static Object getValue(Object tree, Object root) throws OgnlException {    return getValue((Object)tree, (Object)root, (Class)null);}public static Object getValue(Object tree, Object root, Class resultType) throws OgnlException {    return getValue(tree, createDefaultContext(root), root, resultType);}public static Object getValue(String expression, Object root) throws OgnlException {    return getValue((String)expression, (Object)root, (Class)null);}public static Object getValue(String expression, Object root, Class resultType) throws OgnlException {    return getValue(parseExpression(expression), root, resultType);}

那么接下来逐步了解OGNL三要素是什么。

04

expression

表达式是OGNL的核心,OGNL的内容都是从表达式出发的,表达式规定了程序在解析后需要做什么操作。

05

root

 root对象在OGNL中可以看作是一个节点,当表达式被解析后对谁进行操作,这其中的“谁”就是root。

06

context

context上下文,是一个Map类型的数据结构,包含OGNL表达式执行时候的上下文(root也在其中)。

为了更好地了解,我在demo中提供了一个setinfo接口,可以方便观察root和非root节点在使用OGNL表达式时候的差异。

07

OGNL的基本使用

我在demo中留了一个例子来测试OGNL表达式,那么接下来就来熟悉一下OGNL表达式的用法吧:

OGNL的语法和Java很像,基本上熟悉了Java就能简单使用OGNL表达式。

OGNL中可以使用的操作符+, -, *, /, ++, --, ==, !=, =,mod, in, not in

对于非root自定义对象,我们可以通过.来链接,就和Java中一样,比如我要访问people1中的carcarName字段,只需要使用#people1.car.carName就可以访问了。

对于root对象也一样,只不过因为root只有一个,所以不需要加root的名字就可以。

08

OGNL引用静态资源

要引用类的静态方法和字段,他们的表达方式是一样的@class@member或者@class@method(args)

比如我们最喜欢的弹计算器操作,其实就可以写成@java.lang.Runtime@getRuntime().exec("calc")

通过@java.lang.Runtime访问Runtime类,然后通过@getRuntime().exec("calc")拿到Runtime实例并执行命令。

SpringKill的0day|在Mybatis环境下绕过Ognl防护RCE

09

数组、Map、容器

OGNL支持对数组、Map、容器进行操作,如我在People类中新增了一个数组:

public static class People {        public Car car = new Car();        public String[] stratt = new String[]{"1", "2", "3"};    }

同样的对于前文的root和非root节点,只需要分别用以下表达式去访问

root:stratt[1]读取

SpringKill的0day|在Mybatis环境下绕过Ognl防护RCE

非root:#people1.stratt[2]

SpringKill的0day|在Mybatis环境下绕过Ognl防护RCE

也可以用Java中类似的方式新建数组并且访问:

new java.lang.String[]{"a","b","c"}[1]

new String[]{"a","b","c"}[2]

这两种方法都是可以的。

SpringKill的0day|在Mybatis环境下绕过Ognl防护RCE

Map同理,可以使用key的值来直接访问value

#{"A":"a","B":"b","C":"c"}["B"]

SpringKill的0day|在Mybatis环境下绕过Ognl防护RCE

10

选择和投影

这段就作为一个了解吧,我直接复制了milktea师傅的文章中的一部分:

OGNL支持类似数据库中的投影(projection) 和选择(selection)。

投影就是选出集合中每个元素的相同属性组成新的集合,类似于关系数据库的字段操作。投影操作语法为 collection.{XXX},其中XXX是这个集合中每个元素的公共属性。

例如:group.userList.{username}将获得某个group中的所有user的name的列表。

选择就是过滤满足selection条件的集合元素,类似于关系数据库的纪录操作。选择操作的语法为:collection.{X YYY},其中`X`是一个选择操作符,后面则是选择用的逻辑表达式。而选择操作符有三种:

  • ?选择满足条件的所有元素

  • ^选择满足条件的第一个元素

  • $选择满足条件的最后一个元素

例如:group.userList.{? #txxx.xxx != null}将获得某个groupusername不为空的user的列表。

03

在Mybatis环境下绕过Ognl防护

那天SpringKill在公司审代码,发现mybatis中频繁使用了setAccessable(),跟进去发现了这么一个类,正好也是闲的,就用它写了个弹计算器,心想也没啥用。

SpringKill的0day|在Mybatis环境下绕过Ognl防护RCE

SpringKill的0day|在Mybatis环境下绕过Ognl防护RCE

后来闲的没事突然想这东西能不能写进ognl里面然后绕过反射的限制呢?好巧不巧第二天Umbrella让我助他SSTI,这个trick就有用了么这不。

ognl中通过forname()可以判断有没有这个类,也就是判断用没用mybatis

有的那么就尝试构造个表达式获取一下runtime实例,绕过防护试试。

SpringKill的0day|在Mybatis环境下绕过Ognl防护RCE

本地和线上都成功了,那就试试直接r呢?

SpringKill的0day|在Mybatis环境下绕过Ognl防护RCE

本地可以的,但是线上不行,因为有RASP挡住了,不过也算是一次稍微成功的尝试吧。

04

Mybatis issue

 https://github.com/mybatis/mybatis-3/issues/3115

SpringKill的0day|在Mybatis环境下绕过Ognl防护RCE

官方下一个版本修复。

SpringKill的0day|在Mybatis环境下绕过Ognl防护RCE

CVE申请中...

原文始发于微信公众号(阿呆攻防):SpringKill的0day|在Mybatis环境下绕过Ognl防护RCE

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年3月23日21:48:49
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   SpringKill的0day|在Mybatis环境下绕过Ognl防护RCEhttp://cn-sec.com/archives/2597603.html

发表评论

匿名网友 填写信息