Ir al contenido principal

Criptografía (CXXII): Solución Reto backdoor "rsalot"

En este post la solución a otro de los retos de criptografía de la plataforma backdoor.

Este reto tiene el título "rsalot" y mi valoración sobre su dificultad es: .

Su enunciado dice lo siguiente:


The flag is encrypted using a system that makes use of prime factorization of large numbers. Decrypt the flag from this.

Solución: lo primero que se me ocurrió fue crear un script en python para obtener el módulo y el exponente (n y e, respectivamente) de las 100 claves públicas que nos dan como recursos asociados al reto.

from Crypto.PublicKey import RSA

i = 1
print ''
print '[+] Obteniendo el modulo (n) y el exponente publico (e) de las 100 claves'
while i < 101:
    pubkeyi = open(str(i)+'.pem', 'r')
    publickeyi = RSA.importKey(pubkeyi.read())
    ni = publickeyi.n
    ei = publickeyi.e
    print ''
    print 'n'+str(i)+' ...', ni
    print 'e'+str(i)+' ...', ei
    pubkeyi.close()
    i+=1
Por lo que veo todas las claves dadas tienen el mismo exponente público, el número de Fermat para n = 4 (Fn = 22n + 1;  F4 = 224 + 1 = 65537), y a simple vista no veo nada que me pueda revelar alguna vulnerabilidad de estas claves que me permita intentar atacar el criptograma (c) que se nos proporciona como recurso asociado al reto en el archivo flag.enc.

Lo siguiente que se me ocurrió fue completar el script anterior para comprobar si algunos de los módulos (n) comparten factores primos, ya que si ésto es así podría factorizar muy fácilmente dichos módulos y obtener después las claves privadas correspondientes. Es decir, si el máximo común divisor (MCD) de los módulos (n) de dos claves públicas es diferente de 1 entonces dichos módulos comparten como factor ese  MCD y el otro factor de los respectivos módulos puede ser obtenido dividiendo cada uno de ellos entre este último, con lo que ya estaríamos en disposición de calcular el exponente privado (d) de ambas claves.

from fractions import gcd
from Crypto.PublicKey import RSA

primo_compartido = 0
i = 1
print ''
print '[+] Comprobando si los modulos de las claves publicas comparten algun factor primo'
while i < 100:
    pubkeyi = open(str(i)+'.pem', 'r')
    publickeyi = RSA.importKey(pubkeyi.read())
    ni = publickeyi.n
    ei = publickeyi.e
    j = i+1
    while j < 101:
        pubkeyj = open(str(j)+'.pem', 'r')
        publickeyj = RSA.importKey(pubkeyj.read())
        nj = publickeyj.n
        ej = publickeyj.e
        if gcd(ni,nj) != 1:
           primo_compartido = 1
           print '[+] Los modulos n'+str(i)+' y n'+str(j)+' comparten un factor primo'
        pubkeyj.close()
        j+=1
    pubkeyi.close()
    i+=1

if primo_compartido == 1:
   print '[+] Se han encontrado modulos que comparten algun factor primo'
else:
   print '[-] No se han encontado modulos que compartan algun factor primo.'
A la vista de los resultados sabemos que los módulos (n) de las claves públicas 64 y 87 (64.pem y 87.pem, respectivamente) comparten un factor primo, por lo que podemos calcular el exponente privado (d) de cada una de sus correspondientes claves privadas. Alguna de estas últimas puede ser que descifre el criptograma (c) que contiene el archivo flag.enc.

Para realizar todo ello, completo el script anterior de la siguiente manera:

from fractions import gcd
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_OAEP
from base64 import b64decode

def calcular_d(e, fi):
    a, b, u = 0, fi, 1
    while e > 0:
        q = b // e
        e, a, b, u = b % e, u, e, a - q * u 
    if b == 1:
       return a % fi
    print '[-] Error al calcular el exponente de la clave privada'

rsakeys = []

i = 1
print ''
print '[+] Comprobando si los modulos de las claves publicas comparten algun factor primo'
while i < 100:
    pubkeyi = open(str(i)+'.pem', 'r')
    publickeyi = RSA.importKey(pubkeyi.read())
    ni = publickeyi.n
    ei = publickeyi.e
    j = i+1
    while j < 101:
        pubkeyj = open(str(j)+'.pem', 'r')
        publickeyj = RSA.importKey(pubkeyj.read())
        nj = publickeyj.n
        ej = publickeyj.e
        if gcd(ni,nj) != 1:
           print '[+] Los modulos n'+str(i)+' y n'+str(j)+' comparten un factor primo'
           pi = ni/gcd(ni,nj)
           qi = gcd(ni,nj)
           di = calcular_d(ei, (pi-1)*(qi-1))
           print '[+] Construyendo clave privada a partir de n'+str(i)+', e'+str(i)+', d'+str(i)+', p'+str(i)+', q'+str(i)
           rsakeys.append(RSA.construct((ni, ei, di, pi, qi, )))
           pj = nj/gcd(ni,nj)
           qj = gcd(ni,nj)
           dj = calcular_d(ej, (pj-1)*(qj-1))
           print '[+] Construyendo clave privada a partir de n'+str(j)+', e'+str(j)+', d'+str(j)+', p'+str(j)+', q'+str(j)
           rsakeys.append(RSA.construct((nj, ej, dj, pj, qj, )))
        pubkeyj.close()
        j+=1
    pubkeyi.close()
    i+=1

