I have problem with starting processes in impersonated context in ASP.NET 2.0.
I am starting new Process in my web service code. IIS 5.1, .NET 2.0
[WebMethod]
public string HelloWorld()
{
string path = #"C:\KB\GetWindowUser.exe";
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.WorkingDirectory = Path.GetDirectoryName(path);
startInfo.FileName = path;
startInfo.UseShellExecute = false;
startInfo.CreateNoWindow = true;
startInfo.ErrorDialog = false;
startInfo.RedirectStandardOutput = true;
startInfo.RedirectStandardError = true;
Process docCreateProcess = Process.Start(startInfo);
string errors = docCreateProcess.StandardError.ReadToEnd();
string output = docCreateProcess.StandardOutput.ReadToEnd();
}
The "C:\KB\GetWindowUser.exe" is console application containing following code:
static void Main(string[] args)
{
Console.WriteLine("Windows: " + WindowsIdentity.GetCurrent().Name);
}
When I invoke web service without impersonation, everything works fine.
When I turn on impersonation, following error is written in "errors" variable in web service code:
Unhandled Exception: System.Security.SecurityException: Access is denied.\r\n\r\n at System.Security.Principal.WindowsIdentity.GetCurrentInternal(TokenAccessLevels desiredAccess, Boolean threadOnly)\r\n at System.Security.Principal.WindowsIdentity.GetCurrent()\r\n at ObfuscatedMdc.Program.Main(String[] args)\r\nThe Zone of the assembly that failed was:\r\nMyComputer
Impersonated user is local administrator and has access to C:\KB\GetWindowUser.exe executable.
When I specify window user explicitly in ProcesStartInfo properties Domain, User and Password, I got following message:
http://img201.imageshack.us/img201/5870/pstartah8.jpg
Is it possible to start process with different credentials than ASPNET from asp.net (IIS 5.1) ?
You have to put privileged code into the GAC (or run in Full trust).
The code in the GAC must assert the XXXPermission, where XXX is what ever permission you are requesting, be it impersonation, access to the harddrive or what have you.
You should revert the assert immediately afterwords.
You should make sure that the API on your DLL that you put in the GAC has no opportunities for abuse. For example, if you were writing a website for letting users backup the server via a command line application, your API should old expose a method like "BackUp()" and not "LaunchAribitraryProcess(string path)"
The web.config file must have impersonation set up as well, or you will run into NTFS permission problems as well as CAS.
Here is the complete explanation.
You might also try wrapping your code inside
using (Impersonator person = new Impersonator("domainName", "userName",
"password")
{
// do something requiring special permissions
}
as mentioned in
http://www.devnewsgroups.net/group/microsoft.public.dotnet.framework.windowsforms/topic62740.aspx
What exactly are you trying to do? I can't quite see what the point of your code is in creating a different executable. It looks rather odd. Perhaps it would be more helpful to state the busines problem you are trying to solve first.
It looks like you're trying to have the IIS service impersonate a user with higher privileges than the service itself (in this case, an administrator). Windows blocks this as a security hole, since at that point you're basically begging someone to take over your system. There may be a way to work around this limitation, but don't do that--it's for your own good.
Instead, have IIS impersonate a user with limited permissions, who has exactly the rights that you need it to have. E.g. create a user account that owns only the folders that you want your web service to write to, or whatever other combination of rights is appropriate. If impersonating a limited user, you won't see this error code, but should still be able to call the benign executable you have here.
Related
I need to run an executable on the server from an MVC controller. Problem: the executable sits within Program Files folder and will also read a value from registry.
I have granted execution rights on the respective folder to my application pool.
So here's my problem:
Running the exe just with Process.Start(exe) will start the executable which in turn then exits with an error because it cannot read the registry value (no access).
Assigning a local admin user to ProcessStartInfo fails:
var exe = #"C:\Program Files (x86)\[path to exe]";
var secString = new SecureString();
secString.AppendChar('character');
//...
secString.MakeReadOnly();
var procInfo = new ProcessStartInfo(exe, settingsPath)
{
UseShellExecute = false,
UserName = "[username]",
Domain = "[domain]",
Password = secString,
RedirectStandardError = true,
RedirectStandardOutput = true,
RedirectStandardInput = true,
Verb = "runas"
};
var proc = Process.Start(procInfo);
proc.WaitForExit();
This will cause a crash of conhost and the executable.
Using impersonation like this:
var impers = new ImpersonationService();
impers.PerformImpersonatedTask("[user]", "[domain]", "[password]",
ImpersonationService.LOGON32_LOGON_INTERACTIVE, ImpersonationService.LOGON32_PROVIDER_DEFAULT, new Action(RunClient));
...with the method RunClient() simply using Process.Start(exe) will do absolutely nothing! The method is run but the process is not being started. I know that the method is run because I added this line to it:
_logger.Debug("Impersonated: {0}", Environment.UserName);
Which correctly gives me the desired user name the process shall use. That user has local Admin privileges, so there should not be an issue there.
I have even tried starting a different executable from my Controller and have that one use impersonation (both variants) to start the target executable - same outcome.
So right now I'm at a dead end. Can anyone please tell me what I'm doing wrong and what I have to do to make it work?
P.S: running the target executable directly on the server when logged in as the local admin user works perfectly fine, so no prob with the exe itself.
Edit:
It seems one part of my description was incorrect: with impersonation and RunClient method I actually did not use Process.Start(exe) but this:
var procInfo = new ProcessStartInfo(exe, settingsPath)
{
UseShellExecute = false,
};
_logger.Debug("Impersonated: {0}", Environment.UserName);
var proc = Process.Start(procInfo);
Out of desperation I have now circumvented procInfo(don't actually need it) and really called
var proc = Process.Start(exe, argument);
And now the .exe starts! It seems using ProcessStartInfo overrides the impersonation for the process??
Still not OK though, as now I get an "Access denied" error. Despite being local admin. This is just weird.
Edit 2:
This is how my latest attempt went:
Switched back to calling a helper .exe, passing the same arguments later used for the actual target exe in Program Files
added a manifest to that helper exe with level="requireAdministrator"
Added Impersonation to my helper exe according to https://msdn.microsoft.com/en-us/library/w070t6ka(v=vs.110).aspx with [PermissionSetAttribute(SecurityAction.Demand, Name = "FullTrust")] added before the method starting the target process.
Started the process by providing ProcessStartInfo with all the jazz
Resulting code:
try
{
var secString = new SecureString();
//...
secString.MakeReadOnly();
var procInfo = new ProcessStartInfo()
{
FileName = Path.GetFileName(exe),
UserName = "[UserName]",
Domain = "[domain]",
Password = secString,
UseShellExecute = false,
RedirectStandardError = true,
RedirectStandardOutput = true,
RedirectStandardInput = true,
Arguments = settingsPath,
WorkingDirectory = #"C:\Program Files (x86)\[rest]"
};
var proc = Process.Start(procInfo);
proc.WaitForExit();
if (proc.ExitCode != 0)
{
using (var sw = new StreamWriter(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "error.log"), true))
{
sw.WriteLine("Error running process:\r\n{0}", proc.ExitCode.ToString());
}
}
}
catch (Exception ex)
{
using (var sw = new StreamWriter(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "error.log"), true))
{
sw.WriteLine("Error running process:\r\n{0}\r\nRunning as: {1}", ex.ToString(), WindowsIdentity.GetCurrent().Name);
}
}
Resulting output to error.log:
Helper running!
[passed argument]
Error running process: System.ComponentModel.Win32Exception
(0x80004005): Access denied at
System.Diagnostics.Process.StartWithCreateProcess(ProcessStartInfo
startInfo) at System.Diagnostics.Process.Start() at
System.Diagnostics.Process.Start(ProcessStartInfo startInfo) at
RunClient.ImpersonationDemo.RunClient(String settingsPath)
Running as: [correct domain user in Admin group]
So I can start the helper exe but that cannot start the real exe in Program Files due to Acess denied despite running under a local Admin account and all files access locally, not on network drives.
The logic of this eludes me.
Edit 3
Update: I have added a manifest to the target .exe also,
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
This means I now:
Call a helper exe from the controller: works
The helper .exe has a manifest to run with elevated rights (Admin)
The helper .exe uses impersonation to assume the identity of a local admin to start a process
Said process is started using a ProcessStartInfo in which username, domain, and password are additionally set to the same local admin user
The helper exe then tries to run the target exe using Process.Start(Startinfo) with the local admin user set, while still impersonating that user's windows identity
And still the error log spouts "Access denied" while correctly returning WindowsIdentity.GetCurrent().Name as that of the local admin.
And now, the greatest of all happened: I created a new local user on that server, added him to local admin group and used that user for impersonation just in case there is a problem with domain users. Guess what? Now I get an error Access denied to ...\error.log - written to the effing error log.
Really?
Edit 4
I think I'll try TopShelf to convert this shebang to a service. Hope to get this done over the weekend.
According to this article your mvc controller thread should have full-trust permission to run the process:
This class contains a link demand at the class level that applies to
all members. A SecurityException is thrown when the immediate caller
does not have full-trust permission. For details about security
demands, see Link Demands.
Seems you problem is not the user but full-trust. I do not know which version of MVC you use but you can read the articles Trust Levels and Code Access to find out the best way to configure your application. Seems you can grant full-trust permission only to specific .exe file or grant full-trust permission to application pool user (do not forget about folder permissions).
But the best approach is to write some windows service and run it instead of running some .exe file directly.
You can try trust level in web.config of your application
<system.web>
<securityPolicy>
<trustLevel name="Full" policyFile="internal"/>
</securityPolicy>
</system.web>
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 :-)
We have a .exe which we need to execute at the time an order is placed on a website. When
we test this locally it works fine using IIS Express. When we move it to IIS, it fails. We assume this is a permissions error as if we set the App Pool to run as the administrator then the script works again. The question we have is how do we execute the .exe as the administrator whilst the App Pool is ApplicationIdentity? We are using the following code:
var process = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = executablePath,
Arguments = argumentList,
Domain = domain,
UserName = userName,
Password = securePassword,
UseShellExecute = false,
LoadUserProfile = true
}
};
process.Start();
process.WaitForExit();
process.Close();
The .exe is trying to write to the Users AppData folder which is why it fails. It is a 3rd party app so we cannot change the source code.
EDIT: Just to clarify also, when we specify the username and password in procmon it still appears to run from ISUR.
We fixed this by enabling User profile on IIS AppPool and setting permission for the IIS user on the folder it was trying to write to.
We sue ProcMon to find where the program was failing and the folder it was trying towrite to was C:\Windows\System32\config\systemprofile
i dont remember actually, but one of them is working 100% ( i had this issue before)
just let me know ehich one of them is the correct one.
I have an application that requeres to register a dll into a gac on a client computer everytime a new dll is deployed the problem is that the client computers only have restricted users with UAC turned on.
The application knows the credentials of a user that has admin rights.
The problem is if I have processStartInfo.UseShellExecute = false; then I can't get trough the UAC getting the error "The requested opperation requires elevation"
and if I have processStartInfo.UseShellExecute = true; then it does not allow to run the whole thing as a different user: "The process object must have UseShellExecute property set to false in order to start the process as a user"
internal static void Register(String assemblyName)
{
ProcessStartInfo processStartInfo = new ProcessStartInfo(lcDir + "gacutil.exe", string.Format("/i {0}.dll", assemblyName));
processStartInfo.UseShellExecute = false;
processStartInfo.WorkingDirectory = lcDir;
processStartInfo.UserName = lcUser;
processStartInfo.Password = Password;
processStartInfo.Domain = lcDomain;
processStartInfo.Verb = "runas";
Process process = Process.Start(processStartInfo);
process.WaitForExit();
}
I would like to know what the "Best practice" for that kind of thing is? I know the whoe concept looks to windows like a virus.
What I would like to achive the following:
User dosen't need to know an account with admin rights
The credencal's of the admin user are saved in the program's database
As much as possable Automated registration.
You should run this part of your code as Administrator.
What you need is impersonation.
See this article
Aricle Impersonation on Code project
You are not meant to embed such user strings within an application for security reasons.
The design idea is that you deploy using System Management Services or similar to manage the deployment (which sucks).
I got round it my using private assemblies, very similar to the way unix works.
If you are looking to add class support in SQL Native Client, then you will find it is an uphill struggle to get deployed each time.
If you know a local administrator name and password, you could use a central deployment solution and not try to get your app to impersonate an administrator.
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);