第一轮断点调试,先将main函数里面的进行分析,打上断点;
main函数
将主要的函数打上断点
先粗糙的看一下main函数里面的内容,明白每一句函数对链路有什么影响;
在33行的payload没运行时只有之前传入的cmd和key参数
可以看到这行在运行后出现了payload的值,过程相对复杂,后续在第二轮的断电分析里面来详细分析这里面的内容;
但现在要明白payload本质上是一个hashmap:里面只有一对键值对key=tiedmapentry;value=value。
其中的tiedmapentry是攻击的本质,value是占位子的;
接着往后运行
byte[] serialized = serialize(payload)
这一步本质来讲就是把对象之间的网络转化成二进制的字节流,也就让我能看到-84 -19 0 5开头的有java对象字节流特点的byte。
后调试c断点;
后进行AES加密,接着转化成base64编码的文本存入rememberme里面
调试断点D;
这个也就实现了AES解迷,后base64解码,恢复成了字节流,和上文的serlialized里面的值是一样的。
调试断点E也就实现了运行这个字节流,实现了触发calc.exe;
以上是第一轮断点调试,实现了模拟本地构造+服务器响应的全过程
第二轮断点来详细解释一下payload的函数逻辑。
先进行第一个函数;
将其中输入恶意类字节码,makeEvilBytecode是一段恶意代码,
其他的内容基本是凑数的
接着往transformer里面传内容,并且生成一个空的innermap
让后用lazymap里面传入innermap和transformer的内容
后生成一个tiedmapentry的内容,里面放了lazymap和templates的内容;同时66行生成一个空的expmap
后面的67行实现了添加一个键值对进入expmap
后面的lazymap.clear只去掉一个:innerMap 里的那条缓存。tiedMapEntry和expMap都不受影响。
后面就把之前的getclass改成了newtransformer
这样在运行时就实现了服务端反序列化时lazyMap.get(templates)一落空,就会触发transform(templates),而那时方法名已经是newTransformer
先通过了L/M/N断点,然后到了ois.readobject的断点可以看得到里面的decrypted是base64的开头
后续运行到hashcode里面,第二次的this、this.map来自readObject()按字节流重建——是一批新对象,内存地址完全不同,但字段内容(map 指向 LazyMap、key 指向 TemplatesImpl)和序列化前保存的一致。
现在进入了
if (!this.map.containsKey(key)) {也是getvalue的里面的内容;后就进入这个后面的调用
算出map.containsKey(templates) = false(因为缓存被清空过);
走进factory.transform(key)→ 函数切换到InvokerTransformer.transform(Object input)(断点 N),input = TemplatesImpl;
return之后就直接运行exe了