SNMPv3 – Les limites de l’authentification
|Pour faciliter la compréhension de ce billet, je vous invite à lire quelques articles sur SNMPv3 et sur User-based Security Model (USM)… Voici comment fonctionne la procédure d’authentification des messages SNMPv3 émis en utilisant usmHMACMD5AuthProtocol. (avec la fonction de hachage MD5 et non SHA1)
- msgAuthoritativeEngineID : ID permettant d’identifier l’agent SNMP
- msgUserName : Le nom de l’utilisateur
- msgAuthenticationParameters : Variable permettant de contrôler l’authenticité et l’intégrité du message
- wholeMessage : L’intégralité du message SNMP
Signature d’un message SNMPv3
1) Le champ msgAuthenticationParameters est rempli de 12 octets nuls (0x00).
Dans la trame numéro 77 (ci-dessus) le champ msgAuthenticationParameters est 9b1b71e33603a30c125f095d. Pour créer ce paramètre, l’émetteur doit au préalable utiliser une valeur équivalente à 000000000000000000000000.
Note : Le wholeMsg est utilisé pour calculer la valeur du champ msgAuthenticationParameters final. Voilà pourquoi il doit être initialisé à zéro pour ce calcul.
wholeMsg final (Hexa)
30818002010330110204580b8cc70203 00ffe30401050201030431302f041180 001f888062dc7f4c15465c5100000000 02010302017c040475736572040c9b1b 71e33603a30c125f095d040030350411 80001f888062dc7f4c15465c51000000 000400a11e0204334304ff0201000201 003010300e060a2b06010201041e0105 010500
wholeMsg initial (Hexa)
30818002010330110204580b8cc70203 00ffe30401050201030431302f041180 001f888062dc7f4c15465c5100000000 02010302017c040475736572040c0000 00000000000000000000040030350411 80001f888062dc7f4c15465c51000000 000400a11e0204334304ff0201000201 003010300e060a2b06010201041e0105 010500
2) La Secret AuthKey générée à partir de notre mot de passe et de notre msgAuthoritativeEngineID est utilisée pour créer deux autres clés : K1 et K2
a) La clé est étendue avec 48 octets nuls (0x00).
extendedAuthKey (Hexa)
21f1e9d9b30ceaeb84f2812beee685d5
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
b) L’IPAD (à part une horrible tablette), est un masque de 64 octets identiques : 0x36
IPAD (Hexa)
36363636363636363636363636363636
36363636363636363636363636363636
36363636363636363636363636363636
36363636363636363636363636363636
c) K1 est créé en réalisant un XOR de l’extendedAuthKey et de l’IPAD.
K1 (hexa)
17c7dfef853adcddb2c4b71dd8d0b3e3
36363636363636363636363636363636
36363636363636363636363636363636
36363636363636363636363636363636
d) OPAD lui est une succession de 64 octets ayant pour valeur 0x5c.
OPAD (Hexa)
5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c
5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c
5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c
5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c
e) K2 est le résultat d’un XOR entre l’extendedAuthKey et l’OPAD.
K2 (Hexa)
7dadb585ef50b6b7d8aedd77b2bad989
5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c
5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c
5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c
3) K1 et le wholeMsg initial sont concaténés et le hash MD5 digest associé est calculé.
K1 et wholeMsg initial (Hexa)
17c7dfef853adcddb2c4b71dd8d0b3e3 36363636363636363636363636363636 36363636363636363636363636363636 36363636363636363636363636363636 30818002010330110204580b8cc70203 00ffe30401050201030431302f041180 001f888062dc7f4c15465c5100000000 02010302017c040475736572040c0000 00000000000000000000040030350411 80001f888062dc7f4c15465c51000000 000400a11e0204334304ff0201000201 003010300e060a2b06010201041e0105 010500
Hash associé
6a6a830535a7535d20bfebec882d5e4c
4) K2 ainsi que le résultat de la fonction de hachage sur K1+wholeMsg initial sont à leur tour concaténés et le hash MD5 digest associé déduit de cette nouvelle chaîne d’octets.
K2 et Hash associé à K1 (Hexa)
7dadb585ef50b6b7d8aedd77b2bad989
5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c
5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c
5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c
6a6a830535a7535d20bfebec882d5e4c
Hash associé
9b1b71e33603a30c125f095d0ba0a805
5) Le champ msgAuthenticationParameters (pour le moment égal à 000000000000000000000000) est remplacé par les 12 premiers octets du hash associé à K2.
9b1b71e33603a30c125f095d
6) Cet authenticatedWholeMsg est ensuite transmit sur le réseau. Il s’agit de notre paquet SNMPv3 !
Vérification d’un message SNMPv3
Pour vérifier l’intégrité et l’authenticité d’un message, la procédure est sensiblement similaire. Elle consiste à extraire le msgAuthenticationParameters pour le remplacer par 12 octets nuls (0x00). Ensuite il faut simplement réaliser la même procédure que pour l’envoi. En comparant le paramètre d’authentification obtenu avec le msgAuthenticationParameters transmit, on peut déterminer si le message est authentique.
Exploit
L’un des problèmes majeurs est qu’il est possible de déduire la clé secrète uniquement en sniffant le réseau. En effet, le msgAuthoritativeEngineID (0x80001f888062dc7f4c15465c5100000000) est transmis en clair. Il est donc possible de déduire la clé secrète partagée en réalisant un brute force à une seule inconnue ! La commande du package Net-SNMP snmpkey nous permet de calculer la clé secrète :
snmpkey md5 useruseruser 80001f888062dc7f4c15465c5100000000
authKey: 0x21f1e9d9b30ceaeb84f2812beee685d5
privKey: 0x21f1e9d9b30ceaeb84f2812beee685d5
On peut très facilement imaginer un script destiné à brute force cette authentification pour récupérer la clé partagée. Il s’agira d’une lutte contre le temps, plus la clé sera complexe et construite de façon aléatoire et plus la découverte de cette variable sera longue et fastidieuse (oui, comme le mouton !). Quand à trouver le nom d’utilisateur, il est transmis en clair dans le champ msgUserName !
Bien entendu, une attaque par force brute peut être optimisée : utilisation de dictionnaires (très efficace si le mot de passe est intelligible), mutualisation architecturale de l’attaque (CPU + GPU), mutualisation de l’attaque (centaines ou milliers d’ordinateurs dédiés à cette activité : botnet).
D’autres Faiblesses ?
Magic Loop !
On obtient la même clé si l’on répète une même occurrence pour construire le mot de passe. Un étrange phénomène qui diminue légèrement la robustesse de l’authentification…
snmpkey md5 useruser 80001f888062dc7f4c15465c5100000000
authKey: 0x21f1e9d9b30ceaeb84f2812beee685d5
privKey: 0x21f1e9d9b30ceaeb84f2812beee685d5
snmpkey md5 user 80001f888062dc7f4c15465c5100000000
authKey: 0x21f1e9d9b30ceaeb84f2812beee685d5
privKey: 0x21f1e9d9b30ceaeb84f2812beee685d5
Qui c’est ça ?
Le serveur vous indique quand l’utilisateur n’existe pas ! Une aubaine pour brute-forcer cette information ! :)
0x0ff@kali:~# snmpwalk -v3 -l authNoPriv -a MD5 -u FakeUser -A petitmot 192.168.1.31 1 snmpwalk: Unknown user name