Last year, I published a blog that got into a fair amount of depth on UEFI that was surprisingly popular, both at the time I posted it and again last month after an open source newsletter included a link to it. (Someone then copied the entire article and posted it on their site. Grr.) But the recent activity reminded me that there was one thing I couldn’t figure out how to do at the time: Enumerate all the available UEFI variables from within Windows. If you remember, Windows has API calls to get and set UEFI variable values, but not to enumerate them. So I started doing some more research to see if there was any way to do that – it’s obviously possible as the UEFI specs describe it, a UEFI shell can easily do it, and Linux does it (via a file system).
My research took me to a place I wouldn’t have expected: The Mimikatz source code. I suppose that makes sense, as hackers initially looked at UEFI as a potential way to exploit a system (and they found ways in some firmware implementations, which should have been fixed by now). So how do they do it? They call an NtEnumerateSystemEnvironmentValuesEx function in NTDLL.DLL. That function returns the full list in a buffer that you just need to walk through to extract the values. So using that source as a guide, I converted this to PowerShell and posted a new PowerShell module to the PowerShell Gallery:
(Note that there is an older UEFI package published at https://www.powershellgallery.com/packages/UEFI. That one is owned by Microsoft and I can no longer update it, so I published this new module as UEFIv2.)
With this module (which you can install with “Install-Module UEFIv2”) you can specify “-all” to get the full list:
This doesn’t retrieve the variable values, as some of the values are text, some are binary – they may need to be formatted appropriately to be useful.
You can try it if you want to see what I mean, just via a pipe:
Get-UEFIVariable -All | Get-UEFIVariable
Sure, that NtEnumerateSystemEnvironmentValuesEx entry point isn’t documented, but if it’s good enough for Mimikatz it’s good enough for me. (Use at your own risk, may not be supported in the future, blah, blah, blah.)
p.s. Thanks to Jeremiah Cox for pointing out an issue with the module, posted a new version that allocates a big enough buffer for larger variable values.
Categories: Geeking Out