Quantcast
Channel: El blog de García Larragan y Cía
Viewing all 639 articles
Browse latest View live

Codificación (IV): Solución Reto RingZer0 Team "Ask your grandpa!" (II)

$
0
0
Decía en el post anterior que en un post siguiente iba a resolver el  reto de codificación de la plataforma RingZer0 Team que en él aparece utilizando un script de python.

Decía también que este reto tiene el título "Ask your grandpa!" y mi valoración sobre su dificultad es: ☆☆.

Recuerdo de qué se trataba el reto: sólo nos dan un archivo jpg que contiene una imagen de una tarjeta perforada.


En concreto, la tarjeta de este reto tiene 80 columnas (en la siguiente imagen se ha recortado para que se aprecien mejor las perforaciones) y es la siguiente:

Pues bien, para leer esta tarjeta utilizo el siguiente script de python (ver explicación dada en el post anterior):

#!/usr/bin/env python
# -*- coding: utf-8 -*-

def leer_columna_1(perforacion_1):
    """
    Devuelve la lectura que corresponde a una columna con una perforacion.
    """
    columna=''
    columna=[0,1,2,3,4,5,6,7,8,9,'','-','&']
    if columna=='':
       print('Error lectura columna')
    else:
       return columna[perforacion_1]

def leer_columna_2(perforacion_1,perforacion_2):
    """
    Devuelve la lectura que corresponde a una columna con dos perforaciones.
    """
    columna=''
    if perforacion_1==12:
       columna=['','A','B','C','D','E','F','G','H','I','','','']
    elif perforacion_1==11:
       columna=['','J','K','L','M','N','O','P','Q','R','','','']
    elif perforacion_1==0:
       columna=['','/','S','T','U','V','W','X','Y','Z','','','']
    elif perforacion_1==2:
       columna=['','','','','','','','',':','','','','']
    elif perforacion_1==3:
       columna=['','','','','','','','','#','','','','']
    elif perforacion_1==4:
       columna=['','','','','','','','','@','','','','']
    elif perforacion_1==5:
       columna=['','','','','','','','','','','','','']
    elif perforacion_1==6:
       columna=['','','','','','','','','=','','','','']
    elif perforacion_1==7:
       columna=['','','','','','','','','"','','','','']
    else:
       print('Error lectura columna')
    if columna=='':
       print('Error lectura columna')
    else:
       return columna[perforacion_2]

def leer_columna_3(perforacion_1,perforacion_2,perforacion_3):
    """
    Devuelve la lectura que corresponde a una columna con tres perforaciones.
    """
    columna=''
    if perforacion_1==12:
       if perforacion_2==3:
          columna=['','','','','','','','','.','','','','']
       elif perforacion_2==4:
          columna=['','','','','','','','','<','','','','']
       elif perforacion_2==5:
          columna=['','','','','','','','','(','','','','']
       elif perforacion_2==6:
          columna=['','','','','','','','','+','','','','']
       elif perforacion_2==7:
          columna=['','','','','','','','','|','','','','']
       elif perforacion_2==8:
          columna=['','','','','','','','','','','','','']
       else:
          print('Error lectura columna')
    elif perforacion_1==11:
       if perforacion_2==2:
          columna=['','','','','','','','','!','','','','']
       elif perforacion_2==3:
          columna=['','','','','','','','','$','','','','']
       elif perforacion_2==4:
          columna=['','','','','','','','','*','','','','']
       elif perforacion_2==5:
          columna=['','','','','','','','',')','','','','']
       elif perforacion_2==6:
          columna=['','','','','','','','',';','','','','']
       elif perforacion_2==7:
          columna=['','','','','','','','','','','','','']
       else:
          print('Error lectura columna')
    elif perforacion_1==0:
       if perforacion_2==3:
          columna=['','','','','','','','',',','','','','']
       elif perforacion_2==4:
          columna=['','','','','','','','','%','','','','']
       elif perforacion_2==5:
          columna=['','','','','','','','','_','','','','']
       elif perforacion_2==6:
          columna=['','','','','','','','','>','','','','']
       elif perforacion_2==7:
          columna=['','','','','','','','','?','','','','']
       else:
          print('Error lectura columna')
    else:
       print('Error lectura columna')
    if columna=='':
       print('Error lectura columna')
    else:
       return columna[perforacion_3]

# Tarjeta a leer: para cada columna de la tarjeta incluir, separados por comas, numero de perforaciones y posiciones de las perforaciones.

tarjeta=[2,12,6,2,11,3,2,12,1,2,12,7,1,11,2,12,4,2,12,2,1,11,2,12,9,2,11,5,2,12,4,2,12,5,2,0,7,2,2,8,1,3,1,8,1,0,1,1,3,0,3,8,2,0,5,2,12,1,2,11,3,2,2,8,2,12,6,2,11,3,2,12,1,2,12,7,1,11,2,12,2,1,4,1,9,1,3,1,8,1,0,1,1,2,12,3,2,12,4,2,12,2,1,11,1,8,1,3,1,1,2,12,1,2,12,2,2,12,4,2,12,4,2,12,6,2,12,1,1,6,1,2,1,8,2,12,1,2,12,3,1,3,1,1]

lectura_tarjeta=""

i=0
while i < len(tarjeta):
    if tarjeta[i]==0:
       lectura_tarjeta=lectura_tarjeta+""
       i+=1
    elif tarjeta[i]==1:
       lectura_tarjeta=lectura_tarjeta+str(leer_columna_1(tarjeta[i+1]))
       i+=2
    elif tarjeta[i]==2:
       lectura_tarjeta=lectura_tarjeta+str(leer_columna_2(tarjeta[i+1],tarjeta[i+2]))
       i+=3
    else:
       lectura_tarjeta=lectura_tarjeta+str(leer_columna_3(tarjeta[i+1],tarjeta[i+2],tarjeta[i+3]))
       i+=4

print(lectura_tarjeta)

Ejecuto este script:

Y, como se observa en la figura anterior, la solución a este reto es: FLAG-B493801CDB-831ABDDFA628AC31.

