avatar

cindahy

A text-focused Halo theme

  • 首页
  • 文章分类
  • 项目
  • 关于
Home codeql从入门到入土
文章

codeql从入门到入土

Posted 2025-08-1 Updated 2025-08- 1
By Administrator
93~120 min read

参考文章

白盒代码审计工具——CodeQL安装与使用教程【Linux+Windows】

CodeQL从入门到放弃

CodeQL 入门

漏洞发现:代码分析引擎 CodeQ

白盒代码审计:关于VsCode中CodeQL的应用

使用CodeQL分析CTF题目

CodeQL基础

生成jdk数据库

编译OpenJDK8并生成CodeQL数据库

CodeQL构建单cc依赖数据库以及构建jdk数据库

新的API

github官方指南:用于编写自定义 CodeQL 查询的新数据流 API

前言:这一部分的学习看了挺多文章的,后来才发现CodeQL 在 2023~2024 年对整个数据流/污点跟踪 API 做了彻底的重构,而网上的大部分文章都是2023年及以前的,用的都是旧版API。所以在这里写了新的污点追踪模板和新旧迁移对比。

整个学习过程东补一块西补一块的,感觉写的有点乱

安装

引擎

引擎下载地址:code引擎下载地址

下载的目录方式系统变量中

在命令行输入codeql,得到下图表示引擎安装成功

SDK

下载命令:git clone https://github.com/Semmle/ql

VSCode开发插件安装

在扩展中搜索codeql安装,并把之前的引擎安装路径填入codeql的可执行文件路径

生成数据库

codeql只可以对解析引擎编译生成的数据库进行扫描,所以需要先生成目标数据库。

本地生成

codeql database create 数据库名 --language=cpp --source-root=源码路径 --command="编译命令"

#因为我这里没有安装gpg,所以这里直接跳过gpg签名
codeql database create DatabaseName --language=java --command="mvn clean install -Dgpg.skip=true" --source-root=D:\xxl-job --overwrite

  • 主要参数

  • --command 参数如果不指定,会使用默认的编译命令和参数

  • --source-root 源码路径

  • --overwrite 表示create的目标database对已有的database做覆盖

  • --language要根据具体项目的编译语言指定

对应关系如下是:

Language

Identity

C/C++

cpp

C#

csharp

Go

go

Java

java

javascript/Typescript

javascript

Python

python

例子:

#下载源代码
git clone https://gitee.com/xuxueli0323/xxl-job 

#创建源码数据库:
codeql database create DatabaseName --language=java --command="mvn clean install -Dgpg.skip=true" --source-root=D:\xxl-job 
codeql database create DatabaseName --language=java --command="mvn clean install -Dgpg.skip=true" --source-root=D:\text\subtitles-view-main

成功生成

进行项目漏洞扫描

VSCode

  1. 添加之前的数据库Database

  2. 用VSCode打开扫描规则(CodeQL libraries and queries)(即解压后的sdk文件夹)

  3. 根据具体语言进行规则扫描,ql后缀的文件是规则扫描文件

右键对应文件夹选择

比如这里运行了ql\java\ql\src\Security\CWE\CWE-020,共检测到 72 处可能存在此问题的代码位置。

命令行方法

#创建数据库
codeql database create databaseName --source-root=D:/xxl-job --language=java

#更新数据库
codeql database upgrade databaseName

#执行扫描规则

codeql database analyze Databasepath codeql-repo/java --format=csv --output=result.csv
#eg:codeql database analyze D:\xxl-job\databaseName D:\codeql\ql\java\ql\src\Security\CWE --format=csv --output=result.csv
  • Databasepath:数据库路径

  • codeql-repo/java :java扫描规则

  • format:结果输出格式

  • output:结果文件输出路径

相对于vscode的优点在于可以一次性扫很多条规则,而vscode最多只支持20条

vscode的优点在于可视化界面

基本语法

