MOCGuard Automatically Detecting Missing-Owner-Check Vulnerabilities in Java Web Applications

Abstract

Missing-Owner-Check (MOC) 使网站面临未经授权的访问和数据泄露

提出了一种新的端到端漏洞分析方法称为 MOCGuard

与相关技术不同的是,MOCGuard 从以数据库为中心的新角度分析了 MOC 漏洞。MOCGuard 首先应用数据库结构分析来推断用户表和用户拥有的数据。然后,MOCGuard 跨 Java 和 SQL 层执行不安全的访问检查。

成功地发现了161个零日 MOC 漏洞,从而分配了73个CVE

Introduction

为了检测真实 Java Web 应用程序中的 MOC 漏洞, 一种直观的漏洞检测思想是识别用户拥有的数据并审核其相应的所有者检查,即验证所访问的数据是否属于当前登录的用户。虽然这种想法是直截了当的,但在实践中很难直接利用或扩展现有技术来实现目标。

Q1:从现代 Web 应用程序中可用的数千个变量和数据项中确定用户拥有的数据是很困难的,如何在 JavaWeb 应用程序的上下文中推断用户拥有的数据?

Q2:访问用户拥有数据时,如何检测所有者校验是否缺失?即便能成功识别所有用户拥有数据,判断对这些数据的访问是否安全依然困难。尤其是所有者校验实现方式灵活,可在 SQL 层(例如 WHERE 子句)或 Java 层(例如 if 语句)实现

现有技术按其分析用户拥有数据与所有者校验的方式,大体分为两类:

第一类(如 RoleCast、MPChecker)通过代码层启发式识别承载用户拥有数据的关键变量。然而此类启发式在 Java Web 领域无效或不可行。例如,RoleCast 依赖 JSP 文件结构,而这在现代基于模板的 Web 应用中并不适用。MPChecker 则依赖分布式系统特有的系统日志。此外在处理第二个挑战时,它们忽视了 SQL 层的所有者校验,从而可能将大量安全的所有者校验误判为漏洞,导致很多误报。

第二类(如 MACE)基于这样的理解:用户变量在操纵用户拥有数据时至关重要。因此,现有技术从手工标注的用户变量(存储用户身份)向 SQL 语句进行数据流跟踪以识别用户拥有数据。然而,这种方法仍然受限:其一,严重依赖对用户变量的人工标注,费时且易错;其二,仅通过分析 SQL 语句从用户身份推断用户拥有数据的结果不完整。在实践中,应用数据库设计往往复杂,很多用户拥有数据是通过数据元素 ID 而非用户身份被操纵。因此,这种粗粒度方法会带来较高的漏报(在我们的数据集中为 70.81%),从而显著影响 MOC 问题的有效检测。

本文提出 MOCGuard,一种用于检测 Java Web 应用中 MOC 漏洞的新型安全分析方法。其关键观察在于:现代 Java Web 应用需要用关系型数据库管理海量用户数据,数据库结构(如数据表)通常被精心设计。这意味着复杂的数据库结构编码了其语义,既反映用户拥有数据的关键细节,也包含相应的所有权信息。据此,我们设计了一种新型的数据库中心化分析技术来理解用户拥有数据及其对应用户,并在 Java 与 SQL 两层验证所有者校验的安全性。

MOCGuard 包含两个主要阶段:第一阶段进行数据库语义分析以定位存储用户信息的用户表,继而推断承载用户拥有数据的其他表。在用户拥有数据推断中,MOCGuard 采用数据结构分析与跨层代码分析的双路径方法。第二阶段,MOCGuard 在已经识别的用户拥有数据之上,从 SQL 与 Java 两层识别所有者校验并审计其安全性。MOCGuard 有效验证保护用户拥有数据所必需的所有者校验是否到位,从而识别潜在不安全访问点并有效发现 MOC 漏洞。

在 30 个常用开源 Java Web 应用与 7 个工业应用上评估了 MOCGuard 的有效性与性能。MOCGuard 成功发现 161 个(已确认)高风险零日 MOC 漏洞。相较最新工作(改进后的 Java 版 MACE),MOCGuard 多检出 114 个漏洞,且在精度上提升 31.31%,召回上提升 242.55%。这些新发现的 MOC 漏洞可能被用于窃取隐私、删除应用数据,严重破坏数据机密性与完整性;攻击者还可利用支付劫持漏洞进行未授权交易,造成重大经济损失,目前已分配 73 个 CVE。