Codificación (V): Solución Reto RingZer0 Team "Ask your grandpa again!"

$
0
0
En este post la solución a otro de los retos de codificación de la plataforma RingZer0 Team.

Este reto tiene el título "Ask your grandpa again!" y mi valoración sobre su dificultad es:  .

Se trata de un reto en el que al igual que en éste se ven involucradas tarjetas perforadas, pero en este caso su resolución presenta una mayor dificultad. 


Nos dan seis ficheros jpg (card, grandpa, my, programming, punch y yolo) que contienen sendas imágenes de tarjetas perforadas.

Las imágenes de las tarjetas perforadas son las siguientes (en el orden indicado en el párrafo anterior):
Para leer las seis tarjetas adapto el script de python que utilicé en este post, de la siguiente manera:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

def leer_columna_1(perforacion_1):
    """
    Devuelve la lectura que corresponde a una columna con una perforacion.
    """
    columna=''
    columna=[0,1,2,3,4,5,6,7,8,9,'','-','&']
    if columna=='':
       print('Error lectura columna')
    else:
       return columna[perforacion_1]

def leer_columna_2(perforacion_1,perforacion_2):
    """
    Devuelve la lectura que corresponde a una columna con dos perforaciones.
    """
    columna=''
    if perforacion_1==12:
       columna=['','A','B','C','D','E','F','G','H','I','','','']
    elif perforacion_1==11:
       columna=['','J','K','L','M','N','O','P','Q','R','','','']
    elif perforacion_1==0:
       columna=['','/','S','T','U','V','W','X','Y','Z','','','']
    elif perforacion_1==2:
       columna=['','','','','','','','',':','','','','']
    elif perforacion_1==3:
       columna=['','','','','','','','','#','','','','']
    elif perforacion_1==4:
       columna=['','','','','','','','','@','','','','']
    elif perforacion_1==5:
       columna=['','','','','','','','','','','','','']
    elif perforacion_1==6:
       columna=['','','','','','','','','=','','','','']
    elif perforacion_1==7:
       columna=['','','','','','','','','"','','','','']
    else:
       print('Error lectura columna')
    if columna=='':
       print('Error lectura columna')
    else:
       return columna[perforacion_2]

def leer_columna_3(perforacion_1,perforacion_2,perforacion_3):
    """
    Devuelve la lectura que corresponde a una columna con tres perforaciones.
    """
    columna=''
    if perforacion_1==12:
       if perforacion_2==3:
          columna=['','','','','','','','','.','','','','']
       elif perforacion_2==4:
          columna=['','','','','','','','','<','','','','']
       elif perforacion_2==5:
          columna=['','','','','','','','','(','','','','']
       elif perforacion_2==6:
          columna=['','','','','','','','','+','','','','']
       elif perforacion_2==7:
          columna=['','','','','','','','','|','','','','']
       elif perforacion_2==8:
          columna=['','','','','','','','','','','','','']
       else:
          print('Error lectura columna')
    elif perforacion_1==11:
       if perforacion_2==2:
          columna=['','','','','','','','','!','','','','']
       elif perforacion_2==3:
          columna=['','','','','','','','','$','','','','']
       elif perforacion_2==4:
          columna=['','','','','','','','','*','','','','']
       elif perforacion_2==5:
          columna=['','','','','','','','',')','','','','']
       elif perforacion_2==6:
          columna=['','','','','','','','',';','','','','']
       elif perforacion_2==7:
          columna=['','','','','','','','','','','','','']
       else:
          print('Error lectura columna')
    elif perforacion_1==0:
       if perforacion_2==3:
          columna=['','','','','','','','',',','','','','']
       elif perforacion_2==4:
          columna=['','','','','','','','','%','','','','']
       elif perforacion_2==5:
          columna=['','','','','','','','','_','','','','']
       elif perforacion_2==6:
          columna=['','','','','','','','','>','','','','']
       elif perforacion_2==7:
          columna=['','','','','','','','','?','','','','']
       else:
          print('Error lectura columna')
    else:
       print('Error lectura columna')
    if columna=='':
       print('Error lectura columna')
    else:
       return columna[perforacion_3]

# Tarjetas a leer: para cada columna de cada tarjeta incluir, separados por comas, numero de perforaciones y posiciones de las perforaciones.

tarjetas=[[0,1,1,1,3,1,3,1,7,0,2,12,6,2,11,6,2,11,9,2,11,4,2,12,1,2,0,3,3,12,5,8,1,1,1,1,2,12,8,2,12,6,2,11,3,2,12,1,2,12,7,1,11,2,12,4,2,12,6,2,12,5,2,12,2,1,0,2,12,4,3,0,3,8,2,12,9,1,4,3,0,3,8,1,1,2,12,8,1,11,3,0,3,8,2,12,9,1,3,3,0,3,8,1,1,1,0,2,12,8,2,12,6,2,12,4,2,12,2,2,12,5,2,12,3,2,12,4,2,12,6,1,3,1,9,2,12,4,3,0,3,8,2,12,9,1,3,3,11,5,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,3,1,3,1,7,1,0,1,0,1,5,1,0],
[0,0,0,0,0,0,2,12,5,2,11,5,2,12,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,3,1,3,1,7,1,0,1,0,1,6,1,0],
[0,0,0,0,0,0,2,0,6,2,11,9,2,12,9,2,0,3,2,12,5,3,12,5,8,1,6,3,0,3,8,1,1,1,3,1,3,1,7,3,11,5,8,2,11,1,3,12,6,8,1,2,1,9,3,0,3,8,3,12,5,8,2,11,1,2,0,1,1,4,3,11,5,8,3,12,6,8,1,2,1,0,3,0,3,8,2,12,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,3,1,3,1,7,1,0,1,0,1,4,1,0],
[0,0,0,0,0,0,2,11,7,2,11,9,2,11,6,2,12,7,2,11,9,2,12,1,2,11,4,0,2,0,6,2,12,6,2,11,3,2,12,1,2,12,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,3,1,3,1,7,1,0,1,0,1,1,1,0],
[0,0,0,0,0,0,2,12,9,2,6,8,1,9,1,3,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,3,1,3,1,7,1,0,1,0,1,2,1,0],
[0,0,0,0,0,0,2,11,1,2,6,8,1,2,1,8,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,3,1,3,1,7,1,0,1,0,1,3,1,0]]

