I've been testing ( for the last 4 days) the variety of options to start a Process under iis7 ( asp.net)
I did find a solution.
As long as we don't need to interact with desktop, and only need to run cmd ( or something like that) the solution is simple:
w3wp user-> should be high privileged user.
Process start info (StartInfo.Username)-> should be also high privileged user.
However, there is a catch ( according to my testings):
Both users have to be the same (if we want cmd to execute)! this is the only way it will work.
So here are my 2 questions:
Why they both must be the same? Can't w3wp HighPrivilegedUSerA run (via process.startInfo) cmd as HighPriviligedUSerB?
Both users are domain admins.( which are also admins in my local group). Does only domain admin/local admin can run processes on the local machine?
p.s. All folders permissions are everyone : full controll ( including c:\windows\*.* /s and including cmd.exe permissions) and both users, as mentioned, are admins on local machine with same cloned permissions.IIS7 handler mapping * [static file] is set to read+execute
Also, the full cmd command is:
cmd /c time /t >c:\1.txt. A success is if the file exists.( and I succeed, only when both account were the same).
Full code:
Process proc = new Process();
proc.StartInfo.FileName = "cmd";
proc.StartInfo.UserName = "Royin"; //<-- only if this user is the same as w3wp user , the operation succeed !
proc.StartInfo.Domain = ...;
proc.StartInfo.WorkingFolder = #"c:\windows\system32";
proc.StartInfo.Password = ...
proc.StartInfo.Arguments = #"/c time /t >c:\1.txt"
proc.Start();
Ok, first, I HIGHLY recommend using the excellent SysInternals ProcessMonitor to help troubleshoot any issue like this: Process Monitor.
This app will basically tell you every action a process is attempting to take, so in your situation, you'd see it try to call QueryOpen, ProcessCreate, etc.
Have you checked what the ExitCode of the process is under the failing scenario? I'd be willing to bet real money it's coming back as 0xC0000142 (-1073741502), which means something like "failed to load DLL" or something. Running anything under the system32 path, even with privileged user credentials, is going to run into oddball issues with permissions, again due to the initialization procedure for creating a process.
Basically, the Process.Start flow looks something like this:
(assumptions: UserA == process running w3wp, UserB == impersonation ctx, UserC == credentials specified in process start info)
First, UserB isn't going to have much impact, as we've discussed in other conversations; any process creation stuff is going to be based on the process token of the "launching entity", so the credentials of UserA are the ones we'll be looking at.
The runtime says "Hey, can UserA access the file name specified in StartInfo.FileName?"
Windows responds yes/no, but also "BUT, to use that, you also need to be able to read all these other DLLs"
Runtime responds "Ok, can UserA access those DLLs?"
If the answer to all the above is yes, then the runtime says "Ok, logon this user and try creating a new process with cmd line and arguments..."
You are most-likely running into issues with either #2 or #4, as the default app pool identity has no read-access rights to the System32 folder. This is why when you switch the identity of the w3wp process to a privileged account, it works.
You could try a couple things, but the easiest option is probably switching back to a low-privilege account (like default app pool identity), but grant read-only access to the system32 folder to that account.
I would ABSOLUTELY not run w3wp as a privileged user, though - that is just asking for massive headaches should anything nasty happen, like somebody hacking you.
Oh, last thoughts:
DON'T set UseShellExecute to true if you can help it, it does weird stuff.
DON'T set LoadUserProfile to true if you can help it, it also does weird stuff, and is really slow as well.
DO set CreateNoWindow to true if you can, otherwise you'll see lotsa windows opening/closing on the server
DO use RedirectStandardOutput / RedirectStandardError instead of piping the output, it's way more controllable and gives better feedback when things go wrong.
DO always check the ExitCode of the process if it doesn't look like it worked
Related
I am trying to run an antivirus scan on an uploaded file in an ASP.Net web app. We are using Sophos so have access to their command line API sav32cli. In the code I use:
Process proc = new Process();
proc.StartInfo.FileName = #"C:\Program Files (x86)\Sophos\Sophos Anti-Virus\sav32cli.exe";
proc.StartInfo.Arguments = #"-remove -nc " + SavedFile;
proc.StartInfo.Verb = "runas";
proc.Start();
proc.WaitForExit();
int exitCode = proc.ExitCode;
When stepping through the code, when attached to the w3wp process on dev server, the code just jumps from one line to the next seemingly doing nothing at all. When running from code on dev server, it performs as expected scanning file and deleting if it is seen as a virus.
The server is running IIS 8.0, and the app built in .Net Framework 4. I have changed the machine config to allow the process to run as SYSTEM account, in accordance to these instructions. https://support.microsoft.com/en-us/kb/317012#%2Fen-us%2Fkb%2F317012
<processModel userName="SYSTEM" password="AutoGenerate" />
Is there something I'm missing? What is the best practice for this kind of implementation?
EDIT: When called, the Process returns an ExitCode of 2 (Error stopped execution), rather than the expected 0 (Scan worked, no viruses), or 3 (Scan worked, viruses found).
EDIT 2: As per comment below I changed the code to:
Process proc = new Process();
proc.StartInfo.FileName = #"C:\Program Files (x86)\Sophos\Sophos Anti-Virus\sav32cli.exe";
proc.StartInfo.Arguments = #"-remove -nc " + SavedFile;
proc.StartInfo.RedirectStandardOutput = true;
proc.StartInfo.UseShellExecute = false;
proc.Start();
StringBuilder output = new StringBuilder();
while (!proc.StandardOutput.EndOfStream)
{
string line = proc.StandardOutput.ReadLine();
output.AppendLine(line);
}
proc.WaitForExit();
int exitCode = proc.ExitCode;
ASPxMemo2.Text = exitCode.ToString() + Environment.NewLine + output.ToString();
output is always empty when run over IIS, but is populated correctly when running from code.
EDIT 3: Instead of looking at StandardOutput we looked at StandardError and it revealed this error:
Error initialising detection engine [0xa0040200]
(Possible insufficient user Admin rights.)
For the time being we are going to move to another method of virus checking, but would still like to know a possible solution if anyone has it.
You will need to make sure that the application pool that is running your .NET application inside IIS has execute permissions to your file
"C:\Program Files (x86)\Sophos\Sophos Anti-Virus\sav32cli.exe"
You may also need to add this permission to the folder location where the file to be scanned is uploaded (c:\temp) for example
You may also need to have administrator privileges to run the anti virus scan since IIS8 does not run as an administrator. When you are debugging visual studio uses your current logged in windows user(unless you use runas) so this will explain why it would work when debugging.
Have you tried running your web process in elevated trust?
Configuring .NET Trust Levels in IIS 7
<system.web>
<securityPolicy>
<trustLevel name="Full" policyFile="internal"/>
</securityPolicy>
</system.web>
ASP.NET Trust Levels and Policy Files
Most likely the permissions are not configured correctly on the content being scanned (the uploads folder) or the worker process user doesn't have the full permissions it needs to use Sophos. You know the executable itself is accessible by the worker process because you are getting exit codes and error messages that are specific to Sophos.
Because your process will delete files that are perceived as threats you need to grant the user running the process modify or full control permissions on the folders that store the uploaded files.
By default you could use the IIS_IUSRS group for ApplicationPoolIdentity processes, but you can verify (and modify) the user in IIS Manager > App Pools > Advanced.
This answer has more details
Here are some ideas:
Create the process using a different user with elevated privileges on the folder, see for reference start-a-net-process-as-a-different-user
If the previous suggestion fails, login one time on the server using the credentials used in point 1. It will configure registry entries connected to the user profile, some programs requires it.
Develop a simple .net service running on the server and monitoring the upload folder. The service has more probability running the Sophos scan succesfully. Here is a reference on service creation using .net.
The service may also talk to your web page using DB/file system/ etc.. so the operation may seem synchronous.
These are my 4 cents :-)
I have an application that under rare circumstances needs to change its registry setting. Also during it's first execution it needs to create a new key. I'm developing this in Windows 7. I get ThrowUnauthorizedAccessException. How do I force Windows to give me a UAC prompt to temporarily elevate my permissions?
Thanks in advance.
Should all users be allowed to modify this setting? If so, the simplest solution is to modify your installation program to give Users Full Control of the registry key.
If only administrators should be able to modify this setting, then you will need to launch another copy of your program, asking Windows to elevate it:
ProcessStartInfo startInfo = new ProcessStartInfo("C:\Path\To\MyApplication.exe");
startInfo.Verb = "runas"; //trigger a UAC prompt (if UAC is enabled)
System.Diagnostics.Process.Start(startInfo);
If you were smart you would include some command line arguments, so you can tell "yourself" that it should jump straight to the part of the software that the user needs to deal with. Or your command line arguments could just say what you want done:
ProcessStartInfo startInfo = new ProcessStartInfo(
"C:\Path\To\MyApplication.exe",
"/setLoggingEnabled yes");
startInfo.Verb = "runas"; //trigger a UAC prompt (if UAC is enabled)
System.Diagnostics.Process.Start(startInfo);
Have your application check for the setLoggingEnabled switch, make the change, and then exit.
Update: A common situation is players of World of Warcraft. Since the game is allowed to update itself while running, all users must be allowed to modify the game data sitting in Program Files. The correct and valid action is to modify the ACLs on the
C:\Program Files\Blizzard\World of Warcraft
folder so that all users have full control. In fact, before Blizzard got their act together, Microsoft released an application compatibility update that gives all users full control to the WoW folder next time it run as an administrator.
Another common case is when the Blizzard Launcher is launched with administrative privelages, it updates a registry key in HKLM, recording where the game is. This happens when, for example, i move WoW from a hard drive to an SSD drive
run the launcher once as an administator so that the updaters work correctly.
I have a kiosk app and have to disable task manager always to prevent closing the program by users .
But some users need the TaskManager to close hanging programs.
Any help would be appropriated.
However, I am sure there is a function in windows to prevent closing a program's process , as when one attempt to kill rundll.exe process. I want to know that function if I can call it with DllImport
Can anyone help with a trick?
A hack?
A function?
Any other solution?
EDIT:
At least if there is not a way to prevent the process from being closed, I need a way to hide it from processes list appeared in the task manager.
EDIT 2:
I can't find the solution so far
One approach, if you could access the process ID in an administrative context, is to deny the PROCESS_TERMINATE permission on the process to end users. Terminating the process (through task manager or other contexts) is by default granted to the owner, but can be explicitly denied. When it is denied, terminating the process would require the owner to manually change the ACL, and then terminate the process. If the user is neither an administrator nor the owner of the process, he will not be able to forcibly terminate the process (e.g., through Task Manager), although the process will be allowed to exit normally.
The following code puts an explicit deny ACE on the process with the PID processid for members of the Everyone group.
#include "Aclapi.h"
#include "Sddl.h"
DWORD RestrictTerminateOnProcessId(DWORD processid)
{
PACL dacl = NULL, newdacl = NULL;
HANDLE ph = NULL;
PSECURITY_DESCRIPTOR* desc = NULL;
PSID everyonesid = NULL;
ph = OpenProcess(WRITE_DAC | READ_CONTROL, false, processid);
if (!ph) goto cleanup;
if (ERROR_SUCCESS != GetSecurityInfo(ph,
SE_KERNEL_OBJECT,
DACL_SECURITY_INFORMATION,
NULL,
NULL,
&dacl,
NULL,
desc)) goto cleanup;
SID_IDENTIFIER_AUTHORITY WorldAuth = SECURITY_WORLD_SID_AUTHORITY;
if (!AllocateAndInitializeSid(
&WorldAuth,1,SECURITY_WORLD_RID,
0,0,0,0,0,0,0,&everyonesid)) goto cleanup;
// begin copy dacl
_ACL_SIZE_INFORMATION si;
GetAclInformation(dacl,
&si,
sizeof(si),
AclSizeInformation);
DWORD dwNewAclSize = si.AclBytesInUse +
(2*sizeof(ACCESS_DENIED_ACE)) + (2*GetLengthSid(everyonesid)) -
(2*sizeof(DWORD));
newdacl = (PACL)HeapAlloc(
GetProcessHeap(),
HEAP_ZERO_MEMORY,
dwNewAclSize);
if (newdacl == NULL) goto cleanup;
if (!InitializeAcl(newdacl, dwNewAclSize, ACL_REVISION_DS))
goto cleanup;
if (!AddAccessDeniedAce(newdacl,
ACL_REVISION_DS,
PROCESS_TERMINATE,
everyonesid)) goto cleanup;
for (int i = 0; i < si.AceCount; i++)
{
LPVOID pace = NULL;
if (!GetAce(dacl, i, &pace)) goto cleanup;
if (!AddAce(newdacl, ACL_REVISION_DS,
MAXDWORD, pace, ((PACE_HEADER)pace)->AceSize))
goto cleanup;
}
// end copy dacl
if (!SetSecurityInfo(ph,
SE_KERNEL_OBJECT,
DACL_SECURITY_INFORMATION,
NULL,
NULL,
newdacl,
NULL)) goto cleanup;
SetLastError(0);
cleanup:
DWORD ret = GetLastError();
if (desc) LocalFree(desc);
if (newdacl) HeapFree(GetProcessHeap(), 0, (LPVOID)newdacl);
if (ph) CloseHandle(ph);
if (everyonesid) FreeSid(everyonesid);
return !ret;
}
You could create a service that runs on boot. It then monitors when the user logs in and starts your program. From there you have two choices:
Have it wait on the program and revive it for as long as the user's session persists.
Run it under an administrator account and make sure the users are always running on limited accounts. Windows' privilege enforcement should take care of your program not dying.
If you must allow the users administrative privileges (and they can thus kill your service), make sure to register your service so that the SCM restarts it automatically.
Anything beyond that would require some kernel hacking, as mdm said, or diving into rootkit territory. Which I would suggest you avoid.
If by any chance you're still running Windows XP, instead of a system service, you can register a Winlogon notification package. They're basically unkillable, as they run in the context of winlogon.exe, which is the reason they were removed from Vista and above.
The proper way of doing this is:
Create a user for the Kiosk application. Lets say KioskUser
Create other users that do stuff in the computer. Lets say User1
NONE of them should be administrator. They do not need to be admins right? They can have privileges of course.
You are going to use User1 account as usual.
Then, you run the Kiosk application as the KioskUser. (use runas command)
- add the application to the session and make it visible (see the arguments for more info on this)
While the User1 is loged in and the Kiosk application runs from the KioskUser, the User1 cannot kill the process.
I wouldn't recommend "hacking" the system, nor changing something on the registry.
Also, you can make the Kiosk application a service, and run it under the Kiosk user.
It sounds like you can't prevent a process from being killed - see this question and specifically the link posted to an explanation.
You can however prevent the user from opening the task manager from the taskbar (right click -> open task manager), from pressing Ctrl-Shift-Esc, or from typing taskman at the command prompt using the following registry key:
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Policies\System\DisableTaskMgr
Setting it to 1 disables the task manager, 0 enables it again.
From the command prompt, disable it like so:
reg add HKCU\Software\Microsoft\Windows\CurrentVersion\Policies\System /v DisableTaskMgr /t REG_DWORD /d 1 /f
And you can renable with this command:
reg add HKCU\Software\Microsoft\Windows\CurrentVersion\Policies\System /v DisableTaskMgr /t REG_DWORD /d 0 /f
You'll also need to find a way of preventing the user from writing to this key in the registry - although on my Windows 7 64-bit machine I had to open an admin command prompt before I could change the key.
Edit
As suggested in the comments, you could intercept a low level system call to alter the list of processes reported. This answer eludes to it, you will basically have to write a kernel-mode rootkit - probably implemented as a device driver. This will likely only work in Windows XP and below due to kernel changes made in Vista and onwards. It can be done however, a friend of mine created a prototype of this for his BSc final year dissertation.
You'll also have to consider other methods of querying for processes - IIRC NtQuerySystemInformation isn't the only way that processes can be enumerated.
Short of protecting your Kiosk app with some rootkit/driver you can't... although the methods you would need to use in this context are "malware-like" so beware...
Here http://blogs.msdn.com/b/oldnewthing/archive/2004/02/16/73780.aspx you can see on one side why this is a task not really possible and on the other side the several possibilities you would have to defend against... which would be:
deny PROCESS_TERMINATE
deny PROCESS_CREATE_THREAD
deny PROCESS_VM_WRITE
deny PROCESS_SUSPEND_RESUME
Plus any defense you can get your hands on against debuggers - another very tricky business.
Sorry for not posting this as a comment (not enough reputation).
I'm using drfs solution, but it seems to induce a memory leak with the way GetSecurityInfo is used. I changed the code and got rid of the memory leak like this:
Declaration:
PSECURITY_DESCRIPTOR desc = NULL;
Initialisation:
desc = (PSECURITY_DESCRIPTOR)GlobalAlloc(GMEM_FIXED, sizeof(PSECURITY_DESCRIPTOR));
Cleanup:
if (desc) GlobalFree(desc);
Call:
GetSecurityInfo(ph, SE_KERNEL_OBJECT, DACL_SECURITY_INFORMATION, NULL, NULL, &dacl, NULL, &desc)
I also found this article on the issue: http://i1.blogs.msdn.com/b/oldnewthing/archive/2012/12/12/10376639.aspx
While you can't prevent an authorized user from killing your process, you can prevent users from having the necessary permissions to kill your process. That said, a local administrator will always have permission to kill any process. Is your hope to prevent closing of your application by any user or just non-admins? If the former, simply running the process under a dedicated user account may be enough to do the trick.
I was always under the impression that when you're running a process as (domain\user) mydomain\myuser, when using Process.Start() it would start this new process using the same credentials - mydomain\myuser.
The issue I'm having is that my Process.Start() call seems to be creating a process under the SYSTEM account which is causing me permission issues in the started process (which must run under an admin account due to the work it does). If it changes things - I'm spawning this process (a custom built exe) from within a windows installer.
Any suggestions? I've read about windows group policies (possibly) having an impact on this, but if I'm honest, it's lost on me.
EDIT: a little snippet:
Where exename and commandLine are parameters for this method body:
ProcessStartInfo procInfo = new ProcessStartInfo(exeName, commandLine);
procInfo.WorkingDirectory = workingDirectory;
procInfo.UseShellExecute = false;
procInfo.CreateNoWindow = true;
Process process = Process.Start(procInfo);
Process.WaitForExit();
return process.ExitCode;
Either set procInfo.UseShellExecute to true, or execute cmd as a process with your exe as a parameter to the cmd command. When UseShellExecute is set to false, here are a lot of interesting side effects: UseShellExecute
Your impression is true. Process.Start() will always start the new process under current user's credentials - unless you provide alternative credentials in the ProcessStartInfo or use one of the overloads that take credentials.
There must be another problem - share a snippet of your code.
UPDATE
OK! You did not mention anything about installer. All MSI installers will be running under system since they will be run by "Windows Installer" which you can check and they run under SYSTEM.
This is code from a class library:
proc.StartInfo = new ProcessStartInfo(CmdPath, "+an -b");
proc.StartInfo.RedirectStandardOutput = true;
proc.StartInfo.CreateNoWindow = true;
proc.StartInfo.UseShellExecute = false;
proc.Start();
proc.WaitForExit();
This works perfectly as I would expect when called from a console test app. When I take the same library and call the method from an ASP .NET web service it just hangs.
Is there something I am missing here, perhaps permissions? The ASPNET service has access to the folder where the EXE is, and I see it running in Task Manager, though it isn't doing anything.
If anyone could tell me what I'm doing wrong, I would appreciate it. Thanks.
EDIT: Sorry for the lack of information. CmdPath goes to the command line interface for our scheduling software. I'm passing in commands based on the documentation they provided. I have one method to get a list of jobs, and another method to run a job. ...hmm idea. The client normally uses Active Directory to login, I think impersonation is going to be necessary. Going to test now.
EDIT 2: Ok, now the client is blowing up with AccessViolation issues. This is obviously a permissions thing. If the software uses integrated AD authorization, and I impersonate my AD account, will that be sufficient? I'm doing impersonation using the tag in web.config.
I think you will face a lot of problems launching an executable server side using the ASPNET identity, have you tried impersonating an identity with appropriate priveleges (this does work btw), but again launching an executable on the server side is probably not a good idea to begin with.
The ASP.Net user account probably doesn't have permissions to execute. Can you give a bit more information as to why you are trying to do this as there may be a better way of doing it.
It could be a permissions issue. The ASPNET service may have permissions to the executable, but does it have permissions for everything the executable does.
For example, if the executable copies files, does the ASPNET account have full rights to the source and destination paths of those files? The same questions need to be asked of everything the executable does.
If you need to get around this, you can use impersonation, or assign the web site to run under a different account in IIS, but those are not recommended practices, and more trouble than they're worth in most cases.
By default the ASP.NET worker process has less security than most local account (certainly an account that a developer uses or the logged in account on a server.)
There are two main ways to move forward:
Give the asp.net process more priviledges. See This Link for a good explanation of how to do that.
Have asp.net run under an account with more priviledges. See This Link for a good explanation and how to get that process running under a different account.
Either will work for you.
When you redirect standard output don't you need to use ReadToEnd to read the response from StandardOutput?
You probably should check what is your executable performs, cos ASP.NET works under user with limited rights (NETWORK SERVICE on IIS 6.0) and you executable also gets this rights and runs under same user. As far as you waiting on until it finishes its work, probably something wrong in the executable you are trying to run. I suggest you to make a simple experiment - switch your WebApplication to build-in in VS web server, called "Casini" and check your code behavior. By means of this you can prove yourself that it's not ASP.NET's fault. If I am right the only thing you will need to do is to investigate problems of you executable and determine what rights it needs.
Instead of Impersonation or giving Asp.net more privileges, how about launching the process under different credentials.
In the sample below, UserWithVeryLimitedRights would be a new account that you create with just enought rights to run the app.
Doing so may minimize the security risks.
ProcessStartInfo StartInfo = new ProcessStartInfo();
SecureString ss = new SecureString();
string insecurePassword = "SomePassword";
foreach(char passChar in insecurePassword.ToCharArray()) {
ss.AppendChar(passChar);
}
StartInfo.RedirectStandardInput = true;
StartInfo.RedirectStandardError = true;
StartInfo.RedirectStandardOutput = true;
StartInfo.CreateNoWindow = true;
StartInfo.UseShellExecute = false;
StartInfo.Password = ss;
StartInfo.UserName = #"UserWithVeryLimitedRights";
StartInfo.FileName = #"c:\winnt\notepad.exe";
Process.Start(StartInfo);