CodeQL之CWE-401(4)

之前我们简要的将CWE-401分为了两类:第一类为(过程内)不存在一条路径,将已分配的内存进行了释放,而在写脚本时,我们并没有解决参数传递导致的误报

过程内参数传递问题#

只在单个函数内找漏洞,需要解决以下两类参数传递问题

  1. 分配的内存通过函数参数传递回caller
  2. 分配的内存通过函数返回值返回给caller

因此,在之前的查询脚本上,我们可以加入上面两类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import cpp
import semmle.code.cpp.controlflow.Guards
import semmle.code.cpp.dataflow.DataFlow

predicate check_name_pre(Function func) { func.getName().matches("RSA_new") }

predicate check_name_post(Function func) { func.getName().matches("RSA_free") }

from DataFlow::Node source, DataFlow::Node sink, FunctionCall fc
where
exists( |
DataFlow::localFlow(source, sink) and
source.asExpr() instanceof FunctionCall and
check_name_pre(source.asExpr().(FunctionCall).getTarget()) and
check_name_post(fc.getTarget()) and
fc.getAnArgument() = sink.asExpr()
)
or
exists(ReturnStmt ret | // 第一类
DataFlow::localFlow(source, sink) and
source.asExpr() instanceof FunctionCall and
check_name_pre(source.asExpr().(FunctionCall).getTarget()) and
ret.getExpr() = sink.asExpr()
)
or
exists( Parameter p | // 第二类
DataFlow::localFlow(source, sink) and
source.asExpr() instanceof FunctionCall and
check_name_pre(source.asExpr().(FunctionCall).getTarget()) and
sink.asExpr().getAChild*() = p.getAnAccess()
)
select source.asExpr(), source.asExpr().getLocation()

由于codeql对or连接符查询的优化并不好,所以我们可以分为三个脚本,最后取合集

exp1.ql

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import cpp
import semmle.code.cpp.controlflow.Guards
import semmle.code.cpp.dataflow.DataFlow

predicate check_name_pre(Function func) { func.getName().matches("RSA_new") }

predicate check_name_post(Function func) {func.getName().matches("RSA_free")}

// situation 1
from DataFlow::Node source,DataFlow::Node sink,FunctionCall fc
where
exists( |
DataFlow::localFlow(source, sink) and
source.asExpr() instanceof FunctionCall and
check_name_pre(source.asExpr().(FunctionCall).getTarget()) and
check_name_post(fc.getTarget()) and
fc.getAnArgument()=sink.asExpr()
)
select source.asExpr(),source.asExpr().getLocation()

exp2.ql

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import cpp
import semmle.code.cpp.controlflow.Guards
import semmle.code.cpp.dataflow.DataFlow

predicate check_name_pre(Function func) { func.getName().matches("RSA_new") }

predicate check_name_post(Function func) {func.getName().matches("RSA_free")}

// situation 2
from DataFlow::Node source,DataFlow::Node sink,FunctionCall fc
where exists(ReturnStmt ret |
DataFlow::localFlow(source, sink) and
source.asExpr() instanceof FunctionCall and
check_name_pre(source.asExpr().(FunctionCall).getTarget()) and
ret.getExpr() = sink.asExpr())
select source.asExpr(),source.asExpr().getLocation()

exp3.ql

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import cpp
import semmle.code.cpp.controlflow.Guards
import semmle.code.cpp.dataflow.DataFlow

predicate check_name_pre(Function func) { func.getName().matches("RSA_new") }

predicate check_name_post(Function func) {func.getName().matches("RSA_free")}

// situation 3
from DataFlow::Node source,DataFlow::Node sink,Parameter p
where
exists( |
DataFlow::localFlow(source, sink) and
source.asExpr() instanceof FunctionCall and
check_name_pre(source.asExpr().(FunctionCall).getTarget()) and
sink.asExpr().getAChild*() = p.getAnAccess()
)
select source.asExpr(),source.asExpr().getLocation()

三类取合集后,我们最终只剩下由于CodeQL sink点分析有误的一个误报

过程间分析#

使用过程间分析,我们需要考虑全局的数据流关系(跨过程)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import cpp
import semmle.code.cpp.controlflow.Guards
import semmle.code.cpp.dataflow.DataFlow

predicate check_name_pre(Function func) { func.getName().matches("RSA_new") }

predicate check_name_post(Function func) { func.getName().matches("RSA_free") }

class MissingCloseConfig extends DataFlow::Configuration {
MissingCloseConfig() { this = "MissingCloseConfig" }

override predicate isSource(DataFlow::Node source) {
source.asExpr() instanceof FunctionCall and
check_name_pre(source.asExpr().(FunctionCall).getTarget())
}

override predicate isSink(DataFlow::Node sink) {
exists(FunctionCall fc |
check_name_post(fc.getTarget()) and
fc.getAnArgument*() = sink.asExpr()
)
}
}

from DataFlow::Node source, DataFlow::Node sink, MissingCloseConfig conf
where conf.hasFlow(source, sink)
select source.asExpr(), source.asExpr().getLocation()

结果如下:

image-20201122153251231

抛开CodeQL sink点识别失败的情况,我们仍然存在一个误报如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
static int rsa_cb(int operation, ASN1_VALUE **pval, const ASN1_ITEM *it,
void *exarg)
{
if (operation == ASN1_OP_NEW_PRE) {
*pval = (ASN1_VALUE *)RSA_new();
if (*pval != NULL)
return 2;
return 0;
} else if (operation == ASN1_OP_FREE_PRE) {
RSA_free((RSA *)*pval);
*pval = NULL;
return 2;
} else if (operation == ASN1_OP_D2I_POST) {
if (((RSA *)*pval)->version != RSA_ASN1_VERSION_MULTI) {
/* not a multi-prime key, skip */
return 1;
}
return (rsa_multip_calc_product((RSA *)*pval) == 1) ? 2 : 0;
}
return 1;
}

通过查看源代码,我们可以发现误报的原因是:openssl中,没有任何函数,对rsa_cb函数进行了调用,因此确实不存在全局的从RSA_newRSA_free的数据流。

评论