Chaînes de caractères : une vulnérabilité cachée dans les logiciels embarqués
Depuis de nombreuses années, la sécurité des ordinateurs de bureau est une préoccupation majeure. Dès qu'une machine est connectée à Internet, il existe intrinsèquement une possibilité d'attaque. Ces intrusions peuvent viser à voler des données, endommager le système ou modifier son fonctionnement. Les moyens de protection sont bien connus et largement appliqués. Les systèmes embarqués semblaient longtemps à l'abri de tels problèmes, car ils étaient rarement mis en réseau et leur code résidait généralement en ROM. La donne a changé : une grande partie des systèmes modernes sont connectés à Internet et il est courant de copier le code en RAM pour l'exécuter. La sécurité devient donc une considération essentielle dans la conception des logiciels embarqués.
Sécurité vs sûreté : une distinction cruciale
La langue anglaise est un formidable outil de communication, précis et nuancé. Pourtant, dans le langage courant, nous confondons souvent safety (sûreté) et security (sécurité), comme s'ils étaient synonymes. La meilleure définition que j'aie entendue est la suivante : la sûreté consiste à protéger le monde de l'appareil ; la sécurité consiste à protéger l'appareil du monde. C'est la sécurité qui nous intéresse ici.
Quand le chiffrement industriel n'est pas nécessaire
Si un système doit être absolument inviolable, le chiffrement de niveau industriel s'impose. Cela nécessite généralement un support matériel spécifique, disponible mais souvent disproportionné pour des applications n'exigeant pas une sécurité aussi élevée. Dans ces cas, d'autres options existent : c'est ce que nous allons explorer.
L'attaque par analyse de la mémoire
Si un pirate accède au contenu de la mémoire d'un appareil, il peut commencer à comprendre ce qu'il fait et comment. C'est la première étape pour en altérer le fonctionnement. Le code peut être désassemblé, révélant ainsi la logique. Sans chiffrement, peu de choses peuvent empêcher cela. L'étape suivante pour l'attaquant est d'examiner un dump hexadécimal/ASCII des données pour y repérer des motifs et structures reconnaissables. C'est là que des précautions peuvent être prises. Si le chiffrement n'est pas envisageable, l'obfuscation devient une possibilité.
Obfuscation des chaînes de caractères
Le but de l'obfuscation de données est de retarder ou dissuader l'attaquant en rendant les données moins reconnaissables. Dans un dump mémoire, les chaînes de texte sont parmi les éléments les plus faciles à repérer. C'est sur elles que nous nous concentrerons.
En C/C++, les chaînes de texte sont normalement des séquences d'octets contenant des codes ASCII terminés par un octet nul. C'est très facile à identifier, nous allons donc modifier cela. Premièrement, au lieu du terminateur nul, le premier octet de chaque chaîne sera un spécificateur de longueur. Les caractères de la chaîne auront leurs données légèrement mélangées pour les rendre moins familières : nous allons simplement échanger les deux quartets (nibbles) de chaque octet. Nous avons besoin d'un programme utilitaire auquel nous fournissons les chaînes en clair et qui génère la déclaration d'un tableau avec l'initialisation appropriée. Voici la fonction au cœur de cet utilitaire :
void scramble(int index, unsigned char *input) {
unsigned char *charpointer, character;
printf("unsigned char string%d[%d] = {0x%02x,", index, strlen(input)+1, strlen(input));
charpointer = input;
while(*charpointer) {
character = *charpointer++;
character = ((character & 0x0f) << 4) | ((character & 0xf0) >> 4);
printf("0x%02x", character);
if (*charpointer) printf(",");
}
printf("}; // \"%s\"\n", input);
}
Si je passe à cette fonction un index 4 et la chaîne « Hello world » (original, n'est-ce pas ?), la sortie sera :
unsigned char string4[12] = {0x0b, 0x84, 0x56, 0xc6, 0xc6, 0xf6, 0x02, 0x77, 0xf6, 0x27, 0xc6, 0x46}; // "Hello world"
Je peux copier-coller ce code dans mon programme, puis il ne me reste qu'à écrire une fonction pour « désobfusquer » le texte quand j'ai besoin de l'afficher. Au lieu de donner un numéro d'index à chaque chaîne, je pourrais lui donner un nom arbitraire en remplaçant le paramètre index par une chaîne. Notez que le code généré est en quelque sorte auto-documenté, car le commentaire montre la chaîne sous forme lisible, mais bien sûr cela n'apparaît que dans le code source. Si le pirate a accès à votre code source, vous avez des problèmes bien plus graves que je ne peux en résoudre !
Voici un code illustrant le processus de désobfuscation :
void main() {
unsigned char temp, buffer[50];
int count = string4[0], index=0;
while(count--) {
temp = string4[index+1];
temp = ((temp & 0x0f) << 4) | ((temp & 0xf0) >> 4);
buffer[index] = temp;
index++;
}
buffer[index] = 0;
printf("-%s-\n", buffer);
}
L'échange des quartets dans chaque octet n'est qu'une des nombreuses façons de procéder à l'obfuscation. Une autre possibilité consiste, par exemple, à effectuer une rotation vers la gauche de trois bits sur chaque caractère. Voici le code pour cela :
unsigned char leftrotate3(unsigned char c) {
c = (c << 3) | (c >> 5);
return c;
}
Les techniques d'obfuscation décrites ici mélangent la chaîne caractère par caractère. Il serait possible d'agir sur la chaîne entière. Par exemple, traiter la chaîne comme une longue séquence de bits et la faire pivoter d'un nombre arbitraire vers la gauche. Je laisse le codage de cet algorithme aux lecteurs les plus enthousiastes.
Il convient de noter qu'un effet de bord de la localisation de toutes les chaînes de texte est que la création de versions du logiciel pour d'autres langues devient assez simple.
Je dois réitérer et insister : l'obfuscation de données est loin d'être infaillible et ne fera, au mieux, que ralentir un pirate sérieux. Si rien d'autre, le code de désobfuscation pourrait être désassemblé. L'astuce avec cette technique est de rendre la piste d'obfuscation difficile à suivre. Si vous avez besoin d'une sécurité réellement forte, vous devez vous tourner vers le chiffrement complet.
Embarqué
- Le piano : des racines anciennes à la maîtrise moderne
- Comment les Systèmes Embarqués Propulsent l'Innovation Automobile Moderne
- Fondamentaux des Systèmes Embarqués et Applications Réelles
- SOAFEE : une architecture cloud-native pour plateformes automobiles logicielles
- Exploiter le DevOps pour surmonter les défis du logiciel embarqué
- Les robots transforment votre usine : ce que vous devez savoir
- Les logiciels embarqués redéfinissent les chaînes d'approvisionnement hardware pour plus d'agilité
- Interconnexion logicielle : les avantages clés pour l'usine intelligente
- Comment un Logiciel de Gestion d'Établissements Valorise les Institutions Religieuses