i=0
j=0
while i < len(tarjetas):
      lectura_tarjeta=""
      k=0
      while k < len(tarjetas[j]):
            if tarjetas[j][k]==0:
               lectura_tarjeta=lectura_tarjeta+""
               k+=1
            elif tarjetas[j][k]==1:
               lectura_tarjeta=lectura_tarjeta+str(leer_columna_1(tarjetas[j][k+1]))
               k+=2
            elif tarjetas[j][k]==2:
               lectura_tarjeta=lectura_tarjeta+str(leer_columna_2(tarjetas[j][k+1],tarjetas[j][k+2]))
               k+=3
            else:
               lectura_tarjeta=lectura_tarjeta+str(leer_columna_3(tarjetas[j][k+1],tarjetas[j][k+2],tarjetas[j][k+3]))
               k+=4
      print(lectura_tarjeta)
      j+=1
      i+=1

Ejecuto este script:
El resultado de la lectura de las tarjetas me resulta familiar. Yo diría que es un pequeño programa escrito en Fortran.

Los dos primeros lenguajes de programación que estudié, allá por 1983 (hace ya unos 35 años), fueron Cobol y Fortran. Empiezo a sospechar que el abuelo del título de este reto soy yo :(.

En el enlace referido a tarjetas perforadas que he puesto al inicio de este post se ve un ejemplo de una tarjeta perforada de un programa Fortran (fuente: Wikipedia):
Ordeno las sentencias y utilizo una herramienta online para ejecutar el código obtenido con el script de python y ver el resultado:
Y, por tanto, la solución a este reto es: FLAG-DFEB0D2829-720FDBECDF39D931.

Programación (X): Reto 2

$
0
0
Continúo con la serie de posts dedicada a retos de programación. En esta ocasión se trata de un reto de dificultad media en el que también se ven involucradas la codificación y la esteganográfia.

Doy prevalencia a la programación sobre las otras categorías en la catalogación de este reto porque ésta puede ayudar bastante en su resolución.

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 2: "Reunión secreta".

La policía vigila de cerca a los miembros de una secta satánica, ya que sospecha que están involucrados en la comisión de diversos delitos, y ha interceptado las comunicaciones entre sus líderes. Hasta el momento sabe cuándo se Reunirán la próxima vez, pero no ha podido averiGuar dónde. Entre los ficheros interceptados se encuentra el fichero asociado como recurso a este resto y en el que se cree que puede esconderse algún tipo de mensaje; incluso quizá el lugar en el que se mantendrá dicha reunión. ¿Puedes oBtener el mensaje oculto en la imagen y ayudar a la policía a su detención?. 

Dificultad:
Tipo:          Codificación, Esteganografía y Programación.

Recursos:   Reto_2.png.

******** __/__/____
Pista 1:    Por publicar. 

******** __/__/____
Solución: Por publicar. 

******** PRÓXIMO RETO
Reto 3 (Programación): Por publicar.

Programación (XI): Solución Reto 2

$
0
0
El  enunciado del reto de programación que puse en este post era el siguiente:

"La policía vigila de cerca a los miembros de una secta satánica, ya que sospecha que están involucrados en la comisión de diversos delitos, y ha interceptado las comunicaciones entre sus líderes. Hasta el momento sabe cuándo se Reunirán la próxima vez, pero no ha podido averiGuar dónde. Entre los ficheros interceptados se encuentra el fichero asociado como recurso a este resto y en el que se cree que puede esconderse algún tipo de mensaje; incluso quizá el lugar en el que se mantendrá dicha reunión. ¿Puedes oBtener el mensaje oculto en la imagen y ayudar a la policía a su detención?".

1.- En la primera pista que puse para resolver este reto decía que no es difícil darse cuenta de que dentro del fichero de imagen asociado al reto (Reto_2.pnghay otros dos archivos (Lugar cita.zip y password.txt), ya que para ello basta abrir ese fichero con un software de compresión de archivos o con un editor hexadecimal.
Lógicamente, lo más probable es que la solución a este reto se encuentre en el archivo comprimido Lugar cita.txt que contiene el fichero Lugar cita.zippero al intentar abrirlo o descomprimirlo nos pide una contraseña que desconocemos.

2.- Por tanto, centramos ahora nuestra atención en el otro archivo, password.txt, que podría contener la contraseña que necesitamos.

También decía en la primera pista que puse para resolver este reto que las letras en mayúsculas del enunciado pueden dar una pista importante para entender el contenido de este segundo archivo. A la vista del contenido del archivo password.txt, las letras 'R', 'G' y 'B' pueden hacer referencia a los canales 'Red' (Rojo), 'Green' (Verde) y 'Blue' (Azul) de una imagen en la que los pixeles de la misma contendrían valores en un rango de entre 0 y 255 para la intensidad correspondiente a cada uno de dichos canales.

Si esto es así podemos intentar construir una imagen a partir de los valores que contiene el archivo password.txt, una lista plana de n elementos; donde n/3 sería el número total de pixeles de la imagen y cada uno de los elementos se correspondería con un elemento de una tupla de tres elementos para los canales 'RGB' (cuatro elementos para 'RGBA', caso de que la imagen cuente con canal alfa).

Para ello utilizo el siguiente script de python:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

from PIL import Image

def factorizacion_2(n):
# Descomponer un numero en dos factores no triviales
    factores=[]
    i=2
    while i <= int(n**(1/2)):
        if (n%i==0):
           factores.append(i)
           factores.append(int(n/i))
        i+=1
    return factores

# Abrir y leer fichero
fichero=open('password.txt','r')
valores=fichero.read()

# Contar numero de pixels
numero_pixels=int((valores.count(',')+1)/3)
print('Numero de pixels......................................',numero_pixels)

valores=valores.split(",")

factores=factorizacion_2(numero_pixels)
print('Los pares de posibles valores para alto y ancho son...',factores)
x=0
y=x+1
#Crear imágenes considerando los posibles valores para alto y ancho de la imagen y a partir de los valores de los pixels
imagen_ok='N'

while x < len(factores) and (imagen_ok=='N' or imagen_ok=='n'):
    print('*** Probamos con:*************************************')
    print('Ancho imagen..........................................',factores[y])
    print('Alto imagen...........................................',factores[x])
    imagen=Image.new('RGB',(factores[y],factores[x]))
    pixels=imagen.load()
    k=0
    for i in range(factores[x]):
        for j in range(factores[y]):
            pixels[j,i]=(int(valores[k]),int(valores[k+1]),int(valores[k+2]))
            k+=3
    imagen.show()
    imagen_ok= str(input('La imagen es correcta (S/N)?.......................... '))
    if imagen_ok=='N' or imagen_ok=='n':
       if x%2==0:
          x+=1
          y-=1
       else:
          x+=1
          y+=3
    else:
       if imagen_ok=='S' or imagen_ok=='s':
          imagen.save('password.png','PNG')

# Cerrar fichero e imagen
fichero.close()
imagen.close()

Ejecuto este script:
Inicialmente vemos que la imagen tendría 328.635 pixeles, y el script empieza a probar todos los posibles valores para el ancho y alto de la imagen. Tras cada una de las pruebas que realiza nos muestra la imagen resultado y nos pregunta si es correcta.

Finalmente, para un ancho de 603 pixeles y un alto de 545 pixeles, el script no muestra la siguiente imagen:
Con lo que muy probablemente "4k3l4rr3" sea la contraseña del archivo Lugar cita.zip. Por cierto, para quienes no sepan lo que significa "Akelarre" en euskera ("Aquelarre" en castellano) aquí les dejo este enlace de la Wikipedia.

Probamos a abrir el archivo Lugar cita.txt con la contraseña "4k3l4rr3", efectivamente es la password correcta, y nos encontramos con la siguiente cadena de caracteres:

"4qCg4qC14qCl4qCb4qCB4qCX4qCX4qCB4qCN4qCl4qCX4qCZ4qCK"

3.- ¿Qué significado puede tener esto?. Pues veamos que ocurre si utilizamos un decodificador de Base64 de los muchos que existen online:
Yo diría que lo que obtenemos como salida es código Braille y, aunque existen diversas herramientas online para ello, vamos a decodificarlo manualmente, de la siguiente manera:
Con lo que ya tendríamos la solución a este reto. El lugar de la reunión es: "Zugarramurdi"; un precioso pueblo navarro y con una cierta relación con el tema escogido para este reto, pero eso ya lo dejo para que aquellos que no lo conozcan y que estén interesados investiguen por Internet.

Criptografía (CXI): Solución Reto Atenea "Durin's Gates" (I)

$
0
0
En este post la primera entrada correspondiente a la solución al quinto reto de "Criptografía y Esteganografía" de la plataforma ATENEA del CCN-CERT con desafíos de seguridad.

Con esta primera entrada pretendo aportar pistas para que participen en su resolución aquellos lectores de este humilde blog que estén interesados en este tipo de retos, pero sin facilitarles la solución completa al mismo.

En concreto, este quinto reto tiene el título de "Durin´s Gates" y tras su resolución se obtienen 300 puntos (Dificultad: ☆☆).

Su enunciado dice lo siguiente:


Informaciones fidedignas han revelado que cierto grupo de atacantes utilizan técnicas de esteganografía y cifrado junto con determinados servicios online para comunicarse entre ellos y compartir información confidencial. Recientemente se ha podido interceptar una de las imágenes empleadas para compartir este tipo de mensajes. Se sospecha que la imagen se ha enviado para remitir una clave compartida con la que es posible acceder a uno de los servicios en la nube utilizados por los atacantes.

Tu objetivo será investigar el fichero adjunto .jpg y averiguar si está relacionado con una clave o mensaje de estas características.


Solución: la imagen asociada al reto es la siguiente.
La Wikipedia nos cuenta que, en la novela "El Señor de los Anillos"las Puertas de Durin eran la entrada occidental al reino de Khazad-Dûm (la Mina del Enano) y que fueron construidas en colaboración por Elfos y Enanos. Desde el exterior, ninguna fuerza enana, élfica o humana podía mover las puertas, excepto la contraseña inscrita en ellas; entonces se abrían solas, hacia los lados, hasta tocar la pared de roca.

Investigando un poco más en Internet encontré que la primera línea de la inscripción decía: "Puertas de Durin, Señor de Moria. Di amigo, y entra", y que la contraseña era "Amigo" en élfico, es decir, "Mellon". Cuando intentaba resolver este reto no tenía ni idea de si esto podía ser útil para solucionarlo, pero me había entretenido buscando la información :).

Vamos a ver qué podemos hacer para resolver el reto. En primer lugar, descargamos la imagen asociada al mismo, la abrimos con el software ExifToolGUI y analizamos los metadatos:
Salvo en la etiqueta "Artist" no veo nada que me llame la atención.

Tras realizar varias pruebas con diverso software de estegoanálisis sin obtener ningún resultado, pruebo con steghide, con el siguiente comando:

steghide extract -sf doors_of_durin-f686f3e1aa18d5e3f4261bea89a24c17.jpg

y nos pide una contraseña. Tras introducir como password "Mellon" obtenemos el archivo url.txt.
Accedemos con el navegador a la dirección indicada y nos encontramos con el siguiente archivo (SgaSizcn.txt):
¿Qué puede significar ésto?, ¿Cómo continuamos para resolver este reto?.

Se admiten comentarios, sugerencias, soluciones... ;)

Criptografía (CXII): Solución Reto Atenea "Durin's Gates" (II)

$
0
0
Ésta es la segunda parte de este post. En estas entradas voy compartiendo la solución al quinto reto de "Criptografía y Esteganografía" de la plataforma ATENEA del CCN-CERT con desafíos de seguridad.

En esta segunda entrega adelantaré en la solución del reto, pero todavía quedará una tercera entrada en la que desvelaré su solución.

En el citado primer post me quedé en la obtención de un archivo de texto(SgaSizcn.txt), el siguiente:
Y preguntaba: ¿Qué puede significar ésto?. ¿Cómo continuamos para resolver este reto?. Pues bien, el contenido de este archivo de texto parece ser código base64, por lo que podemos utilizar openssl para decodificarlo, mediante el siguiente comando:

openssl base64 -d -in SgaSizcn.txt -out SgaSizcn_decoded.txt

Abrimo el archivo así obtenido (SgaSizcn_decoded.txt) con un editor hexadecimal y vemos lo siguiente:
Pues otra vez creo que son pertinentes las preguntas que hacía al final del post anterior: ¿Qué puede significar ésto?, ¿Cómo continuamos para resolver este reto?.

Se admiten comentarios, sugerencias, soluciones... ;)

Criptografía (CXIII): Solución Reto Atenea "Durin's Gates" (III)

