
Skip ‑ 2.0 du groupe Winnti: une porte dérobée Microsoft SQL Server – Bien choisir son serveur d impression
Un groupe de cyberespionnage notoire rabaisse MSSQL
Les chercheurs d'ESET suivent depuis un certain temps les activités du groupe Winnti, actif depuis au moins 2012 et responsable d'attaques de grande envergure de la chaîne logistique contre l'industrie du jeu vidéo et des logiciels. Récemment, nous avons découvert une porte dérobée non documentée ciblant Microsoft SQL (MSSQL) qui permettait aux attaquants de conserver un pied très discret au sein d’organisations compromises. Cette porte dérobée présente de nombreuses similitudes avec le PortReuse backdoor, autre outil utilisé par le groupe Winnti et documenté pour la première fois par ESET en octobre 2019, tel que l'utilisation du même programme de personnalisation et du même programme de lancement VMProtected. C'est pourquoi nous attribuons cette porte dérobée au groupe Winnti.
Plus tôt cette année, nous avons reçu un échantillon de cette nouvelle porte dérobée appelée skip-2.0 par ses auteurs et une partie de l’arsenal du groupe Winnti. Cette porte dérobée cible MSSQL Server 11 et 12, permettant à l’attaquant de se connecter furtivement à n’importe quel compte MSSQL à l’aide d’un mot de passe magique, tout en masquant automatiquement ces connexions dans les journaux. Une telle porte dérobée pourrait permettre à un attaquant de copier, modifier ou supprimer furtivement le contenu de la base de données. Cela pourrait être utilisé, par exemple, pour manipuler des devises dans le jeu pour un gain financier. Des manipulations de bases de données de devises dans le jeu par des opérateurs de Winnti ont déjà été signalées. Au meilleur de notre connaissance, skip-2.0 est la première porte dérobée du serveur MSSQL à être documentée publiquement. Notez que, même si MSSQL Server 11 et 12 ne sont pas les versions les plus récentes (publiées en 2012 et 2014, respectivement), ce sont les plus couramment utilisées, selon les données de Censys.
Nous avons récemment publié un livre blanc mettant à jour notre compréhension de l’arsenal du groupe Winnti et exposant une de leurs portes secrètes, jusque-là non documentée. PortReuse. Il utilise un packer identique à celui utilisé avec la charge intégrée dans les jeux vidéo compromis découverts par ESET en mars 2019. Le lanceur VMProtected qui laisse tomber le PortReuse porte dérobée a également été trouvée en train d’être utilisée pour lancer ShadowPad versions. Dans ce contexte, nous avons pu trouver un nouvel outil appelé skip.2-0 par son développeur. Il utilise le même lanceur VMProtected ainsi que l’emballeur personnalisé du groupe Winnti et présente de nombreuses similitudes avec d’autres échantillons des outils du groupe Winnti. Cela nous amène à attribuer skip-2.0 à cet ensemble d'outils également.
Cet article porte sur les détails techniques et les fonctionnalités de cette porte dérobée MSSQL Server, ainsi que sur les similitudes techniques entre skip.2-0 l’arsenal connu du groupe Winnti – en particulier avec le PortReuse porte dérobée et ShadowPad. Vous trouverez une note sur les raisons pour lesquelles nous avons choisi la dénomination «Groupe Winnti» sur notre livre blanc.
Sommaire
Lanceur VMProtected
Nous avons trouvé skip-2.0 recherche de lanceurs VMProtected, pour lesquels la charge utile est généralement soit PortReuse ou ShadowPad.
Charge utile intégrée
Comme avec le crypté PortReuse et ShadowPad charges utiles, sautez-2.0 est intégré dans la superposition du lanceur VMProtected, comme illustré dans la figure 1:

