I created a small application which has only one mission to make. It's to get remote computer's information. It works on release & debugs modes very fine. When I use it inside a ".net core" project the application gives "Access denied" error.
What I found so far.
If the cmd.exe run as SYSTEM, the application gives "Access denied" error, but in user mode, it works.
How can I use WMI in SYSTEM mode? What I do wrong?
ConnectionOptions options = new ConnectionOptions
{
Impersonation = ImpersonationLevel.Impersonate,
Authentication = AuthenticationLevel.PacketIntegrity,
EnablePrivileges = true,
SecurePassword = the_secure_password_of_remote_computer,
Username = the_username_of_remote_computer
};
var scope = new ManagementScope(#"\\" + the_ip_address_of_remote_computer +
#"\root\cimv2", options);
try
{
scope.Connect();
if (scope.IsConnected)
{
return true;
}
else
{
false;
}
} catch(Exception exception)
{
// I got the exception here.
Console.WriteLine("Exception|{0}", exception.Message);
return false;
}
I used Marshalling to solve this.
Here: http://rzander.azurewebsites.net/create-a-process-as-loggedon-user/
Related
I use the Microsoft.Management.Infrastructure namespace to connect to remote computers to get WMI information and it works. But when I try to connect to a non-domain PC it does not work. Can anyone pinpoint what I am doing wrong.
Here is the code:
string computer = "Computer_B";
string domain = "WORKGROUP";
string username = ".\\LocalAdminUserName";
string plaintextpassword;
Console.WriteLine("Enter password:");
plaintextpassword = Console.ReadLine();
SecureString securepassword = new SecureString();
foreach (char c in plaintextpassword)
{
securepassword.AppendChar(c);
}
CimCredential Credentials = new
CimCredential(PasswordAuthenticationMechanism.Default, domain,
username,securepassword);
WSManSessionOptions SessionOptions = new WSManSessionOptions();
SessionOptions.AddDestinationCredentials(Credentials);
CimSession Session = CimSession.Create(computer, SessionOptions);
var allVolumes = Session.QueryInstances(#"root\cimv2", "WQL", "SELECT * FROM Win32_LogicalDisk");
// Loop through all volumes
foreach (CimInstance oneVolume in allVolumes)
{
Console.Writeline(oneVolume.CimInstanceProperties["SystemName"].Value.ToString());
}
I am not sure what to take as paramaters for domain and username for a local computer. I have already done/tryed the following:
run winrm quickconfig on the remote local computer
use PasswordAuthenticationMechanism.Negotiate cause I have read Kerberos only
works for domain users and password
added the computer I run the code on to the TrustedHosts on the local computer with winrm config. Also tryed adding * to the TrustedHosts
Used for username="computer_B\LocalAdminUserName". I have also tryed with domain=""
Any suggestions what I am doing wrong?
The error I keep getting is: WinRM cannot process the request. The following error with error code 0x8009030e occurred while using Negotiate authentication: A specified logon session does not exist. It may already have been terminated.
This can occur if the provided credentials are not valid on the target server, or if the server identity could not be verified. If you trust the server identity, add the server name to the TrustedHosts list, and then retry the request. Use winrm.cmd to view or edit the TrustedHosts list. Note that computers in the TrustedHosts list might not be authenticated. For more information about how to edit the TrustedHosts list, run the following command: winrm help config.
Try out the code below, this is working on impersonation logic.
ConnectionOptions cOption = new ConnectionOptions();
ManagementScope scope = null;
Boolean isLocalConnection = isLocalhost(machine);
if (isLocalConnection)
{
scope = new ManagementScope(nameSpaceRoot + "\\" + managementScope, cOption);
}
else
{
scope = new ManagementScope("\\\\" + machine + "\\" + nameSpaceRoot + "\\" + managementScope, cOption);
}
if (!String.IsNullOrEmpty(ACTIVE_DIRECTORY_USERNAME) && !String.IsNullOrEmpty(ACTIVE_DIRECTORY_PASSWORD) && !isLocalConnection)
{
scope.Options.Username = ACTIVE_DIRECTORY_USERNAME;
scope.Options.Password = ACTIVE_DIRECTORY_PASSWORD;
}
scope.Options.EnablePrivileges = true;
scope.Options.Authentication = AuthenticationLevel.PacketPrivacy;
scope.Options.Impersonation = ImpersonationLevel.Impersonate;
scope.Connect();
I have created an ASP.Net application which impersonates the user in order to create an AD group, and then launches a powershell process as the user (separately from the impersonation).
For some reason the group creation works fine and shows as success in the Event Viewer, but when it tries to run the PowerShell script, I get the following error:
The user has not been granted the requested logon type at this machine.
The following is the code I am using which is failing:
SecureString securePassword = new SecureString();
foreach (char c in model.AdminPassword)
{
securePassword.AppendChar(c);
}
PSCredential psCredential = new PSCredential("CONTOSO\\" + User.Identity.Name, securePassword);
ProcessStartInfo info = new ProcessStartInfo("c:\\Windows\\system32\\WindowsPowerShell\\v1.0\\powershell.exe", "c:\\PowershellScripts\\EnableDL.ps1 -dlName '" + model.Name + "'");
info.UseShellExecute = false;
info.RedirectStandardOutput = true;
info.RedirectStandardError = true;
info.RedirectStandardInput = true;
info.CreateNoWindow = true;
info.Domain = "CONTOSO.COM";
info.UserName = User.Identity.Name;
info.Password = securePassword;
Is there any way to bypass this error? I would rather not fiddle with the security policy on the server ideally, and this application needs to be used by around 30+ users.
I have managed to fix this myself. You need to go to Start->Administrative Tools->Local Security Policy.
Navigate to Local Policies->User Rights Assignment->Allow Log On Locally, and add the usernames of the accounts/groups which require access.
For me this didn't work. I also needed to remove Local User from the "Deny log on through Remote Desktop Services" policy. After that I ran gpupdate /force
I am trying to connect to a computer over the workgroup. My code is below:
ConnectionOptions options = new ConnectionOptions();
options.Impersonation = ImpersonationLevel.Impersonate;
options.Username = "testusername";
options.Password = "testpwd";
ManagementScope scope = new ManagementScope(#"\\19x.16x.x.xx\C$\TestFolder", options);
scope.Connect();
if (scope.IsConnected == true)
{
MessageBox.Show("Connection Succeeded", "Alert");
}
else
{
MessageBox.Show("Connection Failed", "Alert");
}
When I run this, I get the exception : "Invalid parameter"
How to sort this out?
edit:
The error is in this line below:
ManagementScope scope = new ManagementScope(#"\\19x.16x.x.xx\C$\TestFolder", options);
How do we specify the drive? I think the $ is causing the problem
Update
Per user's comments, OP is trying to do something entirely different than the question implies. To copy a file from one location to another programmatically one can use File.Copy:
File.Copy(sourcePath, destinationPath)
Where destinationPath is a valid network path. Over a network, I recommend using a machineName instead of an IP address. Especially over a VPN where the potential for an IP address to change is high.
\\machineName\path\filename.csv
I have been writing an application to get the details of remote machine like OS Name, Logon User Name etc using WMI Classes.
In our network we have machines with Windows XP, Windows Vista Windows 7.
I am able to get the informations for all windows 7 and windows vista machines.
But the problem here is i am not able to get the informations for windows XP machines.
Every time i am getting the following exception
Access is denied. (Exception from HRESULT: 0x80070005 (E_ACCESSDENIED))
I have gone through the net but no help. I have done all the steps mentioned in the following link
http://support.microsoft.com/kb/875605.
But no luck still i am not able to solve the problem.
I have domain username with administrator privilages.
Below is the code that i have used. (C#)
private void GetRemoteComputerInfo(string compName)
{
ObjectGetOptions oc = new ObjectGetOptions();
try {
ConnectionOptions connOptions = new ConnectionOptions();
connOptions.Username = "domain\\domainUserName";
connOptions.Password = "domainUserPass";
connOptions.Authority = "kerberos:domain\\" + compName;
connOptions.Impersonation = ImpersonationLevel.Impersonate;
connOptions.EnablePrivileges = true;
ManagementScope msc;
if (compName == Environment.MachineName)
msc = new ManagementScope("\\\\" + compName + "\\root\\cimv2");
else
msc = new ManagementScope("\\\\" + compName + "\\root\\cimv2", connOptions);
msc.Connect();
ManagementClass mc = new ManagementClass("Win32_ComputerSystem");
mc.Scope = msc;
//collection to store all management objects
ManagementObjectCollection moc = mc.GetInstances();
if (moc.Count != 0) {
foreach (ManagementObject mo in mc.GetInstances()) {
Console.WriteLine(string.Format("\nMachine Make: {0}\nMachine Model: {1} System Type: {2} Host Name: {3} Logon User Name: {4}{5}",
mo["Manufacturer"].ToString(),
mo["Model"].ToString(),
mo["SystemType"].ToString(),
mo["DNSHostName"].ToString(),
mo["UserName"].ToString(),
Environment.NewLine));
}
}
}
catch (Exception e) {
Console.WriteLine("{0}: {1}", e.GetType().Name, e.Message);
}
}
Please help me to solve the problem.
Prasad, in the past i have a similar issue related to the WMI DCOM permission, and was resolved following the instructions of these these two links.
Troubleshooting Error Code 80070005 - Access Denied
Securing a Remote WMI Connection
I am trying to check if a process is running on a remote system. I am using the following code:
string procSearc = "notepad";
string remoteSystem = "remoteSystemName";
Process[] proce = System.Diagnostics.Process.GetProcessesByName(procSearch, remoteSystem);
However, when I try to run the code, I get the following error: "Couldn't connect to remote machine."
I am able to run pslist with the following command:
C:>pslist \remoteSystemName
So I know it is possible to get the information I need, but I need it in the code.
Another possibility would be to integrate pslist into C# and search the list to see if the process is there, but I have not found information on how to do this.
Use the System.ServiceProcess.ServiceController class for a service. You can use Status to check if it's running and the Stop() and Start() to control it.
ServiceController sc = new ServiceController();
sc.MachineName = remoteSystem;
sc.ServiceName = procSearc;
if (sc.Status.Equals(ServiceControllerStatus.Running))
{
sc.Stop();
}
else
{
sc.Start();
}
Below is what I did to get this to work:
First I added a reference to System.ServiceProcess and added: using System.ServiceProcess;
string remoteSystem = "remoteSystemName";
string procSearch = "notepad";
Process[] proc = System.Diagnostics.Process.GetProcessesByName(procSearch, remoteSystem);
if (proc.Length > 0)
{
Console.WriteLine("Able to find: " + proc[0]);
}
else
{
Console.WriteLine("Unable to find: " + procSearch);
}
Does the inner Exception say "Access Denied"?
A similar question may help, it mentions needing to be in the Performance Monitor Users group.
GetProcessesByName() and Windows Server 2003 scheduled task
I figured when running the application by itself I would get an exception window, because exceptions were not handled within my code...
There I saw the reason in the stacktrace: access denied. The reason was that the user running the program that was calling the .NET method to get the process list was not part of the "Performance Monitor Users" group of the remote machine.
After that I got another exception saying that the performance monitoring service was not running on the remote machine. So I started the corresponding service at the remote computer and voila it worked!
This was using a Windows 7 client trying to get the process list of a Windows 2008 Server.
Killing the remote process
I found that the Process.Kill() method does not work when the Process.MachineName has been set, so here is a solution for killing the process remotely, hope it helps others.
The extension method to create the method: KillRemoteProcess
public static class ProcessExtensions
{
public static void KillRemoteProcess(this Process p, string user, string password)
{
new Process
{
StartInfo = new ProcessStartInfo
{
FileName = "TaskKill.exe",
Arguments = string.Format("/pid {0} /s {1} /u {2} /p {3}", p.Id, p.MachineName, user, password),
WindowStyle = ProcessWindowStyle.Hidden,
CreateNoWindow = true
}
}.Start();
}
}
And ofcourse the method to find the processes and consume the KillRemoteProcess
public static void KillProcessesRemote()
{
string targetProcessName = "myProcess"; //Do not put 'process.exe' here just 'process'
string targetMachine = "remotMachine"; //Target machine
string username = "myUser"; //Username
string password = "myPassword"; //Password
Parallel.ForEach<Process>( //Kill all processes found
source: System.Diagnostics.Process.GetProcessesByName(targetProcessName, targetMachine),
body: process => {
process.KillRemoteProcess(username, password);
});
}
You may try impersonate to the user that have access to the remote server.
https://learn.microsoft.com/en-us/dotnet/api/system.security.principal.windowsimpersonationcontext?redirectedfrom=MSDN&view=netframework-4.7.2
After Impersonation, you will no longer hit the error.
Also, you have to make sure there is trust between domain, else impersonation will not work.
LogonUser works only for my domain