Présentation de la vulnérabilité de Mikrotik Winbox. Ou un gros fichier

Bonjour, probablement beaucoup ont entendu parler de la récente vulnérabilité des routeurs Mikrotik, qui vous permet d'extraire les mots de passe de tous les utilisateurs. Dans cet article, je voudrais montrer et analyser en détail l'essence de cette vulnérabilité.
Tout le matériel est fourni à titre informatif uniquement, donc le code qui exploite la vulnérabilité ne sera pas ici. Si vous n'êtes pas intéressé à en savoir plus sur les causes et la structure interne d'une vulnérabilité particulière, vous pouvez continuer à lire.

Commençons


La première chose à commencer est l'analyse du trafic entre le client Winbox et l'appareil
Winbox est une application pour le système d'exploitation WIndows, qui répète exactement l'interface Web et est conçue pour administrer et configurer l'appareil avec le système d'exploitation du routeur à bord. 2 modes de fonctionnement pris en charge, TCP et UDP
Avant de commencer, vous devez désactiver le chiffrement du trafic dans Winbox. Cela se fait comme suit: vous devez activer la case à cocher Outils -> Mode avancé . Après cela, l'interface changera comme suit:


Décochez le mode sécurisé . Lancez Wireshark et essayez de vous connecter à l'appareil:


Comme vous pouvez le voir ci-dessous, après autorisation, un fichier de liste est demandé puis son contenu nous est complètement transféré, il peut sembler que tout va bien, mais regardons le tout début de cette session:


Au tout début, Winbox envoie exactement le même package en demandant le fichier de liste :


Considérez sa structure:

  1. 37010035 - taille de l'emballage
  2. M2 est une constante indiquant le début d'un paquet
  3. 0500ff01 - variable 0xff0005 dans la valeur True
  4. 0600ff09 01 - variable 0xff0006 dans la valeur 1 (nombre de paquets transmis)
  5. 0700ff09 07 - variable 0xff0007 à la valeur 7 (ouvrir le fichier en mode lecture)
  6. 01000021 04 6967374 - liste de chaînes de la variable 0x01000001 de 4 octets (généralement cette variable est responsable du nom du fichier)
  7. 0200ff88 02 ... 00 - un tableau de 0xff0002 avec une taille de 2 éléments
  8. 0100ff88 02 ... 00 - un tableau de 0xff0001 avec une taille de 2 éléments

Du fait de l'inversion du protocole et des fichiers binaires correspondants côté client et serveur, il a été possible de restaurer et de mieux comprendre la structure du protocole par lequel Winbox communique avec le périphérique.

Description du protocole NvMessage

Types de champs (nom: désignation numérique)


  • u32: 0x08000000
  • u32_array: 0x88000000
  • chaîne: 0x20000000
  • tableau_chaînes: 0xA0000000
  • addr6: 0x18000000
  • addr6_array: 0x98000000
  • u64: 0x10000000
  • u64_array: 0x90000000
  • vrai: 0x00000000
  • false: 0x01000000
  • bool_array: 0x80000000
  • message: 0x28000000
  • tableau_messages: 0xA8000000
  • brut: 0x30000000
  • raw_array: 0xB0000000
  • u8: 0x09000000
  • be32_array: 0x88000000

Types d'erreurs (nom: désignation numérique)


  • SYS_TO: 0xFF0001
  • STD_UNDOID: 0xFE0006
  • STD_DESCR: 0xFE0009
  • STD_FINISHED: 0xFE000B
  • STD_DYNAMIC: 0xFE0007
  • STD_INACTIVE: 0xFE0008
  • STD_GETALLID: 0xFE0003
  • STD_GETALLNO: 0xFE0004
  • STD_NEXTID: 0xFE0005
  • STD_ID: 0xFE0001
  • STD_OBJS: 0xFE0002
  • SYS_ERRNO: 0xFF0008
  • SYS_POLICY: 0xFF000B
  • SYS_CTRL_ARG: 0xFF000F
  • SYS_RADDR6: 0xFF0013
  • SYS_CTRL: 0xFF000D
  • SYS_ERRSTR: 0xFF0009
  • SYS_USER: 0xFF000A
  • SYS_STATUS: 0xFF0004
  • SYS_FROM: 0xFF0002
  • SYS_TYPE: 0xFF0003
  • SYS_REQID: 0xFF0006

