C# running batch file on remote machine after PsExec has been started - c#

Basically, I'm trying to run the batch file that was copied on the remote machine, by the way, this is my first attempt at coding, so please be nice but critique it if you want, I'm still learning the language and had to spend 3 hours to get this far, thank god for Google, LOL.
Code:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace WindowsFormsApplication4
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void cleanerBtn_Click(object sender, EventArgs e)
{
//Copying Batch File to Remote Host
string fileToCopy = "C:\\Clean.bat";
string newLocation = hostName.Text;
string newFile = (newLocation + "\\clean.bat");
System.IO.File.Copy(fileToCopy, newLocation);
//Run PsExec
string psExec = "psexec -s "+newLocation+" cmd";
System.Diagnostics.Process.Start("CMD.exe", psExec);
//Run Batch File using PsExec
//Removing Batch File from Remote Host
System.IO.File.Delete(newFile);
}
}
}
Thanks in advance.

From PSExec help:
-c Copy the specified program to the remote system for
execution. If you omit this option the application
must be in the system path on the remote system.
Use that flag to make PSExec copy the batch file that you want executed to the remote system and run it. You don't have to write extra code to do that.
Basically you want to do:
psexec \\server cmd.exe /c file_you_want_to_run.bat

Try running CMD.exe with /C. From cmd.exe help:
/C Carries out the command specified by string and then terminates
The /C needs to be appended to the content of the psExec:
System.Diagnostics.Process.Start("CMD.exe", "/C " + psExec);
I believe you'll get the expected results then. If not, you might want to make sure psexec.exe is in a directory that is in the PATH environment variable.
Also, you might be interested to look into ProcessStartInfo to specify different other parameters that can be set for the execution of a process.

Related

Powershell script in C# for joining windows domain

I am using this function below to create powershell script
public static void joinDomain()
{
string path = #"C:\Windows\Temp\Test.ps1";
if(!File.Exists(path))
{
using (StreamWriter sw = File.CreateText(path))
{
sw.WriteLine("add-computer –domainname ad.contoso.com -Credential AD\adminuser -restart –force");
}
}
}
After successfully script creation I run that script using this below code
Classes.Functions.joinDomain();
string strCmdText = #"C:\Windows\Temp\Test.ps1";
System.Diagnostics.Process.Start("C:\\windows\\system32\\windowspowershell\\v1.0\\powershell.exe ", strCmdText);
If i run script from Powershell ISE it prompts for password so the script works.
Even calling it works but i just got blue powershell commandline and then it disseaper it wont ask for password and i dont know why.
Any ideas would be appreciated?
Found solution:
It was cause by windows restricted policy for unsigned scripts.
Solution here:
https://github.com/eapowertools/ReactivateUsers/wiki/Changing-Execution-Signing-Policy-in-Powershell

File copy started from aspnet webforms and implemented using batch, pstools and robocopy doesn't work

