Windows Vista/Windows 7 privilege: SeDebugPrivilege & OpenProcess - c++

Everything I've been able to find about escalating to the appropriate privileges for my needs has agreed with my current methods, but the problem exists. I'm hoping maybe someone has some Windows Vista/Windows 7 internals experience that might shine some light where there is only darkness. I'm sure this will get long, but please bear with me.
Context
I'm working on an application that requires accessing the memory of other processes on the current machine. This, obviously, requires administrator rights. It also requires SeDebugPrivilege (no, it is not a misspelling of SetDebugPrivilege), which I believe myself to be acquiring correctly, although I question if more privileges aren't necessary and thus the cause of my problems. Code has so far worked successfully on all versions of Windows XP, and on my test Vista 32 bit and Windows 7 64 bit environments.
Process
Program will Always be run with Administrator Rights. This can be assumed throughout this post.
Escalating the current process's Access Token to include SeDebugPrivilege rights.
Using EnumProcesses to create a list of current PIDs on the system
Opening a handle using OpenProcess with PROCESS_ALL_ACCESS access rights
Using ReadProcessMemory to read the memory of the other process.
Problem:
Everything has been working fine during development and my personal testing (including Windows XP 32 & 64, Windows Vista 32, and Windows 7 x64). However, during a test deployment onto both Windows Vista (32-bit) and Windows 7 (64-bit) machines of a colleague, there seems to be a privilege/rights problem with OpenProcess failing with a generic Access Denied error. This occurs both when running as a limited User (as would be expected) and also when run explicitly as Administrator (Right-click → Run as Administrator and when run from an Administrator level command prompt).
However, this problem has been unreproducible for myself in my test environment. I have witnessed the problem first hand, so I trust that the problem exists. The only difference that I can discern between the actual environment and my test environment is that the actual error is occurring when using a Domain Administrator account at the UAC prompt, whereas my tests (which work with no errors) use a local administrator account at the UAC prompt.
It appears that although the credentials being used allow UAC to 'run as administrator', the process is still not obtaining the correct rights to be able to OpenProcess on another process. I am not familiar enough with the internals of Vista/Windows 7 to know what this might be, and I am hoping someone has an idea of what could be the cause.
The Kicker
The person who has reported this error, and who's environment can regularly reproduce this bug, has a small application named along the lines of RunWithDebugEnabled which is a small bootstrap program which appears to escalate its own privileges and then launch the executable passed to it (thus inheriting the escalated privileges). When run with this program, using the same Domain Administrator credentials at UAC prompt, the program works correctly and is able to successfully call OpenProcess and operates as intended.
So this is definitely a problem with acquiring the correct privileges, and it is known that the Domain Administrator account is an administrator account that should be able to access the correct rights. (Obviously obtaining this source code would be great, but I wouldn't be here if that were possible).
Notes
As noted, the errors reported by the failed OpenProcess attempts are Access Denied. According to MSDN documentation of OpenProcess:
If the caller has enabled the SeDebugPrivilege privilege, the requested access is granted regardless of the contents of the security descriptor.
This leads me to believe that perhaps there is a problem under these conditions either with (1) Obtaining SeDebugPrivileges or (2) Requiring other privileges which have not been mentioned in any MSDN documentation, and which might differ between a Domain Administrator account and a Local Administrator account
Sample Code:
void sample()
{
/////////////////////////////////////////////////////////
// Note: Enabling SeDebugPrivilege adapted from sample
// MSDN # http://msdn.microsoft.com/en-us/library/aa446619%28VS.85%29.aspx
// Enable SeDebugPrivilege
HANDLE hToken = NULL;
TOKEN_PRIVILEGES tokenPriv;
LUID luidDebug;
if(OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken) != FALSE)
{
if(LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luidDebug) != FALSE)
{
tokenPriv.PrivilegeCount = 1;
tokenPriv.Privileges[0].Luid = luidDebug;
tokenPriv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
if(AdjustTokenPrivileges(hToken, FALSE, &tokenPriv, 0, NULL, NULL) != FALSE)
{
// Always successful, even in the cases which lead to OpenProcess failure
cout << "SUCCESSFULLY CHANGED TOKEN PRIVILEGES" << endl;
}
else
{
cout << "FAILED TO CHANGE TOKEN PRIVILEGES, CODE: " << GetLastError() << endl;
}
}
}
CloseHandle(hToken);
// Enable SeDebugPrivilege
/////////////////////////////////////////////////////////
vector<DWORD> pidList = getPIDs(); // Method that simply enumerates all current process IDs
/////////////////////////////////////////////////////////
// Attempt to open processes
for(int i = 0; i < pidList.size(); ++i)
{
HANDLE hProcess = NULL;
hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pidList[i]);
if(hProcess == NULL)
{
// Error is occurring here under the given conditions
cout << "Error opening process PID(" << pidList[i] << "): " << GetLastError() << endl;
}
CloseHandle(hProcess);
}
// Attempt to open processes
/////////////////////////////////////////////////////////
}
Thanks!
If anyone has some insight into what possible permissions, privileges, rights, etc. that I may be missing to correctly open another process (assuming the executable has been properly "Run as Administrator"ed) on Windows Vista and Windows 7 under the above conditions, it would be most greatly appreciated.
I wouldn't be here if I weren't absolutely stumped, but I'm hopeful that once again the experience and knowledge of the group shines bright. I thank you for taking the time to read this wall of text. The good intentions alone are appreciated, thanks for being the type of person that makes Stack Overflow so useful to all!

