Apache Struts2 文件上传绕过(S2-067)
type
status
date
slug
summary
tags
category
icon
password
AI summary
前言
在S2-066中,
FileUploadInterceptor
采用的HttpParameters.create()
创建参数,由于大小写不敏感。导致FileUploadName
与fileUploadName
属于不同的参数,在ParametersInterceptor#setParameters
调用了acceptableParametersparams.entrySet().iterator()
分别存入TreeMap
中,在TreeMap
中,键(key)的排序是大小写敏感的,所以大写数字会排在小写字母之前。
而最后使用 OGNL 表达式调用 setter 方法对变量赋值,OGNL 在查找 setter 方法时,会自动把属性名第一个字母变成大写,然后查找 setXxx() 这样的方法。
例如,如果你有一个属性名为
name
,那么 OGNL 会查找 setName()
方法。在这个过程中,不管 name
在 OGNL 表达式中是大写还是小写,都可以正确找到 setter
方法。导致最后受到严格限制的FileUploadName
会被fileUploadName
变量覆盖。在补丁中,对HttpParameters.create()
做了修改,变成了大小写不敏感了。为什么不敏感就不能利用了呢?不敏感,那么在
FileUploadInterceptor
中,恶意的fileUploadName
是一开始初始化的,最早存入org.apache.struts2.ActionContext.parameters
而在这里会被正常的FileUploadName
替代(先删除后存入)。漏洞分析
在Struts2框架中,OGNL表达式提供了一种强大的方式来访问和操作当前请求上下文中的数据。其中,
top
属性是一个重要的概念,它代表了当前OGNL上下文中的根对象。通过使用top
属性,我们可以轻松获取当前栈中的Action对象,并利用参数绑定机制动态地修改参数值。我这里的环境上传参数为
upload
,变量名为uploadFileName
。通过
https://www.cnblogs.com/Syria/p/6671273.html
可知
使用
top.uploadFileName
可以获取栈顶对象的uploadFileName
变量,并动态修改值。断点打在
FileUploadInterceptor#intercept
,查看是如何获取的FileName

这里继续跟进
multiWrapper.getFileNames


这里是从最后一个正斜杠或反斜杠的位置往后截取文件名,限制了路径穿越。
当我们构造
top.uploadFileName=../../webapps/ROOT/le1a.jsp
时,这里能够获取到一个File
,一个params

后面正常构造上传对象,把该
文件对象的参数
加入到最开始创建的ActionContext ac = invocation.getInvocationContext();
中,该ac
是 ActionContext 对象。ActionContext 是一个存储与当前请求和处理相关上下文信息的容器。此时
ac.getParameters()
包含以下四种参数。此时是HashMap所以是无序的,但其实最后还是用了TreeMap来存储acceptableParameters,所以最终还是有序的。

后面在
ParametersInterceptor#doIntercept
中获取所有参数,然后调用setParameters
赋值
继续跟进至
ParametersInterceptor#setParameters
这里使用params
暂存了一下参数
后面还是由
acceptableParameters
存储参数,可以看到top
始终都是处在最后的。当然这里上传文件的name还是需要首字母大写,因为u在t后面,所以最后在TreeMap中uplaodFileName会覆盖top.uploadFileName


这里重点关注
top.uploadFileName
的赋值情况




此时根对象为
CompoundRoot
,根对象的栈顶对象为此次访问的Upload
对象。那么如何访问到这个n[0]
呢?这里继续跟进

此时
target
还是root根对象
,继续跟进
这里经过若干次堆栈后,进入到
ASTProperty#getValueBody



这里为参数的
_children[0]
为top
的时候,即可获取栈顶对象。这里设置的
Upload
对象的uploadFileName
变量值为../../webapps/ROOT/le1a.jsp

可以看到此时已经成功修改
uploadFileName
变量。最终构造

Loading...