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.
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 :-)
I have created an WebApi project in which I am calling a exe namely Latlong2XY.exe which takes input file and outputfile as paramreter. And returning me a .txt as output file. When I am executing the application from VS2012 IDE it is successfully creating the required txt file. However when I publish the same application in IIS and running it then it is not able to create the txt file.
it appears IIS Express is creating the txt file while IIS is not.
It appears to be some permission issue. But does not have any clue what to do.
My code is:
int exitCode;
// Prepare the process to run
ProcessStartInfo start = new ProcessStartInfo();
// Enter in the command line arguments, everything you would enter after the executable name itself
start.Arguments = #"D:\RFD\InputFile.txt D:\RFD\Results.txt";
// Enter the executable to run, including the complete path
start.FileName = #"D:\RFD\Latlong2XY.exe";
// Do you want to show a console window?
start.WindowStyle = ProcessWindowStyle.Hidden;
start.CreateNoWindow = true;
// Run the external process & wait for it to finish
using (Process proc = Process.Start(start))
{
proc.WaitForExit();
// Retrieve the app's exit code
exitCode = proc.ExitCode;
}
IIS settings are :
Windows Authentication: disabled;
Forms Authentication: disabled;
Anon auth: enabled;
.Net Impersonation: disabled.
i'm using ASP.NET v4.0 Application pool.
You will need to give the application directory (where the hosted files are on the machine) elevated privileges. (Typically C:\inetpub\wwwroot\YourAppName)
Give the user 'IIS_USR' or something close to that name write access to the folder.
Yes., When you perform these kind of operations from "Visual Studio IDE" It will work because IDE has minimum permission to control your IO operations for (System.Diagnostics.Process.Start).
When you go to Web application hosting from IIS, unfortunately IIS doesn't have these permission settings in built. So you need to set permissions to perform windows native operations.
Note : By using this you are gonna provide your system(server) username and password as encrypted.
You can set windows authentication permission in the Web Config Using Aspnet_setreg.exe. Which will be available in internet with usage notes.
Add the below line in your webconfig:
<authentication mode="Windows"/>
<identity impersonate ="true" userName="registry:HKLM\SOFTWARE\YourAPPName\ASPNET_SETREG,userName" password="registry:HKLM\SOFTWARE\YOURAPPNAME\ASPNET_SETREG,password"/>
The similar problem i have faced during development of "windows service Re-Start from web". The Same permission issues i have resolved and got worked on this way.
This answer may not be perfect. But this is also one way to achieve the solution
I need to run "manage-bde" shell command from C# code.
The main application process is already running as administrator and is Elevated.
I used code from : UAC self-elevation example on MS website for confirming the app process is elevated.
(http://code.msdn.microsoft.com/windowsdesktop/CSUACSelfElevation-644673d3)
However, when I try to run manage-bde from the C# code, I get "System can't find file specified".
Process p = new Process();
p.StartInfo.FileName = "C:\\Windows\\System32\\manage-bde.exe";
p.StartInfo.UseShellExecute = true;
p.Start();
As a workaround, I tried to create a batch file that runs the command.
string batchFileName = DateTime.Now.Ticks + ".bat";
StreamWriter writer = new StreamWriter(batchFileName);
writer.WriteLine("manage-bde");
writer.Flush();
writer.Close();
Process p = new Process();
p.StartInfo.FileName = batchFileName;
p.StartInfo.UseShellExecute = true;
p.Start();
The batch file is written , and executed successfully; However, the command "manage-bde" is not recognized.
I changed the code to use the verb "runas" and use admin password and that works, but I want the batch file to work without the need for providing the admin password. The current logged in user is already administrator on the computer but the batch file is not getting executed with the existing admin privileges . I need the batch file to execute and manage-bde to run successfully.
Your help or advice will be very highly appreciated :)
ps: some commands other than manage-bde work fine without need for admin runas.
The reason of the behavior I encountered was the Windows File System Redirector.
In most cases, whenever a 32-bit application attempts to access %windir%\System32, the access is redirected to %windir%\SysWOW64
https://msdn.microsoft.com/en-us/library/windows/desktop/aa384187%28v=vs.85%29.aspx
My application build was 32 bits. Whenever it tried to access System32 windows automatically redirected it to SysWow64 which does not contain "manage-bde.exe". I changed the build to 64 bits and then the application could access manage-bde.exe from System32
Even if you're running as the Administrator user, you're not fully elevated if UAC is running. Meaning that you'll have either the UAC prompt come up or you'll be prompted for a password.
The only real way you could get around that is to run your application elevated first, or to write a service that runs with elevated permissions to start your new process.
The alternative of course is to disable UAC, but that is undesirable in most situations.
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.