En tant qu'agent spécial au service de la faction XXX vous devez décoder un message intercepté sur un théâtre d'opération entre deux agents ennemis. Pour ce faire, votre spécialiste radio vous livre le message (qui semble être chiffré) ainsi que le firmware d'un terminal de chiffrement malheureusement détruit. A vous de jouer agent XXX 007...
Dans cette épreuve, on nous fournissait un message chiffré ainsi qu'un binaire, cryptomachine.bin qui s'occupait de chiffrer un message.
Le message chiffré, lui, n'était rien d'autre qu'une chaîne hexadécimale :
Cette épreuve faisait directement suite à une première épreuve de la catégorie Embedded, Defused. Je n'ai pas écrit de write-up pour celle-ci, donc un petit peu de contexte s'impose : dans Defused, on nous donnait une image présentant un circuit dans lequel on retrouvait un microcontrôleur ARM (STM32F103C8T6).
Cette fonction va aussi convertir deux caractères hexadécimaux en décimal, mais va ensuite chercher, à un certain endroit dans la mémoire, l'octet résultant, et retourner sa position.
Cette zone mémoire commence en 67096 = 0x10618, c'est-à-dire à l'offset réel 0x618 dans le binaire. On dump sur 256 octets :
94 AE 4F CC A9 BB 78 8A FB 31 C0 06 85 30 F9 C3
54 47 96 7D 6B CF 90 BA DD 29 02 A8 69 79 45 49
38 4B 4C 87 6C 8C C2 82 D2 BD FE 3D F6 37 DE 2A
19 22 E5 F0 E7 AF 98 7F DB D9 32 9F 3E AB 89 73
0D 25 53 D0 A1 07 F7 5F 3B 99 17 3C BF B8 CB FD
DC 44 C5 B2 B5 16 6F D8 83 AA 9B 14 26 50 B7 9C
D1 71 23 04 5A 8E 93 FC 09 2E 55 D3 0A CE 7B D6
C7 2B 97 33 B6 E1 5B 57 1E E3 AC 5D EB 00 36 74
A0 7A A6 15 E8 C9 8B F4 1A BE 13 E9 D7 0B 51 0E
35 70 56 0F F1 92 7E 86 24 DF 8F 10 88 7C 20 2F
68 3F A4 8D 6D 6A 84 C1 9E 65 E0 5E 95 1D EF 0C
4E 05 18 59 4A 27 EE 01 39 76 D4 46 63 A5 DA F2
ED 81 F5 AD D5 66 A7 EA A2 EC E2 3A 5C 1B B4 1C
2C 41 43 60 40 FF 4D CA E4 F8 58 91 52 28 6E 9A
48 11 64 2D 21 34 61 B1 B3 9D 62 E6 BC FA CD 42
72 C8 C6 03 1F B0 F3 75 A3 67 08 77 12 B9 C4 80
Sans surprise, il s'agit d'une permutation de la liste des octets de 0 à 255. La fonction retourne donc la position de notre octet dans cette table.
Enfin, la fonction sub_204 (renommée ici subtilement aMoinsBMoins2) :
Après étude, celle-ci semble simplement renvoyer a - b - 2, où a et b sont les valeurs décimales associées aux arguments (toujours en hexa) a2 et a1.
Avec toutes ces informations en main, nous pouvions alors comprendre la routine de chiffrement du main. C'est un peu fastidieux donc je vais directement sauter à l'explication de l'algorithme :
On lit le plaintext sous forme de chaîne hexadécimale, deux caractères par deux
Soit m[i] le i-ème octet du plaintext, et c[i] son chiffré :
Si i mod 4 = 0, alors c[i] = m[i] + 4
Si i mod 4 = 1, alors c[i] est l'indice de m[i] dans la table de permutation
Si i mod 4 = 2, alors c[i] = m[i] xor 0xCC
Si i mod 4 = 3, alors c[i] = m[i] - c[i-3] - 2
Le texte chiffré est c sous forme de chaîne hexadécimale
Il ne reste plus qu'à coder un algo de déchiffrement.
P = "94AE4FCCA9BB788AFB31C0068530F9C35447967D6BCF90BADD2902A869794549384B4C876C8CC282D2BDFE3DF637DE2A1922E5F0E7AF987FDBD9329F3EAB89730D2553D0A107F75F3B99173CBFB8CBFDDC44C5B2B5166FD883AA9B142650B79CD17123045A8E93FC092E55D30ACE7BD6C72B9733B6E15B571EE3AC5DEB003674A07AA615E8C98BF41ABE13E9D70B510E3570560FF1927E8624DF8F10887C202F683FA48D6D6A84C19E65E05E951DEF0C4E0518594A27EE013976D44663A5DAF2ED81F5ADD566A7EAA2ECE23A5C1BB41C2C41436040FF4DCAE4F8589152286E9A4811642D213461B1B39D62E6BCFACD4272C8C6031FB0F375A367087712B9C480"
P = [int(P[i:i+2], 16)for i inrange(0, len(P), 2)]m = "49D29B3439B8FB013DE2F9FD35B8F8FD36E5F8FC3409FCFF352DA8033BEAA8F83B73FBFC373AAD003573FB026990FEFA65B8FCFE343AA90034E6AE013DE5AA2A6AA9AEFE81"
m = [int(m[i:i+2], 16)for i inrange(0, len(m), 2)]out = []for i inrange(len(m)):if i %4==0: out.append(m[i] -4)if i %4==1: out.append(P[m[i]])if i %4==2: out.append(m[i] ^0xCC)if i %4==3: out.append((m[i] +2+ (m[i-3]-4)) &0xff)print(bytes(out))
Une épreuve plutôt sympathique même si analyser le code en statique est légèrement sale et fastidieux. C'est la première fois que je fais un challenge de ce genre (reverse du ARM microcontrôleur) et je suis content d'avoir fait first blood !
Je suis d'ailleurs étonné du nombre peu élevé de validations sur cette épreuve, que je n'ai pas trouvée spécialement plus difficile que son homologue Defused qui en a 3 fois plus.