resin unicode解码特性和缓存特性以及对缓存投毒的思考利用

前言:resin unicode解码特性和缓存jsp特性,resin的解码功能比起tomcat的解码功能,resin除了正常的支持路径的url解码,还支持unicode的url解码方式

resin的unicode解码特性

稍微提及下,除了resin支持unicode解码的特性外,jetty中间件也同样支持

在com.caucho.server.http.HttpRequest#handleRequest中一直往下跟,会发现com.caucho.server.http.AbstractHttpRequest#buildInvocation中存在splitQueryAndUnescape方法的调用

该splitQueryAndUnescape方法会对请求的uri中的参数和url进行分割和解码操作

进入到splitQueryAndUnescape方法中,首先这一段是对是否有参数传递进行检测,如果存在?问号的话那么存在参数传递

再接着就是通过normalizeUriEscape方法来对请求路径uri来进行解码

跟进com.caucho.server.dispatch.InvocationDecoder#scanUriEscape方法中可以发现会对百分号%来进行检测,resin会认为如果请求的uri路径中存在百分号那么则还需要进行解码操作

如果路径中存在%百分号符号的话,并且接下来的字符是以u开头的,那么就会以unicode的url来进行解码展示

如果存在%百分号符号的话,但是字符并不是以u开头的话,那么默认就是通过url解码来进行展示

请求测试

resin的缓存特性

看resin的源码会发现resin自带缓存的效果,可以观察com.caucho.server.http.HttpRequest#handleRequest,该handleRequest方法是http请求进行处理的方法,在该方法中存在一个方法为com.caucho.server.http.AbstractHttpRequest#getInvocation

com.caucho.server.http.AbstractHttpRequest#getInvocation方法的作用是对用户的请求作出对应的调用处理的一个函数,如果该某个路径的请求是第一次的话,那么会对该调用过程Invocation进行缓存到_invocationCache,如果不是第一次的话那么会从缓存_invocationCache中取出对应的Invocation来进行处理

这里测试访问一个test.jsp,代码如下所示

<html>
<body>
getContextPath: <% request.getContextPath(); %> <br/>
getRequestURL: <% request.getRequestURL(); %> <br/>
getRequestURI: <% request.getRequestURI(); %> <br/>
getServletPath:<% request.getServletPath(); %> <br/>
getPathInfo: <% request.getPathInfo(); %>
</body>
</html>

访问: http://localhost:8080/t1/test.jsp ,断点如下所示

因为是第一次请求,所以从缓存_invocationCache中取出的invocation为空

因为是第一次请求这边的话就需要首次进行构造invocation

com.caucho.server.dispatch.InvocationServer#buildInvocation(java.lang.Object, com.caucho.server.dispatch.Invocation)中会将该invocation存储到_invocationCache对象中下次进行使用

第二次进行请求这边的话就能获取到第一次存储到_invocationCache中

这个时候把target目录下的test.jsp文件进行删除,然后再次访问test.jsp,可以发现还是存在缓存可以进行访问

这里主要还是可以看如果把test.jsp文件删除了之后的逻辑是怎么走的,首先还是从_invocationCache获取到第一次的缓存的结果,如下图所示

缓存特性的思考点

我这边发现了一个比较有意思的东西,这里可以看下缓存是如何进行缓存的,这里直接观察_invocationCache

如果我请求的是userinfo.jspuserinfo.%6a%73%70,那么这两个在_invocationCache中存储的是同一个东西呢?

通过调试发现这两个在_invocationCache是存储的两个字段,如下图所示

基于缓存的特性,我思考了下是否可以进行缓存投毒攻击呢?

首先如果请求的路径是/userinfo.jsp的话(并且userinfo.jsp中存在个人的敏感信息),跟上面说的流程一样第一次还是会进行查找生成调用对象进行缓存然后第二次的时候直接取出来,那么我可以给一个/userinfo.%6a%73%70的链接让别人点,然后我再访问/userinfo.%6a%73%70地址来进行获取信息,但是最后发现是不可行的,这个缓存的实际上是运行的流程代码而并不是处理完的数据,并且如果是这样的话那么我直接可以访问/userinfo.jsp同样可以进行窃取,所以这个想法比较蠢但是也是简单的记录下

存在反向代理的情况下

