记一次Log4j失败的Gadget挖掘记录
内容提要
本文记录了作者尝试用CodeQL挖掘Log4j2新Gadget的失败经历。作者基于CommonsBeanutils链,将BeanComparator视为可变量,寻找替代Comparator,发现Log4j2的PropertySource.Comparator因compare方法参数类型限制而失败。随后参考ysoserial中Apache Click的ColumnComparator成功案例,验证了替换思路,并总结了Gadget构造需精确匹配每一步的要点。
延伸解读
失败原因:Comparator参数类型限制
作者尝试用Log4j2的PropertySource.Comparator替换BeanComparator,但该Comparator的compare方法参数类型被限制为PropertySource,无法接收TemplatesImpl对象,导致无法调用getProperty获取outputProperties,最终无法触发漏洞。这提醒我们,在构造Gadget时,Comparator的泛型参数必须能兼容目标对象类型,否则链路会中断。
成功案例:Apache Click的ColumnComparator
ysoserial中Apache Click的ColumnComparator提供了成功范例:其compare方法参数为Object类型,可接收任意对象,且内部通过getProperty方法获取属性值,配合setName("outputProperties")即可触发TemplatesImpl。这验证了替换Comparator的思路可行,但需确保参数类型和属性获取逻辑满足要求。
Gadget构造的精确性要求
作者总结,一个可用的Gadget链每一步都必须精确匹配,任何细节缺失都会导致失败。例如,PropertySource.Comparator的getProperty返回int而非String,无法获取outputProperties。这提示安全研究者,在挖掘新Gadget时,需仔细分析每个类的接口和类型约束,不能仅凭表面相似就盲目替换。
Q&A
Log4j2的PropertySource.Comparator为什么不能用于替换CommonsBeanutils中的BeanComparator?
因为PropertySource.Comparator的compare方法参数类型被限制为PropertySource类,而BeanComparator的compare方法参数是泛型T,需要传入TemplatesImpl对象并调用getProperty方法获取outputProperties值。但PropertySource.Comparator的getProperty方法返回int类型,无法获取outputProperties字符串,因此无法触发TemplatesImpl的漏洞。
在CommonsBeanutils gadget中,BeanComparator的哪些特性使其可以被替换?
BeanComparator实现了java.util.Comparator和Serializable接口,其compare方法参数是泛型T,可以接受任意对象,并且内部通过反射调用getProperty方法获取属性值。在gadget链中,BeanComparator作为PriorityQueue的比较器,其compare方法会被调用,但实际触发漏洞的关键是getProperty方法获取outputProperties属性,因此只要替换的Comparator满足这些条件,就可以替换。
ysoserial中Apache Click的ColumnComparator是如何成功替换BeanComparator的?
ColumnComparator实现了java.util.Comparator和Serializable接口,compare方法参数是Object类型,可以接受任意对象。在compare方法中,它调用column.getProperty(row)来获取属性值,而column的name被设置为outputProperties,因此当compare被调用时,会通过PropertyUtils获取outputProperties属性,从而触发TemplatesImpl的漏洞。
在构造CommonsBeanutils gadget时,PriorityQueue的作用是什么?
PriorityQueue是一个优先队列,它接受一个Comparator作为参数。在反序列化时,PriorityQueue会调用Comparator的compare方法进行堆排序,从而触发Comparator中的getProperty方法,进而调用TemplatesImpl的getOutputProperties方法,最终触发漏洞。PriorityQueue与TemplatesImpl组合是CommonsBeanutils链的核心。
作者在挖掘新gadget时采用了什么策略?
作者基于CommonsBeanutils链,将BeanComparator视为可变量,PriorityQueue视为永恒量,尝试寻找替代的Comparator。通过分析BeanComparator的接口和compare方法要求,使用CodeQL搜索符合条件的类,并参考ysoserial中的成功案例进行验证。
作者从这次失败的挖掘经历中总结出了什么经验?
作者总结出,一个完美的gadget chain每一步都必不可少,需要精确匹配每一步的细节。挖掘全新gadget chain难度很大,但通过局部替换可变量可以降低难度。这次失败也让作者对每个gadget chain有了更多敬意。