我原本认为,在听音乐这件事上,最不需要的就是提心吊胆了。直到又一例隐藏恶意代码的wav音频文件曝光,隐写术的魔爪终于从png和jpg伸向wav文件。
10月17日,BlackBerry Cylance威胁研究人员最新发现:wav音频文件中嵌入模糊恶意代码。
播放时,你听到的wav文件发出的音乐没有明显的毛刺或质量问题,甚至还有点动听。背地里,音频加载程序解码并执行音频数据中的恶意代码,目标很清晰,就是挖矿。
无独有偶,俄罗斯Turla也用过这一招。今年6月,俄罗斯网络间谍组织Turla就创意性地将恶意代码隐藏在W**音频文件中并传输至攻击目标,这也是全球首个用W**文件传播恶意代码的攻击活动。有点不同的是,人家Turla组织不挖矿,而是用W**音频文件发起的国家级攻击活动,直接指向网络间谍入侵。
Turla组织颠覆了以往文本、图片、链接等形式的局限,拉开了用W**音频文件传播恶意代码的大幕。
继Turla组织的间谍行动后,其他黑客也效仿了起来。
BlackBerry Cylance所发现的是全球首例用W**音频隐写术加密挖矿的网络攻击。受感染主机在下载并使用特定wav加载程序加载W**文件后,会绕开电脑杀毒软件与防火墙,安装XMRrig加密货币矿工应用程序,变身“挖矿机”。
BlackBerry Cylance研究与情报副总裁还补充说,现在Windows桌面和服务器上都发现这种隐写恶意代码的W**文件。简言之,个人及企业用户已暴露在了隐写术加持下的恶意攻击威胁中。
什么是隐写术?
安全员都知道,隐写术就是指将信息隐藏在另一种数据介质中的技术。例如,将纯文本隐藏在图像中,又例如将恶意代码隐藏在音频中。
这挺鸡贼的,因为通过这种手段,暗藏恶意代码的文件就可以躲开安全软件的查杀拦截,甚至“洗白”享受白名单待遇,长久潜伏而不被发现。
隐写术在恶意程序开发中已经流行了十多年,最近开始,隐写术开始从PNG、JPG图片蔓延至W**音频文件。
我们以BlackberryCylance披露的报告威力进行分析,这类wav文件加载器可以分为以下三类:
采用最低有效位(LSB)隐写术的加载程序解码并执行PE文件。
加载程序采用基于rand()的解码算法来解码和执行PE文件。
加载程序采用基于rand()的解码算法来解码和执行shellcode。
第一类:隐写术PE加载器
第一类加载程序采用隐写术从W**文件中提取可执行内容。
隐写术是一种在另一个文件中隐藏文件或消息的做法,一般情况下不会引起对目标文件的怀疑。攻击者大量使用隐写技术来隐藏数据。
实际上,BlackBerry Cylance于4月发布了一份报告,该报告描述了海莲花组织如何利用隐写术来隐藏图像文件中的恶意后门。本文分析的样本使用了最低有效位隐写术(LSB)将恶意代码隐藏在音频文件中,其中单个字节的最右位包含恶意代码。
样本信息如下:
技术细节:
这个加载器读取wav文件头的最后四个字节,这四个字节表示wav文件中存储数据的大小,在这个恶意的wav文件里,这个大小是15,179,552 字节。
图:W**文件头-数据大小
在下面的代码片段中,加载程序读取这四个字节,并依此分配内存空间。然后,它读取数据并关闭W**文件。最后,do-while循环遍历前64个字节。按照下图所示的算法进行解码。
图:解码文件大小
显而易见,循环是在计数器<32且计数器每次迭代递增1时执行的。另外,data_offset表示编码数据内的偏移量,其值每次迭代从零开始递增2。此循环将覆盖数据的前64个字节(32* 2):
图:W**文件数据-64字节
对于每个已处理的字节,加载程序将提取LSB并将其分配给decoded_size中的相应位位置,从31(即最左边的位)开始,并在每次迭代时递减1,并将此算法应用于数据的前64个字节。
接下来,分配大小为decoded_size的存储器,并且执行各种计算。修改了默认标签名称,以指示所有计算的结果。这些数字将在即将到来的解码循环中用作已编码数据的偏移量:
图:分配内存并计算偏移量
按照上面的代码,do-while循环开始解码其余的编码数据:
图:解码文件内容
与前一个解码循环类似,此循环提取每个其他字节的LSB。但是,区别就在于它起始于最低位(最右一位)。每次迭代都从8个字节中提取LSB,以形成8位(1个字节)的解码数据。
如果应用此算法生成两个字节的解码数据,就需要32个编码字节。因为8位组成一个解码字节,并且每两个编码字节提取一个LSB,所以生成两个解码字节就需要32个编码字节(2个解码字节*每个解码字节8个LSB * 2 = 32):
图:32个字节的编码数据
解码数据的第一个字节将产生如下(不包括跳过的编码字节):
十六进制 | 二进制(带下划线的是LSB) | 位位置 | LSB分配到位位置 |
---|---|---|---|
01 | 0000000 1 | 0 | 0000000 1 |
00 | 0000000 0 | 1 | 000000 0 1 |
FF | 1111111 1 | 2 | 00000 1 01 |
FF | 1111111 1 | 3 | 0000 1 101 |
FC | 1111110 0 | 4 | 000 0 1101 |
FC | 1111110 0 | 5 | 00 0 01101 |
FD | 1111110 1 | 6 | 0 1 001101 |
FC | 1111110 0 | 7 | 0 1001101 |
解码数据的第二个字节产生如下(同样,不包括跳过的编码字节):
十六进制 | 二进制(带下划线的是LSB) | 位位置 | LSB分配到位位置 |
---|---|---|---|
FE | 1111111 0 | 0 | 0000000 0 |
FF | 1111111 1 | 1 | 000000 1 0 |
00 | 0000000 0 | 2 | 00000 0 10 |
01 | 0000000 1 | 3 | 0000 1 010 |
FF | 1111111 1 | 4 | 000 1 1010 |
FE | 1111111 0 | 5 | 00 0 11010 |
FB | 1111101 1 | 6 | 0 1 011010 |
FA | 1111101 0 | 7 | 0 1011010 |
结果,前两个字节分别具有二进制值01001101和01011010,两个字节的十六进制表示为0x4D5A,指的是PE文件开头众所周知的“ MZ”。
do-while循环将继续进行迭代,直到在内存中生成XMRigDLL。最后,将解码后的DLL映射到内存中,并执行“start”导出函数,开始挖矿。
第二类 基于Rand()的PE加载程序
第二类加载器使用基于rand()的解码算法来隐藏PE文件。
样本信息如下:
技术细节:
执行后,此加载程序将读取W**文件头,提取数据大小,相应地分配内存,并将W**数据存储在新分配的内存中。接下来,加载器解码W**文件的数据内容:
图:基于Rand()的PE加载器解码循环
注意,size_of_data表示从W**头中提取的数据大小,wave_data包含编码的W**数据的地址。加载程序调用srand函数和rand函数,从W**数据中提取PE文件。
为什么选择这两个函数呢?因为当srand函数接收一个固定的值作为种子的时候,每次调用rand函数就会生成一个固定的伪随机数。
此时,do-while循环遍历编码数据的每个字节,用从编码字节中减去rand()生成的伪随机数替换该字节。例如,让我们解码此处显示的数据的前两个字节:
使用本节分析的加载器和W**文件,下表显示srand()种子值为0x309的前两次迭代的值:
循环运行 | W**数据(字节) | rand()输出(低字节) | 区别 |
---|---|---|---|
1 | 0x5C | 0x0F | 0x5C-0x0F = 0x4D |
2 | 0x99 | 0x3F | 0x99-0x3F = 0x5A |
前两个字节代表通常在PE文件开头出现的“ MZ”。循环遍历所有数据字节后,就解码出XMRig Monero CPU64位挖矿DLL。生成的DLL与Song.wav解码的DLL仅相差四个字节:
图:解码后的click.wav与Song.wav
虽然不清楚这些字节为何不同,但是它们对DLL的功能没有影响,因此这两个XMRig DLL文件实际上是相同的。
接下来,加载器获取在命令行中指定的导出函数地址。如果存在,则加载器将启动一个线程来执行它:
图:确定导出和启动线程的地址以执行它
第三类:基于Rand()的Shellcode加载器
此方式和上述的方式及原理完全相同,不同点仅仅是前者加载的PE文件而后者加载的Shellcode
零日反思
网络威胁的演进,从来都是多维立体的。
隐写术从文本、图片扩散至音频,就是最直观的例子。
而隐写术+恶意代码+音频多种形式融合的网络威胁手段,也不断提醒着我们,网络空间威胁会越来越复杂,单纯的攻防策略已难以有效守护网络空间的一片安宁。
参考资料:
BlackBerry Cylance《恶意负载-隐藏在W**下》
*本文作者:0day情报局,转载请注明来自FreeBuf.COM