Figure 1. En-têtes du lanceur VMProtected. La charge utile est intégrée à la superposition PE.
Cryptage
Le chiffrement de la charge utile est identique à celui utilisé dans les autres lanceurs VMProtected. Il est crypté avec RC5 avec une clé dérivée du VolumeID et la ficelle f @ Ukd! rCto R $. – comme décrit dans notre précédent livre blanc sur l'arsenal du groupe Winnti.
Persistance
Comme dans le cas de PortReuse et ShadowPad, le lanceur persiste probablement en exploitant une vulnérabilité de piratage de DLL en étant installé à C: Windows System32 TSVIPSrv.DLL. Cela entraîne le chargement de la DLL par le Windows standard SessionEnv service au démarrage du système.
Emballeur personnalisé du groupe Winnti
Une fois déchiffré, la charge intégrée est en fait l’emballeur personnalisé de Winnti Group. Ce packer est le même shellcode que celui décrit dans notre précédent article et notre livre blanc. Il est utilisé pour emballer le PortReuse porte dérobée ainsi que la charge utile intégrée aux jeux vidéo compromis.
Configuration du packer
Comme décrit dans notre article précédent, la configuration du programme de compression contient la clé de déchiffrement du fichier binaire compressé, ainsi que son nom de fichier d'origine, sa taille et le type d'exécution (EXE ou DLL). La configuration de l’emballeur de la charge utile est indiquée dans le tableau 1.
Parent SHA-1 | Charge utile SHA-1 | Clé RC4 | Nom de fichier | Type de lancement |
---|---|---|---|---|
9aafe81d07b3e5bb282608f0a2a4656eb485b7c9 | a2571946ab181657eb825cde07188e8bcd689575 | 163716559 | Inner-Loader.dll | 2 |
Tableau 1. Configuration du packer de Payload
La configuration de l’emballeur indique que la charge utile est appelée Chargeur interne. Chargeur interne est le nom d’un injecteur qui fait partie de l’arsenal du groupe Winnti utilisé pour injecter le PortReuse porte dérobée dans les processus d'écoute sur un port particulier, comme décrit dans notre publication précédente. Au-delà de ce nom identique, il apparaît en analysant cette charge utile qu’il s’agit d’une autre variante du Chargeur interne injecteur.
Injecteur à chargement interne
Cette variante de Chargeur interne, au lieu de rechercher un processus d’écoute sur un port particulier, comme dans le cas de l’injection du PortReuse porte dérobée, recherche un processus appelé sqlserv.exe, qui est le nom de processus conventionnel de MSSQL Server. Si trouvé, Chargeur interne puis injecte une charge utile dans ce processus. Cette charge est également fournie avec l’emballeur personnalisé. La configuration de l’emballeur de cette charge est indiquée dans le tableau 2.
Parent SHA-1 | Charge utile SHA-1 | Clé RC4 | Nom de fichier | Type de lancement |
---|---|---|---|---|
a2571946ab181657eb825cde07188e8bcd689575 |
60b9428d00be5ce562ff3d888441220290a6dac7 | 923567961 | skip-2.0.dll | 2 |
Tableau 2. Configuration du packer de la charge utile intégrée à Inner-Loader
Le nom de fichier original de cette charge utile injectée est skip-2.0.dll.
skip-2.0
Après avoir été injecté et lancé par Chargeur interne, sauter-2.0 vérifie d’abord si elle s’exécute dans un sqlserv.exe processus et si oui, récupère une poignée pour sqllang.dll, qui est chargé par sqlserv.exe. Il recherche ensuite et accroche plusieurs fonctions à partir de cette DLL. La figure 2 illustre le skip-2.0 chaîne de compromis.

Figure 2. Déballage et injection skip-2.0
Accrochage sqllang.dll
La procédure d’accrochage utilisée par skip-2.0 est très similaire à celle utilisée par NetAgent, le PortReuse module responsable de l’installation du hook réseau. Cette bibliothèque de hook est basée sur le désassembleur Open Source distorm utilisé par plusieurs infrastructures de hook Open Source. En particulier, une bibliothèque de désassemblage est nécessaire pour calculer correctement la taille des instructions à accrocher. On peut voir sur la figure 3 que la procédure d’accrochage utilisée par NetAgent et skip-2.0 sont presque identiques.

Figure 3. Comparaison de la production de rayons Hex-Ray entre les procédures de raccordement NetAgent (à gauche) et skip-2.0 (à droite)
Il existe une différence notable, à savoir le fait que la fonction d’accrochage de skip-2.0 prend l'adresse du crochet à installer comme argument, alors que pour NetAgent, l'adresse du crochet à installer est codée en dur. Ceci est dû au fait que skip-2.0 doit accrocher plusieurs fonctions dans sqllang.dll pour fonctionner correctement, tout en NetAgent ne cible qu'une seule fonction.
Pour localiser chacun sqllang.dll fonction à accrocher, skip-2.0 récupère d’abord la taille de la DLL une fois chargée en mémoire (c’est-à-dire sa taille virtuelle) en analysant ses en-têtes PE. Ensuite, un tableau d'octets à faire correspondre dans sqllang.dll est initialisé comme indiqué dans la figure 4. Une fois que l'adresse de la première occurrence correspondant au tableau d'octets est trouvée, le hook est installé à l'aide de la procédure indiquée dans la figure 3.

