killchain-compendium/post exploitation/Seatbelt/Seatbelt/Commands/Windows/RegistryValueCommand.cs

216 lines
7.1 KiB
C#

using Microsoft.Win32;
using Seatbelt.Output.Formatters;
using Seatbelt.Output.TextWriters;
using Seatbelt.Util;
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
namespace Seatbelt.Commands.Windows
{
// TODO: Handle x64 vs x86 hives
internal class RegistryValueCommand : CommandBase
{
public override string Command => "reg";
public override string Description => @"Registry key values (HKLM\Software by default) argument == [Path] [intDepth] [Regex] [boolIgnoreErrors]";
public override CommandGroup[] Group => new[] { CommandGroup.Misc };
public override bool SupportRemote => false; // TODO remote, but will take some work
//private string _rootKey;
//private string _rootParentKey;
public RegistryValueCommand(Runtime runtime) : base(runtime)
{
}
public override IEnumerable<CommandDTOBase?> Execute(string[] args)
{
var hive = RegistryHive.LocalMachine;
var keyPath = "Software";
var depth = 0;
var regex = new Regex(".");
var ignoreErrors = true;
var computer = "";
if (args.Length == 0)
{
depth = 1;
regex = new Regex("default");
}
if (args.Length >= 1)
{
var separatorPos = args[0].IndexOf("\\");
if (separatorPos == -1) // e.g. HKLM
{
hive = RegistryUtil.GetHive(args[0]);
keyPath = "";
}
else if (separatorPos == args[0].Length) // e.g. HKLM\
{
var hiveStr = args[0].Substring(0, separatorPos);
hive = RegistryUtil.GetHive(hiveStr);
keyPath = "";
}
else // e.g. HKLM\Software
{
var hiveStr = args[0].Substring(0, separatorPos);
hive = RegistryUtil.GetHive(hiveStr);
keyPath = args[0].Substring(separatorPos + 1);
}
}
if (args.Length >= 2)
{
if (!int.TryParse(args[1], out depth))
{
WriteError("Could not parse depth argument");
}
}
if (args.Length >= 3) { regex = new Regex(args[2], RegexOptions.IgnoreCase); }
if (args.Length >= 4) { ignoreErrors = bool.Parse(args[3]); }
if (args.Length >= 5) { computer = args[4]; }
foreach (var output in EnumerateRootKey(computer, hive, keyPath, regex, depth, ignoreErrors))
{
yield return output;
}
}
private IEnumerable<RegistryValueDTO> EnumerateRootKey(string computer, RegistryHive hive, string keyPath, Regex regex, int depth, bool ignoreErrors)
{
using var rootHive = RegistryKey.OpenRemoteBaseKey(hive, computer);
using var key = rootHive.OpenSubKey(keyPath);
foreach (var output in EnumerateRegistryKey(key, regex, depth, ignoreErrors))
{
yield return output;
}
}
private IEnumerable<RegistryValueDTO> EnumerateRegistryKey(RegistryKey key, Regex regex, int depth, bool ignoreErrors)
{
if (key == null)
{
throw new Exception("NullRegistryHive");
}
if (depth < 0)
{
yield break;
}
var outputKeyPath = key.ToString()
.Replace("HKEY_LOCAL_MACHINE", "HKLM")
.Replace("HKEY_CURRENT_USER", "HKCU")
.Replace("HKEY_CLASSES_ROOT", "HKCR")
.Replace("HKEY_USERS", "HKU");
// 1) Handle key values
// Get the default value since GetValueNames doesn't always return it
var defaultValue = key.GetValue("");
if (regex.IsMatch("default") || (regex.IsMatch(outputKeyPath) || (defaultValue != null && regex.IsMatch($"{defaultValue}"))))
{
yield return new RegistryValueDTO(
outputKeyPath,
"(default)",
defaultValue,
RegistryValueKind.String
);
}
foreach (var valueName in key.GetValueNames())
{
if (valueName == null || valueName == "")
continue;
var valueKind = key.GetValueKind(valueName);
var value = key.GetValue(valueName);
// Skip the default value and non-matching valueNames
if (regex.IsMatch(valueName) || regex.IsMatch($"{value}"))
{
yield return new RegistryValueDTO(
outputKeyPath,
valueName,
value,
valueKind
);
}
}
// 2) Handle subkeys
foreach (var subkeyName in key.GetSubKeyNames())
{
RegistryKey? subkey = null;
try
{
subkey = key.OpenSubKey(subkeyName);
}
catch (Exception e)
{
if (!ignoreErrors)
{
throw new Exception($"Error accessing {(key + "\\" + subkeyName)}: " + e);
}
}
if (subkey == null)
continue;
foreach (var result in EnumerateRegistryKey(subkey, regex, (depth - 1), ignoreErrors))
{
yield return result;
}
}
}
}
public class RegistryValueDTO : CommandDTOBase
{
public RegistryValueDTO(string key, string valueName, object value, object valueKind)
{
Key = key;
ValueName = valueName;
Value = value;
ValueKind = valueKind;
}
public string Key { get; }
public string ValueName { get; }
public object Value { get; }
public object ValueKind { get; }
}
[CommandOutputType(typeof(RegistryValueDTO))]
internal class RegistryValueTextFormatter : TextFormatterBase
{
public RegistryValueTextFormatter(ITextWriter writer) : base(writer)
{
}
//WriteLine("Registry Values");
public override void FormatResult(CommandBase? command, CommandDTOBase result, bool filterResults)
{
if (result == null)
{
return;
}
var dto = (RegistryValueDTO)result;
if ((int)dto.ValueKind == (int)RegistryValueKind.MultiString)
{
var values = (string[])dto.Value;
WriteLine($"{dto.Key} ! {dto.ValueName} :\n{String.Join("\n", values)}");
}
else
{
WriteLine($"{dto.Key} ! {dto.ValueName} : {dto.Value}");
}
}
}
}