https://goodapple.top/archives/1355
ThreadLocal
原理:tomcat处理请求的过程中,中途会调用一个方法,若满足某个条件,则将该次请求的request和response放到一个地方。我们只要去这个地方拿到request对象就行。
中途会调用的方法是ApplicationFilterChain#internalDoFilter
。
要满足的条件是ApplicationDispatcher.WRAP_SAME_OBJECT == true
。
存放request和response的地方分别是ApplicationFilterChain.lastServicedRequest
和ApplicationFilterChain.lastServicedResponse
。
以上3个属性都是static final
的,要进行处理。
所以获取request的完整流程是:第一次请求,修改static final属性,使之满足tomcat存放request的条件。第二次请求,从存放request的地方取出request。
局限性:如果漏洞点在ApplicationFilterChain#internalDoFilter
之前,则无法利用,因为此时tomcat还没往那里放request,自然就获取不到。
完整代码
try { |
全局Response
获取request的链子,自己找挺难找的,只能说师傅们太强大了。
StandardService----->Connector----->Http11NioProtocol----->AbstractProtocol$ConnectoinHandler#process()------->this.global-------->RequestInfo------->Request-------->Response |
如何获取StandardService,和Tomcat的类加载机制有关。
Tomcat的类加载机制
众所周知,Tomcat使用的并不是传统的类加载机制,我们来看下面的例子
我们知道,Tomcat中的一个个Webapp就是一个个Web应用,如果WebAPP A依赖了common-collection 3.1,而WebApp B依赖了common-collection 3.2。这样在加载的时候由于全限定名相同,因此不能同时加载,所以必须对各个Webapp进行隔离,如果使用双亲委派机制,那么在加载一个类的时候会先去他的父加载器加载,这样就无法实现隔离。
Tomcat隔离的实现方式是每个WebApp用一个独有的ClassLoader实例来优先处理加载,并不会传递给父加载器。这个定制的ClassLoader就是
WebappClassLoader
。那么我们又如何将原有的父加载器和
WebappClassLoader
联系起来呢?这里Tomcat使用的机制是线程上下文类加载器Thread ContextClassLoader。Thread类中有
getContextClassLoader()
和setContextClassLoader(ClassLoader cl)
方法用来获取和设置上下文类加载器。如果没有setContextClassLoader(ClassLoader cl)方法通过设置类加载器,那么线程将继承父线程的上下文类加载器,如果在应用程序的全局范围内都没有设置的话,那么这个上下文类加载器默认就是应用程序类加载器。对于Tomcat来说ContextClassLoader被设置为WebAppClassLoader
(在一些框架中可能是继承了public abstract WebappClassLoaderBase的其他Loader)。因此WebappClassLoaderBase就是我们寻找的Thread和Tomcat 运行上下文的联系之一。
这里通过调试,我们能够看到这里的线程类加载器是继承了
WebAppClassLoader
的ParallelWebAppClassLoader
。
获取当前线程类加载器看看有什么东西。
这样就拿到了SandardService。
完整exp构造,直接用的师傅们写好的,不过我有个点不一样:
我的WebappClassLoaderBase
的resources是通过反射获取的,因为我这里的getResources方法已经deprecated了,返回结果是null。