"赤脚医生”和“七段骇客”过招的故事——一例Linux系统被黑的排查和确认

简介: 都说系统诊断就像医生看病,好医生能看出症状的背后是什么病灶和病因。给大家分享这个案例中的“医生”,从一个ssh登陆停滞入手,不仅发现了骇客攻击,还一路顺藤摸瓜,从蛛丝马迹入手,发现了骇客是如何把后门架到系统上,如何偷梁换柱隐藏流量的,而用到的都是大家都习以为常的工具——strace、tcpdump,grep以及文本编辑器。
e0ba57a62ccde975281f3dd60f1c4a9c7d8aa6e6


人物介绍:

直寻:IT老兵,干过运维,码过代码。UNIX old school guy,每天在Emacs里敲着命令行,折腾着代码。目前负责问诊Linux的疑难杂症,赤脚医生是也。

骇客:此案例中,骇客在其攻击的系统上架设了“后门”,还替换掉了正牌门卫,安插了自己的奸细,从手段来看超出了普通“炫技攻击”的范畴,达到了中级以上骇客水准。

        都说系统诊断就像医生看病,说“你身体有毛病”很容易,病人都疼的打滚了,谁不知道是有毛病?就像ssh登陆停滞了,谁都知道系统的安全出问题了。
        但好的医生,一定能看出症状的背后是什么病,还能看出这个病症是怎么来的,而且,根本用不上核磁共振、基因检测,用最简单的b超和化验就能定案。下面给大家分享这个案例中的“医生”,从一个ssh登陆停滞入手,不仅发现了骇客攻击,还一路顺藤摸瓜,从蛛丝马迹入手,发现了骇客是如何把后门架到系统上,如何偷梁换柱隐藏流量的,而用到的都是大家都习以为常的工具——strace、tcpdump,grep以及文本编辑器。
        下面,我们就从一个 Linux系统被黑的排查和确认案例,来看看“医生”和“骇客”过招的故事:

一、问题现象
  • ssh客户端登录时停滞,使用debug模式可以看到下面的情形
1. bash# ssh -vv root@XXX.XXX.XXX.XXX 
2. OpenSSH_7.2p2, LibreSSL 2.4.1 
3. debug1: Reading configuration data /etc/ssh/ssh_config  
4. debug1: /etc/ssh/ssh_config line 20: Applying options for *
5. debug1: /etc/ssh/ssh_config line 56: Applying options for *
6. debug2: resolving "XXX.XXX.XXX.XX" port 22  
7. debug2: ssh_connect_direct: needpriv 0  
8. debug1: Connecting to XXX.XXX.XXX.XXX … port 22.
9. debug1: Connection established.  
10. debug1: permanently_set_uid: 0/0  
11. debug1: key_load_public: No such file or directory  
12. debug1: identity file /var/root/.ssh/id_rsa type -1  
13. ... ...  
14. debug1: identity file /var/root/.ssh/id_ed25519-cert type -1 
15. debug1: Enabling compatibility mode for protocol 2.0  
16. debug1: Local version string SSH-2.0-OpenSSH_7.2  

  • 而服务器端可以看到如下日志记录
1. Feb 11 00:00:11 localhost sshd[22619]: Did not receive identification string from XXX.XXX.XXX.XXX  

  • 而把sshd在前台启动,可以看到如下的报错
1. : command not foundH-2.0-OpenSSH_6.6.1  

二、排查环境
除有问题的实例外,我们还准备了另外一台实例, 其ssh登录正常,用来作对比。两台实例都可以从VNC登录。这样我们可以先停掉init系统启动的sshd守护进程,而把sshd手工启动到前台,并且保证只有一个ssh客户端登录它们。

三、问题排查
既然sshd抱怨没有收到identification string(为方便记,下面我们称之为id字符串),那我们就抓包看看客户端有没有发送id字符串。在开始此之前,我们先简单介绍一下id字符串。
在客户端连接上SSH服务器之后,客户端和服务端都向对方发送一个ssh版本字符串。字符串的格式如下
1. SSH-protoversion-softwareversion SP comments CR LF  
具体id字符串的例子见抓包结果。
  • 抓包排查
为了便于比较,我们也抓一下正常的sshd,并且在转包结果上做上标记。
首先看正常sshd的抓包结果
  b6b48d90bbf61c4d48387e467d3117f9339e08c8
再看看登录停滞sshd的抓包结果 
c8c5d0d5ef74cd03a4ede22528e90e3d111e98c4
很明显,ssh客户端发送了id字符串,反倒是sshd没有发送自己的id字符串。这样为什么sshd还抱怨没有收到id字符串呢?
  • 探究sshd的执行路径
