flawopen.com/Incidents/equifax-struts-ognl-rce-july-2017
Equifax: la brecha expuso datos de casi 150 millones
CVE-2017-5638 permitió inyectar OGNL mediante la cabecera Content-Type en el portal Struts sin parchear de Equifax. El ataque duró unos 76 días y expuso datos de casi 150 millones de personas.[1], [4]
Imagina un sistema de mensajería que convierte una etiqueta de envío rechazada en un aviso de error. Si el aviso interpreta la etiqueta como una plantilla de instrucciones ejecutable, quien la envía puede hacer que el sistema obedezca en vez de limitarse a informar de una dirección incorrecta. Struts ya tenía una corrección, pero el aviso de Equifax llegó a una lista obsoleta de administradores y un análisis de red no detectó la vulnerabilidad en el portal.[1], [4]
Conceptos Clave y Términos
OGNL- Object-Graph Navigation Language, el lenguaje de expresiones que usa Struts para leer y asignar propiedades de objetos Java. Evaluar texto del atacante como OGNL equivale a ejecutar su código.[1], [6]
Parser multipart Jakarta- El componente de Struts que gestiona las subidas de ficheros. Al rechazar un Content-Type mal formado, construía un mensaje de error que después se evaluaba.[1], [3], [2]
Inyección de expresiones (CWE-917)- Una entrada no confiable acaba dentro de una expresión que evalúa un intérprete, de modo que los datos se convierten en instrucciones.[6]
Inventario de activos- Un registro de qué software y qué versión corre en cada sistema. Sin él, no se puede relacionar un aviso de parche con las máquinas que lo necesitan.[4]
Mapeos de marcos
- Referencia CWE para la debilidad descrita en esta guía: CWE-20 CWE-917
- CISA KEV — añadido el 2021-11-03 — CVE-2017-5638
Cronología del Incidente
Apache publica el boletín de seguridad S2-045 y las versiones corregidas 2.3.32 y 2.5.10.1 de Struts.[1]
Equifax explicó después a la GAO que una intrusión distinta comenzó el 13 de mayo y que entonces los atacantes empezaron a extraer datos personales; antes ya se había producido un acceso no autorizado, sin robo de datos.[4]
Equifax detecta tráfico sospechoso tras renovar un certificado caducado en un equipo de inspección de tráfico.[4]
Equifax anuncia públicamente la brecha. La GAO informa después de que se vieron afectados datos de al menos 145,5 millones de personas en EE. UU. y casi un millón fuera del país.[4]
Flujo de Ataque Paso a Paso
Una cabecera Content-Type manipulada
El atacante envía una petición de subida cuya cabecera Content-Type contiene una expresión OGNL en lugar de un tipo de medio.[1]
Una exfiltración lenta y silenciosa
Los atacantes usaron credenciales halladas en un repositorio para ampliar el acceso desde las tres bases de datos asociadas al portal a otras 48 bases de datos no relacionadas con el portal. La GAO informa de unas 9.000 consultas y un ataque de unos 76 días; un certificado caducado impedía inspeccionar el tráfico cifrado.[4]
Código Fuente: Vulnerable vs. Seguro
Java ilustrativo, no es código fuente de Struts; la corrección real de las versiones 2.3.32 y 2.5.10.1 cambió cómo se construía el error del multipart. La versión reforzada usa una clave de mensaje fija y pasa la cabecera solo como argumento posicional, de modo que se formatea como texto y nunca forma parte de una expresión. Además sustituye por un marcador cualquier valor que no parezca un tipo de medio, así que ni un cambio futuro del mensaje puede devolver la cabecera a una cadena evaluada.[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");
// El encabezado sin procesar pasa a formar parte del mensaje que se evalúa.
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();
// Use una clave de mensaje fija; el encabezado solo se admite como argumento posicional,
// y únicamente si tiene el formato de un tipo de contenido.
return text.getText("upload.invalid.content.type",
new String[] { wellFormed ? contentType : "(malformed)" });
}
}Lista de Verificación de Seguridad para Ingeniería
- ✓Mantén un SBOM de cada aplicación desplegada y sé capaz de responder «¿dónde ejecutamos Struts 2.3.x?» pocas horas después de un aviso como S2-045.[4]
- ✓Cruza los objetivos del escáner de vulnerabilidades con el inventario de activos; un escaneo limpio de un host que nunca estuvo en el alcance no demuestra que esté parcheado.[4]
- ✓Alerta de la caducidad de certificados en equipos de inspección TLS y de monitorización con al menos 30 días de antelación; aquí un certificado caducado ocultó meses de exfiltración.[4]
- ✓En la revisión de código, rechaza cualquier llamada que pase texto no confiable concatenado a un resolutor de mensajes, motor de plantillas o evaluador de expresiones; pásalo como argumento.[6]
- ✓Segmenta las bases de datos respecto a las aplicaciones expuestas a internet, saca las credenciales de los ficheros de la aplicación y alerta de volúmenes de consultas inusuales desde cuentas de aplicación.[4]
Fuentes
- Apache Struts — boletín de seguridad S2-045 (CVE-2017-5638)
- Apache Struts — multipart parser error-message fix (commit 3523064)
- Apache Struts — upstream multipart error-handling fix (commit 6b8272c)
- GAO de EE. UU. — Protección de datos: medidas de Equifax y de las agencias federales tras la brecha de 2017 (GAO-18-559)
- CVE-2017-5638 — registro CVE
- MITRE CWE-917 — inyección en lenguajes de expresiones
- Log4Shell: cuando escribir en un registro se convirtió en ejecución remota de código
- Inyección de comandos — referencia completa de la clase