Last active
December 6, 2023 17:25
-
-
Save MiKhai37/fb44cefedb50b89d9ad3b95ed1734e4e to your computer and use it in GitHub Desktop.
StegCryptoDIY-RNG Solve Script
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import imageio.v3 as iio | |
import numpy as np | |
from Crypto.Util.number import inverse | |
ref_path = '/home/bob/CTF/Hackropole-ANSSI/FCSC2019/forensics/StegCryptoDIY/leHACK19_ref.png' | |
chall_path = '/home/bob/CTF/Hackropole-ANSSI/FCSC2019/forensics/StegCryptoDIY/leHACK19_chall.png' | |
# Chargement de l'image du challenge et celle de référence | |
im_ref = iio.imread(ref_path) | |
im_chall = iio.imread(chall_path) | |
# Verification de la taille des images | |
assert im_chall.shape[:2] == im_ref.shape[:2] | |
# On observe les differente valeurs d'opacité de l'image challenge | |
opacity_vals = [] | |
for row in im_chall: | |
for key_pix in row: | |
opacity_vals.append(key_pix[3]) | |
print("Décompte des valeurs uniques d'opacité") | |
print(np.asarray(np.unique(opacity_vals, return_counts=True)).T) | |
# On trace une image avec les pixels dont la valeur d'opacité est de 254 | |
im_hidden = np.zeros(im_ref.shape,dtype='uint8') | |
for i,row in enumerate(im_chall): | |
for j,key_pix in enumerate(row): | |
opacity = key_pix[3] | |
if opacity == 254: | |
im_hidden[i][j] = np.array([255,255,255]) | |
else: | |
im_hidden[i][j] = np.array([0,0,0]) | |
iio.imwrite("./hidden_image.jpg", im_hidden) | |
# Récupération des pixels composants la clé secrete et de leurs coordonnées | |
key_pixs = [] | |
for i,row in enumerate(im_chall): | |
for j,key_pix in enumerate(row): | |
opacity = key_pix[3] | |
if opacity == 254 and i >= 75: | |
key_pixs.append((i, j, key_pix[:-1])) | |
# Récupération des valeurs composants la clé secrete et de leurs coordonnées dans la bonne base (x,y) | |
pix_key_vals = [] | |
for key_pix in key_pixs: | |
y = key_pix[0] | |
x = key_pix[1] | |
pix = key_pix[2] | |
# Recherche du parametres RGB différent | |
for rgb_ind in range(3): | |
if pix[rgb_ind]!=im_ref[y][x][rgb_ind]: | |
pix_key_vals.append((x,y,chr(pix[rgb_ind]))) | |
# On observe les differentes valeurs composants la clé secrete | |
key_vals = [pix[2] for pix in pix_key_vals] | |
print("\nValeurs uniques composants la clé secrete: ",''.join(np.unique(key_vals))) | |
#print("Décompte des valeurs uniques composants la clé secrete") | |
#print(np.asarray(np.unique(key_vals, return_counts=True)).T) | |
# On voit dans le compte des valeurs uniques que la clé contient sans doute notre flag | |
# On recherche donc les coordonnées des pixels correspondant | |
print("\nPixels possibles contenant le début du flag 'leh'") | |
for c in 'leh': | |
for key_val in pix_key_vals: | |
if key_val[2]==c: | |
print(key_val) | |
# Avec les valeurs recuperer on tente de recuperer les composants du LGC ayant permis de générer les x des pixels de la clé | |
modulus_x = 2**9 | |
# Deux possibilités s'offrent a nous, on teste les deux | |
possible_xs = [[53,387,375],[53,6,375]] | |
for known_xs in possible_xs: | |
try: | |
multiplier_x = (known_xs[2] - known_xs[1]) * inverse(known_xs[1] - known_xs[0], modulus_x) % modulus_x | |
increment_x = (known_xs[1] - known_xs[0] * multiplier_x) % modulus_x | |
print(f"\nSuccess for x_states: {known_xs}, multiplier = {multiplier_x}, increment = {increment_x})") | |
except ValueError as e: | |
print(e) | |
print(f"\nFail for x_states: {known_xs}") | |
# Seulement une des combinaisons correspond aux nombres générerés par un LCG | |
# avec les parametres multiplier=417 et incrment=433 | |
class LCG: | |
def __init__(self, seed, a, c, m): | |
self.seed = seed | |
self.a = a | |
self.c = c | |
self.m = m | |
print(f"\nLCG: Valeur Initial={self.seed}, Multiplicateur={self.a}, Incrementeur={self.c}, Modulo={self.m}") | |
def next(self): | |
self.seed = (self.a * self.seed + self.c) % self.m | |
return self.seed | |
# On génere les coordonnées x de notre clé afin de reconstruire la clé | |
lcgx = LCG(53,multiplier_x,increment_x,modulus_x) | |
flag = ['l'] # valeur initiale | |
for i in range(len(pix_key_vals)-1): | |
nx = lcgx.next() | |
for key_val in pix_key_vals: | |
if(key_val[0]==nx): | |
#print(nx,val[2]) | |
flag.append(key_val[2]) | |
print(''.join(flag)) | |
print(len(flag)) | |
# La valeur initiale etait celle de la parenthèse | |
lcgx = LCG(253,multiplier_x,increment_x,modulus_x) | |
flag = ['('] # valeur initiale | |
for i in range(len(pix_key_vals)-1): | |
nx = lcgx.next() | |
for key_val in pix_key_vals: | |
if(key_val[0]==nx): | |
#print(nx,val[2]) | |
flag.append(key_val[2]) | |
print(''.join(flag)) | |
print(len(flag)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment