The FDA’s New Cybersecurity Guidance for Medical Devices Reminds Us That Safety & Security Go Hand in Hand

It’s hard to believe, but medical device manufacturers who are subject to Food and Drug Administration premarket approval — the FDA process of review to evaluate the safety and effectiveness of Class III medical devices — are still operating under the FDA’s original medical device cybersecurity guidance from 2014 and a subsequent update in 2018. But that is about to change in a major way.

Instead of finalizing the 2018 premarket cybersecurity draft guidance, the FDA has decided to issue a new 2022 version to reflect the rapid evolution of cybersecurity, incorporating a new set of quality system regulations (QSRs) with significant changes to its 2018 predecessor.

New FDA Draft Guidance
The new draft guidance, titled “Cybersecurity in Medical Devices: Quality System Considerations and Content of Premarket Submissions,” deals with myriad design, labeling, and documentation issues that will have to be addressed by medical device manufacturers before their new devices can gain FDA premarket approval.

The FDA’s original guidance on cybersecurity was just nine pages while the 2022 version swells to 50 pages, reflecting advancements in the cybersecurity ecosystem and best practices. It appears that, when approving connected medical devices for market, the FDA will be taking a long look at how cybersecurity is implemented, especially regarding levels of risk to patient safety.

Updated Regulations: Why Now?
Requiring greater cybersecurity measures to protect medical devices and their operational and patient data is vital since the healthcare industry has become a massive target of cyberattacks. Data breaches hit an all-time high in 2021, exposing a record volume of protected health information. Besides pilfering data, a growing number of breaches attempt to disrupt the smooth operation of medical devices like computed tomography and magnetic resonance imaging machines, potentially causing incorrect diagnoses, unnecessary medical procedures, or direct harm to patients.

The American Hospital Association’s senior adviser for cybersecurity and risk has stated that medical devices used in hospital rooms suffer from an average of 6.2 vulnerabilities. As devices become more complex and interconnected, opportunities for cyberattackers to exploit vulnerabilities are becoming greater, hence the need for updated regulations.

Incorporating Cybersecurity into Quality System Regulations to Boost Safety
With the new guidance, the FDA seeks to ensure that the next generation of medical devices will be far safer and secure throughout the entire device life cycle, from premarket and throughout the entire useful life, beginning from the earliest stages of design (shift-left) to post-production (shift-right).

With the proposed guidance, the FDA is doubling down on its efforts to incorporate cybersecurity into quality regulations to address the complexity of modern devices and today’s evolving threat landscape.

From CBOM to SBOM: What’s the Difference?
Surprisingly, one of the major changes that the new guidance brings is a leniency in the requirement for manufacturers to provide a complete software bill of materials (SBOM) instead of a more tedious cybersecurity bill of materials (CBOM), as was required in the 2018 draft. Medical device manufacturers were balking at the 2018 guidelines because of this stringency.

An SBOM is more in line with cybersecurity standards across most industries and aligns with the Biden administration’s recently issued Executive Order 14028, “Improving the Nation’s Cybersecurity.” It contains all of the required software packages (commercial and open source) and their versions.

The much more complicated CBOM, according to the 2018 guidance, demands “a list of commercial, open source, and off-the-shelf software and hardware components to enable device users (including patients, care providers, and healthcare delivery organizations) to effectively manage their assets, understand the potential impact of identified vulnerabilities to the device — and the connected system — and to deploy countermeasures to maintain the device’s essential performance.”

A Secure Product Development Framework for Every Device
The latest guidance asks medical device manufacturers to consider using a secure product development framework (SPDF) to achieve the goals of the QSR: “An SPDF encompasses all aspects of a product’s lifecycle, including development, release, support, and decommission.”

Besides compliance with the draft guidance, the call for using an SPDF can add significant value to medical devices. As the draft guideline states: “Using SPDF processes during device design may prevent the need to re-engineer the device when connectivity-based features are added after marketing and distribution, or when vulnerabilities resulting in uncontrolled risks are discovered.”

Is the New FDA Draft Guidance Binding?
Until July 7, the FDA is inviting medical device manufacturers and the public to comment on the new draft, which is expected to be finalized later this year when it will become the new FDA cybersecurity guidance for medical devices. While FDA guidance is nonbinding, the approved version will provide a road map for how medical device manufacturers should address cybersecurity in their products to ensure compliance and patient safety.

The FDA is not the only federal agency looking to strengthen cybersecurity regs. Legislation called the Protecting and Transforming Cyber Health Care (PATCH) Act was recently introduced in the US Congress. The act, the EO, and other proposed bills contain provisions that will strengthen the FDA’s ability to require medical device manufacturers to meet certain cybersecurity objectives.

In order to future-proof for impending legislation, medical device manufacturers should start investigating solutions that can generate detailed SBOMs and continuously detect vulnerabilities and mitigate risks in order to stay compliant with the FDA’s 2022 guidance and beyond.

New Linux-based ransomware targets VMware servers

Researchers at Trend Micro have discovered some new Linux-based ransomware that’s being used to attack VMware ESXi servers, a bare-metal hypervisor for creating and running several virtual machines (VMs) that share the same hard drive storage. Called Cheerscrypt, the bad app is following in the footsteps of other ransomware programs—such as LockBit, Hive and RansomEXX—that have found ESXi an efficient way to infect many computers at once with malicious payloads.

