25/03/2024
Blog technique
A way to find LPE in Windows App
Team CESTI
Through this article, we propose a way to find LPE in Windows applications, by using SysInternals tools. What and how to look at? How to exploit in an easy and quick way?
How it works
When a Windows app is executed, it loads several libraries (DLLs) needed to its functioning (i.e., C:\Windows\System32\cryptbase.dll, etc.). To load DLLs dynamically, the LoadLibraryA() function is usually used (or LoadLibraryExA()).
HMODULE LoadLibraryA(
[in] LPCSTR lpLibFileName
);
When this function is called, a specific search order is respected, as shown below:
The search order is the following:
- Application: installation app repository
- System: C:\Windows\system32\ repository
- Windows: other Windows repositories
- Current PATH: current repository
- %PATH%: local user repository
Sometimes, developers are not using the usual places and put their own libraries in specific folders, accessible by non-privileged users. In specific cases, these folders can be abused and can be used by a local attacker to elevate his privileges. Let’s look how to proceed.
SysInternals: Procmon
In 1996, Mark RUSSINOVICH created the Sysinternals website to host his system utilities. Today, these tools are referenced on the Microsoft site.
One of the proposed tools is ProcessMonitor. This tool is able to monitor the activity of the file system, registers and processes/threads in real time. Here is an example.
It is possible to observe all operations performed by processes and their associated results and details:
- Access to the register (RegOpenKey, RegQueryKey, etc.)
- Windows API calls (CreateFile, ReadFile, WriteFile, etc.)
- Path accessed
- and a lot of more!
By this way, it is so possible to observe paths that an app tried to access. What about « NAME NOT FOUND » paths?
Name not found?
On Windows, when an app tries to access to a file, it first checks if it exists by systematically performing a CREATE FILE API call both on its path and on its name. Such a call can either return a SUCCESS if the file or path exists or PATH NOT FOUND/NAME NOT FOUND otherwise. In our case, by analyzing the execution flow of an app, we only focus on these error results in ProcMon, by filtering. Here is the obtained result.
Several paths/files do not exist. These inaccessible files are often forgotten during a product development or because of bad practices. A bad access attempt generally targets:
- A directory of the current user (%APPDATA% -> C:\Users\Jean-Michel\AppData\Roaming)
- A non-existing directory in C:\ (like C:\dev\, having read/write access by default for a domain user)
- A directory of another file system (C:\proc\stat or C:\bin\bash)
- A hard-coded library (like C:\Users\Jean-Michel\Desktop\dev\debug.dll)
In this example, it is possible to see that the app tries to access to the C:\proc\stat\ folder.
Below, another example that exploits the dll PATH NOT FOUND. An unprivileged user puts its own dll in his environment. Then, a privileged process searches the dll in the user (wrong) environment (instead of a safe environment). The process finds the dll, and then loads it.
How to proceed?
In this example, we consider a non-privileged attacker, having a local access to a Windows workstation. His goal is to elevate his privileges. He observes that a privileged app process tries loads its own DLL from a not found path. This attacker will easily develop his own DLL by following the associated Microsoft documentation (which necessitates a special formalism in order to be executed).
The Microsoft documentation shows the entry points for a DLL. The attacker is able to execute his code, as soon as his DLL is loaded by an app and by executing DLL_PROCESS_ATTACH.
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
switch(fdwReason)
{
case DLL_PROCESS_ATTACH:
Exploit();
break;
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
break;
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
Then, he can copy it in the current directory and wait its loading by the vulnerable app. When loaded, the code is executed with the app rights, i.e., NT AUTHORITY\SYSTEM.
But the app also crashes!
Why does it crash?
Once the DLL is executed, it can crash the app. It’s a usual behavior. After executing the payload, the app seeks to use functions from the DLL (that it believes it has successfully contacted). As the called function does not exist, it crashes.
To overcome this problem and ensure that the product continues to work, the attacker has to perform DLL proxying. It will be the subject of another blog article, to see how to proceed!