fix(cve): run switch commands as normal user on Windows to prevent privilege escalation
CVE-2026-41477
This commit is contained in:
parent
e7040a1f82
commit
5c480ca515
6 changed files with 89 additions and 9 deletions
|
|
@ -73,6 +73,7 @@ int main(int argc, char **argv)
|
||||||
// It's important to write the version number to the log file so we can be certain the old daemon
|
// It's important to write the version number to the log file so we can be certain the old daemon
|
||||||
// was uninstalled, since sometimes Windows services can get stuck and fail to be removed.
|
// was uninstalled, since sometimes Windows services can get stuck and fail to be removed.
|
||||||
LOG_PRINT("%s v%s", qPrintable(QCoreApplication::applicationName()), kDisplayVersion);
|
LOG_PRINT("%s v%s", qPrintable(QCoreApplication::applicationName()), kDisplayVersion);
|
||||||
|
LOG_INFO("settings file: %s", qPrintable(Settings::settingsFile()));
|
||||||
|
|
||||||
// Default log level to system setting (found in Registry).
|
// Default log level to system setting (found in Registry).
|
||||||
auto logLevel = Settings::value(Settings::Daemon::LogLevel).toString().toStdString();
|
auto logLevel = Settings::value(Settings::Daemon::LogLevel).toString().toStdString();
|
||||||
|
|
|
||||||
|
|
@ -91,6 +91,10 @@ add_library(${lib_name} STATIC ${PLATFORM_CODE}
|
||||||
|
|
||||||
target_link_libraries(${lib_name} PUBLIC common Qt6::Core Qt6::Network)
|
target_link_libraries(${lib_name} PUBLIC common Qt6::Core Qt6::Network)
|
||||||
|
|
||||||
|
if(WIN32)
|
||||||
|
target_link_libraries(${lib_name} PRIVATE platform)
|
||||||
|
endif()
|
||||||
|
|
||||||
if(UNIX)
|
if(UNIX)
|
||||||
target_link_libraries(
|
target_link_libraries(
|
||||||
${lib_name}
|
${lib_name}
|
||||||
|
|
|
||||||
|
|
@ -12,8 +12,35 @@
|
||||||
|
|
||||||
#include <QProcess>
|
#include <QProcess>
|
||||||
|
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
#include "arch/win32/ArchMiscWindows.h"
|
||||||
|
#include "platform/MSWindowsProcess.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace deskflow {
|
namespace deskflow {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
bool runScreenCommand(const QString &commandLine)
|
||||||
|
{
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
using deskflow::platform::MSWindowsProcess;
|
||||||
|
if (ArchMiscWindows::isProcessElevated()) {
|
||||||
|
LOG_DEBUG("current process is elevated, starting detached process as session user");
|
||||||
|
return MSWindowsProcess::startDetachedAsSessionUser(commandLine.toStdWString());
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
auto args = QProcess::splitCommand(commandLine);
|
||||||
|
if (args.isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const auto program = args.takeFirst();
|
||||||
|
return QProcess::startDetached(program, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
//
|
//
|
||||||
// Screen
|
// Screen
|
||||||
//
|
//
|
||||||
|
|
@ -123,10 +150,9 @@ void Screen::enter(KeyModifierMask toggleMask)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Settings::value(Settings::Core::EnableEnterCommand).toBool()) {
|
if (Settings::value(Settings::Core::EnableEnterCommand).toBool()) {
|
||||||
auto args = QProcess::splitCommand(Settings::value(Settings::Core::ScreenEnterCommand).toString());
|
const auto commandLine = Settings::value(Settings::Core::ScreenEnterCommand).toString();
|
||||||
const auto command = args.takeFirst();
|
LOG_DEBUG("running screen enter command: %s", qPrintable(commandLine));
|
||||||
LOG_DEBUG("running screen enter command: %s %s", qPrintable(command), qPrintable(args.join(" ")));
|
if (!runScreenCommand(commandLine))
|
||||||
if (!QProcess::startDetached(command, args))
|
|
||||||
LOG_ERR("failed to run screen enter command");
|
LOG_ERR("failed to run screen enter command");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -151,10 +177,9 @@ bool Screen::leave()
|
||||||
|
|
||||||
m_screen->leave();
|
m_screen->leave();
|
||||||
if (Settings::value(Settings::Core::EnableExitCommand).toBool()) {
|
if (Settings::value(Settings::Core::EnableExitCommand).toBool()) {
|
||||||
auto args = QProcess::splitCommand(Settings::value(Settings::Core::ScreenExitCommand).toString());
|
const auto commandLine = Settings::value(Settings::Core::ScreenExitCommand).toString();
|
||||||
const auto command = args.takeFirst();
|
LOG_DEBUG("running screen exit command: %s", qPrintable(commandLine));
|
||||||
LOG_DEBUG("running screen exit command: %s %s", qPrintable(command), qPrintable(args.join(" ")));
|
if (!runScreenCommand(commandLine))
|
||||||
if (!QProcess::startDetached(command, args))
|
|
||||||
LOG_ERR("failed to run screen exit command");
|
LOG_ERR("failed to run screen exit command");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@
|
||||||
#include "base/Log.h"
|
#include "base/Log.h"
|
||||||
#include "common/Constants.h"
|
#include "common/Constants.h"
|
||||||
#include "common/ExitCodes.h"
|
#include "common/ExitCodes.h"
|
||||||
|
#include "platform/MSWindowsSession.h"
|
||||||
|
|
||||||
#define WIN32_LEAN_AND_MEAN
|
#define WIN32_LEAN_AND_MEAN
|
||||||
#include <Windows.h>
|
#include <Windows.h>
|
||||||
|
|
@ -188,6 +189,51 @@ void MSWindowsProcess::shutdown(HANDLE handle, DWORD pid, int timeout)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool MSWindowsProcess::startDetachedAsSessionUser(const std::wstring &command)
|
||||||
|
{
|
||||||
|
MSWindowsSession session;
|
||||||
|
session.updateActiveSession();
|
||||||
|
|
||||||
|
HANDLE userToken = nullptr;
|
||||||
|
try {
|
||||||
|
userToken = session.getUserToken(nullptr);
|
||||||
|
} catch (const std::runtime_error &e) {
|
||||||
|
LOG_ERR("could not get session user token: %s", e.what());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
LPVOID environment = nullptr;
|
||||||
|
if (!CreateEnvironmentBlock(&environment, userToken, FALSE)) {
|
||||||
|
LOG_ERR("could not create environment block, error: %s", windowsErrorToString(GetLastError()).c_str());
|
||||||
|
CloseHandle(userToken);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
STARTUPINFOW si = {};
|
||||||
|
si.cb = sizeof(si);
|
||||||
|
PROCESS_INFORMATION pi = {};
|
||||||
|
|
||||||
|
std::wstring mutableCommand = command;
|
||||||
|
const DWORD flags = CREATE_UNICODE_ENVIRONMENT | CREATE_NEW_CONSOLE;
|
||||||
|
|
||||||
|
LOG_DEBUG("starting detached process as session user, command: %s", command.c_str());
|
||||||
|
const BOOL ok = CreateProcessAsUserW(
|
||||||
|
userToken, nullptr, mutableCommand.data(), nullptr, nullptr, FALSE, flags, environment, nullptr, &si, &pi
|
||||||
|
);
|
||||||
|
|
||||||
|
DestroyEnvironmentBlock(environment);
|
||||||
|
CloseHandle(userToken);
|
||||||
|
|
||||||
|
if (!ok) {
|
||||||
|
LOG_ERR("could not start process as session user, error: %s", windowsErrorToString(GetLastError()).c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
CloseHandle(pi.hProcess);
|
||||||
|
CloseHandle(pi.hThread);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void MSWindowsProcess::createPipes()
|
void MSWindowsProcess::createPipes()
|
||||||
{
|
{
|
||||||
SECURITY_ATTRIBUTES saAttr;
|
SECURITY_ATTRIBUTES saAttr;
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,10 @@ public:
|
||||||
|
|
||||||
static void shutdown(HANDLE handle, DWORD pid, int timeout = kDefaultShutdownTimeout);
|
static void shutdown(HANDLE handle, DWORD pid, int timeout = kDefaultShutdownTimeout);
|
||||||
|
|
||||||
|
/// Launch @p command as a detached process under the active console user's token, so the
|
||||||
|
/// child does not inherit the caller's (SYSTEM) privileges.
|
||||||
|
static bool startDetachedAsSessionUser(const std::wstring &command);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void setStartupInfo(STARTUPINFO &si);
|
void setStartupInfo(STARTUPINFO &si);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
# SPDX-License-Identifier: MIT
|
# SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
set(extra_libs version)
|
set(extra_libs platform version)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
create_test(
|
create_test(
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue