热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

【技术分享】FormGrabber恶意软件的详细分析

译者:WisFree预估稿费:200RMB投稿方式:发送邮件至linwei#360.cn,或登陆网页版在线投稿介绍作为Stormshield安全情报团队的一名新成员,我的第一个任务就是分析这款form

http://p9.qhimg.com/t015cc51bfe1f5a725c.jpg

译者:WisFree

预估稿费:200RMB

投稿方式:发送邮件至linwei#360.cn,或登陆网页版在线投稿


介绍

作为Stormshield安全情报团队的一名新成员,我的第一个任务就是分析这款form-grabber恶意软件,在这种恶意软件的帮助下,攻击者可以利用基于Web浏览器的注入方法来窃取目标用户的密码。在这篇文章中,我将跟大家详细介绍这款恶意软件的技术细节,其中的重点是Web浏览器的注入技术。

这款恶意软件已经有一定“年纪”了,因为从编译后代码的时间戳来看,这款恶意软件早在2012年就已经存在了。但是待会儿等我介绍完之后你就会发现,虽然这款恶意软件已经“上年纪”了,但它仍然能够高效地攻击很多最新版本的浏览器(32位模式)。安全研究专家Xylitol已经在VirusTotal平台上传了一份这款恶意软件的样本,但是目前互联网上还没有针对这款恶意软件的详细分析报告。由于这部分内容的缺失,这种威胁的传播方法很可能不为人所知,这也是我写这篇文章的原因。


脱壳

我们所分析的样本加了两层壳,第一层是UPX的壳,这个可以轻松脱壳。第二层同样非常简单,它利用了一种反调试技巧读取PEB中的‘BeingDebugged’标记。除此之外,这里还实现了一种反混淆技术,即样本使用了一个1字节密钥(0x0F)来进行XOR运算并输出解密后的PE域(缓冲区使用VirtualAlloc()分配)。在调用了VirtualAlloc()之后,我们可以使用下列指令来识别第二阶段的壳是否结束:

push 0x666 ; magic value checked after unpacking
push ebx   ; base address of the unpacked PE
call eax   ; leads to the unpacked PE original entry point

因此我们可以得知,这款恶意软件在执行第一个功能时需要接收两个输入参数:

脱壳后的PE基地址;

一个可当作密钥使用的值;

如果这个值不是恶意软件所定义的(0x666),那么它就会停止运行。


RC4加密

为了防止逆向分析或其他基于检测的静态分析方法,这款恶意软件使用了RC4算法来加密字符串。其中被加密的绝大多数都是与Web浏览器注入相关的DLL、函数名称或参数,它们可以使用LoadLibraryA()GetProcAddress()来动态解析导入的代码库。在地址data+0x30中有一个结构体数组,其中包含有每一个加密字符串的地址。这个结构体如下代码所示:

struct rc4_encrypted_string {
    const char *string;  // pointer to the encrypted string stored in .rdata
    unsigned int length; // length of the string
}

因此,为了解密字符串,恶意软件使用了一个函数来发送结构体数组中加密字符串的个数,并以此来推算其所在地址和长度。在一个IDA Python脚本的帮助下,我们可以轻松找到这个函数的交叉引用,并了解到字符串的个数,并执行解密。

运行脚本之后,我们可以发现很多有意思的字符串,例如DLL或函数名等等:

http://p6.qhimg.com/t01fb17a20a28f6565b.png

用于解密的RC4密钥长度为128位,其地址存放于data+0x14。在这个样本中,其密钥为:27F56A32B728364FBA109F983D148023。


主要的执行流程

这款恶意软件的主线程用来执行一个无限循环,然后在循环中枚举出目标操作系统中正在运行的所有进程,最终找出一个浏览器程序并实现线程注入。枚举过程主要使用了以下几个Windows API函数:

CreateToolhelp32Snapshot()

Process32FirstW()

Process32NextW()

这款恶意软件还会寻找任何名字符合以下字符串的进程,并尝试向其中注入一个线程:

chrome.exe

firefox.exe

opera.exe

iexplorer.exe

WebKit2WebProcess.exe (Safari)


Form-grabber线程注入

一旦其找到了匹配的进程名称,它便会调用OpenProcess()来获取目标进程的控制权。接下来,恶意软件将调用VirtualAlloc()在目标进程的地址空间中分配一个执行的内存区域。这一块内存区域可以用来存储当前运行的PE文件副本(使用WriteProcessMemory()实现)。现在,整个PE文件都会被映射到目标进程的地址空间中,最后再调用CreateRemoteThread()来运行注入的线程。

由于这款恶意软件不会向线程函数传递任何的变量,因此远程线程不需要知道它到底是在哪个进程中运行的。


内联钩子

内联钩子(Inline Hooking)是一种专门用来拦截函数调用的方法,这种方法可以执行一种旁路函数来访问原始函数的参数信息。在我们的分析样本中,它使用了这种内联钩子来拦截浏览器在发送HTTP请求时所调用的函数,并访问其中包含的敏感数据,例如用户名、密码或信用卡号等等。

为了设置内联钩子,这款恶意软件使用了一个全局结构体来存储挂钩函数的信息(存储在.data域)。这种结构体如下代码所示:

// Function pointer definition for calling HTTPSendRequestA()
typedef BOOL (*http_send_request_prototype_t)(
  _In_ HINTERNET hRequest,
  _In_ LPCTSTR   lpszHeaders,
  _In_ DWORD     dwHeadersLength,
  _In_ LPVOID    lpOptional,
  _In_ DWORD     dwOptionalLength
);
 
struct direct_injection_hook {
   http_send_request_prototype_t hooked_function; // address of Wininet.dll!HttpSendRequestA
   http_send_request_prototype_t detour_function; // address of the function written by the author
   unsigned int count_saved_bytes;                // number of bytes overriden at Wininet.dll!HttpSendRequestA
   void (*return_to_dll)(void);                   // address of user-allocated page (RWX) used to execute the original
                                                  // hooked function after execution of the detour function
}

下面是用于设置内联钩子的函数反编译版本,代码位于地址.text+0x40:

http://p8.qhimg.com/t011c616081b1ea2f33.png


挂钩后的执行流程

当设置好了钩子之后,如果iexplorer.exe调用了Wininet.dll!HttpSendRequestA,则执行流程如下:

1.       执行Wininet.dll!HttpSendRequestA的第一条指令,并跳转到旁路函数。

2.       旁路函数访问已挂钩函数的参数信息,例如HTTP Payload,并从中提取出敏感数据。

3.       旁路函数调用跳转函数(return_to_dll),并存储Wininet.dll!HttpSendRequestA执行过的所有指令,最终跳转到Wininet.dll!HttpSendRequestA+5然后执行。

4.       Wininet.dll!HttpSendRequestA剩下的指令会继续执行,直到函数返回并执行HTTP请求。

5.       旁路函数运行完之后会返回到原函数,然后调用Wininet.dll!HttpSendRequestA

完整的执行流程请大家参考下面这张图片:

http://p4.qhimg.com/t01d4a8bd58d9e363bb.png


浏览器与挂钩函数信息

下面这个表格显示的是浏览器可注入的DLL和函数名等信息:

http://p2.qhimg.com/t01d782ac7cb0f0a19b.png

某些函数是在HTTP层运行的,这也就意味着当用户浏览一个HTTPS网站时,函数钩子在将信息传递给TLS层并进行加密之前,它首先拼接出的会是HTTP请求。剩下的操作全部都会在TCP层执行,这也就意味着浏览一个HTTPS网站将会导致恶意软件挂钩TLS记录Payload并通过套接字进行发送,而这对于这款恶意软件来说没有任何的实际意义。

该恶意软件基于BaseHTTPServer所创建出的类代码如下,其主要功能就是捕获POST方法:

import BaseHTTPServer
import urlparse
 
browser_mapping = {
    0 : 'chrome.exe',
    1 : 'chrome.exe',
    2 : 'firefox.exe',
    3 : 'opera.exe',
    4 : 'WebKit2WebProcess.exe', # Safari
    5 : 'iexplorer.exe'
}
 
HOST='localhost'
PORT=80
XOR_KEY=0x07
 
class FormGrabberHTTPDecoder(BaseHTTPServer.BaseHTTPRequestHandler):
 
    def do_POST(self):
        request_line = self.requestline
        content_length = int(self.headers['Content-Length'])
        post_data = self.rfile.read(content_length)
        decoded_data = urlparse.parse_qs(post_data)
        malware_version = decoded_data['v'][0]
        injected_browser =  browser_mapping[int(decoded_data['t'][0])]
        username_hostname = decoded_data['h'][0]
        hexdumped_post_data = decoded_data['c'][0]
        xored_post_data = hexdumped_post_data.decode('hex')
        unxored_post_data = [chr(ord(c) ^ XOR_KEY) for c in xored_post_data]
        unxored_post_data = ''.join(unxored_post_data)
        d1 = ('-' * 79)
        d2 = ('*' * 79)
        print('Received HTTP request line: {}'.format(request_line))
        print('Received HTTP body:n{}n{}n{}'.format(d1, post_data, d1))
        print('Malware version: {}'.format(malware_version))
        print('Injected browser: {}'.format(injected_browser))
        print('Username and Hostname: {}'.format(username_hostname))
        print('Intercepted HTTP request:n{}n{}n{}'.format(
            d1, unxored_post_data, d1))
        print(d2)
 
if __name__ == '__main__':
    s = BaseHTTPServer.HTTPServer((HOST, PORT), FormGrabberHTTPDecoder)
s.serve_forever()

下面是脚本的输出样本,我们尝试通过facebook.com的身份验证,测试环境为IE浏览器+HTTPS:

http://p0.qhimg.com/t01dbc44cb7b30411a8.png

浏览器测试

因为这款form-grabber恶意软件在我分析的时候已经存在了五年之久了,所以我们也很想知道为什么它至今仍然可以攻击很多最新版本的浏览器。实际上在这些年里,浏览器的架构一直都在不断地升级和改进,而这款恶意软件所挂钩的部分函数现在已经不再使用了。下面这个表格所显示的内容是这款恶意软件在Windows 7操作系统(SP1,64位)中可以成功攻击的浏览器版本信息:

http://p7.qhimg.com/t0113b00f64a5e80ce7.png

有意思的是,除了Firefox之外,该恶意软件所有的函数钩子都可以有效地对所有最新版本的32位浏览器实施攻击。


总结

这是一款十分精巧的form-grabber型恶意软件,它使用了一些反调试技术,并对函数调用进行了混淆处理。除此之外,它还使用了RC4算法来加密函数以及DLL名称来执行代码库的动态导入。众所周知,内联钩子是一种用于拦截函数调用的著名技术,而且对于目前最新版本的浏览器来说这种技术仍然是非常有效的。但是,这款恶意软件的开发者并没有花时间去优化所有浏览器HTTP层的钩子函数,因此它很有可能无法针对那些通过https来发送的请求进行拦截和攻击。


额外信息

MD5: cb066c5625aa85957d6b8d4caef4e497

SHA1: bd183265938f81990260d88d3bb6652f5b435be7

SHA256: 9cdb1a336d111fd9fc2451f0bdd883f99756da12156f7e59cca9d63c1c1742ce

VirusTotal的分析报告:【传送门】


推荐阅读
  • WinMain 函数详解及示例
    本文详细介绍了 WinMain 函数的参数及其用途,并提供了一个具体的示例代码来解析 WinMain 函数的实现。 ... [详细]
  • 在Delphi7下要制作系统托盘,只能制作一个比较简单的系统托盘,因为ShellAPI文件定义的TNotifyIconData结构体是比较早的版本。定义如下:1234 ... [详细]
  • h5调用本地摄像头和麦克风一
    h5调用本地摄像头和麦克风一,Go语言社区,Golang程序员人脉社 ... [详细]
  • 兆芯X86 CPU架构的演进与现状(国产CPU系列)
    本文详细介绍了兆芯X86 CPU架构的发展历程,从公司成立背景到关键技术授权,再到具体芯片架构的演进,全面解析了兆芯在国产CPU领域的贡献与挑战。 ... [详细]
  • 2020年9月15日,Oracle正式发布了最新的JDK 15版本。本次更新带来了许多新特性,包括隐藏类、EdDSA签名算法、模式匹配、记录类、封闭类和文本块等。 ... [详细]
  • 高端存储技术演进与趋势
    本文探讨了高端存储技术的发展趋势,包括松耦合架构、虚拟化、高性能、高安全性和智能化等方面。同时,分析了全闪存阵列和中端存储集群对高端存储市场的冲击,以及高端存储在不同应用场景中的发展趋势。 ... [详细]
  • 数据结构第三章,栈、队列、数组,期末不挂科指南,第3篇
    数据结构第三章,栈、队列、数组,期末不挂科指南,第3篇,Go语言社区,Golang程序员人脉社 ... [详细]
  • 本文详细介绍了Java反射机制的基本概念、获取Class对象的方法、反射的主要功能及其在实际开发中的应用。通过具体示例,帮助读者更好地理解和使用Java反射。 ... [详细]
  • 本文介绍如何使用线段树解决洛谷 P1531 我讨厌它问题,重点在于单点更新和区间查询最大值。 ... [详细]
  • 在分析Android的Audio系统时,我们对mpAudioPolicy->get_input进行了详细探讨,发现其背后涉及的机制相当复杂。本文将详细介绍这一过程及其背后的实现细节。 ... [详细]
  • 开机自启动的几种方式
    0x01快速自启动目录快速启动目录自启动方式源于Windows中的一个目录,这个目录一般叫启动或者Startup。位于该目录下的PE文件会在开机后进行自启动 ... [详细]
  • 本文总结了一些开发中常见的问题及其解决方案,包括特性过滤器的使用、NuGet程序集版本冲突、线程存储、溢出检查、ThreadPool的最大线程数设置、Redis使用中的问题以及Task.Result和Task.GetAwaiter().GetResult()的区别。 ... [详细]
  • 题目《BZOJ2654: Tree》的时间限制为30秒,内存限制为512MB。该问题通过结合二分查找和Kruskal算法,提供了一种高效的优化解决方案。具体而言,利用二分查找缩小解的范围,再通过Kruskal算法构建最小生成树,从而在复杂度上实现了显著的优化。此方法不仅提高了算法的效率,还确保了在大规模数据集上的稳定性能。 ... [详细]
  • 在尝试对 QQmlPropertyMap 类进行测试驱动开发时,发现其派生类中无法正常调用槽函数或 Q_INVOKABLE 方法。这可能是由于 QQmlPropertyMap 的内部实现机制导致的,需要进一步研究以找到解决方案。 ... [详细]
  • 本文提出了一种基于栈结构的高效四则运算表达式求值方法。该方法能够处理包含加、减、乘、除运算符以及十进制整数和小括号的算术表达式。通过定义和实现栈的基本操作,如入栈、出栈和判空等,算法能够准确地解析并计算输入的表达式,最终输出其计算结果。此方法不仅提高了计算效率,还增强了对复杂表达式的处理能力。 ... [详细]
author-avatar
影子张被占用
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有