Roger Grimes, a defense evangelist with security awareness training provider KnowBe4, explains that most of the world’s organizations operate using VMware virtual machines. “It makes the job of ransomware attackers far easier because they can encrypt one server—the VMware server—and then encrypt every guest VM it contains. One compromise and encryption command can easily encrypt dozens to hundreds of other virtually run computers all at once.”

“Most VM shops use some sort of VM backup product to back up all guest servers, so finding and deleting or corrupting one backup repository kills the backup image for all the hosted guest servers all at once,” Grimes adds.

Cheerscrypt gang uses “double extortion”

The Trend Micro researchers— Arianne Dela Cruz, Byron Gelera, McJustine De Guzman, and Warren Sto. Tomas—explain in a company blog that after acquiring an input parameter specifying an encryption path, Cheerscrypt issues a command terminating all VM processes to make sure it can encrypt all VM-related files.

The gang behind Cheerscrypt uses a “double extortion” technique to extract money from its targets, the researchers explain. “Security Alert!!!” the attackers’ ransom message declares. “We hacked your company successfully. All files have been stolen and encrypted by us. If you want to restore your files or avoid file leaks, please contact us.”

The researchers note that Cheerscrypt uses public/private encryption technology to scramble the files on a target’s server. The ransomware’s executable file contains a public key, while the attacker holds the private key needed to decrypt the files encrypted with the public key. Files are encrypted using the SOSEMANUK stream cipher, while ECDH is used to create the SOSEMANUK key.

Expect malicious actors to upgrade malware to expand breach scope

ESXi is widely used in enterprise settings for server virtualization, the researchers explained. Therefore, it’s a popular target for ransomware attacks. Because it is a means to swiftly spread the ransomware to many devices, they add, organizations should thus expect malicious actors to upgrade their malware arsenal and breach as many systems and platforms as they can for monetary gain.

“As more organizations improve their security by adopting multi-factor authentication with biometrics, they are effectively locking the front door that has been the vulnerability of choice for hackers,” says John Gunn, CEO of Token. “That doesn’t mean bad actors will go away. They will instead shift their methods to attacks such as this.”

BAZARLOADER: Analysing The Main Loader | 0ffset Training Solutions

BAZARLOADER: Analysing The Main Loader | 0ffset Training Solutions

This post is a follow up on the last one on BAZARLOADER. If you’re interested in how to unpack the initial stages of this malware, you can check it out here

In this post, we’ll cover the final stage of this loader, which has the capability to download and executes remote payloads such as Cobalt Strike and Conti ransomware. To follow along, you can grab the sample as well as the PCAP files for it on

Step 1: Checking System Languages

Similar to a lot of malware, BAZARLOADER manually checks the system’s languages to avoid executing on machines in Russia and nearby countries.

It calls GetSystemDefaultLangID to retrieve the system’s default language and GetKeyboardLayoutList to iterate through the system’s keyboard layouts.

For each of these languages, the malware checks if it’s valid using a bitmask.

If the language identifier is greater than 0x43 or less than 0x18, it’s treated as valid and BAZARLOADER proceeds with its execution.

If it’s in the range between 0x18 and 0x43, the difference between the language identifier and 0x18 is used as the index of the bit to be checked in the bitmask.

The bitmask that BAZARLOADER uses is 0xD8080190C03, which is 11011000000010000000000110010000110000000011 in binary. The first bit in the bitmask is checked if the language ID is 0x18. The second bit is checked if the language ID is 0x19, and so on…

Below is the list of all languages from the bitmask that the malware avoids.

Romanian, Russian, Ukrainian, Belarusian, Tajik, Armenian, Azerbaijani, Georgian, Kazakh, Kyrgyz, Turkmen, Uzbek

Step 2: Run-Once Mutex

To check for multiple running instances of itself, BAZARLOADER first extracts the subauthority of a SID from its process. It does this by calling GetTokenInformation to retrieve the process’s token integrity level and calling GetSidSubAuthorityCount and GetSidSubAuthority to access the subauthority of a SID.

If the SID’s subauthority is SECURITY_MANDATORY_SYSTEM_RID or SECURITY_MANDATORY_PROTECTED_PROCESS_RID, BAZARLOADER checks if the mutex “{b837ef4f-10ee-4821-ac76-2331eb32a23f}” is currently owned by any other process by calling CreateMutexA.

If it is, the malware terminates itself. However, there is a small bug with the condition to check if the mutex object exists, which assumes it fails to open the mutex when it actually succeeds.

After this, the malware resolves the string “{0caa6ebb-cf78-4b01-9b0b-51032c9120ce}” and tries to create a mutex with that name.

If this mutex object already exists, the malware also terminates itself.

If the SID’s subauthority is not SECURITY_MANDATORY_SYSTEM_RID or SECURITY_MANDATORY_PROTECTED_PROCESS_RID, BAZARLOADER still uses these two mutex names but adds the string “Global” in front of them. This checks for the mutexes in the global namespace instead of the per-session namespace, which allows the malware to check if it has instances running in other users’ sessions.

