前言
开掘 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 java
import semmle.code.java.dataflow.FlowSources
class 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 sink
where 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 java
import semmle.code.java.dataflow.FlowSources
class 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 sink
where config.hasFlowPath(source, sink)
select source, sink
搜寻后果
Run Query
其中 GolangGroupRepositoriesApiResource 就是该破绽调用的可控点.
结语
原文有误,还请徒弟们斧正。心愿徒弟们喜爱