进一步追查为什么sshd抱怨没有收到客户端已经发来的id字符串。SELinux和防火墙都快速查看了一下,没有发现可疑迹象。所以祭出strace工具。因为sshd是fork子进程处理客户端连接的,而且我们还要追踪id字符串的处理过程,所以我们这样使用strace
1. strace -s 256 -ff -o /tmp/strace-centos6.7-bad.log $(which sshd) -D   

结果我们拿到了下面这些文件
1. bash$ ls strace-centos6.7-bad.log*  
2. strace-centos6.7-bad.log.23840  strace-centos6.7-bad.log.23936  
3. strace-centos6.7-bad.log.23900  strace-centos6.7-bad.log.23937  
4. strace-centos6.7-bad.log.23901  strace-centos6.7-bad.log.23938  
5. strace-centos6.7-bad.log.23902  
6. bash$ 

通过检索clone调用,我们很容易理清结果里进程间的父子关系
1. bash$ grep -E '^clone\(' strace-centos6.7-bad.log*  
2. strace-centos6.7-bad.log.23840:clone(...) = 23900  
3. strace-centos6.7-bad.log.23840:clone(...) = 23936  
4. strace-centos6.7-bad.log.23900:clone(...) = 23901  
5. strace-centos6.7-bad.log.23901:clone(...) = 23902  
6. strace-centos6.7-bad.log.23936:clone(...) = 23937  
7. strace-centos6.7-bad.log.23937:clone(...) = 23938  
8. bash$ 

明显日志文件结果是有猫腻的。因为sshd是fork子进程处理用户连接的。在我们启动sshd(这个我们称之为controller进程)后,客户端连接时,sshd会fork一个子进程(这个我们称之为worker进程),由子进程负责处理用户连接。在启用-ff选项的情况下,controller进程的strace日志文件应该是strace-centos6.7-bad.log。我们用如下命令抓取的正常sshd的执行路径,用正常sshd的strace日志文件结果来验证我们的推断
1. strace -s 256 -ff -o /tmp/strace-centos6.8-ok.log $(which sshd) -D  
如下结果是我们拿到的结果
1. bash$ ls strace-centos6.8-ok.log*  
2. strace-centos6.8-ok.log     strace-centos6.8-ok.log.1605   
3. strace-centos6.8-ok.log.1606    strace-centos6.8-ok.log.1607  
4. bash$  

同样通过检索clone调用,理清结果里进程间的父子关系。很容易确认正常sshd的controller进程的日志文件是strace-centos6.8-ok.log
1. bash$ grep -E '^clone\(' strace-centos6.8-ok.log*  
2. strace-centos6.8-ok.log:clone(...) = 1640  
3. strace-centos6.8-ok.log.1605:clone(...) = 1606  
4. strace-centos6.8-ok.log.1606:clone(...) = 1607  
5. bash$  

为防止是从服务器上下载时遗漏了日志文件,检查下载方法
1. bash$ history|grep scp|grep strace-centos6.7-bad|grep -v grep  
2.  347  scp root@XXX.XXX.XXX.XXX:/tmp/strace-centos6.7-bad* .  
3.  348  scp nerd@XXX.XXX.XXX.XXX:/tmp/strace-centos6.7-bad* .  
4. bash$

很明显,我们不可能遗漏什么文件。而且,进一步检查strace-centos6.7-bad.log.23840的内容,也可以确认进程23840的确执行了我们的命令
1. bash$ head -4 strace-centos6.7-bad.log.23840  
2. execve("/usr/sbin/sshd", ["/usr/sbin/sshd", "-D"],…) = 0  
3. brk(0)                                  = 0x7f2e57230000  
4. mmap(NULL, 4096, ...) = 0x7f2e553a7000  
5. access("/etc/ld.so.preload", R_OK)      = 0  
6. bash$ 

所以可以断定,登录停滞的sshd的strace日志文件结果是异常的。

