WMCTF2023(CC链花式玩法+盲读文件)

file协议任意读文件,但下载下面的class文件无法反编译

Decompile problem

全部取反,开头改成CAFEBABE

def invert_file_bits(input_file, output_file):
    with open(input_file, 'rb') as f_in:
        with open(output_file, 'wb') as f_out:
            byte = f_in.read(1)
            while byte:
                inverted_byte = bytes([~byte[0] & 0xFF])
                f_out.write(inverted_byte)
                byte = f_in.read(1)


input_file_path = "CmdServlet.class"
output_file_path = "CmdServlet2.class"
invert_file_bits(input_file_path, output_file_path)
image-20230906002625661

反编译后修改的源码也放在仓库了👉Click Me

CC7 recap

后半段和CC6一样,都是调用 LazyMap#get -> Transformer#transform,不过source变了。

HashMap的父类为AbstractMap,当比较两个HashMap时调用的是父类AbstractMapequals

java.util.AbstractMap#equals

比较对象需要满足如下条件才和原Map相等:

  • Map类的实例(o instanceof Map

  • 大小和原Map相同(m.size() == size()

  • 遍历原Map的每个键值对Entry和比较对象一致

其中第三点会获取比较对象中key对应的valuem.get(key)),让比较对象m为LazyMap即可触发后半段链子。

LazyMapequals也是调用的父类(AbstractMapDecorator)的equals

这里的map就是LazyMap.decorate传进来的map

因此我们构造两个LazyMap,一个用于触发HashMap#equals,一个用于触发Transformer#transform

接着哪个类的readObject能调到equals

先对传入的key进行哈希,根据哈希值获取索引

接着获取table对应索引的键值对Entry,若Entry的哈希和当前key的哈希相同,才能走到e.key.equals(key)

Hashtable#reconstitutionPut由该类的readObject调用

image-20230905140826017

先将键值对进行反序列化,再传入reconstitutionPut

即我们放入Hashtable的两个LazyMap的哈希值需要相同

LazyMaphashCode方法也是来自父类,返回this.map.hashCode()

而我们传入的LazyMap的map为HashMapHashMaphashCode也来自父类

HashMap#entrySet获取到的EntryHashMap定义的内部类Node(其实现了Map.Entry<K,V>)

分别对key和value进行哈希后异或,value设置相同即可,比如都为1

问题是key不同(因为后面LazyMap.get(key)需要本身的map不含这个key),但需要其哈希相同,String的哈希可以进行碰撞

即每次的哈希值×31,加上字符的ASCII

我们知道大小写字母的ASCII差32,刚好比31多1

因此可以取两个相邻的小写字母,比如op,第二轮计算时差了31,再取oP,大小写错位刚好补上31

"oo".hashCode() == "pP".hashCode()

到此链子就打通了。

看到这或许你我都很疑惑,为什么还要绕一圈通过AbstractMapDecorator#equals去调用AbstractMap#equals

完全没有必要再多构造一个LazyMap(ysoserial中也是构造了两个LazyMap,可能是为了对称美?)

下面给出稍微简洁一点的POC

调用栈:

java.util.Hashtable.readObject java.util.Hashtable.reconstitutionPut java.util.AbstractMap.equals org.apache.commons.collections.map.LazyMap.get org.apache.commons.collections.functors.ChainedTransformer.transform org.apache.commons.collections.functors.InvokerTransformer.transform java.lang.reflect.Method.invoke sun.reflect.DelegatingMethodAccessorImpl.invoke sun.reflect.NativeMethodAccessorImpl.invoke sun.reflect.NativeMethodAccessorImpl.invoke0 java.lang.Runtime.exec

🎣Hashtable的put问题:

构造EXP时,往Hashtable put也会触发到equals

image-20230905155523017

这里需要执行到addEntry才能真正把Map放到Hashtable

所以第二个判断条件entry.key.equals(key)需要返回false

image-20230905161025036

即这里m.get(key)触发的transform的返回值和当前value不同

因此fake中的ConstantTransformer传2

Bypass SerialKiller

Ban了一堆偏偏InvokerTransformer不Ban。。。。😅

Ban了TiedMapEntry,只能用CC7的source

Ban了ChainedTransformer,如何进行链式调用呢?

回想CC1的两个版本LazyMapTransformedMap,其利用的就是对valuegetset操作时会触发transform

根据LazyMap类的描述,当获取LazyMapkey对应的值时,若key不存在,则通过factory将key转化(transform)得到对应的value,再放入map中,LazyMap之所以Lazy就是因为这种延迟的transform懒加载,不是put的时候就transform,而是get的时候才transform

When the get(Object) method is called with a key that does not exist in the map, the factory is used to create the object. The created object will be added to the map using the requested key.

反观TransformedMap,它就不Lazy了,实现了自己的put方法,先对keyvalue进行transform再放入map

注意到TransformedMap#put这里就存在递归调用put了,让当前的mapTransformedMap即可

(TransformedMap的父类和LazyMap一样,也是AbstractMapDecorator,刚好可以接上CC7)

LazyMap#get这就可以作为递归调用的入口

本题没能回显,也没过滤掉Runtime等命令执行类,若能出网直接弹shell,或者curl、wget等外带数据,亦或者打JDNI,但受JDK版本限制

不出网利用只能依靠字节码加载了,但把TemplatesImpl给Ban了。

Exploitation Without Internet Access

Blind File Read

URL类 + file协议读文件

可以通过类似盲注的方法来确定每一个字符

org.apache.commons.collections.functors.ClosureTransformer

Closure是一个接口,由该接口的描述可知该接口代表了一些基础的代码块(如循环语句、条件语句)

A Closure represents a block of code which is executed from inside some block, function or iteration. It operates an input object.

image-20230905201608817

从它的实现类中可以找到几个有用的

IfClosure用来条件判断,其中iPredicatePredicate接口类,条件谓词

image-20230905202409242

我们需要判断字符是否相等,使用EqualPredicate

当条件为真时(iTrueClosure)调用NOPClosure、条件为假时(iFalseClosure)调用ExceptionClosure

ForClosureTransformerClosure配合读取指定位置的字符

完整代码👉CCRead.java

image-20230905212258768

Js To LoadClass

虽然禁了TemplatesImpl,但只要能任意方法调用,就还有很多方法可以加载字节码,这边用JS加载比较方便

具体代码放仓库了👉Click Me

image-20230906001359586

/shell访问Filter内存马

Last updated

Was this helpful?