Le système d'exploitation OS X, comme tout système d’exploitation, peut être victime d’un logiciel malveillant. Malgré qu’il y ait déjà eu quelques cas documentés de malwares sous OS X avant, OSX/Flashback est de loin celui qui a fait le plus de victimes. Dans cet article, nous décrivons les caractéristiques techniques les plus intéressantes de cette menace, entre autres son mode de « hooking » pour espionner les communications réseau et ses algorithmes de génération dynamique de noms de domaines. Nous présenterons finalement un récapitulatif des événements marquants de ce malware dont le cycle de vie s’est étendu sur plusieurs mois.
1. Introduction
Flashback est une menace sur OS X qui a été détectée pour la première fois à l’automne 2011 [1]. Après être passé inaperçu pendant plusieurs mois, Flashback attire l’attention générale en avril 2012 alors qu’il réussit à infecter plus de 500 000 ordinateurs. Comment le taux d’infection a-t-il pu être si élevé ? Est-ce que les techniques d’obfuscation de Flashback sont aussi complexes que ce qu’on voit généralement dans les logiciels malveillants sous Windows ? Quel était l’objectif de l’auteur ?
Dans cet article, nous verrons d’abord les méthodes de propagation utilisées par Flashback. Ensuite, nous présenterons une analyse de deux des composantes de Flashback : la composante d’installation et la bibliothèque qui sert à intercepter le trafic réseau afin d'espionner l’utilisateur.
2. Vecteur d’infection
La méthode utilisée pour infecter les victimes de Flashback a évolué au fil du temps. Les premières variantes se masquaient comme une mise à jour du lecteur Flash d’Adobe. La victime était dirigée vers un site malveillant, vraisemblablement suite à des campagnes malveillantes de « search engine optimisation » (SEO). La victime, convaincue d’avoir à faire à une mise à jour légitime, téléchargeait et exécutait le fichier offert. En entrant son mot de passe tel que demandé durant l’installation, la victime permettait à Flashback de s’installer sur son Mac.
La seconde méthode d’infection à avoir été recensée utilisait plutôt un applet Java signé. En visitant un site malveillant, la victime recevait un message de son interpréteur Java demandant l’autorisation d’exécuter un applet qui prétendait être signé par Apple. Bien entendu, le certificat ne provenait pas d’Apple, il était auto-signé. Une fois l’autorisation donnée par l’utilisateur, son Mac devenait infecté.
La méthode qui a été de loin la plus efficace pour propager l’infection de Flashback fut d’exploiter deux failles dans Java : CVE-2012-0507 et CVE-2011-3544. Dans ce cas, les vulnérabilités permettaient à Flashback de s’installer à l’insu de l’utilisateur simplement lors de la visite d’un site web contenant l’applet Java malveillant, soit directement ou via un iframe. Plus d’un demi-million de Mac ont été infectés de cette manière.
Au fil du temps, les méthodes d’obfuscation de chaque composante se sont complexifiées. Le reste de cette analyse est basé sur la plus récente variante de Flashback, celle qui a infecté la majorité des ordinateurs à l’aide de la faille CVE-2011-0507.
3. Le module d’installation
Une fois que l’exploit java est exécuté avec succès, un fichier exécutable Mach-O est installé dans le répertoire de l’utilisateur. Le nom du fichier commence par un point pour être caché. Un fichier plist est créé dans ~/Library/LaunchAgents pour lancer l’exécutable à chaque fois que l’utilisateur s’authentifie sur son poste infecté. L’unique but de ce fichier exécutable est le téléchargement et l’installation de la composante d’interception. Nous analyserons d’abord les particularités de ce module d’installation, puis nous verrons la composante d’interception.
3.1. Techniques d’obfuscation
L’analyse dynamique du module d'installation nous montre que lors de sa première exécution, le malware envoie par HTTP le Platform UUID du système infecté au serveur de commandes et contrôle. La réponse à cette première requête n’est pas considérée par le malware. Cette URL n’est donc pas un centre de contrôle. Nous croyons plutôt qu’elle est seulement utilisée par l'opérateur du logiciel malveillant pour des fins de collecte de données statistiques.
Suite à la première exécution, nous remarquons que le fichier exécutable lui-même a été modifié. Que peut-il y avoir de différent ? Nous remarquons premièrement que l’URL de statistiques utilisée lors de la première exécution est retirée du fichier exécutable. De plus, une grande partie de la section des données est totalement modifiée.
Malgré les modifications, le fichier demeure un exécutable valide. Les exécutions subséquentes sont identiques à la première, à une exception près : il n’y a plus de trafic vers l’URL qui a été supprimée. Il semble donc que nous soyons face à un logiciel malveillant qui s’auto-chiffre.
Afin de pouvoir analyser les fichiers malveillants chiffrés soumis par nos clients ou trouvés sur Internet, nous avons dû analyser les méthodes de chiffrement de Flashback. Regardons d’abord comment la section chiffrée est utilisée au début de l’exécution.
v9 = IORegistryEntryFromPath(kIOMasterPortDefault_ptr, "IOService:/");
v10 = *kCFAllocatorDefault_ptr;
v11 = __CFStringMakeConstantString("IOPlatformUUID");
uuid_cfstr = IORegistryEntryCreateCFProperty(v9, v11, v10, 0);
if ( !uuid_cfstr )
return 0;
IOObjectRelease(v9);
uuid = g_uuid_ref;
CFRelease(uuid_cfstr);
strings = (char *)malloc(*g_strings_size_ptr);
{ // Première exécution, g_strings n'est pas encore chiffré
memcpy(strings, g_strings, strings_size);
}
else
{ // Une déchiffrement d'impose
uuid_len = strlen(uuid);
v14 = 0;
do
{ // Initialisation de la table RC4
rc4_table[v14] = v14;
++v14;
}
while ( v14 != 256 );
v15 = rc4_table;
index = 0;
v17 = 0;
v213 = 0;
v214 = 0;
do
{ // Création de la table RC4 avec le Plaform UUID comme clé
v18 = index++;
v19 = *v15;
v17 += (unsigned __int8)(uuid[(unsigned __int64)(v18 % uuid_len)] + *v15);
LODWORD(v18) = &rc4_table[(unsigned __int8)v17];
*v15++ = *(_BYTE *)v18;
*(_BYTE *)v18 = v19;
while ( index < (signed int)strings_size )
{ // Déchiffrement du bloc chiffré
++v213;
v20 = rc4_table[v213] + v214;
v21 = &rc4_table[v213];
v214 = v20;
*v21 = *v22;
*v22 = v23;
strings[index] = rc4_table[(unsigned __int8)(rc4_table[v214] + rc4_table[v213])] ^ g_strings[index];
++index;
}
}
D’abord, on voit que le logiciel malveillant obtient une copie du Platform UUID. Le Platform UUID d’un Mac est un identifiant unique que possèdent tous les ordinateurs Mac, un peu comme un numéro de série ou l’adresse MAC d’une carte réseau. On remarque que si l’exécutable contient une URL, il n’y aura pas de déchiffrement. En effet, c’est qu’il s’agit de sa première exécution, donc il n’a pas encore été chiffré. On se contente de copier directement avec memcpy. Dans le cas où il n’y a pas d’URL, c’est que le fichier a été modifié. L’auteur a réimplémenté l’algorithme RC4 pour déchiffrer le contenu en utilisant le Platform UUID comme clé.
Comme le Platform UUID est unique pour chaque machine, un fichier exécutable chiffré ne peut s’exécuter sur un autre Mac que celui qui l’a chiffré en premier lieu. Les variantes qui nous sont soumises et celles trouvées sur Internet étaient presque exclusivement chiffrées. L'algorithme pour générer les 128 bits qui forment le Platform UUID n'est pas divulgué par Apple. Sans connaître le Platform UUID de la machine infectée, il était impossible de dévoiler leur contenu.
Mais que peut bien contenir cette partie chiffrée ? Même après l’avoir déchiffrée avec RC4, nous n’avons pas de chaînes de caractères en clair ni de structure de données connue. Voyons comment le bloc est utilisé plus loin. Il faudra continuer à suivre l’exécution pour trouver des appels à une fonction qui trouve des chaînes dans la structure. Voici quelques exemples de ces appels :
get_string(&strings_struct, 0xD18Fu, 0xDC737201735473FAuLL, (char *)&v240, &v239);
get_string(&strings_struct, 0xF12Eu, 0x4748FF63A8193474uLL, (char *)&v252, &v251);
get_string(&strings_struct, 0xE002u, 0x836391EF93A94401uLL, (char *)&v250, &v249);
get_string(&strings_struct, 0x6C8Au, 0x9183AACBE1931244uLL, (char *)&v248, &v247);
...
Regardons le contenu de la fonction :
signed int __cdecl get_string(strings_s *strings_struct, unsigned __int16 key, unsigned __int64 xor_key, char **decrypted, int *decrypted_size)
{
signed int v5; // eax@1
signed int ret_value; // edx@1
char *value; // esi@2
int key_byte; // ecx@2
int i; // ebx@2
char xored_value; // dl@3
v5 = find_string(strings_struct, key, decrypted, decrypted_size);
ret_value = 5;
key_byte = 0;
{
xored_value = *((_BYTE *)&xor_key + key_byte++) ^ value[i];
value[i] = xored_value;
if ( key_byte == 8 )
key_byte = 0;
}
ret_value = 0;
}
return ret_value;
}
get_string qui prend 5 paramètres :
- strings_struct : une structure qui contient un pointeur vers nos données ;
- key : la clé de la valeur à aller chercher dans les données ;
- xor_key : la clé XOR a utilisé pour déchiffrer le contenu ;
- decrypted : en sortie, contiendra un pointeur vers la valeur déchiffrée dans le dictionnaire ;
- decrypted_length : en sortie, contiendra la longueur de la chaîne.
get_string trouve la chaîne dans le dictionnaire à partir de la clé avec find_string, puis applique la clé XOR donnée à tous les blocs de 64 bits. Si on analyse find_string, on trouve la structure d’un dictionnaire en mémoire. La table suivante montre la structure représentant ce dictionnaire.
Magic Number |
1 octet (0xFD) |
Clé 1 (k1) |
2 octets |
Longueur 1 (l1) |
4 octets |
Valeur 1 (v1) |
l1 octets |
Magic Number |
1 octet (0xFD) |
Clé 2 (k2) |
2 octets |
Longueur 2 (l2) |
4 octets |
Valeur 2 (v2) |
l2 octets |
... |
... |
Heureusement, les données et leurs clés XOR sont les mêmes d’une variante à l’autre, ce qui nous rend la tâche plus facile pour déchiffrer statiquement les différentes variantes. La partie chiffrée contient donc un dictionnaire de clés et valeurs qui seront utilisées par le module d’installation.
À partir d’ici, on commence à voir des chaînes en clair, mais la plupart sont encore obfusquées. Une dernière passe de déchiffrement permet de dévoiler leur valeur finale. L’algorithme qui est utilisé dans ce dernier déchiffrement ne semble pas être un algorithme connu. En gros, une liste pseudoaléatoire (mais déterministe) de 216 octets est générée et chaque mot de 2 octets dans la chaîne correspond à l’indice de l’octet voulu dans la liste.
Une fois toutes ces étapes complétées, on retrouve plusieurs listes séparées par des |. Voici le résultat final de notre déchiffrement :
$ python extract_dropper_config.py sbm
Filename : sbm
MD5 : 473426b7be5335816c545036cc724021
SHA1 : 94e4b5112e750c7902968d97237618f5b61efeb2
0x0fa7 : Public Key Exponent : 65537
0xd18f : Public Key Modulus : 55ead1182a...81be12abef (2048 bits)
0x6192 : 0xdedbe511, 0x1f2e4872, 0x237345de
0x1f91 :
[00] .com
... [04] .kz
0x4280 :
[00] ##begin##
[01] ##sign##
[02] ##end##
[03] /index.html
[04] Mozilla/5.0 (Windows NT 6.1; WOW64; rv:9.0.1; sv:%s; id:%s) Gecko/20100101 Firefox/9.0.1
[05] nohup "%s" 1>&2 &>/dev/null &
[06] /tmp/
0x6c8a :
[00] 4
[01] sysctl.proc_cputype
0x92be :
[23] kkkgmnbgzrajkk.com
[00] /Library/Little Snitch
[01] /Developer/Applications/Xcode.app/Contents/MacOS/Xcode
...
[06] /Applications/HTTPScoop.app
[07] /Applications/Packet Peeper.app
0xe002 :
[00] _NSGetExecutablePath
[01] CFStringCreateWithCString
...
[30] BN_bin2bn
[31] RSA_new
0xf12e :
[00] /System/Library/Frameworks/IOKit.framework/Versions/A/IOKit …
[05] /usr/lib/libcrypto.dylib
À la clé 0x92fa, on voit une liste de chemins vers des logiciels antivirus, pare-feu ou destinés à des utilisateurs expérimentés. Si un de ces fichiers existe, l’exécution se terminera et le malware se supprimera du système.
On retrouve aussi des noms de bibliothèques et de fonctions aux clés 0xf12e et 0xe002. Celles-ci seront chargées dynamiquement avec dlopen et dlsym. Connaissant maintenant les fonctions qui sont appelées, nous comprendrons mieux le comportement de notre malware.
3.2. Comportement
Périodiquement, le logiciel malveillant interroge une liste de domaines pour obtenir un fichier à télécharger et exécuter. Les domaines proviennent de trois sources distinctes :
- Une liste de domaines est codée en dur dans le module d’installation (à la clé 0x92be).
- 5 préfixes de domaines sont générés dynamiquement à partir de constantes trouvées dans le module d’installation (les trois constantes à la clé 0x6192).
- Un autre préfixe de domaine est généré dynamiquement en fonction de la date.
Pour chacun des préfixes de domaines générés dynamiquement aux points 2 et 3, les suffixes qui seront ajoutés à chacun sont à la clé 0x1f91. Dans toutes les variantes que nous avons répertoriées, il s’agit de 5 domaines de premier niveau. Les préfixes générés sont des chaînes pseudoaléatoires de 11 à 13 lettres. Chaque jour, le logiciel malveillant génère une chaîne pseudoaléatoire et tente avec les 5 suffixes.
En excluant les domaines autogénérés en fonction de la journée au point 3, nous avons identifié 185 domaines en provenance de toutes les variantes à notre disposition. Une des particularités de la composante d’installation de Flashback est que l’auteur n’avait pas préalablement enregistré tous les domaines possibles, leur nombre étant peut-être trop important. De plus, l’algorithme pour générer pour la journée est le même d’une variante à l’autre.
En faisant la rétro-ingénierie de cet algorithme, plusieurs compagnies, dont DrWeb, ESET, Kaspersky et Symantec, ont pu enregistrer des noms de domaines toujours disponibles et mettre en opération des sinkholes. Cela a permis d’estimer le nombre de systèmes infectés.
Une fois la connexion établie avec un des domaines, le logiciel tente un HTTP GET. Il s’attend à avoir une réponse avec le format
##begin##
<executable encodé en base64>
##sign##
<signature en base64>
##end##
Vous avez probablement remarqué la présence d’une clé publique dans les chaînes plus haut à la clé 0xd18f et 0x0fa7. Cette clé sera utilisée pour vérifier la signature.
La seule chose que nous avons vu téléchargée par la composante d’installation est un exécutable permettant d’installer une composante d’interception de trafic réseau. La prochaine section montre les résultats de l'analyse de ce module.
4. Composante d’interception web
Notre analyse semble indiquer que le but premier de la composante d’installation est de mettre en place un second module pour intercepter les communications HTTP et HTTPS. Cette interception permet l’injection de publicités à travers les flux HTTP et ainsi les afficher à l’utilisateur. Ce nouveau module est indépendant de la composante d’installation que nous avons vue précédemment. Dans cette section, nous montrons les fonctionnalités d’interception HTTP utilisées par Flashback.
4.1. Une bibliothèque
La composante d’interception n’est pas sous forme d’un fichier exécutable. Il s'agit d'une bibliothèque. Mais comment donc le code à l’intérieur parvient-il a être exécuté ? La composante de Mac OS X qui s’occupe de charger dynamiquement les bibliothèques s'appelle dyld. En temps normal, les chemins vers les bibliothèques nécessaires à l’exécution d’un programme sont dans son en-tête Mach-O et dyld s’occupe de les charger à l’exécution. La page de manuel de dyld [6] montre différentes variables d’environnement permettant de configurer dyld. Pour être chargé, Flashback utilise DYLD_INSERT_LIBRARY qui permet de charger une bibliothèque avant ceux qui sont spécifiés dans le programme à exécuter. Pour être en mesure de changer cette variable d’environnement de manière persistante, Flashback utilise 2 techniques.
- S’il dispose des privilèges administrateurs, il ira modifier les métadonnées des fureteurs installés pour affecter la variable d’environnement avant l’exécution. Ceci est possible en l’ajoutant dans la clé LSEnvironment du Info.plist à l’intérieur d’une application.
- S’il ne dispose pas des privilèges administrateurs, il ira l'ajouter dans le fichier ~/.MacOSX/environement.plist. Il prend soin de le créer s’il n’existe pas (dans la plupart des cas il n’existe pas). Au login de l’utilisateur, la variable sera affectée, donc la bibliothèque sera chargée dans toutes les applications qui seront démarrées par l’utilisateur.
Pour les utilisateurs infectés par l’exploit Java, c’est la seconde méthode qui est utilisée puisque l’applet n’a pas les privilèges administrateurs.
4.2. Flashback s’interpose
La bibliothèque contient une section __interpose, qui permet de remplacer une fonction fournie par une autre bibliothèque chargée [5]. Avec DYLD_INSERT_LIBRARY, il est donc possible de s’interposer entre l’appelant et la fonction originale. Le résultat est semblable à l’utilisation de LD_PRELOAD sous Linux.
Flashback interpose 2 fonctions : CFReadStreamRead et CFWriteStreamWrite. Ces deux fonctions font partie de CoreFoundation, l’API C de Mac OS X. Comme leurs noms l’indiquent, ces fonctions servent à envoyer et recevoir des données sur un flux. À moins d’utiliser directement les fonctions de bas niveau send et recv, toutes les communications réseau sous Mac OS X passeront par ces fonctions.
Il est intéressant de savoir qu’il est possible de créer un CFStream chiffré en SSL à l’aide des fonctions de CoreFoundation. Cela signifie que l’interposition de Flashback lui permet d’intercepter les données HTTPS dans leur état déchiffré.
4.3 Configuration
Lorsqu’on ouvre la bibliothèque dans un désassembleur, on remarque une grosse chaîne de caractères encodée en base64. Même décodé, le résultat ne donne malheureusement rien d’intelligible. Nous n’aurons donc pas le choix de trouver comment elle est décodée pour pouvoir accéder à son contenu. La section suivante de la bibliothèque montre la routine qui s’occupe du décodage.
std::allocator<char>::allocator(&v29);
std::string::string(&base64_config, (const char *)base64_config_ref + 5, &v29);
base64_decode(&crypted_config, &base64_config);
std::string::_string(&base64_config);
std::allocator<char>::_allocator(&v29);
rc4_crypt(&v10, &a2->uuid, &crypted_config);
std::allocator<char>::allocator(&v30);
std::string::string(&static_rc4_key, g_rc4_key, g_rc4_key_size, &v30);
rc4_crypt(&v20, &static_rc4_key, &v10);
uncompress_h(&plain_text_config, (const Bytef **)&v20);
On voit d’abord le classique décodage base64, décaler de 5 octets plus loin. cfinh sert en fait de marqueur, on le retrouve dans toutes les variantes. Ensuite, il y a déchiffrement avec RC4 en utilisant le Platform UUID comme clé, et enfin, un déchiffrement avec RC4 en utilisant cette fois-ci une clé de 16 caractères codée en dur dans le binaire. Pour terminer, la fonction uncompress est appelée pour décompresser les données déchiffrées. Encore une fois, on remarque qu’une partie intéressante de Flashback est chiffrée avec le Platform UUID, ce qui rend l’analyse très difficile si le rétro-ingénieur ne possède pas cette information.
Une fois décodée, la chaîne de caractères représente un dictionnaire composé de plusieurs éléments.
...{2588545561:3:OTk5},{2279540384:1:40},{201539444:3:aHR0cDovLw==},{3604130400:3:U2FmYXJ8V2ViUHJv}...
On retrouve respectivement pour chaque élément la clé, le type puis sa valeur. On remarque que pour les types autres qu’un entier (type 1), la valeur est encodée en base64.
Ces données sont réellement la clé de notre analyse, car elles représentent la configuration de Flashback : elle contient entre autres les adresses des serveurs de contrôle et une liste de noms de domaines pour se mettre à jour.
Une particularité de Flashback est son importante liste de domaines contenue dans la configuration. On retrouve plusieurs domaines de secours pour les serveurs de contrôle ainsi qu’une grande liste de domaines où il peut se mettre à jour. En analysant tous nos échantillons, nous avons dénombré un total de 276 noms de domaines. Comme pour la composante d’installation, l’auteur n’a enregistré que quelques-uns de ces domaines.
4.4. Validation du serveur de commandes et contrôle
La première chose qu’on trouve dans la trace réseau est un HTTP GET vers /scheck. Voici le format de la réponse :
MWU5MWNiNjJjZDVlYTMwN2E5OWYxZGYzMDU2MmE5NmRiOTUzMTYyNg==|OKOnEr8jeQuUW[...]mlBW2M=
Le décodage du base64 ne donne rien d’intéressant. Pas d’ASCII, pas de fichier compressé, rien que nous connaissons. La deuxième partie fait 512 octets. Il faudra aller voir dans le code pour trouver l’utilisation d’OpenSSL relié à cette requête.
v9 = get_item_at_index(&v20, 0); // la première partie avant le |
std::string::string(&a2, v9);
base64_decode(&hex_digest, &a2);
std::string::_string(&a2);
v10 = get_item_at_index(&v20, 1); // la deuxième partie après le |
std::string::string(&v25, v10);
base64_decode(&signature, &v25);
std::string::_string(&v25);
if ( verify_signature_with_rsa(system_info->rsa, &hex_digest, &signature) )
{
cnc_hostname = get_item_at_index(&cnc_list, cnc_index);
sha1_hexdigest(&cnc_hostname_hash, cnc_hostname);
v12 = std::string::compare(&cnc_hostname_hash, &hex_digest, v15);
std::string::_string(&cnc_hostname_hash);
if ( !v12 )
{
valid_cnc = get_item_at_index(&v19, cnc_index);
if ( !system_info )
system_info = create_system_info();
set_cnc(system_info, valid_cnc);
On regarde d'abord si la signature (la deuxième partie de la réponse) est valide avec une clé RSA de 2048 bits en dur dans la bibliothèque. verify_signature_with_rsa utilise RSA_verify de OpenSSL. Ensuite, on compare la partie signée (la première partie de la réponse) à la somme SHA1 du nom de domaine du centre de contrôle. On peut vérifier que c’est bien le cas ici.
base64(sha1('95.154.246.120') en hex) ⇒ MWU5MWNiNjJjZDVlYTMwN2E5OWYxZGYzMDU2MmE5NmRiOTUzMTYyNg==
Dans la liste de centres de commandes et contrôle, plusieurs domaines n’avaient pas été enregistrés par l’auteur. Cette vérification au démarrage a été implémentée pour éviter qu’un tiers prenne le contrôle et envoie des commandes aux Mac infectés.
4.5. Interception
À l’interception des données, Flashback détermine s’il s’agit d’une requête HTTP GET en regardant le début des données envoyées à CFWriteStream. Lorsqu’il s’agit d’une recherche envoyée à Google, les mots clés recherchés ainsi que des informations sur la machine comme le Platform UUID et la langue configurée sont envoyés au serveur de commandes et contrôle. Celui-ci lui répondra la prochaine action à exécuter en prenant bien soin de la chiffrer à l’aide de RC4 avec le hash MD5 du Platform UUID comme clé. La requête à Google n’est pas modifiée, mais la réponse peut être altérée pour simuler un clic sur une publicité.
Voici un exemple de réponse valide du serveur de commandes et contrôle :
BIDOK|http://searcher-sports.com/?id=psADTepsYZ...2nkZ2NkWZw-CXFI%2C|0.122|http://artkitty.com/?q=casino
On retrouve une liste des commandes possible en clair dans la bibliothèque.
__cstring:00022364 aBidok db 'BIDOK',0 ; DATA XREF: sub_13522+6D7o
__cstring:0002236A aBidfail db 'BIDFAIL',0 ; DATA XREF: sub_13522+78Eo
__cstring:00022372 aH_setup db 'H_SETUP',0 ; DATA XREF: sub_13522+7BAo
__cstring:0002237A aAdd_s db 'ADD_S',0 ; DATA XREF: sub_13522+889o
__cstring:00022380 aMu db 'MU',0 ; DATA XREF: sub_13522+8EDo
__cstring:00022383 aSk db 'SK',0 ; DATA XREF: sub_13522+951o
Durant nos expérimentations, nous avons uniquement observé l’utilisation de deux commandes, soit BIDOK et BIDFAIL. Les autres commandes, qui servent entre autres à ajouter des serveurs dans sa liste (ADD_S) ou encore à s’autodétruire (SK), n’ont pas été vues dans nos captures de trafic.
4.6. Utilisation de Twitter comme mécanisme de commandes et contrôle
On retrouve aussi dans la configuration une URL pour faire une recherche d’un hashtag sur Twitter. À quoi peut-elle bien servir ? Si on regarde comment elle est utilisée, on retrouve une autre technique dont le botmaster dispose pour contrôler son botnet.
generate_string_for_day(&generated_string_for_day, user_agent, day, month, year);
get_config_string(&v14, &twitter_config, 0xE21C0275u);// http://mobile.twitter.com/searches?q=%23
base64_decode_string(&v26, &v14);
string_concat(&twitter_url, &v26, &generated_string_for_day);
std::string::_string(&v26);std::string::_string(&v15);
get_config_string(&v16, &twitter_config, 0xEE3A469Du);// Mozilla/4.0 (compatible; MSIE 7.0; Windows Phone OS 7.0; Trident/3.1; IEMobile/7.0; HTC; 7 Mozart T8698)
base64_decode_string(&random_user_agent, &v16);
std::string::_string(&v17);
get_config_string(&v18, &twitter_config, 0x37CF19CAu);// 442
user_agent_count = string_to_integer(&v18);
std::string::_string(&v19);
if ( user_agent_count > 1 )
{
random_int = rand();
get_config_string(&user_agent_b64, &twitter_config, random_int % user_agent_count + 0xAEEE0000);
base64_decode_string(&v27, &user_agent_b64);// 0xAEEE0000 à 0xAEEE01B9 contient des user agents de différents appareils mobiles
std::string::assign(&random_user_agent, &v27);
std::string::_string(&v27);
std::string::_string(&v21);
}
make_http_request(&v29, &twitter_url, &random_user_agent);
get_config_string(&v22, &twitter_config, 0x9FC4EBA3u);// bumpbegin
base64_decode_string(&v28, &v22);
std::string::_string(&v23);
get_config_string(&v12, &twitter_config, 0xEAC11340u);// endbump
base64_decode_string(&v32, &v12);
std::string::_string(&v13);
v7 = std::string::find(&v29, &v28);
v8 = std::string::find(&v29, &v32);
Un hashtag différent est généré chaque jour. Une recherche de ce hashtag sur Twitter révèle l’adresse IP ou le nom de domaine du nouveau centre de contrôle pour se mettre à jour. Dans le tweet, on trouve l'information entre les délimiteurs beginbump et endbump (ces délimiteurs font aussi partie de la configuration).
generate_string_for_day concatène 3 chaînes de caractères à partir d’une liste dans la configuration. Si, par exemple, dans la configuration on trouve :
1 : abcd
2 : efgh
3 : ijkl
le hashtag pour le 2 février 2003 sera #efghabcdijkl (le mois de janvier étant 0). Nous avons répertorié 6 listes de chaînes de caractères différentes dans les diverses variantes.
Nous n’avons aucune trace de tweet du malfaiteur. Probablement qu’il les a déjà supprimés s’il s’en est vraiment servi. Nous pouvons voir par contre que quelqu'un qui semble travailler pour une compagnie antivirus a tenté de ramener du trafic chez lui en ajoutant des tweets vers un de leurs serveurs.
4.7. Domaines générés dynamiquement
Lors de nos tests, nous avons vu un autre élément intéressant dans notre trace réseau. Flashback essayait de résoudre des noms de domaines qui commencent par le hashtag du jour. On trouve dans la configuration une liste de suffixes à appliquer à la chaîne générée, comme pour la composante d’installation.
Key : 0xb78140d6
Value : .org|.com|.co.uk|.cn|.in
Et dans une variante plus ancienne :
Key : 0xb78140d6
Value : .org|.com|.co.uk|.cn|.in|.PassingGas.net|.MyRedirect.us|.rr.nu|.Kwik.To|.myfw.us|.OnTheWeb.nu|.IsTheBe.st|.Kwik.To|.ByInter.net|
FindHere.org|.OnTheNetAs.com|.UglyAs.com|.AsSexyAs.com|.PassAs.us|.PassingGas.net|.AtHisSite.com|.AtHerSite.com|.IsGre.at|.Lookin.At|.BestDeals.At|.LowestPrices.At
Ces domaines seront utilisés, après la liste dans la configuration, pour se mettre à jour. Les mises à jour sont aussi signées, donc difficile pour un tiers sans la clé privée d’enregistrer le nom de domaine du jour et propager son propre code.
4.8. Déchiffrement en masse des échantillons
Au début du mois d’avril, ESET a été en mesure d’enregistrer des noms de domaines utilisés par la composante d’installation de Flashback. Le malware nous facilitait les choses sur un point : il envoie le Platform UUID de la machine sur lequel il est installé dans le champ User-Agent de l’en-tête HTTP. Il est donc possible pour nous de compter de façon assez précise le nombre de machines infectées puisque le Platform UUID identifie de façon unique un Mac.
Nous avions en notre possession plusieurs échantillons de Flashback, mais nous avions un problème majeur : nous ne sommes pas en mesure de déterminer le Platform UUID de l’ordinateur infecté. Avec notre sinkhole en place, les chances que l’ordinateur infecté ait communiqué avec celui-ci sont grandes. Grâce à cet outil, nous avons pu recueillir environ 600 000 Platform UUID. À partir de ce moment, il était possible d’utiliser cette liste pour déchiffrer les échantillons par force brute autant pour la composante d’installation que pour la composante d’interception.
5. Chronologie des événements
Septembre 2011 : Apparition de la première variante ;
Février 2012 : Oracle rend disponible une mise à jour pour Java qui corrige une faille exploitée par Flashback [7] ;
Mars 2012 : Propagation rapide via l’exploit Java ;
Fin mars 2012 : Premiers sinkholes enregistrés par différentes compagnies antivirus ;
3 avril 2012 : Apple rend disponible la mise à jour de Java avec la faille corrigée ;
4 avril 2012 : Première statistique sur les sinkholes (DrWeb) ;
6 avril 2012 : Apple publie une seconde mise à jour pour Java ;
13 avril 2012 : Apple publie un outil pour nettoyer Flashback [8] ;
1er mai 2012 : Les centres de contrôles ne répondent plus.
Conclusion
Certains utilisateurs Mac se croient à l’abri des logiciels malveillants en utilisant Mac OS X. Certes, les menaces sont moins nombreuses que sur Windows, mais elles ne sont pas inexistantes. Flashback est un exemple d’attaque à grande échelle contre cette plate-forme. On retrouve aussi des attaques plus ciblées comme dans le cas de Lamadai [3] et MacControl [4] qui s’attaquaient aux organisations non gouvernementales tibétaines.
La version de Java installée avec Mac OS X ne peut pas être mise à jour par Oracle. C’est Apple qui la valide et la redistribue via son outil de mise à jour système. Apple a-t-il été trop lent pour publier la mise à jour ? Probablement. Sachant que la mise à jour corrige une faille de sécurité dont la technique d’exploitation est disponible sur Internet, deux mois donnent suffisamment de temps pour faire beaucoup de dégâts.
Depuis Mac OS X Lion (10.7), Apple n’installe plus d’interpréteur Java par défaut sur son système d’exploitation. On peut penser que la compagnie tente de se départir de la responsabilité de la mise à jour des logiciels qui sont hors de son contrôle.
Apple a réagi très rapidement après la médiatisation de Flashback. Ils ont d'abord enregistré tous les noms de domaines disponibles reliés à Flashback, incluant ceux générés dynamiquement. Peu après, une mise à jour d'OS X détectait la présence de Flashback et le désinstallait du système. Apple est resté très silencieux sur sa stratégie. La présence de Flashback dans les médias n'est pas une bonne publicité pour Apple.
Il reste bien des questions sans réponses. Qui sont les auteurs de Flashback ? S’attendaient-ils à avoir un taux d’infection si élevé et d’être médiatisés à ce point ? Ont-ils tout simplement abandonné ? Maintenant que la démonstration est faite que OS X n’est pas à l’abri d’une infection à grande échelle, les auteurs de logiciels malveillants pourraient s’intéresser davantage à OS X pour déployer leur malware. Les utilisateurs de Mac ont donc intérêt à adopter des pratiques d’utilisation sécuritaires.
Remerciements
Merci à Pierre-Marc Bureau et Alexis Dorais-Joncas pour leurs relectures et corrections.
Fichiers analysés
Nom |
MD5 |
SHA1 |
sbm |
473426b7be5335816c545036cc724021 |
94e4b5112e750c7902968d97237618f5b61efeb2 |
fb_10.so |
0de5cb4d61a09d4615f17f1eb85783a9 |
7a5e75b563c87320977e47dc220bea5782e9ce92 |
Références
[1] http://go.eset.com/us/threat-center/threatsense-updates/page/11/?q=flashback
[2] http://blog.eset.com/2012/04/13/fighting-the-osxflashback-hydra
[3] http://blog.eset.com/2012/03/28/osxlamadai-a-the-mac-payload
[4] http://blog.eset.com/2012/04/25/osx-lamadai-flashback-isnt-the-only-mac-threat
[5] http://www.opensource.apple.com/source/dyld/dyld-195.6/include/mach-o/dyld-interposing.h
[6] https://developer.apple.com/library/mac/#documentation/Darwin/Reference/Manpages/man1/dyld.1.html
[7] http://www.oracle.com/technetwork/topics/security/javacpufeb2012-366318.html
[8] http://support.apple.com/kb/DL1517