Running remote exe on remote device - c#

I'm stuck with an issue that may not be easily solvable, but I'm hoping someone will be able to help. I am attempting to run an exe to install some software (SCCM client) on a remote device.
So what is happening is that I am running the program from my PC (Host1) to connect to a remote device (Host2) and instruct that device to run an exe from a server.
I have been using standard remote execution WMI code with no luck:
ConnectionOptions connOptions = new ConnectionOptions();
connOptions.Impersonation = ImpersonationLevel.Impersonate;
connOptions.EnablePrivileges = true;
ManagementScope manScope = new ManagementScope(String.Format(#"\\{0}\ROOT\CIMV2", Host2), connOptions);
manScope.Connect();
if(manScope.IsConnected)
{
ObjectGetOptions objectGetOptions = new ObjectGetOptions();
ManagementPath managementPath = new ManagementPath("Win32_Process");
ManagementClass processClass = new ManagementClass(manScope, managementPath, objectGetOptions);
ManagementBaseObject inParams = processClass.GetMethodParameters("Create");
inParams["CommandLine"] = #sCommand;
ManagementBaseObject outParams = processClass.InvokeMethod("Create", inParams, null);
}
else
{
MessageBox.Show("An error occurerd while attempting to connect to WMI.");
}
The problem I'm running into is executing sCommand when the exe is on a server. So when the parameter is "\\server\share\program.exe" nothing happens. When the parameter is "c:\Folder\program.exe" it works great. These devices we are targeting unfortunately have Admin$ and C$ disabled, and do not have the exe on their hard drive.
I am at a loss unfortunately - is it possible to use the Win32_Process.Create method to run a UNC exe, or is it possible to copy the exe or even the folder it is in to the Host device when Admin$ and C$ are disabled? I am trying to avoid psexec, and I'm honestly wondering if I'd run into the same issue using it anyways.

Can't you deploy a custom Windows Service application on the remote machine? This way you'll just have to communicate with the service using whatever channel you want and run the executable from there.

Related

Access denied when reading / writing to network location as a remote process

I'm currently trying to launch a process on a remote machine using WMI in C#. The process reads and writes to a file that is stored on a separate server.
When I manually login to the remote machine, I can run the process and it all works fine.
However, when I try to launch the process on the remote from my local machine using WMI, I get the following error:
System.UnauthorizedAccessException: Access to the path '\\server\path\input.txt' is denied.
I've tried multiple connection options, but I'm not sure how to re-create the permissions that I seem to have when I login manually... What do I need to do?
Local machine code
static void LaunchRemoteProcess(string remoteMachine, string command)
{
ConnectionOptions connectionOptions = new ConnectionOptions
{
Impersonation = ImpersonationLevel.Impersonate,
EnablePrivileges = true
};
var managementScope = new ManagementScope(string.Format(#"\\{0}\root\cimv2", remoteMachine), connectionOptions);
managementScope.Connect();
var managementPath = new ManagementPath("Win32_Process");
var objectGetOptions = new ObjectGetOptions();
var managementClass = new ManagementClass(managementScope, managementPath, objectGetOptions);
// Launch the command asynchronously
var inParams = managementClass.GetMethodParameters("Create");
inParams["CommandLine"] = command;
var outParams = managementClass.InvokeMethod("Create", inParams, null);
}
Remote machine code
string networkPath = #"\\server\path";
string inputFile = "input.txt";
string inputText = File.ReadAllText(Path.Combine(networkPath, inputFile));
string outputFile = "output.txt";
File.WriteAllText(Path.Combine(networkPath, outputFile), inputText);
Edit 1
I have already tried using the credentials of the user for which the process works if I log on to the remote machine manually and the process still fails with the same error:
ConnectionOptions connectionOptions = new ConnectionOptions
{
Username = "username",
Password = "password",
Authority = "ntlmdomain:COMPANYNAME.CO.UK,
EnablePrivileges = true
};
Am I missing something with regards to the Authority, Authentication, or Impersonation attributes?
Impersonation vs Delegation
Your WMI code uses impersonation, so the server side runs in the security context of the user who calls the code on the client. But this is only valid on the server itself, not for accessing e.g. a remote CIFS share (as in your case).
You have to use delegation.
First, change
Impersonation = ImpersonationLevel.Impersonate,
to
Impersonation = ImpersonationLevel.Delegate,
If you get an exception then, delegation does not yet work in your environment.
Check:
Calling user account: "Account is sensitive and cannot be delegated" must not be checked in the user properties (Active Directory Users and Computers)
server machine account: "Trust this computer for delegation to any service..." must be checked
local security policy on the server: "Enable computer and user accounts to be trusted for delegation" must include the calling user.
See
https://msdn.microsoft.com/en-us/library/aa389288%28VS.85%29.aspx
for further information on this topic.
Added: (see the comments below):
If Delegate is not an option in your environment (e.g. group policies do not allow for this, and you do not have the rights to change them), you may check some alternative ways.
You probably heard of psexec.
Or, what I did some years ago, and which runs in production in a enterprise environment on a few servers for many years very successfull:
I created a scheduled task which starts a program and set the technical user + password for this task. The task was configured for "run once in year 2200 :-)".
Then I wrote commands in a queue (I used a simple command file) and started the task from a remote machine.
Doing it this way, delegation is not required, since the scheduled task itself logs on as the technical user account ("logon as batch" privs are required).
As the reason states, the user id you are using on your PC does not seem to have access for to another computer's location (though it is a server, it is some other computer).
You may get access for your user id or use Impersonation to use an user id that already has access to the location.
Find more details here: https://msdn.microsoft.com/en-us/library/w070t6ka%28v=vs.110%29.aspx
Edited: Add user name password too. That may help.

How to execute debug view on remote computer using wmi

I am looking to run Debugview on a remote machine (I have the username,password and the ip adress of that machine). I have developed this code
object[] theProcessToRun = { "C:\\Dbgview.exe /t /l C:\\debugview1.log" };
ConnectionOptions theConnection = new ConnectionOptions();
theConnection.Username = "Username";
theConnection.Password = "Password";
ManagementScope theScope = new ManagementScope("\\\\ipaddress\\root\\cimv2", theConnection);
ManagementClass theClass = new ManagementClass(theScope, new ManagementPath("Win32_Process"), new ObjectGetOptions());
theClass.InvokeMethod("Create", theProcessToRun);
This code starts the debugview on the remote computer in background(which can be viewed in taskmanager by selecting processes run by all users option) but the problem is, it does not connect the localhost.
so can anyone suggest me the changes or any other way to achieve what i am doing.
EDIT : GOT THE ANSWER
You need to have administrative rights to logon the remote computer so debug view gets connected automatically (to local host)

ManagementScope.Connect failing with error E_ACCESSDENIED error while being run from Windows Service

I am trying to connect to Windows WMI through service by filling the ManagementScope variables and trying to connect to remote machine. The Connect is succeeding if I am running as windows console, but failing when I am running the same code from windows service.
The code Iam using is as follows:
ManagementScope scope = null;
scope = new ManagementScope("\\\\" + m_sComputerName + "\\root\\cimv2");
if (m_sLoginName != null && m_sPassword != null)
{
scope.Options.Username = m_sLoginName;
scope.Options.Password = m_sPassword;
}
scope.Options.EnablePrivileges = true;
scope.Options.Authentication = AuthenticationLevel.PacketPrivacy;
scope.Options.Impersonation = ImpersonationLevel.Impersonate;
scope.Connect();
I am running the windows service as Local System. The code is being written in C# with .net version 4.0
Any help much appreciated.
Thanks
By default, the LocalSystem account does not have access to network resources - see here. If you need to access network resources from your service, consider running it as a domain account. While it is possible to authorize access to remote resources for LocalSystem, doing so is not recommended. Another option would be to run the service as NT AUTHORITY\NetworkService - see here, in which case the service will be authenticated as the machine account when accessing network resources.

How to verify a remote DSN entry?

I am creating a verification utility that check various parts of an application to ensure they are configured correctly. One of the things I need to check is that the DSN entry in a Web.config file is a functional DSN, and that the DSN is pointing to the correct SQL server and database.
Currently you can run the utility in one of two modes, local or remote. My problem occurs when I am trying to verify a remote systems DSN on my local computer. I know why it doesnt work, I don't have the same DSN on my local computer. So what I need to figure out is how to either retrieve the DSN connection information from a remote machine to which I have administrative access, or to get the remote machine to verify it for me.
Any suggestions? Thanks!
EDIT
So apparenty the DSN info is stored in the registry too!
oRegConn = new ConnectionOptions();
oRegConn.Username = username;
oRegConn.Password = password;
scope = new ManagementScope(#"//" + servername + #"/root/default", oRegConn);
registry = new ManagementClass(scope, new ManagementPath("StdRegProv"), null);
inParams = registry.GetMethodParameters("GetStringValue");
inParams["sSubKeyName"] = "SOFTWARE\\ODBC\\ODBC.INI\\ODBC Data Sources\\" +
dsnName;
inParams["sValueName"] = "Server";
outParams = registry.InvokeMethod("GetStringValue", inParams, null);
server = outParams["sValue"].ToString();
Well I would say that its not really a valid check if you run it on another system (yours). The firewall rules could be different, the driver versions might be off, and a million more things. The first thing I would say is if this is a web application, build in a page that can run these checks, like a status page. If this is an application that you cant do that, you can use WMI to run a remote process and gather the results. Other than that your last option might be to do a self hosting WCF service or a windows service that you connect to for querying info. This isnt too uncommon, and I have done it many times using WMI.
In that case, you may want to investigate PSExec (by Sysinternals). You can run your utility on a remote machine in "local" mode, as long as you have Administrative access to that machine.
Your "DSN Verification" part would have to be a separate executable/command, which can be executed by your main utility using syntax similar to the following:
psexec \\REMOTE-Machine <DSN Verification Utility>.exe
The output can be captured within C# by redirecting stdout/stderr.
Forgive me if I'm mistaken, but wouldn't just opening a connection to remote database server let you know if your connection string/DSN is okay?
So the DSN conn values are stored in the registry as plaintext, which is what I needed.
oRegConn = new ConnectionOptions();
oRegConn.Username = username;
oRegConn.Password = password;
scope = new ManagementScope(#"//" + servername + #"/root/default", oRegConn);
registry = new ManagementClass(scope, new ManagementPath("StdRegProv"), null);
inParams = registry.GetMethodParameters("GetStringValue");
inParams["sSubKeyName"] = "SOFTWARE\\ODBC\\ODBC.INI\\ODBC Data Sources\\" +
dsnName;
inParams["sValueName"] = "Server";
outParams = registry.InvokeMethod("GetStringValue", inParams, null);
server = outParams["sValue"].ToString();

How to run a ClickOnce application (.appref-ms) on a remote computer?

I am trying to launch a ClickOnce application via an .appref-ms shortcut on a remote machine using WMI, but cannot succeed. The below code works fine if I try to run notepad.exe.
ManagementPath pm = new ManagementPath(#"\\server\root\cimv2:Win32_process");
ManagementClass processClass = new ManagementClass(pm);
//Get an input parameters object for this method
ManagementBaseObject inParams = processClass.GetMethodParameters("Create");
//Fill in input parameter values
inParams["CommandLine"] = #"C:\Documents and Settings\Start Menu\Programs\New\New App.appref-ms";
//Execute the method
ManagementBaseObject outParams = processClass.InvokeMethod("Create", inParams, null);
Try launching the .appref-ms shortcut via rundll32:
inParams["CommandLine"] = #"rundll32.exe dfshim.dll,ShOpenVerbShortcut ""C:\New App.appref-ms"";
Alternatively, instead of relying on the shortcut path, you could use the application deployment URL (you can see it by opening the .appref-ms file in a text editor):
inParams["CommandLine"] = #"rundll32.exe dfshim.dll,ShOpenVerbApplication http://github-windows.s3.amazonaws.com/GitHub.application";
Keep in mind that Win32_Process.Create cannot create interactive processes remotely.
What is the error that you are getting.
BTW, there seem to be two problems with the commandline path that you are using.
Start Menu folder IMO is in C:\Documents and Settings\"Usernamehere"\StartMenu.
I am not sure but I think that you can't run a program remotely which lies inside a user's profile. Try installing the program to some other location like your c:\program files and then try to call it.
Apart from that, if you mention the exact error you are getting, then it would be helpful to diagnose the problem.

Categories