Onk Onk Onk — CTF HackOn 2021

José Miguel Moreno
2 min readFeb 19, 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:

Onk.png — Enlace a la imagen original

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 res
d = 3330669730730362409693888678127494023180931051009
n = 3404062474937242861395243981141418377549190749193
print(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

--

--