惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Jina AI
Jina AI
V
Vulnerabilities – Threatpost
Security Latest
Security Latest
AI
AI
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
量子位
H
Help Net Security
Attack and Defense Labs
Attack and Defense Labs
The GitHub Blog
The GitHub Blog
L
LINUX DO - 最新话题
A
Arctic Wolf
博客园_首页
S
Securelist
S
Secure Thoughts
Google DeepMind News
Google DeepMind News
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
T
Tailwind CSS Blog
Apple Machine Learning Research
Apple Machine Learning Research
酷 壳 – CoolShell
酷 壳 – CoolShell
Stack Overflow Blog
Stack Overflow Blog
N
Netflix TechBlog - Medium
Cyberwarzone
Cyberwarzone
小众软件
小众软件
T
Threatpost
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
Blog — PlanetScale
Blog — PlanetScale
N
News and Events Feed by Topic
NISL@THU
NISL@THU
Forbes - Security
Forbes - Security
博客园 - 聂微东
F
Fortinet All Blogs
Simon Willison's Weblog
Simon Willison's Weblog
H
Heimdal Security Blog
罗磊的独立博客
S
Security @ Cisco Blogs
B
Blog
T
Troy Hunt's Blog
Engineering at Meta
Engineering at Meta
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
The Hacker News
The Hacker News
The Last Watchdog
The Last Watchdog
Hacker News - Newest:
Hacker News - Newest: "LLM"
I
Intezer
T
Threat Research - Cisco Blogs
C
Cybersecurity and Infrastructure Security Agency CISA
The Cloudflare Blog
S
Schneier on Security
月光博客
月光博客
L
LINUX DO - 热门话题
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org

SumSec's Blog

