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

推荐订阅源

Hacker News: Ask HN
Hacker News: Ask HN
C
Cisco Blogs
The Hacker News
The Hacker News
T
Tor Project blog
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
The GitHub Blog
The GitHub Blog
A
Arctic Wolf
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
The Register - Security
The Register - Security
云风的 BLOG
云风的 BLOG
Simon Willison's Weblog
Simon Willison's Weblog
P
Palo Alto Networks Blog
Vercel News
Vercel News
C
CERT Recently Published Vulnerability Notes
I
InfoQ
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
M
MIT News - Artificial intelligence
I
Intezer
aimingoo的专栏
aimingoo的专栏
U
Unit 42
C
Cyber Attacks, Cyber Crime and Cyber Security
L
LINUX DO - 热门话题
Microsoft Security Blog
Microsoft Security Blog
酷 壳 – CoolShell
酷 壳 – CoolShell
Cyberwarzone
Cyberwarzone
P
Proofpoint News Feed
P
Proofpoint News Feed
B
Blog
T
Threat Research - Cisco Blogs
博客园 - 叶小钗
Recorded Future
Recorded Future
Last Week in AI
Last Week in AI
N
News and Events Feed by Topic
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
Know Your Adversary
Know Your Adversary
Engineering at Meta
Engineering at Meta
G
Google Developers Blog
PCI Perspectives
PCI Perspectives
Google DeepMind News
Google DeepMind News
WordPress大学
WordPress大学
Application and Cybersecurity Blog
Application and Cybersecurity Blog
MyScale Blog
MyScale Blog
Security Archives - TechRepublic
Security Archives - TechRepublic
Schneier on Security
Schneier on Security
N
News | PayPal Newsroom
C
Cybersecurity and Infrastructure Security Agency CISA
H
Help Net Security
博客园 - 聂微东
H
Hackread – Cybersecurity News, Data Breaches, AI and More
G
GRAHAM CLULEY

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