Valeurs d'erreur (nom: désignation numérique)


  • ERROR_FAILED: 0xFE0006
  • ERROR_TOOBIG: 0xFE000A
  • ERROR_EXISTS: 0xFE0007
  • ERROR_NOTALLOWED: 0xFE0009
  • ERROR_BUSY: 0xFE000C
  • ERROR_UNknown: 0xFE0001
  • ERROR_BRKPATH: 0xFE0002
  • ERROR_UNknownID: 0xFE0004
  • ERROR_UNKNOWNNEXTID: 0xFE000B
  • ERROR_TIMEOUT: 0xFE000D
  • ERROR_TOOMUCH: 0xFE000E
  • ERROR_NOTIMP: 0xFE0003
  • ERROR_MISSING: 0xFE0005
  • STATUS_OK: 0x01
  • STATUS_ERROR: 0x02

Structure des champs dans un package


Au début de n'importe quel champ se trouve son type - 4 octets (3 octets - le but de la variable, plus sur plus tard, 1 octet - directement le type de cette variable), puis la longueur est de 1 à 2 octets et la valeur elle-même.

Tableaux


Au figuré, le tableau peut être décrit par la structure suivante:

struct Array { uint32 type; uint8 count; uint32 item1; uint32 item2; ... uint8 zero; } 

Type (4 octets) / Nombre d'éléments (1 octet) / Éléments (4 octets) / À la fin de l'octet \ x00

Lignes


Les chaînes ne sont pas terminées par null, mais ont une longueur clairement définie:

 struct String { uint32 type; uint8 length; char text[length]; } 

Les chiffres


Le type le plus simple du package, il peut être représenté comme un type de valeur:

 struct u* { uint32 type; uint8/32/64 value; } 

Selon le type, la valeur a une dimension de bit correspondante.

Type booléen


La taille du champ est de 4 octets, l'octet haut est responsable de la valeur (True \ False), les 3 octets inférieurs sont pour affecter la variable

De plus, chaque package contient:

  1. marqueurs spéciaux pour indiquer le début du colis
  2. taille de l'emballage
  3. grands marchés de contrôle des colis


Constantes trouvées


  • 0xfe0001 - Contient l'identifiant de session (1 octet)
  • 0xff0006 - Numéro du paquet envoyé (1 octet)
  • 0xff0007 - Mode d'accès aux fichiers (1 octet)

Modes d'accès aux fichiers

  • 7 - ouvert à la lecture
  • 1 - ouvert pour l'enregistrement
  • 6 - créer un répertoire
  • 4 - lire
  • 5 - supprimer


Maintenant, sachant comment fonctionne le protocole, nous pouvons générer de manière aléatoire les packages dont nous avons besoin et regarder comment le périphérique y répond.

Côté périphérique, l'exécutable / nova / bin / mproxy est responsable du traitement des paquets. Étant donné que les noms des fonctions n'ont pas été enregistrés, j'ai appelé une fonction qui traite le package et prend des décisions sur ce qu'il faut faire avec le fichier file_handler () . Jetez un œil à la fonction elle-même:


PS Le code qui nous intéressera est marqué de flèches.

Étape 1


Lors de la réception d'un package pour ouvrir un fichier en lecture, il démarre le traitement à partir de ce bloc:


Au tout début, le nom du fichier est extrait du package en utilisant la fonction nv :: message :: get <nv :: string_id> () .

Ensuite, la fonction tokenize () divise la chaîne résultante en parties distinctes, en utilisant le caractère " / " comme délimiteur.

Le tableau de chaînes résultant est transmis à la fonction path_filter () , qui vérifie la présence de " .. " dans le tableau de chaînes reçu et, en cas d'erreur, renvoie une erreur ERROR_NOTALLOWED (0xFE0009)


