毁尸灭迹!一次服务器被入侵的血泪教训

前言
笔者的工作之一是管理100台左右的服务器,要保证其24小时处于正常working模式。谁知天有不测风云,前几天笔者管理的服务器大范围被植入发送DDOS攻击的程序,每天凌晨发动攻击。不幸中的万幸是我们主动发现问题,要是被老板先发现,估计要准备去人才市场了。

服务器安全策略
笔者管理的主机均为centOS,全部运行在公网上,对外提供服务。第一道安全策略是hosts策略,只允许从指定的主机访问公网服务器。

#
# hosts.allow   This file contains access rules which are used to
#               allow or deny connections to network services that
#               either use the tcp_wrappers library or that have been
#               started through a tcp_wrappers-enabled xinetd.
#
#               See 'man 5 hosts_options' and 'man 5 hosts_access'
#               for information on rule syntax.
#               See 'man tcpd' for information on tcp_wrappers
#
sshd:xxx.xxx.xxx.xxx/255.255.255.255

编辑/etc/hosts.allow,设置“白名单”(即允许SSH登录本机的IP地址),掩码设置为255.255.255.255表示为1台主机。

#
# hosts.deny    This file contains access rules which are used to
#               deny connections to network services that either use
#               the tcp_wrappers library or that have been
#               started through a tcp_wrappers-enabled xinetd.
#
#               The rules in this file can also be set up in
#               /etc/hosts.allow with a 'deny' option instead.
#
#               See 'man 5 hosts_options' and 'man 5 hosts_access'
#               for information on rule syntax.
#               See 'man tcpd' for information on tcp_wrappers
#
sshd:all

编辑/etc/hosts.deny,设置“黑名单”,掩码设置为255.255.255.255表示为1台主机。sshd表示使用ssh协议,all表示所有IP均不允许。hosts.allow的优先级高于hosts.deny。这是第一层防护。

