不久之前,遇到一台Linux服务器受到了攻击,攻击者将一个经过修改后的OpenSSH二进制文件加载到了Web服务器的内存中。攻击者可以利用这个修改后的OpenSSH二进制文件作为控制目标系统的后门来使用。我们的客户当时保存了受感染Web服务器的系统快照以及相关的pcap文件,于是我便想要知道,是否有可能通过内存快照恢复出相关的密钥材料,并解密SSH会话。 首先,我们需要了解OpenSSH的基本知识及其工作原理。幸运的是,OpenSSH是开源的,因此我们可以下载其源代码并从中了解其实现细节。虽然RFC文档读起来非常无聊,但其中包含的丰富信息是我们必须要了解的。抽象一点来看,SSH协议包含了下列内容: SSH协议+软件版本交换 密钥交换 用户身份验证 客户端请求“会话”类型的通道 客户端请求伪终端 与客户端会话交互
在协商加密算法并生成会话密钥之前,SSH帧将被解密,即使帧被加密,根据算法的不同,帧的某些部分也可能不会被加密。例如,aes256 gcm算法不会加密帧中的4字节长度,但chacha20-poly1305算法则会进行加密。 接下来,客户端将向服务器发送KEX_INIT消息来开始协商会话所需的参数,例如密钥交换和加密算法等等。根据这些算法的顺序,客户端和服务器端都将选择双方都支持的首选算法。在KEX_INIT消息之后,则是多个跟密钥交换相关的消息,用于确定双方所使用的新密钥。这个消息用于告诉通信的双方,一切已经准备就绪,可以开始加密会话了,而之后的数据包都将进行加密。在双方使用新的加密密钥后,客户端将请求用户进行身份验证,并根据服务器上配置的身份验证机制执行基于密码或密钥的身份验证流程。经过身份验证之后,客户端将打开一个会话通道,并根据请求的操作(ssh/sftp/scp等)来通过该通道去请求服务。 会话的密钥保存在内存堆中,针对源代码的深入研究让我成功找到了负责发送和接收NEWKEYS帧的函数。我发现有一个“ssh”结构体中存储了一个“session\u state”结构体,而这个结构体中存储了跟当前SSH会话相关的所有类型的消息,其中包括一个新的密钥结构(包含与加密、MAC和压缩算法相关的消息)。深入分析之后,我们终于找到了“sshenc”结构体,它包含了密码的名称、密钥、IV和块长度。 下面给出的的是“sshenc”结构体的定义:
如果我们根据所有这些约束条件进行验证,我们就应该能够可靠地找到sshenc结构体了。于是,我构建了一个用于漏洞验证的PoC脚本,它可以在目标主机上实时运行,而这台主机能够跟目标进程连接并从内存中搜索目标结构体。在这个脚本的帮助下(Python和ptrace),我将能够从一台实时运行的主机中恢复出会话密钥,但是我们如何从系统快照中恢复出会话密钥呢?这里就要介绍Volatility了,Volatility是一款基于Python开发的内存取证框架,而且还允许研究人员开发自定义的插件。经过一番努力之后,我自己编写了一个Volatility 2插件,它将帮助我们分析内存快照,并导出会话密钥。
|