Ins’Hack 2018 - RE/Prog
Détails du challenge
| Event | Challenge | Category | Points | Solves |
|---|---|---|---|---|
| Ins’Hack 2018 | Visual Hashing Hard | RE/Prog | 85 | 44 |
Description
Nous vous recommandons de jeter un œil à la première partie de ce chall (Visual hashing - easy) avant de vous attaquer à celui-ci.
Nos équipes ont réussi à infecter un expert du French Bureau of Investigation qui utilise la même extension, cependant notre espion a oublié de cliquer sur « Retirer le périphérique USB en toute sécurité », si bien que le fichier vidéo est maintenant tout corrompu.
Nous avons réussi à sauvegarder l’une des frames de la capture, voyez ce que vous pouvez en faire. De plus, nous savons que le mot de passe a le format INSA{lowercase letters}. Bonne chance.
extension : https://chrome.google.com/webstore/detail/visual-hashing/lkoelcpcjjehbjcchcbddggjmphfaiie
TL;DR
Dans ce challenge, nous avions une extension de navigateur qui générait 4 couleurs à partir de l’entrée utilisateur pour une entrée donnée, ainsi qu’une capture d’écran d’un mot de passe avec les couleurs associées.
Ce type de coloration peut être comparé à un « visual hashing » du mot de passe : chaque entrée de texte possède ses propres couleurs.
Nous devions d’abord reverse l’extension web : la couleur était générée à partir d’un SHA1() de l’entrée, qui était traduit en 4 couleurs en hexadécimal.
La dernière étape de l’algorithme consistait à ajouter un peu d’aléatoire sur les couleurs (+/- 3 nuances de rouge, vert et bleu).
Pour accomplir la tâche, nous n’avions que 5 caractères à bruteforce, calculer les 4 couleurs pour chaque essai et vérifier que chaque couleur correspond à celle d’origine (+/- 3 tons).
Ressources
Mot de passe secret :

Extension web :
https://chrome.google.com/webstore/detail/visual-hashing/lkoelcpcjjehbjcchcbddggjmphfaiie
Reversing
La première étape du challenge consistait à installer chrome, télécharger l’extension et regarder le code source situé au chemin suivant :
C:\Users\Zeecka\AppData\Local\Google\Chrome\User Data\Default\Extensions\lkoelcpcjjehbjcchcbddggjmphfaiie\1.0.1_0\
Nous pouvons identifier 2 fichiers : visual.js et utils.js. Après quelques simplifications, nous pouvons identifier les fonctions suivantes :
var passwordHash = SHA1(elem.value); // Zeecka: extracted from main
function getDataURLForHash(passwordHash,inputWidth,inputHeight) {
var win = window;
try {
win = unsafeWindow;
}
catch(e) {}
var canvas = win.document.createElement('canvas');
canvas.height = inputHeight;
canvas.width = inputWidth;
var context = canvas.getContext('2d');
passwordHash = randomizeHash(passwordHash); // Zeecka: The line we care !
for (var hashBandX = 0; hashBandX < 4; hashBandX++) {
context.fillStyle='#' + passwordHash.substr(hashBandX*6,6);
context.fillRect(hashBandX/4*inputWidth,0,inputWidth/4,inputHeight);
context.fillStyle='#000000';
context.fillRect(((hashBandX+1)/4*inputWidth)-1,0,2,inputHeight);
}
context.strokeStyle='#000000';
context.strokeRect(0,0,inputWidth,inputHeight);
return canvas.toDataURL();
}
function randomizeHash(passwordHash) {
// Add a little bit of randomness to each byte
for (var byteIdx = 0; byteIdx < passwordHash.length/2; byteIdx++) {
var byte = parseInt(passwordHash.substr(byteIdx*2,2),16);
// +/- 3, within 0-255
byte = Math.min(Math.max(byte + parseInt(Math.random()*6)-3,0),255);
var hexStr = byte.toString(16).length == 2 ? byte.toString(16) : '0' + byte.toString(16);
passwordHash = passwordHash.substr(0,byteIdx*2) + hexStr + passwordHash.substr(byteIdx*2+2);
}
return passwordHash;
}
À partir de cela, nous pouvons identifier comment les couleurs sont générées : le texte de l’entrée est hashé avec la fonction SHA1 (également présente dans le script).
Ce hash est modifié de +/- 3 (hex) tous les 2 chiffres. Par exemple, un hash donné « 3333333333333333333333333333333333333333 » pourrait être modifié en « 3531323034363536333130333436323130323135 ».
Ensuite, le hash modifié est tronqué pour générer 4 couleurs en hexadécimal (pour notre exemple : #353132 #303436 #353633 #313033).
Nous pouvons vérifier le processus avec un secret personnalisé en calculant les couleurs de notre propre secret.
Récupération du mot de passe
En regardant la capture d’écran corrompue, nous pouvons identifier les 4 couleurs suivantes : #1d9c0d, #b55855, #bc6478 et #a047c9.
Si nous tenons compte de l’aléatoire : les couleurs originales peuvent être modifiées de +/- 3 tons.
Le challenge nous donnait le format du flag INSA{lowercase letters} et le mot de passe caché dans l’image fait 11 lettres de long.
Nous pouvons en déduire que nous n’avons que 5 lettres minuscules à bruteforce : INSA{*****} (26**5 = 11,881,376).
C’est parti pour le bruteforce ! Voici mon script :
import hashlib
import math
import string
import itertools
def SHA1(p):
""" SHA1 function """
m = hashlib.sha1()
m.update(p.encode("utf-8"))
return m.hexdigest()
def getColor(password):
""" Return list of 4 colors from a given password """
passwordHash = SHA1(password)
color1 = passwordHash[0:6]
color2 = passwordHash[6:12]
color3 = passwordHash[12:18]
color4 = passwordHash[18:24]
return [color1,color2,color3,color4]
def isSameColor(a,c):
""" Verify if a color is equal to the associated 'c' color (+/-3) """
x = 3 # Randomness
colors = ["1d9c0d","b55855","bc6478","a047c9"] # Extracted colors from pictures
r = int("0x"+a[:2],16)
g = int("0x"+a[2:4],16)
b = int("0x"+a[4:6],16)
return (r >= int("0x"+colors[c][:2],16)-x # R G B in [-3;+3] ?
and r <= int("0x"+colors[c][:2],16)+x
and g >= int("0x"+colors[c][2:4],16)-x
and g <= int("0x"+colors[c][2:4],16)+x
and b >= int("0x"+colors[c][4:6],16)-x
and b <= int("0x"+colors[c][4:6],16)+x)
charset = string.ascii_lowercase # abcdefg...
for f in itertools.product(charset, repeat=5): # each combinaison of 5 chars
tab = getColor("INSA{"+''.join(f)+"}")
if (isSameColor(tab[0],0) # Check if each colors is equal (+/-3)
and isSameColor(tab[1],1)
and isSameColor(tab[2],2)
and isSameColor(tab[3],3)):
print("Flag: "+"INSA{"+''.join(f)+"} !")
FLAG !
FLAG
INSA{hctib}
Zeecka