itm4n / PrivescCheck

Privilege Escalation Enumeration Script for Windows
BSD 3-Clause "New" or "Revised" License
2.79k stars 416 forks source link

Add check for sensitive hive files in shadow copies (HiveNightmare) #23

Closed SAERXCIT closed 2 years ago

SAERXCIT commented 2 years ago

Hi Clément!

This PR adds a new helper function (Get-ShadowCopies) and a new check (Invoke-SensitiveHiveShadowCopyCheck). It will check if the user has read access on the SAM/SECURITY/SYSTEM hives in all existing shadow copies.

Why

Microsoft has patched this August CVE-2021-36934, but as noted in the advisory: To avoid deleting data without users' consent, we have opted to allow users to delete their shadow copies themselves. Since manual intervention is required to remove/regenerate shadow copies after applying this update, this vuln might stay with us for a long time. As such, I felt a check for it was a good addition to PrivescCheck.

How

Get-ShadowCopies uses the Win32 functions NtOpenDirectoryObject and NtQueryDirectoryObject from ntdll.dll to list all existing shadow copies. I had to add their definition to src/01_Win32.ps1 since they were not present there.

Invoke-SensitiveHiveShadowCopyCheck calls [IO.File]::Open on each hive file using Read access, as Get-Acl does not work on the type of paths used (\\?\GLOBALROOT\...). If this call succeeds, I consider that the user has read access on the hive file. I don't know of any scenario where that would not be the case. Additionally, the file LastWriteTime is printed to help find the most recent copy available.

Example

PS C:\> Get-ShadowCopies

Name                      Path
----                      ----
HarddiskVolumeShadowCopy1 \\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy1
HarddiskVolumeShadowCopy2 \\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy2

PS C:\> Invoke-SensitiveHiveShadowCopyCheck

ShadowCopy                Path                                                                             LastWriteTime       UserCanRead
----------                ----                                                                             -------------       -----------
HarddiskVolumeShadowCopy1 \\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy1\Windows\System32\config\SAM      13/08/2021 10:10:52 True
HarddiskVolumeShadowCopy1 \\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy1\Windows\System32\config\SECURITY 13/08/2021 10:10:52 True
HarddiskVolumeShadowCopy1 \\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy1\Windows\System32\config\SYSTEM   13/08/2021 10:10:52 True

Here, HarddiskVolumeShadowCopy1 was created before applying the update/mitigation (icacls $env:windir\system32\config\*.* /inheritance:e), and HarddiskVolumeShadowCopy2 after, so as expected HarddiskVolumeShadowCopy1 is still vulnerable.

This has only been tested on one VM so further testing is welcome :upside_down_face:

Cheers!

itm4n commented 2 years ago

HI! I took a quick look at your PR. Your code looks really great! :astonished: This will be a really nice addition to PrivescCheck. I'll just have to do some tests this weekend but I'm sure it will be fine. 🙂

SAERXCIT commented 2 years ago

Thanks! To be honest most of it is strongly inspired from the links in Get-ShadowCopies comments, so thanks to them. Just a heads up, the code does not work in PSv2 as [IO.File]::Open does not accept paths starting with \\?\GLOBALROOT. I'm currently trying to rewrite it using CreateFile and GetFileAttributesEx.

SAERXCIT commented 2 years ago

Pushed a hopefully fixed version, it now works in my powershell -version 2 shell.

The check is now performed using CreateFile with ReadData access, and I consider that access is possible if the handle returned is not -1. Let me know if this might generate false positives.

Additionally, the last write time is now retrieved using GetFileAttributesEx. I had to add some more Win32 stuff, so if this is not wanted, ditch it, it's not the most important part :upside_down_face:

itm4n commented 2 years ago

In the end, I could not use the code of your PR as is. I had to do a lot of modifications. So I didn't merge it but I used your code as a solid base for my own commit.

Main & Win32

Get-ShadowCopies

Helpers

Credentials

PS C:\> Invoke-SensitiveHiveShadowCopyCheck | fl

Volume            : HarddiskVolumeShadowCopy3
Path              : \\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy3\Windows\System32\config\SAM
IdentityReference : BUILTIN\Users
AccessRights      : ReadData, ReadExtendedAttributes, Execute, ReadAttributes, ReadControl, Synchronize

Volume            : HarddiskVolumeShadowCopy3
Path              : \\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy3\Windows\System32\config\SECURITY
IdentityReference : BUILTIN\Users
AccessRights      : ReadData, ReadExtendedAttributes, Execute, ReadAttributes, ReadControl, Synchronize

Volume            : HarddiskVolumeShadowCopy3
Path              : \\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy3\Windows\System32\config\SYSTEM
IdentityReference : BUILTIN\Users
AccessRights      : ReadData, ReadExtendedAttributes, Execute, ReadAttributes, ReadControl, Synchronize
SAERXCIT commented 2 years ago

Great! Thanks a lot for your work, the detailed changelog, and the shout out :) I'm glad PrivescCheck now checks for that.

There was a lot of issues with my code haha, as you could see I had never programmed for Windows anything more advanced than a dumb DLL with commands in a WinExec, so I got confused :upside_down_face: For that I greatly appreciate your explanations!

Regarding the last write time info, it was quickly determine the most recent copy containing the most up-to-date data, in the edge case where multiple shadow copies exist and the last one is not for some reason the most recent one. I don't even know if this is possible so as you said this is probably irrelevant.

Closing the PR, cheers!