diff --git a/README.md b/README.md index 9aefd31..0d4a74c 100644 --- a/README.md +++ b/README.md @@ -2,11 +2,17 @@ 通过jsp脚本扫描并查杀各类中间件内存马,比Java agent要温和一些。 ## 0x02 截图 + ![Tomcat内存马扫描结果展示](doc/tomcat-memshell-scanner.png) 增加Listener型内存马检测,如果不存在Listener则不显示该项 ![Tomcat内存马扫描结果展示](doc/listener.png) +增加Valve型内存马显示 + +![Tomcat内存马扫描结果展示](doc/valve.png) + ## 0x03 更多 + [Filter/Servlet型内存马的扫描抓捕与查杀](https://gv7.me/articles/2020/filter-servlet-type-memshell-scan-capture-and-kill/) \ No newline at end of file diff --git a/doc/valve.png b/doc/valve.png new file mode 100644 index 0000000..63a930f Binary files /dev/null and b/doc/valve.png differ diff --git a/tomcat-memshell-scanner.jsp b/tomcat-memshell-scanner.jsp index 557b985..74b6235 100644 --- a/tomcat-memshell-scanner.jsp +++ b/tomcat-memshell-scanner.jsp @@ -1,14 +1,14 @@ <%@ page import="java.net.URL" %> <%@ page import="java.lang.reflect.Field" %> -<%@ page import="java.util.HashMap" %> <%@ page import="com.sun.org.apache.bcel.internal.Repository" %> <%@ page import="java.net.URLEncoder" %> -<%@ page import="java.util.Map" %> <%@ page import="org.apache.catalina.core.StandardWrapper" %> <%@ page import="java.lang.reflect.Method" %> -<%@ page import="java.util.ArrayList" %> -<%@ page import="java.util.List" %> <%@ page import="java.util.concurrent.CopyOnWriteArrayList" %> +<%@ page import="org.apache.catalina.Valve" %> +<%@ page import="java.util.*" %> +<%@ page import="org.apache.catalina.core.StandardContext" %> +<%@ page import="org.apache.catalina.connector.Request" %> <%@ page contentType="text/html;charset=UTF-8" language="java" %> @@ -18,6 +18,13 @@
<%! + public Object getRequest(HttpServletRequest request) throws NoSuchFieldException, IllegalAccessException { + Field _request = request.getClass().getDeclaredField("request"); + _request.setAccessible(true); + Object __request = _request.get(request); + return __request; + } + public Object getStandardContext(HttpServletRequest request) throws NoSuchFieldException, IllegalAccessException { Object context = request.getSession().getServletContext(); Field _context = context.getClass().getDeclaredField("context"); @@ -130,6 +137,58 @@ } } + public synchronized void deleteValve(HttpServletRequest request, String containerName, String valveName) + throws Exception { + StandardContext standardContext = (StandardContext) getStandardContext(request); + Request _request = (Request) getRequest(request); + Map> valveMap = getValveMaps(request); + + Object pipeline; +// Object container; + // 使用if兼容tomcat7 + if (containerName.equals("Engine")) { + pipeline = standardContext.getParent().getParent().getPipeline(); + } else if (containerName.equals("Host")) { + pipeline = standardContext.getParent().getPipeline(); + } else if (containerName.equals("Context")) { + pipeline = standardContext.getPipeline(); + } else if (containerName.equals("Wrapper")) { +// container = (_request.getClass().getDeclaredMethod("getWrapper").invoke(_request)); + pipeline = _request.getWrapper().getPipeline(); + } else { + return; + } + + for (Valve valve : valveMap.get(containerName)) { + if (valve.getClass().getName().equals(valveName)) { + pipeline.getClass().getDeclaredMethod("removeValve", Valve.class).invoke(pipeline, valve); + break; + } + } + } + + public synchronized void deleteListener(HttpServletRequest request, String listenerName) throws Exception { + Object standardContext = getStandardContext(request); + List listeners = getListenerList(request); + List newListeners = new ArrayList(); + for (Object listener : listeners) { + if (listener.getClass().getName().equals(listenerName)) { + continue; + } + + newListeners.add(listener); + } + + try { + Method setListenerList = standardContext.getClass().getMethod("setApplicationEventListeners", + Object[].class); + setListenerList.invoke(standardContext, new Object[]{newListeners.toArray()}); + } catch (Exception e) { + e.printStackTrace(); + } + + } + public synchronized HashMap getChildren(HttpServletRequest request) throws Exception { Object standardContext = getStandardContext(request); Field _children = standardContext.getClass().getSuperclass().getDeclaredField("children"); @@ -147,11 +206,39 @@ return servletMappings; } + public synchronized Map> getValveMaps(HttpServletRequest request) throws Exception { + Map> valveMap = new HashMap>(); + + StandardContext standardContext = (StandardContext) getStandardContext(request); + Request req = (Request) getRequest(request); + + // StandardEngine + valveMap.put("Engine", Arrays.asList(standardContext.getParent().getParent().getPipeline().getValves())); + // StandardHost + valveMap.put("Host", Arrays.asList(standardContext.getParent().getPipeline().getValves())); + // StandardContext + valveMap.put("Context", Arrays.asList(standardContext.getPipeline().getValves())); + // StandardWrapper + valveMap.put("Wrapper", Arrays.asList((req.getWrapper()).getPipeline().getValves())); + + return valveMap; + } + public synchronized List getListenerList(HttpServletRequest request) throws Exception { - Object standardContext = getStandardContext(request); - Field _listenersList = standardContext.getClass().getDeclaredField("applicationEventListenersList"); - _listenersList.setAccessible(true); - List listenerList = (CopyOnWriteArrayList) _listenersList.get(standardContext); + List listenerList = new ArrayList(); + + try { // tomcat 8、9 + Object standardContext = getStandardContext(request); + Field _listenersList = standardContext.getClass().getDeclaredField("applicationEventListenersList"); + _listenersList.setAccessible(true); + listenerList.addAll((CopyOnWriteArrayList) _listenersList.get(standardContext)); + } catch (Exception e) { // tomcat 7 + Object standardContext = getStandardContext(request); + Field _listenersList = standardContext.getClass().getDeclaredField("applicationEventListenersObjects"); + _listenersList.setAccessible(true); + listenerList.addAll(Arrays.asList((Object[]) _listenersList.get(standardContext))); + } + return listenerList; } @@ -199,11 +286,19 @@ String action = request.getParameter("action"); String filterName = request.getParameter("filterName"); String servletName = request.getParameter("servletName"); + String valveName = request.getParameter("valveName"); + String listenerName = request.getParameter("listenerName"); String className = request.getParameter("className"); + String containerName = request.getParameter("containerName"); + if (action != null && action.equals("kill") && filterName != null) { deleteFilter(request, filterName); } else if (action != null && action.equals("kill") && servletName != null) { deleteServlet(request, servletName); + } else if (action != null && action.equals("kill") && containerName != null && valveName != null) { + deleteValve(request, containerName, valveName); + } else if (action != null && action.equals("kill") && listenerName != null) { + deleteListener(request, listenerName); } else if (action != null && action.equals("dump") && className != null) { byte[] classBytes = Repository.lookupClass(Class.forName(className)).getBytes(); response.addHeader("content-Type", "application/octet-stream"); @@ -318,12 +413,49 @@ } out.write(""); - List listeners = getListenerList(request); - if (listeners == null || listeners.size() == 0) { - return; + // Scan Valve + out.write("

Valve scan result

"); + out.write("\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " "); + + Map> valveMap = getValveMaps(request); + + int valveId = 0; + String key = ""; + for (Map.Entry> valvemap : valveMap.entrySet()) { + key = valvemap.getKey(); + for (Valve valve : valvemap.getValue()) { + out.write(""); + out.write(String.format("" + , valveId + 1 + , key + , valve.getClass().getName() + , valve.getClass().getClassLoader() + , classFileIsExists(valve.getClass()) + , valve.getClass().getName() + , key + , valve.getClass().getName())); + out.write(""); + valveId++; + } } + out.write("
IDContainerValve classValve classLoaderValve class file pathdump classkill
%d%s%s%s%sdumpkill
"); + + List listeners = getListenerList(request); +// if (listeners == null || listeners.size() == 0) { +// return; +// } out.write(""); - List newListeners = new ArrayList<>(); + List newListeners = new ArrayList(); for (Object o : listeners) { if (o instanceof ServletRequestListener) { newListeners.add((ServletRequestListener) o); @@ -346,7 +478,7 @@ int index = 0; for (ServletRequestListener listener : newListeners) { out.write(""); - out.write(String.format("%d%s%s%sdumpkill" + out.write(String.format("%d%s%s%sdumpkill" , index + 1 , listener.getClass().getName() , listener.getClass().getClassLoader()