if rsakeys != []:
   print '[+] Se han encontrado modulos que comparten algun factor primo'
   flag = open('flag.enc', 'rb')
   c = flag.read()
   flag.close()
   print '[+] Intentando el descifrado con las claves privadas construidas'
   for rsakey in rsakeys:
       try:
           rsakey = PKCS1_OAEP.new(rsakey)
           m = rsakey.decrypt(b64decode(c)) 
           print ""
           print 'texto en claro (m) ...', m
       except:
           pass
else:
   print '[-] No se han encontado modulos que compartan algun factor primo.'  

Y tras ejecutarlo ya podemos ver el texto en claro (m) y la flag contenida en él:
Por tanto, la flag es: b767b9d1fe02eb1825de32c6dacf4c2ef78c738ab0c498013347f4ea1e95e8fa.

Comentarios

Entradas populares de este blog

Criptografía (I): cifrado Vigenère y criptoanálisis Kasiski

Hace unos días mi amigo Iñaki Regidor ( @Inaki_Regidor ), a quien dedico esta entrada :), compartió en las redes sociales un post titulado "Criptografía: el arte de esconder mensajes"  publicado en uno de los blogs de EiTB . En ese post se explican ciertos métodos clásicos para cifrar mensajes , entre ellos el cifrado de Vigenère , y , al final del mismo, se propone un reto consistente en descifrar un mensaje , lo que me ha animado a escribir este post sobre el método Kasiski  para atacar un cifrado polialfabético ( conociendo la clave descifrar el mensaje es muy fácil, pero lo que contaré en este post es la forma de hacerlo sin saberla ). El mensaje a descifrar es el siguiente: LNUDVMUYRMUDVLLPXAFZUEFAIOVWVMUOVMUEVMUEZCUDVSYWCIVCFGUCUNYCGALLGRCYTIJTRNNPJQOPJEMZITYLIAYYKRYEFDUDCAMAVRMZEAMBLEXPJCCQIEHPJTYXVNMLAEZTIMUOFRUFC Como ya he dicho el método de Vigenère es un sistema de sustitución polialfabético , lo que significa que, al contrario que en un sistema...

Criptografía (XXIII): cifrado de Hill (I)

En este post me propongo explicar de forma comprensible lo que he entendido sobre el cifrado de Hill , propuesto por el matemático Lester S. Hill , en 1929, y que se basa en emplear una matriz como clave  para cifrar un texto en claro y su inversa para descifrar el criptograma correspondiente . Hay tres cosas que me gustan de la criptografía clásica, además de que considero que ésta es muy didáctica a la hora de comprender los sistemas criptográficos modernos: la primera de ellas es que me "obliga" a repasar conceptos de matemáticas aprendidos hace mucho tiempo y, desgraciadamente, olvidados también hace demasiado tiempo, y, por consiguiente, que, como dice  Dani , amigo y coautor de este blog, me "obliga" a hacer "gimnasia mental"; la segunda es que, en la mayoría de las ocasiones, pueden cifrarse y descifrase los mensajes, e incluso realizarse el criptoanálisis de los criptogramas, sin más que un simple lápiz y papel, es decir, para mi es como un pasat...

Criptografía (CLXXXIV): Soluciones Retos criptografía de CyberOlympics 2017

En este post pongo las soluciones a los retos de  criptografía que he ido resolviendo de la edición del año 2017 de CyberOlympics , competición en modalidad  'on-line' , estilo  'Capture the Flag'  y formato  'Jeopardy'  dirigida a centros educativos y organizada por el Instituto Nacional de Ciberseguridad (INCIBE) en el marco de la actividad llamada CyberCamp. En esta edición la mayoría de los retos presentaron un  nivel  de dificultad bajo  ( ★ ★ ☆☆☆ ) , lo que entiendo adecuado por el colectivo al que van dirigidos. Las soluciones al resto de desafíos de criptografía de esta edición, cuyos archivos asociados tenga (no me han pasado todos) y que consiga resolver, las pondré en otra entrada. Reto 1 (Criptografía) : Enunciado : Todas las mañanas cuando me despierto, me miro en el espejo y no entiendo lo que veo. Hoy me he levantado dando un salto mortal y no voy a apartarme de mi “otro yo” hasta que no descubra el mensaje. Parece q...