Bad Honey in the Honey Pot

Bad Honey in the Honey Pot

Uploading files to Analysis Environments to better understand the Environments for Anti VM and Sandbox using Discord Web Hook for Communication

·

12 min read

Submitting files to services such as Hybrid Analysis, Virus total and AnyRun it had me wondering how difficult would it be to detect such environments. I seen many malware samples, many had Anti Virtualization method to help Detect the environment if it was a VM and or Sandbox. I've also seen VM Hardening Guides, one of them being from "hfiref0x" user on GitHub and another Contributed and or forked from "hzqst". After following such guides they seem to work pretty well, you will notice it takes away certain features like drag and dropping files into the VM as that is part of guest editions for VirtualBox.
To test such environments you can use a couple tools either "PaFish" or "al-khaser" from "LordNoteworthy". The Results from "PaFish" seem to pass very well but "al-khaser" on the other hand shows some detections. I don't have screenshots as this was sometime ago before I gave up since doing a full harden just doesn't seem possible there for I lost interest.

Analysis Machines

So in todays time we have services such as "Virus total" , "Hybrid Analysis" and "Any Run" to name a few. The goal of these services is to provide you a environment for testing malware and or files to come to the conclusion if they are malicious. They submit the files to Anti Virus scanners so they can scan and provide a result on their end. Since they submit the files to them they often get kept to be analysed later on running them once again inside a virtualized environment to come to a better conclusion if anything was missed. Hybrid Analysis does the same similar thing with another service but they are more known for running it into their own Sandbox provided by Crowd Strike to come to a conclusion if the file is malicious or not. Virustotal has on site Sandboxes as well such as "Zenbox", "Jujubox" and "CAPE". Finally AnyRun typical just provides the Sandbox portion not much of scanning with third party scanners. What these all have in common is an controlled environment to run unknown files onto, and get results if they appear to be malicious as well as runtime analysis of everything the files does, to connections, dropped files, registry edit etc.
They are aware of such detection methods, methods can extend from, Virtual Machine Files, Processes, Services, System Info Attributes, down to how they handle certain Assembly Instructions. They harden against a lot of this there for using tradition methods to detect such environment can prove trivial.
Detecting Files created and or used by the Virtualization platform are hidden, Processes as well, System Info Attributes usually are spoofed, hell even the folder where recent files are stored are spoofed. They fill the recent folder with Random Files, they fill the Desktop with Random Files, they even have random processes open at time. System Manufacturer as well as Name what not are randomized, Usernames seem to be Generic, MAC Addresses can change, Disk Size is randomized as well as Memory. So lets see what we can detect then !

Discord Web Hook

We want to better understand these Machines to see what can be detected. Now understanding basics of them we know they provide screenshots after the target file is done being analysed. We can leverage that by creating a Payload that will test the Environment using Methods from "al-khaser" since that tool seems to have most methods. We will pick methods that appear to fail in the environment I hardened and those are WMI Methods. It these virtual environments they can be missing instances from WMI Classes such as "Win32_Fan". Since virtual machines don't typically have Fans the WMI class that stores that information typically will be empty, as well as many more classes in the WMI.

"Windows Management Instrumentation (WMI) is a Microsoft-designed framework that allows software and system components to manage and access information about the state of Windows operating systems."

Other classes that appear with zero instances but have instances on real machines are:
Win32_VoltageProbe
Win32_MemoryDevice
Win32_PhysicalMemory
Win32_CacheMemory
Win32_PerfFormattedData_Counters_ThermalZoneInformation"
Win32_Fan
Win32_SMBIOSMemory
Win32_PortConnector
Win32_MemoryArray
CIM_Memory
CIM_Sensor
CIM_NumericSensor
CIM_TemperatureSensor
CIM_PhysicalConnector

Now that we know what we are looking for we want to see those have instances, before we do we much test it on a Real Machine to see if any of those can have false positives, meaning someone with a Real machine still appears to have Zero Instances for those objects.
After creating a PoC payload that will write to the Console the Count for each Class instance, I gave it to some people to run it on their real machines and show me a screenshot of the results.

As you see it appears [0] for "CIM_TemperatureSensor" and "CIM_NumericSensor" so we should rules those out since those can cause false positives.
My buddy also made his own version similar to the one above but showing True or False if it passed (if the instances appear to be more than 0 then true for passing else false). He already ran his version on the Service called Hybrid Analysis. I also ran mine onto Hybrid Analysis, looking at his results first the machine passes all WMI Tests ? Looking at mine, well, shows nothing... I suspected that would happen, It would run the sample but not screenshot.