AI Agent 工程的必然演进:CLI、Skills、Harne… 从安全角度谈Java反射机制--前章 · SUMSEC 从安全角度谈Java反射机制--终章 · SUMSEC 逆向学习fastjson反序列化始 · SUMSEC 2020年研究回顾总结 · SUMSEC Abstract syntax tree classes for … Analyzing data flow in Java · SUM… Annotations in Java · SUMSEC Basic query for Java code · SUMSEC BypassSuper使用介绍说明 · SUMSEC CodeQL Create OpenJdk/Jdk8 Databa… CodeQL library for Java · SUMSEC Navigating the call graph · SUMSEC Overflow-prone comparisons in Jav… Types in Java · SUMSEC Working with source locations · S… Aliases · SUMSEC Expression · SUMSEC Formulas · SUMSEC Javadoc · SUMSEC Modules · SUMSEC Predicates · SUMSEC Queries · SUMSEC Type · SUMSEC Variables · SUMSEC 一道shiro反序列化题目引发的思考 · SUMSEC 修改ysoserial使其支持任意代码执行 · SUMSEC 自定义 ClassLoader 隔离运行不同版本jar包的方式 ·… 2020网鼎杯---Java文件上传wp · SUMSEC CNVD-2020-10487(CVE-2020-1938)tom… JDSRC安全课笔记 · SUMSEC Java反序列化链回显解决方案 · SUMSEC Skipped breakpoint because it hap… Windows Terminal 配置文件 · SUMSEC bypass 学习笔记之绕安全狗bypass safedog · … 一次意外的代码审计----JfinalCMS审计 · SUMSEC 一篇文章读懂Java代码审计之XXE · SUMSEC 从安全角度谈Java反射机制--序章 · SUMSEC 小楼昨夜又春风,你知ysoserial-Gadget-URLDNS… 春眠不觉晓,RCE知多少? · SUMSEC 漫谈Commons-Collections反序列化 · SUMSEC 漫谈Java反序列化 · SUMSEC 白头搔更短,SSTI惹人心! · SUMSEC 记一次面试题 · SUMSEC About Me 关于我 · SUMSEC Apache Flink任意Jar包上传导致远程代码执行 · SU… CVE-2019-1388 UAC提权复现 · SUMSEC CVE-2019-16097 || Harbor任意管理员注册漏洞… Python加密shellcode免杀 · SUMSEC Telegram机器人作为渗透测试框架 · SUMSEC VM虚拟机无法安装vmtools解决|本程序需要您将此虚拟机上安装… 谁能想到,电视遥控器竟成了 Vibe Coding 神器 · SU… 从手改 Skill 到自动进化:评测结果和执行轨迹如何让 Agen… 模型人人都能用,什么才是你能带走的?我的答案是一个可进化的SKIL… 模型人人都能用,什么才是你能带走的?我的答案是一个可进化的Skil… AI 时代 ShiroAttack2 5.x:修改了什么 · SU… 在 AGI 降临前,先给 AI 开一条”脑内弹幕”通道 · SUM… 一篇博文,三种时间:网页幻灯与 Remotion 动效的交付逻辑 … 🔍 别让大模型”想太多”:SKILL开发中的语义陷阱与抗幻觉设计 … 2022 年年度总结 · SUMSEC Java Swing To RCE 漏洞分析 · SUMSEC SpringBoot GatewayEL表达式漏洞分析 · SUM… Sensitive keys in codebases · SUM… 论如何优雅注入 Java 内存马 · SUMSEC SUMSEC 知识点 · SUMSEC VMWare Workspace ONE Access Auth … Spring Framework RCE CVE-2022-229… 相似度算法调研 · SUMSEC CVE-2022-33891 Apache Spark shell… 正则匹配配置不当 · SUMSEC Spring Data MongoDB SpEL CVE-2022… CodeQl Usage Tricks · SUMSEC Spring Boot RCE到内存马探索 · SUMSEC Shiro后渗透拓展面 · SUMSEC shiro反序列化漏洞攻击拓展面–修改key · SUMSEC Hack-Tools 转化成Web · SUMSEC CodeQL与Shiro550碰撞 · SUMSEC CodeQL初见Shiro550 · SUMSEC CodeQL与AST之间联系 · SUMSEC Java加载动态链接库 · SUMSEC Log4j2 漏洞分析 · SUMSEC Interprocedural-Analysis 过程间分析 · … Data Analysis Foundation 数据分析基础 ·… Data Flow Analysis · SUMSEC Intermediate Representation 中间代表(… PII泄露–用CodeQL识别日志中的PII数据 · SUMSEC CodeQL workshop for Java: Unsafe … 前言 · SUMSEC 漏洞环境的搭建 · SUMSEC Fastjson回显 · SUMSEC Tomcat通用回显学习笔记 · SUMSEC 从Java反序列化漏洞题看CodeQL数据流 · SUMSEC 概述 · SUMSEC 记一次Log4j失败的Gadget挖掘记录 · SUMSEC Ysoserial改造记录 · SUMSEC JNDI注入 · SUMSEC shiro JRMP gadget · SUMSEC Fastjson MySQL gadget复现 · SUMSEC 2021年度总结 · SUMSEC
GitHub Java CodeQL CTF · SUMSEC
2023-01-09 · via SumSec's Blog

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 地址

CodeQL database 下载地址


简介

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

官方给的漏洞成因代码:

@Override
public 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方法调用上。

image-20210424151151578


数据流和污点跟踪分析

1.1 Source

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

因此,isSource谓词:

/**
 *@name Source
 *@derscription 
 */
import java
import 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" and
		this.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 Evaluation
select "Quick-eval isSource" 

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

image-20210422165129982

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

image-20210422164940185

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

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


1.2 Sink

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

/**
 *@name Sink
 *@derscription
 */

import java
import 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" 

image-20210422173829545


1.3 TaintTracking configuration

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

/** @kind path-problem */
import java
import semmle.code.java.dataflow.TaintTracking
import 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 sink
where cfg.hasFlowPath(source, sink)
select sink, source, sink, "Custom constraint error message contains unsanitized user data"

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

image-20210422200354218


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 java
import semmle.code.java.dataflow.TaintTracking
import 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 sink
where
  cfg.hasPartialFlow(source, sink, _) and
  source.getNode() = ... // TODO restrict to the one source we are interested in, for ease of debugging
select 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 java
import semmle.code.java.dataflow.TaintTracking
// import DataFlow::PathGraph
import DataFlow::PartialPathGraph

/*
source
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" and
		this.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 sink
where 
    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"
    )
}

image-20210422212855935


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

image-20210424151919760

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

image-20210424155832451

但这里官方推荐另一种更通用的方法,使用TaintTracking::AdditionalTaintStep类。

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())
    )
    } 
}

image-20210424173435717


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())
        )
    }
}

image-20210424173454759


1.8 Finish

最终的QL结果:

/**
 * @name finis the ql
 * @kind path-problem
 * @derscription 
 */

import java
import semmle.code.java.dataflow.TaintTracking
import 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())
        )
    }
}


/*
source
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" and
		this.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 sink
where 
    config.hasFlowPath(source, sink)
select sink, source, sink, "Custom constraint error message contains unsanitized user data"

image-20210424173533177


参考

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