diff --git a/README.md b/README.md
index 9aefd31..76e9c34 100644
--- a/README.md
+++ b/README.md
@@ -8,5 +8,9 @@

+增加Tomcat-Value、Timer、Websocket 、Upgrade 、Executor等内存马检测。
+
+
+
## 0x03 更多
-[Filter/Servlet型内存马的扫描抓捕与查杀](https://gv7.me/articles/2020/filter-servlet-type-memshell-scan-capture-and-kill/)
\ No newline at end of file
+[Filter/Servlet型内存马的扫描抓捕与查杀](https://gv7.me/articles/2020/filter-servlet-type-memshell-scan-capture-and-kill/)
diff --git a/doc/othershell.png b/doc/othershell.png
new file mode 100644
index 0000000..95578c0
Binary files /dev/null and b/doc/othershell.png differ
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