
Prueba de concepto que reproduce la falla de clave codificada de CVE-2021-22681 y valida una corrección de TLS mutuo/CRL por dispositivo sobre EtherNet/IP simulado, con mapeo de IEC 62443-4-2.
Seis scripts ejecutables — tráfico real de protocolo EtherNet/IP (Prueba 1), mecánica real de TLS/PKI (Pruebas 3–6) — cero software o licencias de Rockwell en toda la cadena. Construido para probar una afirmación antes de documentarla, no para defenderla por fe.
Procedencia: construido el 2026-07-31, junto con una investigación paralela sobre la planta de tratamiento de aguas residuales (WWTF) de Braham, MN — una de las cuatro utilidades divulgadas públicamente en el incidente coordinado del sector hídrico de Minnesota del 26–27 de julio de 2026. El contexto de ese incidente se encuentra en el aviso de CISA AA26-097A (conjunto FBI/CISA/NSA/EPA/DOE/USCYBERCOM/Tesorería; emitido el 2026-04-07, ampliado el 2026-07-22), que cubre la campaña en curso CyberAv3ngers afiliada al IRGC. Salvedad de atribución, sostenida con precisión: ninguna agencia ha atribuido formalmente el incidente de Minnesota específicamente a ese grupo — solo la campaña más amplia en curso lo está. Esta carpeta es el lado de la corrección técnica, mantenida separada de la investigación del incidente a propósito.
Se contactó a Rockwell PSIRT ([email protected]) y a RA Secure Mail ([email protected]) el 2026-07-31, antes de que este repositorio y el informe se hicieran públicos (~30 minutos antes, según las marcas de tiempo de los archivos). El equipo de arquitectura de seguridad de Rockwell revisó el repositorio y respondió el 2026-08-03. Citado directamente, no parafraseado para convertirlo en una afirmación más fuerte de la que hicieron:
Rockwell Automation no respalda ni valida su interpretación, su mapeo de IEC 62443-4-2, ni ninguna conclusión derivada de la prueba de concepto. Por favor, no presente el trabajo como revisado, aprobado o respaldado por Rockwell Automation... los scripts demuestran principios generales de criptografía y autenticación, más que algo específico de CIP Security o de CVE-2021-22681.
Este trabajo no ha sido revisado, aprobado ni respaldado por Rockwell Automation, punto. Su
caracterización técnica — principios generales, no específicos de CIP Security — es la misma distinción
que la tabla "El límite estricto" a continuación ya establece sobre las afirmaciones de este propio repositorio; su revisión lo confirma
de forma independiente en lugar de refutarlo. Para utilidades en hardware que no puede alcanzar CIP Security,
Rockwell señaló su propia Guía de Diseño e Implementación de Ethernet Convergente de Planta (CPwE)
(citada en PHASED_ROLLOUT.md Fase 1) — el recurso existente del proveedor hacia el que este proyecto intenta orientar
a las personas en lugar de duplicarlo.
El hallazgo de la Prueba 1 — sin autenticación en el estado predeterminado del protocolo — no es exclusivo de EtherNet/IP. Modbus TCP, todavía uno de los protocolos más ampliamente desplegados en sistemas de control de agua/aguas residuales, no tiene ningún concepto de autenticación en la especificación del protocolo; data de comunicaciones seriales de 1979 y nunca fue diseñado con la seguridad en mente. CISA ha nombrado repetidamente exactamente esta falla en avisos de ICS (p. ej., la serie MELSEC iQ-F de Mitsubishi Electric: "MODBUS/TCP carece de autenticación adecuada," permitiendo lectura/escritura/detención no autorizadas). La propia respuesta de la Organización Modbus, Seguridad Modbus/TCP, es encapsulación TLS con certificados X.509 — estructuralmente la misma categoría de corrección que la Prueba 3 demuestra aquí, estandarizada a nivel de la organización del protocolo en lugar de la de un solo proveedor. DNP3 tiene una extensión opcional de Autenticación Segura (SAv5, estandarizada en 2012); tanto análisis independientes como relatos de implementadores la describen como raramente configurada en la práctica, citando brechas de interoperabilidad entre fabricantes de OT y una complejidad genuina del protocolo — dejando la misma exposición que la Prueba 1 demuestra para EtherNet/IP.
El punto arquitectónico, expresado con precisión para no sobrevenderse: la corrección de la Prueba 3 — TLS mutuo, identidad por dispositivo vinculada en el certificado (no solo validez de CA), revocación mediante CRL — opera en la capa de transporte, no en el protocolo de aplicación ICS. El principio es igualmente aplicable debajo de Modbus, DNP3 o un protocolo propietario; lo que cambia es el envoltorio, no la forma de la corrección. Este repositorio no ha construido ni ejecutado una PoC específica de Modbus o DNP3 — esta es una generalización arquitectónica a partir de documentación pública, sujeta a la misma clasificación de "demostrado vs. con fuente" que todo lo demás aquí, no una afirmación nueva probada.
Fuentes: Avisos ICS de CISA sobre brechas de autenticación Modbus/TCP — Industrial Cyber · Descripción general de Seguridad Modbus/TCP — Veridify · Desafíos de adopción de DNP3 SAv5/SAv6 — Step Function I/O
Un paquete de divulgación vive o muere por no mezclar estas, porque cada una tiene una corrección diferente:
La corrección demostrada aquí — vinculación de identidad por dispositivo (Prueba 3) — aborda la falla de la clave de flota.
| Afirmación | Nivel | Por qué | |
|---|---|---|---|
| El principio arquitectónico | "un secreto compartido único en una flota se ve comprometido en toda la flota por una sola fuga; la autenticación vinculada a identidad por dispositivo cierra eso" | PROBADO | demostrado con código real en ejecución incluyendo el control negativo que prueba que la verificación es necesaria, no meramente que se activa: en un endpoint estricto, el certificado genuinamente válido por CA del Dispositivo B es rechazado por identidad (Prueba 3 · caso 3), pero en un endpoint de validez-de-CA-solo, el mismo certificado es aceptado (caso 4 — el control) → "firmado válidamente por CA solo == acceso en toda la flota == Prueba 2 con ropaje TLS." La vinculación también se sostiene en sentido inverso: un servidor rogue que presenta un certificado de flota válido es rechazado por el cliente (caso 5). La Prueba 1 muestra por separado la línea base más amplia sin autenticación. |
| La implementación específica de CIP Security de Rockwell se comporta de manera idéntica | "habilitar CIP Security en hardware real de Rockwell remedia CVE-2021-22681 exactamente de esta manera" | HIPÓTESIS PRINCIPAL, con fuente no verificada | este es el lenguaje del propio aviso de Rockwell (PN1550) — "Cuando se implementa correctamente, CIP Security remedia esta vulnerabilidad... no hace uso de ninguna clave codificada" — no algo que hayamos confirmado de forma independiente contra hardware Logix real. Probamos el principio que describe su aviso, no su implementación exacta a nivel de cable. |
No confunda las dos filas. El principio está probado. La implementación específica del proveedor del mismo es creíble (es su propia intención de diseño declarada) pero no probada por nosotros contra equipos reales.
test1_baseline_vulnerable.py — la línea base sin autenticación, en vivoLevanta un simulador real de PLC EtherNet/IP (cpppo, emulando un Allen-Bradley ControlLogix) y
lee + escribe una etiqueta de control con cero credenciales. (Alcance: esta es la amplia
línea base sin autenticación en la que se apoyó la campaña — no el mecanismo específico de clave codificada
de CVE-2021-22681. Mantenida distinta a propósito; consulte "Tres fallas diferentes" arriba.)
python test1_baseline_vulnerable.py
test2_shared_secret_fails.py — la forma de la falla de clave de flota (puente narrativo, no una prueba)Dos endpoints mantienen una clave estática; una credencial del Dispositivo A abre el Dispositivo B sin cambios — el análogo estructural más cercano a la falla de una-clave-para-todos de CVE-2021-22681. Pero es tautológico: ambos manejadores están construidos para aceptar esa clave, por lo que no hay una ruta de ejecución en la que pueda fallar. No demuestra nada que el código no defina en existencia. Se mantiene como el puente narrativo desde la Prueba 1 hasta la Prueba 3; no tiene ningún peso probatorio y deliberadamente no es una pata de prueba.
python test2_shared_secret_fails.py
test3_mutual_tls_fix.py — la corrección, con su control negativo y ambas direccionesCA real, dos certificados de dispositivo individualmente únicos — identidad vinculada en el SubjectAlternativeName, no en el CommonName obsoleto. Cinco casos, todos ejecutados:
device-a (check_hostname contra un SAN real). Mutuo — ambos extremos vinculan identidad.python test3_mutual_tls_fix.py
test4_revocation.py — la pata del ciclo de vida: revocaciónUna CRL real firmada por CA. Una credencial de cliente (engineer-1) es concedida; luego su serial se agrega a
la CRL, y la misma credencial aún válida, no expirada y firmada por CA es denegada — el contenido
empírico de la cláusula de revocación CR 1.8 / 1.9. Unicidad (Prueba 3) ≠ revocabilidad; esto muestra que una
credencial puede ser retirada.
python test4_revocation.py
test5_rotation.py — la pata del ciclo de vida que la Prueba 4 no cerró: rotaciónEmite una credencial de reemplazo (v2) para la misma identidad (engineer-1) que ya posee una
válida (v1). EL CONTROL (caso 3): v1 se presenta nuevamente después de que v2 existe pero antes
de que v1 sea explícitamente retirada → aún CONCEDIDA — demostrando que la re-emisión por sí sola no retira la credencial
antigua. Solo después de que v1 se agrega explícitamente a la CRL (caso 4) es denegada; v2 no se ve afectada
en todo momento (caso 5) — la identidad nunca pierde acceso durante la transición. El "emitir un
reemplazo y retirar la anterior" de CR 1.8 son dos acciones, y esto muestra ambas, por separado.
python test5_rotation.py
test6_tamper_injection.py — la pata que CR 3.1 nombró solo "por construcción": una prueba de integridad dedicadaUn relé a nivel de registro se sitúa entre un cliente y servidor TLS mutuo real, reenviando registros TLS al
analizar solo el encabezado de 5 bytes — nunca ve el texto plano de la carga útil cifrada. Control: cada
byte reenviado sin modificación → mensaje entregado intacto. Manipulación: un bit invertido dentro del texto cifrado
de un registro de Datos de Aplicación en vivo → la verificación AEAD de la pila TLS receptora falla
(SSLV3_ALERT_BAD_RECORD_MAC) y la conexión se derriba — los datos corruptos nunca se entregan como
si fueran válidos.
Qué byte, y por qué se especifica: el cuerpo de un registro AEAD TLS 1.2 es
explicit_nonce(8) ‖ ciphertext ‖ auth_tag(16), por lo que el byte 0 es el nonce, no la carga útil. Invertir
el nonce también activa la verificación AEAD — pero al alterar el descifrado en lugar de que la etiqueta detecte una
carga útil alterada. CR 3.1 trata sobre la modificación no autorizada de la información transmitida, por lo que
la inversión apunta a un byte en medio del texto cifrado, y la prueba demuestra entonces exactamente la
oración que afirma.
python test6_tamper_injection.py
maximum_version = TLSv1_2 por dos razones que tratan sobre la observabilidad, no la
seguridad: bajo TLS 1.2, un certificado de cliente ausente o rechazado falla durante el handshake, por lo
que la prueba obtiene un error determinista y atribuible en lugar de la falla posterior al handshake de TLS 1.3; y
el tipo de contenido del registro permanece visible en claro, lo que el relé de la Prueba 6 necesita para identificar
un registro de Datos de Aplicación en absoluto. Despliegue la versión más alta de TLS que sus dispositivos soporten — TLS 1.3
donde esté disponible. Nada en este repositorio debe leerse como consejo para limitar un sistema de producción a 1.2.presented == KEY, identity in SAN) no son de tiempo constante. No explotables aquí — los valores
comparados son cadenas de identidad casi públicas y TLS ya ha realizado la autenticación criptográfica real
antes de que la comparación se ejecute — pero se señala porque el patrón se copia en lugares donde
sí importa.python -m venv venv
venv\Scripts\activate # o: source venv/bin/activate
pip install -r requirements.txt
62443-4-2_SL2_MAPPING.md (CR 1.2 / 1.8 / 1.9 / 1.14 / 3.1,
clasificado honestamente, cada brecha nombrada; CR 1.2, CR 1.9 y la pata de emisión/validación/revocación de CR 1.8
están ahora demostradas). Antes de PSIRT ([email protected] / [email protected]):
verificar el texto normativo de cada CR contra una copia comprada de IEC 62443-4-2:2019.PHASED_ROLLOUT.md (Fase 0 detener-la-hemorragia · 1 segmentar · 2
controles compensatorios · 3 CIP Security/PKI, según lo permita el hardware · 4 operar). Dimensionado para una pequeña
utilidad; honesto en que CIP Security está limitado por hardware, por lo que las Fases 0–2 soportan la reducción de riesgo independientemente.PHASE0_INVENTORY_WORKSHEET.md (un inventario de dispositivos completable, no solo la instrucción de hacer
uno), RESOURCES.md (asistencia gratuita de CISA / EPA / WaterISAC / AWWA, verificada en vivo, no recordada),
e INCIDENT_RESPONSE_QUICK_REFERENCE.md (una tarjeta de primeros-60-minutos, explícitamente no un plan completo de
respuesta a incidentes — la seguridad de las operaciones siempre es lo primero en ella).test5_rotation.py mueve CR 1.8
de "habilitado" a completamente demostrado (rotación, con su propio control); test6_tamper_injection.py
mueve CR 3.1 de "por construcción" a demostrado (una inversión de bit real, rechazada por la verificación AEAD de TLS).
Ambos re-ejecutados repetidamente sin inestabilidad antes de agregarse aquí. También se agregó:
PHASE3_CA_QUICKSTART.md (la CA de "pocas líneas de código", como comandos openssl reales probados) y un
ejemplo concreto de lista blanca en PHASED_ROLLOUT.md Fase 1.GLOSSARY.md, INTEGRATOR_CHECKLIST.md, PHASE3_CA_QUICKSTART.md) ahora está
realmente enlazado desde el plan en lugar de estar sin referencia. Nada en esta lista sigue en
BORRADOR — todo lo anterior y posterior está publicado y en vivo en el repositorio público.Cada identificador externo aquí fue extraído de una fuente en vivo el 2026-07-31, no recordado del
entrenamiento: AA26-097A (multifuente, incl. WaterISAC / Tenable / SecurityWeek), Braham como una de las
cuatro víctimas divulgadas, CyberAv3ngers/IRGC, PN1550 confirmó el aviso real de Rockwell con su
cita de la Fila 2 verificada textualmente ("Cuando se implementa correctamente, CIP Security remedia esta vulnerabilidad"
ICSA-21-056-03, y 62443-4-2 CR 1.8 (PKI) + CR 3.1
(integridad de comunicaciones) confirmados exactos.
Disciplina para el paquete: re-extraer cada identificador de fuentes primarias en el momento de la presentación.
Los avisos se renumeran, amplían y reemplazan — AA26-097A ya muestra una ampliación — por lo
que "verificado el 2026-07-31" no es "verificado al presentar." El mapeo formal completo CR-por-CR de 62443-4-2 está
ahora escrito (62443-4-2_SL2_MAPPING.md) — lo que queda es re-extraer su texto normativo citado contra
una copia comprada del estándar antes de la presentación, no escribir el mapeo en sí.l0gic — Patrick Crosby · 2026-07-31.
Registro de endurecimiento, 2026-08-03: se agregaron test5_rotation.py y test6_tamper_injection.py, cerrando los
dos elementos técnicos nombrados como abiertos desde el 2026-07-31 — cada uno re-ejecutado tres veces sin inestabilidad antes de
documentarse aquí. PHASE3_CA_QUICKSTART.md (comandos openssl reales y probados), un ejemplo
concreto de lista blanca de firewall de la Fase 1, PHASE0_INVENTORY_WORKSHEET.md, RESOURCES.md,
INCIDENT_RESPONSE_QUICK_REFERENCE.md y la generalización de Modbus/DNP3 también se agregaron el mismo día.
Registro de endurecimiento, 2026-08-03 (continuación) — dos pasadas de revisión independientes, ambas aplicadas: un
equipo rojo de seguridad encontró y corrigió una vulnerabilidad PKI real en el inicio rápido de la CA (-copy_extensions copyall
permitía que una solicitud de certificado maliciosa se auto-declarara CA:TRUE; corregido mediante -extfile explícito, verificado
contra una solicitud deliberadamente maliciosa en ambos sentidos) y corrigió test6 para invertir el texto cifrado
específicamente en lugar del byte 0 (el nonce), más una corrección real de seguridad de hilos y fijación de dependencias.
Una auditoría estructural separada — leyendo las fases como un sistema a lo largo del tiempo, no como una lista de verificación — encontró
y corrigió: el titular de "pocas líneas" de la Fase 3 ocultaba que la revocación/rotación no son simples; la decisión de CRL
fail-open/fail-closed nunca se tomó (ahora está, con un valor predeterminado y razonamiento); la expiración de certificados
era un nuevo modo de interrupción sin nombre (la Fase 4 ahora incorpora la propia lección de superposición segura de la Prueba 5); la Fase 3
ciega silenciosamente el alertador de escritura de la Fase 2 (ahora nombrado, con reemplazos sugeridos); NTP era un
prerrequisito no declarado; CR 1.14 se enmarcó como "no cumplido" donde "no aplicable al
diseño remediado" es la declaración precisa; y INTEGRATOR_CHECKLIST.md/GLOSSARY.md eran
documentos huérfanos a los que nada enlazaba (ahora enlazados desde la Fase 3 y la parte superior de este plan). Cada corrección verificada
re-ejecutando las pruebas afectadas, no solo releyendo el diff. Publicado y en vivo — las cinco fases
del plan de despliegue están hechas, revisadas dos veces, y nada de hoy queda local.
Registro de endurecimiento: la Prueba 3 se fortaleció para incluir el control negativo (caso 4, que prueba la necesidad no solo la activación), el caso de dirección inversa (caso 5, vinculación mutua) y la identidad basada en SAN (no CN), luego re-verificada re-ejecutando los cinco casos; se agregó la Prueba 4 (revocación CRL). Los títulos de las Pruebas 1/2 se redimensionaron para mantener las tres clases de falla distintas; la Prueba 2 se degradó de "prueba" a puente narrativo; se agregaron los límites de alcance de revocación y tiempo constante. La cadena de citas se recuperó de fuentes primarias y se selló para re-extracción en la presentación.