I have 4 independent servers (not in domain):
IIS, SQL1, SQL2, SQL3
I want to copy a database backup from SQL1 to SQL2 or SQL3 (depending on parameters) by button click on webpage hosted on IIS
I wrote a button click method for that, which is calling batch file located in inetpub folder on IIS
Batch is using pstools to run robocopy on SQL1 which should copy required file to destination server (SQL2 or SQL3)
This solution works if I execute batch directly on IIS (cmd as Administrator) or when I debug it on my local machine, but it doesn't if it is called from the running site.
It even doesn't spend any time between the following lines:
batchProcess.Start();
batchProcess.WaitForExit();
Here is my copy method:
private bool ProcessCopy(string file, string destinationIp)
{
SecureString password = ConvertToSecureString("myPassword");
try
{
string batchPath = Server.MapPath(".") + "\\CopyFile.bat";
string cmd = #"c:\Windows\System32\cmd.exe";
ProcessStartInfo processInfo = new ProcessStartInfo
{
FileName = cmd,
UseShellExecute = false
};
Process batchProcess = new Process {StartInfo = processInfo};
batchProcess.StartInfo.Arguments = $"/C {batchPath} {file} {destinationIp}";
batchProcess.StartInfo.Domain = "";
batchProcess.StartInfo.UserName = "Administrator";
batchProcess.StartInfo.Password = password;
batchProcess.StartInfo.RedirectStandardOutput = true;
batchProcess.StartInfo.RedirectStandardError = true;
batchProcess.StartInfo.CreateNoWindow = true;
batchProcess.Start();
batchProcess.WaitForExit();
string response = batchProcess.StandardOutput.ReadToEnd();
response += batchProcess.StandardError.ReadToEnd();
statusStringAppend($"response: {response}");
return true;
}
catch (Exception ex)
{
statusStringAppend($"Failed: {ex.Message}. {ex.StackTrace}");
}
return false;
}
Batch body is:
#echo off
c:\qa\tools\pstools\psexec64.exe -accepteula -u Administrator -p myPassword \\SourceIP robocopy \\SourceIP\qa\db_backup\ \\%2\qa\db_backup\ %1 /is
My questions are:
1. Why the file was not copied?
2. Is there any better way to get it copied?
CODE UPDATED ACCORDING TO SUGGESTIONS BELOW
My guess is that you never executed pstools as the user that your IIS service is running as before, so the EULA dialog is blocking your execution.
If you remember, you always got a window and needed to press the accept button when running any sysinternals tool like pstools the first time.
I guess this should work for you:
c:\qa\tools\pstools\psexec64.exe -accepteula -u Administrator -p myPassword \\SourceIP robocopy \\SourceIP\qa\db_backup\ \\%2\qa\db_backup\ %1 /is
[EDIT]
You would most likely have hit this problem later on, anyway it did not work for you, so i have to list what else could be wrong with your code:
starting a .bat file needs cmd.exe as mother process, you cannot just start a .bat file as process directly. Instead you can for example use another method than ProcessStartInfo that spawns the system default script interpreter automatically: Executing Batch File in C#
the process for executing batch files is "cmd.exe", first parameter "/C", second parameter the batch file you are executing
when executing typical commandline tools, you might consider reading the SDTOUT (standard output) of the process you are executing, like this: Capturing console output from a .NET application (C#)

How to let my program access a file in System32?

I want to make a C# program that deletes a file in system32. The program can delete a file in an normally accessed area such as the desktop but won't find a file in system32, how would I give the program access to system32?
Here's my code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Security.AccessControl;
using System.Security.Principal;
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
string filepath = #"C:\Windows\System32\New.txt";
if (File.Exists(filepath))
{
File.Delete(filepath);
}
else
{
Console.WriteLine("File not found");
Console.ReadLine();
}
}
}
}
To begin with you SHOULD NOT delete files from system 32 folder, these files usually belong to OS and should not be tempered with.
Anyways ! i would not ask why you have this requirement but windows User Account Control (UAC) will not allow you perform this operation just like that, You will need to elevate the permissions and take ownership of the file as shown below :
//take ownership of the file, code assumes file you want to delete is toBeDeleted.txt
ProcessStartInfo processInfo = new ProcessStartInfo("cmd.exe", #"/k takeown /f C:\Windows\System32\toBeDeleted.txt && icacls C:\Windows\System32\toBeDeleted.txt /grant %username%:F");
processInfo.UseShellExecute = true;
processInfo.Verb = "runas";
processInfo.FileName = fileName;//path of your executable
try
{
Process.Start(processInfo);
// a prompt will be presented to user continue with deletion action
// you may want to have some other checks before deletion
File.Delete(#"C:\Windows\System32\toBeDeleted.txt");
return true;
}
catch (Win32Exception)
{
//Do nothing as user cancelled UAC window.
}
When you run this a prompt will be presented to user to confirm this action, if you want to avoid this you'll need to run your entire host process with elevated permissions by Creating and Embedding an Application Manifest (UAC) to require the 'highestAvailable' execution level: this will cause the UAC prompt to appear as soon as your app is started, and cause all child processes to run with elevated permissions without additional prompting.
Hope this helps !

See command line arguments being passed to a program

You may skip this part
I am using a batch file that I have in my thumb drive in order to
mount a true crypt volume. I created that batch file with the help of
this link. on that batch file I have the username and password
that I pass as arguments to trueCrypt.exe in order for it to be
mounted.
Anyways so my question is: will it be possible to see the arguments being passed to a program from a third party process? In other words, will it be possible to see the arguments being passed to this program:
using System;
using System.Reflection;
using System.Diagnostics;
class Program
{
static string password = "";
static void Main(string[] args)
{
if (args.Length > 0)
password = args[0];
// get location where this program resides
var locationOfThisExe = Assembly.GetExecutingAssembly().Location;
Console.Write("Press enter to start a new instance of this program.");
Console.Read();
var randomArgument = new Random().NextDouble().ToString();
Process.Start(locationOfThisExe, randomArgument);
// I am passing a random argument to a new process!
// is it possible to see these arguments from another process?
}
}
Edit
I am creating an edit cause I think I explained my self incorrectly but this edit should be a solution instead of a question
I think this question has not received enough attention. Executing the command showed by https://stackoverflow.com/users/235660/alois-kraus shows:
(I pasted the output on notepad++)
on the image it does not show very clearly but I was able to see the argument being pass to that process. That matters a lot to me because I mount my true crypt volumes with the command:
"C:\Program Files\TrueCrypt\TrueCrypt.exe" /v "a:\volume.tc" /lz /a /p a
that tells to truecrypt that I want to mount the volume located at a:\volume.tc on drive letter z and the password is a
If I execute that command true crypt will mount that volume on drive z:
the problem is that If I then execute the command wmic process note what shoes up:
Note the password is in there!
So in summary it is not safe to pass secure information as an argument. It may be secure if you close the process that received the arguments but I think it is important to be aware of this...
If other users with administrative rights or with the same user account can execute programs you can see all command lines with
wmic process
from all processes with this single command line.

C# redirect standardinput with PGP -ka command

I am having a problem which seems really daft. I must be missing something silly. We have a PGP keyring that is on one of our production servers. The user account it belongs to is not allowed to be logged on as interactively for security. Our problem is we sometimes need to add new keys and can not do this easily. So we thought we could create a quick console app that would be run as its ID and would call the PGP commands via the command line.
The command gets called but it asks for input to confirm what we are doing. Our problem is the "y" we send to standardinput is never displayed and the key is not verified.
here is the code:
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Text.RegularExpressions;
using System.DirectoryServices;
using System.Threading;
namespace TestConsoleApp
{
class RegExValidator
{
private System.Diagnostics.Process myProcess;
public RegExValidator()
{
}
public static void Main(string[] args)
{
RegExValidator myValidator = new RegExValidator();
myValidator.InstallKeys("C:\\Test\\batch.asc", "batch.asc");
}
private void InstallKeys(string keyPath, string keyName)
{
myProcess = new System.Diagnostics.Process();
myProcess.StartInfo.RedirectStandardInput = true;
myProcess.StartInfo.CreateNoWindow = false;
myProcess.StartInfo.UseShellExecute = false;
myProcess.StartInfo.FileName = "pgp";
myProcess.StartInfo.Arguments = "-ka " + keyPath + "";
myProcess.Start();
StreamWriter myInput = myProcess.StandardInput;
myInput.AutoFlush = true;
Thread.Sleep(3000);
myInput.WriteLine("y");
myInput.WriteLine(Environment.NewLine);
}
}
}
This is the output we get on the command line.
C:\Test>TestConsoleApp.exe
Pretty Good Privacy(tm) Version 6.5.2
(c) 1999 Network Associates Inc.
Uses the BSafe(tm) Toolkit, which is copyright RSA Data Security, Inc.
Export of this software may be restricted by the U.S. government.
WARNING: Environmental variable TZ is not defined, so GMT timestamps
may be wrong. See the PGP User's Guide to properly define TZ
Looking for new keys...
DSS 2048/1024 0xDE053A3D 2007/05/29 Batch Interface <batch#netgiro.com>
sig? 0xDE053A3D (Unknown signator, can't be checked)
keyfile contains 1 new keys. Add these keys to keyring ? (Y/n)
C:\Test>
Can anyone help?
Thanks
EDIT
We tried this process but instead of PGP we just moved a file and we got the Y/N box and that worked. It would seem that you may not be able to do it with PGP. No idea why though.
The message
keyfile contains 1 new keys. Add these keys to keyring ? (Y/n)
suggests replying with an Uppercase Y. try changing your call to:
myInput.WriteLine("Y");
(I have no PGP installed for checking, but have encountered other command line interfaces that insisted on case.)
Another thing to try is flushing stream buffers, which clears all buffers for the stream and causes any buffered data to be written to the underlying device:
myInput.WriteLine("Y");
myInput.Flush();

Categories

Resources