四、初步结论
正如了解一个人是否健康很容易,判定系统是否被黑也很容易。系统上的结果要么是人造成的,要么是进程造成的。只要了解到一个异常结果,而且这个结果不是明确授权的人和进程造成的,那就是被黑了。基于此,因为登录停滞的sshd的strace日志文件结果是异常的,所以可以判定,其系统被黑了。
除此之外,我们还可以凭借这个异常结果进一步推论。很明显,登录停滞的sshd的controller进程是作为一个子进程运行的(所以这个进程的strace日志文件才是strace-centos6.7-bad.log.23840,而不是strace-centos6.7-bad.log)。那就意味着strace还启动了一个进程,这个进程是登录停滞sshd的controller进程的父进程。这个父进程和其strace日志都被隐藏了。这也是一个异常的结果。而且大家都清楚,隐藏进程和修改日志输出是常见的骇客伎俩。
基于以上分析,可以判定,系统一定是被黑了。可是这样的事实和推理过程,恐怕不易说服客户。况且我们还有一个问题没有回答。那就是为什么sshd在前端启动时有下面的输出
1. : command not foundH-2.0-OpenSSH_6.6.1  

五、确认系统被黑
为了让客户了解系统被黑的事实,接受我们的判断,我们要简单做一下系统如何被黑的诊断。
  • 搜寻诊断线索
首先对比登录停滞sshd和正常sshd的congtroller进程的执行路径。
这是正常sshd的执行路径 
  5935a98e9d32af87065a665b05037d642aebb410
这是登录停滞sshd的执行路径  
  caefb09f9bcd304fed7c85c012d1388c394cbab1
可以明显的看到,登录停滞的sshd额外调用了recvfrom syscall。除此之外,正常sshd只fork了一个子进程,而登录停滞的sshd则fork了2个子进程。
既然系统被黑,那骇客肯定是要留后门的。所以这个额外的recvfrom调用和多出的这个子进程很可能就是后门的一部分。
检索openssh-server的源码,很快可以定位到sshd对id字符串的处理的函数
d8f0400e2e29e17d9c6ffb9a52d80f52f41c14c2
从源码可以看出,sshd会使用一个256字节的buffer来处理客户端发来的id字符串,而且每次是读1个字节。但是recvfrom却是使用一个9字节的buffer,一次读了9个字节。而且使用了MSG_PEEK标志。根据recvfrom(2)对MSG_PEEK标志的解释:
“The MSG_PEEK flag causes the receive operation to return data from the beginning of the receive queue without removing that data from the queue.”

从网络连接上读取数据却不消费之,正是骇客常用的把控制流隐藏在正常流量中的伎俩。可以百分百确认,这个recvfrom就是骇客后门的一部分,这就是我们要找的线索。
  • 按图索骥
recvfrom操作的描述符是4,这个文件描述符被子进程23900继承了。如下图所示 
d5600a6e028d33d13aff5c8629d6454b11acadda
通过下图可以进一步确认,这个文件描述4,又被进程23900的子进程23901继承了。除此之外,进程23900还创建了新的会话。
1292d4a0eb43c40323997054427e8c5ca6420130
通过下图可以进一步确认,进程23901把文件描述4复制为它的标准输入和标准输出(shell常见技巧&骇客伎俩),并且启动了shell。
  416d713345c6d40af9e949b7e2586a4748cce373
进一步分析进程23901,可以看到它从标准输入上也就是网络连接上接受了字符串“SSH-2.0-OpenSSH_6.6.1\r\n”。它就去掉末尾的换行符,在各个可能的路径(猜测是根据PATH变量来的)下搜索可执行程序“SSH-2.0-OpenSSH_6.6.1\r”。这是sshd需要的id字符串,不是程序名称。所以搜索失败了。 所以它启动一个子进程报错,而自己继续执行read,等待来自网络的新指令。 
3314f77048f20162b5ba8d823ec174c8c9f93de0
那么,这个报错的子进程的执行路径是什么样的呢? 
  c97e41a0e3cb74ff858edc326c5ab915f5c9f8ce
这就是把sshd在前台启动时,客户端一连,我们看到的那个报错。只是终端显示的不太完整而已。
  • 我们发现了什么
到此为止,我们简单、粗疏的诊断就告一段落了。那么我们发现了什么?
1. 客户系统上的sshd程序肯定被替换了。否则我们执行strace抓取sshd的执行路径时,sshd的controller进程不应该是一个子进程。
2. 替换掉客户系统上sshd的程序会从网络流量中自动分离骇客的控命令和sshd的流量,并且启动真实的sshd应答ssh访问。
3. 系统工具,比如strace,被修改过了,以便隐藏进程和过滤日志,应该是Rootkit攻击。

六、结论
这次排查过程,我们只使用了大家都习以为常的工具,诸如strace、tcpdump,grep以及文本编辑器,就解决了问题,客户也接受我们的解释。所以建议大家多熟悉常见的工具。通过熟悉工具来更好的理解软件的架构与执行路径。
因为生产系统上几乎不会安装调试符号,所以探究sshd之类的执行路径、排查问题,往往要借助*trace类的工具。如果大家对于C库API和系统调用比较熟悉的话,对于排查问题将大有益处。



