From b03a684f8ffd4ced0a2e925b03d3f11755e33104 Mon Sep 17 00:00:00 2001 From: partouf Date: Sat, 9 Sep 2023 15:26:25 -0700 Subject: [PATCH] add desktop, various refactoring, soft fail for when home dir is set wrong, other fixes --- include/access.hpp | 2 ++ include/checks.hpp | 1 + include/exitcodes.hpp | 1 + src/access.cpp | 27 ++++++++++++++++- src/appcontainer.cpp | 4 +-- src/checks.cpp | 8 +++++ src/main.cpp | 70 +++++++++++++++++++++++++++---------------- 7 files changed, 84 insertions(+), 29 deletions(-) diff --git a/include/access.hpp b/include/access.hpp index f2add05..06ce0b4 100644 --- a/include/access.hpp +++ b/include/access.hpp @@ -9,4 +9,6 @@ namespace cewrapper void grant_access_to_path(wchar_t *container_sid, wchar_t *dir, uint32_t permissions); void grant_access_to_registry(wchar_t *container_sid, wchar_t *key, uint32_t permissions, registry_type_t regtype); +void remove_access_to_path(wchar_t *container_sid, wchar_t *dir, uint32_t permissions); + }; diff --git a/include/checks.hpp b/include/checks.hpp index 6eca533..27a109a 100644 --- a/include/checks.hpp +++ b/include/checks.hpp @@ -8,5 +8,6 @@ namespace cewrapper void OutputErrorMessage(DWORD err, const wchar_t *action); void CheckWin32(BOOL res, const wchar_t *action); void CheckStatus(DWORD status, const wchar_t *action); +void CheckStatusAllowFail(DWORD status, const wchar_t *action); }; // namespace cewrapper diff --git a/include/exitcodes.hpp b/include/exitcodes.hpp index 2d91d0b..a2dc73c 100644 --- a/include/exitcodes.hpp +++ b/include/exitcodes.hpp @@ -7,5 +7,6 @@ enum class SpecialExitCode : DWORD InvalidArgs = 2, ProcessTookTooLong = 3, ProcessTookTooLongMethod2 = 9, + ErrorWhenExecutingProcess = 254, UnknownErrorWhileWaitingOnProcess = 255, }; diff --git a/src/access.cpp b/src/access.cpp index 84fd496..9822e2c 100644 --- a/src/access.cpp +++ b/src/access.cpp @@ -22,7 +22,32 @@ void cewrapper::grant_access_to_path(wchar_t *container_sid, wchar_t *dir, uint3 ACL *newAcl = nullptr; cewrapper::CheckStatus(SetEntriesInAclW(1, &access, prevAcl, &newAcl), L"SetEntriesInAclW"); - cewrapper::CheckStatus(SetNamedSecurityInfoW(dir, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION, nullptr, nullptr, newAcl, nullptr), + cewrapper::CheckStatusAllowFail(SetNamedSecurityInfoW(dir, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION, nullptr, nullptr, newAcl, nullptr), + L"SetNamedSecurityInfoW"); +} + +void cewrapper::remove_access_to_path(wchar_t *container_sid, wchar_t *dir, uint32_t permissions) +{ + EXPLICIT_ACCESSW access = {}; + { + access.grfAccessPermissions = permissions; + access.grfAccessMode = REVOKE_ACCESS; + access.grfInheritance = OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE; + access.Trustee.TrusteeForm = TRUSTEE_IS_SID; + access.Trustee.TrusteeType = TRUSTEE_IS_GROUP; + access.Trustee.ptstrName = container_sid; + } + + PSECURITY_DESCRIPTOR pSecurityDescriptor = nullptr; + ACL *prevAcl = nullptr; + cewrapper::CheckStatus(GetNamedSecurityInfoW(dir, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION, nullptr, nullptr, + &prevAcl, nullptr, &pSecurityDescriptor), + L"GetNamedSecurityInfoW"); + + ACL *newAcl = nullptr; + cewrapper::CheckStatus(SetEntriesInAclW(1, &access, prevAcl, &newAcl), L"SetEntriesInAclW"); + cewrapper::CheckStatusAllowFail(SetNamedSecurityInfoW(dir, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION, nullptr, + nullptr, newAcl, nullptr), L"SetNamedSecurityInfoW"); } diff --git a/src/appcontainer.cpp b/src/appcontainer.cpp index a46e2af..31ace80 100644 --- a/src/appcontainer.cpp +++ b/src/appcontainer.cpp @@ -77,9 +77,9 @@ void cewrapper::AppContainer::InitializeCapabilities() sec_cap.CapabilityCount = 1; // https://learn.microsoft.com/en-us/previous-versions/windows/apps/hh780593(v=win.10)#diagnostic-tool-for-network-isolation - // CreateCapabilitySID(sec_cap.Capabilities, 0, WinCapabilityInternetClientSid); + //CreateCapabilitySID(sec_cap.Capabilities, 0, WinCapabilityInternetClientSid); // CreateCapabilitySID(sec_cap.Capabilities, 0, WinCapabilityInternetClientServerSid); - // CreateCapabilitySID(sec_cap.Capabilities, 0, WinCapabilityPrivateNetworkClientServerSid); + //CreateCapabilitySID(sec_cap.Capabilities, 1, WinCapabilityPrivateNetworkClientServerSid); // https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-erref/596a1078-e883-4972-9bbc-49e60bebca55 diff --git a/src/checks.cpp b/src/checks.cpp index b08a95e..c53c2b4 100644 --- a/src/checks.cpp +++ b/src/checks.cpp @@ -43,3 +43,11 @@ void cewrapper::CheckStatus(DWORD status, const wchar_t *action) OutputErrorMessage(status, action); throw std::exception("abort"); } + +void cewrapper::CheckStatusAllowFail(DWORD status, const wchar_t *action) +{ + if (status == ERROR_SUCCESS) + return; + + OutputErrorMessage(status, action); +} diff --git a/src/main.cpp b/src/main.cpp index 53176ad..5b2ec27 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -40,14 +40,19 @@ DWORD SpawnProcess(const cewrapper::Job &job, STARTUPINFOEX &si, HANDLE hUserTok if (config.extra_debugging) std::wcerr << "Running " << config.progid.c_str() << " " << cmdline.data() << "\n"; - // todo: find out the precedence of si.StartupInfo.dwFlags and those passed directly to CreateProcess - si.StartupInfo.dwFlags = CREATE_UNICODE_ENVIRONMENT | CREATE_SUSPENDED; + // sometimes a desktop is required, otherwise we can the process might end up with a 0xc0000142 exit code (STATUS_DLL_INIT_FAILED) + // https://comp.os.ms-windows.programmer.win32.narkive.com/cBqcgpKg/createprocessw-in-a-service-fails-with-error-c0000142-dll-initialization-failed-due-to-desktop + // https://www.betaarchive.com/wiki/index.php/Microsoft_KB_Archive/165194 + wchar_t *desktop = static_cast(malloc(64)); + lstrcpyW(desktop, L"winsta0\\default"); + si.StartupInfo.lpDesktop = desktop; PROCESS_INFORMATION pi = {}; if (hUserToken == nullptr) { cewrapper::CheckWin32(CreateProcessW(config.progid.c_str(), cmdline.data(), nullptr, nullptr, false, - EXTENDED_STARTUPINFO_PRESENT | CREATE_UNICODE_ENVIRONMENT, nullptr, + EXTENDED_STARTUPINFO_PRESENT | CREATE_UNICODE_ENVIRONMENT | CREATE_SUSPENDED, + nullptr, nullptr, &si.StartupInfo, &pi), L"CreateProcessW"); } @@ -80,7 +85,7 @@ DWORD SpawnProcess(const cewrapper::Job &job, STARTUPINFOEX &si, HANDLE hUserTok } } - DWORD app_exit_code = (DWORD)SpecialExitCode::UnknownErrorWhileWaitingOnProcess; + DWORD app_exit_code = std::to_underlying(SpecialExitCode::UnknownErrorWhileWaitingOnProcess); if (maxtime > 0 && timespent >= maxtime) { @@ -109,6 +114,8 @@ DWORD SpawnProcess(const cewrapper::Job &job, STARTUPINFOEX &si, HANDLE hUserTok CloseHandle(pi.hProcess); CloseHandle(pi.hThread); + free(desktop); + return app_exit_code; } @@ -122,8 +129,8 @@ DWORD execute_using_lower_rights(const cewrapper::Job &job) L"SaferComputeTokenFromLevel"); STARTUPINFOEX si; - ZeroMemory(&si, sizeof(STARTUPINFO)); - si.StartupInfo.cb = sizeof(STARTUPINFO); + ZeroMemory(&si, sizeof(STARTUPINFOEX)); + si.StartupInfo.cb = sizeof(STARTUPINFOEX); DWORD app_exit_code = SpawnProcess(job, si, hToken); @@ -154,23 +161,20 @@ DWORD execute_using_appcontainer(const cewrapper::Job &job) // todo: do we need to use this at some point? -> https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-deleteprocthreadattributelist } + std::wstring home_dir; + if (config.home_set) { - auto &dir = config.home; - if (config.debugging) - std::wcerr << "granting access to: " << dir << "\n"; - cewrapper::grant_access_to_path(container.getSid(), dir.data(), GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE); + home_dir = config.home; } else { - // access to its own directory - auto dir = std::filesystem::path(config.progid).parent_path().wstring(); - if (config.debugging) - std::wcerr << "granting access to: " << dir << "\n"; - cewrapper::grant_access_to_path(container.getSid(), dir.data(), GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE); + home_dir = std::filesystem::path(config.progid).parent_path().wstring(); } - // todo: revoke access from the home path after we're done executing if the path keeps existing (should actually just be deleted, so revoking really isn't needed) + if (config.debugging) + std::wcerr << "granting access to: " << home_dir << "\n"; + cewrapper::grant_access_to_path(container.getSid(), home_dir.data(), GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE); for (auto &allowed : config.allowed_dirs) { @@ -182,14 +186,20 @@ DWORD execute_using_appcontainer(const cewrapper::Job &job) for (auto &allowed : config.allowed_registry) { if (config.debugging) - std::wcerr << "granting access to registry: " << allowed.path << ", r" << allowed.rights << "\n"; + std::wcerr << "granting access to registry: " << allowed.path << ", r" << std::hex << allowed.rights << "\n"; cewrapper::grant_access_to_registry(container.getSid(), allowed.path.data(), allowed.rights, allowed.type); } if (config.wait_before_spawn) - Sleep(10000); + Sleep(5000); - return SpawnProcess(job, si); + auto processExitCode = SpawnProcess(job, si); + + if (config.debugging) + std::wcerr << "revoking access to: " << home_dir << "\n"; + cewrapper::remove_access_to_path(container.getSid(), home_dir.data(), GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE); + + return processExitCode; } int wmain(int argc, wchar_t *argv[]) @@ -199,7 +209,7 @@ int wmain(int argc, wchar_t *argv[]) std::wcerr << L"Too few arguments\n"; std::wcerr << L"Usage: cewrapper.exe [-v] [--config=/full/path/to/config.json] [--home=/preferred/cwdpath] " L"[--time_limit=1] ExePath [args]\n"; - return (int)SpecialExitCode::NotEnoughArgs; + return std::to_underlying(SpecialExitCode::NotEnoughArgs); } try @@ -211,19 +221,27 @@ int wmain(int argc, wchar_t *argv[]) if (cewrapper::Config::get().debugging) std::cerr << e.what() << "\n"; std::wcerr << L"Invalid arguments\n"; - return (int)SpecialExitCode::InvalidArgs; + return std::to_underlying(SpecialExitCode::InvalidArgs); } cewrapper::Job job(cewrapper::Config::get()); - DWORD app_exit_code{}; - if (cewrapper::Config::get().use_appcontainer) + DWORD app_exit_code = std::to_underlying(SpecialExitCode::ErrorWhenExecutingProcess); + try { - app_exit_code = execute_using_appcontainer(job); + if (cewrapper::Config::get().use_appcontainer) + { + app_exit_code = execute_using_appcontainer(job); + } + else + { + app_exit_code = execute_using_lower_rights(job); + } } - else + catch (std::exception &e) { - app_exit_code = execute_using_lower_rights(job); + if (cewrapper::Config::get().debugging) + std::cerr << e.what() << "\n"; } return app_exit_code;