Mirai variant leveraging CVE-2023-1389
Introduction
On Feb 18 2024
, our systems logged an activity from an endpoint on the internet trying to the hit the path /cgi-bin/luci
Initial research on the internet quickly gave hints that this was an attempt to exploit CVE-2023-1389 , an unauthenticated remote code execution on TP-Link archer routers.
The payload triggers on the victim router the download of a script http://45.142.214.108/tenda.sh
The script tenda.sh
tries to fetch a variety of static binaries, compiled for various architectures, before trying to run them with the argument tplink
Judging by the naming convention of the aggressor, it is tempting to assume Russian-language proficiency due to the keyword blyat
.
This mode of operation is generally associated with mirai
based IoT botnets. This particular variant seems to have been augmented to exploit CVE-2023-1389
on internet facing devices.
Analysis of static binary.
The x86_64
compatible binary was downloaded for analysis.
Dynamic analysis
To guide our static analysis, the binary was first run with strace
to have a global understanding of which syscalls it invokes, and thus what it attempts to do initially on infected systems.
Upon detonation, the binary greets us with a friendly message.
However, strace logs are more explicit.
root@stracerev:~# cat strace.log
execve("./faggot", ["./faggot"], 0x7ffea8865950 /* 15 vars */) = 0
mmap(0x200000, 1048576, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, 0, 0) = 0x200000
readlink("/proc/self/exe", "/root/faggot", 4096) = 12
mmap(0x400000, 1200128, PROT_NONE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x400000
mmap(0x400000, 69680, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x400000
mprotect(0x400000, 69680, PROT_READ|PROT_EXEC) = 0
mmap(0x511000, 1264, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0x11000) = 0x511000
mprotect(0x511000, 1264, PROT_READ|PROT_WRITE) = 0
mmap(0x512000, 74952, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x512000
munmap(0x101000, 1180904) = 0
ioctl(0, TCGETS, {c_iflag=ICRNL|IXON|IXOFF|IUTF8, c_oflag=NL0|CR0|TAB0|BS0|VT0|FF0|OPOST|ONLCR, c_cflag=B38400|CS8|CREAD|HUPCL, c_lflag=ISIG|ICANON|ECHO|ECHOE|ECHOK|ECHOCTL|ECHOKE, ...}) = 0
ioctl(1, TCGETS, {c_iflag=ICRNL|IXON|IXOFF|IUTF8, c_oflag=NL0|CR0|TAB0|BS0|VT0|FF0|OPOST|ONLCR, c_cflag=B38400|CS8|CREAD|HUPCL, c_lflag=ISIG|ICANON|ECHO|ECHOE|ECHOK|ECHOCTL|ECHOKE, ...}) = 0
unlink("./faggot") = 0
rt_sigprocmask(SIG_BLOCK, [INT], NULL, 8) = 0
rt_sigaction(SIGCHLD, {sa_handler=SIG_IGN, sa_mask=[CHLD], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x40c648}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
rt_sigaction(SIGTRAP, {sa_handler=0x4061d0, sa_mask=[TRAP], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x40c648}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
open("/dev/watchdog", O_RDWR) = -1 ENOENT (No such file or directory)
open("/dev/misc/watchdog", O_RDWR) = -1 ENOENT (No such file or directory)
chdir("/") = 0
socket(AF_INET, SOCK_DGRAM, IPPROTO_IP) = 3
connect(3, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("8.8.8.8")}, 16) = 0
getsockname(3, {sa_family=AF_INET, sin_port=htons(41386), sin_addr=inet_addr("20.0.0.9")}, [16]) = 0
close(3) = 0
brk(NULL) = 0x167e000
brk(0x167f000) = 0x167f000
socket(AF_INET, SOCK_STREAM, IPPROTO_IP) = 3
setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
fcntl(3, F_GETFL) = 0x2 (flags O_RDWR)
fcntl(3, F_SETFL, O_RDWR|O_NONBLOCK) = 0
bind(3, {sa_family=AF_INET, sin_port=htons(43213), sin_addr=inet_addr("127.0.0.1")}, 16) = 0
listen(3, 1) = 0
time(NULL) = 1708270866 (2024-02-18T15:41:06+0000)
getpid() = 645
getppid() = 642
times({tms_utime=0, tms_stime=0, tms_cutime=0, tms_cstime=0}) = 1730067285
prctl(PR_SET_NAME, "suh7eqbodkb74e1"...) = 0
write(1, "faggot got malware'd", 20) = 20
write(1, "\n", 1) = 1
fork() = 646
exit(0) = ?
+++ exited with 0 +++
root@stracerev:~#
Immediately after detonation, the binary sample
unlinks
itself from the filesystem, (line 14 of strace logs)From lines
18, 19
of strace log, the sample tries to open/dev/watchdog
in READ & WRITE mode, suggesting that it intends to write to/dev/watchdog
.In normal circumstances, periodic writes to
/dev/watchdog
is the task of the watchdog daemon, and this is done to prevent the kernel from resetting .This is one more known procedures of mirai based botnets, to prevent their infected routers from rebooting because the malware primarily resides in memory.
Lines
23 - 24
show that the sample tries to make a reverse IP look to8.8.8.8
with ip address of the host system.Its last actions are to open a bind socket listening on
127.0.0.1:43213
, and fork itself, probably to continue listening in the background.
Static analysis
- Hexdump of the file shows from first few bytes that it is UPX encoded.
- So the binary is decoded first
Following the unpacking, a few familiar strings appear
They are primarily mirai’s
M-SEARCH
ssdp flood attack,/dev/watchdog
paths opened during detonation, and the perculiar debugging sentence of the author.
Counter-measures
Based on the strings gathered from the unpacked binary, the following rule was defined for detection.
rule MAL_MIRAI_x86_64_LINUX_FEB_18_2024 {
meta:
description = "Mirai x86_64 yara rule"
author = "Permafr0st security"
md5 = "d476e0c2a8e4e4d90f2eaa15c36d8a90"
sha256 = "5d37a4c89f2e567807e2033f8c8e9cfdb75ee6ec426d58ffd930e7fdbe066157"
strings:
$str1 = "M-SEARCH * HTTP/1.1"
$str2 = "ST: urn:dial-multiscreen-org:service:dial:1"
$str3 = "USER-AGENT: Google Chrome/60.0.3112.90 Windows"
$str4 = "service:service-agent"
$str5 = "/dev/watchdog"
$str6 = "/dev/misc/watchdog"
$str7 = "got malware'd"
$str8 = "/usr/sbin/tcpdump"
$str9 = "/usr/sbin/tshark"
$str10 = "/usr/sbin/wireshark"
$str11 = "/usr/sbin/dumpcap"
$str12 = "/usr/sbin/ettercap"
$str13 = "/usr/sbin/dsniff"
$str14 = "/usr/sbin/ngrep"
$str15 = "/usr/sbin/tcpflow"
$str16 = "/usr/sbin/windump"
$str17 = "/usr/sbin/netsniff-ng"
$str18 = "/usr/bin/tcpdump"
$str19 = "/usr/bin/tshark"
$str20 = "/usr/bin/wireshark"
$str21 = "/usr/bin/dumpcap"
$str22 = "/usr/bin/ettercap"
$str23 = "/usr/bin/dsniff"
$str24 = "/usr/bin/ngrep"
$str25 = "/usr/bin/tcpflow"
$str26 = "/usr/bin/windump"
$str27 = "/usr/bin/netsniff-ng"
condition:
( 4 of them ) and $str7
}