viernes, 7 de diciembre de 2018

Criptografía (CXVII): Solución Reto Atenea "Really???"

En este post la solución al sexto reto de "Criptografía y Esteganografía" de la plataforma ATENEA del CCN-CERT con desafíos de seguridad.

En concreto, este sexto reto tiene el título de "Really???" y tras su resolución se obtienen 425 puntos (Dificultad: ).

Su enunciado dice lo siguiente:


Durante el estudio del disco duro del ordenador de un sospechoso se ha encontrado un fichero cifrado mediante PGP. Al no encontrarse ninguna clave privada dentro del equipo se sospecha que dicho fichero esté cifrado mediante cifrado simétrico.

Por otro lado, todas las contraseñas obtenidas de varias cuentas del sospechoso (a partir de la investigación de su equipo) tienen las siguientes características:

- Son de longitud 6 o 7.
- Sólo contienen letras minúsculas.
- Sólo se utilizan estas letras: qwertyiopnmjk.
- No se repite ninguna de las letras de la contraseña.
- Algunas de ellas contiene únicamente un número entre estos: 013.

Ninguna de esas contraseñas ha servido para descifrar el fichero, pero quizás haya sido cifrado con una contraseña con estas mismas características.

No sabemos si el contenido del fichero es relevante para la investigación, pero sólo hay una forma de averiguarlo...
Pista! La lengua materna del dueño del equipo es el inglés
Solución: Parece claro que lo primero que debemos hacer es crearnos un diccionario de contraseñas que cumplan las características indicadas en el enunciado. Además, la pista que se nos proporciona parece indicarnos que para ello deberíamos partir de algún diccionario de passwords en inglés. El más conocido de estos último es 'rockyou'.

Por tanto, para obtener el diccionario con el que realizar el ataque de fuerza bruta utilizamos 'Grep' (una utilidad que busca en uno o más archivos de entrada las líneas que contienen una coincidencia con un patrón específico), de la siguiente manera:
Lo siguiente que tenemos que hacer para 'crackear' la contraseña con la que descifrar el mensaje que contiene el archivo asociado al reto (message.asc) es obtener el hash correspondiente al mismo. Para ello utilizamos 'John the Ripper', de la siguiente manera:
A partir de los dos archivos así obtenidos (diccionario.txt y message.hash) ejecutamos john y, si tenemos suerte, obtendremos la contraseña:

john --wordlist=diccionario.txt message.hash
Bueno, parece que ha habido suerte y que la contraseña es "monkey3".

Una vez conseguida la contraseña desciframos el mensaje:
Y obtenemos un fichero de texto (message.txt).

A la vista del contenido de ese archivo, sospecho que tiene información oculta y que para ello se ha utilizado la herramienta de esteganografía 'SNOW'. Intentamos recuperar la información escondida:
Buscamos en 'Google' los ingredientes de la receta anterior a ver que obtenemos:
La bebida en cuestión es el grog, y, conforme al fichero txt que contiene la receta, la solución al reto es: groggrog (recordar que hay que introducirla con formato flag{md5}).

jueves, 6 de diciembre de 2018

Criptografía (CXVI): Reto 23

