Introspection et analyse de malware via LibVMI

Magazine
Marque
MISC
Numéro
102
|
Mois de parution
mars 2019
|
Domaines


Résumé

Dans cet article, nous allons illustrer ce qu’est l’introspection des machines virtuelles et comment on peut l’utiliser dans le but d’analyser des malwares.


Body

1. Qu’est-ce que l’introspection ?

De façon générale, le terme introspection désigne l’observation et l’examen de son propre état mental et émotionnel. Il est considéré comme l’acte de se regarder soi-même. Le meilleur exemple pour illustrer cela est l’œil de judas qui permet d’observer le monde extérieur sans avoir besoin d’en faire partie. Toutefois, l’introspection des machines virtuelles est l’art de monitorer les machines virtuelles depuis l’hyperviseur et y accéder sans être dedans (aucun agent n’est installé sur la machine virtuelle).

2. Pourquoi l’introspection ?

Grâce à la technologie d’introspection, il n’y a nul besoin de faire partie de l’environnement du malware pour l’analyser. Tout simplement parce que le comportement des processus à surveiller sera réalisé en dehors de la machine virtuelle, depuis l’hyperviseur. De plus, un malware employant des techniques de détection de débogueurne marchera pas étant donné que le système d’introspection interagit uniquement avec la mémoire de la machine virtuelle et ne s’attache en aucun cas aux processus lancés sur la machine. De même, il est possible de tromper un malware employant des techniques de détection d’environnement d’analyse (modifier à la volée l’accès à une clé de registre VirtualBox par exemple). De ce fait, on ne peut nier que l’application de l’introspection à l’analyse de malware est bien meilleure que les technologies traditionnelles d’analyse automatisée.

3. Architecture de l’introspection

3.1 Types d’hyperviseurs

On considère généralement deux types d’hyperviseurs :

  • Hyperviseur natif : connu aussi sous le nom de l’hyperviseur type-1 ou encore « Bare Metal ». Ce type d’hyperviseur fonctionne directement sur le matériel de la machine afin de contrôler et gérer les machines virtuelles. Exemples d’hyperviseurs de type-1 : Xen, KVM...
  • Hyperviseur hébergé : connu aussi sous le nom de l’hyperviseur de type-2 ou encore « Hosted ». Ce type d’hyperviseur s’exécute à l’intérieur d’un autre système d’exploitation. Exemples d’hyperviseurs de type-2 : VirtualBox, VMware...

La figure suivante illustre le fonctionnement de l’introspection sur l’hyperviseur de type-1. Les outils d’introspection installés sur l’hyperviseur permettent de surveiller et de contrôler tout ce qui se passe sur la machine virtuelle (activité système, réseau, applicative…).

 

introspection_dans_analyse_de_malware_figure_01

 

Fig 1 : Exemple d’hyperviseur natif et d’hyperviseur hébergé.

On peut bien imaginer le même fonctionnement pour l’hyperviseur de type-2.

3.2 Mappage mémoire

De façon générale, il existe deux niveaux de mémoire ; mémoire virtuelle et mémoire physique de la machine physique. Et trois niveaux de mémoire quand on parle d’hyperviseur (mémoire virtuelle et mémoire physique de la machine virtuelle, et mémoire physique de la machine hôte. La mémoire virtuelle de la machine hôte est abstraite dans ce cas). Il faut garder en tête que les hyperviseurs allouent uniquement de la mémoire à la machine virtuelle. Les hyperviseurs par défaut n’ont aucune connaissance de ce qui se passe dans la mémoire virtuelle de la machine virtuelle. Pour ce faire, des outils additionnels doivent être installés. Ci-dessous un exemple simplifié du partage mémoire avec la machine virtuelle.

 

introspection_dans_analyse_de_malware_figure_02

 

Fig 2 : Exemple des trois niveaux d’adressage mémoire sous hyperviseur.

L’un des objectifs des outils d’introspection est de réaliser la traduction des adresses mémoire de la mémoire virtuelle de la machine virtuelle en mémoire physique de la machine virtuelle, et ensuite de la mémoire physique de la machine virtuelle en mémoire physique de la machine physique, dans le but d’aider l’hyperviseur à accéder à la bonne zone mémoire pendant son introspection.

4. LibVMI

4.1 Qu’est-ce que LibVMI ?