想了下如果存在反向代理的情况下会如何作用,对于nginx,apache和iis的话/foo;name=orange/bar/这种路径是允许存在的,如下图所示

如果此时nginx处理的是/userinfo;test.png,nginx设置了对于图片文件的缓存,那么是否可能存在投毒攻击?

配置一个nginx,监听端口为7878,配置文件如下所示

其中8080端口为后端处理服务器,这里搭建的是resin

        location / {
            index  index.html index.htm;
            proxy_pass http://127.0.0.1:8080;
        }

        location ~ .*\.(gif|jpg|jpeg|png|bmp|swf|js|css)$ {
            index  index.php index.html index.htm;
            proxy_cache cache_one;
            proxy_cache_valid  200 304 12h;
            proxy_cache_key $host$uri$is_args$args;
            proxy_set_header Host  $host;
            proxy_set_header X-Forwarded-For  $remote_addr;
            proxy_pass http://127.0.0.1:8080;
            expires   1d;
        }

com.zpchcbd.servlet.HelloServlet.java,模拟接口/test访问,返回当前session的id

public class HelloServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
        res.getWriter().println(req.getSession().getId());
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
        doGet(req, res);
    }
}

默认缓存目录为空,如下图所示

请求路径/t1/test;webshell.jpg,结果如下图所示,当前访问session是aaald6jGQiONl999w8Pzy

查看cache目录可以看到生成了一个32803aed28b567380b87da05e0736f92文件

此时如果拿空session进行访问/t1/test;webshell.jpg,那么访问到的就是该缓存文件,如下图所示,可以看到拿到的结果就是32803aed28b567380b87da05e0736f92文件中的结果

除了图片的缓存外,还可能也有对静态资源的缓存访问,所以这边在实战中测试的时候不仅可以测试下图片的后缀,对于静态文件资源的后缀也可以多进行测试

本地测试apache nginx iis都存在这种特性,这边进行记录下,由于没有实战案例之后如果遇到类似缓存利用之后会放到这里进行记录

总结下resin的运行流程

Resin的大致的执行流程

	-> com.caucho.server.http.AbstractHttpRequest#buildInvocation
		-> com.caucho.server.dispatch.InvocationDecoder#splitQueryAndUnescape
	-> com.caucho.server.dispatch.InvocationServer#buildInvocation(java.lang.Object, com.caucho.server.dispatch.Invocation) 其中有对invocation的缓存键值对进行获取
	-> com.caucho.server.cluster.ServletService#buildInvocation 
	-> com.caucho.server.host.HostContainer#buildInvocation  HostContainer部分
	-> com.caucho.server.host.Host#buildInvocation 
	-> com.caucho.server.webapp.WebAppContainer#buildInvocation
		-> com.caucho.server.webapp.WebAppContainer#getWebAppController
		-> com.caucho.server.webapp.WebAppContainer#findEntryByURI
			-> 其中有对_uriToAppCache的缓存获取
		-> com.caucho.server.dispatch.InvocationDecoder#normalizeUri(java.lang.String) 路径的归一化
		-> com.caucho.server.webapp.WebAppContainer#findByURIImpl
			-> com.caucho.server.dispatch.ServletInvocation#stripPathParameters
	-> com.caucho.server.webapp.WebApp#buildInvocation(com.caucho.server.dispatch.Invocation, boolean)
		-> 其中有对_filterChainCache的缓存获取
		-> com.caucho.server.dispatch.ServletMapper#mapServlet 对请求的文件后缀匹配对应的解析器来处理,servletmapping 或者 resin-jsp 或者 resin-file
			-> com.caucho.server.dispatch.ServletInvocation#stripPathParameters
			-> com.caucho.server.webapp.ServletContextImpl#getResourceAsStream 如果不是接口的访问类型那么会通过该方法来进行读取文件流
				-> com.caucho.server.webapp.ServletContextImpl#getRealPath
					-> com.caucho.server.dispatch.InvocationDecoder#normalizeUri(java.lang.String) 路径的归一化
				-> com.caucho.vfs.Path#lookupNative(java.lang.String)
		-> com.caucho.server.webapp.WebApp#buildSecurity(javax.servlet.FilterChain, com.caucho.server.dispatch.Invocation) 检测非法操作访问web-inf目录
posted @ 2023-02-15 14:57  zpchcbd  阅读(390)  评论(0)    收藏  举报