As any seasoned security professional knows, many published security vulnerabilities and attacks are over-hyped. What makes something newsworthy is not always that it poses a significant risk to most organizations. If an attack doesn’t appear to be technically sophisticated, it will often be overlooked.
One class of security issue that often fails to receive enough scrutiny is the security of Windows services. In this article we’ll help you understand why it is important to secure Windows service configurations, why they are frequently configured unsafely, and what you can do about it.
Very few organizations can completely prevent account compromises. The attack vectors are endless: incidents of phishing, browser drive-bys, watering hole attacks, and malicious apps on employees’ personal devices are all significant risks. Even medium-sized organizations deal with at least a few of these incidents each year.
What separates strong security programs from the rest is how long it takes an attacker to penetrate further into the infrastructure and how quickly the security team can detect the initial attack, neutralize it, and recover.
To understand what a digital intruder might do once they gain a foothold in your environment, you need only to look at the playbook of a typical network pentesting team. With very limited time frames to conduct an assessment, penetration testers will often gravitate to the easiest and most reliable way to gain complete control of an infrastructure. If it is low-hanging fruit for a pentester, then it will be for an adversary (or their automated malware).
Once a pentester gains access to an unprivileged account on a victim’s Windows system, one of the first things they look for is how to escalate privileges to the Local System account. Why is this a go-to play? Because access to Local System on any Windows machine opens up an enormous attack surface to move laterally through the enterprise. An intruder could use this access to:
● Sniff the local network and conduct man-in-the-middle attacks against a wide variety of protocols, including DNS, SMB, RDP, HTTP(s) and more
● Use Mimikatz or similar tools to steal domain credentials of other users out of LSASS process memory
● Steal MSCACHE password hashes out of the registry and crack them offline
● Disable locally installed security software and delete evidence of the initial intrusion
Even if the host and user account that was initially compromised had access to nothing sensitive, having Local System on a host that is a member of an important Windows domain opens up many options. For this reason it is actually extremely important to prevent local privilege escalation flaws in Windows environments.
There are numerous in-depth blog posts on DLL hijacking/sideloading that capture all of the nuances. I won’t try to regurgitate that here, but instead will attempt to give a short summary. For more information, see: DLL Hijacking, Windows DLL Hijacking (Hopefully) Clarified, and Hijack Execution Flow: DLL Side-Loading
Whenever an executable is launched in a Windows system, the EXE file will almost always load secondary libraries containing executable code. These are formatted as dynamic link library (DLL) files which are often loaded from trusted system paths. Typically whenever a DLL is requested by the EXE file, the default behavior is to search a series of folders, starting with the EXE’s own folder, for any DLL that matches that name. The reason for this is that a software developer may choose to provide their own copy of a DLL file and that copy should take precedence over any provided by the system.
So what is a DLL sideloading attack? In general terms, an attacker simply finds a way to place a malicious DLL file in the same directory as a trusted EXE. If the EXE tries to load a system DLL with the same name, then the attacker’s DLL is loaded instead. What’s more, in many cases an attacker doesn’t even need to know what methods the EXE is planning on calling in the DLL because it is possible to build a DLL that runs code as soon as it is loaded (which is by design).
This puts any user account (service account or otherwise) at risk of compromise, provided those users will at some point launch the application. It is a particularly acute problem with Windows services though, because service configurations give attackers an easy way to force the issue immediately by simply adding a malicious DLL and then restarting the service (perhaps through a simple reboot). For an attacker, this is really a no-brainer. The level of sophistication required is low and a single DLL sideloading exploit kit can be used against nearly any software that has unsafe permissions in the installation folder.
You might be wondering why an intruder would bother with DLL sideloading if unsafe permissions means they can modify the EXE itself. As it turns out, it is far more common to have unsafe permissions that allow any user to add a new DLL, while not allowing modifications to the EXE itself. Read on for more details…
In an attempt to understand the pervasiveness of such misconfigurations, we conducted a small study in a number of customer environments using our DeepSurface software that automatically performs detailed service configuration analysis. We found that each customer had an average of five separate software packages (each of which was deployed on numerous hosts) that have permissions problems allowing for local privilege escalation. The remarkable thing is, only ~15% of vulnerable software services in our study were found in more than one customer environment! That means most of the services affected are fairly unique to you and are less likely to be identified by popular vulnerability scanners.
Since these deployment problems show up in a high diversity of obscure packages, there are almost never CVEs published for the issues, making them hard to ferret out, and fixing them won’t be as simple as applying a patch. Whenever an exploit is easy for an attacker to automate, but hard for defenders to fix automatically, it is a recipe for significant long-term risk.
Digging deeper, we found that the vast majority of these unsafe service deployments can be explained by examining two facts about the Windows platform:
Let me give you a concrete example. Suppose I create a new folder under “D:\tools” as a place to install third-party software. Here are the actual default permissions that will apply, as inherited from the parent folder:
The only way to see this level of detail is to dig deep through several layers of modal pop-ups in Windows Explorer: you need to first right click the D:\tools in explorer to view properties. Then, in the “Security” tab, view the permissions of the Users group and click into the advanced settings. From here, select the “Special” permissions and click “View”. You will then need to click the “Show advanced permissions” in the top right corner. Only then will the “Create files” and “Create folders” permissions appear.
Alternatively, you can see the full permissions list using command line tools, such as “get-acl . | fl“:
In other words, the fact that the folder grants all users on the system the right to add new files to this folder (and all sub-folders) is not obvious from the simplified (and legacy) permissions views which may give some developers and administrators a false sense of security.
The ability to append new files to a directory is really key. Even if your software installation locks down permissions on EXE files, existing DLL files, and sensitive configuration files, an attacker can still add additional malicious DLL files to the EXE’s folder. These DLLs would then be loaded when the EXE runs, leading to a trivial privilege escalation.
Why aren’t permissions set safely by the installers of third-party software packages? That’s a really good question. Part of the reason third-party software vendors and open source projects may not feel the need to set permissions explicitly during installation is because the default permissions on “C:\Program Files” are actually safe. That folder overrides the permissions of the parent folder (C:\) and strips away the append permission for local users. So if the administrator just installs a package in the default location, everything is likely safe. The problem comes when the administrator sets up their own folder for the installation. Still, I don’t think administrators should be held responsible for the permissions problem if the software package gives them the option to install elsewhere. In that case, the vendor is implicitly supporting that installation option. As such, they should be responsible for setting safe permissions.
As the saying goes, the first thing to do when you find yourself in a hole is to stop digging. For any new software being deployed in your Windows infrastructure, we recommend having a short security validation step that checks the permissions of all executables, DLLs, scripts and configuration files. For instance, your team could do a test deployment of the package (using the specific configuration you intend to roll out elsewhere) and then validate this installation is safe before moving forward. And we recommend you automate this, because you can! A combination of Powershell scripts and off-the-shelf security tools can greatly help you ensure your new software deployments are safe without a lot of on-going effort.
After you establish a process for new software deployments, you’ll almost certainly have a backlog of software and services that are deployed with questionable permissions. That’s where a good security scanning tool can help you identify and prioritize these in legacy deployments. Once you have found some service deployment permissions issues, your next question to ask yourself is: “Is this a result of unsafe third-party installers, or is this something our team caused by deploying the software in a custom way?”
If it is the former, then it likely makes sense for the software vendor to address the issue and provide you a fix. Depending on the vendor, this can take a bit of time and effort, but it might be safer than trying to fix the permissions yourself and risk breaking the package because it requires some permissions you weren’t aware of.
If you think your team is most likely responsible for the unsafe permissions in a service (or you need a stop-gap fix for a third-party mistake), then you can automate the fix by updating permissions on the installation folder with some simple scripting. As an example, the following Powershell script strips the append permission from the local Users group on a specified folder:
# This script strips the CreateFiles and AppendData permissions # from a specified directory for the local system Users group. # It is intended only as a simple example of how to remove # potentially dangerous permissions from folders that store # service executables. # # Released into the public domain by DeepSurface Security, Inc. # Original author: Robert Chen # # NOTE: This script is provided for educational purposes and # carries absolutely no warranty. Adapt as needed and use # only at your own risk # param([string]$path) if([string]::IsNullOrEmpty($path)) { echo "Usage: $PSCommandPath [path]"; exit } echo "Fixing permissions for: $path" $acl=get - acl "$path"; $createfilesrule = New - Object system.security.AccessControl.FileSystemAccessRule("BUILTIN \ Users ","CreateFiles",1,1,"Deny"); $acl.AddAccessRule($createfilesrule); $appenddatarule = New - Object system.security.AccessControl.FileSystemA ccessRule("BUILTIN \ Users ","AppendData",1,1,"Deny"); $acl.AddAccessRule($appenddatarule); Set - Acl - Path "$path" - AclObject $acl;
To sum everything up, unsafely configured Windows service permissions are an easy mistake to make, are often easily exploitable, and they provide an attacker with a huge attack surface to further escalate privileges. For these reasons they shouldn’t be ignored even if these issues don’t receive the same publicity as the latest batch of leaked red team tools.