Solution de cbothamy pour Connaître ses tables 1/2

hardware crypto attaque side channel

12 avril 2025

Table des matières

On nous fourni :

  • un fichier keys.bin
  • un programme wb_aes qui chiffre l’entrée standard, le fichier de clé keyv.bin étant passée en paramètre, comme dans l’exemple :
$ ./wb-aes keys.bin < /dev/zero
f34789150f962bdcc56e8c585451f34d
  • la code d’un programme python ayant servi a chiffrer le flag en utilisant le sha256 de la clé à trouver comme clé.
def encrypt(k, flag):
    import hashlib
    from Crypto.Cipher import AES
    hk = hashlib.sha256(k).digest()
    E = AES.new(hk, AES.MODE_GCM)
    iv = E.nonce
    c = E.encrypt(flag)
    return {"c": c.hex(), "iv": iv.hex()}
  • et sa sortie (chiffré + iv)
{
  "c": "5fe062ad6a039727a4fe6e42910286a77361a2244e09066b6f57a7f8cecb200cbbb6f3ae39345adf379d0d15f0e1401800e8b71cd2961d67b47518d30f92473c573a1afe367b",
  "iv": "5d88e6fb30fde2ecec264f735dd07b79"
}

Analyse

Le code de wb_aes.c est bien une implémentation de l’AES, mais la fonction

static void
wb_aes_enc_add_round_key(AES_STATE *state, const WB_AES_KEY *key, const unsigned char i)
{
    // empty function
    (void) state;
    (void) key;
    (void) i;
}

est suspicieusement vide.

De plus la fonction :

static void
wb_aes_enc_sub_bytes(AES_STATE *state)
{
    for (unsigned char i = 0; i < 16 ; i++) {
        uint8_t byte = state->state[i];
        fseek(state->key->key, byte, SEEK_CUR);
        fread(state->state+i, 1, 1, state->key->key);
        fseek(state->key->key, 255-byte, SEEK_CUR);
    }
}

n’utilise pas la S-box standard, mais parcourt le fichier de clé.

On en déduit que les étapes de xor de la clé et de substitution sont regroupées en une seule étape.

Solution

En loggant l’entrée et la sortie du premier appel de la fonction wb_aes_enc_sub_bytes en lui passant /dev/zero en paramètre on obtient donc la valeur de la clé mélangée par la S-box

$ ./wb-aes ./keys.bin < /dev/zero
in: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
out: 3b 2f 20 00 84 1b fc 1a 20 3b 84 2f 5a 1a ed 1a 

En y appliquant la S-box inverse, on obtient donc la clé d’origine :

>>> inv_sbox = [ 
...     0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb,
...     0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb,
...     0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e,
...     0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25,
...     0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92,
...     0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84,
...     0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06,
...     0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b,
...     0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73,
...     0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e,
...     0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b,
...     0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4,
...     0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f,
...     0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef,
...     0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61,
...     0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d,
... ]
>>> 
>>> round=[ 0x3b, 0x2f, 0x20, 0x00, 0x84, 0x1b, 0xfc, 0x1a, 0x20, 0x3b, 0x84, 0x2f, 0x5a, 0x1a, 0xed, 0x1a]
>>> 
>>> for c in round:
...    print(f"{inv_sbox[c]:02x}", end="") 
... 
494e54524f44554354494f4e46435343

Reste à déchiffrer le flag avec cette clé :

>>> c='5fe062ad6a039727a4fe6e42910286a77361a2244e09066b6f57a7f8cecb200cbbb6f3ae39345adf379d0d15f0e1401800e8b71cd2961d67b47518d30f92473c573a1afe367b'
>>> iv='5d88e6fb30fde2ecec264f735dd07b79'
>>> 
>>> k='494e54524f44554354494f4e46435343'
>>> 
>>> def decrypt(k):
...     import hashlib
...     from Cryptodome.Cipher import AES
...     hk = hashlib.sha256(k).digest()
...     E = AES.new(hk, AES.MODE_GCM, nonce=bytes.fromhex(iv))
...     flag = E.decrypt(bytes.fromhex(c))
...     return flag
... 
>>> print(decrypt(bytes.fromhex(k)))
b'FCSC{8bbd2b7713a82477a9aa73bd2ae59623deb02f650cd01e2b45e2d34b91c9b3c5}'