$
0
0
Tercera y última entrega de las entradas en las que voy compartiendo la solución al quinto reto de "Criptografía y Esteganografía" de la plataforma ATENEA del CCN-CERT con desafíos de seguridad.

En esta entrada desvelaré ya la solución.

En el primer post llegamos a la obtención de un archivo de texto (SgaSizcn.txt) cuyo contenido estaba codificado en base64, mientras que en el segundo post me quedé en la obtención de otro archivo de texto (SgaSizcn_decoded.txt) con la decodificación del contenido del anterior:
En este caso la pista puede estar en la cabera del archivo. La cadena de caracteres ASCII "Salted__" puede indicar que el contenido del archivo se ha cifrado utilizando openssl.

Desconozco si es posible, a partir de un archivo cifrado, conocer qué algoritmo de cifrado concreto se ha utilizado (entiendo que no es posible, pero si algún lector de este blog me puede sacar del error se lo agradecería), por lo que me temo que sólo me queda probar diferentes alternativas.

Pero hay un problema adicional: ¿Cuál será la password para descifrar el contenido de este archivo?. Cuando me pregunté ésto, recordé el metadato en la etiqueta "Artist" del fichero .jpg asociado al reto:
¿Será esta la password?. Probemos a descifrar el archivo (SgaSizcn_decoded.txt) con el siguiente comando:

openssl aes-256-cbc -d -salt -in SgaSizcn_decoded.txt -out SgaSizcn_deciphered

Y nos pide que introduzcamos la password (enter aes-256-cbc decryption password):

Introducimos 68913499125FAA y obtenemos el fichero de salida (SgaSizcn_deciphered).

Abrimos ese archivo con un editor hexadecimal:
Con lo que parece que el archivo obtenido (SgaSizcn_deciphered) es un  archivo multimedia. En concreto y por su cabecera parecer ser un archivo .mp4. Lo reproducimos:
Y ya podemos ver la solución al reto: "Minas_Tirith_2017" (recordar que hay que introducirla con formato flag{md5}).

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

$
0
0
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.

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

$
0
0
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:

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

$
0
0
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 -inkey_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}.

Criptografía (CXVI): Reto 23

$
0
0
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 sospecho 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.

******** __/__/____
Pista 1:     Por publicar.

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

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

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

$
0
0
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 recta 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}).

Criptografía (CXVIII): Solución Reto 23

$
0
0
El  enunciado del reto de criptografía que puse en este post era el siguiente:

"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 corresponden 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?".

Solución: Decía en la primera pista que puse para intentar ayudar a resolver este reto que aunque por el contexto de éste es muy fácil deducir la contraseña del archivo reto23.zip, 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).

Pare ello utilizo 'pydictor',una herramienta programada en python para construir diccionarios para ataques de fuerza bruta, de la siguiente manera:
Tal y como se ve en la figura anterior y también decía en la citada primera pista, como se trata de permutaciones de 8 elementos (P8), el diccionario obtenido (diccionario.txt) tiene P= 8! = 40.320 entradas.

Lo siguiente que tenemos que hacer para 'crackear' la contraseña del archivo zip asociado al reto es obtener el hash correspondiente al mismo. Para ello utilizo 'John the Ripper', de la siguiente manera:
A partir de los dos archivos así obtenidos (diccionario.txt y reto23.hash) ejecuto john y obtengo la contraseña:
john --wordlist=diccionario.txt reto23.hash
Por tanto, la contraseña del archivo reto23.zip es "B4PH0M37", ya decía que no era muy difícil deducirla :).

Una vez conseguida la contraseña extraemos la imagen (reto23.png) que contiene el archivo zip.

A la vista de esta imagen enseguida reconozco caracteres correspondientes al alfabeto templario (ya conté en un post de este blog la historia, sea cierta o no, que ha inspirado este reto y en el que hablé del cifrado templario).

Utilizando una herramienta online para ello, obtengo el texto en claro correspondiente:
Es decir, ahora tenemos los siguientes caracteres:

OZXOZEVVH:
   QZXIFVHWVNLOZB

¿Qué puede significar esto?. Pues bien, parece claro que se trata de un texto cifrado, pero: ¿Cuál es el criptosistema que se ha empleado?. La segunda y tercera pistas que puse para intentar ayudar a resolver este reto nos dan la clave para deducirlo, ya que la imagen parece ser el reflejo en un espejo de la que sirve para ilustrar los posts de este reto:
Y el criptosistema de sustitución monoalfabética simple al que también se le conoce como "método de espejo" es el Atbash.

Utilizando otra herramienta online, obtengo el siguiente texto en claro:
Por tanto, la solución a este reto es: JACQUESDEMOLAY, el último Gran maestre de la orden del temple.

Criptografía (CXIX): Solución Reto Cybercamp "La orden del Temple"

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

Este reto tiene el título "La orden del Temple" y mi valoración sobre su dificultad es: .