Problem Statement

MOC Vulnerability Definition

先给出若干与 MOC 相关的重要概念:

  • 定义 1(用户):$U$ 为目标 Web 应用 $A$ 中的用户集合。$U = \sum_{i=1}^{m} u_i$
  • 定义 2(用户拥有数据):$D$ 为被用户 $U$ 拥有、由 $A$ 存储与管理的丰富数据集合。$D = \sum_{j=1}^{n} d_j$
  • 定义 3(数据所有权):$own(u_i, d_j)$ 表示在 $A$ 的语境下,数据 $d_j$ 属于用户 $u_i$。
  • 定义 4(数据访问):$access(u_i, d_j)$ 表示用户 $u_i$ 对数据 $d_j$ 的一次访问操作。

据此,我们定义 MOC 漏洞

  • 定义 5(MOC 漏洞):$\exists\ u_i \in U,\ \exists\ d_j \in D,\ \neg\operatorname{own}(u_i,d_j)\ \land\ \operatorname{access}(u_i,d_j) \Rightarrow 存在 MOC(u_i, d_j) 漏洞$

Threat Model

对用户拥有数据的访问与操作应通过所有者校验进行验证。否则,攻击者即可未授权访问他人敏感资源。

Real-World MOC Example

图1
第 3 行 addressMapper.delete() 执行 SQL 删除由变量 addrId 表示的用户拥有数据,但未验证 own(userId, addrId),从而导致严重的 MOC 漏洞,破坏系统的完整性与可用性。第 4 行展示了安全补丁:在 SQL 的 WHERE 子句中增加 userId = #{userId},从而保证所有者一致,修复漏洞。

Detection Challenges

一般来说,MOC 漏洞检测可遵循其定义,从以下两个关键视角展开:

如何推断 Java Web 应用中的用户拥有数据?识别用户拥有数据是检测 MOC 漏洞的第一步,任何差错都会直接导致误报或漏报。挑战在于,用户拥有数据常由程序中的变量表示或访问(例如 addrId),而应用中变量繁多,定位这些特定变量并不容易。

访问用户拥有数据时,如何检测所有者校验的缺失?在识别出用户拥有数据后,需要分析对其访问是否安全。直觉上,源-汇(source-to-sink)分析很适合:首先我们可以识别能操纵用户拥有数据的操作作为 MOC sink,然后我们需要从程序入口分析哪些 MOC sink 对于用户输入 MOC source 是可达的,并判断从 MOC source 到 MOC sink 的程序路径上是否存在适当的保护(所有者校验)。

实现对于 MOC 漏洞的高精度检测并不容易,主要原因在于:

图2
在源到汇路径上识别所有者校验很困难。Java 语言没有内建的“所有者校验函数”,而是由开发者以多种灵活方式实现。除了 SQL 层的 WHERE 条件外,还可能在 Java 代码层通过 if 判断实现(见图2 第 5–7 行)。

即便识别到“有校验”,还需判断是否为合适的校验。系统中可能存在多类用户身份,不同数据的所有者不同(例如电商应用中商品属于卖家、订单属于买家)。如果仅粗粒度地检查“是否存在某种保护”,而不关注保护是否与目标数据的所有权匹配,会直接降低检测准确性。

Existing Techniques and Limitations

近年来已有一些工作探索保护用户拥有数据的技术,但在扩展到 Java Web 应用时存在明显局限。

第一类工作(如 RoleCast、MPChecker)基于代码层启发式识别关键变量,但这在 Java Web 场景下无效或不可行:RoleCast 依赖 JSP 文件结构,而现代应用普遍基于模板与框架(如 Spring Boot);MPChecker 依赖特定格式的运行日志,这在 Java Web 应用中并不存在。此外,它们忽视了 SQL 层的所有者校验,容易带来大量误报。

第二类工作(如 MACE)认为用户变量是操纵用户拥有数据的关键桥梁:需手工标注存储用户身份的变量(如 PHP 的 $SESSION),再跟踪其流向 SQL 语句以识别用户拥有数据。该方法局限在于:强依赖人工标注(费时费力,Java 中缺少 PHP 式“超全局变量”,标注更难);且仅凭用户变量推断用户拥有数据不完整,因为现实中大量用户拥有数据是通过数据元素 ID 而非用户身份被操纵,导致高漏报(我们数据集中 70.81%)。