Step 4: Generating Random Internet Traffic

To generate Internet activities to hide its communication with C2 servers, BAZARLOADER first calls InternetOpenA to initialize the use of WinINet functions with the following string as the HTTP user agent.

Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko

The malware then spawns a thread to periodically connect to random URLs and generate noises to hide the main C2 traffic by utilizing the following structure.

struct random_internet_thread_struct
    HINTERNET internet_sess_handle;
    HANDLE thread_handle;
    random_internet_thread_struct *self;
    LPCRITICAL_SECTION critical_section;
    __int64 padding[4];
    int creation_flag;

First, BAZARLOADER calls InitializeCriticalSection to initialize the structure’s critical section object, which is later used to protect accesses to the creation_flag field.

Next, it sets the self field to point to the structure, the creation_flag field to TRUE, and calls CreateThread to spawn a thread to perform these random Internet operations. If it fails to create a thread, the creation_flag field is set to FALSE.

The thread first tries to obtain ownership of the critical section object and check if the creation flag is enabled. If it is, the malware resolves the following URLs as stack strings.

Next, the thread enters an infinite loop to start generating the traffic noises. For random number generation, BAZARLOADER uses different functions that call the Windows API BCryptGenRandom to generate a set number of random bytes.

It randomly chooses one of the 4 URLs listed above, randomly generates the URL path segments for that, and combines the two to build the full URL.

To generate the path segments, the function takes in the minimum and maximum numbers of path segments to generate and the minimum and maximum length for each path segment.

It generates a count for the path segments randomly in the given range. For each of the segments, the malware randomly generates a string with a random length in the given range that contains numbers and uppercase/lowercase letters.

Finally, the malware calls InternetOpenURLA to establish a connection with the generated URL. It calls HTTPQueryInfoA with the HTTP_QUERY_CONTENT_LENGTH flag to retrieve the content’s length, allocates a buffer with that size, and calls InternetReadFile to read data from that URL.

This is done repeatedly until C2 communication and payload injection are finished, which generates a lot of noise to mask the main traffic coming to and from C2 servers.

Step 4: Cryptographic Structure Population

BAZARLOADER mainly uses the following structure for communication with C2 servers. The fields of the structure will be explained as we go along analyzing the code.

struct __declspec(align(8)) BazarLoader_struct
    C2_connection_struct C2_connection_struct;
    HINTERNET C2_request_handle;
    HINTERNET C2_temp_request_handle;
    crypto_struct crypto_struct;
    SYSTEMTIME curr_system_time;
    char *datetime_string;
    _QWORD datetime_string_hash;
    unsigned int *datetime_string_hash_len;
    opennic_server_struct opennic_DNS_server_struct;
    string_struct_list C2_addr_list;

First, it populates the crypto_struct field in the main structure. This structure contains cryptographic handles that are later used to decrypt executables being sent from C2 servers.

The structure can be reconstructed as below.

struct crypto_struct
    BCRYPT_ALG_HANDLE RSA_algo_handle;
    BCRYPT_ALG_HANDLE SHA384_algo_handle;
    BCRYPT_KEY_HANDLE RSA_public_key_handle;
    BCRYPT_KEY_HANDLE RSA_private_key_handle;
    DWORD RSA_public_block_length;
    DWORD RSA_private_block_length;

The malware resolves the strings “RSA” and “SHA384” and calls BCryptOpenAlgorithmProvider to retrieve handles for these two algorithms. The handles are stored in the corresponding fields in the crypto_struct structure.

Next, it resolves its hard-coded RSA public and private key blobs in memory to import their corresponding key handles.

For each blob, the malware resolves one of the strings “RSAFULLPRIVATEBLOB” or “RSAPUBLICBLOB” and uses it to specify the blob’s type when calling BCryptImportKeyPair to import the corresponding key handle.

Finally, it calls BCryptGetProperty to retrieve the length of the RSA public and private cipher blocks. With this structure fully populated, BAZARLOADER can now perform RSA encryption/decryption as well as SHA384 hashing.

Step 5: C2 Connection Through Raw IP Addresses

Prior to communicating with C2 servers, BAZARLOADER first resolves a list of raw IP addresses and writes them into the C2_addr_list field in the main structure.

This field is a structure representing a list of string structures, both of which can be reconstructed as below.

struct string_struct
    char *buffer;
    char *length;
    char *max_length;

struct string_struct_list
    string_struct *list_ptr;
    __int64 count;
    __int64 max_count;

Below is the list of all IP addresses for the C2 servers used in this sample.


For each of these addresses, the malware attempts to communicate with the corresponding server and download the next stage executable.

To establish a connection, it populates the following structure.

struct C2_connection_struct
    URL_COMPONENTSA C2_URL_components;
    HINTERNET connection_handle;
    __int64 connection_last_error;

The malware calls InternetCrackUrlA to retrieve the C2’s URL components and InternetConnectA to connect to the server.

This connection structure’s fields are then copied into the main structure’s C2_connection_struct. Here, I’m not entirely sure why they don’t just populate the main structure directly instead.

