Intrusion detection – how to implement reverse shell detection?

The essence of the rebound shell: the control terminal listens on a certain TCP/UDP port, the controlled terminal initiates a request to this port, and transfers the input and output of its command line to the control terminal. The reverse shell corresponds to standard shells such as telnet and ssh, and is essentially the role reversal of the client and server of the network concept.

The result of the reverse shell: a bash process on the client can communicate with a process on the server.

The detection of the rebound shell is essentially to detect whether the input and output of the shell process (such as bash) comes from a remote server.

1. Detection ideas

1. Process file descriptor abnormal detection

1.1 Check if the file descriptor points to a socket

Take “redirection character” + “/dev/tcp network communication” as an example of the most classic reverse shell attack method of Bash reverse shell, the essence of this type of reverse shell can be summarized as the redirection of file descriptor to a socket handle.

img

img

1.2 Check whether the file descriptor points to a pipe character (pipe)

For the reverse shell attack method that uses the “pipe character” to pass instructions, the essence of this type of reverse shell can be summarized as redirecting the file descriptor to a pipe handle.

img

Furthermore, no matter how many layers of pipe are made, the essence of the reverse shell is to pass the input of the server to the bash of the client, so there must be a socket connection.

We only need to trace the upstream process of the pipe according to the pid, and judge its process fd, and check whether it is from a socket.

For example, if you trace the pipe and find that the process of the pipe has established a socket connection, then there is a risk of rebounding the shell.

2.netlink monitoring + fd anomaly detection

  • Listen to Netlink Socket to get process EXEC events in real time.
  • If it is a Shell process, check the FD opened by the process startup,
    • Opened the Socket
    • Terminals such as /dev/tty, /dev/pts/n, /dev/ptmx are not used
    • Then it is confirmed as a rebound shell

Bypass risk: You can only judge whether it is a shell process by the name of the process execution file, uploading executable files, copying Bash files to other paths, etc. will bypass this method.

For example, reverse shell by renaming /bin/sh to another name.

3. Script file & amp; & amp; application & amp; & amp; fileless (fileless) rebound shell detection

It should be noted that the operating system is layered. Bash is just an ordinary application of an application, which encapsulates the function of calling glibc execve. In addition to bash, white hats can also implement rebound based on any application layer technology shell, for example:

  • Python/perl implements reverse shell file execution in pure code form: file script detection
  • Python/perl realizes the reverse shell command line command (fileless) in the form of pure code: pure command line fileless detection
  • C/C++ implementation of reverse shell in pure code form: binary file detection

4. Feature detection

4.1 Network layer rebound shell communication feature detection

The communication session of the reverse shell will contain some “cmdline shell features”, such as “#root…”, etc., which can perform NTA real-time detection on the network side.

4.2 DNS rebound shell feature detection

Analyze DNS traffic to determine whether the associated process opens /dev/net/tun, or /dev/net/tap tunnel, etc.

4.3 ICMP rebound shell feature detection

The data generated by the normal ping command has the following characteristics:

● The number of data packets sent per second is relatively small, usually at most two data packets are sent per second;

● The content of the request data packet is the same as that of the corresponding response data packet;

● The size of the payload in the data packet is fixed, 32bytes under windows and 48bytes under linux;

● The content of the payload in the data packet is fixed. It is abcdefghijklmnopqrstuvwabcdefghi under Windows, and it is !”#$% & amp;'() + ,-./01234567 under Linux. If the length of ping sending is specified, it is a fixed string that repeats continuously ;

● There are only two types of type, 0 and 8. 0 is the request data, and 8 is the response data.

The data generated by the ICMP tunnel has the following characteristics:

● The number of data packets sent per second is relatively large, and hundreds or thousands of ICMP data packets will be generated at the same time;

● The content of the request packet is different from the corresponding response packet;

● The size of the payload in the data packet can be any size;

● There are some malformed data packets with payloads of type 13/15/17;

● The content of data packets generated by individual ICMP tunneling tools will be preceded by a ‘TUNL’ mark to identify the tunnel.

Therefore, according to the characteristics of data packets generated by normal ping and ICMP tunnels, ICMP tunnels can be detected by the following characteristics:

● Detect the number of packets from the same source. Normal ping will only send 2 packets per second, while ICMP tunnels can send many packets per second;

