RotaJakiro: A long live secret backdoor with 0 VT detection
Overview
On March 25, 2021, 360 NETLAB's BotMon system flagged a suspiciousELF file (MD5=64f6cfe44ba08b0babdd3904233c4857) with 0 VT detection, the sample communicates with 4 domains on TCP 443 (HTTPS), but the traffic is not of TLS/SSL. A close look at the sample revealed it to be a backdoor targeting Linux X64 systems, a family that has been around for at least 3 years.
We named it RotaJakiro based on the fact that the family uses rotate encryption and behaves differently for root/non-root accounts
when executing.
RotaJakiro
pays quite some attention to hide its trails, using multiple of encryption algorithms, including: the use of AES algorithm to encrypt the resource information within the sample; C2 communication using a combination of AES, XOR, ROTATE encryption
and ZLIB compression
.
RotaJakiro
supports a total of 12 functions, three of which are related to the execution of specific Plugins. Unfortunately, we have no visibilityto the plugins, and therefore do not know its true purpose. From a broad backdoor perspective, the functions can be grouped into the following four categories.
- Reporting device information
- Stealing sensitive information
- File/Plugin management (query, download, delete)
- Execution of specific Plugin
Any more out there?
With the sample we have, we discovered the following 4 samples, all of which have 0 detections on VT, and the earliest First Seen time on VT is in 2018.
FileName | MD5 | Detection | First Seen in VT |
---|---|---|---|
systemd-daemon | 1d45cd2c1283f927940c099b8fab593b | 0/61 | 2018-05-16 04:22:59 |
systemd-daemon | 11ad1e9b74b144d564825d65d7fb37d6 | 0/58 | 2018-12-25 08:02:05 |
systemd-daemon | 5c0f375e92f551e8f2321b141c15c48f | 0/56 | 2020-05-08 05:50:06 |
gvfsd-helper | 64f6cfe44ba08b0babdd3904233c4857 | 0/61 | 2021-01-18 13:13:19 |
These samples all have the following 4 C2s embedded. These 4 C2 domains have very close Crteated,Updated and Expired
time, readers will notice that the crated data was in Dec 2015, 6 years ago.
Domain | Detection | Created | Last Updated | Expired |
---|---|---|---|---|
news.thaprior.net | 0/83 | 2015-12-09 06:24:13 | 2020-12-03 07:24:33 | 2021-12-09 06:24:13 |
blog.eduelects.com | 0/83 | 2015-12-10 13:12:52 | 2020-12-03 07:24:33 | 2021-12-10 13:12:52 |
cdn.mirror-codes.net | 0/83 | 2015-12-09 06:24:19 | 2020-12-03 07:24:32 | 2021-12-09 06:24:19 |
status.sublineover.net | 0/83 | 2015-12-09 06:24:24 | 2020-12-03 07:24:32 | 2021-12-09 06:24:24 |
Reverse Analysis
The 4 RotaJakiro samples, with time distribution from 2018 to 2021, are very close to their functions, and the 2021 sample is selected for analysis in this blog, which has the following basic information:
MD5:64f6cfe44ba08b0babdd3904233c4857
ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, stripped
Packer:No
At the coding level
, RotaJakiro uses techniques such as dynamic AES, double-layer encrypted communication protocols to counteract the binary & network traffic analysis.
At the functional level
, RotaJakiro first determines whether the user is root or non-root at run time, with different execution policies for different accounts, then decrypts the relevant sensitive resources using AES& ROTATE for subsequent persistence
, process guarding
and single instance
use, and finally establishes communication with C2 and waits for the execution of commands issued by C2.
The following will analyze the specific implementation of RotaJakiro from the above perspective.
0x00: Tricks used by the sample
- Dynamically generate a table of constants required by the AES encryption algorithm to prevent the algorithm from being directly identified
- Use stack strings obfuscation technique to store encrypted sensitive resource information
- Network communication using double layer encryption
0x01: Encryption algorithm
All sensitive resources in RotaJakiro are encrypted, and in IDA we can see that the decryption method dec_proc is called 60 times, which is composed of AES and Rotate.
The AES decryption entry is as follows:
Where aes_dec
is AES-256, CBC mode, key&iv are hardcoded.
- KEY
14 BA EE 23 8F 72 1A A6 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
- IV
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
The Rotate decryption entry is shown below:
The so-called Rotate is a cyclic shift, we can see that the number of shifts is determined by the value of plain_len(length of plaintext) &7
.
Take the following C2 cipher text as an example.
ff ba a2 3b cd 5b 7b 24 8c 5f e3 4b fc 56 5b 99
ac 91 cf e3 9a 27 d4 c9 6b 39 34 ce 69 ce 18 60
The various parameters related to decryption are shown below, the length of the ciphertext is 32 bytes and the length of the plaintext is 26 bytes.
First, decrypting with AES, we get the following "sub-ciphertext".
Then, the valid ciphertext is extracted from the sub-ciphertext, where the valid ciphertext starts from the 8th byte, and the length is the plaintext length minus 8, which is 26-8=18 bytes here.
98 1B DB D9 8B 59 19 5D 59 1B 59 D8 1D DC 8B D8
DB 5B
Finally, we can calculate 26(the length of plaintext is 26)&7=2, and get the number of shifts, and shift the above valid ciphertext byte by byte by 2 bits to get C2 plaintext.
blog.eduelects.com
0x02: Persistence
RotaJakiro makes a distinction between root/non-root
users when implementing persistence features, and different techniques are used for different accounts.
root account
-
Depending on the Linux distribution, create the corresponding self-starting script
/etc/init/systemd-agent.conf
or/lib/systemd/system/sys-temd-agent.service
.Content of systemd-agent.conf ----------------------------- #system-daemon - configure for system daemon #This service causes system have an associated #kernel object to be started on boot. description "system daemon" start on filesystem or runlevel [2345] exec /bin/systemd/systemd-daemon respawn
Content of systemd-agent.service ----------------------------- [Unit] Description=System Daemon Wants=network-online.target After=network-online.target [Service] ExecStart=/usr/lib/systemd/systemd-daemon Restart=always [Install]
-
The file name used for the disguise is one of the following twos.
/bin/systemd/systemd-daemon /usr/lib/systemd/systemd-daemon
non-root account
- Create autostart script
$HOME/.config/au-tostart/gnomehelper.desktop
for desktop environment[Desktop Entry] Type=Application Exec=$HOME/.gvfsd/.profile/gvfsd-helper
- Modify the
.bashrc
file to create the autostart script for the shell environment# Add GNOME's helper designed to work with the I/O abstraction of GIO # this environment variable is set, gvfsd will not start the fuse filesystem if [ -d ${HOME} ]; then ${HOME}/.gvfsd/.profile/gvfsd-helper fi
- The file name used for the disguise, both of which exist at the same time
$HOME/.dbus/sessions/session-dbus $HOME/.gvfsd/.profile/gvfsd-helper
0x03:Process guarding
RotaJakiro implements process guarding to protect its own operation, and like persistence, there are different implementations for root/non-root
users.
root account
When running under the root account, depending on the Linux distribution, a new process is automatically created when the service process is terminated by writing Restart=always or respawn
to the service's configuration file.
The actual result is shown in the figure below, where you can see that a new process is created immediately after the systemd-daemon process is terminated.
non-root account
When running under a non-root account, RotaJakiro generates two processes, session-dbus
and gvfsd-helper
, which monitor each other's survival and restore them when one of them is terminated, which is very typical of dual-process protection.
How is RotaJakiro's dual-process protection implemented?
First, it creates a piece of shared memory with the shmget API
, and session-dbus and gvfsd-helper communicate with each other through this shared memory, telling each other their PIDs.
Then, dynamically fetching the process survival through the /proc/[PID]
directory. When the other process is found dead, the process is created by execvp API to help the dead process "resurrect", as shown in the following diagram.
The actual effect is shown in the figure below, you can see that after session-dbus and gvfsd-helper are ended by kill -9, new processes are created right away.
0x04: Single instance
RotaJakiro implements a single instance by file locking, as shown below.
The lockfile used in this differs under the root/non-root account.
- The lockfile under root, one will be created.
/usr/lib32/.X11/X0-lock /bin/lib32/.X11/X0-lock
- The lockfile under non-root, both will be created.
$HOME/.X11/X0-lock $HOME/.X11/.X11-lock
In the actual non-root account, for example, the processes and file locks can be matched by /proc/locks
, and then the corresponding RotaJakiro sample is executed.
0x05: Network communication
RotaJakiro establishes communication with C2 through the following code snippet, pending the execution of subsequent commands.
This process can be divided into 2 stages
- Stage 1, initialization phase
Decrypt the C2 list, establish a connection with C2, send the online information, receive and decrypt the information returned by C2. - Stage 2, wait for C2 calls
Verify the information returned by C2, if it passes the verification, execute the subsequent instructions sent by C2.
Stage 1: Initialization
The C2 list is decrypted by the decryption algorithm described in the previous section, and the following four C2s are built into the sample at present.
news.thaprior.net
blog.eduelects.com
cdn.mirror-codes.net
status.sublineover.net
RotaJakiro will first try to establish a connection with them, and then construct the golive message by the following code snippet.
Then it encrypts the golive information and sends it to the C2s
Finally, it receives the packet back from the C2, decrypts it and checks its legitimacy, and if it passes the check, it goes to Stage 2.
Stage 2: Specific operations
Receive and execute the command from C2 through the following codesnippet.
At present, RotaJakiro supports a total of 12 instructions, and the correspondence between the instruction code and the function is shown in the following table.
CmdId | Function |
---|---|
0x138E3E6 | Exit |
0x208307A | Test |
0x5CCA727 | Heartbeat |
0x17B1CC4 | Set C2 timeout time |
0x25360EA | Steal Senstive Info |
0x18320e0 | Upload Device Info |
0x2E25992 | Deliver File/Plugin |
0x2CD9070 | Query File/Plugin Status |
0x12B3629 | Delete File/Plugin Or Dir |
0x1B25503 | Run Plugin_0x39C93E |
0x1532E65 | Run Plugin_0x75A7A2 |
0x25D5082 | Run Plugin_0x536D01 |
The Run Plugin function reuses the same code and implements the function call through the following logic. | |
We are currently not capturing such payloads, so we use the Plugin_"parameter" form to represent different tasks. |
0x06 Packet analysis
The network communication packet of RotaJakiro consists of three parts: Head, Key, Payload
.
Head
is mandatory and 82 bytes long, and the Key & Payload
parts are optional.
Head & Key
are encrypted with XOR & Rotate, and Payload
is encrypted with AES & ZLIB Compression.
In the following, we will illustrate the composition of network traffic head&key&payload
and the decryption process through a round of interaction between Bot and C2.
C2 -> Bot
The first 0x52 bytes are the content of the Head. How to decrypt the head? Very simple, shift 3 bits left byte by byte, and then XOR with 0x1b
. After decryption, we can get the following content.
00000000 16 11 10 b9 03 b1 0c fb 04 20 00 00 00 08 00 e0 |...¹.±.û. .....à|
00000010 20 83 01 c2 20 64 20 01 e2 00 00 00 00 c2 0c 00 | .. d .â....Â..|
00000020 00 00 32 42 36 39 33 33 34 46 38 34 31 44 30 44 |..2B69334F841D0D|
00000030 39 46 41 30 36 35 38 45 43 33 45 32 39 46 41 44 |9FA0658EC3E29FAD|
00000040 34 39 c8 53 e6 9c 48 c4 8b 77 24 2e 02 1c 96 d9 |49ÈSæ.HÄ.w$....Ù|
00000050 81 28
------------filed parse------------------
offset 0x09, 4 bytes--->payload length
offset 0x0d, 2 bytes--->body length
offset ox0f, 4 bytes--->cmdid
Through the field parsing, we can know that the length of key is 0x8 bytes, the length of payload is 0x20 bytes, and the instruction code to be executed is 0x18320e0, that is, the report device information
.Reading 8 bytes from offset 0x52 gives the Keyea 9a 1a 18 18 44 26 a0
, and using the same decryption method as head, we get 4c cf cb dbdb 39 2a 1e
, which is used as the AES key to decrypt the Payload.
Reading 32 bytes from offset 0x5a gives us the following Payload.
54 c1 c3 69 00 18 31 e4 a2 5b 10 7f 67 ab d1 4b
b2 7b 3d 3f b3 bc 66 6a 26 f6 f6 b3 f7 2e 66 6d
Using the decrypted key as the AES-256 key, decrypt the above data in CBC mode to get the following content.
3b c7 f8 9b 73 2b d1 04 78 9c e3 60 60 60 d8 df d9 c1 71 56 f7 6f 00 00 13 80 04 28
The 8th byte onwards is ZLIB compressed data, decompressed to get the following content.
08 00 00 00 bf 89 88 08 cd 2d fd 50
------------filed parse------------------
offset 0, 4 bytes--->length
What is the use of the decompressed Payload(bf 89 88 08 cd 2d fd 50
)? It is used as a new AES key to decrypt some sensitive resource information.
For example, when Bot collects device information, one of the information is the current OS distribution, which is implemented by the cat /etc/*release | uniq
command.
root@debian:~# cat /etc/*release | uniq
PRETTY_NAME="Debian GNU/Linux 9 (stretch)"
NAME="Debian GNU/Linux"
VERSION_ID="9"
VERSION="9 (stretch)"
ID=debian
HOME_URL="https://www.debian.org/"
SUPPORT_URL="https://www.debian.org/support"
BUG_REPORT_URL="https://bugs.debian.org/"
The cat /etc/*release | uniq
command is the result of the following cipher text
"cat /etc/*release | uniq" cmd_ciphertxt
---------------------------
74 00 dd 79 e6 1e aa bb 99 81 7e ca d9 21 6b 81
6b d9 9d 14 45 73 6a 1c 61 cc 28 a3 0f 2b 41 5a
6b 33 8c 37 25 89 47 05 44 7e f0 6b 17 70 d8 ca
decrypted with the new AES key and the parameters
in the following figure.
Bot -> C2
When BOT receives C2's "report device information" command, it will send the following data to C2, and you can see that the value of the key part is still ea 9a 1a 18 18 44 26 a0
.
The decrypted key value is 4c cf cb db db 39 2a 1e
. After decrypting and decompressing the payload sent by Bot to C2, we get the following data, which is the various information of the device, including the information obtained by cat /etc/*release | uniq
mentioned before, which verifies that our analysis is correct.
Relationship with the Torii Botnet
The Torii botnet was exposed by Avast on September 20, 2018, and we noticed that there are some the similarities between the twos,for example:
1: String similarity
After decrypting the sensitive resources of RotaJakiro & Torii, we found that they reuse a lot of the same commands.
1:semanage fcontext -a -t bin_t '%s' && restorecon '%s'
2:which semanage
3:cat /etc/*release
4:cat /etc/issue
5:systemctl enable
6:initctl start
...
2: Traffic similarity
In the process of constructing the flow, a large number of constants are used and the construction methods are very close.
3: Functional similarity
From the perspective of reverse engineering, RotaJakiro & Torii share similar styles: the use of encryption algorithms to hide sensitive resources, the implementation of a rather old-school style of persistence,structured network traffic, etc.
We don’t exactly know the answer, but it seems that RotaJakiro and Torii have some connections.
The tip of the iceberg
While this concludes our analysis of RotaJakiro, the real work is far from over, and many questions remain unanswered: "How did RotaJakiro spread, and what was its purpose?" , "Does RotaJakiro have a specific target?”, We would love to know if the community has relevant leads.
Contact us
Readers are always welcomed to reach us on twitter, or email to netlabat[at]360.cn.
IOC
Sample MD5
1d45cd2c1283f927940c099b8fab593b
11ad1e9b74b144d564825d65d7fb37d6
5c0f375e92f551e8f2321b141c15c48f
64f6cfe44ba08b0babdd3904233c4857
C2
news.thaprior.net:443
blog.eduelects.com:443
cdn.mirror-codes.net:443
status.sublineover.net:443
IP
176.107.176.16 Ukraine|Kiev|Unknown 42331|PE_Freehost