Solución a otro reto de criptografía de la plataforma picoCTF 2018.
En esta ocasión se trata de un reto en el que se ve involucrado el método de operación más sencillo, ECB ('Electronic Code-Book'), de los algoritmos de cifrado por bloques, y que, en mi opinión, presenta un nivel de dificultad alto (★★★★☆).
- SpyFi - Points: 300:
Su enunciado dice lo siguiente: 'James Brahm, James Bond's less-franchised cousin, has left his secure communication with HQ running, but we couldn't find a way to steal his agent identification code. Can you? Conect with
Solución: se proporciona un archivo fuente en el que puedo ver el cifrado de un mensaje utilizando el criptosistema AES en modo ECB. El tamaño del bloque es de 16 Bytes.
Tal y como nos cuenta wikipedia, en el tipo de cifradores indicado anteriormente los mensajes en claro se dividen en bloques (en el caso de nuestro reto de 16 Bytes), y en el modo ECB cada uno de ellos es cifrado por separado utilizando la misma clave.
La desventaja de este modo es que a bloques de texto plano o en claro idénticos les corresponden bloques idénticos de texto cifrado.
Dicho lo anterior vuelvo al reto. Me conecto al servidor: se me da la bienvenida y se me solicita que introduzca mi informe de situación, y después de introducir este último se muestra un texto cifrado o criptograma:
Analizo el archivo fuente que se nos proporciona con relación al criptograma obtenido y llego a la conclusión de que el cifrado del mensaje se ha realizado de la siguiente manera:
message = """Agent,
...
Como informe de situación he introducido 59 caracteres "a" con lo que:
- Veo que, efectivamente, a idénticos bloques de texto plano o en claro les corresponden idénticos textos cifrados (líneas resaltadas en color verde).
- He obtenido un texto cifrado correspondiente a un bloque (línea resaltada en color azul) que me permitirá utilizarlo como valor de referencia para realizar un ataque de fuerza bruta con objeto de obtener el primer carácter de texto en claro (el carácter desconocido "x" que figura en el texto en claro de dicha línea) del código de identificación del agente 006, que es lo que pretendo robar y, por tanto, es la solución a este reto.
Es decir, si realizo fuerza bruta sobre el último carácter ("x") de la línea resaltada en azul (voy probando con todos los posibles caracteres: a, b, c,..., z, A, B, C,..., Z, 0, 1, 2,..., 9, !...) llegará un momento en que el criptograma que obtenga para ese bloque será igual a nuestro valor de referencia, ya que, tal y como he dicho, a bloques idénticos les corresponden textos cifrados iguales, y el carácter probado será precisamente el primero del código de identificación de agente que busco.
Indico cómo funcionaría para el primer carácter del código de identificación de agente:
- Introduzco como informe de situación: aaaaaaaaaaafying code is: a
...
...
- El texto cifrado obtenido para el último bloque que se muestra coincide con nuestro valor de referencia. Por tanto, "p" es el primer carácter del texto en claro correspondiente al código de identificación de agente.
Creo un pequeño script en python para obtener este primer carácter (al final de este post pongo el script para 'crackear' completamente el código de identificación de agente). Lo ejecuto y obtengo:
Una vez obtenido el primer carácter del texto en claro, para obtener el segundo sólo tengo que introducir como informe de situación 58 caracteres "a", con objeto de calcular el nuevo valor de referencia incorporando el carácter del texto en claro hallado (primer carácter del código de identificación de agente), y continuar con el mismo procedimiento de fuerza bruta.
Explicado lo anterior, para conseguir la solución de este reto generalizo el script empleado para obtener el primer carácter, de la siguiente manera:
#!/usr/bin/env python3
import socket
charset='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~ '
encontrado=' '
flag=''
situation_report_ref='aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
situation_report_car=' aaaaaaaaaaafying code is: '
n=1
while encontrado!='' and encontrado!='}':
print('[+] Informe de situacion ...', situation_report_ref)
s = socket.socket()
s.connect(('2018shell.picoctf.com', 33893))
while True:
recibido=s.recv(448).decode().strip()
if 'Welcome, Agent 006!' in recibido:
print(recibido)
elif 'Please enter your situation report:' in recibido:
print(recibido)
s.send((situation_report_ref).encode())
s.send(b'\n')
else:
str_brute_force=recibido[256:288]
break
s.close
if n>11:
situation_report_car=' aaaaaaaaaaa'+situation_report_car[12:27]
n=0
else:
n+=1
situation_report=situation_report_car[1:27]
for i in charset:
s = socket.socket()
s.connect(('2018shell.picoctf.com', 33893))
situation_report_car=situation_report+i
print(situation_report_car)
encontrado=''
while True:
recibido=s.recv(384).decode().strip()
if 'Welcome, Agent 006!' in recibido:
pass
elif 'Please enter your situation report:' in recibido:
s.send((situation_report_car).encode())
s.send(b'\n')
else:
if recibido[128:160]==str_brute_force:
encontrado=i
print('[+] Encontrado un caracter del codigo de identificacion de agente:', i)
flag=flag+i
print('[+] Flag =', flag)
break
if encontrado!='':
situation_report_ref=situation_report_ref[0:59-len(flag)]
break
elif i==' ' and encontrado=='':
print('[-] No se ha encontrado un caracter del codigo de identificacion de agente.')
break
s.close()
Lo ejecuto:
Y cuando finaliza obtengo la flag completa:
En esta ocasión se trata de un reto en el que se ve involucrado el método de operación más sencillo, ECB ('Electronic Code-Book'), de los algoritmos de cifrado por bloques, y que, en mi opinión, presenta un nivel de dificultad alto (★★★★☆).
- SpyFi - Points: 300:
Su enunciado dice lo siguiente: 'James Brahm, James Bond's less-franchised cousin, has left his secure communication with HQ running, but we couldn't find a way to steal his agent identification code. Can you? Conect with
nc 2018shell.picoctf. com 33893
. Source'.Solución: se proporciona un archivo fuente en el que puedo ver el cifrado de un mensaje utilizando el criptosistema AES en modo ECB. El tamaño del bloque es de 16 Bytes.
Tal y como nos cuenta wikipedia, en el tipo de cifradores indicado anteriormente los mensajes en claro se dividen en bloques (en el caso de nuestro reto de 16 Bytes), y en el modo ECB cada uno de ellos es cifrado por separado utilizando la misma clave.
La desventaja de este modo es que a bloques de texto plano o en claro idénticos les corresponden bloques idénticos de texto cifrado.
Dicho lo anterior vuelvo al reto. Me conecto al servidor: se me da la bienvenida y se me solicita que introduzca mi informe de situación, y después de introducir este último se muestra un texto cifrado o criptograma:
Analizo el archivo fuente que se nos proporciona con relación al criptograma obtenido y llego a la conclusión de que el cifrado del mensaje se ha realizado de la siguiente manera:
message = """Agent,
Greetings. My
situation report is as follows:
{0}
My agent identifying
code is: {1}.
Down with the
Soviets,
006
"""
Utilizando “/” para indicar el salto de línea:
Utilizando “/” para indicar el salto de línea:
Agent,/Greetings
|
72f8928a2758a3379144f3c01fb298a4
|
. My situation r
|
1527c40d73f78fd78ba96d842e6d9587
|
eport is as foll
|
d2d89cbf39636928ba9b43db8bf5e348
|
ows:/aaaaaaaaaaa
|
29cca0a3f1dc5a9ffc881c3975e0b576
|
aaaaaaaaaaaaaaaa
|
99c96c6acd5a7be8bceefe7438374c89
|
aaaaaaaaaaaaaaaa
|
99c96c6acd5a7be8bceefe7438374c89
|
aaaaaaaaaaaaaaaa
|
99c96c6acd5a7be8bceefe7438374c89
|
/My agent identi
|
5c55697e5d18d2b94fffe188b4f3f279
|
fying code is: x
|
c551f6fbef081e96daec728c670444d3
|
Como informe de situación he introducido 59 caracteres "a" con lo que:
- Veo que, efectivamente, a idénticos bloques de texto plano o en claro les corresponden idénticos textos cifrados (líneas resaltadas en color verde).
- He obtenido un texto cifrado correspondiente a un bloque (línea resaltada en color azul) que me permitirá utilizarlo como valor de referencia para realizar un ataque de fuerza bruta con objeto de obtener el primer carácter de texto en claro (el carácter desconocido "x" que figura en el texto en claro de dicha línea) del código de identificación del agente 006, que es lo que pretendo robar y, por tanto, es la solución a este reto.
Es decir, si realizo fuerza bruta sobre el último carácter ("x") de la línea resaltada en azul (voy probando con todos los posibles caracteres: a, b, c,..., z, A, B, C,..., Z, 0, 1, 2,..., 9, !...) llegará un momento en que el criptograma que obtenga para ese bloque será igual a nuestro valor de referencia, ya que, tal y como he dicho, a bloques idénticos les corresponden textos cifrados iguales, y el carácter probado será precisamente el primero del código de identificación de agente que busco.
Indico cómo funcionaría para el primer carácter del código de identificación de agente:
- Introduzco como informe de situación: aaaaaaaaaaafying code is: a
Agent,/Greetings
|
72f8928a2758a3379144f3c01fb298a4
|
. My situation r
|
1527c40d73f78fd78ba96d842e6d9587
|
eport is as foll
|
d2d89cbf39636928ba9b43db8bf5e348
|
ows:/aaaaaaaaaaa
|
29cca0a3f1dc5a9ffc881c3975e0b576
|
fying code is: a
|
c17da5606c552f2f750a47a9f342a75c
|
- El texto cifrado obtenido para el último bloque que se muestra no coincide con nuestro valor de referencia. Por tanto, "a" no es el primer carácter del texto en claro correspondiente al código de identificación de agente.
- Voy probando con los demás caracteres hasta que llego a "p", es decir, introduzco como informe de situación: aaaaaaaaaaafying code is: p
- Voy probando con los demás caracteres hasta que llego a "p", es decir, introduzco como informe de situación: aaaaaaaaaaafying code is: p
Agent,/Greetings
|
72f8928a2758a3379144f3c01fb298a4
|
. My situation r
|
1527c40d73f78fd78ba96d842e6d9587
|
eport is as foll
|
d2d89cbf39636928ba9b43db8bf5e348
|
ows:/aaaaaaaaaaa
|
29cca0a3f1dc5a9ffc881c3975e0b576
|
fying code is: p
|
c551f6fbef081e96daec728c670444d3
|
Creo un pequeño script en python para obtener este primer carácter (al final de este post pongo el script para 'crackear' completamente el código de identificación de agente). Lo ejecuto y obtengo:
Una vez obtenido el primer carácter del texto en claro, para obtener el segundo sólo tengo que introducir como informe de situación 58 caracteres "a", con objeto de calcular el nuevo valor de referencia incorporando el carácter del texto en claro hallado (primer carácter del código de identificación de agente), y continuar con el mismo procedimiento de fuerza bruta.
Explicado lo anterior, para conseguir la solución de este reto generalizo el script empleado para obtener el primer carácter, de la siguiente manera:
#!/usr/bin/env python3
import socket
charset='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~ '
encontrado=' '
flag=''
situation_report_ref='aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
situation_report_car=' aaaaaaaaaaafying code is: '
n=1
while encontrado!='' and encontrado!='}':
print('[+] Informe de situacion ...', situation_report_ref)
s = socket.socket()
s.connect(('2018shell.picoctf.com', 33893))
while True:
recibido=s.recv(448).decode().strip()
if 'Welcome, Agent 006!' in recibido:
print(recibido)
elif 'Please enter your situation report:' in recibido:
print(recibido)
s.send((situation_report_ref).encode())
s.send(b'\n')
else:
str_brute_force=recibido[256:288]
break
s.close
if n>11:
situation_report_car=' aaaaaaaaaaa'+situation_report_car[12:27]
n=0
else:
n+=1
situation_report=situation_report_car[1:27]
for i in charset:
s = socket.socket()
s.connect(('2018shell.picoctf.com', 33893))
situation_report_car=situation_report+i
print(situation_report_car)
encontrado=''
while True:
recibido=s.recv(384).decode().strip()
if 'Welcome, Agent 006!' in recibido:
pass
elif 'Please enter your situation report:' in recibido:
s.send((situation_report_car).encode())
s.send(b'\n')
else:
if recibido[128:160]==str_brute_force:
encontrado=i
print('[+] Encontrado un caracter del codigo de identificacion de agente:', i)
flag=flag+i
print('[+] Flag =', flag)
break
if encontrado!='':
situation_report_ref=situation_report_ref[0:59-len(flag)]
break
elif i==' ' and encontrado=='':
print('[-] No se ha encontrado un caracter del codigo de identificacion de agente.')
break
s.close()
Lo ejecuto:
Y cuando finaliza obtengo la flag completa:
picoCTF{@g3nt6_1$_th3_c00l3$t_6081670}.
Comentarios
Publicar un comentario