S2-019、S2-020

前言

“Struts2系列起始篇”是我整各系列的核心,希望大家能花些时间先看看。

正文

我发现关于一些早期的Struts2的漏洞,网上的分析文章并不多,不知道是不是我打开浏览器的方式不对,唯一看到的两篇文章也仅有poc,没有细节分析。于是我在官方上看了下漏洞详情,如下:

大概意思是动态方法调用开启了会导致安全问题,给出的信息太少了,于是我又看了下网上的poc(我做了一点修改):

http://127.0.0.1:8080/struts2-showcase-2.1.6/showcase.action?debug=command&expression=%23p%3d%23context.get(%27com.opensymphony.xwork2.dispatcher.HttpServletResponse%27).getWriter()%2c%23p.println(%22hacker%22)%2c%23p.close()

老实说根据这个poc我还是什么都看不出来(并且公告中说和动态方法调用有关,但是这里并没有用到类似xxx.action!method啊),但运行它确实能执行代码,没办法我只好一步步调试了。由于过程有点坎坷,这里就只说下我自己的思路吧,我先是在StrutsPrepareFilter的doFilter方法中下断点(当然也有可能是StrutsPrepareAndExecuteFilter,看大家在web.xml中是怎么配置的了,详细情况参考系列文章的第一篇),然后就是一步步往下走了:

关于这段代码DefaultActionInvocation.invoke(),在第一篇文章中介绍过了,在执行action之前会先循环调用Struts2的默认拦截器栈。说明Ognl表达式是在某个自带的拦截器中执行的,看下此时的变量栈,发现此时的拦截器是DebuggingInterceptor(实际上我测试的时候是根据poc后带的参数猜出来了,哈哈哈嗝~),进入DebuggingInterceptor.intercept方法中:

方法看起来很长,但实际上流程很简单,首先判断“org.apache.struts2.interceptor.debugging.DebuggingInterceptor.devMode”这个常量是否被设置为true了,如果是true说明项目开启了开发者模式(此漏洞的鸡肋之处),DebuggingInterceptor才会工作,进入if语句中,162行处获取请求中的“debug”参数,随后根据“debug”参数的值进入不同的分支中。总共有四个分支,但是很明显后面两个分支中调用了OgnlValueStack.findValue方法,熟悉Struts2的同学都知道该方法中会执行ognl表达式(第二篇文章中在分析S2-016时跟进过这个函数),而findValue方法的参数也是直接使用的请求中的另一个参数。这样构造poc起来也很简单(另外表达式也要url编码下):

?debug=command&expression=exp

?debug=browser&object=exp

到这里就S2-019就分析完了,问题是官网上说和动态方法调用有关,为什么我在struts.xml中配置了<constant
name="struts.enable.DynamicMethodInvocation"
value="false"/>,但是还是可以RCE啊,且分析过程中并没涉及动态方法调用~~~

S2-20

我不准备详细分析这个漏洞了,主要是因为漏洞较难利用起来,不过我觉得思路有独特之处,所以还是要介绍下。漏洞出现的地方是Struts2的另一个默认拦截器ParametersInterceptor,该拦截器其主要功能是把ActionContext中的请求参数设置到ValueStack中,看下他的代码:

跟进setParameters方法中:

先对请求参数名做判断是否合法,合法才会加入到acceptableParameters中,循环acceptableParameters中的实体,调用OgnlValueStack.setValue,我调试的时候最先想到的就是Ognl表达式注入,但是失败了原因一言难尽~

看下acceptableName方法,该方法就是为了防范一些安全问题:

isExcluded用于将参数名和配置文件中的一些特殊参数进行对比,禁止访问一些特殊参数,不过从下图可以看到,配置文件中的正则一直在修改,因为经常被绕过,S2-20之后就禁用了class开头的参数。。。

isAccepted方法也是做正则,防止ognl表达式注入,使用的正则如下图:

禁用了一些特殊字符,我尝试FUZZ了一下,都没成功,不过这里我说下我测试的结果吧。

禁用逗号是防止执行多条语句,例如我们之前使用的poc中

new java.lang.ProcessBuilder(new java.lang.String[]{‘cmd.exe‘, ‘/c‘,‘calc‘})).start()  但是我们使用

new java.lang.ProcessBuilder(new java.lang.String[]{‘calc‘})).start() 还是可以弹出计算机的

至于#,rce的poc中不使用#也是可以的,如下图(以S2-019为例)

冒号不让用我不知道是为什么,还有等号,大家可以看上面我并没有使用等号,而是用的“-”,实际上我fuzz了一下,逗号、点号、减号还有加号在这里有时候也可以,为什么是有时候呢,因为感觉像六脉神功似的,时灵时不灵。但大多数时候都能成功,也有可能是tomcat缓存的原因?大家感兴趣可以自己尝试下,希望不要误导大家,最后我觉得使用“

?a%2d(new+java.lang.ProcessBuilder(new+java.lang.String%5b%5d%7b%27calc%27%7d)).start()=xxx”应该能成功,然而并不行。