相关实践学习
【涂鸦即艺术】基于云应用开发平台CAP部署AI实时生图绘板
【涂鸦即艺术】基于云应用开发平台CAP部署AI实时生图绘板
相关文章
|
12月前
|
Ubuntu Linux Anolis
Linux系统禁用swap
本文介绍了在新版本Linux系统(如Ubuntu 20.04+、CentOS Stream、openEuler等)中禁用swap的两种方法。传统通过注释/etc/fstab中swap行的方式已失效,现需使用systemd管理swap.target服务或在/etc/fstab中添加noauto参数实现禁用。方法1通过屏蔽swap.target适用于新版系统,方法2通过修改fstab挂载选项更通用,兼容所有系统。
983 3
Linux系统禁用swap
|
12月前
|
Linux
Linux系统修改网卡名为eth0、eth1
在Linux系统中,可通过修改GRUB配置和创建Udev规则或使用systemd链接文件,将网卡名改为`eth0`、`eth1`等传统命名方式,适用于多种发行版并支持多网卡配置。
1572 3
|
Ubuntu Linux 网络安全
Linux系统初始化脚本
一款支持Rocky、CentOS、Ubuntu、Debian、openEuler等主流Linux发行版的系统初始化Shell脚本,涵盖网络配置、主机名设置、镜像源更换、安全加固等多项功能,适配单/双网卡环境,支持UEFI引导,提供多版本下载与持续更新。
1054 3
Linux系统初始化脚本
|
运维 Linux 开发者
Linux系统中使用Python的ping3库进行网络连通性测试
以上步骤展示了如何利用 Python 的 `ping3` 库来检测网络连通性,并且提供了基本错误处理方法以确保程序能够优雅地处理各种意外情形。通过简洁明快、易读易懂、实操性强等特点使得该方法非常适合开发者或系统管理员快速集成至自动化工具链之内进行日常运维任务之需求满足。
738 18
|
12月前
|
安全 Linux Shell
Linux系统提权方式全面总结:从基础到高级攻防技术
本文全面总结Linux系统提权技术,涵盖权限体系、配置错误、漏洞利用、密码攻击等方法,帮助安全研究人员掌握攻防技术,提升系统防护能力。
1201 1
|
12月前
|
监控 安全 Linux
Linux系统提权之计划任务(Cron Jobs)提权
在Linux系统中,计划任务(Cron Jobs)常用于定时执行脚本或命令。若配置不当,攻击者可利用其提权至root权限。常见漏洞包括可写的Cron脚本、目录、通配符注入及PATH变量劫持。攻击者通过修改脚本、创建恶意任务或注入命令实现提权。系统管理员应遵循最小权限原则、使用绝对路径、避免通配符、设置安全PATH并定期审计,以防范此类攻击。
1483 1
|
缓存 监控 Linux
Linux系统清理缓存(buff/cache)的有效方法。
总结而言,在大多数情形下你不必担心Linux中buffer与cache占用过多内存在影响到其他程序运行;因为当程序请求更多内存在没有足够可用资源时,Linux会自行调整其占有量。只有当你明确知道当前环境与需求并希望立即回收这部分资源给即将运行重负载任务之前才考虑上述方法去主动干预。
2682 10
|
安全 Linux 数据安全/隐私保护
为Linux系统的普通账户授予sudo访问权限的过程
完成上述步骤后,你提升的用户就能够使用 `sudo`命令来执行管理员级别的操作,而无需切换到root用户。这是一种更加安全和便捷的权限管理方式,因为它能够留下完整的权限使用记录,并以最小权限的方式工作。需要注意的是,随意授予sudo权限可能会使系统暴露在风险之中,尤其是在用户不了解其所执行命令可能带来的后果的情况下。所以在配置sudo权限时,必须谨慎行事。
1951 0
|
Ubuntu Linux 开发者
国产 Linux 发行版再添新成员,CutefishOS 系统简单体验
当然,系统生态构建过程并不简单,不过为了帮助国产操作系统优化生态圈,部分企业也开始用国产操作系统替代 Windows,我们相信肯定会有越来越多的精品软件登录 Linux 平台。
934 0
|
Ubuntu 安全 Linux
Linux系统入门指南:从零开始学习Linux
Shell脚本是一种强大的自动化工具,可以帮助您简化重复的任务或创建复杂的脚本程序。了解Shell脚本的基本语法和常用命令,以及编写和运行Shell脚本的步骤,将使您更高效地处理日常任务。
779 0