397 lines
14 KiB
C#
397 lines
14 KiB
C#
#nullable disable
|
|
using Seatbelt.Commands;
|
|
using Seatbelt.Interop;
|
|
using Seatbelt.Util;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Reflection;
|
|
using Seatbelt.Output.Sinks;
|
|
using System.Management;
|
|
using Microsoft.Win32;
|
|
using System.Diagnostics.Eventing.Reader;
|
|
|
|
namespace Seatbelt
|
|
{
|
|
internal class Runtime
|
|
{
|
|
public List<CommandBase> AllCommands { get; private set; } = new List<CommandBase>();
|
|
public readonly IOutputSink OutputSink;
|
|
private IEnumerable<string> Commands { get; set; }
|
|
private IEnumerable<string> CommandGroups { get; set; }
|
|
public bool FilterResults { get; }
|
|
public string ComputerName { get; } // for remote connections
|
|
private string UserName { get; } // for remote connections
|
|
private string Password { get; } // for remote connections
|
|
private ManagementClass wmiRegProv { get; }
|
|
|
|
public Runtime(IOutputSink outputSink, IEnumerable<string> commands, IEnumerable<string> commandGroups, bool filter)
|
|
: this(outputSink, commands, commandGroups, filter, "", "", "")
|
|
{
|
|
}
|
|
|
|
public Runtime(IOutputSink outputSink, IEnumerable<string> commands, IEnumerable<string> commandGroups, bool filter, string computerName)
|
|
: this(outputSink, commands, commandGroups, filter, computerName, "", "")
|
|
{
|
|
}
|
|
|
|
public Runtime(IOutputSink outputSink, IEnumerable<string> commands, IEnumerable<string> commandGroups, bool filter, string computerName, string userName, string password)
|
|
{
|
|
OutputSink = outputSink;
|
|
Commands = commands;
|
|
CommandGroups = commandGroups;
|
|
FilterResults = filter;
|
|
ComputerName = computerName;
|
|
UserName = userName;
|
|
Password = password;
|
|
|
|
// test a remote connection first if a remote system is specified
|
|
if (!string.IsNullOrEmpty(computerName))
|
|
{
|
|
try
|
|
{
|
|
if (!string.IsNullOrEmpty(userName) && !string.IsNullOrEmpty(password))
|
|
{
|
|
OutputSink.WriteHost($"[*] Running commands remotely against the host '{computerName}' with credentials -> user:{UserName} , password:{Password}\r\n");
|
|
|
|
var options = new ConnectionOptions();
|
|
options.Username = UserName;
|
|
options.Password = Password;
|
|
options.Impersonation = ImpersonationLevel.Impersonate;
|
|
options.EnablePrivileges = true;
|
|
|
|
var scope = new ManagementScope($"\\\\{computerName}\\root\\cimv2", options);
|
|
scope.Connect();
|
|
}
|
|
else
|
|
{
|
|
OutputSink.WriteHost($"[*] Running commands remotely against the host '{computerName}' with current user credentials\r\n");
|
|
|
|
var scope = new ManagementScope($"\\\\{computerName}\\root\\cimv2");
|
|
scope.Connect();
|
|
}
|
|
InitializeCommands();
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
OutputSink.WriteError($"Error connecting to \"{computerName}\" : {e.Message}");
|
|
throw e;
|
|
}
|
|
|
|
wmiRegProv = WMIUtil.WMIRegConnection(computerName, userName, password);
|
|
}
|
|
else
|
|
{
|
|
InitializeCommands();
|
|
}
|
|
}
|
|
|
|
public ManagementObjectSearcher GetManagementObjectSearcher(string nameSpace, string query)
|
|
{
|
|
|
|
// helper that takes the current configuration for a remote management and builds the proper ManagementObjectSearcher object
|
|
// used for WMI searching
|
|
|
|
if (string.IsNullOrEmpty(ComputerName))
|
|
return new ManagementObjectSearcher(nameSpace, query);
|
|
|
|
try
|
|
{
|
|
if (!string.IsNullOrEmpty(UserName) && !string.IsNullOrEmpty(Password))
|
|
{
|
|
var options = new ConnectionOptions
|
|
{
|
|
Username = UserName,
|
|
Password = Password,
|
|
Impersonation = ImpersonationLevel.Impersonate,
|
|
EnablePrivileges = true
|
|
};
|
|
|
|
var scope = new ManagementScope($"\\\\{ComputerName}\\{nameSpace}", options);
|
|
scope.Connect();
|
|
var queryObj = new ObjectQuery(query);
|
|
return new ManagementObjectSearcher(scope, queryObj);
|
|
}
|
|
else
|
|
{
|
|
var scope = new ManagementScope($"\\\\{ComputerName}\\{nameSpace}");
|
|
scope.Connect();
|
|
var queryObj = new ObjectQuery(query);
|
|
return new ManagementObjectSearcher(scope, queryObj);
|
|
}
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
throw new Exception($"Error connecting to \"{ComputerName}\" : {e.Message}");
|
|
}
|
|
}
|
|
|
|
public string[]? GetSubkeyNames(RegistryHive hive, string path)
|
|
{
|
|
if (!string.IsNullOrEmpty(ComputerName))
|
|
{
|
|
return RegistryUtil.GetSubkeyNames(hive, path, wmiRegProv);
|
|
}
|
|
|
|
return RegistryUtil.GetSubkeyNames(hive, path);
|
|
}
|
|
|
|
public string? GetStringValue(RegistryHive hive, string path, string value)
|
|
{
|
|
if (!string.IsNullOrEmpty(ComputerName))
|
|
{
|
|
return RegistryUtil.GetStringValue(hive, path, value, wmiRegProv);
|
|
}
|
|
|
|
return RegistryUtil.GetStringValue(hive, path, value);
|
|
}
|
|
|
|
public uint? GetDwordValue(RegistryHive hive, string path, string value)
|
|
{
|
|
if (!string.IsNullOrEmpty(ComputerName))
|
|
{
|
|
return RegistryUtil.GetDwordValue(hive, path, value, wmiRegProv);
|
|
}
|
|
|
|
return RegistryUtil.GetDwordValue(hive, path, value);
|
|
}
|
|
|
|
|
|
public byte[]? GetBinaryValue(RegistryHive hive, string path, string value)
|
|
{
|
|
if (!string.IsNullOrEmpty(ComputerName))
|
|
{
|
|
return RegistryUtil.GetBinaryValue(hive, path, value, wmiRegProv);
|
|
}
|
|
|
|
return RegistryUtil.GetBinaryValue(hive, path, value);
|
|
}
|
|
|
|
public Dictionary<string, object> GetValues(RegistryHive hive, string path)
|
|
{
|
|
if (!string.IsNullOrEmpty(ComputerName))
|
|
{
|
|
return RegistryUtil.GetValues(hive, path, wmiRegProv);
|
|
}
|
|
|
|
return RegistryUtil.GetValues(hive, path);
|
|
}
|
|
|
|
public string[] GetUserSIDs()
|
|
{
|
|
if (!string.IsNullOrEmpty(ComputerName))
|
|
{
|
|
return RegistryUtil.GetUserSIDs(wmiRegProv);
|
|
}
|
|
|
|
return RegistryUtil.GetUserSIDs();
|
|
}
|
|
|
|
public string[] GetDirectories(string relPath)
|
|
{
|
|
relPath = relPath.Trim('\\');
|
|
|
|
if (!string.IsNullOrEmpty(ComputerName))
|
|
{
|
|
return System.IO.Directory.GetDirectories($"\\\\{ComputerName}\\C$\\{relPath}\\");
|
|
}
|
|
else
|
|
{
|
|
return System.IO.Directory.GetDirectories($"{Environment.GetEnvironmentVariable("SystemDrive")}\\{relPath}\\");
|
|
}
|
|
}
|
|
public EventLogReader GetEventLogReader(string path, string query)
|
|
{
|
|
// TODO: investigate https://docs.microsoft.com/en-us/previous-versions/windows/desktop/eventlogprov/win32-ntlogevent
|
|
|
|
var eventsQuery = new EventLogQuery(path, PathType.LogName, query) { ReverseDirection = true };
|
|
|
|
if (!string.IsNullOrEmpty(ComputerName))
|
|
{
|
|
//EventLogSession session = new EventLogSession(
|
|
// ComputerName,
|
|
// "Domain", // Domain
|
|
// "Username", // Username
|
|
// pw,
|
|
// SessionAuthentication.Default); // TODO password specification! https://docs.microsoft.com/en-us/dotnet/api/system.diagnostics.eventing.reader.eventlogsession.-ctor?view=dotnet-plat-ext-3.1#System_Diagnostics_Eventing_Reader_EventLogSession__ctor_System_String_System_String_System_String_System_Security_SecureString_System_Diagnostics_Eventing_Reader_SessionAuthentication_
|
|
|
|
var session = new EventLogSession(ComputerName);
|
|
eventsQuery.Session = session;
|
|
}
|
|
|
|
var logReader = new EventLogReader(eventsQuery);
|
|
return logReader;
|
|
}
|
|
|
|
public string GetEnvironmentVariable(string variableName)
|
|
{
|
|
if (!string.IsNullOrEmpty(ComputerName))
|
|
{
|
|
var result = "";
|
|
|
|
var wmiData = this.GetManagementObjectSearcher(@"root\cimv2", $"SELECT VariableValue from win32_environment WHERE name='{variableName}' AND UserName='<SYSTEM>'");
|
|
|
|
foreach (var wmiResult in wmiData.Get())
|
|
{
|
|
result = wmiResult["VariableValue"].ToString();
|
|
}
|
|
|
|
return result;
|
|
}
|
|
else
|
|
{
|
|
return Environment.GetEnvironmentVariable(variableName);
|
|
}
|
|
}
|
|
|
|
|
|
public bool ISRemote()
|
|
{
|
|
return !string.IsNullOrEmpty(ComputerName);
|
|
}
|
|
|
|
private void InitializeCommands()
|
|
{
|
|
foreach (var type in Assembly.GetExecutingAssembly().GetTypes())
|
|
{
|
|
if (!type.IsSubclassOf(typeof(CommandBase)) || type.IsAbstract)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
var instance = (CommandBase)Activator.CreateInstance(type, new object[] { this });
|
|
|
|
#if DEBUG
|
|
if (instance.Command != "TEMPLATE")
|
|
{
|
|
AllCommands.Add(instance);
|
|
}
|
|
#else
|
|
AllCommands.Add(instance);
|
|
#endif
|
|
}
|
|
|
|
AllCommands = AllCommands.OrderBy(c => c.Command).ToList();
|
|
}
|
|
|
|
|
|
public void Execute()
|
|
{
|
|
foreach (var group in CommandGroups)
|
|
{
|
|
if (!ProcessGroup(group))
|
|
{
|
|
OutputSink.WriteError($"Invalid command group \"{group}\"");
|
|
}
|
|
}
|
|
|
|
foreach (var command in Commands)
|
|
{
|
|
try
|
|
{
|
|
if (!ProcessCommand(command))
|
|
{
|
|
OutputSink.WriteError($"Error running command \"{command}\"");
|
|
}
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
OutputSink.WriteError($"Error running {command}: {e}");
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
private bool ProcessGroup(string command)
|
|
{
|
|
var commandGroupStrings = Enum.GetNames(typeof(CommandGroup)).ToList().Select(g=> g.ToLower());
|
|
|
|
if (!commandGroupStrings.Contains(command.ToLower()))
|
|
return false;
|
|
|
|
List<CommandBase> toExecute;
|
|
|
|
switch (command.ToLower())
|
|
{
|
|
case "all":
|
|
toExecute = AllCommands.ToList();
|
|
break;
|
|
|
|
default:
|
|
CommandGroup commandGroup;
|
|
try
|
|
{
|
|
var groupName = Enum.GetNames(typeof(CommandGroup)).FirstOrDefault(c => c.ToLower() == command.ToLower());
|
|
commandGroup = (CommandGroup)Enum.Parse(typeof(CommandGroup), groupName);
|
|
}
|
|
catch (ArgumentException)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
toExecute = AllCommands.Where(g => g.Group.Contains(commandGroup)).ToList();
|
|
break;
|
|
}
|
|
|
|
toExecute.ForEach(c =>
|
|
{
|
|
ExecuteCommand(c, new string[] { });
|
|
});
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
private bool ProcessCommand(string commandLine)
|
|
{
|
|
var args = Shell32.CommandLineToArgs(commandLine);
|
|
|
|
var commandName = args[0];
|
|
var command = AllCommands.FirstOrDefault(c => c.Command.Equals(commandName, StringComparison.InvariantCultureIgnoreCase));
|
|
|
|
if (command == null)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
var commandArgs = new string[] { };
|
|
if (args.Length > 1)
|
|
{
|
|
commandArgs = args.SubArray(1, args.Length - 1);
|
|
}
|
|
|
|
ExecuteCommand(command, commandArgs);
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
private void ExecuteCommand(CommandBase? command, string[] commandArgs)
|
|
{
|
|
try
|
|
{
|
|
OutputSink.WriteOutput(new HostDTO($"====== {command.Command} ======\n"));
|
|
var results = command.Execute(commandArgs);
|
|
if (results != null)
|
|
{
|
|
// OutputSink.BeginOutput();
|
|
foreach (var result in results)
|
|
{
|
|
// pass the command version from the command module to the DTO
|
|
result.SetCommandVersion(command.CommandVersion);
|
|
|
|
OutputSink.WriteOutput(result);
|
|
}
|
|
// OutputSink.EndOutput();
|
|
}
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
// TODO: Return an error DTO
|
|
OutputSink.WriteError($" [!] Terminating exception running command '{command.Command}': " + e);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#nullable enable |