Onk Onk Onk — CTF HackOn 2021
Este artículo es un breve Write-up del reto de criptografía “Onk Onk Onk” del CTF organizado por la URJC durante el evento HackOn 2021.
Introducción
Se parte de una imagen que contiene un QR del que se espera extraer el flag:
Análisis
El primer paso es extraer el mensaje contenido en el código QR. Para ello se utiliza la herramienta online ZXing, obteniendo el siguiente texto:
⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
⣿N=3404062474937242861395243981141418377549190749193⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
⣿⣿⣿⣿⣿⣿⣿⣿⡿⢋⣩⣭⣶⣶⣮⣭⡙⠿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
⣿⣿⣿⣿⣿⣿⠿⣋⣴⣿⣿⣿⣿⣿⣿⣿⣿⣿⣦⡙⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
⣿⣿⣿⣿⣿⡃⠄⠹⡿⣿⣿⣿⣿⠟⠛⣿⣿⣿⣿⣷⡌⢿⣿⣿⣿⣿⣿⣿e = 2^16 + 1⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
⣿⣿⣿⣿⣿⠐⣠⡶⣶⣲⡎⢻⣿⣤⣴⣾⣿⣿⣿⣿⣿⠸⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
⣿⠟⣋⡥⡶⣞⡯⣟⣾⣺⢽⡧⣥⣭⣉⢻⣿⣿⣿⣿⣿⣆⢻⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
⡃⣾⢯⢿⢽⣫⡯⣷⣳⢯⡯⠯⠷⠻⠞⣼⣿⣿⣿⣿⣿⣿⡌⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
⣦⣍⡙⠫⠛⠕⣋⡓⠭⣡⢶⠗⣡⣶⡝⣿⣿⣿⣿⣿⣿⣿⣧⢹⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
⣿⣿⣿⣿⣿⣿⣘⣛⣋⣡⣵⣾⣿⣿⣿⢸⣿⣿⣿⣿⣿⣿⣿⢸⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⢸⣿⣿⣿⣿⣿⣿⣿⢸⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
⣿⣿⣿⣿⣿0699647424488547213538018687288179468997406246529 # ⣿⣿⣿⣿⣿⣿⣿⣿
1602895004162842681114653387147102789102453404163 #⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
2094836235654395727458589429933425704847883731085 #⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
1750607186559703713851933077207983420223532519062⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
Claramente estamos ante un reto de clave pública, ya que se proporcionan tres datos clave: N
(módulo), e
(exponente público) y lo que suponemos son varios criptogramas (los cuatro últimos números).
El único impedimento para descifrar los criptogramas es que solo tenemos la clave pública (tupla N
, e
) y no la privada. La solución más razonable pasa por obtener el exponente privado (d
) por fuerza bruta, algo factible dada la longitud del módulo.
Solución
Obtención de la clave privada
Se emplea la herramienta Ganapati/RsaCtfTool que implementa múltiples ataques típicos a sistemas criptográficos de clave pública:
$ ./RsaCtfTool.py -n 3404062474937242861395243981141418377549190749193 -e 65537 --private
Results for /tmp/tmpnbysz2la:Private key :
-----BEGIN RSA PRIVATE KEY-----
MHYCAQACFQJUQ3DK1lAO8qTRb1EDEJrCabdcCQIDAQABAhUCR2hlaFIhqz6HOsSP
md8hzZiQLgECCwE3UI4HV9O+rXKxAgsB6lGeQeOYFyRk2QIKKBAz1voUg+tqYQIL
AJjXOlrVpFpKaPkCCwDhvNA1vqDzjHsN
-----END RSA PRIVATE KEY-----$ ./RsaCtfTool.py --key ../priv.pem --dumpkey
n: 3404062474937242861395243981141418377549190749193
e: 65537
d: 3330669730730362409693888678127494023180931051009
p: 1470141949943601597084337
q: 2315465166522070469993689
Descifrado de los criptogramas
El último paso es descifrar los cuatro números del mensaje inicial que todavía no hemos utilizado.
La función de descifrado es (m ^ d) % n
, que da como resultado otro número entero. Este último se debe codificar como una cadena ASCII para poder dar sentido al mensaje.
El script final escrito en Python que permite obtener el flag es el siguiente:
def decrypt(d, n, m):
return pow(m, d, n)def num_to_str(num):
res = ""
while num > 0:
res = chr(num % 256) + res
num = num / 256
return resd = 3330669730730362409693888678127494023180931051009
n = 3404062474937242861395243981141418377549190749193print(num_to_str(decrypt(d, n, 699647424488547213538018687288179468997406246529)))
print(num_to_str(decrypt(d, n, 1602895004162842681114653387147102789102453404163)))
print(num_to_str(decrypt(d, n, 2094836235654395727458589429933425704847883731085)))
print(num_to_str(decrypt(d, n, 1750607186559703713851933077207983420223532519062)))
Al ejecutarlo se obtiene finalmente el flag HackOn{G00se_Game}
:
$ ./decrypt.py
Onk.Onk.Onk.Onk.Onk.
OnkHackOn{G00se_Game
}Onk.Onk.OnkOnk.Onk.
OnkOnk.Onk