So after a lot of debugging and bothering a lot of people for information, I was finally able to track down the guy who wrote the RunWithDebugEnabled application and get a rundown of how it operates.
The problem, in this case, is that Debug programs privilege in the local policy for the domain administrator had been removed, and thus the SeDebugPrivilege token was not present in the process's access token. It can't be enabled if it's not present at all, and I still know of no way to add the privilege to an existing access token.
How the current magic works:
So the RunWithDebugEnabled magic application would use its Administrator rights to install itself as a service and start itself, thus running under the SYSTEM user account rather than the Domain Administrator. With SYSTEM privileges, the app then creates a new access token that is identical to the Administrator token, only with the SeDebugPrivilege token present. This new token is used to CreateProcessAsUser() and run the program with the newly enabled SeDebugPrivilege that was missing before.
I actually do not like this "solution" here, and have been continuing my search for a 'cleaner' way to obtain this privilege. I will be posting this as another question here on SO, which I will try to remember to link here as well to help others follow along and for future reference.
EDIT: Impersonate SYSTEM (or equivalent) from Administrator Account
I thank you all for your time and energies in helping to debug and solve this problem. It really is much appreciated!

Related

Windows 10 registry access

I have an old Windows program that reads information from the registry, like so:
CRegKey rkey;
if(rkey.Open(HKEY_LOCAL_MACHINE, "SOFTWARE\\xxx\\yyy", KEY_READ) == ERROR_SUCCESS)
{
...
}
On a Windows 10 locked down user account, this fails!
It cannot even read the registry, at least not that part of it.
I've looked on Microsoft's support site, and I could not find where it addresses this.
Can anyone point me to info, or just tell me, how accessing the registry from a (C++) program is affected by UAC in Windows 10?
I need to retract this question. Further investigation shows that the User account has read access to HKLM etc in the registry, and that is not the problem. In fact, the installer already wrote the registry entries I was looking for under HKCU (current user) where User account has both R and W permission. ... So, I blamed Windows 10 user mode unnecessarily. Issue seems to be communication from that program to another is not succeeding. We will look into it. I apologize for posting the question which, on investigation, turned out not to be the source of the problem.

Windows privileges needed to close a external app