Similarly, BAZARLOADER populates the structure below to create an HTTP request to C2. The request’s object name and HTTP verb are resolved to be “/data/service” and “GET”.

struct C2_request_struct
    HINTERNET request_handle;
    __int64 request_error;

The request’s HTTP version is resolved to be “HTTP/1.1”, and BAZARLOADER calls HttpOpenRequestA to create this request for the C2 server using the connection handle retrieved above.

It also calls InternetSetOptionA to set the timeout for receiving a response and sending the request to 300 seconds and the timeout for connecting to C2s to 120 seconds.

BAZARLOADER then generates the HTTP header to be appended to the request. It does this by calling GetSystemTime to populate the curr_system_time and the datetime_string field of the main structure with the current date and time.

It also generates the SHA384 hash of the datetime string to populate the structure’s datetime_string_hash and datetime_string_hash_len fields.

Next, BAZARLOADER signs the generated hash with its RSA private by calling BCryptSignHash and uses this hash signature to randomly generate the HTTP header.

Below is the form of the random HTTP header.


Date: Tue, 17 May 2022 20:18:27 GMT

Cookie: CGIC=YKK%2BIFrld%2FC5FqKj%2Fq1F9a06T0WgC4cOvCqqo3cfsyww1EwAb2TNFWqy8wBcDtObrgkjKtmIBSnsD%2Bmn2eR6MzQeUvHqOBJqA%2FqYS3KEkUQoKUgpOfuW%2BTtuz2OjhVeOJ1oMWnellOIJ4IuNBO5aVtzV8AI3CD4H8Z9tgjhG7i6cXrsAh5EUwKjHqbeEBszHmUhSsCNx0vypRWWd6MkM1HBeENiww7W8GjhoXvX8AwZ5VDTRSX3QSr9jpUuWYXHmJDhOtdG%2FW9UAWEHYkv6IpRsGPKWtAdR%2FzHNzWmZRC3HsMP4LyIOXCs5O8lvUZH2Bv2gtZ1SGZOD0Kd7Oi4nFwA%3D%3D;HSID=gbN30kmxz%2BKKygRK6DJjGJ%2FzGQcMDOSmyQoYi9p2ITFNy6zMnfCERvugTwb5O%2FZyAJ5lGvveYZlNK3N%2BRoNPqCThC%2Fwr2OQbPA3aFJeXZerPSF7bWmhHVGGunlw8HqV3dcjs67tLT7irrJLctXzonzWE%2B6Ukd9kdFTNWW5j7s2JEEAcW9Dp6x6WIdRemz6HC3BifjKQ%2BmwvryFaI%2BsLRi9T042hwKQqV7ikOLOsHOOCqlCKs0vcpFS5vUXzdGwSWdONWLHMEkuApe5B%2BUC7bULbJvnoqnT1jFFEE3vm5gSU%2BGKIbVqBCseLx42av7mxWCmJfB1mpDH3K6tJof0YKpQ%3D%3D;DV=arPk%2BRhtodeAHMzW%2FqoqfkfCFpUR1CYM5oW%2FK7onqCf46Xqk2OR0LjLK%2BQL7jCS5yrcZA2r2hYulBxnpVa%2B8tO%2BJImuPOM9B8JkIbEAMHp%2BvNq8vcSj78wA%2FoW0Wgx8Sw9lSMno4CgPoNgF1mrkm9m9cd7U6wQS7WGKR6Wufxuvjlyt6zf5xtU2gq9WtsxOglu%2BXOYApwXiTIEO853sEnzlVNopwMbYkBm5Mu2LSEycyBm4A9nWKVCr9r0xisHCGzde3sBO4hY7WyykPK6vsU3Ds36FVNH3Kv7jx1Kf6tCVe9ukypCd8j%2FXJWAgPwv%2BsWcyA6ky9skKq1GDuoMJQ5A%3D%3D;ANID=modcr%2B3xO4FT10Fb3jE3afiK3eFqJCs1ylPBjge%2F6lgEM3Li5i5UZ%2BviQCBS1cuiaiQ%2F9Cj62z%2BapXfRMVWSFEeZTv9rkmPKmp5FZHUq5VLHNyhrBtjScNXwDZVakrx9ZYt1uzzh2X93c6Z6YQsyNWlkfdmlZS1FCGOf0aaVZfIRiUW%2FXX6zNVph%2FBpYTicXqZ1qaiPAWb5FNF%2BfJylCjmVDOFCZHJ4E6M0Pph%2BvvgjuCO9bRHWjI2Ouz%2B8ETyMBoQOkSDGzzInMeapRKmXYNDhdcEogwNmRkJqoRJjBhHzvbIYgCXGNJFWVnxywcZSaHfQwGonWClY3vT3PSlEnHg%3D%3D;

X-Tag: f1DOF2QJkbCTJAUqiSLclK%2BsRkiAFHGmQusq2an%2FAud1WpfWVK%2F6gpLmtEQ38e65ILH8bHOzUd51lMpkh2xmHI5WpDJHgCtz5Q%2F%2BCL5usiShe35weofsQr9dQk%2BLsCl%2FulJlRg4cToY4ffie2MvfrtF4FCCZpbMy66UZUNqShGcbuIlRELkGKCTimwpB2wi4J6r%2FaIWAzINIP413ERqcQJ42Q1eQvvMkrEsWZDGEi4PHVtESHkcKKRVlZ9s3%2BPaQgq6TOR9YJxQOJaQ4NgaNuJAyHJnzkvtlQHNAy%2B%2FuLVa%2FpCuXxLvnw6dCIpmMwNHdzA09SL4T%2Bc5I3S6%2BFcu3mA%3D%3D