MOCGuard Methodology

在代码层,用户与数据的所有权关系并不显式

提出一种数据库中心化的方法,天然更适合应对这一挑战。受 Spider-Scents 启发,核心思想是:Web 应用需管理大量用户拥有数据,因此其数据库结构(表的组织与字段)往往经过精心设计。这意味着复杂的数据库结构编码了丰富的依赖关系,反映了数据的关键细节及其所有权信息。

Key Insights and Observations

图3
观察 1:数据库结构包含用户凭据,有助于识别用户。 复杂 Web 应用通常提供注册/登录功能;为在登录时校验身份,开发者会在数据库中存储每个用户的认证相关信息。我们将包含认证数据的表称为用户表(user-table)。列名的语义往往显露了认证过程的线索,例如 username、password 等,以及主键 id 作为唯一标识(图3)。

观察 2:数据库结构可据用户表显式推断用户拥有数据。 为方便记录用户拥有的数据 D 及其对应用户 U,数据库会把 D 与 U 存在同一张或相关表中,并通过外键等结构性连接与用户表关联。我们把所有包含用户拥有数据的表称为用户拥有表(user-owned tables)。例如图3中 order 表显式包含 user_id 外键,指向用户表 member(id)。

图4
观察 3:也存在与用户表隐式关联的用户拥有数据(数据库范式化所致)。 开发者经常通过代码在不同的用户拥有表之间搬运数据:先从数据库读数据到代码变量,再把这些变量作为参数传给执行其他数据库操作的方法。我们将这类通过敏感变量串联起来的相关表称为隐式用户拥有表。图4展示了:从 order 表按 user_id 查出若干 id 保存到变量 ids,再用 ids 去 order_item 查询 order_id in #{ids}。据此可把 order_item 识别为隐式用户拥有表。敏感变量对于后续识别所有者校验也很关键。

图5
观察 4:对用户拥有数据的操作若缺少所有者校验保护,将导致不安全访问。 开发者应在访问数据 D 时,验证请求者与数据所有者是否一致(所有者校验),这一校验可能出现在 SQL 层(WHERE)或 Java 层(if)。若校验缺失或不完整,恶意用户即可未授权访问他人数据。图5展示了一个真实 0day:order_item 被识别为用户拥有表,但 selectByOrderId 的访问没有所有者校验,从而导致不安全访问。

Approach Overview

基于上述观察,提出数据库中心化方法,从“推断用户拥有数据”与“审计访问安全性”两大视角有效开展 MOC 检测。流程分两阶段:

用户拥有数据推断:首先进行数据库语义分析识别用户表,随后通过两条路径识别用户拥有表:其一基于外键关系识别显式用户拥有表,其二通过跨层代码分析识别隐式用户拥有表。在隐式表推断中,我们将 Java 层的数据流与 SQL 层的列/语句建立关联,追踪从用户拥有表读出的敏感变量如何流入其他 SQL 的 WHERE 条件,从而把被条件约束的目标表也纳入用户拥有表集合。

不安全访问检测:在识别出用户拥有表之后,开展源到汇分析:

  • 识别所有访问用户拥有表的数据库操作作为 MOC 汇点(sink);
  • 沿调用图定位这些汇点的调用位置,自后向前(backward)追踪其参数的数据依赖直至程序入口,识别 MOC 源点(source);
  • 分别在 SQL 层与 Java 层识别所有者校验,判断每条源到汇路径上是否存在恰当的所有者校验;若缺失,则报告 MOC 漏洞。

更具体地:

  • SQL 层校验:检查访问用户拥有表的 SQL 的 WHERE 子句,是否对用户列(如 user_id)施加了由不可被用户控制的变量提供的约束;若仅约束了“用户拥有列”(如对象 ID),而未约束“用户列”,则记为缺少 SQL 层所有者校验。
  • Java 层校验:识别在数据库操作前的 if 条件等常见权限判断,要求:其一,判断中一侧值来自相关 SELECT 的返回(即用户拥有数据对应记录中的“用户列”或其 getter);其二,另一侧为当前已登录用户标识,且不可被用户伪造控制;其三,若不匹配则通过抛异常/返回等方式中断控制流。

