flawopen.com/Incidents/equifax-struts-ognl-rce-july-2017
Equifax : données de près de 150 millions exposées
CVE-2017-5638 permettait une injection OGNL via l’en-tête Content-Type sur le portail Struts non corrigé d’Equifax. L’attaque a duré environ 76 jours et a exposé les données de près de 150 millions de personnes.[1], [4]
Imaginez un logiciel de courrier qui transforme une étiquette d’expédition rejetée en message d’erreur. Si ce message interprète l’étiquette comme un modèle d’instructions actif, l’expéditeur peut faire exécuter ses instructions au logiciel au lieu de signaler une adresse invalide. Struts avait déjà un correctif, mais l’avis d’Equifax a été envoyé à une liste d’administrateurs obsolète et une analyse réseau n’a pas détecté la vulnérabilité sur le portail.[1], [4]
Concepts Clés et Termes
OGNL- Object-Graph Navigation Language, le langage d'expressions qu'utilise Struts pour lire et modifier les propriétés d'objets Java. Évaluer du texte d'attaquant en OGNL revient à exécuter son code.[1], [6]
Parseur multipart Jakarta- Le composant de Struts qui gère l'envoi de fichiers. Quand il rejetait un Content-Type mal formé, il construisait un message d'erreur qui était ensuite évalué.[1], [3], [2]
Injection d'expression (CWE-917)- Une entrée non fiable se retrouve dans une expression qu'un interpréteur évalue : les données deviennent des instructions.[6]
Inventaire des actifs- Un registre des logiciels et versions présents sur chaque système. Sans lui, impossible de rapprocher un avis de correctif des machines concernées.[4]
Correspondances de référentiels
- Référence CWE pour la faiblesse décrite dans ce guide : CWE-20 CWE-917
- CISA KEV — ajouté le 2021-11-03 — CVE-2017-5638
Chronologie de l'Incident
Apache publie le bulletin de sécurité S2-045 et les versions corrigées 2.3.32 et 2.5.10.1 de Struts.[1]
Equifax a indiqué plus tard au GAO qu’une nouvelle intrusion avait commencé le 13 mai et que les attaquants avaient alors commencé à extraire des données personnelles ; un accès non autorisé antérieur avait déjà eu lieu, sans vol de données.[4]
Equifax repère un trafic suspect après avoir renouvelé un certificat expiré sur un équipement d'inspection du trafic.[4]
Equifax annonce publiquement la violation. Le GAO indique ensuite que les données d’au moins 145,5 millions de personnes aux États-Unis et de près d’un million à l’étranger ont été concernées.[4]
Déroulement de l'Attaque Étape par Étape
Un en-tête Content-Type forgé
L'attaquant envoie une requête d'envoi de fichier dont l'en-tête Content-Type contient une expression OGNL au lieu d'un type de média.[1]
Une exfiltration lente et discrète
Les attaquants ont utilisé des identifiants trouvés dans un dépôt pour passer des trois bases associées au portail à 48 autres bases sans lien avec le portail. Le GAO rapporte environ 9 000 requêtes et une attaque d’environ 76 jours ; un certificat expiré empêchait l’inspection du trafic chiffré.[4]
Code Source : Vulnérable vs Sécurisé
Java illustratif, pas du code source Struts ; le vrai correctif des versions 2.3.32 et 2.5.10.1 a modifié la construction de l'erreur multipart. La version durcie utilise une clé de message fixe et ne transmet l'en-tête que comme argument positionnel : il est mis en forme comme du texte et n'entre jamais dans une expression. Elle remplace aussi par un marqueur toute valeur qui ne ressemble pas à un type de média, si bien qu'une modification ultérieure du message ne peut pas réintroduire l'en-tête dans une chaîne évaluée.[3], [2], [6]
import com.opensymphony.xwork2.TextProvider;
import javax.servlet.http.HttpServletRequest;
public final class UploadErrors {
private final TextProvider text; // resolves messages and evaluates expressions in them
public UploadErrors(TextProvider text) {
this.text = text;
}
public String describe(HttpServletRequest request) {
String contentType = request.getHeader("Content-Type");
// L’en-tête brut est intégré au message qui sera évalué.
return text.getText("Invalid content type: " + contentType);
}
}import com.opensymphony.xwork2.TextProvider;
import java.util.Locale;
import java.util.regex.Pattern;
import javax.servlet.http.HttpServletRequest;
public final class UploadErrors {
private static final Pattern MEDIA_TYPE = Pattern.compile(
"^[a-z]+/[a-z0-9.+-]+(;\\s*[a-z0-9-]+=[a-z0-9._-]+)*$");
private final TextProvider text;
public UploadErrors(TextProvider text) {
this.text = text;
}
public String describe(HttpServletRequest request) {
String contentType = request.getHeader("Content-Type");
boolean wellFormed = contentType != null && contentType.length() <= 256
&& MEDIA_TYPE.matcher(contentType.toLowerCase(Locale.ROOT)).matches();
// Utiliser une clé de message fixe ; l’en-tête n’est qu’un argument positionnel,
// et seulement s’il a la forme d’un type MIME.
return text.getText("upload.invalid.content.type",
new String[] { wellFormed ? contentType : "(malformed)" });
}
}Liste de Contrôle de Sécurité pour l'Ingénierie
- ✓Tenez un SBOM pour chaque application déployée et soyez capable de répondre à « où tourne Struts 2.3.x ? » dans les heures qui suivent un avis comme S2-045.[4]
- ✓Rapprochez les cibles du scanner de vulnérabilités de l'inventaire des actifs ; un scan propre d'un hôte jamais inclus dans le périmètre ne prouve pas qu'il est corrigé.[4]
- ✓Alertez sur l'expiration des certificats des équipements d'inspection TLS et de supervision au moins 30 jours à l'avance ; ici, un certificat expiré a masqué des mois d'exfiltration.[4]
- ✓En revue de code, refusez tout appel qui transmet du texte non fiable concaténé à un résolveur de messages, un moteur de gabarits ou un évaluateur d'expressions ; passez-le en argument.[6]
- ✓Séparez les bases de données des applications exposées à Internet, sortez les identifiants des fichiers applicatifs et alertez sur les volumes de requêtes inhabituels des comptes applicatifs.[4]
Sources
- Apache Struts — bulletin de sécurité S2-045 (CVE-2017-5638)
- Apache Struts — multipart parser error-message fix (commit 3523064)
- Apache Struts — upstream multipart error-handling fix (commit 6b8272c)
- GAO (États-Unis) — Protection des données : mesures prises par Equifax et les agences fédérales après la violation de 2017 (GAO-18-559)
- CVE-2017-5638 — fiche CVE
- MITRE CWE-917 — injection dans un langage d'expressions
- Log4Shell : quand écrire dans un journal est devenu une exécution de code à distance
- Injection de commandes — référence complète de la classe