Earlier this month, Microsoft released an advisory for CVE-2020-1317 which describes a privilege escalation vulnerability in Group Policy. This was further detailed by the discoverer of the vulnerability on the Cyberark website. The nature of this issue is interesting and worth understanding. For years, Group Policy has had this dichotomy built into it’s design. Namely, the need to deliver per-user policy to otherwise unprivileged users, through a mechanism that runs as a privileged process–namely the Group Policy engine. Microsoft has handled this in a variety of ways. For example, per-user Administrative Templates policy was delivered to a set of two registry keys in HKEY_CURRENT_USER that happened to be permissioned such that a user could not write to them, even though they could write to pretty much any other key in this hive. But in the case of this CVE, there were a couple of glaring holes. The first of these was detailed in the Cyberark blog post. Namely, GP Preferences has always had the concept of history files. These files were created to essentially undo the state of certain GP Preferences when those settings no longer applied. These history files were essentially copies of the actual settings file that was stored in SYSVOL for the GPO delivering the settings, and were stored in the user’s profile under %appdata%\Local\Microsoft\Group Policy, as you can see here:
The location of this file was the key to this privesc vulnerability. the files themselves are stored in the user’s profile in an area that a regular, non-privileged user has full rights to. However, the files are written here whenever a new GPP setting is processed, or the setting is changed, by the GP engine, which itself runs as localSystem. So, we have a localSystem process writing data to unprivileged user space, and this is where the fun begins.
For this particular exploit, the author used Object Manager symbolic links to redirect any arbitrary GPP XML file to a location in c:\window\system32, where it can be exploited by a further process to essentially open a process running as localSystem. The details of the actual escalation code to localSystem are not shown, but the use of OM symbolic links (and the exploitation of other types of Windows symbolic links), is described in this excellent video from James Forshaw, who also has sample code for creating these links here. Suffice it to say that to exploit this, an attacker has only to delete the file that GPP left in their user profile, and replace it with an OM symbolic link. The GP Engine will then come along, happily running as local system, and essentially write a new XML file to whatever the destination that is specified in c:\windows\system32. They can then write whatever content they want to that file and exploit it from there. Pretty slick.
Alternative Exploit Paths & the Patch
When I first saw this exploit described, I wondered out loud if GPP History files were the only place where this could be exploited. I knew for a fact that my good friend, the registry archive file (ntuser.pol), was another one of these per-user GPO files that were held in the user’s profile, and writeable by the user, but updated by the GP Engine. So it was logical to assume that this file could also be exploited in the same way as GP Preferences history files. So when I look at a Windows system after applying the patch for this vulnerability, I noted some interesting things. First, the way Microsoft solved this, was to move GPP history files out of the user profile, and into the protected %programdata% folder (specifically under %programdata%\Microsoft\Group Policy\Users). This folder is not writeable by normal users, which helps to avoid the use of symbolic links in the previous version. The full path to the file is a bit wonky, as you can see here:
The folder structure is per-user (i.e. the SID folder path), then per GPO (the GPO GUID), then per-user again (another SID folder), which seems a bit excessive, but I’m sure they had a reason for it 🙂
The next question I was curious about, was the ntuser.pol file I mentioned above. Sure enough, Microsoft also moved that file to a location in %programdata% as well, albeit in a different folder structure from GPP history files. Namely, under %programdata%\Microsoft\GroupPolicy\Users\<SID of User>. I thought it was mildly funny that they have two folder structures under the Microsoft folder, one called “Group Policy” and the other called “GroupPolicy”. Oh well. This latter path is also where the per-user Group Policy Cache files are kept. See this article for a review of the Group Policy cache.
Now, according to the author of this exploit, it took Microsoft nearly a year to fix this. I can absolutely imagine this being the case. Given that, between all the GP Preferences areas, and, all the areas that use ntuser.pol, including Admin Templates, AppLocker, Windows Firewall, Public Key Policy and probably a few I’m missing, that could have been potentially a lot of code to go through to make sure moving those files didn’t break everything. I wouldn’t be surprised if some breakage does still show up in some obscure corner of policy before this is all over. Nevertheless, this was an interesting exploit, made more interesting by some 20 year old warts that GP brought to the party.