QL语法

基本语法

from [datatype] var
where condition(var = something)
select var

类库

把我们的靶场项目通过codeql引擎转换成codeql可以识别的database的过程中,

codeql引擎会把我们的java代码转换成可以识别的AST数据库

类似于这种格式

我们的类库实际上就是上面AST的对应关系。

  • AST(抽象语法树):是源代码的结构化树形表示,保留了程序逻辑的核心结构,是编译器、静态分析工具(如codeql)和 代码转换工具的核心数据结构。

谓词

和SQL一样,where部分的查询条件如果过长,会显得很乱。CodeQL提供一种机制可以让你把很长的查询语句封装成函数。

这个函数,就叫谓词。

RefType中常用的谓词

https://codeql.github.com/codeql-standard-libraries/java/semmle/code/java/Type.qll/type.Type$RefType.html

getACallable() 获取所有可以调用方法(其中包括构造方法)
getAMember() 获取所有成员,其中包括调用方法,字段和内部类这些
getAField() 获取所有字段
getAMethod() 获取所有方法
getASupertype() 获取父类
getAnAncestor() 获取所有的父类相当于递归的getASupertype*()

Callable常用谓词:
https://codeql.github.com/codeql-standard-libraries/java/semmle/code/java/Member.qll/type.Member$Callable.html

polyCalls(Callable target) 一个Callable 是否调用了另外的Callable,这里面包含了类似虚函数的调用
hasName(name) 可以对方法名进行限制

Call 常用谓词

https://codeql.github.com/codeql-standard-libraries/java/semmle/code/java/Expr.qll/type.Expr$Call.html

getCallee() 返回函数声明的位置
getCaller() 返回调用这个函数的函数位置

设置Source和Sink

  • 什么是source和sink

在代码自动化安全审计的理论当中,有一个最核心的三元组概念,就是(source,sink和sanitizer)

  • source是指漏洞污染链条的输入点。比如获取http请求的参数部分,就是非常明显的Source。

  • sink是指漏洞污染链条的执行点,比如SQL注入漏洞,最终执行SQL语句的函数就是sink(这个函数可能叫query或者exeSql,或者其它)。

  • sanitizer又叫净化函数,是指在整个的漏洞链条当中,如果存在一个方法阻断了整个传递链,那么这个方法就叫sanitizer。

只有当source和sink同时存在,并且从source到sink的链路是通的,才表示当前漏洞是存在的

  • 设置Source

在CodeQL中我们通过此方法设置Source

predicate isSink(DataFlow::Node sink) { sinkNode(sink, "log-injection") }

在本例中我们设置Source的代码为:

predicate isSource(DataFlow::Node src) {
     src instanceof RemoteFlowSource
   }

RemoteFlowSource表示能由远程用户操控的数据流源。

这是SDK自带的规则,里面包含了大多常用的Source入口。我们使用的SpringBoot也包含在其中,我们可以直接使用。

  • 设置Sink

在本案例中,我们的sink应该为query方法(Method)的调用(MethodAccess),所以我们设置Sink为

predicate isSink(DataFlow::Node sink) {
     exists(Method method, MethodCall call |
       method.hasName("query") and
       call.getMethod() = method and
       sink.asExpr() = call.getArgument(0)
     )
   }

这里代码的主要作用是查找一个query()方法的调用点,并把它的第一个参数设置为sink

Flow数据流

设置好Source和Sink,就相当于搞定了首尾,但是首尾是否能够连通才能决定是否存在漏洞

由于CodeQL 的数据流 API 在 2023 年后发生了重大变更,旧的 DataFlow::PathGraph 已被废弃,现在需要使用模块化方法导入路径图

比如以下代码:


module MyFlow = DataFlow::Global<MyConfig>;
import MyFlow::PathGraph

from MyFlow::PathNode source, MyFlow::PathNode sink
where MyFlow::flowPath(source, sink)
select sink.getNode(), source, sink, "Data flow found"
  • 在MyConfig自定义配置模块,要先实例化之后再导入查询

  • from子句:从路径图中选择source和sink

  • where子句:限定数据流路径

  • select子句:输出结果

语句优化

这里用到的靶场是 micro_service_seclab

生成数据库

codeql database create DATABASE --language="java" --source-root=D:\text\micro_service_seclab-main --command="mvn clean package -Dmaven.test.skip=true"

初步成果

这里踩了一个坑,刚开始DataFlow::PathGraph和MethodAccess都会报错,后来发现是DataFlow::PathGraph和MethodAccess都已经被弃用,下面是正确的写法。

/**
 * @id   java/examples/vuldemo
 * @name Sql-Injection
 * @description Sql-Injection
 * @kind path-problem
 * @problem.severity warning
 */

 import java
 import semmle.code.java.dataflow.DataFlow
 import semmle.code.java.dataflow.TaintTracking
 import semmle.code.java.dataflow.FlowSources
 import semmle.code.java.security.QueryInjection

 module SqlInjectionConfig implements DataFlow::ConfigSig {
   predicate isSource(DataFlow::Node src) {
     src instanceof RemoteFlowSource
   }
 
   predicate isSink(DataFlow::Node sink) {
     exists(Method method, MethodCall call |
       method.hasName("query") and
       call.getMethod() = method and
       sink.asExpr() = call.getArgument(0)
     )
   }
 }
 

 module SqlFlow = TaintTracking::Global<SqlInjectionConfig>;
 

 import SqlFlow::PathGraph

 from SqlFlow::PathNode source, SqlFlow::PathNode sink
 where SqlFlow::flowPath(source, sink)
 select sink.getNode(), source, sink,
        "Potential SQL-injection: user input flows to SQL query."

相关解析

根据官方更新https://github.blog/changelog/2023-08-13-new-dataflow-api-for-writing-custom-codeql-queries/,现在需要使用模块化方法导入路径图,更新后的查询应如下:


module MyFlow = TaintTracking::Global<MyConfig>;
import MyFlow::PathGraph

from MyFlow::PathNode source, MyFlow::PathNode sink
where MyFlow::flowPath(source, sink)
select sink.getNode(), source, sink, "Data flow found"

在这里MyConfig配置模块就是需要我们自己写的source和sink规则,在此例的配置模块SqlInjectionConfig中:

  • 定义了一个实现 DataFlow::ConfigSig 的模块

  • isSource 谓词: 将远程用户输入定义为污染源

  • isSink 谓词: 查找名为 "query" 的方法调用,并将其第一个参数定义为汇聚点

module SqlFlow = TaintTracking::Global<SqlInjectionConfig>,使用上面的配置实例化一个全局污点追踪模块(TaintTracking::Global)

查询主体:

  • from子句:从路径图中选择source和sink

  • where子句:限定数据流路径

  • select子句:输出结果

运行结果:

误报解决

这里的List<long>类型由于强类型约束,而纯数字无法构成可执行的SQL片段,所以这里属于误报

这里形成误报的根本原因其实是:

SqlInjectionConfig 里没有定义任何 isBarrier 谓词,导致 任何 通过 RemoteFlowSource 获得的值,即使已经经过安全的类型转换(例如把用户输入 String 解析成 long 后放进 List<Long>),仍被当成“受污染的”数据一路向下游传播,最终在 query(...) 处被判定为漏洞,从而把完全无害的 List<Long> 参数也当作注入风险,产生误报。

注意:现在的isSanitizer已经被isBarrie替代

模板


  predicate isBarrier(DataFlow::Node sanitizer) {  // 4: 'isBarrier' replaces 'isSanitizer'
    sanitizer.asExpr() instanceof LiveLiteral or
    sanitizer.getType() instanceof PrimitiveType or
    sanitizer.getType() instanceof BoxedType or
    sanitizer.getType() instanceof NumberType or
    sanitizer.getType() instanceof TypeType
  }