This can be due to the fact it never triggered the virtual environment to create a screenshot, such as a message box pop up or something that would invoke that behaviour. On top of that its not good friends version is saying the machine is passing. He wrote his in C++ using the WMI Win Apis I created mine in C# / .NET to make it easy to manage. Lets look at the code that grabs the Instances from WMI.

Running it on my Real Machine shows Proper results.

    public class DiscordWebhook {
        private readonly string _webhookUrl;
        private readonly string _tag;
        private readonly string _id;

        public string GetID() => _id;
        public string GetTAG() => _tag;

        public DiscordWebhook(string webhookUrl, string tag, string id) {
            _webhookUrl = webhookUrl;
            _tag = tag;
            _id = id;
        }

        public string GetHeader(string kind) {
            return new StringBuilder()
                .Append("   =============[HEADER]=============\n")
                .Append("[TAG][").Append(_tag).Append("]\n")
                .Append("[UID][").Append(_id).Append("]\n")
                .Append("[TSK][").Append(kind).Append("]\n")
                .Append("   =============[DATA]=============\n\n")
                .ToString();
        }

        public async Task UploadBytesFileAsync(byte[] bys, string message, string fileName) {
            using (var client = new HttpClient())  {
                using (var content = new MultipartFormDataContent()) {
                    content.Add(new StringContent(message), "content");
                    var fileContent = new ByteArrayContent(bys);
                    fileContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("text/plain");
                    content.Add(fileContent, "file", fileName);
                    var response = await client.PostAsync(_webhookUrl, content);
                    if (!response.IsSuccessStatusCode) {
                        throw new Exception($"Failed to upload file: {response.StatusCode}");
                    }
                }
            }
        }

        public async Task SendImageAsync(string imagePath, string message) {
            using (var client = new HttpClient()) {
                using (var content = new MultipartFormDataContent()) {
                    content.Add(new StringContent(message), "content");
                    byte[] fileBytes = File.ReadAllBytes(imagePath);
                    var fileContent = new ByteArrayContent(fileBytes);
                    fileContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("image/png"); // Change to the appropriate media type if not PNG
                    content.Add(fileContent, "file", Path.GetFileName(imagePath));
                    var response = await client.PostAsync(_webhookUrl, content);
                    if (!response.IsSuccessStatusCode){
                        throw new Exception($"Failed to upload image: {response.StatusCode}");
                    }
                }
            }
        }

        public async Task UploadTextFileAsync(string filePath, string message) {
            using (var client = new HttpClient()) {
                using (var content = new MultipartFormDataContent()) {
                    content.Add(new StringContent(message), "content");
                    byte[] fileBytes = File.ReadAllBytes(filePath);
                    var fileContent = new ByteArrayContent(fileBytes);
                    fileContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("text/plain");
                    content.Add(fileContent, "file", Path.GetFileName(filePath));
                    var response = await client.PostAsync(_webhookUrl, content);
                    if (!response.IsSuccessStatusCode) {
                        throw new Exception($"Failed to upload file: {response.StatusCode}");
                    }
                }
            }
        }

        public async Task SendMessageAsync(string message, string kind) {
            var header = GetHeader(kind);
            foreach (var msg in SplitMessage(message, header)) {
                using (var client = new HttpClient()) {
                    var payload = new Payload { content = msg };
                    var jsonPayload = SerializeToJson(payload);
                    var httpContent = new StringContent(jsonPayload, Encoding.UTF8, "application/json");
                    Thread.Sleep(500);
                    var response = await client.PostAsync(_webhookUrl, httpContent);
                    if (!response.IsSuccessStatusCode) {
                        Console.WriteLine("Failed to send message: " + response.StatusCode);
                    }
                }
            }
        }

        private string SerializeToJson(Payload payload)  {
            var serializer = new DataContractJsonSerializer(typeof(Payload));
            using (var memoryStream = new MemoryStream()) {
                serializer.WriteObject(memoryStream, payload);
                return Encoding.UTF8.GetString(memoryStream.ToArray());
            }
        }

        public static IEnumerable<string> SplitMessage(string message, string header)  {
            int maxSize = 1900;
            int sizeCanTake = maxSize - header.Length;
            if (message.Length + header.Length < maxSize) {
                yield return header + message;
            }
            else
            {
                int leftToSend = message.Length;
                int taken = 0;
                while (leftToSend > 0)
                {
                    int szTake = leftToSend > sizeCanTake ? sizeCanTake : leftToSend;
                    string block = header + message.Substring(taken, szTake);
                    yield return block;
                    taken += szTake;
                    leftToSend -= szTake;
                }
            }
        }

        [DataContract]
        private class Payload
        {
            [DataMember(Name = "content")]
            public string content { get; set; }
        }
    }

