(Je n’ai pas pu valider ce challenge, qui était déverrouillé seulement après son acolyte Hackllebarde Ransomware 3/4)
Hackllebarde Ransomware 4/4
Pendant que vous travailliez sur l'analyse forensique, nos experts en
rétro-ingénierie ont pu extraire le code utilisé pour chiffrer nos
documents. Hélas, pas moyen de retrouver la clé de chiffrement, elle n'a
pas été sauvegardée du tout !! Le département de cryptographie est
formel, les données sont bel et bien perdues. Néanmoins jetez un coup
d'oeil, on ne sait jamais...
Auteur : mh4ckt3mh4ckt1c4s#0705
Catégorie: cryptanalyse
Difficulté: difficile
Description
Sont fournis:
- un fichier
flag.pdf.enc
(151772 octets) - un fichier source
ransomware.c
Le protocole de chiffrement est un XOR avec un flux de clé obtenu en appelant rand()
char array[8];
initstate(seed, array, 27);
...
while ((len = fread(&data, sizeof(char), 4, file)) == 4) {
// on ne peut rien faire contre une clé 100% aléatoire !!!
key = rand();
keychar = (char*)&key;
for(int i=0; i<len; i++) {
data[i] ^= keychar[i];
}
fwrite(&data, sizeof(char), 4, encryptedfile);
}
Le générateur aléatoire
D’après le code de la glibc, le paramètre n=27 dans initstate va sélectionner le générateur linéaire congruentiel:
#define BREAK_1 32
int
__initstate_r (unsigned int seed, char *arg_state, size_t n,
struct random_data *buf)
{
...
else if (n < BREAK_1)
{
if (n < BREAK_0)
goto fail;
type = TYPE_0;
}
...
}
int
__random_r (struct random_data *buf, int32_t *result)
{
...
if (buf->rand_type == TYPE_0)
{
int32_t val = ((state[0] * 1103515245U) + 12345U) & 0x7fffffff;
state[0] = val;
*result = val;
}
...
À consulter ici: (random_r.c)[https://sourceware.org/git/?p=glibc.git;a=blob;f=stdlib/random_r.c;hb=glibc-2.31]
Il est donc très facile de reconstituer son état à partir de données connues: par exemple les 4 premiers octets %PDF
d’un fichier PDF.
Solution
import struct
data = open("flag.pdf.enc", "rb").read()
seed = None
dec = []
for (n,) in struct.iter_unpack("<I", data):
if seed is None:
seed = n ^ struct.unpack("<I", b"%PDF")[0]
else:
seed = ((seed * 1103515245) + 12345) % (2**31)
dec.append(seed ^ n)
open("flag.pdf", "wb").write(struct.pack("<%dI"%len(dec), *dec))
On obtient un fichier PDF contenant une image représentant le flag.