RotaJakiro: A long live secret backdoor with 0 VT detection
Share this

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 & Keyare 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