所以这里应该是

 predicate isBarrier(DataFlow::Node sanitizer) {  // 4: 'isBarrier' replaces 'isSanitizer'
      sanitizer.getType() instanceof PrimitiveType or//基本类型
      sanitizer.getType() instanceof BoxedType or//基本类型的对象类型
      sanitizer.getType() instanceof NumberType or//数字类型
      exists(ParameterizedType pt| sanitizer.getType() = pt and pt.getTypeArgument(0) instanceof NumberType )  
     //判断是否是泛型类型并且其泛型参数是数字类型   
 }

这里isBarrier 检测到 userIds 的类型是 List<Long>,并且泛型参数 Long 是 NumberType,因此阻止数据流继续传播,避免误报。

漏报解决

可能是版本差异,这个漏洞点我是能扫描到的,但是还是说一下漏报解决办法

关于漏报可以给它直接续上

现在统一用isAdditionalFlowStep方法,以下是完整代码。

/**
 * @id   java/examples/vuldemo
 * @name Sql-Injection
 * @description Sql-Injection
 * @kind path-problem
 * @problem.severity warning
 */

import java
import semmle.code.java.dataflow.DataFlow
import semmle.code.java.dataflow.TaintTracking
import semmle.code.java.dataflow.FlowSources
import semmle.code.java.security.QueryInjection

predicate isTaintedString(Expr expSrc, Expr expDest) {
    exists(Method method, MethodCall call, MethodCall call1|
           expSrc = call1.getArgument(0) and expDest = call and call.getMethod() = method
    and method.hasName("get") and method.getDeclaringType().toString() = "Optional<String>"
    and call1.getArgument(0).getType().toString() = "Optional<String>"
    )
}

module SqlInjectionConfig implements DataFlow::ConfigSig {
    predicate isSource(DataFlow::Node src) {
        src instanceof RemoteFlowSource
    }

    predicate isSink(DataFlow::Node sink) {
        exists(Method method, MethodCall call |
               method.hasName("query") and
               call.getMethod() = method and
        sink.asExpr() = call.getArgument(0)
        )
    }

    predicate isBarrier(DataFlow::Node sanitizer) {  // 4: 'isBarrier' replaces 'isSanitizer'
        sanitizer.getType() instanceof PrimitiveType or
        sanitizer.getType() instanceof BoxedType or
        sanitizer.getType() instanceof NumberType or
        exists(ParameterizedType pt| sanitizer.getType() = pt and pt.getTypeArgument(0) instanceof NumberType )  
    }
    predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
        isTaintedString(node1.asExpr(), node2.asExpr())
    }

}


module SqlFlow = TaintTracking::Global<SqlInjectionConfig>;


import SqlFlow::PathGraph

from SqlFlow::PathNode source, SqlFlow::PathNode sink
where SqlFlow::flowPath(source, sink)
select sink.getNode(), source, sink,
"Potential SQL-injection: user input flows to SQL query."

Lombok 插件漏报(在新版本中已经被解决)

在旧版的codeql中:

Lombok 的注解在编译期才生成 getter / setter / builder 等代码,而 CodeQL 在 源代码→字节码之间的 AST 阶段 进行分析,导致生成的代码不在数据库里,数据流被截断,于是把本应触发的漏洞判定为“不可达”,出现漏报。

而这个Lombok 插件漏报问题在新版本的codeql中其实已经被解决。

使用 CodeQL 进行代码扫描改进了对使用 Project Lombok 的 Java 代码库的支持:

https://github.blog/changelog/2023-09-01-code-scanning-with-codeql-improves-support-for-java-codebases-that-use-project-lombok/

从 2024-05-07 官方公告起,GitHub 已经在 CodeQL 引擎 2.17.0+ / 2.17.1+(CLI 与 GitHub.com Actions 同步更新) 中内置了对 Lombok 的“零配置”支持。

也就是说:

  • GitHub.com 的默认 CodeQL workflow(github/codeql-action 3.x)现在已经不需要任何 delombok 步骤,Lombok 生成的 getter / builder / @Slf4j 等代码会被 直接解析,数据流能够正常穿过这些节点。

  • CLI 用户只要把 CLI 升到 2.17.1 及以上(2024-05-07 发布),再用常规的codeql database create … --language=java即可,无需在 lombok-maven-plugin 里加 delombok 目标,也无需手动 java -jar lombok.jar delombok。

Java污点追踪模板总结

见https://github.blog/changelog/2023-08-13-new-dataflow-api-for-writing-custom-codeql-queries/

github官方指南:用于编写自定义 CodeQL 查询的新数据流 API

module SensitiveLoggerConfig implements DataFlow::ConfigSig {  // 1: module always implements DataFlow::ConfigSig or DataFlow::StateConfigSig
  predicate isSource(DataFlow::Node source) { source.asExpr() instanceof CredentialExpr } // 3: no need to specify 'override'
  predicate isSink(DataFlow::Node sink) { sinkNode(sink, "log-injection") }

  predicate isBarrier(DataFlow::Node sanitizer) {  // 4: 'isBarrier' replaces 'isSanitizer'
    sanitizer.asExpr() instanceof LiveLiteral or
    sanitizer.getType() instanceof PrimitiveType or
    sanitizer.getType() instanceof BoxedType or
    sanitizer.getType() instanceof NumberType or
    sanitizer.getType() instanceof TypeType
  }

}

module SensitiveLoggerFlow = TaintTracking::Global<SensitiveLoggerConfig>; // 2: TaintTracking selected 

import SensitiveLoggerFlow::PathGraph  // 7: the PathGraph specific to the module you are using

from SensitiveLoggerFlow::PathNode source, SensitiveLoggerFlow::PathNode sink  // 8 & 9: using the module directly
where SensitiveLoggerFlow::flowPath(source, sink)  // 9: using the flowPath from the module 
select sink.getNode(), source, sink, "This $@ is written to a log file.", source.getNode(),
  "potentially sensitive information"

以下是新旧迁移对照表

旧 API(将被弃用)

新 API(2024 以后推荐)

class MyConfig extends TaintTracking::Configuration

module MyConfig implements DataFlow::ConfigSig

或 TaintTracking::StateConfigSig

this = "MyConfig"

特征字符串

不再需要

override predicate isSource/isSink/isSanitizer...

直接写 predicate isSource/isSink/isBarrier...

isSanitizer

/ isSanitizerIn

统一为 isBarrier

isAdditionalTaintStep

统一为 isAdditionalFlowStep

TaintTracking::Global<MyConfig> myFlow

实例化

直接用 TaintTracking::Global<MyConfig>

config.hasFlowPath(source, sink)

MyFlow::flowPath(source, sink)

使用CodeQL分析CTF题目

对类进行限制

在CodeQL中,RefType就包含了我们在Java里面使用到的Class,Interface的声明,比如我们现在需要查询一个类名为XStreamHandler的类,但是我们不确定他是Class还是Interface,我们就可以通过 RefType定义变量后进行查询

#自定义规则扫描文件
import java

from RefType c
where c.hasName("XStreamHandler")
select c

得到查询XStreamHandler类

结合

RefType中的常见谓词可以构造以下查询获取XStreamHandler的fromObject

import java

from RefType c,Callable cf
where 
  c.hasName("XStreamHandler") and
  cf.hasName("fromObject")and
  cf=c.getACallable()
select c,cf

自定义扫描规则

以下部分是codeql通过(匹配Context.lookup()函数检测JNDI注入安全漏洞)的自定义扫描规则示例

