The 2 trojans:
Wacatac.G!ml
Persistence.G!ml
Here's the code that I believe is causing the issue. The purpose is to create/modify a registry key to make the program run on startup:
void SoftwareDlg::SetSURegValue() {
string regSubKey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run\\");
string regValueName = "Software";
string regValue = "D:\\Users\\Skew\\Documents\\repos\\Software\\Debug\\Software.exe"
try
{
size_t bufferSize = 0xFFF;
auto cbData = static_cast<DWORD>(regValue.size() * sizeof(char));
HKEY hKey;
DWORD position;
auto rc = RegCreateKeyEx(HKEY_CURRENT_USER, regSubKey.c_str(), 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey, &position);
if (position == REG_OPENED_EXISTING_KEY) {
DEBUG_PRINT("Key already exists & has been opened.")
}
else if (position == REG_CREATED_NEW_KEY) {
DEBUG_PRINT("Created new key.")
}
else {
DEBUG_PRINT("ERROR: Key does not exist, and a new key was not created.")
}
if (rc == ERROR_SUCCESS) {
auto rc = RegSetValueEx(hKey, regValueName.c_str(), 0, REG_SZ, (BYTE*)regValue.data(), cbData);
if (rc != ERROR_SUCCESS)
{
throw std::runtime_error("Windows system error code: " + to_string(rc));
}
}
else {
DEBUG_PRINT("Error opening key.\n")
}
}
catch (std::exception& e)
{
DEBUG_PRINT(e.what())
}
}
In my attempts to solve this issue I began testing different scenarios of creating/modifying the key, but my results became inconclusive when I realized that Windows Defender had seemingly stopped logging each run as "new threats" and seemed to log them together as a single "permeant threat" I guess? Not really sure.
With that said, Windows Defender did not seem to log the threat when I would initially create the key or when I would open it and assign it the same value, but did appear to log the threat when I would move the program to a new directory(and the program would attempt to change the value of the "Software" registry value to the new EXE location).
That's left me with several questions:
Does my program mimic the behavior of the 2 trojans through some coding mistake?
Or do I have some latent, opportunistic piece of malware on my machine that's just been waiting to take advantage?
Is deleting the existing value necessary before attempting to change it? The behavior of RegCreateKeyEx leads me to believe this is not the case.
Is writing to the registry without elevated permissions a no-no? If so... why does my machine let me do it?
Am I doing some incorrect type conversion in the RegSetValueEx() function?
If #4 is the case, I guess I'm just really surprised that I was notified by Windows Defender and not Visual Studio or a UAC prompt.
Edit 1: No engines on VirusTotal.com detected the file as malware.
Can someone please tell me how to go about creating a maximum of N instances of an application in MFC?
Also, if N instances are running, and one instance gets closed, then one new instance can be created (but no more than N instances can run at any one time).
Thank you in advance.
a.
You can create a global semaphore that up to n process instances can enter. The n+1 th instance of your process will fail to enter the semaphore. Of course you should select a short timeout for the locking operation so you can present a meaningful feedback to the user.
For the semaphore stuff you can take a look at MSDN.
I'd use lock files. In your CMyApp::InitInstance() add:
CString Path;
// better get the path to the global app data or local user app data folder,
// depending on if you want to allow the three instances machine-wide or per user.
// Windows' file system virtualization will make GetModuleFileName() per user:
DWORD dw = GetModuleFileName(m_hInstance,
Path.GetBuffer(MAX_PATH), MAX_PATH);
Path.ReleaseBuffer();
// strip "exe" from filename and replace it with "lock"
Path = Path.Left(Path.GetLength()-3) + T("lock");
int i;
// better have the locking file in your class and do a clean Close on ExitInstance()!
CFile *pLockingFile = NULL;
for (i = 0; i < 3; i++) // restrict to three instances
{
CString Counter;
Counter.Format(T("%d"), i);
TRY
{
pLockingFile = new CFile(Path + Counter,
CFile::modeCreate|CFile::modeWrite|CFile::shareExclusive);
pLockingFile.Close();
break; // got an instance slot
}
CATCH( CFileException, e )
{
// maybe do something else here, if file open fails
}
END_CATCH
if (i >= 3)
return TRUE; // close instance, no slot available
}
Edit: To lock the software machine-wide, retrieve the common app folder using the following function instead of calling GetModuleFileName().
#pragma warning(disable: 4996) // no risk, no fun
BOOL GetCommonAppDataPath(char *path)
{
*path = '\0';
if (SHGetSpecialFolderPath(NULL, path, CSIDL_COMMON_APPDATA, TRUE))
{
strcat(path, T("\\MyApplication")); // usually found under C:\ProgramData\MyApplication
DWORD dwFileStat = GetFileAttributes(path);
if (dwFileStat == 0xffffffff) // no MyApplication directory yet?
CreateDirectory(path, NULL); // create it
dwFileStat = GetFileAttributes(path); // 2nd try, just to be sure
if (dwFileStat == 0xffffffff || !(dwFileStat & FILE_ATTRIBUTE_DIRECTORY))
return FALSE;
return TRUE;
}
return FALSE;
}
Note: This will only work from Vista on. If you have XP, writing to a global directory is an easy task, e.g. C:\Windows\Temp. I have put the function in a helper dll I only load if the OS is Vista or higher. Otherwise your software won't start because of unresolved references in system dlls.
Scenario
I have a remote computer that I want to run installers (arbitrary executables) on programatically. These installers require two things:
They must run in Administrator mode.
They must run under a specific user context (Specifically, a local user who is a member of the Administrators group).
This has proven to be very challenging.
It appears as though there are a few external tools that exist that do this, but I am looking for a solution that comes with Windows.
What a valid solution to this problem would look like
From an elevated context (e.g. an elevated batch file or executable program) a valid solution should be able to programatically launch a process in Administrator mode under another user context. Assume that the other user's id and password are available, and that the other user is a member of the Administrators group. Additional restrictions:
A valid solution cannot rely on an external tool. Since newer versions of Windows come with .NET and PowerShell by default, these are valid tools to use.
A valid solution cannot require user interactions. This means that if a UAC window pops up, or if any user confirmation is required, the solution is invalid.
Please test your solution before posting it to make sure it works! If you are going to provide a link to another solution, please verify that the linked solution works before posting. Many people who claim to have working solutions to this problem in fact do not.
What I have tried
I have tried using Batch Scripts, PowerShell, and C#. As far as I can tell, none of these technologies will accomplish the task. They all suffer from the same fundamental problem - running a task as another user and in Administrator mode are mutually exclusive processes. Let me be more specific:
Why Not Batch
The command that one would use to run under a different user context is Runas, which does not launch the process elevated. There are several external tools that claim to get around this, but as stated earlier these are not permitted.
Why Not PowerShell
The command to start a new process, Start-Process, can elevate a new process and run it as a different user, but not at the same time. I have an open question here referring to this issue. Unfortunately no one has provided a solution, which leads me to believe that it is impossible.
Why Not C#
This also appears to be impossible, as the Process class does not appear to support launching a process in Administrator mode and under a different user's credentials.
Why not an external tool?
This forces me to rely on someone else's code to do the right thing, and I would rather code it up myself than do that. In fact I have a solution that is one step better than relying on someone else, but is rather hackish:
Create a task using the Task Scheduler to launch the executable in administrator mode on the specified account at some time in the very distant future.
Force the Task to run immediately.
Wait to see if the task has finished. Answered here.
Thanks in advance to anyone who tries to help! It is greatly appreciated and I hope that if nothing else, other people are able to find this for the Task Scheduler work around.
OK, so it turns out that CreateProcessWithLogonW function filters the user token, and so does LogonUser. This would seem to leave us stuck, since we don't have the right privileges to correct the problem (see footnote) but it turns out that LogonUser does not filter the token if you use LOGON32_LOGON_BATCH rather than LOGON32_LOGON_INTERACTIVE.
Here's some code that actually works. We use the CreateProcessAsTokenW function to launch the process, because this particular variant requires only SE_IMPERSONATE_NAME privilege, which is granted to administrator accounts by default.
This sample program launches a subprocess which creates a directory in c:\windows\system32, which would not be possible if the subprocess was not elevated.
#define _WIN32_WINNT 0x0501
#include <Windows.h>
#include <Sddl.h>
#include <conio.h>
#include <stdio.h>
wchar_t command[] = L"c:\\windows\\system32\\cmd.exe /c md c:\\windows\\system32\\proof-that-i-am-an-admin";
int main(int argc, char **argv)
{
HANDLE usertoken;
STARTUPINFO sinfo;
PROCESS_INFORMATION pinfo;
ZeroMemory(&sinfo, sizeof(sinfo));
sinfo.cb = sizeof(sinfo);
if (!LogonUser(L"username", L"domain", L"password", LOGON32_LOGON_BATCH, LOGON32_PROVIDER_DEFAULT, &usertoken))
{
printf("LogonUser: %u\n", GetLastError());
return 1;
}
if (!CreateProcessWithTokenW(usertoken, LOGON_WITH_PROFILE, L"c:\\windows\\system32\\cmd.exe", command, 0, NULL, NULL, &sinfo, &pinfo))
{
printf("CreateProcess: %u\n", GetLastError());
return 1;
}
return 0;
}
However, if the target process is a GUI process (including a process with a visible console) it won't display properly. Apparently CreateProcessWithTokenW only assigns the minimum desktop and window station permissions necessary for a process to run, which is not enough to actually display a GUI.
Even if you don't actually need to see the output, there's a risk that the broken GUI will cause functional problems with the program.
So, unless the target process runs in the background, we should probably assign permissions appropriately. In general, it is best to create a new window station and a new desktop, to isolate the target process; in this case, though, the target process is going to be running as admin anyway, so there's no point - we can make life easier by just changing the permissions on the existing window station and desktop.
Edit 24 November 2014: corrected access rights in window station ACE so they will work for non-administrative users. Note that doing this may allow the non-admin user in question to compromise processes in the target session.
#define _WIN32_WINNT 0x0501
#include <Windows.h>
#include <AccCtrl.h>
#include <Aclapi.h>
#include <stdio.h>
wchar_t command[] = L"c:\\windows\\system32\\notepad.exe";
int main(int argc, char **argv)
{
HANDLE usertoken;
STARTUPINFO sinfo;
PROCESS_INFORMATION pinfo;
HDESK desktop;
EXPLICIT_ACCESS explicit_access;
BYTE buffer_token_user[SECURITY_MAX_SID_SIZE];
PTOKEN_USER token_user = (PTOKEN_USER)buffer_token_user;
PSECURITY_DESCRIPTOR existing_sd;
SECURITY_DESCRIPTOR new_sd;
PACL existing_dacl, new_dacl;
BOOL dacl_present, dacl_defaulted;
SECURITY_INFORMATION sec_info_dacl = DACL_SECURITY_INFORMATION;
DWORD dw, size;
HWINSTA window_station;
if (!LogonUser(L"username", L"domain", L"password", LOGON32_LOGON_BATCH, LOGON32_PROVIDER_DEFAULT, &usertoken))
{
printf("LogonUser: %u\n", GetLastError());
return 1;
}
if (!GetTokenInformation(usertoken, TokenUser, buffer_token_user, sizeof(buffer_token_user), &dw))
{
printf("GetTokenInformation(TokenUser): %u\n", GetLastError());
return 1;
}
window_station = GetProcessWindowStation();
if (window_station == NULL)
{
printf("GetProcessWindowStation: %u\n", GetLastError());
return 1;
}
if (!GetUserObjectSecurity(window_station, &sec_info_dacl, &dw, sizeof(dw), &size) && GetLastError() != ERROR_INSUFFICIENT_BUFFER)
{
printf("GetUserObjectSecurity(window_station) call 1: %u\n", GetLastError());
return 1;
}
existing_sd = malloc(size);
if (existing_sd == NULL)
{
printf("malloc failed\n");
return 1;
}
if (!GetUserObjectSecurity(window_station, &sec_info_dacl, existing_sd, size, &dw))
{
printf("GetUserObjectSecurity(window_station) call 2: %u\n", GetLastError());
return 1;
}
if (!GetSecurityDescriptorDacl(existing_sd, &dacl_present, &existing_dacl, &dacl_defaulted))
{
printf("GetSecurityDescriptorDacl(window_station): %u\n", GetLastError());
return 1;
}
if (!dacl_present)
{
printf("no DACL present on window station\n");
return 1;
}
explicit_access.grfAccessMode = SET_ACCESS;
explicit_access.grfAccessPermissions = WINSTA_ALL_ACCESS | READ_CONTROL;
explicit_access.grfInheritance = NO_INHERITANCE;
explicit_access.Trustee.pMultipleTrustee = NULL;
explicit_access.Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE;
explicit_access.Trustee.TrusteeForm = TRUSTEE_IS_SID;
explicit_access.Trustee.TrusteeType = TRUSTEE_IS_USER;
explicit_access.Trustee.ptstrName = (LPTSTR)token_user->User.Sid;
dw = SetEntriesInAcl(1, &explicit_access, existing_dacl, &new_dacl);
if (dw != ERROR_SUCCESS) {
printf("SetEntriesInAcl(window_station): %u\n", dw);
return 1;
}
if (!InitializeSecurityDescriptor(&new_sd, SECURITY_DESCRIPTOR_REVISION))
{
printf("InitializeSecurityDescriptor(window_station): %u\n", GetLastError());
return 1;
}
if (!SetSecurityDescriptorDacl(&new_sd, TRUE, new_dacl, FALSE))
{
printf("SetSecurityDescriptorDacl(window_station): %u\n", GetLastError());
return 1;
}
if (!SetUserObjectSecurity(window_station, &sec_info_dacl, &new_sd))
{
printf("SetUserObjectSecurity(window_station): %u\n", GetLastError());
return 1;
}
free(existing_sd);
LocalFree(new_dacl);
desktop = GetThreadDesktop(GetCurrentThreadId());
if (desktop == NULL)
{
printf("GetThreadDesktop: %u\n", GetLastError());
return 1;
}
if (!GetUserObjectSecurity(desktop, &sec_info_dacl, &dw, sizeof(dw), &size) && GetLastError() != ERROR_INSUFFICIENT_BUFFER)
{
printf("GetUserObjectSecurity(desktop) call 1: %u\n", GetLastError());
return 1;
}
existing_sd = malloc(size);
if (existing_sd == NULL)
{
printf("malloc failed\n");
return 1;
}
if (!GetUserObjectSecurity(desktop, &sec_info_dacl, existing_sd, size, &dw))
{
printf("GetUserObjectSecurity(desktop) call 2: %u\n", GetLastError());
return 1;
}
if (!GetUserObjectSecurity(desktop, &sec_info_dacl, existing_sd, 4096, &dw))
{
printf("GetUserObjectSecurity: %u\n", GetLastError());
return 1;
}
if (!GetSecurityDescriptorDacl(existing_sd, &dacl_present, &existing_dacl, &dacl_defaulted))
{
printf("GetSecurityDescriptorDacl: %u\n", GetLastError());
return 1;
}
if (!dacl_present)
{
printf("no DACL present\n");
return 1;
}
explicit_access.grfAccessMode = SET_ACCESS;
explicit_access.grfAccessPermissions = GENERIC_ALL;
explicit_access.grfInheritance = NO_INHERITANCE;
explicit_access.Trustee.pMultipleTrustee = NULL;
explicit_access.Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE;
explicit_access.Trustee.TrusteeForm = TRUSTEE_IS_SID;
explicit_access.Trustee.TrusteeType = TRUSTEE_IS_USER;
explicit_access.Trustee.ptstrName = (LPTSTR)token_user->User.Sid;
dw = SetEntriesInAcl(1, &explicit_access, existing_dacl, &new_dacl);
if (dw != ERROR_SUCCESS) {
printf("SetEntriesInAcl: %u\n", dw);
return 1;
}
if (!InitializeSecurityDescriptor(&new_sd, SECURITY_DESCRIPTOR_REVISION))
{
printf("InitializeSecurityDescriptor: %u\n", GetLastError());
return 1;
}
if (!SetSecurityDescriptorDacl(&new_sd, TRUE, new_dacl, FALSE))
{
printf("SetSecurityDescriptorDacl: %u\n", GetLastError());
return 1;
}
if (!SetUserObjectSecurity(desktop, &sec_info_dacl, &new_sd))
{
printf("SetUserObjectSecurity(window_station): %u\n", GetLastError());
return 1;
}
free(existing_sd);
LocalFree(new_dacl);
ZeroMemory(&sinfo, sizeof(sinfo));
sinfo.cb = sizeof(sinfo);
if (!CreateProcessWithTokenW(usertoken, LOGON_WITH_PROFILE, L"c:\\windows\\system32\\notepad.exe", command, 0, NULL, NULL, &sinfo, &pinfo))
{
printf("CreateProcess: %u\n", GetLastError());
return 1;
}
return 0;
}
Note the use of LOGON_WITH_PROFILE. This is not necessary to display a GUI, and it slows down launching the process considerably, so remove it if you don't need it - but if you are an administrator, the most likely reason that you are launching a process as a different administrator is that you need something in that administrator's user profile. (Another scenario might be that you need to use a specific domain account in order to access resources on another machine.)
Footnote:
Specifically, you need SeTcbPrivilege in order to use GetTokenInformation and TokenLinkedToken to obtain a usable handle to the elevated token that LogonUser generates. Unfortunately, this privilege is usually only available if you are running as local system.
If you do not have SeTcbPrivilege you can still obtain a copy of the linked token, but in this case it is an impersonation token at SecurityIdentification level so is of no use when creating a new process. Thanks to RbMm for helping me clarify this.
I'm working with Windows Script Hosting (JScript). And my primary goal is to know if registry key exist. I'm trying to 'RegRead' this key and if it throws an error then key does not exist (that's how documentation says http://msdn.microsoft.com/en-us/library/x05fawxd(v=vs.84).aspx). Everything works fine in Win7, but when I test my code in WinXP it fails. The problem is that when you try to 'RegRead' key that has no default value set it will throw an error (only on WinXP, on Win7 it is ok). And the more disgusting point is that error numbers are equal when you try to read non existing key (Error message: Invalid root in registry key "HKEY_CURRENT_USER\Software\NonExistKey\". Error code: 80070002) and existing key that has no default value (Error message: Unable to open registry key "HKEY_CURRENT_USER\Software\" for reading. Error code: 80070002). Yeah, error messages are different, but I don't want to compare messages or use indexOf("invalid") or indexOf("unable") to distinguish one error from another, because in defferent windows locales will be different messages and words.
This is my samle code, Just make a JS file with this content and run it.
var shell = new ActiveXObject('WScript.Shell');
try{
var valueKeyExist = shell.RegRead('HKEY_CURRENT_USER\\Software\\');
}catch(e2) {
WScript.Echo([e2.message, e2.number]);
}
if (e2 !== undefined) {
try{
var valueNonExistKey = shell.RegRead('HKEY_CURRENT_USER\\Software\\NonExistKey\\');
}catch(e1) {
WScript.Echo([e1.message, e1.number]);
}
WScript.Echo('Seems like you are running this script on WinXP and reading reg key with default value not set throws an error.\nError messages are equal: ' + (e1.message == e2.message) + '\nError numbers are equal: ' + (e1.number == e2.number));
} else {
WScript.Echo('Seems like you are running this script on Win7 and reading reg key with default value not set is ok');
}
Any suggestions how to fix this or how can I know if registry key exist?
Eventually I used WMI for this:
function _getRootCode(root) {
var rootCode = null;
switch(root){
case 'HKCR':
case 'HKEY_CLASSES_ROOT':
rootCode = 0x80000000;
break;
case 'HKCU':
case 'HKEY_CURRENT_USER':
rootCode = 0x80000001;
break;
case 'HKLM':
case 'HKEY_LOCAL_MACHINE':
rootCode = 0x80000002;
break;
case 'HKU':
case 'HKEY_USERS':
rootCode = 0x80000003;
break;
case 'HKCC':
case 'HKEY_CURRENT_CONFIG':
rootCode = 0x80000005;
break;
}
return rootCode;
},
function keyExist(root, path) {
var WMIRegistry = GetObject("winmgmts:{impersonationLevel=impersonate}!\\\\.\\root\\default:StdRegProv");
var keys;
return WMIRegistry.EnumKey(_getRootCode(root), path, keys) == 0;
}
Works fine on Win7 and WinXP.
Attempting to implement a poor man's test of whether a process is still running or not (essentially an equivalent of the trivial kill(pid, 0).)
Hoped to be able to simply call OpenProcess with some minimal desired access then test for either GetLastError() == ERROR_INVALID_PARAMETER or GetExitCodeProcess(...) != STILL_ACTIVE.
Nice try... Running on Windows XP, as administrator:
HANDLE hProc = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid);
if (!hProc) {
DWORD dwLastError = GetLastError();
}
...fails miserably with dwLastError == ERROR_ACCESS_DENIED when pid is owned by a different (not SYSTEM) user. Moreover, if pid was originally owned by a different user but has since terminated, OpenProcess also fails with ERROR_ACCESS_DENIED (not ERROR_INVALID_PARAMETER.)
Do I have to use Process32First/Process32Next or EnumProcesses?
I absolutely do not want to use SeDebugPrivilege.
Thanks,
V
If you have a process ID:
// this should succeed even when a medium integrity process
// requests access to a high integrity process
if (HANDLE h = OpenProcess(SYNCHRONIZE, FALSE, pid))
{
// do a wait, if the handle is signaled: not running
DWORD wait = WaitForSingleObject(h, 0);
if (wait == WAIT_OBJECT_0) return FALSE;
}
// cannot get a handle to the process:
// probably running at system integrity level
// I'm not sure how reliable this check is, but it seems to work:
// if access is denied: running
// if invalid parameter: not running
else if (GetLastError() != ERROR_ACCESS_DENIED) return FALSE;
If you have a window handle that should be valid for as long as the process is running, this is a good alternative:
if (hWnd && !IsWindow(hWnd)) return FALSE;
static BOOL
isProcessAlive(DWORD th32ProcessID) {
BOOL bSuccess = FALSE;
HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hSnap != INVALID_HANDLE_VALUE) {
PROCESSENTRY32 pe32 = { sizeof(pe32), 0 };
if (Process32First(hSnap, &pe32)) {
while (pe32.th32ProcessID != pid && Process32Next(hSnap, &pe32));
_ASSERT(GetLastError() == 0 || GetLastError() == ERROR_NO_MORE_FILES);
bSuccess = (pe32.th32ProcessID == th32ProcessID);
}
CloseHandle(hSnap);
}
return bSuccess;
}