算了不浪费大家的时间了,这里直接说别人的利用方法吧。这是百度src的一位师傅写的文章(https://www.freebuf.com/articles/web/31039.html)。写的很清楚,大概意思是可以通过class.xx这种方式遍历属性,通过修改web容器的日志文件的路径和文件名,写入一句话木马。细节上文章中写的很清楚了,我就不赘述了。

修复方法

针对S2-20,Struts2在isExcluded方法中过滤了class关键字。

参考文章:

https://www.freebuf.com/articles/web/31039.html

https://cwiki.apache.org/confluence/display/WW/Security+Bulletins

原文地址:https://www.cnblogs.com/jinqi520/p/10814738.html

时间: 2024-11-02 09:51:43

S2-019、S2-020的相关文章

计算机S0、S1、S2、S3、S4、S5状态

S1 Standby.即指说系统处于低电源供应状态,在 windows or BIOS 中可设定屏幕信号输出关闭.硬盘停止运转进入待机状态.电源灯处于闪烁状态.此时动一动鼠标.按键盘任一键均可叫醒电脑. S2 Power Standby.和 S1 几乎是一样的.即是把 windows 当前存在内存中的所有资料保存不动,然后进入"假关机".此时除了内存需要电源来保持资料以外,其它的设备,装置全部停止供电.也就是说,理论上可以把CPU,PCI卡,AGP Device拿掉又插回去,电脑也可能

hdu 2203 亲和串(给两个字符串s1,s2,问s2可不可能出现在以s1为循环节的串中)

1.strcat函数,strcat(char *s , char *p);注意这里的s和p所指内存区域不可以重叠且s必须有足够的空间来容纳p的字符串 2.strcpy函数,strcpy(char *s,char *p),将p拷贝到s 3.代码: #include<cstdio> #include<cstring> using namespace std; char s1[1000000],s2[100005],t[1000000]; int len1,len2; //int LCP

019、Java中定义字符

01.代码如下: package TIANPAN; /** * 此处为文档注释 * * @author 田攀 微信382477247 */ public class TestDemo { public static void main(String[] args) { char c = 'A'; // 字符 int num = c; // 字符可以和int型互相转换(以编码的形式出现) System.out.println(c); System.out.println(num); } } 02.

旌旗灯号量:整型、记载型旌旗灯号量以及应用旌旗灯号量完成过程互斥和前驱关系

旌旗灯号量机构是一种功用较强的机制,可用来处理互斥与同步的成绩,它只能被两个规范的原语wait(S)和signal(S)来拜访,也可以记为"P操作"和"V操作".原语是指完成某种功用且不被联系不被中缀履行的操作序列,平日可由硬件来完成完成不被联系履行特征的功用.如前述的"Test-and-Set"和"Swap"指令,就是由硬件完成的原子操作.原语功用的不被中缀履行特征在单处置机时可由软件经过屏障中缀办法完成.原语之所以不克不及

整型信号量与记录型信号量

信号量机构是一种功能较强的机制,可用来解决互斥与同步的问题,它只能被两个标准的原语wait(S)和signal(S)来访问,也可以记为"P操作"和"V操作". 原语是指完成某种功能且不被分割不被中断执行的操作序列,通常可由硬件来实现完成不被分割执行特性的功能.如前述的"Test-and-Set"和"Swap"指令,就是由硬件实现的原子操作.原语功能的不被中断执行特性在单处理机时可由软件通过屏蔽中断方法实现. 原语之所以不能被中

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

昨天因为要从JFrame控件获取密码,注意到一个问题,那就是用toString方法得到的不一定是你想要的,如下: jPasswordField是JFrame中的密码输入框,如果用下面的方法是得不到密码的value的: jPasswordField.getPassword().toString(); 这是因为jPasswordField.getPassword()得到的是字符数组char[],然后调用toString方法得到的是这个字符数组的hashCode,即字符数组的内存地址. 只有用下面的方

C语言--关键字 typedef

1.基本使用 1> typedef 在基本数据类型中的使用 typedef int MyInt; // 相当于给 int 起了一个别名 typedef MyInt MyInt2; // 相当于给 MyInt 起了一个别名 MyInt a = 10; // MyInt相当于 int MyInt2 b = 12; // MyInt2相当于 int 2> typedef 和指针在一起时的使用 char *name = "jack"; typedef char * string;/

高效的多维空间点索引算法 — Geohash 和 Google S2

原文地址:https://www.jianshu.com/p/7332dcb978b2 引子 每天我们晚上加班回家,可能都会用到滴滴或者共享单车.打开 app 会看到如下的界面: app 界面上会显示出自己附近一个范围内可用的出租车或者共享单车.假设地图上会显示以自己为圆心,5公里为半径,这个范围内的车.如何实现呢?最直观的想法就是去数据库里面查表,计算并查询车距离用户小于等于5公里的,筛选出来,把数据返回给客户端. 这种做法比较笨,一般也不会这么做.为什么呢?因为这种做法需要对整个表里面的每一

实现 reverse(const char *s1 , char *s2)

题目: 将字符串s1的内容反转后输出到s2,比如s1="12345678",则输出s2为"87654321",实现语言选择一种即可.  C语言实现时要求不能使用包括strlen在内的任何库函数,也不能定义和使用除s1.s2以外的其它变量.使用如下原型的C函数void reverse(const char *s1, char *s2); 总结: 如果是定义的字符串常量的话,默认类型是const类型的,也就说是如果我们把字符传直接传进去,类型是const,也就是无法修改

445. Add Two Numbers II ——while s1 or s2 or carry 题目再简单也要些测试用例

You are given two linked lists representing two non-negative numbers. The most significant digit comes first and each of their nodes contain a single digit. Add the two numbers and return it as a linked list. You may assume the two numbers do not con