Vary: x564msS%2Bd%2BIrc97apj6SftcyuZTeoDUdyeLRN7n%2BkEJYVoJYAeuxpHT1XhTQ%2FywsKB7tZuNCJpid2qbr5DtOphE9Yvu2MfVTPH7nuK3yrk2nl93yuTpwiB%2F2kcFx57oLxtgqXkDlPENI3p31zdp%2B7quVz9C3ZKwA0Pi8%2FjH%2Fj%2BMKFZHpAWxlAWtxWe0xvwPKj3aroG6ujoEB9Fw%2Fq3IvIBsjoCXwIWPCSX7PCIk97ccsOyzqE6jGkgvVhfw%2BcvPL78g7gfvfx6eSDoOQj5M6xcH0aTPEld8rckxqFQmB9Gy%2B%2BNZ0YACTuCn7Aw9ziB9OmeKAy%2B5I%2F1J2ij7aSH3YgA%3D%3D

Var: wG852ANm2aHtGTrbsFHawff1eBZc9MnnPFOLEWeX3o7Ulc0fSj1qhaw%2BFlqpKs6ABhhs4opIe%2Bs%2BKqhT5G3jw9xRH%2FxeEYysL5AYbHsguuOivvXTofbFPPOLM%2ByoYIbq9Navi9C0VsaghxYUgsZ3wt4aHJAWXqvYacWoAowqCaZ7pg3FY0iKgLG403wZRS25%2BOrJL6gdo98qXYntX%2B4ZlPAUa49MiLJVQvI1xFCKLo5WbCb3b3aHQ2hpwvcYoh5P6F2su6Br3YL5DxtmpjdQY9IwLubcax%2BSZ4uZrDhX6MfnqQRa8vwA61OYhQUJM7OMeqPZ9lo%2BqkzrlPux1XxOyw%3D%3D

With the generated HTTP header and the request handle, BAZARLOADER calls HttpSendRequestA to send the request to the C2 server and calls HttpQueryInfoA to retrieve the status code.

If the status code is not HTTP_STATUS_OK, the malware moves on to another C2 address.

If the status code is HTTP_STATUS_OK, BAZARLOADER calls InternetQueryDataAvailable to determine the size of data to read, allocates the memory buffer according to the size, and calls InternetReadFile to read the next-stage payload until everything is written into memory.

Finally, the malware decrypts the payload with its RSA public key by calling BCryptDecrypt and checks to make sure the payload’s size is greater than 64 bytes and that it contains an MZ header.

Step 6: C2 Connection Through Custom URLs

If BAZARLOADER fails to download the next stage executable from the IP addresses listed above, it attempts to resolve custom C2 domains using OpenNIC, a user-owned DNS community service.

To begin querying OpenNIC’s API, the malware first resolves the URL “” and calls InternetConnectA to establish a connection to the site.

Next, it calls HttpOpenRequestA to create a GET request handle with the object name “/geoip/?bare&ipv=4&wl=all&res=8” and send the request using HttpSendRequestA.

By examining OpenNIC’s APIs, we can break down this object name to see what BAZARLOADER is requesting. The “bare” parameter requests to only list the DNS server IP address, the “ipv” parameter requests to only list IPv4 servers, the “wl” parameter requests to only list whitelisted servers, and the “res” parameter requests to list 8 servers only.

To test this, we can simply paste the path below to a browser of our choosing.

The malware then enters a loop to call InternetQueryDataAvailable and InternetReadFile to read the 8 OpenNIC’s DNS servers into memory. 

For each DNS server IP address, BAZARLOADER parses it from string to int and populates the opennic_server_struct field in the main structure. Below is the structure used to store OpenNIC IP addresses.

struct opennic_server_struct
   _QWORD init_server_count;
   HINTERNET opennic_internet_handle;
   DWORD opennic_server_IP_list[7];
   _BYTE gap2C[28];
   _QWORD server_count;

Finally, the malware decodes the following custom C2 domains, attempts to resolve them using the DNS servers, and downloads the next-stage executable.


For each of these custom domains, BAZARLOADER calls DnsQuery_A to query a DNS Resource Record from OpenNIC’s servers to resolve the C2 server’s IP address.

After checking if the IP address is valid, the malware tries connecting to it and requests to download the next stage executable similar to what we have seen in the previous step.

Step 5: Injection Through Process Hollowing

After successfully downloading the next stage executable, BAZARLOADER begins the injection functionality to launch it from another process.

For this functionality, BAZARLOADER populates the following structure.

struct injection_struct
   HANDLE browser_proc_handle;
   PVOID full_exec_command;
   PVOID thread_curr_directory;
   PVOID browser_environment_struct;
   STARTUPINFOA thread_startup_info;
   LPPROC_THREAD_ATTRIBUTE_LIST proc_thread_attr_list;

