一、问题描述

大家在使用CodeQL复现历史漏洞时,可能经常会碰到查询出来的Path和实际执行的不一致的问题,例如:

在复现Log4j2 RCE漏洞时,我们debug实际执行的路径是这样的:

image-20240108155725393

但是在CodeQL查询出这样一条Path

image-20240108155836737

实际执行时调用的是DefaultReliabilityStrategy类的log方法,但是查询结果中却是AwaitCompletionReliabilityStrategy类的

二、问题分析

通过观察源码

image-20240108160258349

image-20240108160324163

我们发现这两个类都实现了LocationAwareReliabilityStrategy接口

所以在分析以下代码时:

1
((LocationAwareReliabilityStrategy) strategy).log(this, getName(), fqcn, location, marker, level, message, throwable)

CodeQL不能确定此处实际使用的是哪个实现了LocationAwareReliabilityStrategy接口的实现类,所以数据流会传播到LocationAwareReliabilityStrategy接口的每一个实现类,这样会造成一个时间的问题。CodeQL对此做了一些优化,通过降低精度来提升了速度。

详细的原因在大佬在这篇文章中说明:CodeQL能找到log4shell(CVE-2021-44228)漏洞吗? - 跳跳糖 (tttang.com)

但是大佬没说怎么解决

三、问题解决

经过探究,我认为既然已经知道实际执行不到这些类,我们可以写一个清洗器(Sanitizer),将实际执行不到的清洗掉。

例如,针对以上问题,我们可以写下面这样的清洗器:

1
2
3
4
5
6
7
override predicate isSanitizer(DataFlow::Node node) { 
exists(Callable ca|
//当节点为ca的任意参数时,中断数据流
node.asParameter() = ca.getAParameter() and
//限定ca为AwaitCompletionReliabilityStrategy类的log方法
ca.getDeclaringType().getASubtype*().hasQualifiedName("org.apache.logging.log4j.core.config", "AwaitCompletionReliabilityStrategy") and
ca.hasName("log") )

这样,我们就能清洗掉实际执行不到的类

image-20240108161203168

另外,在查找解决问题的资料时发现一个CodeQL团队的回答,也对我们有些帮助

https://github.com/github/codeql/issues/7449

image-20240108161449681