我们可以发现 C odeQ L 污点到源点的数据流走动遵从这样一个简单的规则:从一个节点走到另外一个节点,对象类型发生变化时,如果没有相应的流中继step,则该流会断掉。
另外,当流走到我们规则定义的source点后,如果source点为Parameter类型, C odeQ L 还会将该Parameter所在方法作为节点,尝试继续找source,而如果source点不为Parameter类型,则不会继续走流。 flow step
稍微看看 C odeQ L 底层流处理库DataFlow。
针对接口情况, C odeQ L 会关联Interface与其implements,这点似乎是codeql的基础机制,可能其构建节点关系的时候就会进行这一步处理(主要笔者没有在lib代码中找到这部分的逻辑,所以暂时做此猜想),后面sink到souce的流跟踪过程会触发这种节点上的逻辑关联。
下图中,sinkMethod为污点,数据流随后跟踪到 myClass.getS() ,并发现 IMyClass 为Interface时,关联节时会关联到implements该接口的类 MyClass 的 getS 。
C odeQ L 甚至还会自动分析 getS 与 setS 两 个方法间是否存在数据关联,从而决定是否进行流跳转。
本例中, C odeQ L 实际通过 DataFlowPrivate.qll 中的 storeStep 谓词判断这一情况。该谓词的逻辑方法逻辑为,上游节点node1,即 this.s = s 中右边的赋值变量,下游节点node2,即 myClass.get() 的 myClass ,也是 this.s = s 中的 this 。
但目前这种功能无法针对lib库的class进行分析,如本例中的接口类与接口实现类在jar包中的话,我们就需要另外编写flow step。但相信我们在真正深入了解 C odeQ L 后,这一问题能够解决。
规则案例 丰富传播规则
C odeQ L 提供了一系列通用的核心基础 source、flow,但我们在使用过程中,也需要不断完善其中的不足。
下图案例中,存在污点方法 sinkMethod ,其source来源于 request ,由于 C odeQ L 关于 java.io.InputStream 、 ByteArrayOutputStream 的flow step不完善,所以在进行污点跟踪时会断掉(真实漏洞案例)。
另外,如果过滤器 isSanitizer 中不能过滤 int 类型(可能不能一股脑过滤这些数据类型):
非典型污点
起初笔者在编写AST代码审计工具时,认为污点跟踪只需要针对类的方法或构造函数的跟踪,由于该认知的局限,之后的实践使用过程中,就遇到了尴尬的问题。与之鲜明对比的是 C odeQ L 中污点的使用:数据流中的节点都可以是污点,甚至即便某个节点即便不是数据经过的节点,你也只需要阐明该节点与某个具备流跟踪逻辑的节点的关系,之后返回具备流跟踪逻辑关系的节点即可, C odeQ L 引擎将遵从代码流逻辑,从具备流跟踪逻辑的节点向上走流。
我们可以从SpringBoot Thymeleaf漏洞加深理解这一点,漏洞demo为github的veracode-research/spring-view-manipulation,codeql提供的规则案例为SpringViewManipulationLib.qll,不过不知道为什么作者少判断了PathVariable的情况,笔者给出的代码已补全了这一问题(spring默认视图设置为thymeafleaf后,当符合某些情况会存在模板注入漏洞,漏洞情形可参考污点与过滤器中的代码逻辑)。
下图中可以看到,return代码块拼接用户输入时,return代码块可直接作为一个污点;另外参数Parameter也可作为污点,也得益于SpringRequestMappingParameter源点的匹配。
看规则学Web
当研究了别人的规则之后,笔者才发现自己没有真正把java 漏洞“弄懂”,包括其中涉及到的污点类、web框架,缺少工程性系统性地研究漏洞,并转换为能力上积累。
这里举例,Spring MVC中的 @RequestMapping 用于标记一个控制器的入口方法, C odeQ L 判断一个注解是否为 @RequestMapping 时使用的是下图中的 SpringRequestMappingAnnotationType 。当笔者看到 91行 这行代码时,就有点懵:难道一个注解,被 @RequestMapping 注解后,也是有效的 @RequestMapping 注解了?Spring框架这也认?