So in this new Sample we will modify it to push a Message Box to hopefully invoke the Machine to Screenshot. On top of that we will add a extra feature, Discord, we will use Discord web hook to send the Results of the machine to the hook channel. The above code shows the Discord Web hook feature, I have options send via File since sending messages proved over time to hit Rate Limits since Data exceeds 2000 chars many times over. So we create a byte stream of the text result and submit it to Web hook as a File.

Now lets Run it! Within settings for Hybrid I also increased the Time to Analyse the File, and selected Heavy Anti Evasion Box for the second time Analysis. We also submitted it to Virus Total as VT shows the same thing a black box so I submitted the new sample with the Text Box and Web Hook. For VT we tweaked the Code a bit nothing crazy.

Hybrid Analysis Result

Virus Total

AnyRun
Here are the results being piped to the Discord Web Hook

Now looking at the results we see they all have in common they fail the "CIM_Memory" test, some pass the other tests some don't, it is odd how Hybrid Fails all the tests YET for my buddy who wrote his code in C++ it passed. My theory is they are hooking some API in C++ versus the .NET method might be using something that isn't being hooked. Lets look deeper , the next phase of testing we want to do is check for System Information Attributes check for Running Processes Services, Computer Information, Video Card Information, Disk Information, Accounts, Recent Files everything we can send to the Hook. We also notice on the Hybrid Analysis machines it has a custom wallpaper unique to them...
So lets start creating our payload, Our first issue we will run into is the amount of data that can be send via message, splitting it into checks eventually rate limits us so new approach is to pipe a byte stream of the full result to the web hook as a file.
Using the WMI we use these following classes to either get each Instance of item and or get the main instance item information.

Win32_ComputerSystem
Win32_OperatingSystem
Win32_Desktop
Win32_DiskDrive
Win32_LogicalDisk
Win32_PnPEntity
CIM_PhysicalConnector
Win32_Service
Win32_UserAccount
Win32_VideoController
Win32_Volume

We have created functions to retrieve each of those classes get information on each instance, information that we want such as "Name" fields, "Description" fields what not to see if we can identify anything that ties with a virtualized environment.
We also want to get each Network Card Mac Address, Gateway, Internal IP Address, Name, ID whatever else we can grab from it. We also want Current logged in User Name, Domain and Location of Wallpaper as well as the actual Image of the Wallpaper with bytes. In our discord web hook class we have a function to upload a image as well as a file and a stream to file.

Creating the payload the re-submitting it to Virus Total and Hybrid Analysis (heavy anti evasion and regular environment) our results start rolling in into our web hook channels. Each time it is ran we link it with a GUID so we can help identify what data is what, also each Service has there own channel with there web hook. So a Virus Total Text channel and Hybrid analysis Test channel.

Sometimes the results don't appear to upload wallpaper, for the Hybrid Analysis it didn't upload that suspected generic static wallpaper they always use. It does upload the "Tests.txt" so we are getting Test Results.

Something we notice off the spot is the wallpaper is located in a folder called "VxStream" a Folder often used by these analysis machines.
Mac address can appear "00000000000000E0"
Something more weird is that the "Win32_OperatingSystem" fields are all Empty "[]"
We also don't see the information grabbed for the other WMI Classes ....
It seems that Hybrid Analysis is blocking the WMI ? Almost like if they cant control the result then they will just block it .

Looking at the VT Results we do get the WMI Information back.

Virus Total submits files to Antivirus products so we can also get results from them.
Lets analyse all the incoming results now. For Hybrid we did get a machine that was not blocking WMI, for Virus Total we got few different machines.

Results

Looking at the results here are some irregularities I have noticed.
Of course they all fail the WMI Instance Count Tests at least for "CIM_Memory".

