I have a program which adjusts SeDebugPrivilege and then starts to iterate through system processes and calls OpenProcess for them (and does other stuff, but it's not important now). Also the program runs in administrator mode of course. On Windows XP and Windows 7 it works fine, but on Windows 8.1 OpenProcess fails for the following system processes with ERROR_ACCESS_DENIED(5): smss.exe, csrss.exe, services.exe. As I know with SeDebugPrivilege I should be able to open these processes and retrieve a handle for them. Does anybody have a clue, what kind of magic causes this error only on Windows 8.1?
(Anyway I have the same error with the same processes for CreateToolhelp32Snapshot)
Windows 8.1 introduces the concept of a system protected process. This is documented in the context of third-party anti-malware software, but it seems reasonable to suppose that it is also used to protect particularly critical system processes.
System protected processes are an extension of the Protected Process mechanism (Microsoft Word document) introduced in Windows Vista as a DRM measure.
You cannot obtain any of these access rights for a protected process, even with debug privilege:
DELETE
READ_CONTROL
WRITE_DAC
WRITE_OWNER
PROCESS_CREATE_THREAD
PROCESS_DUP_HANDLE
PROCESS_QUERY_INFORMATION
PROCESS_SET_QUOTA
PROCESS_SET_INFORMATION
PROCESS_VM_OPERATION
PROCESS_VM_READ
PROCESS_VM_WRITE
You should still be able to open the process by requesting PROCESS_QUERY_LIMITED_INFORMATION access. According to the documentation, SYNCHRONIZE and PROCESS_TERMINATE access are also permitted.
It can only be done in the kernel. The best way to get the info you need would be to:
PsLookupProcessByProcessId()
KeStackAttachProcess()
ZwQueryInformationProcess() or whatever other functions you need to now call within the context of the attached process.
KeStackDetachProcess()
Or if you are just experimenting and not putting anything into production code, you can traverse the various semi-opaque structures (EPROCESS, PEB, VAD, etc) to get the information you need.
I was recently running into Access is Denied errors (error code 5 in my case), while running the Win32 OpenProcess API and then later while running CreateProcessAsUser. In my case, I was running on Windows 10, but I suspect it's similar, but since I got it working I thought I would share a couple things that helped me.
As I was using C# my Win32 method signature is as follows:
[DllImport("kernel32.dll")]
private static extern IntPtr OpenProcess(uint dwDesiredAccess, bool bInheritHandle, uint dwProcessId);
One key factor that effected the success of accessing the existing process, which in my case was a winlogon.exe process, was to properly defined the right "desired access" value. In my case, I used a constant "MAXIMUM_ALLOWED" defined as:
private const uint MAXIMUM_ALLOWED = 0x2000000;
This call to the service looks like this:
IntPtr hProcess = OpenProcess(MAXIMUM_ALLOWED, false, targetWinlogonProcessId);
This established the right kind of access. I was also running my process (web service) as the LocalSystem account, which had pretty good privileges. It started off as:
Please note, I was able to run this command using the SYSTEM account, by downloading PsExec.exe and running PsExec.exe -i -s cmd.exe to launch a command prompt so I could query the privileges using that account. You can find a good list of permissions here:
https://learn.microsoft.com/en-us/windows/security/threat-protection/security-policy-settings/user-rights-assignment
In my case, I wanted to add SeAssignPrimaryTokenPrivilege and SeIncreaseQuotaPrivilege, which I added via secpol.msc:
Your particular permissions required may depend on the account you're using, but I hope this helps!
Related
I am trying to run program.exe in a very restricted mode (Windows). This means program.exe should have access to five .txt files, and no other permissions like staring new process, shutdown, edit other files, etc.
I've spent a month trying to achieve it, but there is still no results. I tried to run it as constrained user: runas /trustlevel:10000 "program.exe", and then added permissions:icacls program.exe /grant *S-1-5-12:F
But it seems such user still have some rights, for instace he can run notepad.exe and explorer.exe, and this is unacceptable in my case. Also I played with CreateProcessAsUser() winapi function, but it lead to same result.
So my question is: how can I run program.exe with this limitations, maybe I should create new user with restrictions? How?
P.S: Basically I want to get primitive sandbox.
Thank you.
So thanks to your help, I've written this code:
hToken = NULL;
OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &hToken);
SID_AND_ATTRIBUTES sidsToDisable;
ConvertStringSidToSid(L"S-1-1-0", &sidsToDisable.Sid);
sidsToDisable.Attributes = NULL;
SID_AND_ATTRIBUTES sidsToRestrict;
ConvertStringSidToSid(L"S-1-1-0", &sidsToRestrict.Sid);
sidsToRestrict.Attributes = NULL;
CreateRestrictedToken(hToken,NULL,1,&sidsToDisable,0,NULL,1,&sidsToRestrict,&hToken);
PROCESS_INFORMATION pi;
if (CreateProcessAsUser(hToken, wszPath, NULL, NULL, NULL,TRUE, CREATE_NEW_CONSOLE /*| CREATE_SUSPENDED*/, NULL, NULL, &si, &pi))
{
//...
//ok process created
}
But it does not work. 0xc0000022 errors occur all the time, or 0xc00000142 when using admin SID. When I create hToken by SaferComputeTokenFromLevel() it works fine. What am I doing wrong?
Sandboxing that thorough is believed to be impossible. See also the linked PDF.
If this needs to be robust, your only option is a fully-fledged VM. Of course if the sandboxed application has to be a Windows executable, that has licensing implications.
If it doesn't need to be all that robust, it should be possible in principle to put something together that might be sufficient. You would need administrative privilege to do so, but a system service could launch the sandboxed application on a non-privileged user's behalf.
The hard part, I think, will be creating a suitable token. I think that ideally, you want a token that has a unique logon session SID but nothing else.
Using the LSA API is probably the best approach, since it allows you to synthesize an arbitrary token, but it is complicated and not terribly well documented.
You could experiment with duplicating and/or modifying the token you get from ImpersonateAnonymousToken, but I'm not sure whether it would be possible to include a unique logon session SID.
Combining LogonUser (against a user account created for the purpose) with CreateRestrictedToken should be both straightforward and adequate. This is probably the place to start.
You should also make sure the integrity level for the token is suitably low. I would assume that either S-1-16-0 or S-1-16-1 is the lowest level possible.
You'll want to create a new window station for the sandboxed process. That's why we need the logon session SID, so we can grant the token permissions to the window station. Otherwise, the process won't be able to run.
If the process generates a GUI, you'll have to remote it somehow. Ideally you would modify the process itself so that the GUI part is separate and does not need to be sandboxed. Failing that, it should be possible in principle to copy the bitmap from the sandboxed desktop, and forward mouse clicks and keystrokes back to it. I'm not sure how reliable this would be.
To prevent the sandboxed process from launching child processes, put it in a job object. You can set the active process limit for the job to 1 to prevent child processes from being created. (This isn't really necessary IMO since the child processes would have all the same restrictions as the parent, but it can be done.)
Your last comment suggests that perhaps you don't need even this level of robustness. If so, you could save yourself the trouble of remoting the GUI by running the process in the user's own desktop and window station.
You would still need to generate a suitable token, but you might not need to use a separate user account and logon session; if so, CreateRestrictedToken should be sufficient. You can use the interactive user's own token as the source.
You will still need to lower the token's integrity level to protect against shatter attacks.
You can still use a job object to prevent child processes being launched.
JOBOBJECT_BASIC_UI_RESTRICTIONS can be used to provide some level of protection against attacks on the user interface.
In this scenario, you might not even need admin access. I'm not sure offhand.
This approach is not robust enough for running potentially malicious executables, but if the sandbox is only intended as a backup precaution it may be adequate.
I'm developing a driver that hooks some functions in windows (hardening driver that will block some actions). The problem is, that I want to log which was the user who attempted to run those actions.
For example, I've put an hook on ZwSetValueKey in order to filter registry writing.
The hook works perfectly, But I don't know how to get the caller SID. I've found out that I can determine whether the mode of the caller(i.e. user mode or kernel mode) using ExGetPreviousMode. But I'm not really sure how to determine the SID if the caller was in user mode.
Thanks.
If this were user mode, you'd start with GetCurrentProcess or GetCurrentThread, and then call GetProcessToken or GetThreadToken. This would get you an access token, from which the SID can be extracted directly. In kernel model, there's PsGetCurrentProcess and ZwOpenProcessTokenEx, and the like for threads.
Here's the equivalent question for user space: How to get Calling-Process Windows User Access Token.
I haven't tested this, but I hope it gets you started.
VOID SeCaptureSubjectContext(Out PSECURITY_SUBJECT_CONTEXT) is probably the kernel-mode API you're looking for, at least if you have ntifs.h available.
There is any way to run another process with ordinary rights from a process running as administrator? From a process with elevated rights I want to start a process with ordinary rights like it were started from explorer. I tried impersonation but I didn't work.
Use CreateProcessAsUser(). Details are in the linked SDK docs.
No this is not possible (There are several hacky ways to do this (Inject into explorer, task scheduler, SaferAPI+MediumIL etc) but none of them work in all scenarios)
This seems like a nice way to do it, provided you don't care about situations where the Shell is not running (e.g. possibly some Terminal Services application-only setups, perhaps, though I'm not sure):
http://brandonlive.com/2008/04/27/getting-the-shell-to-run-an-application-for-you-part-2-how/
It gets an interface to Explorer.exe, which should be running in the user's normal context, and asks Explorer to execute a command in its behalf. This is done just using simple, documented COM interfaces and without having to mess around with process tokens or code/DLL injection.
The technique I've used with success is to use IShellDispatch2::ShellExecute to ask Explorer to launch the process. Since Explorer typically runs as normal integrity, this is effective. The trick is that it's a bit of work to get the IShellDispatch2 object. I followed the process outlined in this blog.
on linux you can use setuid to change the program user id
for windows
you can look at his:
https://serverfault.com/questions/16886/is-there-an-equivalent-of-su-for-windows
If at all possible, use Larry Osterman's solution (in the comments to Anders' answer) instead, i.e., have a parent process that runs without elevation, and launch both the elevated process and the non-elevated process from there.
If that isn't possible, there is an approach that should work in almost all cases, though it is usually more trouble than it's worth:
Install and start a system service, configured to run as local system. Give it your Remote Desktop session ID, e.g., via a command-line argument or a registry setting, as well as your process ID and the command line you want to run.
From the system service, use GetTokenInformation with TokenLinkedToken to get the linked token from the target process. You have to do this from a system service because you need SE_TCB_NAME to get a usable token. (This limitation does not appear to be documented but is consistent with the behaviour of tokens in Windows.)
Or, if there is no linked token, use WTSQueryUserToken to get a copy of the user's token. I believe this always gives you the limited token (if there is one) but if you want to be safe you can check whether it is an elevated or limited token using GetTokenInformation and TokenElevationType; if it is an elevated token, you can use TokenLinkedToken to get the limited one.
If there is no linked token (TokenElevationTypeDefault) you should use the token as-is. This may happen either because the user is not an administrator, or because UAC is disabled, either globally or because the user logged in using the built-in Administrator account. If the user is not an administrator then the token is already suitable. If UAC is disabled, you should respect the user's intent and use the administrative token.
You can then use CreateProcessAsUser or CreateProcessWithTokenW from the system service to launch the new process.
Finally, the service should delete itself and stop.
There is at least one edge case: if your elevated process is launched from a command window (or other process) which was launched using runas and non-administrative credentials. There is no split token in this case, and the original token may have already been deleted, so unless you captured a copy before elevation took place (Larry's solution, or variants thereof) there is no general way to run the child process in the same user context that the elevated process was originally launched from. The best you can do is the context of the logged on user (via WTSQueryUserToken as described above) which may not be the behaviour the end user would expect. (This may, however, be an acceptable limitation, depending on the scenario.)
When Explorer needs to de-elevate, it uses a scheduled task. This is probably a good solution in most cases, and much easier than my earlier answer.
A simple example in Powershell:
$action = New-ScheduledTaskAction -Execute 'notepad.exe'
Register-ScheduledTask -TaskName 'Launch Notepad' -Action $action -User $env:username
Start-ScheduledTask -TaskName 'Launch Notepad'
I maintain a variety of managed userlabs on a university campus. These machines all currently run Windows XP and we have a windows service that is used to "lock" a machine by blocking any keyboard or mouse input. The locking happens during our scripted OS installation so that users aren't able to accidentally halt or break the process. It is also used to prevent users from logging into machines until they are checked out at the front desk of a given lab. Ctrl+Alt+Del is blocked via a keyboard filter driver and the rest of the keys and mouse are currently blocked using the BlockInput() function from user32.dll.
In XP, the service runs as Local System and the checkbox for "Allow service to interact with desktop" must be enabled from the BlockInput() call to succeed. Under Vista, this no longer works I'm guessing because of the Session 0 isolation changes. The call succeeds, but input is not actually blocked.
The keyboard filter driver still works just fine and we can use that to block the whole keyboard instead of just Ctrl+Alt+Del. But I'm at a loss as to how we're going to block the mouse now. I'm not even entirely sure the Session 0 isolation is to blame.
Can anyone recommend a fix or a workaround to allow us to block mouse input from a service in Vista and beyond? I've looked for alternative win32 API's without luck. Assuming Session 0 isolation is to blame, is there a legitimate way to call the function from Session 1 or would that sort of defeat the purpose of the isolation? Will I have to rely on an elevated companion exe that runs on user login and communicates with the service?
From a service, you can use WTSEnumerateSessions to get all logged on user sessions, WTSQueryUserToken to get the logged on user's token, and then use CreateProcessAsUser with that token to get code running on the user's desktop. This has the disadvantage that the code will run as the logged on user. Since all input is disabled, this is probably safe enough and is at least as safe as the existing XP solution.]
EDIT: BlockInput requires a high mandatory integrity level. You could try adding this group to the token, but then you have code with higher privileges running on the desktop and potentially open to attacks.
An alternative might be to use your service just to disable all HID class devices on the machine.
I need to ShellExecute something as another user, currently I start a helper process with CreateProcessAsUser that calls ShellExecute, but that seems like too much of a hack (Wrong parent process etc.) Is there a better way to do this?
#PabloG: ImpersonateLoggedOnUser does not work:
HANDLE hTok;
VERIFY(LogonUser("otheruser",0,"password",LOGON32_LOGON_INTERACTIVE,LOGON32_PROVIDER_DEFAULT,&hTok));
VERIFY(ImpersonateLoggedOnUser(hTok));
ShellExecute(0,0,"calc.exe",0,0,SW_SHOW);
RevertToSelf();
CloseHandle(hTok);
will just start calc as the logged in user, not "otheruser"
#1800 INFORMATION: CreateProcess/CreateProcessAsUser is not the same as ShellExecute, with UAC on Vista, CreateProcess is useless when you don't have control over what program the user is executing (CreateProcess will return with a error if you give it a exe file with a manifest marked as requireAdmin)
#Brian R. Bondy: I already know this info (And don't get me wrong, its good stuff), but it is off topic (IMHO) I am asking for a ShellExecuteAsUser, not about starting processes as another user, I already know how to do that.
The solution really depends on what your needs are, and can be pretty complex (Thanks fully to Windows Vista). This is probably going to be beyond your need, but this will help others that find this page via search.
If you do not need the process to run with a GUI and you do not require elevation
If the user you want to run as is already logged into a session
If you need to run the process with a GUI, and the user may, or may not be logged in
If you need to run the process with elevation
Regarding 1:
In windows Vista there exists something called session 0 isolation. All services run as session 0 and you are not supposed to have a GUI in session 0. The first logged on user is logged into session 1. In previous versions of windows (pre Vista), the first logged on user was also ran fully in session 0.
You can run several different processes with different usernames in the same session. You can find a good document about session 0 isolation here.
Since we're dealing with option 1), you don't need a GUI. Therefore you can start your process in session 0.
You'll want a call sequence something like this:
LogonUser, ExpandEnvironmentStringsForUser, GetLogonSID, LoadUserProfile, CreateEnvironmentBlock, CreateProcessAsUser.
Example code for this can be found via any search engine, or via Google code search
Regarding 2: If the user you'd like to run the process as is already logged in, you can simply use: WTSEnumerateSessions, and WTSQuerySessionInformation to get the session ID, and then WTSQueryUserToken to get the user token. From there you can just use the user token in the CreateProcessAsUser Win32 API.
This is a great method because you don't even need to login as the user nor know the user's username/password. I believe this is only possible via a service though running as local system account.
You can get the current session via WTSGetActiveConsoleSessionId.
Regarding 3:
You would follow the same steps as #1, but in addition you would use the STARTUPINFO's lpDesktop field. Set this to winsta0\Default. You will also need to try to use the OpenDesktop Win32 API and if this fails you can CreateDesktop. Before using the station and desktop handles you should use SetSecurityInfo on each of them with SE_WINDOW_OBJECT, and GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION.
If the user in question later tries to login, he will actually see the running process.
Regarding 4:
This can be done as well, but it requires you to already be running an elevated process. A service running as local system account does run as elevated. I could also only get it to work by having an authenticode signed process that I wanted to start. The process you want to start also must have a manifest file associated with it with the requestedExecutionLevel level="requireAdministrator"
Other notes:
You can set a token's session via SetTokenInformation and TokenSessionId
You cannot change the session ID of an already running process.
This whole process would be drastically more simple if Vista was not in the equation.
If you need ShellExecute semantics you can feed following:
C:\windwos\system32\cmd.exe /k" start <your_target_to_be_ShellExecuted>"
to CreateProcessAsUser and you are done.
You can wrap the ShellExecute between ImpersonateLoggedOnUser / RevertToSelf
links:
ImpersonateLoggedOnUser: http://msdn.microsoft.com/en-us/library/aa378612(VS.85).aspx
RevertToSelf: http://msdn.microsoft.com/en-us/library/aa379317.aspx
sorry, cannot hyperlink URLs with "()"
Why don't you just do CreateProcessAsUser specifying the process you want to run?
You may also be able to use SHCreateProcessAsUserW.