第二道安全策略是修改SSH默认端口(SSH默认使用TCP协议22端口,如果你对TCP协议不太了解,请参考我的另一篇文章:TCP和UDP,你要知道的123 (TCP篇)。关键配置如下:

Port 22222
#Port 22

配置文件位置是etc/ssh/sshd_config。#号代表不生效,本例中将SSH端口改为了22222(理论上你可以在0-65535范围任意选择端口号,但不建议选择1024以下的端口,这些端口为系统保留端口)。

第三层防护是防火墙,只允许指定的IP连接SSH端口。

#只允许IP地址为x.x.x.x的设备访问本机22222端口,限定TCP
/sbin/iptables -A INPUT -s x.x.x.x -p tcp --dport 22222 -j ACCEPT
#拒绝所有其它地址访问本机22222端口。
/sbin/iptables -A INPUT -p tcp --dport 22222 -j DROP

防火墙也是按从上到下的顺序执行,满足条件后就不再往下执行,类似于程序中的break;

入侵分析及处理

所有主机并没有提供web服务,排除了web注入的可能性。想要植入程序,只有登录主机才可能。入侵者就是在我自建的三层防护下完成了植入,并且删除掉了登录日志(/var/log/secure、/var/log/message)、清掉了登录接入记录(/var/log/wtmp和//var/run/wtmp、/var/log/lastlog),甚至连历史命令记录(~/.bash_history)也全部删除了,可谓“毁尸来迹”!我连入侵者做了什么操作都不知道。

首要的工作当然是清理植入程序。通过netstat -napt | more命令,查询主机所有已开启的侦听端口,未发现异常。

《毁尸灭迹!一次服务器被入侵的血泪教训》 image.png

使用ps ax命令列出当前系统运行的所有进程,发现了植入程序。(因为当时太着急清除,忘记截取病毒进程照片。但我想说的是如何根据进程找到其程序目录。假设我们要查的进程如下图

《毁尸灭迹!一次服务器被入侵的血泪教训》 image.png

这是一个运行中的进程,进程号是30199,从上面我们得不到程序的安装路径。我们可以通过如下方法查询其安装路径

$ cd /proc/30199
$ ll

《毁尸灭迹!一次服务器被入侵的血泪教训》 image.png

图中标注的就是进程的安装路径,我们先通过kill命令杀掉进程,然后删除整个程序安装目录。其次,使用
crontab -l查看是否有轮询执行的异常脚本,检查cron的执行内容,最后检查启动服务
chkconfig --list检查是否有异常程序加入了服务,
/etc/rc.local等启动会执行的文件也要进行检查,防止攻击程序自动启动。到这里,植入的程序算是清除了。

但是很奇怪,我的防火墙策略、hosts策略都没有被动过痕迹,也保持着开启状态,入侵者是怎么进来的呢?

经过排查,我发现了两点可疑情况,一是部分未被植入的服务器都是最近才新上线,二是我管理的设备分部在天南地北,为何命中如此精准,绝大部分都中了招。
我开始怀疑是“内鬼”所为,因为设备列表只有我和合作方才有,新加的服务器还没来得及告诉相关所有人员。
不管如何推测,当前最重要的一件事就是提升系统安全性。

提升系统安全性

  • 思路:
    1. 建立日志服务器,将所有主机的登录日志和历史命令日志监控起来,并发送给日志服务器,防止入侵者销毁记录。
    2. 所有主机关闭密码登录,全部改为证书登录方式,并且只能通过指定主机才能够登录服务器。

有了思路,我们开始实现。

建立日志系统

  • 日志发送端

我们的第一个目标是将历史命令传输到日志服务器保存。历史命令保存在~/.bash_history文件中,一般我们只需要提出root用户的历史命令,即/root/.bash_history,我们想实时的将主机上输入的的命令传到日志服务器。先要做两个设置。

#不同登录会话共享history历史命令库
shopt -s histappend
#将内存中的命令写入文件,而不是当前用户登出后再写入。
PROMPT_COMMAND="history -a"

/root/.bash_profile(用户登录系统会读取该配置文件的设置)
默认情况下,同时几个用户登录同一台主机,他们只会读取登录前的历史记录,但彼此之间录入的命令相互是看不到的,shopt -s histappend就是让同时登录的用户共享一个历史命令库。还有一个默认设置,当前用户输入的命令并不会直接写入到历史命令日志文件,而要在其登出时才写入,这就不符合我们实时传输命令信息的要求,通过PROMPT_COMMAND="history -a",将其设置为输入命令即写入到本机的历史命令日志文件(/root/.bash_history)中。

设定好后,我们开始配置日志客户端。
centOS默认采用rsyslog程序管理日志,我们通过一些配置就能够使其满足我们的要求。rsyslog的配置文件存放在/etc/rsyslog.conf中。

#默认加载模块,提供本地系统日志功能。
$ModLoad imuxsock 
#默认加载模块,日志核心模块。
$ModLoad imklog  
#加载imfile模块,Text File Input Module(文本文件输入模块),可以将任何的标准输入转换为syslog消息。需要开启此模块,后续会详细讲解为何要开启此模块。
$ModLoad imfile
#模认日志格式
$ActionFileDefaultTemplate RSYSLOG_TraditionalFileFormat
$IncludeConfig /etc/rsyslog.d/*.conf
$IncludeConfig /etc/rsyslog.d/*.conf

#表明要监控的日志文件为/root/.bash_history,历史命令都会记录在此文件中。(手工监控的原因就是因为syslog不会处理history历史命令,前面开启imfile模块就是为了手工监控此文件)
$InputFileName /root/.bash_history
#给此监控操作打一个tag(标志),用于日志接收端根据tag分别处理。
$InputFileTag historyCommand
#记录指定监控日志文件的处理状态,比如/root/.bash_history文件已经处理了多少行等状态信息,可以自定义名称。根据笔者观察,文件默认生成在/目录下。
$InputFileStateFile state_product_cache-Statistic
#对指定监控文件进行轮询访问的时间间隔,这是的10是以秒为单位,每隔10秒以确认所监控的文件是否有新数据产生。
$InputFilePollInterval 10
#表示处理多少行后更新状态文件,建议改大一点,写状态文件需要消耗系统资源。
$InputFilePersistStateInterval 25000
#设置syslog消息类型,以local开始表示自定义类型,本例为local5,用于日志接收端分类处理。
$InputFileFacility local5
#定义日志的报警级别,此处定义为info级(有info,notice,warning等等)
$InputFileSeverity info
#设定监控操作是否生效,必选项
$InputRunFileMonitor

#第2个要监控的文件
$InputFileName /var/log/secure
$InputFileTag sshLogin
$InputFileStateFile state_product_cache-sshLogin
$InputFilePollInterval 10
$InputFilePersistStateInterval 25000
$InputFileFacility local5
$InputFileSeverity info
$InputRunFileMonitor

#表示监控的类型和级别,符合条件的写入到/var/log/message
*.info;mail.none;authpriv.none;cron.none;local5.none                /var/log/messages
#表示类型为authpriv(登录认证信息)的,级别为所有的消息写入/var/log/secure
authpriv.*                                              /var/log/secure
mail.*                                                  -/var/log/maillog
cron.*                                                  /var/log/cron
*.emerg                                                 *
uucp,news.crit                                          /var/log/spooler
local7.*                                                /var/log/boot.log
//loca5是我们的自定义类型,@@表示使用tcp协议将类型为loca5的所有级别的日志消息发送到192.168.100.100的100514端口上。
local5.* @@192.168.100.100:10514

日志发送端主机/etc/rsyslog.conf完整配置

以上配置的目的就是为了监控2个日志文件(/root/.bash_history和/var/log/secure),一旦文件内容发生了更新,就会将日志转发到192.168.100.100上,由100服务器处理日志消息,注意更新配置文件后记得重启rsyslog服务,命令是service rsyslogd restart,下面我们看看日志接收端的配置。

$ModLoad imuxsock 
$ModLoad imklog 

#开启tcp监听模块,用于监听发送端主机发送的日志消息。
$ModLoad imtcp
#监听端口为TCP 10514端口,端口可自定义,不和现有监听端口冲突即可。
$InputTCPServerRun 10514
$ActionFileDefaultTemplate RSYSLOG_TraditionalFileFormat
$IncludeConfig /etc/rsyslog.d/*.conf
#创建模板,定义写入日志消息格式。#template是关键字,historyFormat为自定义模板名称,%timegenerated%是系统内部属性变量,表示产生消息的时间,其它几个分别是消息发送主机,主机名,发送的消息内容(文字消息)
$template historyFormat,"%timegenerated% %FROMHOST-IP%(%HOSTNAME%) : :%msg%\n"
$template sshLoginFormat,"%timegenerated% %FROMHOST-IP%(%HOSTNAME%) : %syslogtag%:%msg%\n"
#syslogtag是syslog内部属性变量,还记得在发送端我们配置了2个tag吗?这里派上用场了,一个是`historyCommand`,另一个是`sshLogin`。/root/historyTag.log表示日志记录位置,historyFormat使用上面定义的消息格式。
:syslogtag, startswith, "historyCommand"  /root/historyTag.log;historyFormat
:syslogtag, startswith, "sshLogin" /root/sshlogin.log;sshLoginFormat
*.info;mail.none;authpriv.none;cron.none;local5.none                /var/log/messages
authpriv.*;local5.none                                              /var/log/secure
mail.*                                                  -/var/log/maillog
cron.*                                                  /var/log/cron
*.emerg                                                 *
uucp,news.crit                                          /var/log/spooler
local7.*                                                /var/log/boot.log

日志接收端主机/etc/rsyslog.conf完整配置

关键配置其实就是模板定义,及后续的消息处理。设定好配置,记得重启rsyslog服务。在客户机上输入一条命令,然后在服务器上查看是否能够接收到客户机输入的信息。

《毁尸灭迹!一次服务器被入侵的血泪教训》 image.png

在客户机上随意执行一条命令。

《毁尸灭迹!一次服务器被入侵的血泪教训》 image.png

我们看到在客户机上输入的命令已经传送到服务器并保存在了服务器上。
再来看看登录信息。

《毁尸灭迹!一次服务器被入侵的血泪教训》 image.png

在客户机上的登录信息在其登入时就已经发送到了日志服务器上。

第一个目标总算基本完成了,可能有一点点复杂,但也就是操作几个配置文件的事,以上的配置都是经过了我实机测试,是能够正常工作的。
接下来的问题是如何批量部署到客户机上,如果您看过我之前的文章(Ansible,运维人员的好助手),一定会选择类似Ansible的批量执行作业的软件。这里我们使用Ansible来进行日志发送端的部署工作。
首先将整理好的rsyslog.conf文件存放在安装了Ansible的主机上。

#将本机rsyslog.conf拷贝到目标主机群(centos7)每一台机器,并存放到/etc/目录下,文件名同样为rsyslog.conf。force的意思是如果目标文件已经存在,强制覆盖。
ansible centos7 -m copy -a "src=/root/rsyslog.conf dest=/etc/rsyslog.conf force=yes"
#写入shopt -s histappend配置到/root/.bash_profile文件中。
ansible centos7 -m shell -a "echo 'shopt -s histappend' >> /root/.bash_profile"
#写入PROMPT_COMMAND=\"history -a\"到/root/.bash_profile文件中。
ansible centos7 -m shell -a "echo 'PROMPT_COMMAND=\"history -a\"' >> /root/.bash_profile"
#重启rsyslog服务(centos 7使用该命令,如果为centos 6使用service rsyslog restart
ansible centos7 -m shell -a "systemctl restart rsyslog"

通过以上几行命令,执行顺利的话,所有日志发送端的配置已经完成并生效了。

第二个目标由于篇幅关系,以后有机会再和大家分享。通过此次教训让我明白了安全防护的重要性,安全问题再怎么小心也不为过,希望通过我的教训引起大家对安全的重视。

补充声明,本人其实不是安全专业的从业者,只是自己在实际运维中摸索出来的经验,只是希望写出来和大家一起交流、探讨。水平有限,请大家见谅。
更多文章请访问我的公 众 号:Ted的技术乐园。

    原文作者:相当嚣张
    原文地址: https://www.jianshu.com/p/53865f4afc48
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