前言

开掘Java破绽时候存在一个痛点在已知破绽点的状况下,如何找到可控点,以及确定一条具体的数据流.

前几天看到@Alvaro Muñoz 利用codeql开掘到[CVE-2020-10199]Nexus Repository Manager近程代码执行破绽.于是就对codeql产生强烈的好奇心,怼了几天codeql语法和CVE案例。抠出开掘该CVE的Ql代码

Codeql

对Codeql具体介绍能够参考:https://help.semmle.com/

应用codeql可能很明确搜寻出办法调用的数据流, QL代码编写思路相当于在一个代码数据库中利用编写的逻辑搜寻出可能的调用点。重点是确定Souce和Sink搜寻逻辑.

破绽原理简要剖析

该破绽是因为未做过滤造成表达式注入,作者确定破绽点执行函数 buildConstraintViolationWithTemplate, 其该办法中 bean.getMessage() 返回后果 message 并未应用 stripJavaEl 办法过滤造成表达式注入

最初调用 org.hibernate.validator.internal.engine.messageinterpolation#interpolate ,执行插入操作并解析歹意字符串,造成表达式注入.

HelperBean bean#message

编写Codeql最重要的是先确定数据流的Sink点即净化点字段.这里跟进 org.sonatype.nexus.validation.HelperBean#getMessage 办法.返回message值.该值经由HelperBean办法传入

Ql代码

这里间接给出代码并做简略解说:搜寻出流经 HelperBean 办法中 message 字段的数据流

isSink办法搜寻逻辑: 定义参数p,该参数p由被调用的 HelperBean 办法应用且该p参数名为message

isSouce办法搜寻逻辑:因为不分明哪里数据源最终通过该sink,所以间接应用source.asExpr()调用所有可能的表达式作为数据流.

import javaimport semmle.code.java.dataflow.FlowSourcesclass Config extends DataFlow::Configuration{    Config(){        this = "1"    }    override predicate isSource(DataFlow::Node source){        exists(source.asExpr())     }    override predicate isSink(DataFlow::Node sink){        exists(            Parameter p | p = sink.asParameter() and p.getCallable().getName() = "HelperBean" and p.getName() = "message"        )    }}from Config config ,DataFlow::PathNode source, DataFlow::PathNode sinkwhere config.hasFlowPath(source, sink)select source.getNode().getLocation(),source,sink

搜寻后果

Run Query

AbstractGroupRepositoriesApiResource#validateGroupMembers

依据搜寻后果,这里最可疑的 souce 就是 AbstractGroupRepositoriesApiResource:92 .点击该后果跟进,Codeql很敌对对净化点respositoryName进行标记。向上溯源失去 respositoryName 值最终由 request 中获取.

Ql代码

当初就是须要确定 AbstractGroupRepositoriesApiResource#validateGroupMembers() 办法由哪里调用.

isSink办法搜寻逻辑:定义参数p,该参数p由被调用的 validateGroupMembers 办法应用且该p参数名为request

isSouce办法搜寻逻辑:这里souce本来是用Codeql自带 RemoteFlowSource 获取,然而发现并没有后果。所以这里间接用souce.asExpr()搜寻全局表达式。

import javaimport semmle.code.java.dataflow.FlowSourcesclass Config extends DataFlow::Configuration{    Config(){        this = "1"    }    override predicate isSource(DataFlow::Node source){       exists(source.asExpr())       }    override predicate isSink(DataFlow::Node sink){        exists(            Parameter p | p = sink.asParameter() and p.getCallable().getName() = "validateGroupMembers" and p.getName() = "request"        )    }}// 失去可能可控的函数from Config config ,DataFlow::PathNode source, DataFlow::PathNode sinkwhere config.hasFlowPath(source, sink)select source, sink

搜寻后果

Run Query

其中 GolangGroupRepositoriesApiResource 就是该破绽调用的可控点.

结语

原文有误,还请徒弟们斧正。心愿徒弟们喜爱