前言 用一条简单反序列化链分析的例子来加深一下理解,熟练一下使用。
环境搭建 根据参考文章,反序列化链与MimeType类有关,可以用Maven下载spring-core包作为待分析项目,解压后修改options.yml里面的appClassPath配置。
配置入口点 与前一篇入口点在配置了注解的控制器方法的情况类似,反序列化的入口点主要为equals、hashCode和toString等常见且易触发的函数,所以需要先在onStart里添加入口点:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 private boolean isEntryPoint (JMethod jMethod) { return jMethod.getName().equals("equals" ) && jMethod.getParamCount() == 1 && jMethod.getParamType(0 ).getName().equals("java.lang.Object" ) && !jMethod.getModifiers().contains(Modifier.ABSTRACT); }@Override public void onStart () { List<JClass> list = solver.getHierarchy().applicationClasses().toList(); for (JClass jClass : list) { jClass.getDeclaredMethods().forEach(jMethod->{ if (isEntryPoint(jMethod)) { solver.addEntryPoint(new EntryPoint (jMethod, EmptyParamProvider.get())); } }); } }
sinks 然后需要修改sinks为Map.get方法,用来触发LazyMap.get接上反序列化,要求对象主体map为污染对象:
1 2 sinks: - { vuln: "Deserialization" , level: 4 , method: "<java.util.Map: java.lang.Object get(java.lang.Object)>" , index: base }
sources 最重要的一环,在反序列化中,污染源主要为不具有transient和final static修饰符的成员。因此,要配置反序列化污点分析的污染源,不能像方法输入参数那样直接在onNewCSMethod里配置,需要抓住对象拥有的每一个成员。
但是具体实现时又遇到了问题,Tai-e通过makeTaint方法配置污染源需要一个SourcePoint对象,前一篇将方法输入参数配置为污染源所需的ParamSourcePoint对象只需要一个输入参数的下标。而要将成员配置为污染源需要的是FieldSourcePoint,它需要一个LoadField对象,而这个对象在我们的流程中是不好自己创造的。
翻了翻代码,在SourceHandler类里面可以找到能借鉴的处理方法,在该类中有一个loadedFieldSources表用于保存每个函数对应的LoadField对象,后续创造FieldSourcePoint需要时再取出来使用:
1 2 3 4 5 6 7 8 9 10 11 12 Set<LoadField> loads = loadedFieldSources.get(method);if (!loads.isEmpty()) { Context context = csMethod.getContext(); loads.forEach(load -> { Var lhs = load.getLValue(); SourcePoint sourcePoint = new FieldSourcePoint (method, load); JField field = load.getFieldRef().resolve(); Type type = fieldSources.get(field); Obj taint = manager.makeTaint(sourcePoint, type); solver.addVarPointsTo(context, lhs, taint); }); }
loadedFieldSources表通过onNewStmt函数进行填充,这里的fieldSources表是配置里的sources,与我们无关:
1 2 3 4 5 6 7 8 9 10 11 12 13 @Override public void onNewStmt (Stmt stmt, JMethod container) { if (handleFieldSources && stmt instanceof LoadField loadField) { JField field = loadField.getFieldRef().resolveNullable(); if (fieldSources.containsKey(field)) { loadedFieldSources.put(container, loadField); } } ... }
仿照SourceHandler类,我们首先同样要在onNewStmt里填充loadedFieldSources:
1 2 3 4 5 6 @Override public void onNewStmt (Stmt stmt, JMethod container) { if (stmt instanceof LoadField loadField) { loadedFieldSources.put(container, loadField); } }
然后在onNewCSMethod方法里添加污染源:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 JMethod method = csMethod.getMethod(); Set<LoadField> loads = loadedFieldSources.get(method);if (!loads.isEmpty()) { Context context = csMethod.getContext(); loads.forEach(load -> { Var lhs = load.getLValue(); SourcePoint sourcePoint = new FieldSourcePoint (method, load); JField field = load.getFieldRef().resolve(); Set<Modifier> modifiers = field.getModifiers(); if (!modifiers.contains(Modifier.TRANSIENT) && !(modifiers.contains(Modifier.FINAL) && modifiers.contains(Modifier.STATIC))) { Obj taint = makeTaint(field.getType(), sourcePoint); solver.addVarPointsTo(context, lhs, taint); } }); }
最后检出了几十条链,简单观察一下发现是没有过滤掉没有继承Serializable接口的类的问题,所以再修改一下,加入继承判断:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 private boolean isSerializable (JClass jClass) { Collection<JClass> superClasses = new HashSet <>(); getAllSuperClasses(jClass, superClasses); return !superClasses.stream().filter(superClass -> superClass.getName().equals("java.io.Serializable" )).toList().isEmpty(); }private void getAllSuperClasses (JClass jClass, Collection<JClass> superClasses) { JClass superClass = jClass.getSuperClass(); if (superClass != null ) { superClasses.add(superClass); getAllSuperClasses(superClass, superClasses); } Collection<JClass> interfaces = jClass.getInterfaces(); superClasses.addAll(interfaces); for (JClass i: interfaces) { getAllSuperClasses(i, superClasses); } }
发现没有检测到MimeType的反序列化链,调试发现问题出在loadedFieldSources上面,里面没有parameters这个成员的信息。
仔细思考了一下,Tai-e里构造成员污染源用的是LoadField类,可能只在遇到a=b.c这种loadfield指令的时候才会构造污染源,而不是直接绑定到成员上。而onNewCSMethod又存在问题,无法捕捉到后续的MimeType函数调用,不能到时候再给要用到的成员添加污染。
因此,想搞定这个问题还需要对Tai-e有更深的理解。
参考 Spring的新入口类反序列化触发CC链