A local privilege escalation bug in HP Support Assistant by abusing hard link
Researcher: ManhNDd of Bkav
In HP Support Assistant GUI, when you click Check for updates and messages, the service HP Support Solution Framework will run some tasks, start HPSAObjUtil8.exe (run as SYSTEM) which changes the permission of all .xml files in C:\ProgramData\Hewlett-Packard\HP Support Framework\Logs\Temp\HPSA. In detail, HPSAObjUtil8.exe gives full controls over these .xml files to all users. Following is the vulnerable code in HPSAObjUtil8:
public static void AddTempLogsPermission()
{
string path = Environment.GetEnvironmentVariable("ProgramData") + "\\Hewlett-Packard\\HP Support Framework\\Logs\\Temp\\HPSA";
if (Directory.Exists(path))
{
string[] files = Directory.GetFiles(path, "*.xml");
if (files != null && files.Length != 0)
{
foreach (string fileOrFolderName in files)
{
Patches._utils.AddFileSecurity(fileOrFolderName);
}
}
}
}
A user can create a hardlink with name ???.xml in the folder C:\ProgramData\Hewlett-Packard\HP Support Framework\Logs\Temp\HPSA, and point it to a victim file of our choosing (all we need is read access), then let HPSAObjUtil8 modifies the permissions of it so that we have full permissions over the file (read/write/delete). So this bug allow us to get full permissions over system files => you can overwrite a system DLL to get system privileges. By the way, the CVSS 3.0 score of the bug sets Privileges Required High. This should be None, because I tested the bug successfully on non-admin user.
By reversing HPSF.exe and HPSupportSolutionsFrameworkService.exe, I figure out that the client HPSF.exe communicates with the service through the interface HP.SupportFramework.ServiceManager.Interfaces.IServiceInterface (defined in HP.SupportFramework.ServiceManager.dll). If the client calls the method IserviceInterface.RunAnalysis, the service will start HPSAObjUtil8 and trigger the bug.
ServiceInterface.Instance.RunAnalysis("en-US", true);
So you have to create a fake client and call RunAnalysis. However, the service validates its client before processing the request:
- Check whether the client is signed with HP signature and in C volume.
- Check whether parent, grandparent, great-grandparent... of the client is signed with HP signature or is a valid Windows process (in System32, for example). However, if the method GetParent returns null, the checking stops.
{
Process processById = Process.GetProcessById(\u0002);
string text = \u000E.\u0001(processById);
if (processById.MainModule.FileName.ToLowerInvariant() != text.ToLowerInvariant())
{
if (DebugLog.IsDebug)
{
DebugLog.LogDebugMessage("Invalid File detected: " + text, DebugLog.IndentType.None);
}
return false;
}
Process parent = processById.GetParent();
bool flag = true;
int num = 0;
while (parent != null && flag && num <= 5)
{
bool flag2;
flag = \u000E.\u0001(parent, out flag2);
if (!flag || flag2)
{
break;
}
num++;
parent = parent.GetParent();
}
return flag;
}
Let's bypass the client validation:
- To bypass signature checking, you have to start an HP signed EXE and inject code into it, or create some fake DLL, and the HP EXE loads it up. I choose the second method. I find out that BatteryTest.exe (in C:\Program Files (x86)\Hewlett-Packard\HP Support Framework\Resources\HPBatteryCheck) is signed with HP signature, and when I start BatteryTest.exe, it tries to load userenv.dll in its directory before searching in %PATH%. So I create a fake userenv.dll and copy it and BatteryTest.exe into the same folder, then run BatterTest => BatterTest becomes our client exe and trigger RunAnalysis for you => bypass client signature checking.
- I copy BatterTest.exe and other necessary files into %tmp%, so that they are in C volume => assuring that the client is in C volume.
- To bypass parent checking, I create a program that calls BatteryTest.exe up and exits immediately, so that GetParent returns null!
Now you need to find out an executable that can be the target for the hardlink. It should be an EXE or DLL that will be run as SYSTEM automatically some time, so that when you overwrite it, you get SYSTEM privileges. I see that when calling RunAnalysis, HPSupportSolutionsFrameworkService starts many processes as SYSTEM. You can overwrite those processes with your own executable. However, the service checks signatures of those EXEs before starting them, so you cannot create fake EXEs, but resort to DLLs loaded by those EXEs. There are many DLLs already loaded somewhere, and many .NET DLLs signature-checked before being loaded, so you cannot use them. Luckily, there are still some DLLs usable:
- c:\program files (x86)\hp\shared\hputils64.dll, loaded by Detect_BatteryFailure.exe (HP signed).
- c:\windows\syswow64\rtutils.dll
- c:\windows\system32\msxml3.dll
I tried to modify hputils64.dll and succeeded. I create a hardlink to hputils64.dll. When calling RunAnalysis, HPSAObjUtil8 changes the hputils64.dll's permissions, then a few seconds later Detect_BatteryFailure.exe starts (as LOCAL SYSTEM) and loads up hputils64.dll. Before hputils64 loaded, you can overwrite it, so that Detect_BatteryFailure.exe loads your malicious malicious DLL up and you finally get the SYSTEM right.
To write the exploit, I created:
- CreateProcess.exe: call up a process and exit. This is used for parent validation bypass.
- CreateHardlink.exe: create hardlink.
- Userenv.dll: set up things for executing .NET assembly and execute the assembly invader.exe in the context of BatterTest. This is used for client signature validation bypass.
- Invader.exe: call RunAnalysis which will trigger the bug.
- MalDll.dll: call up notepad. hputils64.dll is overwritten with this DLL.
The following is the exploitation steps in PrivHP4.py:
- Copy BatteryTest.exe and other necessary files into %tmp% folder.
- Create hardlink from C:\ProgramData\Hewlett-Packard\HP Support Framework\Logs\Temp\HPSA\hardlink.xml to C:\Program Files (x86)\HP\Shared\hputils64.dll
- Start BatteryTest.exe with CreateProcess.exe. CreateProcess.exe will terminate immediately after starting BatterTest.
- Wait for the permission of C:\Program Files (x86)\HP\Shared\hputils64.dll to be changed, then overwrite it with MalDll.dll, and then just wait for notepad to start up as SYSTEM!
To test the PoC, just clone the repository and run PrivHP4.py.