Su enunciado dice lo siguiente:


Se incauta a un sospechoso de terrorismo su equipo, dentro de éste se encuentran ficheros que se podrían considerar de vital importancia para continuar con la investigación, pero muchos de esos ficheros están cifrados y se sabe que mediante PGP simétrico.

Gracias a la investigación del sospechoso tus compañeros te han dado las siguientes pautas que sigue el sospechoso a la hora de crear sus contraseñas:

- Son de longitud de 6 a 7 caracteres.
- Sólo contienen letras minúsculas.
- Sólo se utilizan estas letras: eghotu.
No se repite ninguna de las letras de la contraseña.
- Algunas de ellas contienen dos números entre estos: 0134.

Tu trabajo será intentar descifrar el fichero gracias a la investigación realizada sobre el sospechoso y a los datos proporcionados para determinar si el contenido es de vital importancia para la investigación en curso.

Solución: Reto muy parecido a dos de los últimos de los que he tratado en este blog ("Really???" y "BAPHOMET", respectivamente), pero intentaré aportar alguna novedad en su resolución.

Como recurso asociados al reto nos dan el archivo medium_11.gpg, pero: ¿Qué tipo de archivo es?. Aunque el enunciado del reto y su extensión nos dan una pista muy importante, utilizo 'TrID', una utilidad para averiguar el tipo de fichero a partir de su firma:
Por lo que deduzco que se trata de un mensaje cifrado con clave simétrica PGP.

Como primer paso para intentar 'crackear' la contraseña con la que descifrar el mensaje que contiene el archivo asociado al reto (medium_11.gpg) obtengo el hash correspondiente al mismo. Para ello utilizo 'John the Ripper', de la siguiente manera:
Ahora, como segundo paso, genero el diccionario con la pautas seguidas por el sospechoso a la hora de crear sus contraseñas, o, al menos, algo parecido :). Para ello utilizo el siguiente script de python:

import wordlist

f = open('diccionario.txt','w')

generator = wordlist.Generator('eghotu0134')

for entrada in generator.generate(6,7):
    f.write(entrada+'\n')

f.close()
Con el archivo hash (medium_11.hash) y el diccionario (diccionario.txt) así obtenidos, ejecuto 'john' para obtener la contraseña:

john --wordlist=diccionario.txt medium_11.hash
Parece que la contraseña con la que descifrar el mensaje que contiene el archivo asociado al reto (medium_11.gpg) es "eg1u03".

El siguiente paso consiste en descifrar el mensaje:
Al abrir el fichero descifrado (medium_11) con un editor hexadecimal o el Bloc de notas vemos que se trata de una archivo de imagen .png:
Y ya el último paso consiste en decodificar los caracteres del alfabeto templario que figuran en la imagen. Para ello utilizo una herramienta online especializada:
Por tanto, la Flag es: ERESMUYGOLOSO.

Criptografía (CXX): Reto 24

$
0
0
Otro reto de dificultad media sobre criptografía. En esta ocasión se ve involucrado un criptosistema clásico muy relacionado con el código morse.

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 24: "Morse".

Para descifrar el contenido del archivo asociado al reto y así obtener la solución necesitarás la clave: U0FNVUVM.

Dificultad:
Tipo:           Criptografía.

Recursos:   reto24.wav.

******** __/__/____
Pista 1:     Por publicar.

******** __/__/____
Pista 2:     Por publicar.

******** __/__/____
Pista 3:     Por publicar.

******** __/__/____
Solución: Por publicar.

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

Criptografía (CXXI): Solución Reto backdoor "complex-rsa"

$
0
0
En este post la solución a uno de los retos de criptografía de backdoorctf17.

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

Su enunciado dice lo siguiente:


noob heard that single RSA encryption can be cracked sometimes due to bad implementation, so he encrypted the message twice. See if you can break it.

Y como recursos asociados al reto nos dan los siguientes:

- pubkey1.
- pubkey2.
- flag.enc.

Solución: creo un script en python para obtener el módulo y el exponente (n y e, respectivamente) de ambas claves públicas.

from Crypto.PublicKey import RSA

# Obtener parametros de ambas claves publicas

print ''
print 'pubkey1:'

pubkey1 = open('pubkey1', 'r')
param_pubkey1 = RSA.importKey(pubkey1.read())
n1 = param_pubkey1.n
e1 = param_pubkey1.e
print '   n1  ...:', n1
print '   e1  ...:', e1
pubkey1.close()

print ''
print 'pubkey2:'

pubkey2 = open('pubkey2', 'r')
param_pubkey2 = RSA.importKey(pubkey2.read())
n2 = param_pubkey2.n
e2 = param_pubkey2.e
print '   n2  ...:', n2
print '   e2  ...:', e2
pubkey2.close()

Ejecuto este script y obtengo lo siguiente:
Como se observa en la figura anterior el módulo (n) es el mismo en ambos casos, en decimal:

554264859105764813308660999731057971935100899008191382001838196926947542874512190874402841957978974562758951331436856029517893995971179950228409634742368823490858553015862605452077729540463185207987338059905256552215054036643656077780363670065154151957507791559734841291875379738678210733333998195096643491711

Mientras que el exponente público de la primera clave en decimal es:

37507589401

y el exponente público de la segunda clave en decimal es:

4268405784672563577566143285906824408738650526784746749170468318123056940297449811287105187623419766934370809781249030117023876215912795037797160740003478418767197450012472858547143622542113157392499087427939336504102036205305906052998841826136038160560099357503377453502865716581429205507834478651

Tal y como se puede ver el exponente público de la segunda clave es muy grande, por lo que el exponente de la clave privada (d) podría estar expuesto (ver ataque de Wiener), ya que en consecuencia éste sería pequeño.

Por otra parte, en el enunciado se nos dice que el mensaje en claro (m) se cifró dos veces, es decir, entiendo que el criptograma (c2) que contiene el archivo flag.enc se obtuvo de la siguiente manera:

c1 = m^e1 mod n

c2 = c1^e2 mod n

Por lo que:

c2 = (m^e1 mod n) ^e2 mod n = m^(e1*e2) mod n 

Dicho todo esto creo el siguiente script en python para obtener la flag:

import owiener
import hashlib

# Ataque de Wiener

print('')
print ('*** Ataque de Wiener')

e1 = 37507589401
e2 = 4268405784672563577566143285906824408738650526784746749170468318123056940297449811287105187623419766934370809781249030117023876215912795037797160740003478418767197450012472858547143622542113157392499087427939336504102036205305906052998841826136038160560099357503377453502865716581429205507834478651
e  = e1*e2
n  = 554264859105764813308660999731057971935100899008191382001838196926947542874512190874402841957978974562758951331436856029517893995971179950228409634742368823490858553015862605452077729540463185207987338059905256552215054036643656077780363670065154151957507791559734841291875379738678210733333998195096643491711
d  = owiener.attack(e, n)

print ('d ...........:', d)

# Descifrado

print('')
print ('*** Descifrado')

flag = open('flag.enc', 'rb')
c  = flag.read()
c  = int(c.hex(),16)

m  = pow(c, d, n)

print ('c ...........:', c)
print('')
print ('m ...........:', m)

mh = hex(m)

print('')
print ('m (hex) .....:', mh)

# Representacion ascii del texto en claro en hexadecimal

print('')
print ('*** Flag')

ma = bytearray.fromhex(mh[2:]).decode()

print('m (ascii) ...:', ma)

# Hash SHA256 de la flag

print('')
print ('*** Hash SHA256 de la flag')

ms = hashlib.sha256(ma.encode('utf-8')).hexdigest()

print('m (SHA256) ...:', ms)

Y tras ejecutarlo obtengo la flag y el hash SHA256 de la misma para introducirlo como solución al reto:

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

$
0
0
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.

Criptografía (CXXIII): Solución Reto 24

$
0
0
El  enunciado del reto de criptografía que puse en este post era el siguiente:

"Para descifrar el contenido del archivo asociado al reto y así obtener la solución necesitarás la clave: U0FNVUVM".

SoluciónEn la primera pista que puse para ayudar a resolver este reto decía que si sabías o averiguabas quién es la persona que aparece en la imagen que ilustra este post entonces también sabrías el criptosistema involucrado en este reto. Pues bien, busco con Google imágenes y obtengo: "Consulta más probable para esta imagen: Georges Painvin"y la Wikipedia nos cuenta que éste fue un criptoanalista francés cuyo principal logro fue romper el cifrado ADFGVX utilizado por el ejército alemán durante la Primera Guerra Mundial.
Con lo que, como también decía, puedo concluir que el criptosistema involucrado en este reto es el ADFGVX (ya escribí en su día varios posts sobre él en este blog. Ver primero de ellos).

Además, en el post en el que planteé el enunciado de este reto decía que el criptosistema involucrado está muy relacionado con el código morse (las letras ADFGVX, que son las únicas que aparecen en los criptogramas cifrados mediante este método, fueron elegidas porque son muy diferentes entre sí en código morse y ésto evitaba errores en la transmisión de los mensajes), mientras que en la segunda pista decía que el archivo de audio asociado al reto (reto24.wav) contiene precisamente una señal en código morse.

Esto último es fácilmente comprobable escuchando el archivo de audio y viendo el espectrograma de la señal:
Si decodifico esta señal enseguida reconozco y, por tanto, se confirma que el criptosistema empleado es el ADFGVX, y obtengo el criptograma.
Ahora ya, mediante la clave que se proporciona en el enunciado del reto ("U0FNVUVM"), debería estar en disposición de revertir la fase de transposición de columnas realizada en el cifrado del texto en claro, pero ésta ni parece ser muy adecuada para este criptosistema (en principio debería contener sólo letras y ésta contiene el número 0) ni parece tener nada que ver con el código morse, tal y como se afirma en la primera pista dada para ayudar a resolver este reto.

Veo si la clave dada está codificada de alguna manera, para lo que, en primer lugar, pruebo a ver si está codificada en base64:
Bastante mejor :) (ver Samuel Morse).

Utilizando como clave "SAMUEL" revierto la fase de transposición de las columnas realizada en el cifrado del texto en claro, de la siguiente manera:

1.- El criptograma tiene 442 caracteres de largo y la clave 6. Divido 442 entre 6 y obtengo que el cociente es 73 y el resto 4, por lo que tendría una tabla de 6 columnas, 4 de ellas con 74 caracteres y 2 con 73.

2.- El orden de las 6 columnas de la tabla (de izquierda a derecha) se correspondería con el orden alfabético de los 6 caracteres de la clave, es decir, sería: "A", "E", "L", "M", "S", "U".

3.- Dispongo debajo de cada una de estas columnas (por columna, de arriba a abajo y de izquierda de derecha) los caracteres del criptograma, ya he dicho que 4 de esas columnas tendrían 74 caracteres y las otras 2 tendrían 73, pero, ¿cuáles de ellas 74 y cuáles 73?. Si repaso el primer post que escribí sobre este método veo que las 4 columnas de 74 caracteres serían las correspondientes a los 4 primeros caracteres de la clave en su posición original ("S", "A", "M", "U"), mientas que la 2 columnas de 73 caracteres se corresponden con las del resto de ellos ("E" y "L"). Es decir:
4.- Y finalmente, ordeno las columnas conforme a la posición original de los caracteres de la clave utilizada (en este caso: "S","A","M","U","E", "L") y leo los caracteres de la tabla resultante por fila, de izquierda a derecha y de arriba a abajo:
Con lo que ya habría revertido la fase de transposición de columnas y lo único que faltaría para obtener el texto en claro es realizar un análisis de frecuencias de los pares de caracteres obtenidos con relación a los caracteres del idioma en el que esté escrito el mensaje en claro (en nuestro caso el español). De esta forma resolvería la fase de sustitución monoalfabética realizada como primera fase en el proceso de cifrado del texto en claro.  

Podríamos realizar el análisis de frecuencias manualmente, pero es mucho más cómodo y rápido hacerlo utilizando una herramienta on-line especializada. Para ello, sustituyo cada par de caracteres obtenido hasta ahora por un carácter arbitrario (da igual por cuál, pero yo voy a sustituir cada uno de ellos por la letra que más probablemente le correspondería conforme a la frecuencia de aparición de éstas en un texto en claro escrito en español):
Incluyocomo texto cifrado en la herramienta el resultado de la sustitución realizada en el paso anterior:
Y obtengo el siguiente texto en claro (salvo pequeño error que es muy fácil de detectar y subsanar):

LOS ALEMANES CREIAN QUE EL CIFRADO ADFGVX ERA INVULNERABLE AL CRIPTOANALISIS YA QUE COMBINABA SUSTITUCION Y TRANSPOSICION Y NO ERA POSIBLE REALIZAR UN ANALISIS DE FRECUENCIAS SIN EMBARGO GEORGES PAINVIN CONSIGUIO ROMPERLO Y SU NOMBRE ES LA SOLUCION DE ESTE RETO

Por tanto, la solución es: "GEORGES PAINVIN".

******** PRÓXIMO RETO
Reto 25: "El tamaño sí importa (II)".

Criptografía (CXXIV): Reto 25

$
0
0
Continúo con un reto fácil sobre criptografía. En esta ocasión se ve involucrado el criptosistema moderno de cifrado asimétrico más utilizado actualmente, RSA.

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 25: "El tamaño sí importa (II)".

El primer reto que puse en este blog con este mismo título, "El tamaño sí importa", era un reto de esteganografía, pero como en él decía creo que el tamaño importa en muchos ámbitos de nuestra vida, y la criptografía no es una excepción. Dados los archivos asociados al reto, con la clave pública y el criptograma, ¿puedes descifrar este último?.

Dificultad:
Tipo:           Criptografía.

Recursos:   public_key.pem.
                   criptograma.enc.

******** 20/01/2019
Pista 1:     Como habrás observado el exponente de la clave pública (e) es igual a 3. Un número muy pequeño cuya utilización presenta como ventaja el agilizar el tiempo de cifrado de los mensajes en claro (m), pero que tiene claros inconvenientes. Por ejemplo: ¿Qué ocurre cuando e = 3 y m < n^(1/3)?. 

******** __/__/____
Pista 2:    Por publicar

******** __/__/____
Pista 3:     Por publicar.

******** __/__/____
Solución:  Por publicar.

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

Criptografía (CXXV): Solución Reto 25

$
0
0
El  enunciado del reto de criptografía que formulé en este post era el siguiente:

"El primer reto que puse en este blog con este mismo título, "El tamaño sí importa", era un reto de esteganografía, pero como en él decía creo que el tamaño importa en muchos ámbitos de nuestra vida, y la criptografía no es una excepción. Dados los archivos asociados al reto, con la clave pública y el criptograma, ¿puedes descifrar este último?".

Solución: En primer lugar creo un script en python para obtener los parámetros de la clave pública (public_key.pem) que nos dan como recurso asociado al reto:

from Crypto.PublicKey import RSA

# Obtener parametros de la clave publica

pubkey = open('public_key.pem', 'r')
param_pubkey = RSA.importKey(pubkey.read())
print ''
print '   n  ...:', param_pubkey.n
print '   e  ...:', param_pubkey.e
pubkey.close()

Tras ejecutar este script veo el módulo (n) y el exponente de la clave pública (e):
Como se observa en la figura anterior, el exponente de la clave pública (e) es 3. El que sea tan pequeño presenta como ventaja el que se agiliza el tiempo del cifrado de los mensajes en claro (m), pero si estos últimos son cortos (otra vez vemos que el tamaño sí importa) se podrían descifrar de forma muy fácil sin disponer del exponente de la clave privada (d), ya que para ello bastaría con calcular la raíz cúbica del criptograma (c).

¿Por qué ocurre esto?. Muy fácil, recuerdo que el cifrado en RSA se realiza de la siguiente manera:


c = m^e mod n

Por tanto, si e = 3 y m < n^(1/3) entonces: c = m^3 y, en consecuencia, la raíz cúbica del criptograma (c) nos daría el mensaje en claro (m), es decir: m = c^(1/3).

Compruebo si en este caso el criptograma (c) es los suficientemente corto como para descifrarlo de la forma que se ha indicado anteriormente. Para ello creo el siguiente script en python:

#!/usr/bin/env python

#
# Ataque exponente publico bajo
#

import gmpy2
import binascii

n = 25358708488728181547379765072637472261822866039782321290930545283699835785844177134795501427697386547523791810191564809603212812573582763202006937344282814899554035096947429817448213415956367329710417740775992913107881882863330779615693984157953732370112680698175512130953235440345474556520766813072401642619809013843857569878005633859523035343481070924471866175937749288550580672605895380631590884330443015408874300766985932923428115805390406727912357891382757381978366684003693502532684457914223908684664564959071411555168826004659130324369048267651740694589068661464981037950639216053381207201710576480024833267099
e = 3
criptograma = open('criptograma.enc', 'rb')
c  = criptograma.read()
c = binascii.b2a_hex(c)
c = int(c,16)
criptograma.close()

print ''
print 'Modulo (n) ....................', n
print ''
print 'exponente clave publica (e) ...', e
print ''
print 'Criptograma (c) ...............', c

m,exacta = gmpy2.iroot(c,e)

if exacta:
   print ''
   print 'Texto en claro (m) ............', binascii.unhexlify(gmpy2.digits(m,16))

Y tras ejecutar este script confirmo que, efectivamente, el criptograma (c) puede descifrarse sin más que calcular la raíz cúbica del mismo:
Por tanto, la solución de este reto es: N0 35 UN4 BU3N4 1D34 U71L1Z4R 3 C0M0 3XP0N3N73 D3 L4 CL4V3 PUBL1C4.

******** PRÓXIMO RETO
Reto 26: Por publicar.
Viewing all 639 articles
Browse latest View live