以下插播一下JNDI注入的概念

  • 基本概念:

  • JNDI 是 Java 平台的一项技术,用于在应用程序中查找和访问命名和目录服务,通过 JNDI,应用程序可以以统一的方式访问各种资源。

  • 这些资源在命名服务中都有对应的名称,应用程序使用javax.naming.Context.lookup() 方法,根据名称来获取对应的资源对象。

  • 注入原理:

  • 当 lookup() 方法的参数可控,且应用程序没有对输入进行严格的验证和过滤时,攻击者就可以构造恶意的 JNDI 名称,让应用程序去连接恶意的命名服务,从而执行恶意代码。


靶场:mybatis-3的下载链接:https://github.com/mybatis/mybatis-3

##因为我这里git-commit-id-maven-plugin 插件报错,所以我这里强制跳过了
##生成数据库
codeql database create mybatis_3_db --language=java --command="mvn clean compile -Dgpg.skip=true -Dmaven.gitcommitid.skip=true" --source-root=D:\mybatis-3-master --overwrite

自定义扫描规则:

/**
* @kind path-problem
*/

import java

class LookupMethod extends Call {
    LookupMethod() {
        this.getCallee().getDeclaringType().getASupertype*().hasQualifiedName("javax.naming", "Context") and
        this.getCallee().hasName("lookup")
    }
}


class GetterCallable extends Callable {
    GetterCallable() {
        getName().matches("get%") and
        hasNoParameters() and
        getName().length() > 3
        or
        getName().matches("set%") and
        getNumberOfParameters() = 1
    }
}


query predicate edges(Callable a, Callable b) { a.polyCalls(b) }

from LookupMethod endcall, GetterCallable entryPoint, Callable endCallAble
where
endcall.getCallee() = endCallAble and
edges+(entryPoint, endCallAble)
select endcall.getCaller(), entryPoint, endcall.getCaller(), "Geter jndi"

代码解析:

  • @kind path-problem:声明这是一个路径查询,会显示源点到问题点的调用链。

  • 定义 LookupMethod 类(匹配所有 Context.lookup()方法调用)

  • 继承自Call,表示方法调用

  • 谓词逻辑

  • getCallee():获取被调用的方法

  • getDeclaringType().getASupertype*():获取声明该方法的类及其所有父类/接口

  • hasQualifiedName("javax.naming", "Context"):限定为 javax.naming.Context 类或其子类

  • hasName("lookup"):方法名为 "lookup"

  • 定义 GetterCallable 类(识别所有 getter 和 setter 方法)

  • 谓词逻辑

  • 第一部分匹配 getter:

matches("get%"):以 "get" 开头

hasNoParameters():无参数

length() > 3:方法名长度 > 3(排除 "get" 本身)

  • 第二部分匹配 setter:

matches("set%"):以 "set" 开头

getNumberOfParameters()=1:只有一个参数

  • 定义 edges 谓词(建立方法间的调用关系图)

  • 定义调用关系边:

  • a.polyCalls(b):表示 a 可能调用 b(考虑多态)

  • 主查询部分

  • 数据流:

  • endcall:终点(LookupMethod 调用)

  • entryPoint:起点(GetterCallable)

  • endCallAble:被调用的lookup方法

  • 条件:

  • endcall.getCallee() = endCallAble:确保 endCallAble 是被调用的 lookup 方法

  • edges+(entryPoint, endCallAble):存在从 entryPoint 到 endCallAble 的一条或多条调用路径

  • 输出

  • 显示从调用者到 lookup 的路径

  • 标记为 "Geter jndi"

  • 总结:这个查询检测的是:从任意 getter/setter 方法出发,通过一系列调用最终到达 Context.lookup() 的调用链。典型场景是检测通过 JavaBean 属性访问触发的潜在危险 JNDI 查询。

执行结果:

在ctf中的运用

构建jdk数据库

