One type of assessment we perform a lot, especially last year with Windows 10 coming to the end of its life, is what we call a workstation assessment. The goal is to identify weaknesses and vulnerabilities within a standard image or build and determine whether they can be exploited from various standpoints.
During such an assessment last year, when vulnerability research wasn’t yet fully performed by Claude, I was faced with an image which had a limited attack surface and very few additional software. One of them was HP One Agent, which I had never heard of at the time, but eventually allowed me to elevate privileges on the workstation due to a race condition and DLL side loading. Interestingly, this could have tied in quite nicely with my colleague Leon‘s research surrounding bloatware which he presented at Defcon last year.
Based on initial observations, it appeared to collect telemetry from the device and send it somewhere, but the more interesting part for me was that it installed a SYSTEM service, dropped a bunch of “plugin” DLLs under C:\ProgramData, and periodically executes them with SYSTEM privileges.
I’m sure some of you will already be groaning… with good reason.
Installation overview
One of the issues I had while assessing the solution was determining whether the version I was looking at was actually the latest version of the solution or not. A privilege escalation vulnerability had already been discovered in the past but limited information was available and the version I was looking at (1.1.789.5870) was more recent than the report. The latest version at the time seemed to be 1.1.912.0346 which for the purpose of the currently described vulnerability is identical to the one our customer had installed. So I assumed I had not just rediscovered an old vulnerability.
When running the installer, HP One Agent installs files in two main locations:
C:\Program Files\HP\HP One AgentC:\ProgramData\HP
The Program Files folder is not writable by standard users (as expected), but C:\ProgramData\HP allows authenticated users to add files (though not overwrite existing ones).
It also creates a service running as SYSTEM which calls a binary file aptly named hp-one-agent-service.exe.
The service itself calls a hp-plugin-executor.exe binary which in turn loads and runs a number of plugins located under:
C:\ProgramData\HP\telemetry\collectors\...
I’m sure there’s a good reason for these not to be in the Program Files folder, but as an attacker, I certainly enjoyed this design choice.
Unwrapping hp-plugin-executor.exe
A quick reverse engineering session with Ghidra (and some ProcMon confirmation) shows behaviour roughly like the following pseudo-code:
For each subfolder in C:\ProgramData\HP\telemetry\collectors
file = FindFirstFile(*.dll, subfolder)
if not WinVerifyTrust(file)
exit
while file=FindNextFile(subfolder)
if not WinVerifyTrust(file)
exit
loadlibrary(plugin.dll)
It then goes on to call a function named LoadPlugin() within the discovered DLL.
It is obvious that the service should only load signed DLLs and we all know that no one has ever been able to sign a malicious DLL. In my case, I had no way of doing this, but quickly discovered that this isn’t actually necessary.
Enter DLL sideloading
I noticed that once a plugin is loaded, it will often in turn load additional DLL dependencies that are not present in the current plugin folder. ProcMon showed the executor and/or plugin attempting to resolve missing DLLs from the plugin directory first before finding them elsewhere, typically in a shared folder with various HP DLLs present.
Since the plugin directory is writable by authenticated users, that gives a straightforward DLL sideloading opportunity where we can drop a DLL with the right name into the right collector folder and simply wait for the service to run.
A concrete example I discovered is the following:
- Plugin DLL:
C:\ProgramData\HP\telemetry\collectors\hp-telemetry-device-info-collector_ver_4.910.27770\hp-telemetry-device-info-collector.dll
- It imports functions from:
hp-shared-error-v2.dll
- The legitimate
hp-shared-error-v2.dllexists in another collector folder and not alongside the plugin itself.
So exploitation seems quite easy at first, just place a malicious hp-shared-error-v2.dll next to the plugin, and Windows’ loader will happily use it when the service loads the DLL. For those paying attention and who read my pseudo-code earlier, you’ve probably realised it wasn’t quite that straightforward.
Before loading the plugin, the executor enumerates all *.dll in the plugin subfolder and checks their signature with WinVerifyTrust. As mentioned above, if you have a legitimate code signing certificate, you can just sign a malicious DLL, drop it in, and you’re done. But again, this was not my case. However, there was still a way forward.
Race condition to the rescue
The signature verification is based on enumerating the folder:
- call
FindFirstFile("*.dll") - repeatedly call
FindNextFile() - verify each file returned by enumeration
While this is not particularly well documented (at least in my eyes), these function calls cache at least partial results before iterating through the files, which means that DLLs added to the folder after the initial call will not necessarily be checked but can still be loaded later as dependencies.
So the exploit strategy becomes:
- Let the executor start verifying DLLs
- While it is still busy verifying those files, drop a new malicious DLL into the folder
- The new DLL bypasses the signature checks and can be loaded via dependency resolution
Making the race winnable
Winning a race condition can generally be done in two ways:
- Be extremely precise
- Increase the timing window
I went for the second option by slowing down signature verification by copying multiple large, legitimately signed DLLs into the target folder, so WinVerifyTrust takes long enough to give us more time to drop the new DLL.
Knowing when to drop the malicious DLL
To decide when the executor is in the middle of its verification loop, I’m sure there are multiple techniques that can be used. I went with something relatively simple that ended up working quite well.
- Create a “trigger” DLL (a copy of a legitimate signed DLL) in the folder
- Repeatedly attempt to open it for writing
- When you get an invalid handle / sharing issue, it likely means the executor opened it during verification
- At that moment, drop the malicious DLL into the folder
In my first attempt, I expected that simply dropping a malicious hp-shared-error-v2.dll would lead to code execution (e.g., via DllMain).
But… nothing happened. Eventually, I got everything to work by using DLL proxying:
- Implement the exports expected by the plugin,
- Forward them to the original legitimate
hp-shared-error-v2.dll, - Add a small “bonus” payload (e.g., add a local admin user).
With proxying, the plugin continues to operate normally, and the payload executes in the context of the SYSTEM service, resulting in a pretty standard local privilege escalation vulnerability.
I won’t release the PoC I wrote because it would burn your eyes, but with the information above, it should be relatively straightforward to reproduce.
Affected versions
- All versions prior to
1.3.214.7339which is the one that introduced the patch
Note that I did not test the patch and do not know how the issue was corrected.
Disclosure timeline
- 17.08.2025 : Initial disclosure to HP through web form
- 24.08.2025 : Follow-up by email requesting whether web form was received
- 25.08.2025 : HP requests PoC, details sent again
- 29.09.2025 : Follow-up by email requesting whether issue could be reproduced
- 05.12.2025 : Follow-up by email requesting for news and indicate desire to write blog post
- 05.12.2025 : Response from HP indicating a fix has been developed but release has been postponed to first week of January. I indicate I will wait for the fix to publish anything.
- 26.03.2026 : Follow-up by email requesting a status update
- 27.03.2026 : HP indicate they are still working on the bulletin and that
CVE-2026-5064has been reserved, but patch has been available in the windows store since 19th of January - 30.03.2026 : Request to publish blog post
- 30.03.2026 : HP ask if publication can be delayed until security bulletin is published (expected in a few days)
- 09.04.2026 : HP indicate that fixes were released to windows update, the commercial solution is still vulnerable. They will follow-up with timeline when they know it.
- 17.07.2026 : Follow-up by email requesting a status update
- 24.07.2026 : HP indicates that the security bulletin was published in June
Conclusions
Given the nearly year-long timeline to get a fix out to everyone, I’m not sure we’re quite ready for what frontier-ai-powered-vulnerability-research is now bringing us…