LibVMI est une librairie en C permettant de mettre en place un système d’introspection des machines virtuelles sous Linux ou Windows. Elle permet d’accéder à la mémoire d’une machine virtuelle en cours d’exécution. Ceci tout en offrant une panoplie de fonctions permettant d’accéder à la mémoire grâce à l’adressage physique ou virtuel et des symboles Kernel. LibVMI offre aussi un accès à la mémoire à partir d’un Snapshot de mémoire physique, ce qui peut s’avérer intéressant lors de débogage ou d’investigation numérique.

En plus de l’accès mémoire, LibVMI supporte les événements mémoire. Ces événements déclenchent des notifications quand une région mémoire est accédée en lecture, écriture ou en exécution.

Plusieurs niveaux d’abstraction complexes existent quand on parle d’introspection. Ces niveaux sont heureusement gérés par LibVMI [1] et nous sont complètement transparents.

4.2 Les API LibVMI

LibVMI met à disposition une variété de fonctions et d’API intuitives permettant au développeur d’interagir plus facilement avec la mémoire. Il existe des API basiques et d’autres, plus avancées.

Toutes les API peuvent être retrouvées sur le site officiel de LibVMI.

4.3 Exemples d’utilisation

LibVMI fournit par défaut un ensemble d’exemples afin de tester ce concept, voire même reposer sur ces exemples comme base dans le but de créer son propre système d’introspection.

 

Tous les exemples listés ci-après sont à retrouver au complet sur le GitHub de LibVMI [2].

4.3.1 Listing de processus

Le code suivant permet de lister les processus lancés sur la machine virtuelle depuis l’hyperviseur.

On commence par initialiser les contextes de fonctionnement de LibVMI et donc l’instance LibVMI qui correspondra à la machine virtuelle sur laquelle nous lancerons notre programme.

...
vmi_init_complete(&vmi, name, VMI_INIT_DOMAINNAME, NULL, VMI_CONFIG_GLOBAL_FILE_ENTRY, NULL, NULL)

...

Ensuite, suivant le système d’exploitation sur lequel nous souhaitons lister les processus, l’initialisation des champs sera différente (dans l’exemple fourni, on considère trois systèmes d’exploitation différents : Windows, Linux et FreeBSD).

...

if (VMI_OS_LINUX == vmi_get_ostype(vmi)) {
   if (VMI_FAILURE == vmi_get_offset(vmi, "linux_tasks", &tasks_offset))
      goto error_exit;

   if (VMI_FAILURE == vmi_get_offset(vmi, "linux_name", &name_offset))

      goto error_exit;

   if (VMI_FAILURE == vmi_get_offset(vmi, "linux_pid", &pid_offset))

      goto error_exit;

} else if (VMI_OS_WINDOWS == vmi_get_ostype(vmi)) {

   if (VMI_FAILURE == vmi_get_offset(vmi, "win_tasks", &tasks_offset))

      goto error_exit;

   if (VMI_FAILURE == vmi_get_offset(vmi, "win_pname", &name_offset))

      goto error_exit;

   if (VMI_FAILURE == vmi_get_offset(vmi, "win_pid", &pid_offset))

      goto error_exit;

} else if (VMI_OS_FREEBSD == vmi_get_ostype(vmi)) {

   tasks_offset = 0;

   if (VMI_FAILURE == vmi_get_offset(vmi, "freebsd_name", &name_offset))

      goto error_exit;

   if (VMI_FAILURE == vmi_get_offset(vmi, "freebsd_pid", &pid_offset))

      goto error_exit;

}

...

Après avoir récupéré les données nous intéressant, nous allons ensuite boucler sur la liste des processus et les afficher (PID, nom du processus et son adresse mémoire) un par un, jusqu’à la fin de la chaîne des processus.

...

while (1) {

   current_process = cur_list_entry – tasks_offset;

   vmi_read_32_va(vmi, current_process + pid_offset, 0, (uint32_t*)&pid);

   procname = vmi_read_str_va(vmi, current_process + name_offset, 0);

 

   if (!procname) {

      printf("Failed to find procname\n");

      goto error_exit;

   }

 

   printf("[%5d] %s (struct addr:%"PRIx64")\n", pid, procname, current_process);

 

   if (procname) {

      free(procname);

      procname = NULL;

   }

 

   if (VMI_OS_FREEBSD == os && next_list_entry == list_head) {

      break;

   }

 

   cur_list_entry = next_list_entry;

   status = vmi_read_addr_va(vmi, cur_list_entry, 0, &next_list_entry);

 

   if (status == VMI_FAILURE) {

      printf("Failed to read next pointer in loop at %"PRIx64"\n", cur_list_entry);

      goto error_exit;

   }

 

   if (VMI_OS_WINDOWS == os && next_list_entry == list_head) {

      break;

   } else if (VMI_OS_LINUX == os && cur_list_entry == list_head) {

      break;

   }

};

...

Et enfin, nous allons appeler la fonction qui va détruire l’instance LibVMI créée sur la machine virtuelle et libérer la mémoire concernée.

...

vmi_destroy(vmi);
...

4.3.2 Monitoring des événements

Mis à part son utilisation classique comme démontré dans l’exemple précédent, LibVMI permet aussi de monitorer des « événements » à la volée. Cela en notifiant l’hyperviseur de tout accès mémoire qui peut avoir lieu (lecture, écriture, exécution).

Ci-dessous, un exemple tiré aussi des exemples LibVMI permettant de créer des événements d’interruption.

Contrairement à la phase d’initialisation dans l’exemple du listing de processus, une petite modification est nécessaire. Le flag VMI_INIT_EVENTS doit être ajouté pour permettre à l’instance de monitorer les événements à la volée.

...
vmi_init(&vmi, VMI_XEN, (void*)name, VMI_INIT_DOMAINNAME | VMI_INIT_EVENTS, NULL, NULL)

...

Après avoir initialisé l’instance LibVMI, il est possible de créer un callback afin de traquer le type d’événement souhaité. Dans notre exemple, le choix a été fait sur l’événement d’interruption INT3 (breakpoint).

...
memset(&interrupt_event, 0, sizeof(vmi_event_t));
interrupt_event.version = VMI_EVENTS_VERSION;
interrupt_event.type = VMI_EVENT_INTERRUPT;
interrupt_event.interrupt_event.intr = INT3;

vmi_register_event(vmi, &interrupt_event);

...

On déclare ensuite une fonction de callback, qui sera exécutée à chaque fois qu’une interruption a lieu. Cette fonction de callback suppose que tous les événements INT3 sont causés par un débogueur par exemple, et va tout simplement les réinjecter sans rien faire.

event_response_t int3_cb(vmi_instance_t vmi, vmi_event_t *event)

{

   event->interrupt_event.reinject = 1;

 

   if (!event->interrupt_event.insn_length)

      event->interrupt_event.insn_length = 1;

 

   return 0;

}

5. Analyse de malware avec LibVMI

LibVMI permet de manipuler la mémoire, mais en aucun cas cela ne permet de faire de l’analyse de malware tel qu’il est. Afin de profiter du système d’introspection de LibVMI, il faudra développer son propre système pour analyser les malwares en reposant sur les API fournis par LibVMI.

Dans cette partie, nous allons nous focaliser sur le monitoring des API Kernel Windows, afin de lister les différents comportements du malware. Ci-dessous, quelques possibilités fournies par LibVMI pour monitorer la mémoire d’un exécutable.

5.1 Monitoring via des breakpoints

L’idée est donc de placer des breakpoints (points d’arrêt d’exécution, souvent utilisés dans le reverse engineering) sur les API Kernel qu’on souhaite monitorer sur la machine virtuelle lors de l’initialisation du système d’introspection. Ceci, tout en maintenant une table de correspondance des offsets des breakpoints placés et les octets modifiés. Après la mise en place et le lancement du système d’introspection, un malware sollicitant une API Kernel en passant par une API Usermode (l’appel à l’API Usermode CreateFile requiert l’appel à l’API Kernel NtCreateFile) sera interrompu si l’API Kernel figure parmi les API monitorées. Une notification est donc remontée au niveau de l’hyperviseur, nous permettant de retrouver quel exécutable dans l’espace utilisateur a atteint le breakpoint dans l’espace Kernel. Une fois que les informations souhaitées (la fonction, ses arguments…) sont récupérées, l’exécution du malware continue (surtout pour ne pas modifier son comportement) jusqu’au prochain breakpoint. Grâce à ces informations, il nous est possible de comprendre le comportement du malware analysé.

5.2 Monitoring via des pages mémoire