为了解决特定环境下的依赖关系或者要深入分析Java底层机制时,且没有找到对应的jdk数据库时,就需要手动构建jdk数据库。

由于JDK的构建对系统环境有严格要求,不同系统上可能表现不同,而Docker 容器能提供与宿主环境隔离的标准化环境,确保构建过程可复现。所以这里一般需要通过docker构建

在此例中:

操作系统:ubuntu 16.04

Target_OpenJDK:OpenJDK 8u73

#拉取一个ubuntu 16.04镜像
docker pull ubuntu:16.04
# 使用镜像并且进入容器
docker run -it ubuntu:16.04 /bin/bash
#更新软件源
apt-get update
# 下载必要构建工具
apt install -y build-essential gdb  cmake openjdk-8-jdk cpio file unzip zip wget
apt install -y --no-install-recommends  libfontconfig1-dev libfreetype6-dev  libcups2-dev libx11-dev  libxext-dev  libxrender-dev  libxrandr-dev  libxtst-dev  libxt-dev libasound2-dev  libffi-dev  autoconf
# 降级 Make
wget http://ftp.gnu.org/gnu/make/make-3.81.tar.gz 
  && tar -zxvf make-3.81.tar.gz 
  && cd /make-3.81 
  && bash configure -prefix=/usr 
  && make 
  && make install
  

安装完后如图:

#获取jdk源码
apt-get update && apt-get install -y git
git clone https://github.com/openjdk/jdk8u.git
#进入jdk源码目录
cd jdk8u
#运行configure进行编译配置和编译环境检查
bash configure --with-debug-level=fastdebug  \
   --with-jvm-variants=server  \
   --with-boot-jdk=/usr/lib/jvm/java-1.8.0-openjdk-amd64 \
   --with-target-bits=64 \
   --enable-debug-symbols  \
   --with-native-debug-symbols=internal

检查通过后如图

执行make命令开始编译OpenJDK

make images JOBS=4

编译完成后如图

安装codeql

wget https://github.com/github/codeql-cli-binaries/releases/download/v2.14.6/codeql-linux64.zip
unzip codeql-linux64.zip -d /oopt
# 添加到PATH
export PATH=$PATH:/oopt/codeql

构建数据库

cd /jdk8u

codeql database create openjdk8u-db \
  --language=java \
  --command="make images JOBS=4" \
  --source-root=.

可算是跑出来了

ezchain题目

hfctf2022的ezchain题目考察的是:hessian反序列化链构造等。

题目环境:

https://github.com/waderwu/My-CTF-Challenges/tree/master/hfctf-2022/ezchain

这里说是要先构建jdk的数据库再扫的,但是构建出来之后怎么搞我还要再试试。

github上的项目漏洞扫描

项目一

试了几个就扫出来,https://github.com/slabiak/AppointmentScheduler这个项目,逐一分析一下这个项目的几个漏洞点

漏洞点

log injection(日志注入)

属于log injection(日志注入),这里的漏洞是会把token直接记录到日志中,获取token后可能会被用于未授权操作。

csrf保护被禁用

临时文件本地信息泄露漏洞

@Component
public class PdfGeneratorUtil {

    private final SpringTemplateEngine templateEngine;
    private final String baseUrl;

    public PdfGeneratorUtil(SpringTemplateEngine templateEngine, @Value("${base.url}") String baseUrl) {
        this.templateEngine = templateEngine;
        this.baseUrl = baseUrl;
    }

    public File generatePdfFromInvoice(Invoice invoice) {

        Context ctx = new Context();
        ctx.setVariable("invoice", invoice);
        String processedHtml = templateEngine.process("email/pdf/invoice", ctx);

        ITextRenderer renderer = new ITextRenderer();
        renderer.setDocumentFromString(processedHtml, baseUrl);
        renderer.layout();

        String fileName = UUID.randomUUID().toString();
        FileOutputStream os = null;
        try {
            final File outputFile = File.createTempFile(fileName, ".pdf");
            os = new FileOutputStream(outputFile);
            renderer.createPDF(os, false);
            renderer.finishPDF();
            return outputFile;
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (DocumentException e) {
            e.printStackTrace();
        } finally {
            if (os != null) {
                try {
                    os.close();

                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return null;
    }

}

此代码主要用于根据发票信息生成PDF文件

问题本质:

  • 使用File.createTempFile()创建的临时PDF文件默认权限为644(rw-r--r--)

  • 这意味着同一系统上的其他用户都可以读取这些PDF文件

  • 发票PDF可能包含敏感的商业或个人数据

SensitiveInfoLog(敏感信息日志记录)

一样的将敏感信息插入日志文件

项目二

https://github.com/dustin/java-memcached-client

CWE-327(不安全的加密算法)

使用了 MD5 算法 (KETAMA_HASH 部分),而MD5已被证明存在严重碰撞漏洞

  • 碰撞攻击 (Collision Attacks):MD5 最严重的缺陷是它容易受到碰撞攻击。这意味着可以找到两个不同的输入数据,它们生成相同的 MD5 散列值。

  • 不可逆性受损:虽然散列函数理论上是不可逆的(无法从散列值推导出原始数据),但由于碰撞攻击的存在,MD5 在某些场景下作为安全校验或密码存储已经不再可靠。

项目三

https://github.com/xuxueli/xxl-mq

CWE-352(CSRF)

漏洞点

AccessTokenController 中有几个方法执行了对系统状态进行修改的操作,例如:

  • @RequestMapping("/insert"):新增 AccessToken

  • @RequestMapping("/delete"):删除 AccessToken

  • @RequestMapping("/update"):更新 AccessToken

这些方法是用来修改系统数据的,这是CSRF攻击的常见目标,但是没有任何的CSRF Token的生成和验证机制。

在一个典型的安全防御中,服务器在渲染页面时生成一个随机且唯一的CSRF Token,将其嵌入到表单的隐藏字段或页面的JavaScript变量中。当用户提交表单或发送AJAX请求时,这个Token会随请求一起发送到服务器。服务器会在处理请求前验证这个Token是否匹配,如何Token不匹配或缺失,则拒绝请求,但是此代码中缺少这一步骤。

这里系统显然依赖于用户的会话 Cookie 来进行认证。这意味着只要用户处于登录状态,他们的浏览器就会自动将这些 Cookie 附加到发送到域名的任何请求中,包括由恶意网站触发的请求。

师傅这是我这周的笔记,主要就是对codeql再研究了一下,

对codeql新旧版本的API和污点追踪模板做了对比,然后github上找了几个项目扫描了一下,找到了几个漏洞点,但是还没有进一步的利用

工具
工具
License:  CC BY 4.0
Share

Further Reading

Oct 16, 2025

bp+mumu模拟器app抓包

下载好mumu模拟器之后将root权限和可写系统盘打开 将模拟器的代理设置成本机ip

Oct 9, 2025

xray漏扫工具

下载 根据操作系统选择下载,下载地址https://github.com/chaitin/xray/releases 解压之后首先生成证书 xray_windows_amd64.exe genca 将生成的证书导入 基本命令

Aug 1, 2025

codeql从入门到入土

参考文章 白盒代码审计工具——CodeQL安装与使用教程【Linux+Windows】 CodeQL

OLDER

第38天反序列化JAVA

NEWER

L3HCTF 2025

Recently Updated

  • 常见安全产品整理(防火墙,WAF,EDR)
  • ELK从入门到实践
  • bp+mumu模拟器app抓包
  • xray漏扫工具
  • Java反序列化-RMI的几种攻击方式

Trending Tags

安全运营 文件上传 php反序列化 xss csrf ssrf xxe sql php 白帽子讲web安全

Contents

©2025 cindahy. Some rights reserved.

Using the Halo theme Chirpy