Oracle WebLogic Deserialization (CVE-2020-2883) (2)

CVE

라온화이트햇 핵심연구팀 김동민

소개

Oracle WebLogic Deserialization (CVE-2020-2883)

지난 게시글(CVE-2020-2555)에 이어 해당 취약점 패치 이후 재발견된 취약점이며, ZDI 버그 리포트를 번역한 내용입니다. 또한, 이전 게시글의 확장 버전으로 많은 내용이 생략되었기 때문에 아래 링크를 통해 먼저 확인해 주시면 감사하겠습니다.

Oracle WebLogic Deserialization CVE-2020-2555_1

Patch Bypass

BadAttributeValueExpException.readObject()
  com.tangosol.util.filter.LimitFilter.toString() //<--- CVE-2020-2555 patched here
    com.tangosol.util.extractor.ChainedExtractor.extract()
        com.tangosol.util.extractor.ReflectionExtractor().extract()
            Method.invoke()
            //...
            com.tangosol.util.extractor.ReflectionExtractor().extract()
            Method.invoke()
                Runtime.exec()
public int compare(T o1, T o2) {
    Comparable a1 = (o1 instanceof InvocableMap.Entry) ? (Comparable)((InvocableMap.Entry)o1).extract(this.m_extractor) 
                                                                                : (Comparable)this.m_extractor.extract(o1);

    
    Comparable a2 = (o2 instanceof InvocableMap.Entry) ? (Comparable)((InvocableMap.Entry)o2).extract(this.m_extractor)
                                                                                 : (Comparable)this.m_extractor.extract(o2);
    
    if (a1 == null)
    {
      return (a2 == null) ? 0 : -1;
    }
    
    if (a2 == null)
    {
      return 1;
    }
    
    return a1.compareTo(a2);
  }
public int compare(Object o1, Object o2) { return SafeComparator.compareSafe(null, extract(o1), extract(o2)); }
public abstract class AbstractCompositeExtractor<T, E>
  extends AbstractExtractor<T, E>
  [...Truncated...]
  public class MultiExtractor
  extends AbstractCompositeExtractor
  [...Truncated...]

    public Object extract(Object oTarget) {
    if (oTarget == null)
    {
      return null;
    }
    
    ValueExtractor[] aExtractor = getExtractors();
    int cExtractors = aExtractor.length;
    Object[] aValue = new Object[cExtractors];
    
    for (int i = 0; i < cExtractors; i++)
    {
      aValue[i] = aExtractor[i].extract(oTarget);<-----------------------
    }
    
    return new ImmutableArrayList(aValue);
  }

The Full Gadget Chain

java.util.PriorityQueue.readObject()
  java.util.PriorityQueue.heapify()
  java.util.PriorityQueue.siftDown()
  java.util.PriorityQueue.siftDownUsingComparator()
  private void siftUpUsingComparator(int paramInt, E paramE) {
    while (paramInt > 0) {
      int i = paramInt - 1 >>> 1;
      Object object = this.queue[i];
      if (this.comparator.compare(paramE, object) >= 0)<----------------
        break; 
      this.queue[paramInt] = object;
      paramInt = i;
    } 
    this.queue[paramInt] = paramE;
  }
javax.management.BadAttributeValueExpException.readObject()  
  com.tangosol.internal.sleepycat.persist.evolve.Mutations.toString()
    java.util.concurrent.ConcurrentSkipListMap$SubMap.size()
    java.util.concurrent.ConcurrentSkipListMap$SubMap.isBeforeEnd()
      java.util.concurrent.ConcurrentSkipListMap.cpr()
ConcurrentSkipListMap$SubMap.class
    public int size() {
        Comparator<? super K> cmp = m.comparator;
        long count = 0;
        for (ConcurrentSkipListMap.Node<K,V> n = loNode(cmp);
            isBeforeEnd(n, cmp); <---------------------------------------------------
            n = n.next) {
            if (n.getValidValue() != null)
                    ++count;
            }
            return count >= Integer.MAX_VALUE ? Integer.MAX_VALUE : (int)count;
        }
  [...Truncated...]
    
  boolean isBeforeEnd(ConcurrentSkipListMap.Node<K,V> n, Comparator<? super K> cmp) {
        ....
        int c = cpr(cmp, k, hi);<------------------------------------------------------
        if (c > 0 || (c == 0 && !hiInclusive))
                return false;
            return true;
        } 
  [...Truncated...]
  
  static final int cpr(Comparator c, Object x, Object y) {
        return (c != null) ? c.compare(x, y) : ((Comparable)x).compareTo(y); <--------
    }

Demonstrating the Gadget Chains

javax.management.BadAttributeValueExpException.readObject()  
  com.tangosol.internal.sleepycat.persist.evolve.Mutations.toString()
    java.util.concurrent.ConcurrentSkipListMap$SubMap.size()
    java.util.concurrent.ConcurrentSkipListMap$SubMap.isBeforeEnd()
      java.util.concurrent.ConcurrentSkipListMap.cpr()
        com.tangosol.util.comparator.ExtractorComparator.compare()
java.util.PriorityQueue.readObject()
  java.util.PriorityQueue.heapify()
  java.util.PriorityQueue.siftDown()
  java.util.PriorityQueue.siftDownUsingComparator()
  com.tangosol.util.extractor.AbstractExtractor.compare()
    com.tangosol.util.extractor.MultiExtractor.extract()
      com.tangosol.util.extractor.ChainedExtractor.extract()
        //...
        Method.invoke()
            //...
          Runtime.exec()

Conclusion

Reference