Skip to content

Instantly share code, notes, and snippets.

@jthuraisamy
Last active August 17, 2023 13:09
Show Gist options
  • Save jthuraisamy/e602d5d870230df3ce00178001f9ac16 to your computer and use it in GitHub Desktop.
Save jthuraisamy/e602d5d870230df3ce00178001f9ac16 to your computer and use it in GitHub Desktop.
GospelRoom: Data Storage in UEFI NVRAM Variables

GospelRoom: Data Storage in UEFI NVRAM Variables

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. Administrative access required (SE_SYSTEM_ENVIRONMENT_NAME privilege).
  3. Buffer size is limited up to the size of the flash chip.
  4. Possible to enumerate from kernel mode.

Example Output

[SET] TestVariable: Hello, NVRAM!
[GET] TestVariable: Hello, NVRAM!

References

/**
* 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;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment