原创 | GitHub Java CodeQL CTF

admin 2022年4月22日23:58:29评论27 views字数 12533阅读41分46秒阅读模式

点击蓝字




关注我们



前言



Part 1

原创 | GitHub Java CodeQL CTF

GitHub Security Lab CTF 4: CodeQL and Chill - The Java Edition

CVE-2020-9297

https://securitylab.github.com/advisories/GHSL-2020-028-netflix-titus/

CTF 地址https://securitylab.github.com/ctf/codeql-and-chill/

CodeQL database 下载地址https://drive.google.com/open?id=10ju0t2QZjsKI8qrAqwzsPA3K-lBgqPVF


简介



Part 2

原创 | GitHub Java CodeQL CTF

题目介绍:用户控制的数据被传入支持Java EL表达式的Bean Validation库函数ConstraintValidatorContext.buildConstraintViolationWithTemplate,但问题是如何获取RCE。因此,远程代码执行。这似乎问题已经结束,但事实并非如此。想要RCE并不像仅仅传递一个EL表达式那么简单。一些问题,如用户输入的小写字母,使我们无法获得利用。解释如何使用CodeQL找到流向目标函数的特定用户控制数据的,了解远程代码成功执行有哪些要求。

官方给的漏洞成因代码:

@Overridepublic boolean isValid(Container container, ConstraintValidatorContext context) {    if (container == null) {        return true;    }    Set<String> common = new HashSet<>(container.getSoftConstraints().keySet());    common.retainAll(container.getHardConstraints().keySet());    if (common.isEmpty()) {        return true;    }    context.buildConstraintViolationWithTemplate(            "Soft and hard constraints not unique. Shared constraints: " + common    ).addConstraintViolation().disableDefaultConstraintViolation();    return false;}

大致逻辑如下图,关键点在buildConstraintViolationWithTemplate方法调用上。

原创 | GitHub Java CodeQL CTF

数据流和污点跟踪分析



Part 3

原创 | GitHub Java CodeQL CTF

1.1 Source

找到漏洞的一个重要部分是找到所有用户控制的数据从哪里来。在挑战页面本身就有提示--函数isValid的第一个参数。

因此,isSource谓词:

/** *@name Source *@derscription  */import javaimport semmle.code.java.dataflow.DataFlow
/*Map overrides of isValid method from ConstraintValidator*/class ConstraintValidator extends RefType {ConstraintValidator() {this.hasQualifiedName("javax.validation", "ConstraintValidator") }}
class ConstraintValidatorIsValid extends Method {ConstraintValidatorIsValid() {this.getName() = "isValid" andthis.getDeclaringType().getASourceSupertype() instanceof ConstraintValidator }}
predicate isSource(DataFlow::Node source) {exists(ConstraintValidatorIsValid isValidMethod |source.asParameter() = isValidMethod.getParameter(0))}
//There has to be a query in scope even when you use Quick Evaluationselect "Quick-eval isSource"

第一个container是符合要求,但要求只有6个结果也就是说有两个不符合要求。

原创 | GitHub Java CodeQL CTF

根据提示:Map overrides of isValid method from ConstraintValidator,所以排除不是重写的方法即可。

原创 | GitHub Java CodeQL CTF

只需要在加一行and this.isOverridable()判断即可,或者注释掉第四、五行再去掉第六行注释也可以。getASourceOverriddenMethod首先会判断是否是重写的方法。

