From 5473ebb90e1198892ed5c7e20be7712cc34a5f55 Mon Sep 17 00:00:00 2001
From: ruyueattention <33751054+ruyueattention@users.noreply.github.com>
Date: Thu, 9 Mar 2023 15:06:33 +0800
Subject: [PATCH 1/3] Update tomcat-memshell-scanner.jsp
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
增加Tomcat-Value、Timer、Websocket 、Upgrade 、ExecutorShell内存马的查杀逻辑。
---
tomcat-memshell-scanner.jsp | 391 +++++++++++++++++++++++++++++++++++-
1 file changed, 381 insertions(+), 10 deletions(-)
diff --git a/tomcat-memshell-scanner.jsp b/tomcat-memshell-scanner.jsp
index 557b985..8bd0e13 100644
--- a/tomcat-memshell-scanner.jsp
+++ b/tomcat-memshell-scanner.jsp
@@ -1,14 +1,24 @@
<%@ 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.Pipeline" %>
+<%@ page import="org.apache.catalina.Valve" %>
+<%@ page import="org.apache.catalina.core.StandardContext" %>
+<%@ page import="org.apache.catalina.connector.Request" %>
+<%@ page import="java.util.*" %>
+<%@ page import="org.apache.tomcat.websocket.server.WsServerContainer" %>
+<%@ page import="javax.websocket.server.ServerEndpointConfig" %>
+<%@ page import="javax.websocket.server.ServerContainer" %>
+<%@ page import="org.apache.coyote.UpgradeProtocol" %>
+<%@ page import="org.apache.coyote.http11.AbstractHttp11Protocol" %>
+<%@ page import="org.apache.catalina.connector.Connector" %>
+<%@ page import="org.apache.tomcat.util.net.NioEndpoint" %>
+<%@ page import="org.apache.tomcat.util.threads.ThreadPoolExecutor" %>
+<%@ page import="java.util.concurrent.TimeUnit" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
@@ -28,7 +38,26 @@
Object standardContext = __context.get(appContext);
return standardContext;
}
+ private static Object getField(Object object, String fieldName) throws Exception {
+ Field field = null;
+ Class clazz = object.getClass();
+ while (clazz != Object.class) {
+ try {
+ field = clazz.getDeclaredField(fieldName);
+ break;
+ } catch (NoSuchFieldException var5) {
+ clazz = clazz.getSuperclass();
+ }
+ }
+
+ if (field == null) {
+ throw new NoSuchFieldException(fieldName);
+ } else {
+ field.setAccessible(true);
+ return field.get(object);
+ }
+ }
public HashMap getFilterConfig(HttpServletRequest request) throws Exception {
Object standardContext = getStandardContext(request);
Field _filterConfigs = standardContext.getClass().getDeclaredField("filterConfigs");
@@ -192,23 +221,143 @@
res += "]";
return res;
}
+ public Object getNioEndpoint() throws Exception {
+ Thread[] threads = (Thread[]) getField(Thread.currentThread().getThreadGroup(), "threads");
+ Field f = ThreadGroup.class.getDeclaredField("threads");
+ f.setAccessible(true);
+ for (Thread thread: threads) {
+ if (thread.getName().contains("http") && thread.getName().contains("Poller")) {
+ f = Thread.class.getDeclaredField("target");
+ f.setAccessible(true);
+ Object pollor = f.get(thread);
+ f = pollor.getClass().getDeclaredField("this$0");
+ f.setAccessible(true);
+ Object nioEndpoint = (NioEndpoint)f.get(pollor);
+ return nioEndpoint;
+ }
+ }
+ return new Object();
+ }
%>
<%
- out.write("Tomcat memshell scanner 0.1.0
");
+ out.write("Tomcat memshell scanner
");
String action = request.getParameter("action");
String filterName = request.getParameter("filterName");
String servletName = request.getParameter("servletName");
String className = request.getParameter("className");
+ String tomcatValue = request.getParameter("tomcatValue");
+ String threadName = request.getParameter("threadName");
+ String webSocket = request.getParameter("webSocket");
+ String upgrade = request.getParameter("upgrade");
+ String executors = request.getParameter("executor");
+
+ //获取ServletContext对象(得到的其实是ApplicationContextFacade对象)
+ ServletContext servletContext = request.getServletContext();
+ StandardContext standardContext = null;
+ //从 request 的 ServletContext 对象中循环判断获取 Tomcat StandardContext对象
+ while (standardContext == null) {
+ //因为是StandardContext对象是私有属性,所以需要用反射去获取
+ Field f = servletContext.getClass().getDeclaredField("context");
+ f.setAccessible(true);
+ Object object = f.get(servletContext);
+
+ if (object instanceof ServletContext) {
+ servletContext = (ServletContext) object;
+ } else if (object instanceof StandardContext) {
+ standardContext = (StandardContext) object;
+ }
+ }
+ Pipeline pipeline = standardContext.getPipeline();
+ Valve[] valves = pipeline.getValves();
+
if (action != null && action.equals("kill") && filterName != null) {
deleteFilter(request, filterName);
+ out.write("");
+
} else if (action != null && action.equals("kill") && servletName != null) {
deleteServlet(request, servletName);
- } else if (action != null && action.equals("dump") && className != null) {
+ out.write("");
+
+ }else if (action != null && action.equals("kill") && tomcatValue != null){
+ int id = Integer.valueOf(tomcatValue).intValue();
+ if(id!=0 & id!=valves.length-1 ){
+ pipeline.removeValve(valves[id]);
+ out.write("");
+
+ }
+ }else if(action!=null && action.equals("kill") && threadName!= null){
+ Thread[] threads = (Thread[]) ((Thread[]) getField(Thread.currentThread().getThreadGroup(), "threads"));
+ for (Thread thread : threads) {
+ if(threadName.equals(thread.getName())){
+ Class clzTimerThread = thread.getClass();
+ Field queueField = clzTimerThread.getDeclaredField("queue");
+ queueField.setAccessible(true);
+ //Timer里面的TaskQueue()对象
+ Object queue = queueField.get(thread);
+ Class clzTaskQueue = queue.getClass();
+ Method getTimeTask = clzTaskQueue.getDeclaredMethod("get",int.class);
+ getTimeTask.setAccessible(true);
+ //从TaskQueue对象中获取TimerTask,然后取消这个TimerTask以清除任务。
+ TimerTask timerTask = (TimerTask) getTimeTask.invoke(queue,1);
+ if(timerTask!=null){
+ timerTask.cancel();
+ out.write("");
+ break;
+
+ }
+ }
+ }
+ } else if(action!=null && action.equals("kill") && webSocket!= null){
+ WsServerContainer wsServerContainer = (WsServerContainer) servletContext.getAttribute(ServerContainer.class.getName());
+
+ // 利用反射获取 WsServerContainer 类中的私有变量 configExactMatchMap
+ Class> obj = Class.forName("org.apache.tomcat.websocket.server.WsServerContainer");
+ Field field = obj.getDeclaredField("configExactMatchMap");
+ field.setAccessible(true);
+ Map configExactMatchMap = (Map) field.get(wsServerContainer);
+
+ // 遍历configExactMatchMap, 打印所有注册的 websocket 服务
+ Set keyset = configExactMatchMap.keySet();
+
+ configExactMatchMap.remove(webSocket);
+ out.write("");
+
+
+
+ }else if(action!=null && action.equals("kill") && upgrade!= null){
+ Request request1 =(Request) getField(request, "request");;
+ Connector realConnector = (Connector) getField(request1, "connector");
+ AbstractHttp11Protocol handler = (AbstractHttp11Protocol) getField(realConnector, "protocolHandler");
+ HashMap upgradeProtocols = (HashMap) getField(handler,"httpUpgradeProtocols");
+ upgradeProtocols.remove(upgrade);
+ out.write("");
+
+
+
+ }else if(action!=null && action.equals("kill") && executors!= null){
+ NioEndpoint nioEndpoint = null;
+ try {
+ nioEndpoint = (NioEndpoint) getNioEndpoint();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ ThreadPoolExecutor executor = (ThreadPoolExecutor)nioEndpoint.getExecutor();
+
+ nioEndpoint.setExecutor(new org.apache.tomcat.util.threads.ThreadPoolExecutor(executor.getCorePoolSize(), executor.getMaximumPoolSize(),
+ executor.getKeepAliveTime(TimeUnit.MILLISECONDS), TimeUnit.MILLISECONDS, executor.getQueue(),
+ executor.getThreadFactory(), executor.getRejectedExecutionHandler()));
+ out.write("");
+
+ }else if (action != null &&
+ action.equals("dump") && className != null) {
byte[] classBytes = Repository.lookupClass(Class.forName(className)).getBytes();
response.addHeader("content-Type", "application/octet-stream");
String filename = Class.forName(className).getSimpleName() + ".class";
+ if(".class".equals(filename)){
+ filename = "tmp.class";
+ }
String agent = request.getHeader("User-Agent");
if (agent.toLowerCase().indexOf("chrome") > 0) {
response.addHeader("content-Disposition", "attachment;filename=" + new String(filename.getBytes("UTF-8"), "ISO8859-1"));
@@ -319,9 +468,6 @@
out.write("");
List