RFD漏洞
前言:这篇参考了一位公司内的师傅的文章,所以这边简单的记录下,主要学习一下分号的绕过技巧
参考文章:https://www.blackhat.com/docs/eu-14/materials/eu-14-Hafif-Reflected-File-Download-A-New-Web-Attack-Vector.pdf
参考文章:https://github.com/spring-projects/spring-framework/commit/2bd1daa75ee0b8ec33608ca6ab065ef3e1815543
参考文章:https://github.com/spring-projects/spring-framework/commit/aec3a4c69e02d87f87258b0ab5c1d6c83f4cb44f
什么是RFD漏洞
RFD,即Reflected File Download反射型文件下载漏洞,是一个2014年来自BlackHat的漏洞,攻击者可以通过一个URL地址使用户下载一个恶意文件,从而危害用户的终端PC,不过这个漏洞危害上可能接近于self-xss,因为就算浏览器下载了,打开的话也还是需要自己点击打开,并不是自动打开,就比较鸡肋吧,想要利用的难度就比较高
想要实现RFD漏洞,还需要满足如下三个条件
-
输入反射:用户输入被“反射”到响应内容,这个点的话就是说明能够控制文件的内容
-
文件名可控: URL允许并接受用户的其他输入,攻击者将其用于将文件扩展名设置为可执行扩展名,那么后缀名就可以控制,比如sh,bat脚本之类就可以控制运行
-
下载:响应被作为文件里的内容进行下载,这里可以控制Content-Type或者在自己的服务器上创建一个HTML文件,设置download属性,诱导点击下载。
CVE-2015-5211
pom.xml
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.2.2.RELEASE</version> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> <scope>test</scope> </dependency> </dependencies>
这边直接可以来看org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor.writeWithMessageConverters的部分
首先是获取返回值的类型 org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor#getReturnValueType,我这边请求的是http://127.0.0.1:8080/spring/content.sh?content=test
,所以这边拿到的就是字符串"test"
再接着就是进行内容协商机制的操作,首先是org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor#getAcceptableMediaTypes,这个操作主要就是获取当前请求的accept的类型是什么,它会根据请求对象servletRequest来进行判断
其中会调用org.springframework.web.accept.ContentNegotiationManager#resolveMediaTypes,其中有contentNegotiationStrategies中两个策略通过resolveMediaTypes来进行匹配
其中的一个strategy是org.springframework.web.accept.PathExtensionContentNegotiationStrategy#getMediaTypeKey,分别获得请求过来的文件名和文件的后缀名
String filename = WebUtils.extractFullFilenameFromUrlPath(path); String extension = StringUtils.getFilenameExtension(filename);
getProducibleMediaTypes函数根据当前的接口返回的类型returnValueClass来获取能够处理该返回类型的accpet字段
接着遍历getProducibleMediaTypes,将能够处理该returnValueClass的accept字段放入到compatibleMediaTypes
最后再通过遍历messageConverters数组来调用messageConverters的canWrite方法来查看是否能处理该compatibleMediaTypes的messageConverter
满足的messageConverter作为org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdviceChain#invoke的参数
最后再调用org.springframework.http.converter.HttpMessageConverter#write写入返回值,因为匹配到的是sh文件,最终就是以sh文件来进行输出
输出的结果如下所示
漏洞修复
参考地址:https://github.com/spring-projects/spring-framework/commit/2bd1daa75ee0b8ec33608ca6ab065ef3e1815543
把pom.xml文件修改到如下修复版本进行测试
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <version>2.1.7.RELEASE</version> </dependency> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter</artifactId> <version>5.9.2</version> <scope>test</scope> </dependency>
可以看到其中被修改的地方,在写入的时候又重新通过addContentDispositionHeader方法来校验了下后缀名,如下图所示
跟到addContentDispositionHeader中,可以发现调用了safeExtension方法
跟进safeExtension方法,首先会通过白名单判断,如果是白名单的话直接返回true
跟进safeExtension方法中会有个safeMediaType方法,最终还是会通过如下三个进行判断,如果不符合下面三个并且结尾不是+xml
的话,那么最终还是返回false
如果还是之前的利用发包的话,那么最终为false的情况下就会被修改为f.txt来进行展示,如下图所示
CVE-2020-5421
CVE-2020-5421实际上就是对CVE-2015-5211的绕过
这边还需要在application.properties配置如下两条信息
spring.mvc.pathmatch.use-suffix-pattern=true spring.mvc.contentnegotiation.favor-path-extension=true
如果不配置的话,默认请求的接口在当前版本不允许有后缀,如下图所示
主要的绕过点是在org.springframework.web.util.UrlPathHelper#getOriginatingRequestUri,还是经典的逻辑绕过配合;
来进行绕过
这里分析下org.springframework.web.util.UrlPathHelper#getOriginatingRequestUri方法,这个就是对uri进行的处理,实际上会调用decodeAndCleanUriString方法
而这里的话org.springframework.web.util.UrlPathHelper#removeSemicolonContent方法就是对分号后面的进行处理
而如果我们让getOriginatingRequestUri拿到的requestUri的值是无后缀名的话,那么最终filename就是无后缀名的,那么在safeExtension方法中直接满足空返回为true
getOriginatingRequestUri中是通过removeSemicolonContent分号处理,所以这边就可以来进行绕过
访问:http://127.0.0.1:8080/spring/;jsessionid=/content.sh?content=123456
,可以看到此时的ext后缀名就是null,那么此时safeExtension默认就是为true,则不会进入判断内
这边为什么要;jessionid呢?主要的原因是AbstractMessageConverterMethodProcessor类中初始化的时候对如下的两个属性进行了设置
导致在removeSemicolonContent调用的时候走的是removeJsessionid方法,如下图所示
漏洞修复
参考文章:https://github.com/spring-projects/spring-framework/commit/aec3a4c69e02d87f87258b0ab5c1d6c83f4cb44f
pom.xml
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <version>2.3.11.RELEASE</version> </dependency>
可以看到直接把removeSemicolonContent的功能直接给去掉了,如下图所示
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
2020-02-21 反汇编:函数指针
2020-02-21 反汇编:指针数组和数组指针
2020-02-21 反汇编:字符串的3种表达方式