class ConstraintValidatorIsValid extends Method {ConstraintValidatorIsValid() {this.getName() = "isValid" andthis.getDeclaringType().getASourceSupertype() instanceof ConstraintValidator        and this.isOverridable()        // this.getASourceOverriddenMethod().getDeclaringType() instanceof ConstraintValidator    }}

1.2 Sink

根据提示SinkConstraintValidatorContext.buildConstraintViolationWithTemplate(...)调用的第一个参数,要求5个结果。

/** *@name Sink *@derscription */import javaimport semmle.code.java.dataflow.DataFlow
class TypeConstraintValidatorContext extends RefType { TypeConstraintValidatorContext() { this.hasQualifiedName("javax.validation", "ConstraintValidatorContext") }}

predicate isBuildConstraintViolationWithTemplate(MethodAccess ma){ ma.getMethod().hasName("buildConstraintViolationWithTemplate") and ma.getMethod().getDeclaringType() instanceof TypeConstraintValidatorContext}
predicate isSink(DataFlow::Node sink){ exists(MethodAccess ma| isBuildConstraintViolationWithTemplate(ma) and ma.getArgument(0) = sink.asExpr() )}
select "Quick-eval isSink"


原创 | GitHub Java CodeQL CTF


1.3 TaintTracking configuration

题目给了TaintTracking configuration模板,如果前面isSourceisSink谓词都是正确的话,可以得到正确的路径。

/** @kind path-problem */import javaimport semmle.code.java.dataflow.TaintTrackingimport DataFlow::PathGraph
class MyTaintTrackingConfig extends TaintTracking::Configuration { MyTaintTrackingConfig() { this = "MyTaintTrackingConfig" }
override predicate isSource(DataFlow::Node source) { // TODO }
override predicate isSink(DataFlow::Node sink) { // TODO }}
from MyTaintTrackingConfig cfg, DataFlow::PathNode source, DataFlow::PathNode sinkwhere cfg.hasFlowPath(source, sink)select sink, source, sink, "Custom constraint error message contains unsanitized user data"

没有任何结果,和出题人意料一样。

原创 | GitHub Java CodeQL CTF


1.4 Partial Flow to the rescue

没有任何结果查询刚学的CodeQL新手小白估计此时都会束手无策了,我们确定了sourcesink,这表明我们的分析在从源到汇的路径上缺少一个步骤。,但CodeQL也考虑到了这种情况,故内置Partial Data Flow来debug。Partial Data Flow允许寻找从一个给定的source到任何可能的sink的流动,让sink不受限制,同时限制从sourcesink的搜索步骤的数量。因此,可以使用这个来跟踪污点数据从源头到所有可能的汇的流动,并查看流动在哪里停止被进一步track

About:

参考:Predicate hasPartialFlow

中文对照翻译:

如果存在从sourcenode的部分数据流路径,则保留。node和最近的源之间的近似距离是dist,并被限制为小于或等于explorationLimit()。这个谓词完全不考虑sink的定义。

该谓词用于数据流探索和调试,如果源的数量太大,和/或探索限制设置得太高而没有使用障碍,则可能表现不佳。
这个谓词默认是禁用的(没有结果)。用一个合适的数字覆盖
explorationLimit()来启用这个谓词。
在 "路径问题 "查询中使用,请导入 "PartialPathGraph "模块。

Partial Flow 模板

/** @kind path-problem */import javaimport semmle.code.java.dataflow.TaintTrackingimport DataFlow::PartialPathGraph // this is different!
class MyTaintTrackingConfig extends TaintTracking::Configuration { MyTaintTrackingConfig() { ... } // same as before override predicate isSource(DataFlow::Node source) { ... } // same as before override predicate isSink(DataFlow::Node sink) { ... } // same as before override int explorationLimit() { result = 10} // this is different!}from MyTaintTrackingConfig cfg, DataFlow::PartialPathNode source, DataFlow::PartialPathNode sinkwhere cfg.hasPartialFlow(source, sink, _) and source.getNode() = ... // TODO restrict to the one source we are interested in, for ease of debuggingselect sink, source, sink, "Partial flow from unsanitized user data"
predicate partial_flow(PartialPathNode n, Node src, int dist) { exists(MyTaintTrackingConfig conf, PartialPathNode source | conf.hasPartialFlow(source, n, dist) and src = source.getNode() and source = // TODO - restrict to THE source we are interested in )}

在76行和83行都是source.getNode().asParameter().getName() = "container",在题目开头描述了container很可能是不安全的。这里也可以这么写source.getNode().getLocation().getFile().getBaseName() = "SchedulingConstraintValidator.java"source文件来限制。

/** *@name Partial Flow to the rescue *@kind path-problem *@derscription  */import javaimport semmle.code.java.dataflow.TaintTracking// import DataFlow::PathGraphimport DataFlow::PartialPathGraph
/*sourceMap overrides of isValid method from ConstraintValidator*/class ConstraintValidator extends RefType {ConstraintValidator() {this.hasQualifiedName("javax.validation", "ConstraintValidator") }}
class ConstraintValidatorIsValid extends Method {ConstraintValidatorIsValid() {this.getName() = "isValid" andthis.getDeclaringType().getASourceSupertype() instanceof ConstraintValidator and this.isOverridable() // this.getASourceOverriddenMethod().getDeclaringType() instanceof ConstraintValidator }}
/*sink */class TypeConstraintValidatorContext extends RefType { TypeConstraintValidatorContext() { this.hasQualifiedName("javax.validation", "ConstraintValidatorContext") }}

predicate isBuildConstraintViolationWithTemplate(MethodAccess ma){ ma.getMethod().hasName("buildConstraintViolationWithTemplate") and ma.getMethod().getDeclaringType() instanceof TypeConstraintValidatorContext}
class MyTraintTrackConfig extends TaintTracking::Configuration{ MyTraintTrackConfig(){ this = "MyTraintTrackConfig" }
override predicate isSource(DataFlow::Node source){ exists(ConstraintValidatorIsValid isValidMethod |source.asParameter() = isValidMethod.getParameter(0))    }
override predicate isSink(DataFlow::Node sink){ exists(MethodAccess ma| isBuildConstraintViolationWithTemplate(ma) and ma.getArgument(0) = sink.asExpr() )    }
override int explorationLimit(){ result = 10 }
}
from MyTraintTrackConfig config, DataFlow::PartialPathNode source, DataFlow::PartialPathNode sinkwhere config.hasPartialFlow(source, sink, _) and source.getNode().asParameter().getName() = "container"select sink, source, sink, "Partial flow from unsanitized user data"
predicate partial_flow(DataFlow::PartialPathNode n, DataFlow::Node src, int dist) { exists(MyTraintTrackConfig conf, DataFlow::PartialPathNode source | conf.hasPartialFlow(source, n, dist) and src = source.getNode() and source.getNode().asParameter().getName() = "container" )}

原创 | GitHub Java CodeQL CTF


1.5 Identifying a missing taint step

You must have found that CodeQL does not propagate taint through getters like container.getHardConstraints and container.getSoftConstraints. Can you guess why this default behaviour was implemented?

你一定发现,CodeQL不会通过getters传播污点,比如container.getHardConstraintscontainer.getSoftConstraints。你能猜到为什么要实施这种默认行为吗?

答案:

默认步骤是小心翼翼地避免假阳性,特别是对于生产级别的查询。追踪漏洞可以让我们看到流程在哪里停止了。猜测是,getters/setters方法经常会覆盖有污点的数据,而让它不受约束也可能会返回非常多的结果。当一个写得不好的查询消耗会所有的内存时,分析部分流量时,需要限制来源的数量。

例如:

public class SomeThingObject {    private String tainted;    private int something;
public SomeThingObject(String tainted, int something) { this.tainted = tainted; this.something = something; }
public String getTainted() { return tainted;    }
public String getSomething() { return something; }}
SomeThingObject stb = new SomeThingObject("tainted", 123);int notTainted = someObject.getSomething()dangerousSink(notTainted);

如果我们认为字符串 "tainted "是一个危险的用户输入,那么stb实例也会被污染。正因为如此,假设该实例上的每个getter都会返回一个有污点的值,就会导致dangerousSink调用中出现假阳性。这可能就是为什么CodeQL默认情况下不会将污点传播给有污点的对象实例上的getter。


1.6 Adding additional taint steps

分析1.4的结果,可以发现在停止getSoftConstraintsgetHardConstraints被进一步track

原创 | GitHub Java CodeQL CTF

CodeQL允许在在TaintTracking::Configuration中声明额外的污点步骤,也就是谓词isAdditionalTaintStep

原创 | GitHub Java CodeQL CTF

但这里官方推荐另一种更通用的方法,使用TaintTracking::AdditionalTaintStep类。https://github.com/github/codeql/blob/bc7163aa68017f93c25ec7423044727a5d785142/java/ql/src/semmle/code/java/dataflow/internal/TaintTrackingUtil.qll

class MyAdditionalTaintStep extends TaintTracking::AdditionalTaintStep {  override predicate step(DataFlow::Node pred, DataFlow::Node succ) {// pred 先前 Previously  succ 先后 Successively    exists( | | )  }}

根据提示:在步骤谓词中,你应该指出这2个nodes是MethodAccess的2个元素:一个是它的qualifier ,一个是在调用发现的返回值。利用模板进行进一步track

predicate flowMethodCallable(Callable m) {    exists(string s |    s = m.getName() and    (        s = "getSoftConstraints" or        s = "getHardConstraints" or        s = "keySet"    )    )}
class StepThroughMemberMethodCallable extends TaintTracking::AdditionalTaintStep { override predicate step(DataFlow::Node n1, DataFlow::Node n2) { exists(MethodAccess ma | n1.asExpr() = ma.getQualifier() and // `qualifier ` n2.asExpr() = ma and // retrun vlaue flowMethodCallable(ma.getMethod()) ) } }

原创 | GitHub Java CodeQL CTF


1.7 Adding taint steps through a constructor

要求为HashSet构造函数加AdditionalTaintStep

/*HashSet Contructor */predicate flowContructorCallable(Callable cc) {    exists(string s |    s = cc.getName()    and s.matches("HashSet%")    )}
class StepThroughConstructor extends TaintTracking::AdditionalTaintStep { override predicate step(DataFlow::Node pred, DataFlow::Node succ) { exists(ConstructorCall cc | pred.asExpr() = cc.getAnArgument() and succ.asExpr() = cc and flowContructorCallable(cc.getConstructor()) ) }}

原创 | GitHub Java CodeQL CTF


1.8 Finish

最终的QL结果:

/** * @name finis the ql * @kind path-problem * @derscription  */import javaimport semmle.code.java.dataflow.TaintTrackingimport DataFlow::PathGraph// import DataFlow::PartialPathGraph/*MethodCallable additional Taint Step*/ predicate flowMethodCallable(Callable m) {    exists(string s |    s = m.getName() and    (        s = "getSoftConstraints" or        s = "getHardConstraints" or        s = "keySet"     )    )}

class StepThroughMemberMethodCallable extends TaintTracking::AdditionalTaintStep { override predicate step(DataFlow::Node n1, DataFlow::Node n2) { exists(MethodAccess ma | n1.asExpr() = ma.getQualifier() and // `qualifier ` n2.asExpr() = ma and // retrun vlaue flowMethodCallable(ma.getMethod()) ) } }/*HashSet Contructor */predicate flowContructorCallable(Callable cc) { exists(string s | s = cc.getName() and s.matches("HashSet%") )}
class StepThroughConstructor extends TaintTracking::AdditionalTaintStep { override predicate step(DataFlow::Node pred, DataFlow::Node succ) { exists(ConstructorCall cc | pred.asExpr() = cc.getAnArgument() and succ.asExpr() = cc and flowContructorCallable(cc.getConstructor()) ) }}

/*sourceMap overrides of isValid method from ConstraintValidator*/class ConstraintValidator extends RefType {ConstraintValidator() {this.hasQualifiedName("javax.validation", "ConstraintValidator") }}
class ConstraintValidatorIsValid extends Method {ConstraintValidatorIsValid() {this.getName() = "isValid" andthis.getDeclaringType().getASourceSupertype() instanceof ConstraintValidator and this.isOverridable() // this.getASourceOverriddenMethod().getDeclaringType() instanceof ConstraintValidator }}
/*sink */class TypeConstraintValidatorContext extends RefType { TypeConstraintValidatorContext() { this.hasQualifiedName("javax.validation", "ConstraintValidatorContext") }}

predicate isBuildConstraintViolationWithTemplate(MethodAccess ma){ ma.getMethod().hasName("buildConstraintViolationWithTemplate") and ma.getMethod().getDeclaringType() instanceof TypeConstraintValidatorContext}
class MyTraintTrackConfig extends TaintTracking::Configuration{ MyTraintTrackConfig(){ this = "MyTraintTrackConfig" }
override predicate isSource(DataFlow::Node source){ exists(ConstraintValidatorIsValid isValidMethod |source.asParameter() = isValidMethod.getParameter(0))    }
override predicate isSink(DataFlow::Node sink){ exists(MethodAccess ma| isBuildConstraintViolationWithTemplate(ma) and ma.getArgument(0) = sink.asExpr() ) }
override int explorationLimit(){ result = 10    }
}
from MyTraintTrackConfig config, DataFlow::PathNode source, DataFlow::PathNode sinkwhere config.hasFlowPath(source, sink)select sink, source, sink, "Custom constraint error message contains unsanitized user data"

原创 | GitHub Java CodeQL CTF

参考



Part 4

原创 | GitHub Java CodeQL CTF

https://github.com/atorralba/GHSL_CTF_4

https://securitylab.github.com/ctf/codeql-and-chill/

https://github.com/github/securitylab/discussions/141

https://xz.aliyun.com/t/7979#toc-6



往期推荐



原创 | 浅谈Log4j2在Springboot的检测-2

原创 | 我在人间凑数的日子---网恋篇(一)

原创 | java安全-java反序列化之URLDNS


原文始发于微信公众号(SecIN技术平台):原创 | GitHub Java CodeQL CTF

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2022年4月22日23:58:29
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   原创 | GitHub Java CodeQL CTFhttp://cn-sec.com/archives/935192.html

发表评论

匿名网友 填写信息