PS ERROR_NOTALLOWED sera également reçu dans la réponse s'il n'y a aucune autorisation de fichier

Si tout va bien, le chemin vers le répertoire webfig ou pckg est concaténé au début du nom de fichier

Étape 2



Si tout s'est bien passé, le fichier s'ouvre et sa poignée est enregistrée dans l'objet global.

Si le fichier n'a pas pu être ouvert, alors dans la réponse nous obtenons une erreur: impossible d'ouvrir le fichier source .


Ainsi, pour recevoir le contenu d'un fichier, 3 conditions doivent être remplies:

  1. Le chemin du fichier ne contient pas " .. ";
  2. Il existe des droits d'accès au fichier;
  3. Le fichier existe et peut être ouvert avec succès.

Essayons maintenant d'envoyer des packages pour tester la fonctionnalité de cette fonction:

 $ ./untitled.py -t 192.168.88.1 -f /etc/passwd Error: SYS_ERRNO => ERROR_FAILED Error: SYS_ERRSTR => cannot open source file $ ./untitled.py -t 192.168.88.1 -f /../../../etc/passwd Error: SYS_ERRNO => ERROR_NOTALLOWED $ ./untitled.py -t 192.168.88.1 -f //./././././../etc/passwd Error: SYS_ERRNO => ERROR_FAILED Error: SYS_ERRSTR => cannot open source file 

Alors! Mais c'est déjà étrange ... Nous nous souvenons que ERROR_NOTALLOWED apparaît si la vérification dans path_filter () n'a pas réussi, sinon nous recevrions toujours un message sur le manque de droits d'accès, mais dans ce dernier cas, il s'avère que le fichier a été recherché dans le répertoire de niveau supérieur?

Essayons de cette façon:

 $ ./untitled.py -t 192.168.88.1 -f //./.././.././../etc/passwd xvM2        1Enobody:*:99:99:nobody:/tmp:/bin/sh root::0:0:root:/home/root:/bin/sh 

Et ça a marché. Mais pourquoi? Jetons un œil au code de la fonction path_filter () :


Le code montre clairement que la recherche de l'occurrence de " .. " dans le tableau de chaînes résultant se produit réellement. Mais alors la partie la plus intéressante, j'ai mis en évidence ce fragment en rouge.
L'essence de ce code est la suivante: si l'élément précédent est également " .. ", la vérification est considérée comme ayant échoué. Sinon, considérez que tout va bien.

C'est-à-dire pour que tout fonctionne, il vous suffit d'alterner " /./ " et " /../ " pour naviguer avec succès dans tous les répertoires et descendre à n'importe quel niveau du FS.


Voyons comment les développeurs de Mikrotik l'ont corrigé:


Comparaison de codes pseudo-C


Maintenant, la sortie du cycle de vérification se produit lors de la première détection de " .. ". Certes, il n'est pas tout à fait clair pour moi pourquoi ils ont ajouté un chèque pour l'entrée d'un point. Et en raison d'un changement dans le mécanisme d'activation de l'utilisateur devel , malheureusement, il n'y a aucun moyen de voir cela dans la dynamique.

Pour résumer


  1. Le système d'exploitation du routeur traite les paquets entrants sans problème avant même l'autorisation de l'utilisateur
  2. En raison d'un filtre incorrect, nous avons accès à n'importe quel fichier

Compte tenu des paragraphes précédents, nous pouvons facilement: créer, supprimer, lire et écrire des fichiers, ainsi que créer des répertoires arbitraires

Il n'est donc pas surprenant que d'avoir accès à tous les fichiers sans autorisation, la première chose qui a été faite a été de lire le fichier avec des mots de passe utilisateur. Heureusement, le réseau dispose de nombreuses informations sur son emplacement et sur la manière d'en extraire des données.

De plus, cette vulnérabilité peut être un excellent remplacement pour la possibilité précédemment connue d'activer le mode développeur, car vous n'avez pas besoin de redémarrer l'appareil, sauvegardez / restaurez le fichier de configuration maintenant.

Source: https://habr.com/ru/post/fr416067/


All Articles