I need to close some external programs.
Does my app require admin privileges to use Windows API GetWindowThreadProcessId, OpenProcess and TerminateProcess or user mode is enought?
I've checked in XP and 7 and works fine, but I know newer versions are more restrictive.
I'm using the following code:
bool CloseApp(const char *WindowName)
{
HWND hWnd;
hWnd=FindWindow(nullptr,WindowName);
if(!hWnd)return(true); // Not running
DWORD pid;
GetWindowThreadProcessId(hWnd,&pid);
if(!pid)return(false);
HANDLE prc;
prc=OpenProcess(SYNCHRONIZE|PROCESS_TERMINATE,FALSE,pid);
if(!prc)return(false);
TerminateProcess(prc,0);
DWORD rst=WaitForSingleObject(prc,10000); // Wait until completion
CloseHandle(prc);
if(FindWindow(nullptr,WindowName))return(false); // Check if still running
return(true);
}
Thanks.
It really depends on your application and the external process you want to kill. Windows has a pretty complicated system of authorizations for process interactions (everything revolves around Access Tokens).
Process
Since Windows Vista, process now have an integrity level which can be :
Untrusted. No logon SID, no access to the filesystem. Basically a pariah.
Low. Can only access %LocalLow% and a restricted set of API. Usually used to sandbox dangerous parts of an application (parsers, evaluators, etc.)
Medium. Your run-of-the-mill application/user
High. Superior privileges. Need UAC confirmation.
As you can imagine, lower integrity level applications can not interfere with higher integrity level ones. Since this mechanism is rarely used by developers (apart from MS products and web browsers) I assume your program run as Medium.
From integrity levels analysis, your program can probably only terminate process created by the same user and without admin privileges.
Services
Services are long-term process that have they own set of access mechanism. Services usually require admin levels (or even SYSTEM) to be controlled and terminated. Some can even be modified only by Network admins (e.g. in a AD) or only by Windows itself (e.g. for PPL services)
Anyway, the best way to check it is to call OpenProcess with your arguements and, if the calls fails, retrieve the last error set (using GetLastError() and check check for the ERROR_ACCESS_DENIED (0x5) value.

E_ACCESSDENIED on CoCreateInstance, where it used to work

I have some code that used to work, but recently stopped. It's in an Adobe Reader Plugin, and the latest Reader version has a "Protected Mode" which causes my problem.
My plugin can load some of my normal dlls, which load in-process:
MyNormalLib::IMyClassPtr foo;
HRESULT hr = foo.CreateInstance(__uuidof(MyNormalLib::MyClass));
But when I try to get a com pointer to my service (running on the same machine):
MyOtherLib::IMyServicePtr bar;
HRESULT hr = bar.CreateInstance(__uuidof(MyOtherLib::MyService));
I get E_ACCESSDENIED for my HRESULT.
This used to work fine, until Adobe Reader X came along. Without Protected Mode, Adobe runs normally and everything works. With Protected Mode, Adobe spawns another Reader process, with some restrictions on it. Looking at this with Process Explorer, I can see that the Security Tab for the parent Reader process has pretty much everything set to Mandatory; but the child Reader process has most groups set to "Deny, Mandatory", some "Mandatory, Restricted", some are just Mandatory. If this matters, I can provide more details.
All processes (my service and both Reader) are run as the same user -- me. I have admin rights, if that matters.
What can cause an AccessDenied error when trying to reach my own service? What security hoops do I have to jump through to get this to work?
The restricted process does not have admin rights. That's pretty much the point of the exercise - Reader X drops as many rights as it can from its token so that if it is pwned your computer is not.
(That's basically how UAC works too, you have to go to the Kernel to get permission to re-enable your Administrator group once you have disabled it).
Basically you need to look at the privs that Reader X has, and make sure your component can be used with those permissions. Process Monitor is your friend - just filter for DENIED and the problems will pop right out at you!

Why does Win32 API function CredEnumerate() return ERROR_NOT_FOUND if I'm impersonated?

I've written some sample code which when I call from the windows command prompt under the context of a normal user account, dump's all the user's saved credentials using CredEnumerate(). However, I really want to be able to do this from SYSTEM user context so I've tested my program from a SYSTEM cmd prompt.
When I running my program as SYSTEM, I run LogonUser like so:
bLoggedOn = LogonUser(userName.c_str(), domain.c_str(), password.c_str(), LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, &userToken_);
Then I run ImpersonateLoggedOnUser() on the token to give me the security context of the local user. After this I do:
bOk = CredEnumerate(NULL, 0, &count, &pCredentials);
And I'd expect this to return the credentials in the same way as if I'd not gone in from system and impersonated. Can anyone spot anything that I've missed to truly put myself in the user's context?
I guess I aught to answer this question myself since I've now spent ages working out how to do this and I'm not sure it's widely known. CredEnumerate/CredRead will never provide password information for domain passwords no matter what process context you're in or what token you have despite what it seems to hint at on MSDN. The only way to get access to the saved credential information is to do so using the undocumented function LSAICryptUnprotectData() which is in lsasrv.dll. This can decrypt the files you find in %APPDATA%\Microsoft\Credentials and can provide an identical data structure to CredEnumerate except with the password filled in. The only catch is that this must be done in the process context of lsass.exe (The windows security subsystem), no setting of privilledges etc is enough to give a normal process the rights to do this. If you're a hacker you can do this by performing a CreateRemoteThread() to inject a thread into lsass.exe or if you're trying to do this in a legitimate way, i.e you're extending the Windows operating system in some way for a third party application, like I was, you can do this by creating a Windows authentication package which lsass will load. This AP can then use a named pipe or some such method to allow interaction with the rest of your code.

Implementing custom windows authentication package

I'm building a custom authentication subpackage for MSV1_0 for Windows 7. I've used the msvsubauth sample in from the Windows SDK and I have 2 questions regarding some problems I'm facing with that:
When I'm trying just to make sure that the routine get's invoked and set the Auth0 property in the registry to my package and add a simple code at the end of the Msv1_0SubAuthenticationRoutine that creates a file:
//
// Cleanup up before returning.
//
Cleanup:
hTestFile = CreateFile(
TEXT("C:\\lsa\\lsa.txt"),
GENERIC_READ|GENERIC_WRITE, 0,
NULL, CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL, NULL);
if(hTestFile != INVALID_HANDLE_VALUE) {
CloseHandle(hTestFile);
}
return Status;
} // Msv1_0SubAuthenticationRoutine
Apparently the package gets invoked because when I enter my password I get an error message from windows "the parameter is incorrect" which is a good sign. But why I'm getting that error? when the exactly same code is executed from a separate .exe file it runs perfectly and creates the test text file. I've checked the permissions and set "full control" for "everyone". Any ideas? the SDK doesn't exactly mention what kind of isolation LSA is creating for code within auth packages.
The second problem is testing the AP. Currently with every change I rebuild the library, copy it to a test VM and then to the System32 folder and reboot it. Is there an easier way to do that?
Thank in advance!
Debugging in Winlogon and LSASS makes for the most time consuming debugging.
To ease your debugging, you could write a proxy AP that exports the same functions. When loaded, you proxy_ap would
Copy the real AP from a known location to a temp locationand.
LoadLibrary that DLL, GetProcAddress of everything, and forward any calls it receives to that newly loaded DLL.
Watch for changes in the directory where the original AP was copied from
When a change occurs (and if your AP changed) FreeLibrary and goto step 2
But you need to keep a tight grip on what happens on your development target, because handling the dll switch while dealing with requests comming from many threads can become a worse nightmare that what you are trying to solve.
LogonUI.exe starts a new instance every time, but LSASS.exe is long lived.
+Have a look at CVSNT source code (http://cvsnt.sourcearchive.com/). They have a pretty nice AP that implements su. Run the sample in the local system account with psexec -s (from Microsoft/SysInternals pstools suite)
Perhaps your problem is Everyone only includes Authenticated users? This is just a guess.
I suggest you use Process Monitor to monitor for Access Denied messages or for your path. It is fantastic for debugging permission/path problems of all kinds.
http://technet.microsoft.com/en-us/sysinternals/bb896645.aspx
If you experience the issue at the "Unlock Workstation" or "change Password" screens, and it doesn't prevent you logging in, this should be easy to do - set it running, reproduce the problem, log back in and hey presto.
Otherwise you might have to resort to tricks like executing that code path only for certain user accounts, on the Nth try, etc.