Avec cette technique, il n’y a nul besoin de placer des breakpoints et donc d’altérer la mémoire. L’idée consiste à placer une surveillance avec des événements mémoire sur chaque page mémoire exécutable et de calculer l’adresse des API qu’on souhaite monitorer. À chaque fois qu’une région de la page marquée en exécution est accédée, l’information est remontée et une comparaison est faite avec l’adresse de l’API monitorée.

5.3 Monitoring via des breakpoints + altp2m

Comme décrit précédemment, l’utilisation des breakpoints seule ne fonctionnera pas, c’est pourquoi les développeurs de Xen ont élaboré une technique intitulée altp2m. Cette technique a pour but de créer un shadow copy des pages mémoire souhaitées. L’idée est donc de créer une vue altérée (contenant les breakpoints sur les API Kernel qu’on souhaite monitorer) et garder une vue intacte qui sera retournée à chaque fois qu’il y a un accès en lecture ou écriture à ces pages mémoire.

Grâce à cette méthode, on résout les deux problèmes cités dans 5.1 Monitoring via des breakpoints.

6. Étude de cas

6.1 Préparation de l’environnement

Après avoir choisi le nom du domaine XEN de la machine, créé le fichier contenant le dictionnaire et déterminé le nom de fichier de malware à analyser, on lance depuis l’hyperviseur la commande suivante :

./monitor_api w6164-1 /tmp/w6164-1.json malware.exe

Notre script va prendre en argument plusieurs entrées :

  • monitor_api : un script que nous avons développé et qui repose sur les fonctions de LibVMI ;
  • w6164-1 : le nom du domaine XEN correspondant à de la machine virtuelle ;
  • /tmp/w6164-1.json : le fichier contenant les fonctions/structures et les offsets correspondants ;
  • malware.exe : le malware que nous souhaitons analyser.

Ci-dessous, le code permettant l’initialisation de notre système d’introspection :

...

vmi_init(&vmi, VMI_XEN | VMI_INIT_COMPLETE | VMI_INIT_EVENTS, argv[1]);

...

On crée ensuite la vue altp2m qui sera altérée :

...

xc_altp2m_set_domain_state(xch, domain_id, 1);

...
xc_altp2m_create_view(xch, domain_id, 0, &shadow_view);

...

6.2 Insertion des breakpoints

On définit dans un premier temps les API que nous souhaitons monitorer. Prenons par exemple les trois API suivantes :

  • NtCreateFile : permet la création/ouverture de fichier ;
  • NtSetValueKey: permet de créer ou remplacer la valeur d’une clé de registre ;
  • NtDelayExecution : permet de retarder l’exécution (cette API est exécutée quand l’appel à l’API Sleep est effectué). Elle peut être utilisée à des fins d’évasion.

Pour plus d’informations sur ces API, vous pouvez vous référer à la documentation MSDN [3].

Ensuite, on insère nos breakpoints dans la vue altp2m pour chacune des API.

...
uint8_t trap = 0xcc;

vmi_write_8_pa(vmi, (shadow << 12) + shadow_offset, &trap)

...

6.3 Déclaration des callbacks

6.3.1 Callback des événements d’interruption

Ce callback est déclenché à chaque fois qu’un breakpoint est atteint. Il va permettre de récupérer les informations souhaitées grâce à la fonction process_event(). Cette fonction va nous permettre d’identifier le nom de l’API qui a atteint le breakpoint ainsi que ses arguments.

...
SETUP_INTERRUPT_EVENT(&trap_event, 0, interrupt_event_cb);

vmi_register_event(vmi, &trap_event);
...
event_response_t interrupt_event_cb(vmi_instance_t vmi, vmi_event_t *event)

{

   ...

   process_event(...);

   event->slat_id = 0;

   event->interrupt_event.reinject = 0;

   return VMI_EVENT_RESPONSE_TOGGLE_SINGLESTEP | VMI_EVENT_RESPONSE_VMM_PAGETABLE_ID;

}

6.3.2 Callback des événements de pas-à-pas

Ce callback est déclenché directement après l’exécution du callback d’interruption. Cela permet d’exécuter en pas-à-pas (comme dans un débugueur) l’instruction du breakpoint depuis la vue non altérée. Ensuite, on reprend l’exécution depuis la vue altérée. Dans le cas où plusieurs vcpus sont attribués à la machine virtuelle, il est nécessaire de boucler sur le nombre de vcpus et de créer un callback pour chaque vcpu.

...

int vcpus = vmi_get_num_vcpus(vmi);

for (i = 0; i < vcpus; i++)