First, it checks if its process is elevated with admin privileges. It calls GetCurrentProcess and OpenProcessToken to retrieve its own process token handle and GetTokenInformation to get the token’s elevation information.

If the process is not elevated, it resolves the following processes’ names and tries to populate the injection structure’s fields.


For each process name, the malware enumerates the process’s snapshot to retrieve its ID and calls OpenProcess to get its handle.

To populate the full_exec_command and thread_curr_directory fields which contain the process’s command line and full path, BAZARLOADER first extracts the process parameters from the Process Environment Block (PEB).

To access the PEB, the malware calls NtQueryInformationProcess to retrieve the PEB’s adress and calls ReadProcessMemory to read the PEB into memory.

Next, it calls ReadProcessMemory to read the process parameters from the process’s memory.

With the process parameter RTL_USER_PROCESS_PARAMETERS structure, BAZARLOADER reads the process’s command line and full path to populate the injection structure.

Similarly, it also uses the process parameter to access the browser’s environment block and writes it to the injection structure.

If BAZARLOADER has admin privilege, instead of a browser’s process, it tries to populate the injection structure with a svchost.exe process from the following command line.

\system32\svchost.exe -k unistackSvcGroup

Next, using the injection struct, the malware calls CreateProcessA to create the target process in the suspended state to perform process hollowing.

We won’t dive too deep into this process hollowing implementation, since it’s almost the exact same implementation as seen here.

We can quickly spot that process hollowing is taking place through the Windows APIs being called. NtUnmapViewOfSection is called to unmap and carve out the parent’s memory. VirtualAllocEx and WriteProcessMemory are then called to allocate virtual memory in the parent’s process and write the malicious payload into it.

We can also see that the malware iterates through the parent’s section header to find the “.reloc” section and performs relocation on the injected image in memory.

Finally, BAZARLOADER calls SetThreadContext to set the new entry point for the parent process and calls ResumeThread to resume the parent’s process again, which will execute the injected executable.

And with that, we have analyzed how BAZARLOADER downloads a remote executable and executes it using process hollowing! If you have any questions regarding the analysis, feel free to reach out to me via Twitter.

Just another Recon Guide for Pentesters and Bug Bounty Hunters

Just another Recon Guide for Pentesters and Bug Bounty Hunters

Especially when it comes to Bug Bounty hunting, reconnaissance is one of the most valuable things to do. There are still “easy wins“ out there which can be found, if you have a good strategy when it comes to reconnaissance. Bounty hunters like @NahamSec, @Th3g3nt3lman and @TomNomNom are showing this regularly and I can only recommend to follow them and use their tools.

In this Blogpost I want to explain, how I am normally performing reconnaissance during Pentests and for Bug Bounties.

Who we are

We are a team of security enthusiasts based in Austria that want to make the Internet a better and safer place. Offensity helps professional IT admins identify vulnerabilities by scanning their infrastructure and uses a lot of the techniques described here. Make sure to test our tool – it’s completely free for up to 2 domains and 50 subdomains!
Go ahead! Get free security reports for your company’s domain!
You will need to verify that you are the owner of the domain you want to scan, though. It’s not a penetration testing tool 😉

An Overview

The biggest challenge is: WHERE SHOULD I START?!
Well, you need a plan. The following illustration (click to enlarge) might look a bit confusing, but I try to explain a lot of the steps in this post:

Recon Masterplan

Basically, we want to identify as many endpoints as possible, sort and filter them, scan them automatically and perform manual assessments where applicable – easy right?

We need to identify assets which belong to the target company and are in-scope. The first thing is to identify domains and sub-domains belonging to the target.

Subdomain Enumeration

Subfinder is a subdomain discovery tool that discovers valid subdomains for websites. Designed as a passive framework to be useful for bug bounties and safe for penetration testing.
GitHub Link

# Install
go get

# Basic usage
subfinder -d >

# Recursive
subfinder -d  -recursive -silent -t 200 -o

# Use censys for even more results
subfinder -d -b -w wordlist.txt -t 100 -sources censys -set-settings CensysPages=2 -v -o
# passive
amass enum --passive -d -o 

# active
amass enum  -src -ip -brute -min-for-recursive 2 -d -o

Use certificate transparency logs provides a PostgreSQL interface to their data. The script below extracts sub-domains for a given domain name using PostgreSQL Interface
GitHub Link

Get alerted if a new subdomain appears on the target (using a Slack Bot)
Sublert is a security and reconnaissance tool which leverages certificate transparency to automatically monitor new subdomains deployed by specific organizations and issued TLS/SSL certificate. Technical details here: here
GitHub Link

Find domains and subdomains related to a given domain
GitHub Link

# Install
go get -u

# Basic usage
assetfinder [--subs-only] <domain>

GetAllUrls (gau) for Subdomain-Enumeration
Fetch known URLs from AlienVault’s Open Threat Exchange, the Wayback Machine, and Common Crawl.
Github Link

# Install 
GO111MODULE=on go get -u -v

# Extract subdomains from output
gau -subs | cut -d / -f 3 | sort -u

Search Engines

