From 9af6ee7031536ccb91fc2eb2f8521a0ac286db2c Mon Sep 17 00:00:00 2001 From: Nir Bar Date: Thu, 3 Apr 2025 09:26:20 +0300 Subject: Support launching rundll32.exe as a safe executable if the dll it loads is in a secure location --- src/burn/engine/approvedexe.cpp | 105 +++++++++++++++++++++++++++++++- src/burn/engine/approvedexe.h | 4 +- src/burn/engine/bundlepackageengine.cpp | 2 +- src/burn/engine/elevation.cpp | 2 +- src/burn/engine/exeengine.cpp | 29 +++++---- 5 files changed, 125 insertions(+), 17 deletions(-) (limited to 'src/burn/engine') diff --git a/src/burn/engine/approvedexe.cpp b/src/burn/engine/approvedexe.cpp index 28b26d6d..383ee7fa 100644 --- a/src/burn/engine/approvedexe.cpp +++ b/src/burn/engine/approvedexe.cpp @@ -3,6 +3,13 @@ #include "precomp.h" +// internal function declarations + +static HRESULT IsRunDll32( + __in BURN_VARIABLES* pVariables, + __in LPCWSTR wzExecutablePath + ); + // function definitions extern "C" HRESULT ApprovedExesParseFromXml( @@ -221,12 +228,15 @@ LExit: extern "C" HRESULT ApprovedExesVerifySecureLocation( __in BURN_CACHE* pCache, __in BURN_VARIABLES* pVariables, - __in LPCWSTR wzExecutablePath + __in LPCWSTR wzExecutablePath, + __in int argc, + __in LPCWSTR* argv ) { HRESULT hr = S_OK; LPWSTR scz = NULL; LPWSTR sczSecondary = NULL; + LPWSTR sczRunDll32Param = NULL; const LPCWSTR vrgSecureFolderVariables[] = { L"ProgramFiles64Folder", @@ -273,11 +283,104 @@ extern "C" HRESULT ApprovedExesVerifySecureLocation( ExitFunction(); } + // Test if executable is rundll32.exe, and it's target is in a secure location + // Example for CUDA UninstallString: "C:\WINDOWS\SysWOW64\RunDll32.EXE" "C:\Program Files\NVIDIA Corporation\Installer2\InstallerCore\NVI2.DLL",UninstallPackage CUDAToolkit_12.8 + if (argc && argv && argv[0] && *argv[0]) + { + hr = IsRunDll32(pVariables, wzExecutablePath); + ExitOnFailure(hr, "Failed to test whether executable is rundll32"); + + if (hr == S_OK) + { + LPCWSTR szComma = wcschr(argv[0], L','); + if (szComma && *szComma) + { + hr = StrAllocString(&sczRunDll32Param, argv[0], szComma - argv[0]); + ExitOnFailure(hr, "Failed to allocate string"); + } + else + { + hr = StrAllocString(&sczRunDll32Param, argv[0], 0); + ExitOnFailure(hr, "Failed to allocate string"); + } + + hr = ApprovedExesVerifySecureLocation(pCache, pVariables, sczRunDll32Param, 0, NULL); + ExitOnFailure(hr, "Failed to test whether rundll32's parameter, '%ls', is in a secure location", sczRunDll32Param); + if (hr == S_OK) + { + ExitFunction(); + } + } + } + hr = S_FALSE; LExit: ReleaseStr(scz); ReleaseStr(sczSecondary); + ReleaseStr(sczRunDll32Param); + + return hr; +} + +static HRESULT IsRunDll32( + __in BURN_VARIABLES* pVariables, + __in LPCWSTR wzExecutablePath + ) +{ + HRESULT hr = S_OK; + LPWSTR sczFolder = NULL; + LPWSTR sczFullPath = NULL; + BOOL fEqual = FALSE; + + hr = VariableGetString(pVariables, L"SystemFolder", &sczFolder); + ExitOnFailure(hr, "Failed to get the variable: SystemFolder"); + + hr = PathConcat(sczFolder, L"rundll32.exe", &sczFullPath); + ExitOnFailure(hr, "Failed to combine paths"); + + hr = PathCompareCanonicalized(wzExecutablePath, sczFullPath, &fEqual); + ExitOnFailure(hr, "Failed to compare paths"); + if (fEqual) + { + hr = S_OK; + ExitFunction(); + } + + hr = VariableGetString(pVariables, L"System64Folder", &sczFolder); + ExitOnFailure(hr, "Failed to get the variable: System64Folder"); + + hr = PathConcat(sczFolder, L"rundll32.exe", &sczFullPath); + ExitOnFailure(hr, "Failed to combine paths"); + + hr = PathCompareCanonicalized(wzExecutablePath, sczFullPath, &fEqual); + ExitOnFailure(hr, "Failed to compare paths"); + if (fEqual) + { + hr = S_OK; + ExitFunction(); + } + + // Sysnative + hr = PathSystemWindowsSubdirectory(L"SysNative\\", &sczFolder); + ExitOnFailure(hr, "Failed to append SysNative directory."); + + hr = PathConcat(sczFolder, L"rundll32.exe", &sczFullPath); + ExitOnFailure(hr, "Failed to combine paths"); + + hr = PathCompareCanonicalized(wzExecutablePath, sczFullPath, &fEqual); + ExitOnFailure(hr, "Failed to compare paths"); + if (fEqual) + { + hr = S_OK; + ExitFunction(); + } + + hr = S_FALSE; + +LExit: + ReleaseStr(sczFolder); + ReleaseStr(sczFullPath); return hr; } diff --git a/src/burn/engine/approvedexe.h b/src/burn/engine/approvedexe.h index 7a68c174..0f441485 100644 --- a/src/burn/engine/approvedexe.h +++ b/src/burn/engine/approvedexe.h @@ -58,7 +58,9 @@ HRESULT ApprovedExesLaunch( HRESULT ApprovedExesVerifySecureLocation( __in BURN_CACHE* pCache, __in BURN_VARIABLES* pVariables, - __in LPCWSTR wzExecutablePath + __in LPCWSTR wzExecutablePath, + __in int argc, + __in LPCWSTR* argv ); diff --git a/src/burn/engine/bundlepackageengine.cpp b/src/burn/engine/bundlepackageengine.cpp index 574317e1..db433918 100644 --- a/src/burn/engine/bundlepackageengine.cpp +++ b/src/burn/engine/bundlepackageengine.cpp @@ -817,7 +817,7 @@ static HRESULT ExecuteBundle( if (pPackage->fPerMachine) { - hr = ApprovedExesVerifySecureLocation(pCache, pVariables, sczExecutablePath); + hr = ApprovedExesVerifySecureLocation(pCache, pVariables, sczExecutablePath, argcArp - 1, (argcArp > 1) ? const_cast(argvArp + 1) : NULL); ExitOnFailure(hr, "Failed to verify the QuietUninstallString executable path is in a secure location: %ls", sczExecutablePath); if (S_FALSE == hr) { diff --git a/src/burn/engine/elevation.cpp b/src/burn/engine/elevation.cpp index 90e9db01..f208efc9 100644 --- a/src/burn/engine/elevation.cpp +++ b/src/burn/engine/elevation.cpp @@ -3876,7 +3876,7 @@ static HRESULT OnLaunchApprovedExe( hr = RegReadString(hKey, pApprovedExe->sczValueName, &pLaunchApprovedExe->sczExecutablePath); ExitOnFailure(hr, "Failed to read the value for the approved exe path."); - hr = ApprovedExesVerifySecureLocation(pCache, pVariables, pLaunchApprovedExe->sczExecutablePath); + hr = ApprovedExesVerifySecureLocation(pCache, pVariables, pLaunchApprovedExe->sczExecutablePath, 0, NULL); ExitOnFailure(hr, "Failed to verify the executable path is in a secure location: %ls", pLaunchApprovedExe->sczExecutablePath); if (S_FALSE == hr) { diff --git a/src/burn/engine/exeengine.cpp b/src/burn/engine/exeengine.cpp index 85168943..3a2816dd 100644 --- a/src/burn/engine/exeengine.cpp +++ b/src/burn/engine/exeengine.cpp @@ -489,29 +489,30 @@ extern "C" HRESULT ExeEngineExecutePackage( } else if (BURN_EXE_DETECTION_TYPE_ARP == pPackage->Exe.detectionType && BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pExecuteAction->exePackage.action) { - ExitOnNull(sczArpUninstallString, hr, E_INVALIDARG, "%hs is null.", pPackage->Exe.fArpUseUninstallString ? "UninstallString" : "QuietUninstallString"); + LPCWSTR szRegName = pPackage->Exe.fArpUseUninstallString ? L"UninstallString" : L"QuietUninstallString"; + ExitOnNull(sczArpUninstallString, hr, E_INVALIDARG, "%ls is null.", szRegName); hr = AppParseCommandLine(sczArpUninstallString, &argcArp, &argvArp); - ExitOnFailure(hr, "Failed to parse QuietUninstallString: %ls.", sczArpUninstallString); + ExitOnFailure(hr, "Failed to parse %ls: %ls.", szRegName, sczArpUninstallString); - ExitOnNull(argcArp, hr, E_INVALIDARG, "QuietUninstallString must contain an executable path."); + ExitOnNull(argcArp, hr, E_INVALIDARG, "%ls must contain an executable path.", szRegName); hr = StrAllocString(&sczExecutablePath, argvArp[0], 0); ExitOnFailure(hr, "Failed to copy executable path."); if (pPackage->fPerMachine) { - hr = ApprovedExesVerifySecureLocation(pCache, pVariables, sczExecutablePath); - ExitOnFailure(hr, "Failed to verify the QuietUninstallString executable path is in a secure location: %ls", sczExecutablePath); + hr = ApprovedExesVerifySecureLocation(pCache, pVariables, sczExecutablePath, argcArp - 1, (argcArp > 1) ? const_cast(argvArp + 1) : NULL); + ExitOnFailure(hr, "Failed to verify the %ls executable path is in a secure location: %ls", szRegName, sczExecutablePath); if (S_FALSE == hr) { - LogStringLine(REPORT_STANDARD, "The QuietUninstallString executable path is not in a secure location: %ls", sczExecutablePath); + LogStringLine(REPORT_STANDARD, "The %ls executable path is not in a secure location: %ls", szRegName, sczExecutablePath); ExitFunction1(hr = HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED)); } } hr = PathGetDirectory(sczExecutablePath, &sczCachedDirectory); - ExitOnFailure(hr, "Failed to get parent directory for QuietUninstallString executable path: %ls", sczExecutablePath); + ExitOnFailure(hr, "Failed to get parent directory for %ls executable path: %ls", szRegName, sczExecutablePath); } else { @@ -587,13 +588,15 @@ extern "C" HRESULT ExeEngineExecutePackage( } // build base command - hr = StrAllocFormatted(&sczBaseCommand, L"\"%ls\"", sczExecutablePath); - ExitOnFailure(hr, "Failed to allocate base command."); - - for (int i = 1; i < argcArp; ++i) + if (sczArpUninstallString && *sczArpUninstallString) + { + hr = StrAllocString(&sczBaseCommand, sczArpUninstallString, 0); + ExitOnFailure(hr, "Failed to allocate base command."); + } + else { - hr = AppAppendCommandLineArgument(&sczBaseCommand, argvArp[i]); - ExitOnFailure(hr, "Failed to append argument from ARP."); + hr = StrAllocFormatted(&sczBaseCommand, L"\"%ls\"", sczExecutablePath); + ExitOnFailure(hr, "Failed to allocate base command."); } if (pPackage->Exe.fBundle) -- cgit v1.2.3-55-g6feb