Figure 4. Sortie Hex-Rays de la procédure initialisant le tableau d'octets pour correspondre à sqllang.dll
La réussite de l’installation du hook est ensuite consignée en cleartext dans un fichier journal situé sur le chemin codé en dur. C: Windows Temp TS_2CE1.tmp et montré à la figure 5.

Figure 5. Journal généré lors de l'installation des points d'ancrage
Si la fonction ciblée n'est pas trouvée, le programme d'installation de points d'ancrage recherche une fonction de secours, avec un ensemble différent de motifs d'octet.
Faire correspondre une séquence d'octets pour localiser l'adresse de la fonction ciblée au lieu d'utiliser un décalage statique, plus d'utiliser une séquence d'octets de secours, permet skip-2.0 être plus résilient aux mises à jour MSSQL et potentiellement cibler plusieurs sqllang.dll mises à jour.
Un mot de passe pour les gouverner tous
Les fonctions visées par skip-2.0 sont liés à l'authentification et à la journalisation des événements. Les fonctions visées incluent:
- CPwdPolicyManager :: ValidatePwdForLogin
- CSECAuthenticate :: AuthenticateLoginIdentity
- ReportLoginSuccess
- IssueLoginSuccessReport
- FExecuteLogonTriggers
- XeSqlPkg :: sql_statement_completed :: Publish
- XeSqlPkg :: sql_batch_completed :: Publish
- SecAuditPkg :: audit_event :: Publish
- XeSqlPkg :: login :: Publier
- XeSqlPkg :: ual_instrument_called :: Publish
La fonction la plus intéressante est la première (CPwdPolicyManager :: ValidatePwdForLogin), qui est responsable de la validation du mot de passe fourni pour un utilisateur donné. Le crochet de cette fonction vérifie si le mot de passe fourni par l’utilisateur correspond au mot de passe magique; Si tel est le cas, la fonction d'origine ne sera pas appelée et le hook renverra 0, permettant ainsi la connexion même si le mot de passe correct n'a pas été fourni. Un indicateur global est ensuite défini et sera vérifié par les autres fonctions raccordées responsables de la journalisation des événements. La procédure décompilée correspondante est illustrée à la figure 6. Dans le cas où cet indicateur global est défini, les fonctions de consignation raccordées retourneront en mode silencieux sans appeler leurs fonctions d'origine correspondantes, de sorte que l'action ne sera pas consignée. Dans le cas où un mot de passe différent est fourni, la fonction d'origine est appelée.