Use Github search and other search engines
The tool subfinder (look above) already provides the possibility to use search engines for subdomain enumeration, but it does not support GitHub.
Make sure you check Github – type in the Domain of the company and manually look through the code-results. Interesting endpoints and probably secrets that shouldn’t be there can be found!

Github Recon
GitHub is a Goldmine @Th3g3nt3lman mastered it to find secrets on GitHub. I can only recommend to watch his Video together with @Nahamsec where he shares some insights.
Be creative when it comes to keywords and use their search! Check their GitHub company profile, filter for languages and start searching: hmac

Within the results check the Repositories, Code, Commits and Issues. Code is the biggest one where you will probably find the most. Issues is a goldmine – Developers tend to share too much information there 😉

Some things you can search for:

  • “” “dev”
  • “”
  • “” API_key
  • “” password
  • “” authorization

Additionally, here are some tools (won’t go into detail here) which I use regularly:

Do not forget Google – it can be worth it! Some examples (taken from here):

  • -www
  • intitle:”test” -support
  • ext:php | ext:html
  • inurl:auth
  • inurl:dev

So, if you want to find WP-Config files with cleartext DB-credentials in it, just go ahead:

inurl:wp-config.php intext:DB_PASSWORD -stackoverflow -wpbeginner -foro -forum -topic -blog -about -docs -articles

Do not forget to use other search engines such as Shodan. Some examples (taken from here):

  • country: find devices in a particular country
  • geo: you can pass it coordinates
  • hostname: find values that match the hostname
  • net: search based on an IP or /x CIDR
  • os: search based on operating system
  • port: find particular ports that are open
  • before/after: find results within a timeframe

Shodan also provides a facet interface, which can be very helpful if you want to get an overview about bigger network-ranges.

Shodan Facet

Get Subdomains from IPs

HostHunter a recon tool for discovering hostnames using OSINT techniques.
GitHub Link (includes installation instructions)

# Basic usage
python3 <target-ips.txt> > vhosts.txt

After enumerating subdomains, we can try to find additional subdomains by generating permutations, alterations and mutations of known subdomains.

Altdns is a DNS recon tool that allows for the discovery of subdomains that conform to patterns. Altdns takes in words that could be present in subdomains under a domain (such as test, dev, staging) as well as takes in a list of subdomains that you know of.

GitHub Link

# Installation 
pip install py-altdns

# Basic usage
altdns -i known-subdomains.txt -o raw-data_output -w words.txt -r -s results_output.txt

Generates combination of domain names from the provided input.
GitHub Link

# Installation 
pip3 install dnsgen

# Basic usage
dnsgen known-domains.txt

When doing DNS permutations using various tools, not all of them check, if the outcome actually resolves to an IP-Address. The fastest way to resolve thousands of (sub)-domains is massdns.

A high-performance DNS stub resolver for bulk lookups and reconnaissance (subdomain enumeration)
GitHub Link

# Installation 
git clone
cd massdns

# Basic usage
./bin/massdns -r lists/resolvers.txt -t A domains.txt > results.txt

#In combination with dnsgen 
cat domains.txt | dnsgen -w words.txt -f - | massdns -r lists/resolvers.txt -t A -o S -w massdns.out

Now you should have a fairly large list of subdomains and corresponding IPs. I will not go into detail on how you do a TCP or UDP portscan or how you conduct an automated vulnerability scan in this post.
An interesting fact for us as security researchers is, if the discovered subdomains have web-services running.

Take a list of domains and probe for working HTTP and HTTPS servers
GitHub Link

# Install
go get -u

# Basic usage
$ cat recon/example/domains.txt
$ cat recon/example/domains.txt | httprobe

# Use other ports
cat domains.txt | httprobe -p http:81 -p https:8443

# Concurrency - You can set the concurrency level with the -c flag: 
cat domains.txt | httprobe -c 50

Additionally, we can check if any subdomain is vulnerable to subdomain takeover:

Subjack is a Subdomain Takeover tool written in Go designed to scan a list of subdomains concurrently and identify ones that can be hijacked.

GitHub Link

# Install 
go get

# Basic usage
./subjack -w subdomains.txt -t 100 -timeout 30 -o results.txt -ssl

Other tools to scan for subdomain takeover vulnerabilities:

Screenshot all Websites for Visual Recon
After we compiled our list of HTTP enabled targets, we want to know, what webservices are running on these hosts. One of the first steps I perform is to actually have a look at the website. The easiest and fastest way to do this for a lot of targets is to perform automated screenshotting of all targets.


EyeWitness is designed to take screenshots of websites provide some server header info, and identify default credentials (if known).
GitHub Link

# Install instructions can be found here

# Basic usage
./ -f filename --timeout optionaltimeout

# Further examples
./EyeWitness -f urls.txt --web
./EyeWitness -x urls.xml --timeout 8 
./ -f urls.txt --web --proxy-ip --proxy-port 8080 --proxy-type socks5 --timeout 120


A simple script to screenshot a list of websites, based on the url-to-image PhantomJS script.
GitHub Link

# Clone
git clone

# Basic usage
python -i list.txt -w 40

URL and Parameter Discovery

The easiest active way to discover URLs and corresponding parameters on the target is to crawl the site.

