REVERSE ENGINEERING

Inside Symbiote: Malware Deep Dive

Symbiosis is a close and long-term biological interaction between two different biological organisms, either mutual or parasitic. In this case, this userland-level rootkit is named "Symbiote". Due to its parasitic behaviour in the Linux kernel ~ infecting every active process ~ I could say that the name is a nice fit.

It was first discovered in June 2022 by Joakim Kennedy, Security Researcher at Intezer, and the BlackBerry Threat Research & Intelligence team. What makes rootkits interesting is how they behave stealthily when an administrator starts a packet-capturing tool. It's essentially hooking pcap_loop() and pcap_stats() functions to have control over captured packets and modify the filtered packet count. Detailed explanation is present in the following sections.

Today, we are going to deep dive into one of the first samples:

Hash

File Name

ec67bbdf55d3679fca72d3c814186ff4646dd779a862999c82c6faa8e6615180

search.so


How does it infect in the first place?

Unlike other executable malware, this is a Linux shared library (Shared Object) that can be loaded into processes. `LD_PRELOAD` is a Linux environment variable where you can specify a shared library to be loaded before all others. Symbiote uses this variable to import itself into binaries before they even get executed and override existing functions. Exported functions are shown in the image below:

Overview

This sample seems to be:

  • Hiding itself from the process, directory and the ldd list.

  • Filtering malicious network packets from specific ports and domains.

  • Resolving DNS and communicating with the C2 server.

  • Stealing passwords from the PAM authentication hook.

  • Allows PAM authentication with a specific password.

  • Downloading and executing a backdoor script from the C2 server.

  • Implementing a keylogger.

Linker Stealth

As shown below, the malware hooks the `execve` function and checks if the hijacked process has `LD_TRACE_LOADED_OBJECTS` environment enabled. If set, it causes the program to list its dynamic dependencies, as if run by ldd(1), instead of running normally. So Symbiote prevents this by providing a fake tracing function. The original `execve` function is preserved when this option is disabled.

Commonly, it uses the RC4 encryption algorithm to hide hard-coded strings.


Directory/Process List Stealth

As notated in the readdir() documentation, it is used to get the pointer to a `dirent` structure representing the *next* directory entry in the directory. It first checks if the directory entry is coming from a process or a file. Then iterate through entries until it's not in the hidden list. Simply skipping over hidden entries and providing the next valid one instead.

Looking at the `hidden_proc` function, it inspects the file name if it is similar to a command line process file (`/proc/PID/cmdline`). This read-only file holds the complete command line for the process, unless the process is a zombie.

Reading its content, it looks for a string related to strace, a command to intercept and record system calls, which reveals the malicious library operations. Then, going through the list of blacklisted paths, effectively skipping over the process in this case.

Additionally, applies the same check with the `/proc/PID/status` file that holds memory usage and status information.

Blacklisted processes are:

  • certbotx64

  • certbotx86

  • javautils

Unlike `hidden_proc`, `hidden_file` is very straightforward. There is a total of 6 entries.

  • apache2start

  • apache2stop

  • profiles.php

  • 404erro.php

  • javaserverx64

  • javaclientex64


PCAP Hook and Packet filtering

PCAP Loop is a special function that is called from packet capturing tools and as shown below, the malware hooks it by replacing it with its own loop function.

If we take a look at the check packet function, it's filtering packets based on some conditions, as shown below.

If all these conditions are met, check the domain `bancodobrasil.dev` in the packet content bytes. If so, effectively dropping the packet and not letting the original loop handle it. This way, the malware succeeds to communicate with the C2 server without any detections.

Along with the `pcap_loop`, it hooks the `pcap_stats` function to hide the *packet count* as well. After calling the original function, it subtracts the filtered packet count and returns the valid value.

PAM Authenticate Hook

The malware stealthily hooks the pam_authenticate() function that is used for authentication across important services like SSH (Secure Shell). If the password is `suporte42cbb32`, allowing the service to authenticate successfully as shown below. This gives a hidden access to the malicious actor.

If the password is something else, the malware simply saves passwords to the file `/usr/include/certbot.h` and sends it to the C2 server through DNS.


Bash Script Backdoor