Otro reto de dificultad media sobre criptografía. En esta ocasión se ven involucrados dos criptosistemas clásicos y una historia  muy curiosa (https://es.wikipedia.org/wiki/Baphomet), sea o no cierta.

Como siempre, se admiten soluciones en forma de comentarios a esta entrada. Pasado un tiempo iré proporcionando pistas para su resolución, un máximo de tres, y posteriormente actualizaré este post con la solución.

Reto 23: "BAPHOMET".

En una memoria USB incautada a un sospechoso se ha encontrado el archivo asociado al reto. Parece que ese fichero contiene una imagen que podría, a su vez, contener una clave que el sospechoso habría recibido para acceder a un servicio en la nube, pero el archivo está protegido mediante una contraseña.

En la memoria USB incautada se ha encontrado una lista de contraseñas en claro que el sospechoso utiliza para proteger su información, pero ninguna de ellas ha servido para acceder al fichero asociado al reto, y tampoco al servicio en la nube. Sin embargo, todas estas contraseñas siguen un mismo patrón:

- Son de longitud 8.
- Cada una de las contraseñas esta formada por cuatro caracteres y por cuatro dígitos.
- Los caracteres utilizados están siempre en mayúsculas y se corresponde con los siguientes: BHMP. En una misma contraseña no se repite ningún carácter.
- Los dígitos utilizados son los siguientes: 0347. En una misma contraseña no se repite ningún dígito.

El acceso al servicio en la nube es vital para continuar con la investigación. ¿Puedes acceder a la imagen que contiene el archivo asociado al reto y obtener así la clave que ésta contiene?.

Dificultad:
Tipo:           Criptografía.

Recursos:   reto23.zip.

******** 08/12/2018
Pista 1:     Aunque por el contexto del reto es muy fácil deducir la contraseña del archivo .zip asociado al reto, si no nos damos cuenta de cuál es dicha clave podemos generar un diccionario con las permutaciones de los 4 cuatro caracteres y 4 dígitos dados (BHMP0347). Como se trata de permutaciones de 8 elementos (P8), este diccionario tendrá P8 = 8! = 40.320 entradas. A partir de ahí 'John the Ripper' puede ser de utilidad.

******** 09/12/2018

Pista 2:     Si has accedido ya a la imagen que contiene el archivo .zip asociado al reto habrás observado que ésta parece ser el reflejo en un espejo de la imagen original, es decir, de la que ha servido para ilustrar este post.

******** __/__/____
Solución (por publicar).

******** PRÓXIMO RETO
Reto 24:   Por publicar.

viernes, 30 de noviembre de 2018

Criptografía (CXV): Solución Reto Cybercamp "Redundancia innecesaria"

En este post la solución a uno de los retos de criptografía de Cybercamp 2018 Online.

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

Su enunciado dice lo siguiente:


Nuestros expertos han capturado un pendrive que contenía estos dos ficheros, pero parece que uno de ellos ha sufrido daños... (Respuesta: flag{X})


Como recursos asociados al retnos dan los archivos key.pem y secret.txt.


El primero de ellos (key.pem) contiene una clave privada RSA, pero está dañada:

-----BEGIN RSA PRIVATE KEY-----
MIIBOwIBAAJBAMSwf+/I42wFwNpDQiGuv0fb9w5Ria2JJAjzrYEYKp4HAKB8nXxmyGx6O
WAhI+4PYFYT3pf95J/mg5buCvP19fMCAwEAAQJAKuxRnyR57PL8eSVAY1VdTPNF4QwO
PZ62DHYRISEC++UtRemqE1eBPkRgswiJ91+r9y8EnVw/SvL4GYQmeovSsQIhAOq8Heinx
e4udriNOd35SgJV9e87YglCCIfCoAirR0qtAiEA1oIMcKaiRiUj2S/Q4YFTNySdT+fH16huoS
QrEapD9x8*********************************************************************
************************************************************************
-----END RSA PRIVATE KEY-----

Mientras que el segundo (secret.txt) parece ser un archivo cifrado mediante el algoritmo de cifrado asimétrico RSA.

Por el título del reto deduzco que la parte de la clave privada RSA que está dañada es redundante, es decir, puede obtenerse a partir de la parte que no está dañada y, por tanto, es posible reconstruir la clave original. Veamos qué podemos obtener.

Para ello, en primer lugar sustituimos todos los "*" de la clave que nos dan por "0", es decir:

-----BEGIN RSA PRIVATE KEY-----
MIIBOwIBAAJBAMSwf+/I42wFwNpDQiGuv0fb9w5Ria2JJAjzrYEYKp4HAKB8nXxmyGx6O
WAhI+4PYFYT3pf95J/mg5buCvP19fMCAwEAAQJAKuxRnyR57PL8eSVAY1VdTPNF4QwO
PZ62DHYRISEC++UtRemqE1eBPkRgswiJ91+r9y8EnVw/SvL4GYQmeovSsQIhAOq8Heinx
e4udriNOd35SgJV9e87YglCCIfCoAirR0qtAiEA1oIMcKaiRiUj2S/Q4YFTNySdT+fH16huoS
QrEapD9x8000000000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000000000000000000000000000
-----END RSA PRIVATE KEY-----

A este archivo lo llamo key_2.pem.

Antes que nada decir que el archivo PEM de clave privada RSA PKCS#1 comienza y termina con las etiquetas:

-----BEGIN RSA PRIVATE KEY-----
BASE64 ENCODED DATA
-----END RSA PRIVATE KEY-----

y que dentro de los datos codificados en base64 está presente la siguiente estructura:

RSAPrivateKey ::= SEQUENCE {
  version                 Version,
  modulus               INTEGER,  -- n
  publicExponent    INTEGER,  -- e
  privateExponent   INTEGER,  -- d
  prime1                  INTEGER,  -- p
  prime2                  INTEGER,  -- q
  exponent1            INTEGER,  -- d mod (p-1)
  exponent2            INTEGER,  -- d mod (q-1)
  coefficient            INTEGER,  -- (inverse of q) mod p
  otherPrimeInfos   OtherPrimeInfos OPTIONAL
}

El comando asn1parse es una utilidad de diagnóstico que puede analizar estas estructuras y que también se puede utilizar para extraer los datos con este formato, por lo que probamos con:

openssl asn1parse -in key_2.pem

Y obtenemos lo siguiente:
Como se observa en la figura anterior hemos obtenido (parte no dañada de la clave), lo siguiente:

Módulo (hexadecimal):
n = C4B07FEFC8E36C05C0DA434221AEBF47DBF70E5189AD892408F3AD81182A9E0700A07C9D7C66C86C7A39602123EE0F605613DE97FDE49FE68396EE0AF3F5F5F3

Exponente de la clave pública (hexadecimal):
e = 010001

Exponente de la clave privada (hexadecimal):
d = 2AEC519F2479ECF2FC79254063555D4CF345E10C0E3D9EB60C7611212102FBE52D45E9AA1357813E4460B30889F75FABF72F049D5C3F4AF2F81984267A8BD2B1

Primer factor primo del módulo (hexadecimal):
p = EABC1DE8A7C5EE2E76B88D39DDF94A0255F5EF3B6209420887C2A008AB474AAD

Segundo factor primo del módulo (hexadecimal):
q = D6820C70A6A2462523D92FD0E1815337249D4FE7C7D7A86EA1242B11AA43F71F

Los datos que faltan (parte dañada de la clave) son: d mod (p-1), d mod (q-1), (inverse of q) mod p, que se pueden obtener a partir de los anteriores y, de esta forma, reconstruir la clave privada RSA original. Tal y como dije en este postjunto con su clave privada (d, n), el receptor debe guardar los números primos p y q, y, además, para aplicar de forma óptima el teorema chino del resto en el descifrado y así simplificar los cálculos, los tres valores que faltan.

Para reconstruir la clave privada RSA original y, posteriormente, poder obtener la flag creo el siguiente script de python:

#!/usr/bin/python

from egcd import egcd
import pyasn1.codec.der.encoder
import pyasn1.type.univ
import base64

def inv(a, m):
    gcd, x, y = egcd(a, m)
    if gcd != 1:
        return None
    else:
        return x % m

def generar_pem(n, e, d, p, q, dp, dq, q1):
    key = '-----BEGIN RSA PRIVATE KEY-----\n{}-----END RSA PRIVATE KEY-----\n'
    seq = pyasn1.type.univ.Sequence()
    for x in [0, n, e, d, p, q, dp, dq, q1]:
        seq.setComponentByPosition(len(seq), pyasn1.type.univ.Integer(x))
    der = pyasn1.codec.der.encoder.encode(seq)
    return key.format(base64.encodestring(der).decode('ascii'))

# Valores de la clave correspondientes a modulo (n), exponente clave publica (e), exponente clave privada (d) y factores primos del modulo (p y q)
n = 0xC4B07FEFC8E36C05C0DA434221AEBF47DBF70E5189AD892408F3AD81182A9E0700A07C9D7C66C86C7A39602123EE0F605613DE97FDE49FE68396EE0AF3F5F5F3
e = 0x010001
d = 0x2AEC519F2479ECF2FC79254063555D4CF345E10C0E3D9EB60C7611212102FBE52D45E9AA1357813E4460B30889F75FABF72F049D5C3F4AF2F81984267A8BD2B1
p = 0xEABC1DE8A7C5EE2E76B88D39DDF94A0255F5EF3B6209420887C2A008AB474AAD
q = 0xD6820C70A6A2462523D92FD0E1815337249D4FE7C7D7A86EA1242B11AA43F71F

print('')

# Calculo de los valores que faltan en la clave: d mod (p-1), d mod (q-1), (inverse of q) mod p
print('Los valores que faltan en la clave privada RSA son:')
dp = d%(p-1)
print('dp = ', dp)
dq = d%(q-1)
print('dq = ', dq)
q1 = inv(q, p)
print('q1 = ', dq)

print('')

# Generacion de la clave privada RSA original
key = generar_pem(n, e, d, p, q, dp, dq, q1)
print('La clave privada RSA original es:')
print('key = ', key)
f = open("key_3.pem", "w")
f.write(key)
f.close()

Tras ejecutar este script obtenemos la clave privada RSA original, que se graba en el archivo key_3.pem:
Y ya sólo nos queda descifrar el contenido del archivo secret.txt:

openssl rsautl -decrypt -in secret.txt -inkey key_3.pem
Por tanto, la Flag es: flag{gk83h280fwlo2}.

domingo, 25 de noviembre de 2018

Criptografía (CXIV): Solución Reto Cybercamp "It is not Caesar"

En este post la solución a uno de los retos de criptografía de Cybercamp 2017 Online.

Este reto tiene el título "It is not Caesar" y mi valoración sobre su dificultad es: ☆☆.

Su enunciado dice lo siguiente:


A time traveler saved the following message:

ESNTOTGCESLDUMOHIESLF:QACAIEOS

Como recurso asociado al retnos dan el archivo objective1.jpg, que contiene la siguiente imagen:
La primer pista para resolver este reto está en su título, ya que aunque se nos dice que no se trata de un cifrado César, parece indicarnos que se ha utilizado un criptosistema clásico para cifrar el mensaje. Además, entiendo que en esa misma línea va también el enunciado al referirse a un viajero del tiempo. Todo ello, unido a la columna clásica que aparece en la imagen más al "6x5" que figura en la misma, hace que podamos especular que el criptosistema empleado es la transposición columnar simple.

Si no estoy equivocado, para descifrar el criptograma, en primer lugar, disponemos sus caracteres en una tabla de 6 columnas y 5 filas, por columnas, de arriba a abajo y de izquierda a derecha:
Lo siguiente que tenemos que hacer es reordenar las columnas conforme a la clave utilizada. Pero, ¿cuál puede ser la clave?. Puede serlo perfectamente la palabra "BLANCO", que aparece en la imagen y tiene 6 caracteres:
Y, finalmente, obtenemos el texto en claro leyendo la tabla anterior por filas, de izquierda a derecha y de arriba a abajo, es decir:

THE FLAG IS: DICEN QUE ESTAMOS LOCOS

Hemos obtenido la solución de forma manual, pero también lo podríamos haber hecho mediante una de las muchas herramientas de cifrado/descifrado online existentes. Por ejemplo:

Reversing (I): Solución Reto Cybercamp "Oh my G0d!"

En este post la solución a uno de los retos de reversing de Cybercamp 2018 Online.

Este reto tiene el título "Oh my G0d!" y mi valoración sobre su dificultad es: .

Su enunciado dice lo siguiente:


Se ha interceptado un código en la conversación entre dos delincuentes cuyo funcionamiento tendrás que averiguar para llegar a la FLAG.


Como recurso asociado al reto nos dan el archivo medium_8.pyc, es decir, un fichero compilado de python.

Solución: al ejecutar el archivo medium_8.pyc vemos lo siguiente:
Por tanto, queda claro que lo primero que hay que hace es decompilar este archivo.

Para ello utilizo un decompilador de Python y obtengo el siguiente script:
Básicamente, lo que hace este script es comprobar la longitud de la Flag que se introduce; si ésta es menor o igual que 25, múltiplo de 5 y diferente de 0, compara el hash MD5 de los cinco primeros caracteres de la Flag (en decimal) con el primer elemento de la lista SHA1, y si son iguales repite esta comparación con el hash MD5 de los cinco siguientes caracteres de la Flag (en decimal) y el siguiente elemento de la lista SHA1, y así sucesivamente hasta finalizar con todos los caracteres de la Flag (recordar las validaciones sobre su longitud efectuadas anteriormente). Finalmente, si la longitud de la Flag introducida es 25 y previamente el hash MD5 de los 5 grupos de 5 caracteres de la misma han coincidido con los respectivos elementos de la lista SHA1, el script nos confirma que la Flag que hemos introducido es la correcta.

Para obtener la Flag creo el siguiente script de Python:

import hashlib
from colorama import init, Fore, Back, Style

init()

hashes_decimal = [15474416150235697017043280589699178375,
 291181071307803139498438131966588955205,
 109873136872180403981887852593133114079,
 115202235886395046817983293445716821568,
 242056712403709180973346710358452011247]

print('')
print(Fore.WHITE+Back.BLUE+'Los cinco hashes MD5 correspondientes a las 5 palabras de la Flag, cada una de ellas de 5 letras, son:'+Back.RESET)
print('')
hashes_hexadecimal=[]
for hash in hashes_decimal:
    hashes_hexadecimal.append('{:032x}'.format(hash))
    print('{:032x}'.format(hash))

print('')
print(Fore.WHITE+Back.BLUE+'Obtenga la palabra correspondiente a cada uno de los hashes MD5 indicados mediante una herramienta online'+Back.RESET)
print('')

i=1
flag=''
while i < 6:
    print(Style.RESET_ALL+'Introduzca la palabra numero', i, 'de 5 letras de la Flag ..... ', end='')
    palabra=str(input())
    if len(palabra)!=5:
       print (Style.BRIGHT+Fore.RED+'La palabra tiene que tener una longitud de 5 caracteres')
    else:
       hash_palabra=(hashlib.md5(palabra.encode()))
       j=i-1
       if (hash_palabra.hexdigest())==hashes_hexadecimal[j]:
          error=0
          flag=flag+palabra
          print(Style.BRIGHT+Fore.GREEN+'La palabra numero', i, 'de 5 letras de la Flag es correcta')
          i+=1
       else:
          error=1
          print(Style.BRIGHT+Fore.RED+'La palabra numero', i, 'de 5 letras de la Flag es incorrecta')
          print(Style.RESET_ALL+'Desea continuar introduciendo la palabra numero', i, '(S/N) ... ', end='')
          cont=str(input())
          if cont!='S' and cont!='s':
             i=6

if error==0:
   print('')
   print(Style.BRIGHT+Fore.GREEN+'La Flag es:', flag)
else:
   print('')
   print(Style.BRIGHT+Fore.RED+'Flag no encontrada')

En primer lugar y tras ejecutar este script, se nos pide obtener las 5 palabras de 5 letras que se corresponden con los hashes MD5 que se muestran:
Para obtener las 5 palabras utilizo una de la muchas herramientas online existentes para ello:
Como se observa en la figura anterior el texto en claro correspondiente a cada uno de los 5 hashes MD5 introducidos se corresponde, respectivamente, con las siguientes palabras: "check", "group", "zezex", "happy" y "tests".

Introducimos esas 5 palabras y el script, tras validar que cada una de ellas es correcta, nos muestra la Flag (la concatenación de todas ellas):
Por tanto,  la Flag es: checkgroupzezexhappytests.