当前位置: 首页 > 科技观察

取证分析OpenSSH会话信息解密

时间:2023-03-14 00:49:33 科技观察

不久前,我们遇到了一个取证案例,Linux服务器被入侵,修改后的OpenSSH二进制文件被加载到Web服务器的内存中。修改后的OpenSSH二进制文件被攻击者用作系统的后门。在系统遭到破坏时,客户有系统的pcap和管理程序屏幕截图。基于这一发现,我们开始怀疑是否有可能通过从内存屏幕截图中恢复密钥材料来解密SSH会话并获取有关它的知识。在本文中,我将介绍我对OpenSSH所做的研究,并发布一些从内存中转储OpenSSH会话密钥的工具,结合pcap解密和会话解析,并提交我的研究成果。SSH协议首先,我会了解OpenSSH及其工作原理。幸运的是,OpenSSH是开源的,因此我们可以轻松下载并阅读实现细节。RFC的内容,虽然读起来有点枯燥,但也包含了丰富的信息。从更高的层次上看,SSH协议是这样的:(1)SSH协议+软件版本交换;(2)算法协商(KEXINIT):密钥交换算法;加密演算法;MAC算法;压缩算法;(3)密钥交换;(4)用户认证;(5)客户端请求一个“session”类型的通道;(6)客户端请求一个伪终端;(7)client与session交互;连接服务器并发送协议版本和软件版本:SSH-2.0-OpenSSH_8.3,服务器响应其协议和软件版本。交换初始协议和软件版本后,所有流量都封装在SSH帧中。SSH帧主要存在帧长、填充长度、载荷数据、填充内容、MAC。SSH帧示例如下:使用dissect.cstruct解析的SSH帧示例在协商加密算法和生成会话密钥之前,SSH帧不会被加密,即使帧被加密,根据算法的不同,某些帧可能未加密也已加密。比如aes256-gcm不会加密frame中的4字节长度,但是chacha20-poly1305会。接下来,客户端会向服务器发送一条KEX_INIT消息,开始协商密钥交换和加密算法等会话参数。根据这些算法的顺序,客户端和服务器将选择双方都支持的第一个首选算法。在KEX_INIT消息之后,交换了几个keyexchange相关的消息,然后双方互相发送NEWKEYS消息。此消息将告诉对方一切已准备就绪,可以开始加密会话,并且过程中的下一帧将被加密。双方获取新的加密密钥生效后,客户端将请求用户认证,并根据服务器配置的认证机制进行基于密码/密钥/等的认证。session认证通过后,客户端会开启一个通道,根据请求的操作(ssh/sftp/scp等)通过通道请求服务。恢复会话密钥恢复会话密钥的第一步是分析OpenSSH源代码并调试现有的OpenSSH二进制文件,我尝试自己编译OpenSSH,在某处记录生成的会话密钥,并附加调试器,然后在程序的记忆。成功后,会话密钥保存在堆上的内存中。进一步深入研究源代码,我找到了负责发送和接收NEWKEYS帧的函数。我发现有一个“ssh”结构体存储了一个“session_state”结构体,这个结构体又包含了当前SSH会话相关的所有信息,包括一个newkeys结构体,里面包含了加密、mac和压缩算法相关的信息。经过一番挖掘,我们最终找到了包含密码名称、密钥、IV和块长度的“shenc”结构。OpenSSH的结构概述如下所示:SSHENC结构和关系以及shenc结构的定义:SSHENC结构使得很难在内存中找到密钥本身(因为它只是一串随机字节),但是shenc和其他结构是更独特,有一些我们可以验证的属性。然后我们可以获取程序的整个内存地址空间并根据这些约束验证每个偏移量。我们可以检查以下属性:name、cipher、key和iv成员都是有效的指针;name成员指向一个有效的密码名等于cipher->name;key_len在有效范围内;iv_len在有效范围内;block_size在有效范围内;如果我们针对所有这些约束进行验证,我们应该能够可靠地找到shenc结构。为此,我着手构建一个POCPython脚本,该脚本将在连接到进程并概述结构内存的实时主机上运行。这个脚本的源代码可以在这里找到,它实际上工作得很好,并为找到的每个键输出一个jsonblob。因此,我演示了可以使用Python和ptrace从实时主机恢复会话密钥,但是我们如何从内存屏幕截图中恢复会话密钥?这就是波动性发挥作用的地方。Volatility是一个用Python编写的内存取证框架,能够编写自定义插件。经过一番努力,我能够编写一个Volatility2插件,并且能够分析内存屏幕截图和转储会话密钥!为了Volatility3插件大赛,我也把插件移植到了Volatility3上,提交研究。Volatility2SSH会话密钥转储器输出解密和解析的流量用于加密和解密通信的会话密钥恢复成功,随后是解密流量!我开始使用pynids(TCP解析和重组库)解析一些pcaps。我使用内部开发的dissect.cstruct库来解析数据结构,并开发了一个解析框架来解析像ssh这样的协议。解析框架基本上以正确的顺序将数据包送入协议解析器,因此如果客户端发送2个数据包而服务器响应3个数据包,那么这些数据包也将以相同的顺序提供给解析器。这对于维护整个协议状态很重要,解析器基本上使用SSH帧,直到遇到NEWKEYS帧,这表明下一帧已加密。解析器现在查看来自该源的流中的下一帧,并迭代提供的会话密钥以尝试解密该帧。如果成功,解析器将在此状态下安装会话密钥以解密会话中的其余帧。解析器可以处理几乎所有OpenSSH支持的加密算法。具体过程可以看下面的动图:SSH协议解析终于运行parsing,这里可以看到解密和解析一个SSH会话的过程,同时也暴露了用户的密码用于认证:DecryptedandParsedSSHSessionExampleSummary中在这篇文章中,我研究了SSH协议以及会话密钥如何存储和保存在OpenSSH的内存中,并找到了一种从内存中提取它们并在网络解析器中使用它们来解密SSH会话并将其解析为可读输出的方法。可以在以下链接中找到本研究中使用的脚本:将PythonPOC转储为SSH会话密钥;波动率2插件;波动率3插件;SSH协议解析器;更好的选择是在Wireshark和解析器中实现这个解密器。有趣的是,在我的研究过程中,我还在OpenSSH源代码的ssh_set_newkeys函数中遇到了这些注释行。具有讽刺意味的是,如果不注释这些行并将它们编译到OpenSSH二进制文件中,这项研究会困难得多。OpenSSH源代码片段本文翻译自:https://research.nccgroup.com/2020/11/11/decrypting-openssh-sessions-for-fun-and-profit/【责任编辑:赵宁宁电话:(010)68476606]