From the previous screenshot, you can see the `check_backdoor()` function. Let's have a closer look at it.

Firstly, it's forking the process (see fork()). And if there is a currently forked process, it just waits for it to finish with `waitpid(pid, 0LL, 0)`. Otherwise, create a new fork again. We can also see that it is pointing all available file descriptors to `/dev/null`. This is to prevent exposing the command output to the user ~ Main file descriptors are `STDIN`, `STDOUT` and `STDERR`.

After setting everything up, it executes the script from the server and terminates.

While downloading the script, it uses `ID[.]dev21[.]bancodobrasil[.]dev` format as the domain and again `suporte42bb32` for the RC4 decryption key.

For every packet chunk, it modifies the subdomain with such a format `NUMBER[.]ID[.]dev21[.]bancodobrasil[.]dev`.

After getting the text and decoding it with Base64, it validates the content using the public key ~ determining whether it is coming from a valid C2 server or not.

Finally, forking the process again and closing file descriptors, followed by the bash shell spawn. It interacts with the shell throughthe `STDIN` file descriptor, afterwards, directly writing the command into the shell. Then terminates itself.


Keylogger

To collect keys, it seems to hook the read() function and only call the `keylogger()` function when the hook mode is `2`.

As shown below, if the read() request comes from services like SCP (Secure Copy) or SSH (Secure Shell), it returns 2, which triggers the keylogger function.

In the keylogger function, it first checks if the current process has a terminal interface with the isatty() function. And then copying the buffer from the original read() input.

Finally, setting up the log format and storing the input buffer along with the command line logs to the same file `/usr/include/certbot.h`. As usual, the updated log file is sent through the DNS again.

When it is done storing credentials, it cleans up leftover buffers and disables the hook to avoid duplicate data.


Conclusion

Symbiote is one of the most evasive Linux malware. It's best to act on improving security and the detection of such malware before it infects critical infrastructures.

References


Author: Bilgehan Afşar

Symbiosis is a close and long-term biological interaction between two different biological organisms, either mutual or parasitic. In this case, this userland-level rootkit is named "Symbiote". Due to its parasitic behaviour in the Linux kernel ~ infecting every active process ~ I could say that the name is a nice fit.

It was first discovered in June 2022 by Joakim Kennedy, Security Researcher at Intezer, and the BlackBerry Threat Research & Intelligence team. What makes rootkits interesting is how they behave stealthily when an administrator starts a packet-capturing tool. It's essentially hooking pcap_loop() and pcap_stats() functions to have control over captured packets and modify the filtered packet count. Detailed explanation is present in the following sections.

Today, we are going to deep dive into one of the first samples:

Hash

File Name

ec67bbdf55d3679fca72d3c814186ff4646dd779a862999c82c6faa8e6615180

search.so


How does it infect in the first place?

Unlike other executable malware, this is a Linux shared library (Shared Object) that can be loaded into processes. `LD_PRELOAD` is a Linux environment variable where you can specify a shared library to be loaded before all others. Symbiote uses this variable to import itself into binaries before they even get executed and override existing functions. Exported functions are shown in the image below:

Overview

This sample seems to be:

  • Hiding itself from the process, directory and the ldd list.

  • Filtering malicious network packets from specific ports and domains.

  • Resolving DNS and communicating with the C2 server.

  • Stealing passwords from the PAM authentication hook.

  • Allows PAM authentication with a specific password.

  • Downloading and executing a backdoor script from the C2 server.

  • Implementing a keylogger.

Linker Stealth

As shown below, the malware hooks the `execve` function and checks if the hijacked process has `LD_TRACE_LOADED_OBJECTS` environment enabled. If set, it causes the program to list its dynamic dependencies, as if run by ldd(1), instead of running normally. So Symbiote prevents this by providing a fake tracing function. The original `execve` function is preserved when this option is disabled.

Commonly, it uses the RC4 encryption algorithm to hide hard-coded strings.


Directory/Process List Stealth

As notated in the readdir() documentation, it is used to get the pointer to a `dirent` structure representing the *next* directory entry in the directory. It first checks if the directory entry is coming from a process or a file. Then iterate through entries until it's not in the hidden list. Simply skipping over hidden entries and providing the next valid one instead.

Looking at the `hidden_proc` function, it inspects the file name if it is similar to a command line process file (`/proc/PID/cmdline`). This read-only file holds the complete command line for the process, unless the process is a zombie.

Reading its content, it looks for a string related to strace, a command to intercept and record system calls, which reveals the malicious library operations. Then, going through the list of blacklisted paths, effectively skipping over the process in this case.

Additionally, applies the same check with the `/proc/PID/status` file that holds memory usage and status information.

Blacklisted processes are:

  • certbotx64

  • certbotx86

  • javautils

Unlike `hidden_proc`, `hidden_file` is very straightforward. There is a total of 6 entries.

  • apache2start

  • apache2stop

  • profiles.php

  • 404erro.php

  • javaserverx64

  • javaclientex64


PCAP Hook and Packet filtering

PCAP Loop is a special function that is called from packet capturing tools and as shown below, the malware hooks it by replacing it with its own loop function.

If we take a look at the check packet function, it's filtering packets based on some conditions, as shown below.

If all these conditions are met, check the domain `bancodobrasil.dev` in the packet content bytes. If so, effectively dropping the packet and not letting the original loop handle it. This way, the malware succeeds to communicate with the C2 server without any detections.

Along with the `pcap_loop`, it hooks the `pcap_stats` function to hide the *packet count* as well. After calling the original function, it subtracts the filtered packet count and returns the valid value.

PAM Authenticate Hook

The malware stealthily hooks the pam_authenticate() function that is used for authentication across important services like SSH (Secure Shell). If the password is `suporte42cbb32`, allowing the service to authenticate successfully as shown below. This gives a hidden access to the malicious actor.

If the password is something else, the malware simply saves passwords to the file `/usr/include/certbot.h` and sends it to the C2 server through DNS.


Bash Script Backdoor

From the previous screenshot, you can see the `check_backdoor()` function. Let's have a closer look at it.

Firstly, it's forking the process (see fork()). And if there is a currently forked process, it just waits for it to finish with `waitpid(pid, 0LL, 0)`. Otherwise, create a new fork again. We can also see that it is pointing all available file descriptors to `/dev/null`. This is to prevent exposing the command output to the user ~ Main file descriptors are `STDIN`, `STDOUT` and `STDERR`.

After setting everything up, it executes the script from the server and terminates.

While downloading the script, it uses `ID[.]dev21[.]bancodobrasil[.]dev` format as the domain and again `suporte42bb32` for the RC4 decryption key.

For every packet chunk, it modifies the subdomain with such a format `NUMBER[.]ID[.]dev21[.]bancodobrasil[.]dev`.

After getting the text and decoding it with Base64, it validates the content using the public key ~ determining whether it is coming from a valid C2 server or not.

Finally, forking the process again and closing file descriptors, followed by the bash shell spawn. It interacts with the shell throughthe `STDIN` file descriptor, afterwards, directly writing the command into the shell. Then terminates itself.


Keylogger

To collect keys, it seems to hook the read() function and only call the `keylogger()` function when the hook mode is `2`.

As shown below, if the read() request comes from services like SCP (Secure Copy) or SSH (Secure Shell), it returns 2, which triggers the keylogger function.

In the keylogger function, it first checks if the current process has a terminal interface with the isatty() function. And then copying the buffer from the original read() input.

Finally, setting up the log format and storing the input buffer along with the command line logs to the same file `/usr/include/certbot.h`. As usual, the updated log file is sent through the DNS again.

When it is done storing credentials, it cleans up leftover buffers and disables the hook to avoid duplicate data.


Conclusion

Symbiote is one of the most evasive Linux malware. It's best to act on improving security and the detection of such malware before it infects critical infrastructures.

References


Author: Bilgehan Afşar

zerone

Zero Trust, One Security.


© 2025 Zerone Security, Inc.

Terms of service

Privacy policy

zerone

Zero Trust, One Security.


© 2025 Zerone Security, Inc.

Terms of service

Privacy policy

zerone

Zero Trust, One Security.


© 2025 Zerone Security, Inc.

Terms of service

Privacy policy

zerone

Zero Trust, One Security.


© 2025 Zerone Security, Inc.

Terms of service

Privacy policy