Virus Total Processes or Files: (Win32_Process)
"C:\Program Files (x86)\JUZtrnvMLtWKDpUoRnLNEQYslFVHbnYjoysgBUjkpEEqp\DuHkhTjyUhXMuNTgqjSuKucaFWzK.exe"
"Z:\BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB.exe" "c:\users\walker\appdata\local\temp\3K6WXTU2DA2Q0ESU.exe"
"C:\Windows\system32\cmd.exe /c c:\tools\winaube-stripped.cmd --verbosity=0 --alsologtostderr=1 --log_file=c:\Users\John\winaube.log --port=17203" "python.exe -OO -u -m a.b.a.a.a.a.a --verbosity=0 --alsologtostderr=1 --log_file=c:\Users\John\winaube.log --port=1720"

I've even got a machine that was running Metasploit programs.
"C:\Program Files\Meterpreter\metsvc-server.exe"
"C:\Program Files\Meterpreter\metsvc.exe"

Virus Total Video Card Attributes: (Win32_VideoController)
SeaBIOS Developers
Bochs/Plex86 Developers
Bochs/Plex86 VBE(C) 2003 http:/

Virus Total PnP Device IDs: (Win32_PnPEntity)
"ACPI\QEMU0002\3&11583659&0" (Contains QEMU)

Virus Total Manufacturer Attributes: (Win32_OperatingSystem)
GRWZEbwPoKyNd19 (Some random non existent manufacturer)
QEMU

Virtus Total Network Card Attributes:
"Red Hat VirtIO Ethernet Adapter #2"

Hybrid Analysis Disk Serial Number:
"beaf1291"
" " (Just clear Text)

Hybrid Analysis Wallpaper Attributes:
"C:\VxStream\desktop.jpg" (VxStream)

Hybrid Analysis Processes or Files: (Win32_Process)
"C:\tmpzioe5b\bin\imdisk\x64\imdsksvc.exe"

Hybrid Analysis Physical Connectors: (Win32_PhyisicalConnector)
There only seems to be USB and LAN Connectors never Hard Drives, SSDs,
or even HDMI / Video Input Physical Connectors.

Hybrid Analysis PnP Device IDs: (Win32_PnPEntity)
"ACPI\QEMU0002\3&13C0B0C5&0" (Contains QEMU)
"ROOT\RDP_KBD\0000" "Terminal Server Keyboard Driver" (Virtual Server Keyboard)

Hybrid Analysis User Names:
They often can be randomized strings like "OyT0LC33iFQHM1j" "HVms4SH"

Finally:
They all have in Common Missing Drive Ports / Slots (SLC,PCI,SATA,M.2) (CIM_Slot) / (CIM_PhysicalConnector)
System information as well seems to be inconsistent such as the machine being a "OptiPlex 7010" with a "Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz" Despite the OptiPlex usually being sold with "i7-3770 3.4 GHz Turbo 3.9 GHz". Main giveaway is the lack of actual Slots / Physical Device Slots. (CIM_Memory) Will also often fail.

Despite it being more focused on Hybrid Analysis and VT it will often carry out to other QEMU / Virtualized environments as they all seem to inherit similar configs, even after getting new 'tests' in within the web hook from third party scanner environments they all still had similar inconsistencies listed above.

We see Video Controller devices that are only present on virtual environments, and may more attributes that point to a virtual environment. That being said using those attributes above you can create detection methods for Virus Total machines, Hybrid Analysis machines and or AnyRun most likely more as well. Looking deeper into WMI Classes such as "CIM_Memory" it 'SEEMS' impossible to spoof it ? I am not 100% sure obviously using Hooks can accomplish your task but spoofing it via system wide seems to be a challenge for these virtual environments.

Combing these methods as if of (5-27-2024) will Result in complete detection in the following Virtualized Environments:
Hybrid Analysis (Crowd Strike Sandbox)
VirusTotal (Zen box, Cape, Juju box) and Including its AV brands such as "Kaspersky"
AnyRun
Triage (They have very poor hardening, a lot of QEMU Strings)
and Many more !......

Source code for the Payload:
https://github.com/0bbedCode/DetectDetect

More Anti VM Detection:
https://github.com/zhaodice/qemu-anti-detection
https://forum.proxmox.com/threads/anti-cheat-kvm-settings.121249/#post-526947
https://forum.proxmox.com/threads/windows-11-vm-for-gaming-setup-guide.137718/
https://www.unknowncheats.me/forum/pubg-mobile/516523-emulator-detection-bypass-x86-hook-qemu-using-anogs-sdk.html