Merge pull request #22 from allamiro/bugfix/issue-2-escape-wrong-layer-createprocess

Fix #2 - replace CMD shell escaping with correct CreateProcess quoting
This commit is contained in:
Token2
2026-06-09 16:21:55 +02:00
committed by GitHub
+37 -53
View File
@@ -26,42 +26,32 @@ struct PasskeyInfo {
std::wstring domain;
};
std::wstring EscapeCommandLineArgument(const std::wstring& input) {
std::wstring escaped = L"\""; // Start with a quote to properly handle spaces
// Quotes one argument for a CreateProcess command string using the
// Windows C-runtime parsing rules: wrap in double quotes, double any
// backslashes that immediately precede a double quote or the closing
// quote, and escape embedded double quotes with a backslash.
// Do NOT use this for cmd.exe shell strings (ShellExecute paths).
std::wstring QuoteArg(const std::wstring& arg) {
std::wstring result = L"\"";
int backslashes = 0;
for (wchar_t ch : input) {
switch (ch) {
case L'"': escaped += L"\\\""; break; // Escape double quotes
case L'^':
case L'&':
case L'|':
case L'<':
case L'>':
case L'%':
case L'!':
case L'(':
case L')':
case L'=':
case L';':
case L'`':
case L',':
case L'[':
case L']':
case L'{':
case L'}':
case L'*':
case L'?':
case L'\\': // Escape special characters for CMD
escaped += L'^';
escaped += ch;
break;
default:
escaped += ch;
for (wchar_t c : arg) {
if (c == L'\\') {
backslashes++;
} else if (c == L'"') {
result.append(backslashes * 2, L'\\');
result += L"\\\"";
backslashes = 0;
} else {
result.append(backslashes, L'\\');
result += c;
backslashes = 0;
}
}
escaped += L"\""; // End with a quote
return escaped;
result.append(backslashes * 2, L'\\');
result += L'"';
return result;
}
@@ -140,7 +130,7 @@ std::vector<PasskeyInfo> ParseResidentKeys(const std::vector<std::wstring>& outp
std::vector<std::wstring> GetDomains(const std::wstring& deviceNumber, const std::wstring& globalPin) {
// Command for the first run
std::wstring command = L".\\fido2-manage.exe -residentkeys -device " + deviceNumber + L" -pin " + globalPin;
std::wstring command = L".\\fido2-manage.exe -residentkeys -device " + QuoteArg(deviceNumber) + L" -pin " + globalPin;
// Debug: Show the command being executed
//MessageBox(NULL, command.c_str(), L"Debug: Command Being Executed", MB_OK);
@@ -171,9 +161,9 @@ std::vector<PasskeyInfo> GetResidentKeysWithDomains(const std::wstring& deviceNu
for (const auto& domain : domains) {
// Command for the second run
std::wstring command = L".\\fido2-manage.exe -residentkeys -device " + deviceNumber +
L" -domain " + domain + L" -pin " + globalPin;
std::vector<std::wstring> output = RunCommandAndGetOutputWithTimeout(command, DEVICE_TIMEOUT_MS);
std::wstring command = L".\\fido2-manage.exe -residentkeys -device " + QuoteArg(deviceNumber) +
L" -domain " + QuoteArg(domain) + L" -pin " + globalPin;
std::vector<std::wstring> output = RunCommandAndGetOutput(command);
// Debug: Check the raw output
for (const auto& line : output) {
@@ -305,8 +295,8 @@ INT_PTR CALLBACK PasskeysDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPAR
// Step 1: First Run - Fetch domains
std::wstring firstCommand = L".\\fido2-manage.exe -residentkeys -device " + deviceNumber + L" -pin " + globalPin;
std::vector<std::wstring> firstOutput = RunCommandAndGetOutputWithTimeout(firstCommand, DEVICE_TIMEOUT_MS);
std::wstring firstCommand = L".\\fido2-manage.exe -residentkeys -device " + QuoteArg(deviceNumber) + L" -pin " + globalPin;
std::vector<std::wstring> firstOutput = RunCommandAndGetOutput(firstCommand);
// Parse the output to get domains (users)
std::vector<std::wstring> domains;
@@ -327,9 +317,9 @@ INT_PTR CALLBACK PasskeysDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPAR
// Step 2: Second Run - Fetch resident keys for each domain
std::vector<PasskeyInfo> refreshedPasskeys;
for (const auto& domain : domains) {
std::wstring secondCommand = L".\\fido2-manage.exe -residentkeys -device " + deviceNumber +
L" -domain " + domain + L" -pin " + globalPin;
std::vector<std::wstring> secondOutput = RunCommandAndGetOutputWithTimeout(secondCommand, DEVICE_TIMEOUT_MS);
std::wstring secondCommand = L".\\fido2-manage.exe -residentkeys -device " + QuoteArg(deviceNumber) +
L" -domain " + QuoteArg(domain) + L" -pin " + globalPin;
std::vector<std::wstring> secondOutput = RunCommandAndGetOutput(secondCommand);
// Parse the second run output
std::vector<PasskeyInfo> parsedPasskeys = ParseResidentKeys(secondOutput);
@@ -465,8 +455,8 @@ INT_PTR CALLBACK FingerprintDialogProc(HWND hDlg, UINT message, WPARAM wParam, L
SendMessage(hListView, LVM_INSERTCOLUMN, 1, (LPARAM)&lvColumn);
// Populate the ListView with fingerprints
std::wstring command = L".\\fido2-manage.exe -fingerprintlist -device " + deviceNumber + L" -pin " + globalPin;
std::vector<std::wstring> output = RunCommandAndGetOutputWithTimeout(command, DEVICE_TIMEOUT_MS);
std::wstring command = L".\\fido2-manage.exe -fingerprintlist -device " + QuoteArg(deviceNumber) + L" -pin " + globalPin;
std::vector<std::wstring> output = RunCommandAndGetOutput(command);
for (const auto& line : output) {
// Split the line into parts
@@ -704,14 +694,6 @@ void PopulateListView(HWND hwnd, const std::wstring& deviceNumber) {
// Show the input box and store the result in globalPIN
globalPin = ShowInputBox(NULL, L"Enter PIN", L"Please enter your PIN:");
if (globalPin.empty()) {
// User cancelled the dialog — reset UI silently
RefreshData();
return;
}
globalPin = EscapeCommandLineArgument(globalPin);
const size_t MIN_PIN_LENGTH = 4;
if (globalPin.length() < MIN_PIN_LENGTH) {
@@ -722,9 +704,11 @@ void PopulateListView(HWND hwnd, const std::wstring& deviceNumber) {
return;
}
globalPin = QuoteArg(globalPin);
// Construct the command
std::wstring command = L".\\fido2-manage.exe -storage -device " + deviceNumber + L" -pin " + globalPin;
std::wstring command = L".\\fido2-manage.exe -storage -device " + QuoteArg(deviceNumber) + L" -pin " + globalPin;
// Execute the command
std::vector<std::wstring> output = RunCommandAndGetOutputWithTimeout(command, DEVICE_TIMEOUT_MS);
@@ -751,7 +735,7 @@ void PopulateListView(HWND hwnd, const std::wstring& deviceNumber) {
}
// Construct the second command
std::wstring command2 = L".\\fido2-manage.exe -info -device " + deviceNumber;
std::wstring command2 = L".\\fido2-manage.exe -info -device " + QuoteArg(deviceNumber);
// Execute the second command
std::vector<std::wstring> additionalOutput = RunCommandAndGetOutputWithTimeout(command2, DEVICE_TIMEOUT_MS);