● Detect the size of the payload in the data packet. The size of the data packet payload generated by normal ping is fixed, while the size of the ICMP tunnel data packet can be arbitrary;

● Detect whether the payload in the response packet is inconsistent with the request packet. The data packet request response content generated by normal ping is consistent, but the ICMP tunnel request response data packet can be consistent or inconsistent;

● Detect the payload content in the data packet. The payload generated by normal ping is a fixed string, and the payload of ICMP tunnel can be any;

● Check whether the type of ICMP data packet is 0 or 8. The data packets with payload generated by normal ping can only have type 0 and 8, and the type of ICMP tunnel can be 13/15/17.

image name

For specific implementation, please refer to https://blog.riskivy.com/ICMP tunnel detection method and implementation based on statistical analysis/

2. Specific implementation examples

1. Listen to Netlink Socket and poll for processing

func (ns *NetlinkSocket) ReceiveFrom() ([]syscall.NetlinkMessage, syscall.Sockaddr, error) {<!-- -->
nr, from, err := syscall.Recvfrom(ns.fd, ns.buf, 0)
if err != nil {<!-- -->
return nil, from, err
}
if nr < syscall.NLMSG_HDRLEN {<!-- -->
return nil, from, fmt.Errorf("Got short response from netlink")
}

msg, err := syscall.ParseNetlinkMessage(ns.buf[:nr])
return msg, from, err
}

2. Process EXEC events in real time

func (p *Probe) netLinkHandler(e *netlinkProcEvent) {<!-- -->
switch e.Event {<!-- -->
case netlink.PROC_EVENT_EXEC:
p.handleProcExec(e.Pid, false) // pid
}
}

3. According to the basic principle of rebound shell, judge whether the process file descriptor is abnormal

//According to the basic principle of rebound shell, judge whether the process file descriptor is abnormal (that is, whether it is equal)
func (p *Probe) checkReverseShell(pid int) {<!-- -->
    //Get the corresponding inode
   inodeStdin, err := osutil. GetFDSocketInode(pid, 0)
   if err != nil {<!-- -->
      return nil
   }
   inodeStdout, err := osutil. GetFDSocketInode(pid, 1)
   if err != nil {<!-- -->
      return nil
   }
   if inodeStdin != inodeStdout || inodeStdin == 0 {<!-- -->
      return nil
   }
   return osutil.GetProcessConnection(pid, nil, utils.NewSet(inodeStdin))
}

/ / Get the connection corresponding to the process
func GetProcessConnection(pid int, clientPort *share.CLUSProtoPort, inodes utils.Set) *Connection {<!-- -->
var err error
if inodes == nil {<!-- -->
inodes, err = GetProcessSocketInodes(pid)
if err != nil {<!-- -->
return nil
}
}
if inodes. Cardinality() == 0 {<!-- -->
return nil
}
var sport uint16
if clientPort != nil {<!-- -->
sport = clientPort. Port
}
pidDir := global.SYS.ContainerProcFilePath(pid, "/")
if clientPort == nil || clientPort.IPProto == syscall.IPPROTO_TCP {<!-- -->
if conn := getConnectionByFile(pidDir + "net/tcp", inodes, true, sport); conn != nil {<!-- -->
return conn
}
if conn := getConnectionByFile(pidDir + "net/tcp6", inodes, true, sport); conn != nil {<!-- -->
return conn
}
}
if clientPort == nil || clientPort.IPProto == syscall.IPPROTO_UDP {<!-- -->
if conn := getConnectionByFile(pidDir + "net/udp", inodes, false, sport); conn != nil {<!-- -->
return conn
}
if conn := getConnectionByFile(pidDir + "net/udp6", inodes, false, sport); conn != nil {<!-- -->
return conn
}
}
return nil
}

The disadvantage of the above method is: only the name of the process execution file can be used to determine whether it is a shell process, uploading executable files, copying Bash files to other paths, etc. will bypass this method.

In addition, specific scenarios can also be analyzed to see if the fd corresponding to the process is abnormal and connected. For open source code, please refer to: https://github.com/zhanghaoyil/seesaw;

3. Summary

This article mainly introduces common rebound shell detection ideas, as well as implementation examples, welcome to add and share!

4. Reference link

  1. Zheng Han Andrew’s Rebound Shell Principle and Detection Technology Research