shiro codeql分析理解(附带完整ql代码)
字数 919 2025-08-22 12:23:06

Shiro反序列化漏洞的CodeQL分析教学文档

1. 反序列化漏洞分析基础

在分析Shiro反序列化漏洞时,CodeQL审计的核心是寻找source(入口点)和sink(危险操作点)。

1.1 反序列化入口点(source)

反序列化漏洞的入口点通常是实现了Serializable接口的类:

import java
from Class cls
where 
  cls.getASupertype() instanceof TypeSerializable 
  and cls.fromSource()
select cls

这条查询会找出所有实现了Serializable接口且来自源代码的类。

2. 定位关键类和方法

2.1 查找特定类的实例化

要查找User类的实例化:

import java

/* 找到实例化User的类 */
class FindUser extends RefType {
  FindUser() {
    this.hasQualifiedName("com.summersec.shiroctf.bean", "User")
  }
}

from ClassInstanceExpr clie
where 
  clie.getType() instanceof FindUser
select clie
  • FindUser类构造函数验证当前类是否是User类的引用
  • from ClassInstanceExpr clie获取所有实例化对象
  • where条件判断实例化对象是否为User类的实例

2.2 查找关键方法

查找Tools包下的三个关键方法:

import java
from Method m
where 
  (m.getName() = "deserialize" 
  or m.getName() = "serialize" 
  or m.getName() = "base64Encode")
  and m.getDeclaringType().getPackage().getName() = "com.summersec.shiroctf.Tools"
select m

3. 查找危险方法调用

查找所有调用exeCmd方法的位置:

import java
from MethodAccess call
where call.getMethod().hasName("exeCmd")
select call, "调用 exeCmd 方法"

4. 完整的数据流分析

4.1 定义sink点

predicate deser(Expr arg) {
  exists(MethodAccess des |
    des.getMethod().hasName("deserialize")
    and arg = des.getArgument(0)
  )
}

4.2 使用RemoteFlowSource

CodeQL提供了RemoteFlowSource来覆盖所有不受信任的远端输入:

override predicate isSource(DataFlow::Node source) {
  source instanceof RemoteFlowSource
}

4.3 完整QL规则

/**
 * @name Unsafe shiro deserialization
 * @kind path-problem
 * @id java/unsafe-shiro-deserialization
 */
import java
import semmle.code.java.dataflow.FlowSources
import DataFlow::PathGraph

// 定义反序列化sink点
predicate deser(Expr arg) {
  exists(MethodAccess des |
    des.getMethod().hasName("deserialize")
    and arg = des.getArgument(0)
  )
}

// 配置类
class ShiroUnsafeDeserializationConfig extends TaintTracking::Configuration {
  ShiroUnsafeDeserializationConfig() {
    this = "StrutsUnsafeDeserializationConfig"
  }
  
  // 定义source
  override predicate isSource(DataFlow::Node source) {
    source instanceof RemoteFlowSource
  }
  
  // 定义sink
  override predicate isSink(DataFlow::Node sink) {
    exists(Expr arg |
      deser(arg)
      and sink.asExpr() = arg
    )
  }
}

// 查询数据流路径
from ShiroUnsafeDeserializationConfig config, 
     DataFlow::PathNode source, 
     DataFlow::PathNode sink
where config.hasFlowPath(source, sink)
select sink.getNode(), source, sink, "Unsafe shiro deserialization", 
       source.getNode(), "this user input"

5. 漏洞利用链分析

  1. 入口点IndexController类中可以设置request参数,通过cookie传入
  2. 处理流程:cookie经过一系列处理后到达deserialize方法
  3. 危险操作:如果能将exeCmd方法放入反序列化中,就能完成利用

关键点:

  • deserialize方法的bytes参数是sink点
  • 远端输入(如cookie)是source点
  • 数据流从source流向sink即构成漏洞

6. 总结

通过CodeQL分析Shiro反序列化漏洞的关键步骤:

  1. 识别反序列化入口点(实现Serializable的类)
  2. 定位关键方法(serialize/deserialize
  3. 查找危险方法调用(如exeCmd
  4. 定义source和sink
  5. 分析数据流路径
  6. 确认漏洞存在条件(数据从source流向sink)

这种分析方法不仅适用于Shiro,也可应用于其他Java反序列化漏洞的分析。

Shiro反序列化漏洞的CodeQL分析教学文档 1. 反序列化漏洞分析基础 在分析Shiro反序列化漏洞时,CodeQL审计的核心是寻找 source (入口点)和 sink (危险操作点)。 1.1 反序列化入口点(source) 反序列化漏洞的入口点通常是实现了 Serializable 接口的类: 这条查询会找出所有实现了 Serializable 接口且来自源代码的类。 2. 定位关键类和方法 2.1 查找特定类的实例化 要查找 User 类的实例化: FindUser 类构造函数验证当前类是否是 User 类的引用 from ClassInstanceExpr clie 获取所有实例化对象 where 条件判断实例化对象是否为 User 类的实例 2.2 查找关键方法 查找 Tools 包下的三个关键方法: 3. 查找危险方法调用 查找所有调用 exeCmd 方法的位置: 4. 完整的数据流分析 4.1 定义sink点 4.2 使用RemoteFlowSource CodeQL提供了 RemoteFlowSource 来覆盖所有不受信任的远端输入: 4.3 完整QL规则 5. 漏洞利用链分析 入口点 : IndexController 类中可以设置 request 参数,通过cookie传入 处理流程 :cookie经过一系列处理后到达 deserialize 方法 危险操作 :如果能将 exeCmd 方法放入反序列化中,就能完成利用 关键点: deserialize 方法的 bytes 参数是sink点 远端输入(如cookie)是source点 数据流从source流向sink即构成漏洞 6. 总结 通过CodeQL分析Shiro反序列化漏洞的关键步骤: 识别反序列化入口点(实现 Serializable 的类) 定位关键方法( serialize / deserialize ) 查找危险方法调用(如 exeCmd ) 定义source和sink 分析数据流路径 确认漏洞存在条件(数据从source流向sink) 这种分析方法不仅适用于Shiro,也可应用于其他Java反序列化漏洞的分析。