最终,若在某条源到汇路径上既无 SQL 层校验,也无 Java 层校验,则报告 MOC 漏洞。

Running Real-World Example

以开源高星电商系统 mall 为例(开源社区 Star 数 >70k)。我们在该系统中确认了一个关键 0day(CVE-2023-33***),允许远程攻击者不受限地访问所有用户拥有数据。

  • 推断显式用户拥有表:根据图 3,凭借列名关键词匹配(如 usernamepassword),识别 member 为用户表;解析建表 SQL 的外键约束,识别 order.user_id → member.id,因而 order 为显式用户拥有表。
  • 推断隐式用户拥有表:如图 4 所示,selectByUserIdorder 查询 id 集合存入 Java 变量 ids(敏感变量),随后 ids 作为参数流入 selectByOrderIds 且在 SQL 中用于 where order_id in #{ids},从而把 order_item 识别为隐式用户拥有表。
  • 检测 MOC 漏洞:图 5(b) 中 /detail 为入口,参数 orderId 经数据流进入 selectByOrderId,其 SQL(图 5(a))访问了前述用户拥有表 order_item。检查发现,路径上在 SQL 层与 Java 层均缺失所有者校验。于是,恶意用户仅需提供任意订单号即可查看他人订单详情,满足 access(ui, dj) 而不满足 own(ui, dj),构成 MOC 漏洞。

MOCGuard Design

图6

User-owned Data Inference

User Table Identification

在此阶段,MOCGuard 进行数据库语义分析以推断用户表。具体而言,MOCGuard 首先解析目标应用中的 SQL 文件,以定位所有表与列。SQL 文件指以 .sql 为后缀、用于初始化应用数据库结构的文件。值得注意的是,我们的数据集中有 37 个应用提供了用于创建数据库的 .sql 文件。

随后,MOCGuard 通过关键词匹配来识别用户表。我们发现,Web 开发者常用相似的列命名风格来存储凭据与认证信息。因此,我们构建了一份可扩展的关联关键词列表(例如:password),以定位数据库表中的认证相关列。这些关键词来源于 Key Insights and Observations 一节的观察:用于表示登录凭据的列名在不同应用中往往有一致的命名词干,例如“password”。受此启发,我们的词典包含三个命名用户登录凭据的常用词干:passtokenauth。我们可直接使用这些关键词搜索表列,从而找到用户表。进一步地,为提升匹配率,MOCGuard 采用程序分析定位到对这些表进行数据操作的数据库调用点,并提取其调用方法名;若方法名与认证相关,则这些方法访问的对应数据库表也可判定为用户表。

Foreign Key Analysis

图7

在此阶段,在已经找到用户表的基础上,MOCGuard 利用外键分析来推断“显式的”用户所有表。第一步是提取目标应用中表与表之间的外键。为确保数据库中数据存储的参照完整性并提升性能,开发规范通常建议为与其他表存在数据依赖的列添加外键。通常,这些外键会在 .sql 数据库文件中被显式定义。因此,MOCGuard 通过解析用户提供的数据库文件,抽取目标应用中的外键关系。具体而言,MOCGuard 从数据库文件中提取 SQL 语句并对 CREATE TABLE 语句进行语法分析;随后,借助 FOREIGN KEYREFERENCES 子句识别两个表之间的外键关系。以图 7 为例,通过解析建表语句中外键子句(第 5–6 行),MOCGuard 识别出 comments 表中的 user_id 列与 member 表中的 id 列存在外键关系。随后,借助抽取到的外键约束,MOCGuard 将那些经由外键与用户表主键相关联的表识别为显式用户所有表。

Cross-layer Code Analysis

第三阶段,MOCGuard 通过跨层代码分析,进一步在应用内部推断“隐式的”用户所有数据,有效衔接 SQL 层与 Java 层之间的数据流。

— 在 Java 层,应用数据流分析。一个重要问题是:工具中的数据流“断边”(broken edges)会显著影响数据流分析的有效性。为缓解这一问题,我们通过纳入额外的污点(taint)传播步骤来扩展数据流能力,并为典型断边场景(即 transfer 规则)建模。例如:针对 Java 中常用的 valueOf 方法,MOCGuard 在该方法参数与其返回值之间新增数据流边,允许 valueOf 的参数污染其返回值;再如,针对常见的 for-each 语句 for (Long num : nums),其会隐式声明局部变量 num,MOCGuard 在 numsnum 之间添加数据流边。

— 在 SQL 层,MOCGuard 建立流入/流出 SQL 语句的变量与相应列(例如数据库操作所查询的列)之间的数据流关联。具体而言,SQL 层数据流可分为“流出(outflows)”与“流入(inflows)”。对于流出,MOCGuard 识别并抽取通过 SELECT 语句查询到的表与列,并在 Java 代码中用以存放结果的变量与这些 SQL 语句所查询的列之间建立数据流关联。需要注意的是,MOCGuard 主要关注 SELECT 语句,因为其他类型(如 UPDATEDELETE)的返回值通常仅表示操作成功与否,而非提供数据输出。

通过上述两级分析,位于用户所有表中的数据可被视作“用户所有数据”。通过隐式用户所有表分析,还可识别出敏感变量,这对下一阶段的所有者校验分析至关重要。

隐式用户拥有表推断算法

算法1

首先,MOCGuard 将数据库操作与应用程序内的 SQL 语句相关联,并提取所有与推断的显式用户所属表交互的数据库操作。如算法1的第11-12行所示,RetrieveSQLResult 函数检索所有操作显式用户拥有表的数据库操作。

接下来,MOCGuard 将这些操纵用户拥有表的数据库操作的调用函数的返回值作为跨层数据流分析的起点,将其他 SQL 语句的 WHERE 子句中的变量作为终点。算法1的第1-7行描述了 Cross Layer Analysis 函数。该功能执行跨层数据流分析,以跟踪 Java 层和 SQL 层之间的可变数据流,有效地跟踪从起点到这些端点的所有数据流。

最后,基于跨层数据流分析的结果,MOCGuard 将与显式用户拥有表有数据流关联的表识别为隐式用户拥有表(算法1的第14-19行)。

MOC Vulnerability Detection

Source-to-Sink

第一步,分析目标应用中的所有 SQL 语句,筛选出那些查询用户所有表的语句;随后,将在 Java 代码中操纵这些语句的数据库操作视为 MOC sinks。
第二步,使用调用图定位这些 MOC sink 的调用点;沿调用点对其参数执行后向数据流分析,回溯至程序入口以进一步确定 MOC sources。
第三步,MOCGuard 从 Java 代码层与 SQL 层两级分析,判断每条 source-to-sink 路径是否具有适当的所有者校验保护,从而检测 MOC 漏洞。

Owner Check Identification

SQL 层所有者校验。开发者通常通过 SQL 语句的 WHERE 子句实现所有者校验。因此,针对每条查询了用户所有表的 SQL 语句,MOCGuard 分析其 WHERE 子句是否仅包含用户所有列(owner columns)而不包含用户列(user columns)。若满足该条件,则操纵该 SQL 语句的数据库操作被标记为缺少 SQL 层所有者校验保护。

Java 层所有者校验。 开发者通常在代码层使用 if 条件语句来保护对表示用户所有数据的变量之操作。MOCGuard 要求:作为比较一方的实参必须与数据库操作的返回值存在数据流关联,并借助已推断的用户列判断该实参是否(通过 Java getter)访问了表示用户列的类字段;而另一个比较实参必须为用户不可控。对于开发者自定义的包装函数,MOCGuard 通过分析已识别的 if 语句是否后支配(post-dominate)函数入口点来判断其是否为所有者校验的包装。最终,满足上述条件的条件语句被视为所有者校验。

MOC Vulnerability Determination

基于已识别的所有者校验与 source-to-sink 路径,MOCGuard 从 Java 代码与 SQL 两层对目标应用进行两级分析以检测 MOC 漏洞:对每条路径检查是否存在保护用户所有数据的所有者校验;在 SQL 层检查 SQL 语句是否包含 SQL 层校验;在 Java 层检查是否在数据库操作的控制流内实施了 Java 层校验。若两类校验均不存在,则报告 MOC 漏洞。

Evaluation

评估围绕以下四个研究问题展开:
• RQ1: MOCGuard 在真实应用中检测 MOC 漏洞的有效性如何?
• RQ2: 与当前最先进方法相比,MOCGuard 的有效性如何?
• RQ3: MOCGuard 所考虑的不同层面的所有者校验分别有何贡献?
• RQ4: MOCGuard 的端到端分析效率如何?

Experimental Setup

实现。 我们实现了 Java Web 应用场景下的 MOCGuard 原型。目前原型支持常用 Java Web 技术,如 J2EE Servlets 与 Spring 框架;在数据库操作方面,原型可处理 JDBC API 以及常用 ORM 框架(如 MyBatis 与 Hibernate)。在各类静态分析任务中,我们使用了 CodeQL 静态分析框架;涉及数据库语义与外键分析的任务则采用 Python 实现。具体地,CodeQL 脚本主要用于数据流分析与采集关键代码/数据库特征;原型将 CodeQL 脚本获取的信息存入 SARIF 文件,再由 Python 脚本解析做进一步分析。整个原型共 4,520 行代码。所有实验在一台 Ubuntu 20.04 机器上运行(64 核 CPU、245 GB 内存)。

数据集。 我们的数据集包含 30 个开源 Java Web 应用与 7 个工业 Java Web 应用。对开源部分,我们基于如下步骤从流行的开源仓库(如 GitHub)收集 30 个广泛使用的 Java Web 应用:先使用 GitHub 的 Java 语言过滤并按 star 数排序以反映流行度(基于 star 收集开源应用作为数据集是常见做法);再使用多个关键词从不同类别(CMS、博客、开发、电子商务等)收集开源 Web 应用;最后选取 star 最高的 30 个应用,并剔除教程类应用(演示/学习项目)以确保应用的实用性。最终,我们的数据集中包含 18 个 star 数超过 1k 的应用与 4 个 star 数超过 10k 的应用,覆盖电商网站、内容管理系统、博客系统、后台管理系统与开发平台等多个领域,我们认为具有代表性;且其中大多数已被现有研究使用,从而验证数据集的可靠性。
此外,我们还纳入了 7 个工业 Java Web 应用。为在工业环境中评估 MOCGuard 的有效性,我们与一家为超过十亿用户提供服务的全球领先科技公司合作。出于代码安全考虑,该公司选择了其 7 个代表复杂业务场景的核心应用并应用 MOCGuard 进行分析。因此,我们相信对如此具有代表性的数据集进行评估可以有效衡量 MOCGuard 的精准度,体现其通用性与有效性。

Effectiveness: MOC Detection (RQ1)

表1
表2
结果概览。 表 1 展示了 MOCGuard 的详细检测结果。总体上,MOCGuard 成功识别出 180 处缺少适当所有者校验的易受攻击 MOC sink,并据此报告潜在的 MOC 漏洞。最终,经手工构造 PoC 并测试,我们在整个数据集中确认了 161 个 MOC 漏洞,召回率达到 89.44%
表 2 展示了这些漏洞在开源与工业应用中的分布细节。具体来看,在 30 个开源应用中,MOCGuard 在 17 个应用内共报告 128 个 MOC 漏洞,其中 116 个为真阳性,准确率 90.63%;在 7 个工业应用中,MOCGuard 共报告 52 个 MOC 漏洞,准确率 86.54%

漏洞验证。 为验证工具报告的准确性,我们为每个应用建立了运行环境。开源应用在漏洞验证阶段采用本地部署测试;工业 Web 应用则与合作公司在镜像环境中开展测试。需要说明的是,此过程不涉及任何用户隐私或敏感数据。
此外,MOCGuard 的输出提供了验证所需的关键细节,包括从用户输入到数据库操作的精确路径,以及用户可控参数。这些报告有助于高效构造 PoC。例如,针对图5所示的 MOC 漏洞,输出包含访问端点("/detail")的路径与用户可控参数(orderId),因此可直接据此构造 PoC:/detail?orderId=${他人订单号}

漏洞披露。 在使用特制利用进行评估后,我们认为这些漏洞具有严重安全风险。对于开源应用,攻击者可利用这些漏洞导致用户隐私泄露,甚至删除应用中存储的数据,严重破坏数据的机密性与完整性。这些安全事件凸显了有效漏洞检测机制以保护用户隐私与资产的关键性。因此,我们已将所有确认的漏洞及时报告给相关开源应用的开发者。截至目前,已有 73 个漏洞获配 CVE 标识符。对于工业应用,检测到的 45 个 MOC 漏洞均为新发现,这些漏洞对公司的数据安全构成重大威胁,可能导致大量员工与用户信息泄露,并对应用的业务功能产生不利影响。这些发现展示了 MOCGuard 的实践价值。

误报分析。 接下来,我们介绍 MOCGuard 在漏洞检测中报告的 19 个误报。经深入分析,我们发现它们均源于同一原因:难以判定开发者对数据可访问性的意图。具体地,这些误报均来自数据库查询操作(SELECT 类型)。以电商网站为例:当卖家更新商品价格时,必须进行所有者校验以防其任意修改他人商品价格;然而买家可以查看任意商品的价格,这意味着“查看商品价格”的数据库操作并不需要所有者校验。因此,就产品表而言,MOCGuard 会将通过 SELECT 查询产品信息的数据库操作视为 MOC 漏洞,从而可能产生误报。我们认为,辨别开发者关于资源访问的意图是静态分析中的固有挑战,已有许多工作提及。

Effectiveness: Comparison (RQ2)

在此实验中,我们将 MOCGuard 与最先进技术(MACE)在整个数据集上进行对比。
基线设置:MACE-Java。 MACE 是当前最先进的方法,其在检测 MOC 漏洞时同时考虑了代码层与 SQL 层的保护。因此我们将其作为基线。鉴于 MACE 为 PHP 设计且非开源,我们遵循其论文中方法学实现了 Java 版本——MACE-Java。然而,我们发现需要审慎处理若干难点:
将 MACE 应用于 Java 并非易事,挑战来自两方面:其一,MACE 在 Java Web 应用语境下所需的输入极难提供。MACE 依赖 PHP 语言特性(如超全局变量),并需要手工注解来识别用户;然而 Java 中不存在类似超全局变量,要在成千上万个 Java 变量中手工识别表示“用户”的变量显然困难。因此,我们使用由 MOCGuard 自动推断的用户列作为 MACE-Java 的输入。其二,相较 MOCGuard,MACE 的不一致性分析检测策略实现需要大量工程工作。由于开发者的编码实践灵活,要判定两条路径上的所有者校验是否一致是复杂任务。为此,MACE-Java 首先形式化抽取到的所有者校验,以便在不一致分析中准确判定不同路径间校验的一致性;具体地,MACE-Java 从所有者校验中抽取变量名与方法名,并用具体类名替换变量名。

表3
表3描述了 MOCGuard 和 MACE-Java 在整个数据集上的有效性比较结果。总体而言,MOCGuard 准确度超过 MACE-Java 31.31%,召回率超过 242.55%。值得注意的是,针对真实的 161 个漏洞,MOCGuard 成功地识别了所有的漏洞,而 MACE-Java 仅检测到 47 个MOC漏洞,并报告了 22 个误报。这清楚地说明了 MOCGuard 在有效识别 MOC 漏洞方面的卓越能力。

MACE-Java 共报告 22 例假阳性。经过严格的分析,我们发现除了 MOCGuard 报告的 10 个病例外,MACE-Java 还发现了 12 个额外的假阳性。造成这种情况的主要原因是 MACE-Java 缺乏理解用户和用户拥有的数据之间的所有权关系的能力。因此,在数据库操作访问用户拥有的数据时,MACE-Java 无法准确判断是否存在所有者检查,最终导致误报。

对于 114 个假阴性,我们现在详细说明 MACE-Java 未能检测到它们的原因。如前文所述,MACE 采用不一致的保护分析策略来检测漏洞。然而,这种策略依赖于开发人员的假设,即开发人员的目的是得到大多数正确的检查,只有一些偶然的检查被错误地执行。因此,在开发人员未能正确执行任何检查的情况下,MACE 将无法检测出目标应用中的所有漏洞。正如文献中所描述的那样,超过 60% 的漏洞是由程序中任何地方都没有被保护的操作触发的。因此,在 MACE-Java 中导致了 114 个漏报。

Ablation Study (RQ3)

在本实验中,我们分别在 Java 代码层和 SQL 代码层禁用了 MOCGuard 的所有者检查分析模块,以证明其对 MOC 漏洞高精度检测的重要性。两个变体的具体内容如下:

表4
表4给出了 MOCGuard 及其两个变体在整个数据集上的比较结果。显然,Java 代码层和 SQL 层的所有者检查分析模块对于 MOCGuard 精确检测漏洞是至关重要的。当 SQL 层的所有者检查分析失效时,检测精度下降了 28.57 %。同样,当 Java 代码层的所有者检查分析被禁用时,检测精度也显著下降了 22.74%。新引入的假阳性将大大增加对 MOCGuard 终端用户的分析要求,涉及大量的人力工作。

Efficiency (RQ4)

在本实验中,我们评估了 MOCGuard 在整个数据集上执行端到端的分析的效率。MOCGuard 总共花费约 47min 完成了 37 个目标应用的分析任务,平均耗时 76.22s/次。分析的效率归功于我们的数据库语义分析方法。与传统的静态分析相比,MOCGuard 不需要对目标应用的源代码进行重量级的分析,也不需要依赖任何人力来提供特定应用的输入。相比之下,MACE 的相关工作,正如其原始论文所描述的那样,需要人工标注,每个应用程序平均需要几十分钟的时间。相比较而言,MOCGuard 在检测 MOC 漏洞时更加轻量和高效。

Case Study

Payment Hijacking in mall Application (over 70k stars on GitHub)

图8
mall 是一个开源且应用广泛的电子商务应用,在GitHub上有超过7万的star。如图8所示,MOCGuard 在应用内检测到一个可能导致支付劫持的 MOC 漏洞。易受攻击的数据库操作 updateByPrimaryKey 位于 pay() 方法内,该方法操纵用户拥有的表顺序。此数据库操作负责设置订单的付款状态,通过更新语句(第8行)修改付款状态列来实现。setPayStatus 方法的参数被设置为’1’,这意味着订单已经支付(第5行)。由于缺乏对订单的所有者检查,攻击者可以输入任意订单编号,将无偿订单设置为有偿状态,这会给商家造成严重的经济损失。考虑到这种脆弱性带来的广泛潜在损害,我们立即向开发人员报告了这一关键问题,并收到了CVE(CVE-2023-49***)。

Arbitrary Order Details Leakage in platform Application (over 20k stars on Gitee)

图9
图9展示了该应用中的一处 MOC 漏洞,可能导致任意订单详情被窃取。在 detail() 方法中,用户可通过提供参数 orderId 来查询订单中的商品,该参数存储于 nideshop_order_goods 表。由于开发者疏忽,代码未对 nideshop_order_goods 表进行所有权验证。攻击者可通过遍历数据库中所有已存储的订单号,任意访问其他用户订单中的商品。鉴于漏洞严重性,我们第一时间向该应用的开发者报告并积极讨论修复方案。最终,我们获得了一个 CVE 标识(例如 CVE-2023-37***)。

Discussion

适配性。 我们将 MOCGuard 设计为高效检测 MOC 漏洞。由于 Java Web 应用通常将用户所有数据存储在关系型数据库中以保证完整性与可靠性,我们的设计主要面向关系型数据库。目前,MOCGuard 原型已能检测构建在主流关系型数据库管理系统(DBMS,如 MySQL、PostgreSQL)之上的 Java Web 应用中的 MOC 问题。由于 MOCGuard 的方法具有普适性,它也可容易扩展至其他关系型数据库。此外,得益于其以数据库为中心的分析方式,MOCGuard 对编程语言的依赖最小。因此,我们认为该方法的关键思想可扩展至其他领域(包括 PHP 应用)。
所有者推断改进。 与认证相关的语义分析对 MOCGuard 中的“用户列”推断帮助显著。但仍有少量误报源于非常规命名习惯。重要原因之一在于难以判定某个列名是否属于认证范畴,因而难以作为“用户列”识别。未来,我们计划采用更强的分析技术(例如用于理解列名语义的大语言模型(LLM))以缓解此问题。
合法性与伦理。 本研究不存在法律或伦理问题。我们获取源代码在本地进行分析,并将检测到的开源应用漏洞负责任地报告给 CVE 编号机构(CNA),同时协助企业修复工业应用中的漏洞。此外,我们已就发现的 MOC 漏洞与所有开发者取得联系,并将在披露流程中持续沟通。

为什么传统方法不行?
误报漏报原因?