flawopen.com/Incidents/capital-one-ssrf-imds-july-2019
Capital One : SSRF et vol d’identifiants IMDSv1
La plainte fédérale alléguait qu’un WAF mal configuré permettait un SSRF vers IMDSv1 et exposait des identifiants temporaires et des données S3 concernant environ 106 millions de personnes. Exigez les jetons et limitez les rôles.[10], [2]
La plainte pénale du DOJ alléguait une chaîne de ce type : Imaginez une réceptionniste qui récupère des documents aux adresses que vous lui indiquez. Un relais de requêtes peut permettre à quelqu’un de l’orienter vers un bureau interne. Elle peut alors obtenir un badge temporaire pour la charge de travail ; ce badge n’ouvre que les pièces autorisées par le rôle associé. L’erreur consistait à supposer que seul le personnel pouvait atteindre le bureau, alors que la réceptionniste pouvait y être dirigée.[10], [8]
Concepts Clés et Termes
SSRF- Falsification de requête côté serveur (
CWE-918) : amener un serveur à envoyer une requête choisie par l'attaquant, qui hérite alors de la position réseau et de la confiance du serveur.[9] Pare-feu applicatif web- Un filtre devant une application web. La plainte pénale alléguait que le WAF mal configuré de Capital One pouvait relayer des requêtes vers des adresses internes.[10]
IMDSv1- Lorsque IMDSv1 est activé, les requêtes de métadonnées n’exigent pas de jeton. Le chemin des identifiants de sécurité d’un rôle IAM renvoie les identifiants temporaires associés à ce rôle.[7], [8]
IMDSv2- IMDSv2 utilise un jeton de session obtenu par une requête PUT. Définissez
HttpTokens=requiredpour rejeter les requêtes IMDSv1 sans jeton ; cela bloque de nombreux relais SSRF limités à un GET simple, sans constituer une défense SSRF complète.[7], [6] Identifiants de rôle IAM- Des clés AWS temporaires délivrées à une instance. Tout ce que le rôle peut faire, le détenteur des clés peut le faire aussi jusqu'à leur expiration.[8]
Correspondances de référentiels
- Référence CWE pour la faiblesse décrite dans ce guide : CWE-918
- D3-OTF Filtrage du trafic sortant — La restriction du trafic sortant décrite dans ce guide limite les destinations accessibles au service ou à l’hôte.
Chronologie de l'Incident
Un chercheur externe signale les données exposées via le programme de divulgation responsable de Capital One.[2]
Capital One annonce l'incident et le FBI arrête une suspecte à Seattle.[1]
Déroulement de l'Attaque Étape par Étape
Le pare-feu relaie une requête forgée
La plainte pénale allègue que le WAF mal configuré pouvait être amené à envoyer des requêtes vers des adresses choisies par l’attaquante, y compris internes.[10]
Les données partent en masse
Des données de demandes de carte de crédit de 2005 à début 2019 ont été copiées : environ 100 millions de personnes aux États-Unis et 6 millions au Canada, dont environ 140 000 numéros de sécurité sociale américains et 80 000 numéros de comptes bancaires associés.[2]
Code Source : Vulnérable vs Sécurisé
Code Flask illustratif, et non la configuration de Capital One. La liste d’hôtes autorisés est le principal contrôle applicatif. Le proxy de sortie obligatoire doit appliquer la politique à l’adresse résolue au moment de la connexion, bloquer les plages privées et link-local, y compris le service de métadonnées, et répéter ces contrôles pour chaque requête et nouvelle tentative ; vérifiez ce comportement dans sa configuration. Les redirections ne sont pas suivies. Une résolution séparée avant la connexion laisserait une course liée au DNS rebinding.[9], [5], [6]
import requests
from flask import Flask, request
app = Flask(__name__)
@app.get("/fetch")
def fetch():
# Le serveur récupère n’importe quelle URL fournie par l’appelant.
url = request.args["target"]
resp = requests.get(url, timeout=5)
return resp.text
from urllib.parse import urlsplit
import requests
from flask import Flask, abort, request
app = Flask(__name__)
ALLOWED_HOSTS = {"images.example-cdn.com", "api.partner.example"}
EGRESS_PROXY = "http://egress-proxy.internal:3128"
# Configurez ce proxy pour appliquer la politique de destination après la résolution DNS.
@app.get("/fetch")
def fetch():
parts = urlsplit(request.args.get("target", ""))
if parts.scheme != "https" or parts.hostname not in ALLOWED_HOSTS:
abort(400, "destination not allowed")
if parts.port not in (None, 443) or parts.username or parts.password:
abort(400, "destination not allowed")
resp = requests.get(parts.geturl(), timeout=(2, 5), allow_redirects=False, proxies={"https": EGRESS_PROXY}, trust_env=False)
if resp.is_redirect:
abort(502, "upstream redirect refused")
return resp.text
Liste de Contrôle de Sécurité pour l'Ingénierie
- ✓Faites passer les récupérations d’URL par un proxy de sortie et appliquez la liste d’hôtes autorisés et la politique de plages IP à la frontière de connexion : ne connectez que l’adresse vérifiée et appliquez aussi la politique aux nouvelles tentatives. Désactivez les redirections ou revalidez chaque saut ; bloquer uniquement le texte 169.254.169.254 ne suffit pas.[9], [4]
- ✓Définissez
HttpTokens=requiredsur chaque instance concernée et vérifiez les instances existantes, car les valeurs par défaut du compte ne mettent pas automatiquement à jour chaque instance. ChoisissezHttpPutResponseHopLimitselon le chemin réseau de la charge de travail ; AWS avertit qu’une valeur de 1 peut poser problème dans les conteneurs. L’exigence du jeton rejette les requêtes IMDSv1 ; la limite de sauts est un réglage réseau distinct, pas le contrôle qui bloque un GET sans jeton.[6], [7] - ✓La plainte pénale allègue que le chemin SSRF exposait le rôle de l’instance. Limitez ce rôle aux actions et ressources nécessaires à la charge de travail afin qu’une fuite d’identifiants ne donne pas accès à des données S3 sans rapport.[10], [8]
- ✓Conseil de détection de FlawOpen : déclenchez une alerte lorsque des identifiants temporaires du rôle sont utilisés hors du réseau de l’instance, puis examinez le contexte de la charge de travail et des journaux d’audit cloud.
- ✓En revue de code, traitez tout chemin où l'utilisateur influence une URL récupérée par le serveur comme un chemin où il influence une requête SQL, et exigez un test de liste d'autorisation.[9], [5]
Sources
- Département de la Justice des États-Unis — arrestation à Seattle d'une employée tech pour vol de données d'une grande société financière (29 juillet 2019)
- Capital One — informations sur l'incident de cybersécurité de 2019
- OCC — sanction civile contre Capital One pour l'incident de 2019 (août 2020)
- Pourquoi le SSRF est-il si dangereux dans le cloud ?
- fetch() peut-il provoquer un SSRF ?
- AWS — Configure the Instance Metadata Service options
- AWS — Use the Instance Metadata Service to access instance metadata
- AWS — Retrieve security credentials from instance metadata
- OWASP — Server Side Request Forgery Prevention Cheat Sheet
- DOJ — United States v. Paige Thompson, criminal complaint (29 July 2019)