The interesting case of WinSCP
A couple of months ago, while analyzing one of our environments, we had noticed instances of the LogonUI.exe process – running as NT AUTHORITY/SYSTEM – loading a DLL file named DragExt64.dll from local user %LOCALAPPDATA%\Programs\WinSCP\ directories, e.g., C:\Users\bob\AppData\Local\Programs\WinSCP\DragExt64.dll. Since such DLL files are owned by the user (ownership by default inherited from the directory), but executed as SYSTEM, it was clear that this phenomenon created a potential vector for local privilege escalation, as simple as replacing the original DLL with a custom one, containing arbitrary code.
Following up, it turned out that DragExt64.dll is an extension of WinSCP responsible for drag & drop support, distributed along with WinSCP. Further investigation revealed that in some systems that file could be found at C:\Program Files (x86)\WinSCP\DragExt64.dll, while in other systems the location was the local user %LOCALAPPDATA%\Programs\WinSCP folder – suggesting that the difference came from whether the user, during installation, chose “Install for all users (recommended)” or “Install for me only”, as demonstrated in the screenshot below:
The default and recommended option deploys WinSCP into C:\Program Files (x86)\WinSCP\ and therefore requires the installer to be run with administrative privileges. The second choice results in deployment into the current user’s %LOCALAPPDATA%\Programs\WinSCP directory and therefore does not require administrative privileges. No surprise here.
When installing for all users, WinSCP installer (running as SYSTEM), creates a new string entry in the registry at HKLM\Software\Classes\Directory\ShellEx\CopyHookHandlers\WinSCPCopyHook, containing the full path to the DragExt64.dll (C:\Program Files (x86)\WinSCP\DragExt64.dll by default) – which can be easily confirmed with either the registry editor or Sysinternals Autoruns. The presence of that path in that registry entry causes LogonUI.exe to load the DLL file from the path pointed by that entry. The event can be reproduced by triggering Winlogon to create a new instance of LogonUI.exe by interactively logging into the system, either after booting the system, waking it up from sleep/hibernation or just unlocking the desktop.
The question that remained, however, was why would in some cases when WinSCP was clearly deployed for the current user only, the path to the DragExt64.dll end up in a HKLM entry, which clearly requires administrative privileges to be written to.
The answer was quickly discovered by trial-and-error approach – it turned out that if WinSCP installer was run with administrative privileges, it would create that registry entry regardless of whether the user chose installation for all users or for the current user only, ending up with the relevant registry entry pointing at the local user path to DragExt64.dll, as demonstrated in the screenshot below:
The ability to abuse this entry to trick LogonUI.exe to load an arbitrary DLL file by replacing the original DragExt64.dll while operating as that user was confirmed with a simple proof of concept.
The following DLL file was compiled and written over the original DragExt64.dll:
After several reboots and manual desktop lock and unlock actions, the content of the poc.txt showed multiple instances of execution from within LogonUI.exe (as SYSTEM), as well as some occurrences of injection into notepad++.exe and Explorer.exe, run by my regular user:
As further investigation revealed, in all discovered cases local users did not have local administrator privileges on their systems, but they were allowed to temporarily obtain them using software known as Admin By Request – which is an interesting solution providing a compromise between security and flexibility, so users can still perform some administrative tasks, like installing software they need, but those permissions are temporary and granted only for explicitly requested operations.
So just to be clear, the discussed LPE vector was created in result of a minor bug in WinSCP installer manifesting itself only when running elevated, whereas the elevation itself was totally legitimate. WinSCP’s DragExt64.dll is just one of several examples of cases we observed, when elevated LogonUI.exe loads extension DLLs from regular user directories that were discovered during the process. A couple of other popular ones are FileMonitor64.dll (Avanquest Powerdesk File Monitor) and fzshellext_64.dll (FileZilla).
One could argue that this does create a few low-risk security problems.
The administrative access could be granted temporarily and only for one, specific action – like running a well-known, digitally signed software installer and nothing else.
It also creates local persistence, which will keep working regardless to file owner’s group memberships. Such user could have had relevant group membership and later have had it revoked, while maintaining access to code execution as SYSTEM.
As a method to execute code as SYSTEM it is also relatively stealthy, as it does not require any further authentication events (no requests for elevation, no UAC, no logons), which means it stays unnoticed unless someone explicitly looks into DLL load events.
The general phenomenon
Even without the logical bug in the installer, a very similar effect could be achieved with an elevated installer, if the user chose deployment for all users or deployed a service, while changing the installation path from default C:\Program Files (x86)\ to a path for which they have write permissions, like a path in their own home directory or C:\Users\Public.
First, the software industry should put more attention to the growing number of deployments where users do not have administrative privileges and make sure their software comes in portable versions/versions that allow full installation in user’s home directory and utilizing only private HKCU registry keys – in other words, not requesting administrative privileges unless it is necessary for the application to work correctly.
For security specialists out there, system administrators and pentesters/red teamers, it is worth keeping in mind that user-owned files often end up being executed as SYSTEM. If we investigate our EDR telemetry, searching for privileged code running from paths most likely owned by regular users – not only process creations, but also DLL load events – we will find way more events than one could initially expect. And those that originate from processes spawned from global system locations owned by SYSTEM/Administrators/TrustedInstaller (like C:\Windows\*, C:\Program Files\*), but load DLL files from user directories, and are not caused by the use of ABR – are all potential Local Privilege Escalation cases like CVE-2022-38395. I emphasize the word “potential”, as what we see in EDR telemetry are DLL load events of legitimate files (unless we are dealing with actual exploitation of software vulnerable to DLL side loading) – and to exploit those scenarios our target service must not conduct proper integrity checks (signature verification).