I have to start a command line program with System.Diagnostics.Process.Start() and run it as Administrator.
This action will also be run by a Scheduled Task every day.
I've just try to use :
Process p = new Process();
p.StartInfo.Verb = "runas";
this works fine if I'm running my program as Administrator, but when the Scheduled Task runs it, it doesn't take the 'runas' in consideration I think.
A better secure option to run a process with login and password is use the SecureString class to encrypt the password. Here a sample of code:
string pass = "yourpass";
string name ="login";
SecureString str;
ProcessStartInfo startInfo = new ProcessStartInfo();
char[] chArray = pass.ToCharArray();
fixed (char* chRef = chArray)
{
str = new SecureString(chRef, chArray.Length);
}
startInfo.Password = str;
startInfo.UserName = name;
Process.Start(startInfo);
You must allow unsafe code in your project properties.
Hope this help.
If you are using scheduled tasks, you can set the user and password for the task to run under.
Use the administrator credentials on the task and you will be fine.
With Process.Start, you need to supply the UserName and Password for the ProcessStartInfo:
Process p = new Process("pathto.exe");
p.StartInfo.UserName = "Administrator";
p.StartInfo.Password = "password";
p.Start();
Be aware that storing a password in clear text in a program is never secure as long as someone can peruse the application and see what is in it. Using SecureString to convert a stored password into a SecureString password does not make it more secure, because the clear text password would still be present.
The best way to use SecureString is to pass a single character for conversion at a time in someway that does not require having the complete unencrypted password anywhere in memory or on the hard drive. After that character is converted, the program should forget it, and then go on to the next.
This could be done I think only by passing characters through for translation as they are being typed into the console by the user.
I found it, I need to set the Scheduled Task to run the application with highest privileges in General settings.
Related
I need to write a file while impersonated as another user John (Member of administrator).
What happens:
File is written and created, then all Access Control is removed and added (Full Control).
No matter where in this process i try to Encrypt file access is denied
using FileOptions
Using File.Encrypt.
my worked around to at least encrypt was with opening another impersonation later on.
But No matter what i do, i can't Decrypt or even read the encrypted Text file.
If file is encrypted reading or decrypting fails, even though i can read or write just fine while impersonated.
In some scenarios Everything works fine, But after a system restart or logout, again fails with access denied.
For impersonation i used this answers Impersonation
using (impersonate = new ImpersonateUser(ImpersonationType.WinIdentity, Domain, User, SecurePassword))
{
System.Diagnostics.Debug.WriteLine($"Before Impersonation: {Environment.UserName}");
done = impersonate.RunImpersonated(() =>
{
System.Diagnostics.Debug.WriteLine($"After Impersonation: {Environment.UserName}");
//if (Util.IsWindowsEncrypted(ConfigFullPath)) File.Decrypt(ConfigFullPath);
var json = File.ReadAllText(ConfigFullPath);
var configFile_data = SerializationUtil.Deserialize<Config>(json);
//if (!Util.IsWindowsEncrypted(ConfigFullPath)) File.Encrypt(ConfigFullPath);
});
}
A working repository to reproduce is available here Repository containing full sample,
See CreateConfig or ReadConfig method in Config.cs
(if you were able to create and read a encrypted file, check if after
restart it is the same)
A Workaround i just found was to create a Process before doing any impersonation.
using (Process cmd = new Process())
{
ProcessStartInfo startInfo = new ProcessStartInfo
{
FileName = "cmd.exe",
Arguments = $"/c",
UseShellExecute = false,
UserName = user,
Password = pass,
LoadUserProfile = true
};
cmd.StartInfo = startInfo;
cmd.Start();
}
I have been having trouble passing input programmatically to the system net user program using C#. I am trying to activate and set a password for a certain user account. By my investigations, it seems like the process finishes before anything can be passed. I do not know why the background net user program does not wait for input before exiting.
Here is the command I am running programmatically to accomplish this:
net user username /active:yes & net user username *
The output of the second command is as follows:
Type a password for the user:
Retype the password to confirm:
The command completed successfully
If you were to run the above command manually, it would ask you for the password and hide what you're typing from the screen. However, the program doesn't seem to stop when ran programmatically.
To call the program, I have a function that starts the program and returns the process to another function, which sends input to the process. Here is the first function:
static Process RunCommandGetProcess(string command)
{
Process process = new Process();
ProcessStartInfo psInfo = new ProcessStartInfo();
psInfo.FileName = "CMD.exe";
psInfo.Arguments = "/C " + command + "& PAUSE";
// Allow for Input redirection
psInfo.UseShellExecute = false;
psInfo.RedirectStandardInput = true;
// Window style
psInfo.WindowStyle = ProcessWindowStyle.Normal;
// Start the mothertrucker!
process.StartInfo = psInfo;
process.Start();
return process;
}
And the Calling function:
static int ActivateUserWithPassword(string password)
{
// Start net user with that other function
Process process = RunCommandGetProcess("net user username /active:yes & net user username *");
StreamWriter streamWriter = process.StandardInput;
streamWriter.WriteLine(password); // First Prompt
streamWriter.WriteLine(password); // Second Prompt
process.WaitForExit();
return process.ExitCode;
}
However, When I run the debugger, the commands complete successfully before the two streamWriter.WriteLine(password); lines are even met! I have tried Googling, but came to no avail.
You people are my only hope.
Okay! I have been highly motivated to solve your issue as soon as i saw it!. after 2 hours of non-stop debugging, i have a solution for you!.
Issue No .1 : Your application does not have Administrator privileges, that is why the command exits right after you start. Start the .exe with Admin Privileges.
Issue No. 2 : Since the password is masked in the input, Even if i did a streamWriter.WriteLine(password) once,The output is "Command Executed Successfully". There is no way of knowing if the password was actually passed or it took empty string as the password.
Solution
You can use the net user command with a parameter for password like this
net user user_name password. No need to prompt the user for password i.e Don't use '*' after username ,since you are passing it through the program.
So this is how it works
Process proc = new Process();
ProcessStartInfo start = new ProcessStartInfo();
start.FileName = "cmd";
start.Arguments = "/k";
start.RedirectStandardInput = true;
start.WorkingDirectory = Environment.CurrentDirectory;
start.UseShellExecute = false;
proc.StartInfo = start;
proc.Start();
proc.StandardInput.WriteLine("net user \"username\" password");
Very Very Important!!
Start the exe as an administrator OR you will have to do this
start.UseShellExecute = true;
start.Verb = "runas";
But then you wont be able to redirect the Output/Input streams!.
I am writing a C# application that uses WinForms.
I want to launch my own shell as an Admin then use Stream Writer to run some commands.
I am not doing any thing malicious. It's to fix the connection issues with another internal application (for work). The other team isn't willing to fix their program so they provided us with a command line fix. However I have to run this on many PC's so I am learning C# and trying to build something that I would use.
As soon as myProcess.Start executes, a black box appears then quickly disappears before going to the next line.
public void ProcessStartAsAdmin(string command)
{
string cname = Environment.UserDomainName + "\\" + Environment.UserName;
Process myProcess = new Process();
myProcess.StartInfo.FileName = #"C:\Windows\System32\CMD.exe";
myProcess.StartInfo.Verb = #"runas";
//myProcess.StartInfo.Arguments = command;
myProcess.StartInfo.UseShellExecute = false;
myProcess.StartInfo.RedirectStandardInput = true;
myProcess.StartInfo.RedirectStandardError = true;
myProcess.StartInfo.RedirectStandardOutput = true;
myProcess.Start();
StreamWriter myStreamWriter = myProcess.StandardInput;
String inputText;
string resetConnection = "C:\\APP\\application.exe /reset /server:example.com /uid:" + cname + " /pwd:" + textBoxPassword.Text.Trim();
myStreamWriter.WriteLine(resetConnection);
myStreamWriter.Close();
myProcess.WaitForExit();
myProcess.Close();
}
If I am doing anything wrong, please let me know. If I can use a better technique, I'm open for suggestions.
Thanks.
I don't know whether this is the only issue, but there's no space between these two arguments:
/uid:" + cname + "/pwd:"
it should be:
/uid:" + cname + " /pwd:"
string resetConnection = "C:\\APP\application.exe
\a is illegal. You should write \\ (it seems you know it, and missed).
Edit:
The screen that your see is empty, because of you chose to redirect output.
What is probably happening is that the line has some error (or it's running time is very small), and you not see that.
Cancel temporary the output redirection, and the process closing, and see what happened.
Let's take a look at how you initialize the start info:
myProcess.StartInfo.Verb = #"runas";
myProcess.StartInfo.UseShellExecute = false;
myProcess.StartInfo.RedirectStandardInput = true;
myProcess.StartInfo.RedirectStandardError = true;
myProcess.StartInfo.RedirectStandardOutput = true;
You are redirecting the standard input, output and error streams. The documentation for this says:
You must set UseShellExecute to false if you want to set RedirectStandardOutput to true. Otherwise, reading from the StandardOutput stream throws an exception.
And likewise for the other two standard streams. So, redirection implies that you must set UseShellExecute to false.
On the other hand, you wish the new process to be elevated. Which means that you want to use the runas verb.
The documentation does not say so, but the Verb property only has impact when UseShellExecute is set to true. So, requesting elevation implies that you must set UseShellExecute to true. There is no other way to request elevation for a new process. You have to pass through the ShellExecuteEx (or its relatives) with the runas verb and that forces you to set UseShellExecute to true.
So there are two conflicting requirements here. You've got no option at all over elevation I presume. You must elevate for this task. Which means you'll have to give up on redirection. I suppose the only other option would be for you to run the calling process elevated. That would mean that any child processes would also be elevated and there would be no need for runas and so you could set UseShellExecute to false.
However, I see no reason for redirection. You are only doing that, I think, so that you can send commands to the cmd process. Instead you can pass the commands as arguments, or place them in an external .bat or .cmd file. So if I were you I would stop redirecting, and set UseShellExecute to true, and take it from there.
I have to run a tool from our internal website. There are few inputs that user will provide from UI and then I want to run the tool with user credential. My code works fine when I run it from my local machine, but when I deploy it to production server and try to execute it from there it does not start anything.
Here is my code, any advice will be greatly appreciated.
System.Diagnostics.ProcessStartInfo PSI = new System.Diagnostics.ProcessStartInfo(#"D:\run.bat");
PSI.UserName = "username";
PSI.Domain = "domain";
System.Security.SecureString securePwd = new System.Security.SecureString();
string password = "password";
foreach (char c in password)
{
// Append the character to the password.
securePwd.AppendChar(c);
}
PSI.Password = securePwd;
PSI.UseShellExecute = false;
process.StartInfo = PSI;
PSI.Arguments = SiteId.Text + " " + WebServiceUrl.Text + " " +LogFile.Text;
process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
process.Start();
First:
Check the identity of your application pool (advanced settings).
Switch the identity to "system" and see if the batch file runs from the web app.
If it does:
Change the identity back to network service,
Make sure your batch file has execute permissions applied for user Network Service (or whichever identity you chose)
If it doesn't:
Try opening the file with your code and appending some harmless text to the end.
If that works, you can at least rule out permissions and web-app view-ability concerns.
I want to close this thread as my problem of running an application from production server solved. But I'm facing issue to run that application using specific user name & password. It always run with whatever identity is set in apppool. So for now if I only use this line it works on app pool default identity
Process.Start("notepad");
But if I use this line of code it don't even start a notepad in that server
string password = "XXXXX";
System.Security.SecureString securePwd = new System.Security.SecureString();
foreach (char c in password)
{
// Append the character to the password.
securePwd.AppendChar(c);
}
Process.Start("notepad", "username", securePwd, "domain");
I'm going to ask a separate question on this. Thanks for all who replied.
I am running one batch file every few seconds to do timesync with server using following code:
Process process = new Process();
process.StartInfo.WorkingDirectory = Environment.GetFolderPath(Environment.SpecialFolder.System);
process.StartInfo.FileName = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.System), "cmd.exe");
process.StartInfo.Arguments = #"/C C:\TimeSync.bat";
process.StartInfo.CreateNoWindow = true;
process.StartInfo.UseShellExecute = false;
process.StartInfo.WindowStyle = ProcessWindowStyle.Minimized;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.UserName = "username";
SecureString pwd = new SecureString();
Char[] pwdCharacters = "password".ToCharArray();
foreach (char t in pwdCharacters)
{
pwd.AppendChar(t);
}
process.StartInfo.Password = pwd;
process.Start();
string output = process.StandardOutput.ReadToEnd();
The problem is it flashes the command windows on the screen which I don't want. How can I prevent that?
One behavior I have seen is if I run the same code with UseShellExecute = true and don't provide username and password then the command window doesn't flash.
So basically I want to run .bat file using c# code as different user silently.
Because you are passing a user name and password, the CreateNoWindow parameters are not respected. This is a feature (i.e. bug) in windows. Here's the five year old connect details:
http://connect.microsoft.com/VisualStudio/feedback/details/98476/cmd-windows-shows-using-process-with-createnowindow-when-using-username-password-option
Process.Start() calls advapi32.dll's
CreateProcessWithLogonW in the event
that a user supplies a username and
password, and CreateProcessWithLogonW
always opens a new window.
Unfortunately, there is no workaround
for this behavior
An excellent overview of the create no window option is given here:
http://blogs.msdn.com/b/jmstall/archive/2006/09/28/createnowindow.aspx
which also points out errors in the msdn documentation on this topic.
And there's a very good overview in this stackoverflow answer:
How to hide cmd window while running a batch file?
In the end, I think you want to create a separate little app that you call out to only once, and it runs the whole time, as the escalated user. It can then perform the time sync as often as you want, in the same manner as you've described above, but without having to specify username and password. Thus there will only be one 'flash' of a console window for the entire duration of the application.
Hope this helps
lb
Change your line:
process.StartInfo.WindowStyle = ProcessWindowStyle.Minimized;
to
process.StartInfo.WindowStyle =
ProcessWindowStyle.Hidden;
That will hide the window and never show it.
Hope that helps!
Phil
Did you try to just specify:
process.StartInfo.CreateNoWindow=true;
?
You could use impersonation. I've written an impersonation class that implements the IDisposable interface and is rather straightforward to use, I hope.