Figure 6. Sortie Hex-Rays de la procédure chargée de la correspondance du mot de passe fourni lors de la connexion avec la chaîne codée en dur
Une technique de backdooring similaire, basée sur des mots de passe codés en dur, a été utilisée avec les backdoors SSH précédemment découverts par ESET. La différence ici est que skip-2.0 est installé en mémoire, tandis que dans le cas des portes dérobées SSH, la sshd l'exécutable a été modifié avant l'exécution.
Aditionellement, CSECAuthenticate :: AuthenticateLoginIdentity sera appelé à partir de son code de crochet mais le crochet retournera toujours 0. Le ReportLoginSucess et IssueLoginSuccessReport Les hooks n'appelleront pas les fonctions d'origine si le mot de passe magique a été utilisé pour se connecter. Le même comportement est appliqué à FEExecuteLogonTriggers. Autres fonctions de journalisation telles que XeSqlPkg :: sql_statement_completed :: Publish ou XeSqlPkg :: sql_batch_completed :: Publish sera également désactivé dans le cas où l'utilisateur s'est connecté avec le mot de passe magique. Plusieurs événements d'audit sont également désactivés, notamment: SecAuditPkg :: audit_event :: Publish, XeSqlPkg :: login :: Publier et XeSqlPkg :: ual_instrument_called :: Publish.
Cette série de points d'ancrage permet non seulement à l'attaquant de conserver sa persistance dans le serveur MSSQL de la victime grâce à l'utilisation d'un mot de passe spécifique, mais également de rester non détecté grâce aux multiples mécanismes de publication des journaux et des événements désactivés lors de l'utilisation de ce mot de passe.
Nous avons testé skip-2.0 contre plusieurs versions de MSSQL Server et nous avons constaté que nous pouvions nous connecter avec le mot de passe spécial avec MSSQL Server 11 et 12. Pour vérifier si un sqllang.dll la version est ciblée par skip-2.0 (c’est-à-dire qui correspond aux modèles d’octets), nous avons créé une règle YARA, qui peut être trouvée dans notre référentiel GitHub.
Connexion avec le groupe Winnti
Nous avons observé de multiples similitudes entre skip-2.0 et d’autres outils de l’arsenal du groupe Winnti. Son lanceur VMProtected, emballeur personnalisé, Chargeur interne L'injecteur et le cadre d'accrochage font partie des outils déjà connus du groupe Winnti. Cela nous amène à penser que skip-2.0 fait également partie de cet ensemble d'outils.
Conclusion
le skip-2.0 backdoor est un ajout intéressant à l’arsenal du groupe Winnti, car il partage de nombreuses similitudes avec les outils déjà connus du groupe et permet à l’attaquant d’obtenir de la persistance sur un serveur MSSQL. Considérant que des privilèges d’administration sont requis pour l’installation des hooks, skip-2.0 doit être utilisé sur des serveurs MSSQL déjà compromis pour assurer la persistance et la furtivité.
Nous continuerons de surveiller les nouvelles activités du groupe Winnti et publierons des informations pertinentes sur notre blog. Pour toute demande de renseignements, contactez-nous à l'adresse suivante :urtintel@eset.com.
Indicateurs de compromis (IoC)
Composant | SHA-1 | Nom de détection ESET |
---|---|---|
Chargeur VMP | 18E4FEB988CB95D71D81E1964AA6280E22361B9F 4AF89296A15C1EA9068A279E05CC4A41B967C956 |
Win64 / Packed.VMProtect.HX |
Injecteur à chargement interne | A2571946AB181657EB825CDE07188E8BCD689575 | Win64 / Injector.BS |
skip-2.0 | 60B9428D00BE5CE562FF3D888441220290A6DAC7 | Win32 / Agent.SOK |
Connu ciblé sqllang.dll fichiers (liste non exhaustive) | 4396D3C904CD340984D474065959E8DD11915444 BE352631E6A6A9D0B7BBA9B82D910FA5AB40C64E D4ADBC3F77ADE63B836FC4D9E5915A3479F09BD4 0BBD3321F93F3DCDD2A332D1F0326142B3F4961A FAE6B48F1D6EDDEC79E62844C444FE3955411EE3 A25B25FFA17E63C6884E28E96B487F58DF4502E7 DE76419331381C390A758E634BF2E165A42D4807 ED08E9B4BA6C4B5A1F26D671AD212AA2FB0874A2 1E1B0D91B37BAEBF77F85D1B7C640B8CC02FE11A 59FB000D36612950FEBC36004F1317F7D000AA0B 661DA36BDD115A1E649F3AAE11AD6F7D6FF2DB63 |
N / A |
Techniques MITRE ATT & CK
Tactique | ID | prénom | La description |
---|---|---|---|
Exécution | T1035 | Exécution du service | skip-2.0 est commencé avec le SessionEnv un service |
Persistance | T1038 | Détournement d'ordre de recherche de DLL | skip-2.0 utilise probablement une technique de piratage de DLL contre le SessionEnv un service |
T1179 | Crochet | skip-2.0 accroche plusieurs fonctions dans sqllang.dll service pour contourner l'authentification et rester furtif | |
Défense Evasion | T1054 | Blocage des indicateurs | skip-2.0 bloque la journalisation des événements |
T1045 | Emballage logiciel | skip.2-0 et Chargeur interne sont emballés à l'aide de l'emballage personnalisé de Winnti. De plus, le lanceur est VMProtected. | |
Découverte | T1057 | Découverte de processus | Chargeur interne liste les processus en cours afin de trouver le processus exécutant MSSQL Server |
Impact | T1485 | Destruction de données | skip-2.0 permet l'accès non autorisé aux bases de données MSSQL, permettant ainsi la destruction ou la falsification des données |
T1494 | Manipulation des données d'exécution | skip-2.0 manipule la journalisation des événements au moment de l'exécution | |
T1492 | Manipulation de données stockées | skip-2.0 permet un accès non autorisé aux bases de données MSSQL, permettant la manipulation des données stockées |
Mathieu Tartare
Commentaires
Laisser un commentaire