主机入侵检测系统系列:这一篇讲述检测外连行为的原理和技术,可统一检测宿主机和docker子机
一台主机入侵后,入侵者往往会把数据发送出去或启动reverse shell。一般在IDC
的出口防火墙都会有检测异常外连行为,可能由于中间有NAT
,并不一定知道是哪台机器过来,但即使是知道哪台机器过来的,也不知道是该台机器哪个程序发起的外连行为。
通常的操作,都是用netstat
命令来获取
[[email protected] test]# netstat -anp4
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 127.0.0.1:42485 0.0.0.0:* LISTEN 33041/node
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 1640/sshd
tcp 0 0 127.0.0.1:25 0.0.0.0:* LISTEN 1885/master
tcp 0 0 127.0.0.1:55622 127.0.0.1:42485 ESTABLISHED 25101/sshd: [email protected]
tcp 0 0 127.0.0.1:42485 127.0.0.1:50352 ESTABLISHED 33041/node
tcp 0 0 127.0.0.1:42485 127.0.0.1:55622 ESTABLISHED 25171/node
tcp 0 0 127.0.0.1:50354 127.0.0.1:42485 ESTABLISHED 128902/sshd: buckxu
tcp 0 0 192.168.190.129:45782 192.168.190.128:1514 ESTABLISHED 2109/ossec-agentd
tcp 0 0 127.0.0.1:50352 127.0.0.1:42485 ESTABLISHED 128902/sshd: buckxu
tcp 0 0 192.168.190.129:22 192.168.190.1:62331 ESTABLISHED 25087/sshd: buckxu
tcp 0 0 127.0.0.1:42485 127.0.0.1:50354 ESTABLISHED 128969/node
tcp 0 0 127.0.0.1:42485 127.0.0.1:55620 ESTABLISHED 33041/node
tcp 0 0 192.168.190.129:22 192.168.190.1:52429 ESTABLISHED 128898/sshd: buckxu
tcp 0 0 127.0.0.1:55620 127.0.0.1:42485 ESTABLISHED 25101/sshd: [email protected]
udp 0 0 0.0.0.0:68 0.0.0.0:* 103880/dhclient
但如果放在HIDS(主机入侵检测系统)
实现,就不可能调用命令,原因如下:
netstat
命令netstat
命令,也可能由于之前的操作,导致netstat
运行时依赖的so库缺失或符号缺失,导致无法执行这个命令netstat
命令执行有异常,变成僵尸进程netstat
命令在宿主机是没办法查到docker里的外连行为按照Unix哲学
,一切皆文件,像端口和进程信息这些内容都可以从/proc
文件系统下找到,所以HIDS
是会/proc
获取这些信息。
下面拿上面命令结果的2109/node
来做例子展示这种手段。
由于一个socket
在Linux里是一个fd
,先看一下/proc/2109/fd
的内容
[[email protected] ~]# ls -l /proc/2109/fd
total 0
lrwx------. 1 root root 64 Jan 4 18:35 0 -> /dev/null
lrwx------. 1 root root 64 Jan 4 18:35 1 -> /dev/null
lrwx------. 1 root root 64 Jan 4 18:35 2 -> /dev/null
lrwx------. 1 root root 64 Jan 4 18:35 3 -> socket:[26069]
lr-x------. 1 root root 64 Jan 4 18:35 4 -> /dev/urandom
lrwx------. 1 root root 64 Jan 4 18:35 5 -> /var/ossec/queue/rids/005
lrwx------. 1 root root 64 Jan 4 18:35 6 -> /var/ossec/queue/rids/sender_counter
lrwx------. 1 root root 64 Jan 4 18:35 7 -> socket:[18173675]
lrwx------. 1 root root 64 Jan 4 18:35 8 -> socket:[2986416]
取这一行
lrwx------. 1 root root 64 Jan 4 18:35 7 -> socket:[18173675]
其中2969198是inode
。到/proc/33041/net
找一下这个inode
是在哪个文件有使用
[[email protected] ~]# grep -rIn "18173675" /proc/2109/net
/proc/2109/net/tcp:9: 7: 81BEA8C0:B2D6 80BEA8C0:05EA 01 00000000:00000000 00:00000000 00000000 983 0 18173675 1 ffff9259346e3640 20 4 30 10 7
看一下/proc/2109/net/tcp
的内容
[[email protected] ~]# cat /proc/2109/net/tcp
sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode
0: 0100007F:A5F5 00000000:0000 0A 00000000:00000000 00:00000000 00000000 1000 0 2969198 1 ffff92593308be00 100 0 0 10 0
1: 00000000:0016 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 24908 1 ffff925934638000 100 0 0 10 0
2: 0100007F:0019 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 25073 1 ffff9259346387c0 100 0 0 10 0
3: 0100007F:D946 0100007F:A5F5 01 00000000:00000000 00:00000000 00000000 1000 0 28151168 1 ffff92593308b640 20 4 30 3 2
4: 0100007F:A5F5 0100007F:C4B0 01 00000000:00000000 00:00000000 00000000 1000 0 4419751 1 ffff9259346e3e00 20 4 28 10 12
5: 0100007F:A5F5 0100007F:D946 01 00000000:00000000 00:00000000 00000000 1000 0 28146298 1 ffff92593308f440 21 4 28 10 -1
6: 0100007F:C4B2 0100007F:A5F5 01 00000000:00000000 00:00000000 00000000 1000 0 4418745 1 ffff9259346e45c0 20 4 28 3 2
7: 81BEA8C0:B2D6 80BEA8C0:05EA 01 00000000:00000000 00:00000000 00000000 983 0 18173675 1 ffff9259346e3640 20 4 30 10 7
8: 0100007F:C4B0 0100007F:A5F5 01 00000000:00000000 00:00000000 00000000 1000 0 4418744 1 ffff9259346e2e80 20 4 30 4 4
9: 81BEA8C0:0016 01BEA8C0:F37B 01 0000004C:00000000 01:00000015 00000000 0 0 28146223 4 ffff9259346e7440 21 4 31 10 663
10: 0100007F:A5F5 0100007F:C4B2 01 00000000:00000000 00:00000000 00000000 1000 0 4419752 1 ffff9259346e4d80 20 4 30 10 7
11: 0100007F:A5F5 0100007F:D944 01 00000000:00000000 00:00000000 00000000 1000 0 28146297 3 ffff9259330887c0 20 4 31 10 25
12: 81BEA8C0:0016 01BEA8C0:CCCD 01 00000000:00000000 02:0004A056 00000000 0 0 4418688 2 ffff9259346e1f00 23 4 30 10 579
13: 0100007F:D944 0100007F:A5F5 01 00000000:00000000 00:00000000 00000000 1000 0 28151167 2 ffff92593308ae80 20 4 30 10 -1
由于每个/proc/<pid>/net/tcp
文件的格式和/proc/net/tcp
是一样的,按照Linux内核文档Documentation/networking/proc_net_tcp.txt
的内容
This document describes the interfaces /proc/net/tcp and /proc/net/tcp6.
Note that these interfaces are deprecated in favor of tcp_diag.These /proc interfaces provide information about currently active TCP
connections, and are implemented by tcp4_seq_show() in net/ipv4/tcp_ipv4.c
and tcp6_seq_show() in net/ipv6/tcp_ipv6.c, respectively.
It will first list all listening TCP sockets, and next list all established
TCP connections. A typical entry of /proc/net/tcp would look like this (split
up into 3 parts because of the length of the line):
46: 010310AC:9C4C 030310AC:1770 01
| | | | | |--> connection state
| | | | |------> remote TCP port number
| | | |-------------> remote IPv4 address
| | |--------------------> local TCP port number
| |---------------------------> local IPv4 address
|----------------------------------> number of entry
00000150:00000000 01:00000019 00000000
| | | | |--> number of unrecovered RTO timeouts
| | | |----------> number of jiffies until timer expires
| | |----------------> timer_active (see below)
| |----------------------> receive-queue
|-------------------------------> transmit-queue
1000 0 54165785 4 cd1e6040 25 4 27 3 -1
| | | | | | | | | |--> slow start size threshold,
| | | | | | | | | or -1 if the threshold
| | | | | | | | | is >= 0xFFFF
| | | | | | | | |----> sending congestion window
| | | | | | | |-------> (ack.quick<<1)|ack.pingpong
| | | | | | |---------> Predicted tick of soft clock
| | | | | | (delayed ACK control data)
| | | | | |------------> retransmit timeout
| | | | |------------------> location of socket in memory
| | | |-----------------------> socket reference count
| | |-----------------------------> inode
| |----------------------------------> unanswered 0-window probes
|---------------------------------------------> uid
timer_active:
0 no timer is pending
1 retransmit-timer is pending
2 another timer (e.g. delayed ack or keepalive) is pending
3 this is a socket in TIME_WAIT state. Not all fields will contain
data (or even exist)
4 zero window probe timer is pending
上面说明这个socket
的信息如下:
根据Linux内核代码
net/ipv4/tcp_ipv4.c
的tcp4_seq_show
实现, IP地址还是网络字节序,而端口则已经转为本机字节序
根据Linux内核代码
include/net/tcp_states.h
的枚举定义,socket状态的定义如下enum {
TCP_ESTABLISHED = 1,
TCP_SYN_SENT,
TCP_SYN_RECV,
TCP_FIN_WAIT1,
TCP_FIN_WAIT2,
TCP_TIME_WAIT,
TCP_CLOSE,
TCP_CLOSE_WAIT,
TCP_LAST_ACK,
TCP_LISTEN,
TCP_CLOSING, /* Now a valid state */TCP_MAX_STATES /* Leave at the end! */
};
从上面来看,2109这个进程通过本机地址192.168.190.129的45782端口连接远端192.168.190.128的端口1514。那么在HIDS里实现,就可以定时去扫描/proc/<pid>/fd
和/proc/<pid>/net/tcp
,就可以获取到哪个进程启动了连接到哪些机器,再对远程IP进行筛选,就知道是否有外连行为。
通过这种技术,也可以监控高危端口开启,大量socket time_wait堆积的情况,检测发送队列和接收队列是否有数据包堆积。
用同样方式在unix socket上,还可以得到进程的IPC连接
192.168.190.129:45782
,对应数据(81BEA8C0:B2D6)
192.168.190.128:1514
, 对应数据(80BEA8C0:05EA)
01
00000000:00000000
00:00000000
0
18173675
上面技术能否检测Docker内部的外连行为,答案是可以的。
看一下docker的运行情况,有一个docker在运行,ID是b0d8c7be723f
[[email protected] ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
b0d8c7be723f nginx "/docker-entrypoint.…" 3 months ago Up 7 days 80/tcp great_hawking
看一下b0d8c7be723f
运行的进程,可以看到运行了nginx, pid分别为78299, 78350
[email protected] ~]# docker top b0d8c7be723f
UID PID PPID C STIME TTY TIME CMD
root 78299 78280 0 2020 ? 00:00:00 nginx: master process nginx -g daemon off;
101 78350 78299 0 2020 ? 00:00:00 nginx: worker process
用netstat
命令却是看不到nginx监听在80端口的, 怎么办呢?
[[email protected] ~]# netstat -anp4
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 127.0.0.1:42485 0.0.0.0:* LISTEN 33041/node
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 1640/sshd
tcp 0 0 127.0.0.1:25 0.0.0.0:* LISTEN 1885/master
tcp 0 0 127.0.0.1:55622 127.0.0.1:42485 ESTABLISHED 25101/sshd: [email protected]
tcp 0 0 127.0.0.1:42485 127.0.0.1:50352 ESTABLISHED 33041/node
tcp 0 0 127.0.0.1:42485 127.0.0.1:55622 ESTABLISHED 25171/node
tcp 0 0 127.0.0.1:50354 127.0.0.1:42485 ESTABLISHED 128902/sshd: buckxu
tcp 0 0 192.168.190.129:45782 192.168.190.128:1514 ESTABLISHED 2109/ossec-agentd
tcp 0 0 127.0.0.1:50352 127.0.0.1:42485 ESTABLISHED 128902/sshd: buckxu
tcp 0 0 192.168.190.129:22 192.168.190.1:62331 ESTABLISHED 25087/sshd: buckxu
tcp 0 0 127.0.0.1:42485 127.0.0.1:50354 ESTABLISHED 128969/node
tcp 0 0 127.0.0.1:42485 127.0.0.1:55620 ESTABLISHED 33041/node
tcp 0 0 192.168.190.129:22 192.168.190.1:52429 ESTABLISHED 128898/sshd: buckxu
tcp 0 0 127.0.0.1:55620 127.0.0.1:42485 ESTABLISHED 25101/sshd: [email protected]
udp 0 0 0.0.0.0:68 0.0.0.0:* 103880/dhclient
根据Linux命名空间的机制,在物理机上是可以看到docker里的进程,选择78350为例
[[email protected] ~]# ls -l /proc/78350/exe
lrwxrwxrwx. 1 101 101 0 Jan 4 16:43 /proc/78350/exe -> /usr/sbin/nginx
通过查看/proc/78350/net/tcp
文件,是可以看到这个进程监听在80端口。可见,这种手段也是可以应用于docker场景
[[email protected] ~]# cat /proc/78350/net/tcp
sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode
0: 00000000:0050 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 11678003 1 ffff92593463cd80 100 0 0 10 0