A fast web spider written in Go
GitHub Link

# Install
go get -u

# Basic usage
# Run with single site
gospider -s "" -o output -c 10 -d 1

# Run with site list
gospider -S sites.txt -o output -c 10 -d 1

# Also get URLs from 3rd party (,,, and include subdomains
gospider -s "" -o output -c 10 -d 1 --other-source --include-subs

# Blacklist url/file extension.
gospider -s "" -o output -c 10 -d 1 --blacklist ".(woff|pdf)"

Web applications use parameters (or queries) to accept user input. We want to find as many parameters as possible which we can later scan or review manually. That’s where Arjun comes in:
GitHub Link

# Install
git clone

# Scanning a single URL
python3 -u --get
python3 -u --post

# Scanning multiple URLs
python3 --urls targets.txt --get

# Multi-threading
python3 -u --get -t 22

GetAllUrls (gau)
We already covered gau above. GetAllUrls (gau) fetches known URLs from AlienVault’s Open Threat Exchange, the Wayback Machine, and Common Crawl for any given domain. Inspired by Tomnomnom’s waybackurls.

# Install 
GO111MODULE=on go get -u -v

# Basic usage
printf | gau
cat domains.txt | gau
gau -subs


After having assembled a huge list of subdomains, URLs, and parameters, we now want to filter them, and remove duplicates.

Removes duplicate URLs and parameter combinations
GitHub Link

# Install
go get -u

# Basic usage
~# cat urls.txt¶m2=2¶m1=2¶m1=2¶m4=2
# Remove duplicates
~# cat urls.txt |qsreplace -a¶m2=2¶m2=1

We can use the following tool to find potentially interesting URLs

A wrapper around grep to avoid typing common patterns. For example one can write the following gf template to grep for potential URLs that are vulnerable to open-redirects or SSRF
GitHub Link

~/.gf# cat redirect.json 
    "flags" : "-HanrE",
    "pattern" : "url=|from_url=|load_url=|file_url=|page_url=|file_name=|page=|folder=|folder_url=|login_url=|img_url=|return_url=|return_to=|next=|redirect=|redirect_to=|logout=|checkout=|checko

~# cat urls-uniq.txt¶m2=2¶m2=1¶m3=1
~# gf redirect urls-uniq.txt

Some more ideas on gf patterns can be found here, including patterns for interesting subdomains, SSRF and more:

Use BurpSuite’s passive scans
It makes total sense to “import” as many URLs as possible into BurpSuite. How to “import”? Here is how I do it:

cat urls.txt |  parallel -j50 -q curl -x -w 'Status:%{http_code}t Size:%{size_download}t %{url_effective}n' -o /dev/null -sk

BurpSuite automatically performs passive checks on the way (e.g. DOM-Based-XSS).
Use extensions like Secret Finder to find secrets in responses (e.g. API keys).
Use AWS Security Checks to find AWS Bucket security issues.
There a tons of useful extensions which to (semi) passive checks – have a look in the BApp-Store!

Discover even more content

Find all js files
JavaScipt files are always worth to have a look at. I always filter for URLs returning JavaScript files and I save them in an extra file for later.

A great write-up about static JavaScript analysis can be found here:
Static Analysis of Client-Side JavaScript for pen testers and bug bounty hunters

cat urls.txt | grep ".js" > js-urls.txt

# check, if they are actually available
cat js-urls.txt | parallel -j50 -q curl -w 'Status:%{http_code}t Size:%{size_download}t %{url_effective}n' -o /dev/null -sk | grep Status:200

A python script that finds endpoints in JavaScript files
GitHub Link

# Install
git clone
cd LinkFinder
python install
pip3 install -r requirements.txt

# Basic usage
# Get HTML report
python -i -o results.html

# Output to CLI
python -i -o cli

As explained before, there are BurpSuite Plugins checking for secrets in HTTP responses.
There are also other tools available to discover potential secrets in various files (again, check all JS files!):

Find hidden directories or files

Fast web fuzzer written in Go
GitHub Link

# Installation
go get

# Basic usage
ffuf -w wordlist.txt -u

# Automatically calibrate filtering options
ffuf -w wordlist.txt -u -ac

# Fuzz file paths from wordlist.txt, match all responses but filter out those with content-size 42
ffuf -w wordlist.txt -u -mc all -fs 42 -c -v

For Web fuzzing, you need good wordlists. You can use default wordlists, provided by DirBuster, or special wordlists from the SecLists repository. You should also use a custom wordlist which fits the current target. You can use CeWL for that:

CeWL is a Custom Word List Generator
GitHub Link

~ cewl -d 2 -m 5 -w docswords.txt
~ wc -l docswords.txt
13 docswords.txt

Final Thoughts

This is just the way I do it and I tried to cover most of my default procedure here in this post. I will try to update this every now and then – there are tons of great tools out there which make our lives easier.

In my opinion, good recon is essential. You have to find things that nobody else found before in order to find those critical bugs. Make sure you have a plan and document everything you found, you will probably need it later.

If you have questions or suggestions, just drop me an E-Mail

Make sure to follow @Offensity on Twitter for future updates!

Further Resources and Tools

  • Useful search engines