1|前言
本着研究学习的目的,在开源渠道获取国家计算机病毒应急处理中心关于《西北工业大学遭美国NSA网络攻击事件调查报告》中的在类unix操作系统中使用主战武器之一。
相比分析木马功能,分析其通讯模块的加解密操作可能更有意义,此篇文章主要记录此款木马的密钥协商、加解密以及双向认证流程。
2|背景
样本在完成部分参数解密、信号处理机制置定等的初始化操作后,fork了子进程,在子进程中创建了socket套接字描述符,并修改属性为非阻塞模式,与攻击者端完成的tcp链接建立后,后续做通讯密钥协商和双向验证等操作。
2.1|RSA初始化
类似TLS,木马使用RSA非对称加密算法,交换位于受害者端生成的RC6对称加密算法的iv、初始的key值,完成密钥协商后使用RC6对称加密通讯。
在密钥协商阶段,木马首先异或解密了参数m、mu、exp、cli、svr,前三个是RSA运算相关的模数m(即大素数p和q的乘积)、模数的逆mu、以及指数exp(用于加密的公钥(m, e),用于加密rc6相关算子的)。后两个cli、svr是分别用于客户端、服务端验证(后续验证模块中会做说明)的参数。
这些将被存放于整数数组中,参与RSA的加密运算,后续就是的RSA加密,就不详细叙述。
加密结果存放于内存中等待被发往攻击者控制端。控制端通过私钥(d、m)解密获取RC6对称算法相关参数。
2.2|RC6初始化
2.2.1|生成RC6初始密钥
进入RC6的初始化程序,首先调用文件“/dev/random”生成255字节的随机数,利用sha1算法hash生成的随机数,获得20字节的计算结果。
参数buf即生成的随机数地址指针,sha1计算结果存于参数v10中,v10信息如下(地址0xfffeb018起始的20字节)。
将sha1计算结果的后16个字节,作为RC6轮计算的初始key值。
这里说明一些sha1算法的识别,开源的sha1算法库通常包含一些固定的初始变量,例如0x67452301、0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0。关于为什么使用这样固定的初始变量,查阅了一些资料,并没有获得很明确的答案,更多的人说这组数据可以使得算法获取较低的撞库率,不是主题具体就不再深入了。
既然是固定字节,可以在函数中全局搜索,或者使用IDA插件可以直接定位,从而识别算法。
2.2.2|生成RC6初始IV值
同样调用文件“/dev/random”生成16字节随机数,利用这16字节随机数作为IV(初始向量)值参与RC6的块异或计算。IV值如下。
2.2.3|RC6密钥扩展
在RC6中密钥扩展的目的是将输入的初始密钥转化为用于轮函数的轮密钥数组,这个数组将被用来做加解密对称计算。
关于RC6的算法识别,就在这个阶段里,0x61C88647 和 0xB7E15163 是用于混淆密钥和数据的固定常数,他们在算法领域的叫黄金分割等数。这样可以很好的帮助我们在逆向分析中识别算法
所以密钥扩展函数需要导入初始化的rc6_key,第三个参数是分配在堆中的,在这里用来接收密钥扩展的计算结果,该计算结果即轮密钥数组,用于后续对称加解密。
密钥扩展结果如图。
随后将IV值拼接在密钥结果后。
2.2.4|密钥交互
受控端将RC6_iv值、rsa初始化值、经过RSA加密的RC6_key值,共计0x110字节发送给控制端,用于通讯两端后续的正确做对称加解密。
利用wirte将要发送的数据写入socket发送缓冲区中,之前生成的套接字描述符中定义了相关属性,随后会经过socket读取并发包。
动态调试即将发送的数据如图,其中红框为RC6_iv值,剩余字节中包含了密文的RC6_key以及RSA相关的一些参数,在服务端中可以解出。
抓取数据包看看。
0x110即272字节,上下两图即受控端内存中和通讯流量中的相同数据,有大小端序的变化,x86架构用的小端序,多数主要网络协议(例如tcp/ip)用的大端序。
这里看看攻击者的控制台,攻击者端程序通过RSA解密获取了受害者端随机生成的RC6_iv、RC6_key,利用相同的算法就可以完成rc6加解密计算了,攻击者加密发送了第一个验证字符串,看来上图数据包中蓝色二进制流是一个验证机制的流。
2.3|控制端与被控制端双向验证
2.3.1|被控制端验证
利用read函数在socket套接字描述符中读取接收到的前16个字节。
对比通信流量可以看到是一致的。
利用rc6解密这16字节,0x8a000000。
解密后读取剩余字节,继续解密,第二段密文长度是0x8a,看来数据包的前16字节解密是剩余密文的长度。
第二段密文解密如下。
这里并非最后的明文,这个流的头是“425a”,为Bzip2 文件的开头包含两个字节的魔数,即ASCII码中的字母 “B” 和 “Z”,Bzip2 文件是一种用于数据压缩的文件格式。
后续就调用了木马中内置的解压缩算法解密,获取明文的二进制流。
随后将明文和长度写入当前函数的参数中,用于在父函数中操作。
这里明文实际上是验证机制,木马获取这个明文信息后,用来与之前在RSA初始化阶段生成的二进制流做比对,相同才继续往后执行,不同则直接善后。
动态调试如图。
2.3.2|控制端验证
在RSA初始化阶段中还生成了一个二进制流,这个参数是被用来发往控制端对比验证的。
利用“/dev/random”生成随机长度、随机内容的二进制流,填充再验证参数的后变。
将明文做Bzip2压缩运算,获取一个42 5a开头的二进制流。
将经过压缩算法的二进制流拿去做rc6加密,密文及长度信息如下。
再将密文写入socket套接字描述符中发送。
捕获通讯流量如图。
来看看控制端可知已经完成了校验流程。
完成善后工作退出程序。
2.4|受害端上线
2.4.1|获取系统基本信息发往控制端-指令码0x28
完成密钥交互和双向验证机制后,受害者端就发送上线数据包。
后续受害者端所有的发包,都是先经过压缩算法,再做rc6对称加密,再写入socket套接字描述符中被发送。
首先向攻击者端发送密文版本号。动态调试如图。
攻击者端解密并打印版本号在终端,并且接发送了密文指令码。
受害者端解密返回信息,指令码很简短0x28000000。
利用汇编指令bswap完成大小端序转换,即0x28。
利用switch case…循环结构接收和执行攻击者端发送的命令。
做0x28的跳转,该指令码功能为获取系统基本信息,获取到的基本信息如下,同样利用压缩和rc6加密后发送。
看看攻击端口面板。
攻击端接收到后同样循环发送指令。
2.4.2|获取当前路径-指令码0x18
解密获取指令码0x18,该指令码功能为获取系统当前路径发送至攻击者端。执行完看看攻击者端口面板。
2.4.3|获取当前进程pid-指令码0x19
解密获取指令码0x19,该指令码功能为获取当前进程的pid信息,攻击者端口面板如下。
对比着看看
完成这一步后,攻击者端就可以自由输入指令了,上线结束。
后续就可以自由操作了。
原文始发于微信公众号(帅仔回忆录):间谍软件加解密通讯模块分析