{

   SETUP_SINGLESTEP_EVENT(&singlestep_event[i], 1u << i, singlestep_event_cb, 0);

   vmi_register_event(vmi, &singlestep_event[i]);

}

...

event_response_t singlestep_event_cb(vmi_instance_t vmi, vmi_event_t *event)

{

   event->slat_id = shadow_view;

   return VMI_EVENT_RESPONSE_TOGGLE_SINGLESTEP | VMI_EVENT_RESPONSE_VMM_PAGETABLE_ID;

}

6.3.3 Callback des événements lecture/écriture mémoire

Ce callback est déclenché quand une lecture ou écriture est faite sur l’une des pages mémoire du shadow copy. Si tel est le cas, un changement de vue est instantanément fait sur la vue non altérée. Ceci permet d’éviter un BSOD lorsque le système ou un malware effectue une vérification d’intégrité.

...

SETUP_MEM_EVENT(&mem_event, ~0ULL, VMI_MEMACCESS_RW, memory_event_cb, 1);

vmi_register_event(vmi, &mem_event);

...

event_response_t memory_event_cb(vmi_instance_t vmi, vmi_event_t *event)

{

   event->slat_id = 0;

   return VMI_EVENT_RESPONSE_TOGGLE_SINGLESTEP | VMI_EVENT_RESPONSE_VMM_PAGETABLE_ID;

}

Pour plus d’informations sur le fonctionnement de ces callbacks, veuillez consulter le site de LibVMI.

6.4 Récupération des informations

Quand un breakpoint est atteint, on le confronte au dictionnaire d’offsets et d’API, on récupère les informations souhaitées (arguments de l’API) et on poursuit l’exécution.

Prenons exemple d’un breakpoint sur NtCreateFile (ci-dessous son prototype).

NTSTATUS NtCreateFile(

   OUT PHANDLE                       FileHandle,

   IN ACCESS_MASK                    DesiredAccess,

   IN POBJECT_ATTRIBUTES             ObjectAttributes,

   OUT PIO_STATUS_BLOCK              IoStatusBlock,

   IN PLARGE_INTEGER AllocationSize  OPTIONAL,

   IN ULONG                          FileAttributes,

   IN ULONG                          ShareAccess,

   IN ULONG                          CreateDisposition,

   IN ULONG                          CreateOptions,

   IN PVOID                          EaBuffer OPTIONAL,

   IN ULONG                          EaLength

);

Le nom du fichier et les droits d’accès sont principalement les informations intéressantes de cette API. Le nom de fichier se trouve dans la structure OBJECT_ATTRIBUTES. Afin de parser cette structure, on utilisera le dictionnaire généré précédemment et qui contient les offsets des différentes entrées de la structure :

"_OBJECT_ATTRIBUTES": [48, {

   "Attributes": [24, ["unsigned long", {}]],

   "Length": [0, ["unsigned long", {}]],

   "ObjectName": [16, ["Pointer", { "target": "_UNICODE_STRING"}]]

On fera de même pour les autres structures.

Il n’y a nul besoin de modifier les arguments de l’API, même s’il est possible de le faire. Le but est de laisser exécuter le malware sans l’interrompre, récupérer les informations intéressantes à la volée, puis ensuite à la fin de son exécution, analyser les informations remontées et déterminer son comportement.

Ci-dessous un extrait de log des événements générés lors d’analyse de malware. Seules les informations faisant référence à un potentiel comportement malveillant sont gardées.

...

{"target":"C:\\Users\\Francis\\AppData\\Roaming\\Windows\\conhost.exe","process":"C:\\Users\\Francis\\Desktop\\sample.exe","pid":2616,"api":"NtCreateFile","access_mask":1074790528},

...

...

{"target":"\\REGISTRY\\USER\\S-1-5-21-336141597-709016518-532797093-1001\\SOFTWARE\\MICROSOFT\\WINDOWS\\CURRENTVERSION\\RUN\\Persistence","process" : "C:\\Users\\Francis\\Desktop\\sample.exe","pid":2616,"value":"C:\\Users\\Francis\\AppData\\Roaming\\Windows\\conhost.exe","api":"NtSetValueKey"},

...

...

{"value":3600,"process":"C:\\Users\\Francis\\Desktop\\sample.exe","pid":2616,"api":"NtDelayExecution"},

...

Conclusion

Les cyberattaques à base de malware ne cessent d’augmenter et sont de plus en plus sophistiquées, ce qui rend leur détection difficile. L’utilisation de l’introspection peut s’avérer très efficace. Comme démontré dans cet article, elle permet d’aller profondément dans l’analyse de malware et de tirer un maximum d’information sur son fonctionnement, sans avoir besoin d’interrompre son exécution normale.

Références

[1] http://libvmi.com

[2] https://github.com/libvmi/

[3] https://docs.microsoft.com/

 

Sur le même sujet

L’intégration du « Privacy by Design » et de la SSI dans la gestion de projets en mode V ou Agile

Magazine
Marque
MISC
Numéro
106
|
Mois de parution
novembre 2019
|
Domaines
Résumé

L’analyse de l’actualité ne cesse de nous alerter sur la très faible prise en compte de la sécurité native dans un grand nombre de projets et plus particulièrement sur la sous-estimation de l’intégration des exigences de protection de la vie privée.Les articles 25 du RGPD « Protection des données dès la conception et protection des données par défaut » et 32 « Sécurité du traitement », formalisent l’obligation pour le responsable du traitement de prendre en compte les exigences juridiques et techniques pendant toutes les phases des projets de la conception jusqu’à la fin de vie du système cible.Nous nous attacherons à identifier les principaux acteurs concernés et leurs modes de concertation dans les gestions de projets en V ou Agile.Nous chercherons à souligner les points d’attention et d’amélioration dans les deux méthodes.

Élévation de privilèges sur macOS avec CVE-2018-4193

Magazine
Marque
MISC
Numéro
106
|
Mois de parution
novembre 2019
|
Domaines
Résumé

Cet article explique comment exploiter la CVE-2018-4193, une élévation de privilèges affectant les versions de macOS inférieures à 10.13.5, en moins de 10 secondes. Les différents prérequis nécessaires à la compréhension de l’exploit seront détaillés de sorte qu’aucune connaissance préalable de macOS ne soit nécessaire, ce qui permet d’aborder l’exploitation de vulnérabilités dans les démons macOS en général.

Auditer la sécurité d'une application iOS

Magazine
Marque
MISC
Numéro
106
|
Mois de parution
novembre 2019
|
Domaines
Résumé

Auditer la sécurité d'une application iOS n'est toujours pas une tâche aisée. Force est de constater que la plupart des auditeurs, amateurs de bug bounty ou autres curieux préfèrent travailler sur les applications Android malgré les récentes protections ajoutées au système d'exploitation de Google. Nous allons malgré tout essayer de présenter une méthodologie qui rend possible l'analyse orientée sécurité d'une application iOS, même sans jailbreak. Un bref rappel sera effectué pour ensuite introduire quelques outils et documentations apparues ces derniers mois.

Sondes de détection : performances, évaluations et biais

Magazine
Marque
MISC
Numéro
106
|
Mois de parution
novembre 2019
|
Domaines
Résumé

En avril 2019, l’ANSSI a qualifié les premières sondes pour assurer la supervision de sécurité de réseaux. Les opérateurs d’importance vitale (OIV), les opérateurs de services essentiels (OSE) et, d’une manière générale, les organismes opérant des fonctions sensibles disposent ainsi de produits français de confiance : Cybels Sensor de Thales et Trackwatch Full Edition de Gatewatcher.La méthodologie d’évaluation des sondes n’est, hélas, pas publique. Les ingénieurs sécurité et réseau devant intégrer ces sondes ne disposent donc pas de guides pour effectuer la recette de leur efficacité en production. Cet article propose un retour d’expérience sur l’évaluation des sondes, notamment sous l’angle de la performance. Cet aspect est, en effet, particulièrement significatif puisque le taux de détection d’une sonde diminue si elle est submergée, quand bien même elle serait équipée des meilleurs signatures et moteurs d’analyse.

Richelieu : solution du challenge de la DGSE

Magazine
Marque
MISC
Numéro
106
|
Mois de parution
novembre 2019
|
Domaines
Résumé

En mai 2019, la DGSE (Direction Générale de la Sécurité Extérieure) a organisé pour la première fois un challenge de sécurité informatique ouvert au public, qu’elle a baptisé Challenge Richelieu. Je vous invite à écouter la bande originale du Bureau des Légendes en fond sonore. Imprégnez-vous de cette atmosphère pleine de tension où les secrets sont rois, puis regardons ensemble ce que nous réservait ce challenge.

Par le même auteur