mirror of
https://github.com/peass-ng/PEASS-ng.git
synced 2026-07-30 23:50:27 -07:00
* feat: MITRE ATT&CK integration for LinPEAS and WinPEAS - Add -T T1234,T5678 flag to LinPEAS to filter checks by technique - Add mitre=T1234,T5678 argument to WinPEAS for technique-based filtering - Annotate every check title with MITRE technique ID(s) displayed in grey - Add $_mitre_tag to Generated Global Variables in 0_variables_base.sh - Add check_mitre_filter() shell function with prefix-match support - Add MitreAttackIds property to ISystemCheck interface (C#) - Update MainPrint/GreatPrint in Beaprint.cs to accept optional mitreIds - Tag all 158 LinPEAS check modules with # Mitre: metadata - Tag all 16 WinPEAS check classes with MitreAttackIds property - Update linpeasModule.py to parse # Mitre: metadata field - Update linpeasBaseBuilder.py to emit check_mitre_filter wrappers - Add 3 MITRE argument parsing tests to ArgumentParsingTests.cs * test: add MITRE filter coverage for LinPEAS builder and WinPEAS LinPEAS (test_builder.py): - test_mitre_flag_present_in_getopts: -T: must appear in getopts string - test_mitre_flag_present_in_help_text: -T must appear in built help text - test_mitre_filter_function_present: check_mitre_filter() must be in built script WinPEAS (ArgumentParsingTests.cs): - PassesMitreFilter_EmptyFilter_AllChecksPass: no filter -> all checks run - PassesMitreFilter_ExactMatch_Passes: T1082 filter matches T1082 check - PassesMitreFilter_NoMatch_Fails: T1082 filter rejects T1057 check - PassesMitreFilter_PrefixMatch_Passes: T1552 filter matches T1552.001/T1552.005 - PassesMitreFilter_SubtechniqueDoesNotMatchDifferentBase_Fails: T1548 != T1552.001 * chore: ignore .github/instructions/ and untrack todos.instructions.md * fix: complete and accurate MITRE ATT&CK mappings for LinPEAS and WinPEAS gitignore: - Add .github/instructions/ to .gitignore and untrack todos.instructions.md LinPEAS — corrected mappings: - 29_Interesting_environment_variables.sh: add missing T1552.007,T1082 - 3_USBCreator.sh: T1548 → T1548.003,T1068 (polkit bypass + CVE-class exploit) - 9_Doas.sh: T1548 → T1548.003 (doas is a sudo/sudo-caching equivalent) - 10_Pkexec.sh: T1548 → T1548.003,T1548.004,T1068 per-section specificity - 2_Process_cred_in_memory.sh: T1003,T1055 → T1003.007 (Proc Filesystem, drop wrong T1055) - 11_Superusers.sh: T1087.001,T1548 → T1087.001 (discovery only, no elevation abuse) - 14/15/16 writable files: T1574 → T1574.009,T1574.010 (specific sub-techniques) WinPEAS — corrected mappings: - SystemInfo: class expanded to full technique union; WSUS T1195→T1072,T1068; KrbRelayUp T1558→T1187,T1558; Object Manager T1548→T1068; Named Pipes T1559.001→T1559; Low-priv pipes T1559.001→T1134.001,T1559 - EventsInfo: class expanded with T1078.003,T1552.001,T1059.001,T1082 - UserInfo: class expanded; Token privileges T1134→T1134.001 - ProcessInfo: Leaked Handlers T1134.003→T1134.001 (token impersonation, not make-token) - ServicesInfo: class adds T1574.011,T1068 - ApplicationsInfo: class adds T1010,T1014 - NetworkInfo: class adds T1018,T1090 - ActiveDirectoryInfo: T1484→T1484.001; class adds T1003 - WindowsCreds: class sub-techniques T1552→T1552.001,T1552.002, T1555→T1555.003,T1555.004; SSClient T1059→T1552.001 (wrong technique entirely) - FilesInfo: class expanded with T1552.002,T1552.004,T1552.006,T1564.001,T1574.001, T1059.004,T1114.001,T1218,T1649; Cloud Credentials T1552.005→T1552.001 - SoapClientInfo: T1059,T1071→T1559,T1071.001 (IPC/Web protocol, not scripting) * fix: add missing T1613 and T1562.001 to SystemInfo class-level MitreAttackIds; label AD object enumeration with T1087.002 and T1018 * fix: correct linpeas mitre filter matching logic * fix: MITRE code bugs — pass-through for untagged checks, remove dead OR in section gate - PassesMitreFilter (Checks.cs): when MitreAttackIds is null or empty and a filter is active, return true (pass-through) instead of false. Previously any future ISystemCheck added without MITRE IDs would be silently excluded by an active filter. - linpeasBaseBuilder.py: remove redundant '|| [ -z "$MITRE_FILTER" ]' from the generated section-level gate. check_mitre_filter already returns 0 immediately when MITRE_FILTER is empty, so the OR branch was unreachable and inconsistent with the check-level gate which uses the same function without the extra guard. - ArgumentParsingTests.cs: add PassesMitreFilter_NullMitreAttackIds_PassesThrough and PassesMitreFilter_EmptyMitreAttackIds_PassesThrough regression tests. * fix(mitre): 4 bugs — dead arg parser, wait logic, subprocess forks, cleanup race Checks.cs: max-regex-file-size used string.Equals which requires exact match, so 'max-regex-file-size=500000' could never match and MaxRegexFileSize was stuck at 1000000 forever. Fixed to arg.StartsWith. Checks.cs RunChecks: wait compared loop index i against _systemCheckSelectedKeysHashSet.Count, which is 0 when all checks run (so i < -1 is always false) and semantically wrong when a key subset is selected. Replaced with a pre-count of checks that pass both filters and a running counter. 0_variables_base.sh check_mitre_filter: replaced two $(echo ... | tr ...) subprocess forks per call with pure parameter-expansion while-loops. Zero process forks, POSIX-compliant, ~632 fork()s saved per full filtered run. Declares _mitre_tags_left and _mitre_filters_left in Generated Global Variables. linpeas_builder.py: os.remove of the shared temp file raised FileNotFoundError when multiple sequential builder invocations ran (the second saw the file already deleted by the first). Wrapped in try/except FileNotFoundError. Tests: Added PassesMitreFilter_SubtechniqueFilter_DoesNotMatchParentOnlyTag and MaxRegexFileSize_ArgParsed_Correctly regression tests (16 total). * ci: add manual build-artifacts workflow (winPEAS.exe + linpeas.sh) * fix(linpeas): getopts silent mode — clear error when -T given without argument Switch getopts to silent mode (leading ':') so the shell does not emit its own terse 'No arg for -T option' message. Add explicit :) case that prints ERROR: -T requires an argument (e.g. -T T1082,T1552) and then dumps the help text before exiting 1. Add *) case for unrecognised flags with the same pattern. Behaviour for all valid flags is unchanged. * chore: untrack build-artifacts workflow, add to .gitignore
1530 lines
68 KiB
C#
1530 lines
68 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Management;
|
|
using System.Reflection;
|
|
using System.Runtime.InteropServices;
|
|
using System.Text.RegularExpressions;
|
|
using winPEAS.Helpers;
|
|
using winPEAS.Helpers.AppLocker;
|
|
using winPEAS.Helpers.Extensions;
|
|
using winPEAS.Helpers.Registry;
|
|
using winPEAS.Info.SystemInfo;
|
|
using winPEAS.Info.SystemInfo.AuditPolicies;
|
|
using winPEAS.Info.SystemInfo.DotNet;
|
|
using winPEAS.Info.SystemInfo.GroupPolicy;
|
|
using winPEAS.Info.SystemInfo.NamedPipes;
|
|
using winPEAS.Info.SystemInfo.Ntlm;
|
|
using winPEAS.Info.SystemInfo.PowerShell;
|
|
using winPEAS.Info.SystemInfo.Printers;
|
|
using winPEAS.Info.SystemInfo.SysMon;
|
|
using winPEAS.Info.SystemInfo.WindowsDefender;
|
|
using winPEAS.Native.Enums;
|
|
|
|
namespace winPEAS.Checks
|
|
{
|
|
class SystemInfo : ISystemCheck
|
|
{
|
|
static string badUAC = "No prompting|PromptForNonWindowsBinaries";
|
|
static string goodUAC = "PromptPermitDenyOnSecureDesktop";
|
|
static string badLAPS = "LAPS not installed";
|
|
static Dictionary<string, string> _basicSystemInfo;
|
|
|
|
|
|
private static readonly Dictionary<string, string> _asrGuids = new Dictionary<string, string>
|
|
{
|
|
{ "01443614-cd74-433a-b99e-2ecdc07bfc25" , "Block executable files from running unless they meet a prevalence, age, or trusted list criteria"},
|
|
{ "c1db55ab-c21a-4637-bb3f-a12568109d35" , "Use advanced protection against ransomware"},
|
|
{ "9e6c4e1f-7d60-472f-ba1a-a39ef669e4b2" , "Block credential stealing from the Windows local security authority subsystem (lsass.exe)"},
|
|
{ "d1e49aac-8f56-4280-b9ba-993a6d77406c" , "Block process creations originating from PSExec and WMI commands"},
|
|
{ "b2b3f03d-6a65-4f7b-a9c7-1c7ef74a9ba4" , "Block untrusted and unsigned processes that run from USB"},
|
|
{ "26190899-1602-49e8-8b27-eb1d0a1ce869" , "Block Office communication applications from creating child processes"},
|
|
{ "7674ba52-37eb-4a4f-a9a1-f0f9a1619a2c" , "Block Adobe Reader from creating child processes"},
|
|
{ "e6db77e5-3df2-4cf1-b95a-636979351e5b" , "Block persistence through WMI event subscription"},
|
|
{ "d4f940ab-401b-4efc-aadc-ad5f3c50688a" , "Block all Office applications from creating child processes"},
|
|
{ "5beb7efe-fd9a-4556-801d-275e5ffc04cc" , "Block execution of potentially obfuscated scripts"},
|
|
{ "92e97fa1-2edf-4476-bdd6-9dd0b4dddc7b" , "Block Win32 API calls from Office macro "},
|
|
{ "3b576869-a4ec-4529-8536-b80a7769e899" , "Block Office applications from creating executable content "},
|
|
{ "75668c1f-73b5-4cf0-bb93-3ecf5cb7cc84" , "Block Office applications from injecting code into other processes"},
|
|
{ "d3e037e1-3eb8-44c8-a917-57927947596d" , "Block JavaScript or VBScript from launching downloaded executable content"},
|
|
{ "be9ba2d9-53ea-4cdc-84e5-9b1eeee46550" , "Block executable content from email client and webmail"},
|
|
};
|
|
|
|
public string[] MitreAttackIds { get; } = new[] { "T1082", "T1068", "T1548.002", "T1003.001", "T1003.004", "T1003.005", "T1059.001", "T1552.001", "T1552.002", "T1562.001", "T1562.002", "T1518.001", "T1557.001", "T1558", "T1559", "T1134.001", "T1547.005", "T1484.001", "T1613", "T1654", "T1072", "T1187" };
|
|
|
|
public void PrintInfo(bool isDebug)
|
|
{
|
|
Beaprint.GreatPrint("System Information", "T1082,T1068,T1548.002,T1003.001,T1003.004,T1003.005,T1059.001,T1552.001,T1552.002,T1562.001,T1562.002,T1518.001,T1557.001,T1558,T1559,T1134.001,T1547.005,T1484.001,T1613,T1654,T1072,T1187");
|
|
|
|
new List<Action>
|
|
{
|
|
PrintBasicSystemInfo,
|
|
PrintWindowsVersionVulnerabilities,
|
|
PrintMicrosoftUpdatesCOM,
|
|
PrintSystemLastShutdownTime,
|
|
PrintUserEV,
|
|
PrintSystemEV,
|
|
PrintAuditInfo,
|
|
PrintAuditPoliciesInfo,
|
|
PrintWEFInfo,
|
|
PrintLAPSInfo,
|
|
PrintWdigest,
|
|
PrintLSAProtection,
|
|
PrintCredentialGuard,
|
|
PrintCachedCreds,
|
|
PrintRegistryCreds,
|
|
PrintAVInfo,
|
|
PrintWindowsDefenderInfo,
|
|
PrintUACInfo,
|
|
PrintPSInfo,
|
|
PrintPowerShellSessionSettings,
|
|
PrintTranscriptPS,
|
|
PrintInetInfo,
|
|
PrintDrivesInfo,
|
|
PrintWSUS,
|
|
PrintKrbRelayUp,
|
|
PrintInsideContainer,
|
|
PrintAlwaysInstallElevated,
|
|
PrintObjectManagerRaceAmplification,
|
|
PrintLSAInfo,
|
|
PrintNtlmSettings,
|
|
PrintLocalGroupPolicy,
|
|
PrintPotentialGPOAbuse,
|
|
AppLockerHelper.PrintAppLockerPolicy,
|
|
PrintPrintNightmarePointAndPrint,
|
|
PrintPrintersWMIInfo,
|
|
PrintNamedPipes,
|
|
PrintNamedPipeAbuseCandidates,
|
|
PrintAMSIProviders,
|
|
PrintSysmon,
|
|
PrintDotNetVersions
|
|
}.ForEach(action => CheckRunner.Run(action, isDebug));
|
|
}
|
|
|
|
private static void PrintBasicSystemInfo()
|
|
{
|
|
try
|
|
{
|
|
Beaprint.MainPrint("Basic System Information", "T1082");
|
|
Beaprint.LinkPrint("https://book.hacktricks.wiki/en/windows-hardening/windows-local-privilege-escalation/index.html#version-exploits", "Check if the Windows versions is vulnerable to some known exploit");
|
|
Dictionary<string, string> basicDictSystem = Info.SystemInfo.SystemInfo.GetBasicOSInfo();
|
|
_basicSystemInfo = new Dictionary<string, string>(basicDictSystem);
|
|
basicDictSystem["Hotfixes"] = Beaprint.ansi_color_good + basicDictSystem["Hotfixes"] + Beaprint.NOCOLOR;
|
|
Dictionary<string, string> colorsSI = new Dictionary<string, string>
|
|
{
|
|
{ Globals.StrTrue, Beaprint.ansi_color_bad },
|
|
};
|
|
Beaprint.DictPrint(basicDictSystem, colorsSI, false);
|
|
Console.WriteLine();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Beaprint.PrintException(ex.Message);
|
|
}
|
|
}
|
|
|
|
private static void PrintWindowsVersionVulnerabilities()
|
|
{
|
|
try
|
|
{
|
|
Beaprint.MainPrint("Windows Version Vulnerabilities", "T1082,T1068");
|
|
|
|
var basicInfo = _basicSystemInfo ?? Info.SystemInfo.SystemInfo.GetBasicOSInfo();
|
|
var report = WindowsVersionVulns.GetVulnerabilityReport(basicInfo);
|
|
|
|
if (report.CandidateProducts.Count == 0)
|
|
{
|
|
Beaprint.InfoPrint("Unable to map this OS to WES-NG product definitions.");
|
|
return;
|
|
}
|
|
|
|
Beaprint.InfoPrint("WES-NG product candidates: " + string.Join(" | ", report.CandidateProducts));
|
|
if (!string.IsNullOrEmpty(report.DefinitionsDate))
|
|
{
|
|
Beaprint.InfoPrint("Definitions date: " + report.DefinitionsDate);
|
|
}
|
|
Beaprint.InfoPrint("Installed hotfixes detected: " + report.InstalledHotfixesCount);
|
|
if (report.TotalMatchedBeforeFiltering > 0)
|
|
{
|
|
Beaprint.InfoPrint($"Pre-filter matches: {report.TotalMatchedBeforeFiltering}, filtered by installed/superseded KBs: {report.FilteredByPatches}");
|
|
}
|
|
|
|
if (report.Vulnerabilities.Count == 0)
|
|
{
|
|
Beaprint.GoodPrint("No known exploited vulnerabilities matched this running Windows version.");
|
|
return;
|
|
}
|
|
|
|
Beaprint.BadPrint($"Matched {report.Vulnerabilities.Count} known exploited vulnerabilities for this running Windows version.");
|
|
if (report.MatchedProducts.Count > 0)
|
|
{
|
|
Beaprint.BadPrint("Matched products: " + string.Join(" | ", report.MatchedProducts));
|
|
}
|
|
|
|
int maxToPrint = 20;
|
|
foreach (var vuln in report.Vulnerabilities.Take(maxToPrint))
|
|
{
|
|
string vulnId = string.IsNullOrWhiteSpace(vuln.cve) ? $"KB{vuln.kb}" : vuln.cve;
|
|
string kbInfo = string.IsNullOrWhiteSpace(vuln.kb) ? "" : $" KB{vuln.kb}";
|
|
string severityInfo = string.IsNullOrWhiteSpace(vuln.severity) ? "" : $" [{vuln.severity}]";
|
|
string impactInfo = string.IsNullOrWhiteSpace(vuln.impact) ? "" : $" {vuln.impact}";
|
|
Beaprint.BadPrint($" {vulnId}{kbInfo}{severityInfo}{impactInfo}");
|
|
}
|
|
|
|
if (report.Vulnerabilities.Count > maxToPrint)
|
|
{
|
|
Beaprint.InfoPrint($"Showing {maxToPrint}/{report.Vulnerabilities.Count} results.");
|
|
}
|
|
|
|
Beaprint.InfoPrint("This check applies version matching with installed/superseded KB filtering.");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Beaprint.PrintException(ex.Message);
|
|
}
|
|
}
|
|
|
|
private static void PrintMicrosoftUpdatesCOM()
|
|
{
|
|
try
|
|
{
|
|
Beaprint.MainPrint("Showing All Microsoft Updates", "T1082");
|
|
|
|
var searcher = Type.GetTypeFromProgID("Microsoft.Update.Searcher");
|
|
var searcherObj = Activator.CreateInstance(searcher);
|
|
|
|
// get the total number of updates
|
|
var count = (int)searcherObj.GetType().InvokeMember("GetTotalHistoryCount", BindingFlags.InvokeMethod, null, searcherObj, new object[] { });
|
|
|
|
// get the pointer to the update collection
|
|
var results = searcherObj.GetType().InvokeMember("QueryHistory", BindingFlags.InvokeMethod, null, searcherObj, new object[] { 0, count });
|
|
|
|
for (int i = 0; i < count; ++i)
|
|
{
|
|
// get the actual update item
|
|
var item = searcherObj.GetType().InvokeMember("Item", BindingFlags.GetProperty, null, results, new object[] { i });
|
|
|
|
// get our properties
|
|
// ref - https://docs.microsoft.com/en-us/windows/win32/api/wuapi/nn-wuapi-iupdatehistoryentry
|
|
var title = searcherObj.GetType().InvokeMember("Title", BindingFlags.GetProperty, null, item, new object[] { })?.ToString() ?? string.Empty;
|
|
var date = searcherObj.GetType().InvokeMember("Date", BindingFlags.GetProperty, null, item, new object[] { });
|
|
var description = searcherObj.GetType().InvokeMember("Description", BindingFlags.GetProperty, null, item, new object[] { });
|
|
var clientApplicationID = searcherObj.GetType().InvokeMember("ClientApplicationID", BindingFlags.GetProperty, null, item, new object[] { });
|
|
|
|
string hotfixId = "";
|
|
Regex reg = new Regex(@"KB\d+");
|
|
var matches = reg.Matches(title);
|
|
if (matches.Count > 0)
|
|
{
|
|
hotfixId = matches[0].ToString();
|
|
}
|
|
|
|
Beaprint.NoColorPrint($" HotFix ID : {hotfixId}\n" +
|
|
$" Installed At (UTC) : {Convert.ToDateTime(date.ToString()).ToUniversalTime()}\n" +
|
|
$" Title : {title}\n" +
|
|
$" Client Application ID : {clientApplicationID}\n" +
|
|
$" Description : {description}\n");
|
|
|
|
Beaprint.PrintLineSeparator();
|
|
|
|
Marshal.ReleaseComObject(item);
|
|
}
|
|
|
|
Marshal.ReleaseComObject(results);
|
|
Marshal.ReleaseComObject(searcherObj);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Beaprint.PrintException(ex.Message);
|
|
}
|
|
}
|
|
|
|
static void PrintPSInfo()
|
|
{
|
|
try
|
|
{
|
|
Dictionary<string, string> colorsPSI = new Dictionary<string, string>()
|
|
{
|
|
{ "PS history file: .+", Beaprint.ansi_color_bad },
|
|
{ "PS history size: .+", Beaprint.ansi_color_bad }
|
|
};
|
|
Beaprint.MainPrint("PowerShell Settings", "T1059.001");
|
|
Dictionary<string, string> PSs = Info.SystemInfo.SystemInfo.GetPowerShellSettings();
|
|
Beaprint.DictPrint(PSs, colorsPSI, false);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Beaprint.PrintException(ex.Message);
|
|
}
|
|
}
|
|
|
|
static void PrintTranscriptPS()
|
|
{
|
|
try
|
|
{
|
|
Beaprint.MainPrint("PS default transcripts history", "T1552.001");
|
|
Beaprint.InfoPrint("Read the PS history inside these files (if any)");
|
|
string drive = Path.GetPathRoot(Environment.SystemDirectory);
|
|
string transcriptsPath = drive + @"transcripts\";
|
|
string usersPath = $"{drive}users";
|
|
|
|
var users = Directory.EnumerateDirectories(usersPath, "*", SearchOption.TopDirectoryOnly);
|
|
string powershellTranscriptFilter = "powershell_transcript*";
|
|
|
|
var colors = new Dictionary<string, string>()
|
|
{
|
|
{ "^.*", Beaprint.ansi_color_bad },
|
|
};
|
|
|
|
var results = new List<string>();
|
|
|
|
var dict = new Dictionary<string, string>()
|
|
{
|
|
// check \\transcripts\ folder
|
|
{transcriptsPath, "*"},
|
|
};
|
|
|
|
foreach (var user in users)
|
|
{
|
|
// check the users directories
|
|
dict.Add($"{user}\\Documents", powershellTranscriptFilter);
|
|
}
|
|
|
|
foreach (var kvp in dict)
|
|
{
|
|
var path = kvp.Key;
|
|
var filter = kvp.Value;
|
|
|
|
if (Directory.Exists(path))
|
|
{
|
|
try
|
|
{
|
|
var files = Directory.EnumerateFiles(path, filter, SearchOption.TopDirectoryOnly).ToList();
|
|
|
|
foreach (var file in files)
|
|
{
|
|
var fileInfo = new FileInfo(file);
|
|
var humanReadableSize = MyUtils.ConvertBytesToHumanReadable(fileInfo.Length);
|
|
var item = $"[{humanReadableSize}] - {file}";
|
|
|
|
results.Add(item);
|
|
}
|
|
}
|
|
catch (UnauthorizedAccessException) { }
|
|
catch (PathTooLongException) { }
|
|
catch (DirectoryNotFoundException) { }
|
|
}
|
|
}
|
|
|
|
if (results.Count > 0)
|
|
{
|
|
Beaprint.ListPrint(results, colors);
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Beaprint.PrintException(ex.Message);
|
|
}
|
|
}
|
|
|
|
private static void PrintAuditInfo()
|
|
{
|
|
try
|
|
{
|
|
Beaprint.MainPrint("Audit Settings", "T1562.002");
|
|
Beaprint.LinkPrint("", "Check what is being logged");
|
|
Dictionary<string, string> auditDict = Info.SystemInfo.SystemInfo.GetAuditSettings();
|
|
Beaprint.DictPrint(auditDict, false);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Beaprint.PrintException(ex.Message);
|
|
}
|
|
}
|
|
|
|
private static void PrintAuditPoliciesInfo()
|
|
{
|
|
try
|
|
{
|
|
Beaprint.MainPrint("Audit Policy Settings - Classic & Advanced", "T1562.002");
|
|
|
|
var policies = AuditPolicies.GetAuditPoliciesInfos();
|
|
|
|
foreach (var policy in policies)
|
|
{
|
|
Beaprint.NoColorPrint($" Domain : {policy.Domain}\n" +
|
|
$" GPO : {policy.GPO}\n" +
|
|
$" Type : {policy.Type}\n");
|
|
|
|
foreach (var entry in policy.Settings)
|
|
{
|
|
Beaprint.NoColorPrint($" {entry.Subcategory,50} : {entry.AuditType}");
|
|
}
|
|
|
|
Beaprint.PrintLineSeparator();
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Beaprint.PrintException(ex.Message);
|
|
}
|
|
}
|
|
|
|
static void PrintWEFInfo()
|
|
{
|
|
try
|
|
{
|
|
Beaprint.MainPrint("WEF Settings", "T1562.002");
|
|
Beaprint.LinkPrint("", "Windows Event Forwarding, is interesting to know were are sent the logs");
|
|
Dictionary<string, string> weftDict = Info.SystemInfo.SystemInfo.GetWEFSettings();
|
|
Beaprint.DictPrint(weftDict, false);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Beaprint.PrintException(ex.Message);
|
|
}
|
|
}
|
|
|
|
void PrintLAPSInfo()
|
|
{
|
|
try
|
|
{
|
|
Beaprint.MainPrint("LAPS Settings", "T1003.004");
|
|
Beaprint.LinkPrint("", "If installed, local administrator password is changed frequently and is restricted by ACL");
|
|
Dictionary<string, string> lapsDict = Info.SystemInfo.SystemInfo.GetLapsSettings();
|
|
Dictionary<string, string> colorsSI = new Dictionary<string, string>()
|
|
{
|
|
{ badLAPS, Beaprint.ansi_color_bad }
|
|
};
|
|
Beaprint.DictPrint(lapsDict, colorsSI, false);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Beaprint.PrintException(ex.Message);
|
|
}
|
|
}
|
|
|
|
static void PrintWdigest()
|
|
{
|
|
Beaprint.MainPrint("Wdigest", "T1003.001");
|
|
Beaprint.LinkPrint("https://book.hacktricks.wiki/en/windows-hardening/windows-local-privilege-escalation/index.html#wdigest", "If enabled, plain-text crds could be stored in LSASS");
|
|
string useLogonCredential = RegistryHelper.GetRegValue("HKLM", @"SYSTEM\CurrentControlSet\Control\SecurityProviders\WDigest", "UseLogonCredential");
|
|
if (useLogonCredential == "1")
|
|
Beaprint.BadPrint(" Wdigest is active");
|
|
else
|
|
Beaprint.GoodPrint(" Wdigest is not enabled");
|
|
}
|
|
|
|
static void PrintLSAProtection()
|
|
{
|
|
Beaprint.MainPrint("LSA Protection", "T1003.001");
|
|
Beaprint.LinkPrint("https://book.hacktricks.wiki/en/windows-hardening/windows-local-privilege-escalation/index.html#lsa-protection", "If enabled, a driver is needed to read LSASS memory (If Secure Boot or UEFI, RunAsPPL cannot be disabled by deleting the registry key)");
|
|
string useLogonCredential = RegistryHelper.GetRegValue("HKLM", @"SYSTEM\CurrentControlSet\Control\LSA", "RunAsPPL");
|
|
if (useLogonCredential == "1")
|
|
Beaprint.GoodPrint(" LSA Protection is active");
|
|
else
|
|
Beaprint.BadPrint(" LSA Protection is not enabled");
|
|
}
|
|
|
|
static void PrintCredentialGuard()
|
|
{
|
|
Beaprint.MainPrint("Credentials Guard", "T1003.001");
|
|
Beaprint.LinkPrint("https://book.hacktricks.wiki/windows-hardening/stealing-credentials/credentials-protections#credentials-guard", "If enabled, a driver is needed to read LSASS memory");
|
|
string lsaCfgFlags = RegistryHelper.GetRegValue("HKLM", @"System\CurrentControlSet\Control\LSA", "LsaCfgFlags");
|
|
|
|
if (lsaCfgFlags == "1")
|
|
{
|
|
Console.WriteLine(" Please, note that this only checks the LsaCfgFlags key value. This is not enough to enable Credentials Guard (but it's a strong indicator).");
|
|
Beaprint.GoodPrint(" CredentialGuard is active with UEFI lock");
|
|
}
|
|
else if (lsaCfgFlags == "2")
|
|
{
|
|
Console.WriteLine(" Please, note that this only checks the LsaCfgFlags key value. This is not enough to enable Credentials Guard (but it's a strong indicator).");
|
|
Beaprint.GoodPrint(" CredentialGuard is active without UEFI lock");
|
|
}
|
|
else
|
|
{
|
|
Beaprint.BadPrint(" CredentialGuard is not enabled");
|
|
}
|
|
|
|
CredentialGuard.PrintInfo();
|
|
}
|
|
|
|
static void PrintCachedCreds()
|
|
{
|
|
try{
|
|
Beaprint.MainPrint("Cached Creds", "T1003.005");
|
|
Beaprint.LinkPrint("https://book.hacktricks.wiki/en/windows-hardening/windows-local-privilege-escalation/index.html#cached-credentials", "If > 0, credentials will be cached in the registry and accessible by SYSTEM user");
|
|
string cachedlogonscount = RegistryHelper.GetRegValue("HKLM", @"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon", "CACHEDLOGONSCOUNT");
|
|
if (!string.IsNullOrEmpty(cachedlogonscount))
|
|
{
|
|
int clc = Int16.Parse(cachedlogonscount);
|
|
if (clc > 0)
|
|
{
|
|
Beaprint.BadPrint(" cachedlogonscount is " + cachedlogonscount);
|
|
}
|
|
else
|
|
{
|
|
Beaprint.BadPrint(" cachedlogonscount is " + cachedlogonscount);
|
|
}
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Beaprint.PrintException(ex.Message);
|
|
}
|
|
}
|
|
|
|
static void PrintUserEV()
|
|
{
|
|
try
|
|
{
|
|
Beaprint.MainPrint("User Environment Variables", "T1082");
|
|
Beaprint.LinkPrint("", "Check for some passwords or keys in the env variables");
|
|
Dictionary<string, string> userEnvDict = Info.SystemInfo.SystemInfo.GetUserEnvVariables();
|
|
Dictionary<string, string> colorsSI = new Dictionary<string, string>()
|
|
{
|
|
{ Globals.PrintCredStringsLimited, Beaprint.ansi_color_bad }
|
|
};
|
|
Beaprint.DictPrint(userEnvDict, colorsSI, false);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Beaprint.PrintException(ex.Message);
|
|
}
|
|
}
|
|
|
|
static void PrintSystemEV()
|
|
{
|
|
try
|
|
{
|
|
Beaprint.MainPrint("System Environment Variables", "T1082");
|
|
Beaprint.LinkPrint("", "Check for some passwords or keys in the env variables");
|
|
Dictionary<string, string> sysEnvDict = Info.SystemInfo.SystemInfo.GetSystemEnvVariables();
|
|
Dictionary<string, string> colorsSI = new Dictionary<string, string>()
|
|
{
|
|
{ Globals.PrintCredStringsLimited, Beaprint.ansi_color_bad }
|
|
};
|
|
Beaprint.DictPrint(sysEnvDict, colorsSI, false);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Beaprint.PrintException(ex.Message);
|
|
}
|
|
}
|
|
|
|
static void PrintInetInfo()
|
|
{
|
|
try
|
|
{
|
|
Dictionary<string, string> colorsSI = new Dictionary<string, string>()
|
|
{
|
|
{ "ProxyServer.*", Beaprint.ansi_color_bad }
|
|
};
|
|
|
|
Beaprint.MainPrint("HKCU Internet Settings", "T1082");
|
|
Dictionary<string, string> HKCUDict = Info.SystemInfo.SystemInfo.GetInternetSettings("HKCU");
|
|
Beaprint.DictPrint(HKCUDict, colorsSI, true);
|
|
|
|
Beaprint.MainPrint("HKLM Internet Settings", "T1082");
|
|
Dictionary<string, string> HKMLDict = Info.SystemInfo.SystemInfo.GetInternetSettings("HKLM");
|
|
Beaprint.DictPrint(HKMLDict, colorsSI, true);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Beaprint.PrintException(ex.Message);
|
|
}
|
|
}
|
|
|
|
static void PrintDrivesInfo()
|
|
{
|
|
try
|
|
{
|
|
Beaprint.MainPrint("Drives Information", "T1082");
|
|
Beaprint.LinkPrint("", "Remember that you should search more info inside the other drives");
|
|
Dictionary<string, string> colorsSI = new Dictionary<string, string>()
|
|
{
|
|
{ "Permissions.*", Beaprint.ansi_color_bad}
|
|
};
|
|
|
|
foreach (Dictionary<string, string> drive in Info.SystemInfo.SystemInfo.GetDrivesInfo())
|
|
{
|
|
string drive_permissions = string.Join(", ", PermissionsHelper.GetPermissionsFolder(drive["Name"], Checks.CurrentUserSiDs));
|
|
string dToPrint = string.Format(" {0} (Type: {1})", drive["Name"], drive["Type"]);
|
|
if (!string.IsNullOrEmpty(drive["Volume label"]))
|
|
dToPrint += "(Volume label: " + drive["Volume label"] + ")";
|
|
|
|
if (!string.IsNullOrEmpty(drive["Filesystem"]))
|
|
dToPrint += "(Filesystem: " + drive["Filesystem"] + ")";
|
|
|
|
if (!string.IsNullOrEmpty(drive["Available space"]))
|
|
dToPrint += "(Available space: " + (((Int64.Parse(drive["Available space"]) / 1024) / 1024) / 1024).ToString() + " GB)";
|
|
|
|
if (drive_permissions.Length > 0)
|
|
dToPrint += "(Permissions: " + drive_permissions + ")";
|
|
|
|
Beaprint.AnsiPrint(dToPrint, colorsSI);
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Beaprint.PrintException(ex.Message);
|
|
}
|
|
}
|
|
|
|
static void PrintAVInfo()
|
|
{
|
|
try
|
|
{
|
|
Beaprint.MainPrint("AV Information", "T1518.001");
|
|
Dictionary<string, string> AVInfo = Info.SystemInfo.SystemInfo.GetAVInfo();
|
|
if (AVInfo.ContainsKey("Name") && AVInfo["Name"].Length > 0)
|
|
Beaprint.GoodPrint(" Some AV was detected, search for bypasses");
|
|
else
|
|
Beaprint.BadPrint(" No AV was detected!!");
|
|
|
|
Beaprint.DictPrint(AVInfo, true);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Beaprint.PrintException(ex.Message);
|
|
}
|
|
}
|
|
|
|
static void PrintUACInfo()
|
|
{
|
|
try
|
|
{
|
|
Beaprint.MainPrint("UAC Status", "T1548.002");
|
|
Beaprint.LinkPrint("https://book.hacktricks.wiki/en/windows-hardening/windows-local-privilege-escalation/index.html#from-administrator-medium-to-high-integrity-level--uac-bypasss", "If you are in the Administrators group check how to bypass the UAC");
|
|
Dictionary<string, string> uacDict = Info.SystemInfo.SystemInfo.GetUACSystemPolicies();
|
|
|
|
Dictionary<string, string> colorsSI = new Dictionary<string, string>()
|
|
{
|
|
{ badUAC, Beaprint.ansi_color_bad },
|
|
{ goodUAC, Beaprint.ansi_color_good }
|
|
};
|
|
Beaprint.DictPrint(uacDict, colorsSI, false);
|
|
|
|
if ((uacDict["EnableLUA"] == "") || (uacDict["EnableLUA"] == "0"))
|
|
Beaprint.BadPrint(" [*] EnableLUA != 1, UAC policies disabled.\r\n [+] Any local account can be used for lateral movement.");
|
|
|
|
if ((uacDict["EnableLUA"] == "1") && (uacDict["LocalAccountTokenFilterPolicy"] == "1"))
|
|
Beaprint.BadPrint(" [*] LocalAccountTokenFilterPolicy set to 1.\r\n [+] Any local account can be used for lateral movement.");
|
|
|
|
if ((uacDict["EnableLUA"] == "1") && (uacDict["LocalAccountTokenFilterPolicy"] != "1") && (uacDict["FilterAdministratorToken"] != "1"))
|
|
Beaprint.GoodPrint(" [*] LocalAccountTokenFilterPolicy set to 0 and FilterAdministratorToken != 1.\r\n [-] Only the RID-500 local admin account can be used for lateral movement.");
|
|
|
|
if ((uacDict["EnableLUA"] == "1") && (uacDict["LocalAccountTokenFilterPolicy"] != "1") && (uacDict["FilterAdministratorToken"] == "1"))
|
|
Beaprint.GoodPrint(" [*] LocalAccountTokenFilterPolicy set to 0 and FilterAdministratorToken == 1.\r\n [-] No local accounts can be used for lateral movement.");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Beaprint.PrintException(ex.Message);
|
|
}
|
|
}
|
|
|
|
static void PrintWSUS()
|
|
{
|
|
try
|
|
{
|
|
Beaprint.MainPrint("Checking WSUS", "T1072,T1068");
|
|
Beaprint.LinkPrint("https://book.hacktricks.wiki/en/windows-hardening/windows-local-privilege-escalation/index.html#wsus");
|
|
string policyPath = "Software\\Policies\\Microsoft\\Windows\\WindowsUpdate";
|
|
string policyAUPath = "Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU";
|
|
string wsusPolicyValue = RegistryHelper.GetRegValue("HKLM", policyPath, "WUServer");
|
|
string useWUServerValue = RegistryHelper.GetRegValue("HKLM", policyAUPath, "UseWUServer");
|
|
|
|
if (!string.IsNullOrEmpty(wsusPolicyValue) && wsusPolicyValue.StartsWith("http://", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
Beaprint.BadPrint(" WSUS is using http: " + wsusPolicyValue);
|
|
Beaprint.InfoPrint("You can test https://github.com/pimps/wsuxploit to escalate privileges");
|
|
if (useWUServerValue == "1")
|
|
Beaprint.BadPrint(" And UseWUServer is equals to 1, so it is vulnerable!");
|
|
else if (useWUServerValue == "0")
|
|
Beaprint.GoodPrint(" But UseWUServer is equals to 0, so it is not vulnerable!");
|
|
else
|
|
Console.WriteLine(" But UseWUServer is equals to " + useWUServerValue + ", so it may work or not");
|
|
}
|
|
else
|
|
{
|
|
if (string.IsNullOrEmpty(wsusPolicyValue))
|
|
Beaprint.NotFoundPrint();
|
|
else
|
|
Beaprint.GoodPrint(" WSUS value: " + wsusPolicyValue);
|
|
}
|
|
|
|
if (!string.IsNullOrEmpty(wsusPolicyValue))
|
|
{
|
|
bool clientsForced = useWUServerValue == "1";
|
|
if (clientsForced)
|
|
{
|
|
Beaprint.BadPrint(" CVE-2025-59287: Clients talk to WSUS at " + wsusPolicyValue + " (UseWUServer=1). Unpatched WSUS allows unauthenticated deserialization to SYSTEM.");
|
|
}
|
|
else
|
|
{
|
|
Beaprint.InfoPrint(" CVE-2025-59287: WSUS endpoint discovered at " + wsusPolicyValue + ". Confirm patch level before attempting exploitation.");
|
|
if (!string.IsNullOrEmpty(useWUServerValue))
|
|
Beaprint.InfoPrint(" UseWUServer is set to " + useWUServerValue + ", clients may still reach Microsoft Update.");
|
|
}
|
|
}
|
|
|
|
string wsusSetupPath = @"SOFTWARE\Microsoft\Update Services\Server\Setup";
|
|
string wsusVersion = RegistryHelper.GetRegValue("HKLM", wsusSetupPath, "VersionString");
|
|
string wsusInstallPath = RegistryHelper.GetRegValue("HKLM", wsusSetupPath, "InstallPath");
|
|
bool wsusRoleDetected = !string.IsNullOrEmpty(wsusVersion) || !string.IsNullOrEmpty(wsusInstallPath);
|
|
|
|
if (TryGetServiceStateAndAccount("WSUSService", out string wsusServiceState, out string wsusServiceAccount))
|
|
{
|
|
wsusRoleDetected = true;
|
|
string serviceMsg = " WSUSService status: " + wsusServiceState;
|
|
if (!string.IsNullOrEmpty(wsusServiceAccount))
|
|
serviceMsg += " (runs as " + wsusServiceAccount + ")";
|
|
Beaprint.BadPrint(serviceMsg);
|
|
}
|
|
|
|
if (wsusRoleDetected)
|
|
{
|
|
if (!string.IsNullOrEmpty(wsusVersion))
|
|
Beaprint.BadPrint(" WSUS Server version: " + wsusVersion + " (verify patch level for CVE-2025-59287).");
|
|
if (!string.IsNullOrEmpty(wsusInstallPath))
|
|
Beaprint.InfoPrint(" WSUS install path: " + wsusInstallPath);
|
|
Beaprint.BadPrint(" CVE-2025-59287: Local WSUS server exposes an unauthenticated deserialization surface reachable over HTTP(S). Patch or restrict access.");
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Beaprint.PrintException(ex.Message);
|
|
}
|
|
}
|
|
|
|
private static bool TryGetServiceStateAndAccount(string serviceName, out string state, out string account)
|
|
{
|
|
state = string.Empty;
|
|
account = string.Empty;
|
|
|
|
try
|
|
{
|
|
string query = $"SELECT Name, State, StartName FROM Win32_Service WHERE Name='{serviceName.Replace("'", "''")}'";
|
|
using (var searcher = new ManagementObjectSearcher(@"root\cimv2", query))
|
|
{
|
|
foreach (ManagementObject service in searcher.Get())
|
|
{
|
|
state = service["State"]?.ToString() ?? string.Empty;
|
|
account = service["StartName"]?.ToString() ?? string.Empty;
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Beaprint.PrintException(ex.Message);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static void PrintKrbRelayUp()
|
|
{
|
|
try
|
|
{
|
|
Beaprint.MainPrint("Checking KrbRelayUp", "T1187,T1558");
|
|
Beaprint.LinkPrint("https://book.hacktricks.wiki/en/windows-hardening/windows-local-privilege-escalation/index.html#krbrelayup");
|
|
|
|
if (Checks.CurrentAdDomainName.Length > 0)
|
|
{
|
|
Beaprint.BadPrint(" The system is inside a domain (" + Checks.CurrentAdDomainName + ") so it could be vulnerable.");
|
|
Beaprint.InfoPrint("You can try https://github.com/Dec0ne/KrbRelayUp to escalate privileges");
|
|
}
|
|
else
|
|
{
|
|
Beaprint.GoodPrint(" The system isn't inside a domain, so it isn't vulnerable");
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Beaprint.PrintException(ex.Message);
|
|
}
|
|
}
|
|
|
|
static void PrintInsideContainer()
|
|
{
|
|
try
|
|
{
|
|
Beaprint.MainPrint("Checking If Inside Container", "T1613");
|
|
Beaprint.LinkPrint("", "If the binary cexecsvc.exe or associated service exists, you are inside Docker");
|
|
Dictionary<string, object> regVal = RegistryHelper.GetRegValues("HKLM", @"System\CurrentControlSet\Services\cexecsvc");
|
|
bool cexecsvcExist = File.Exists(Environment.SystemDirectory + @"\cexecsvc.exe");
|
|
if (regVal != null || cexecsvcExist)
|
|
{
|
|
Beaprint.BadPrint("You are inside a container");
|
|
}
|
|
else
|
|
{
|
|
Beaprint.GoodPrint("You are NOT inside a container");
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Beaprint.PrintException(ex.Message);
|
|
}
|
|
}
|
|
|
|
static void PrintAlwaysInstallElevated()
|
|
{
|
|
try
|
|
{
|
|
Beaprint.MainPrint("Checking AlwaysInstallElevated", "T1548.002");
|
|
Beaprint.LinkPrint("https://book.hacktricks.wiki/en/windows-hardening/windows-local-privilege-escalation/index.html#alwaysinstallelevated");
|
|
string path = "Software\\Policies\\Microsoft\\Windows\\Installer";
|
|
string HKLM_AIE = RegistryHelper.GetRegValue("HKLM", path, "AlwaysInstallElevated");
|
|
string HKCU_AIE = RegistryHelper.GetRegValue("HKCU", path, "AlwaysInstallElevated");
|
|
|
|
if (HKLM_AIE == "1")
|
|
{
|
|
Beaprint.BadPrint(" AlwaysInstallElevated set to 1 in HKLM!");
|
|
}
|
|
|
|
if (HKCU_AIE == "1")
|
|
{
|
|
Beaprint.BadPrint(" AlwaysInstallElevated set to 1 in HKCU!");
|
|
}
|
|
|
|
if (HKLM_AIE != "1" && HKCU_AIE != "1")
|
|
{
|
|
Beaprint.GoodPrint(" AlwaysInstallElevated isn't available");
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Beaprint.PrintException(ex.Message);
|
|
}
|
|
}
|
|
|
|
static void PrintObjectManagerRaceAmplification()
|
|
{
|
|
try
|
|
{
|
|
Beaprint.MainPrint("Object Manager race-window amplification primitives", "T1068");
|
|
Beaprint.LinkPrint("https://projectzero.google/2025/12/windows-exploitation-techniques.html", "Project Zero write-up:");
|
|
|
|
if (ObjectManagerHelper.TryCreateSessionEvent(out var objectName, out var error))
|
|
{
|
|
Beaprint.BadPrint($" Created a test named event ({objectName}) under \\BaseNamedObjects.");
|
|
Beaprint.InfoPrint(" -> Low-privileged users can slow NtOpen*/NtCreate* lookups using ~32k-character names or ~16k-level directory chains.");
|
|
Beaprint.InfoPrint(" -> Point attacker-controlled symbolic links to the slow path to stretch kernel race windows.");
|
|
Beaprint.InfoPrint(" -> Use this whenever a bug follows check -> NtOpenX -> privileged action patterns.");
|
|
}
|
|
else
|
|
{
|
|
Beaprint.InfoPrint($" Could not create a test event under \\BaseNamedObjects ({error}). The namespace might be locked down.");
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Beaprint.PrintException(ex.Message);
|
|
}
|
|
}
|
|
|
|
private static void PrintNtlmSettings()
|
|
{
|
|
Beaprint.MainPrint($"Enumerating NTLM Settings", "T1557.001");
|
|
|
|
try
|
|
{
|
|
var info = Ntlm.GetNtlmSettingsInfo();
|
|
|
|
string lmCompatibilityLevelColor = info.LanmanCompatibilityLevel >= 3 ? Beaprint.ansi_color_good : Beaprint.ansi_color_bad;
|
|
Beaprint.ColorPrint($" LanmanCompatibilityLevel : {info.LanmanCompatibilityLevel} ({info.LanmanCompatibilityLevelString})\n", lmCompatibilityLevelColor);
|
|
|
|
var ntlmSettingsColors = new Dictionary<string, string>
|
|
{
|
|
{ "True", Beaprint.ansi_color_good },
|
|
{ "False", Beaprint.ansi_color_bad },
|
|
{ "No signing", Beaprint.ansi_color_bad},
|
|
{ "null", Beaprint.ansi_color_bad},
|
|
{ "Require Signing", Beaprint.ansi_color_good},
|
|
{ "Negotiate signing", Beaprint.ansi_color_yellow},
|
|
{ "Unknown", Beaprint.ansi_color_bad},
|
|
};
|
|
|
|
Beaprint.ColorPrint("\n NTLM Signing Settings", Beaprint.LBLUE);
|
|
Beaprint.AnsiPrint($" ClientRequireSigning : {info.ClientRequireSigning}\n" +
|
|
$" ClientNegotiateSigning : {info.ClientNegotiateSigning}\n" +
|
|
$" ServerRequireSigning : {info.ServerRequireSigning}\n" +
|
|
$" ServerNegotiateSigning : {info.ServerNegotiateSigning}\n" +
|
|
$" LdapSigning : {(info.LdapSigning != null ? info.LdapSigningString : "null")} ({info.LdapSigningString})",
|
|
ntlmSettingsColors);
|
|
|
|
Beaprint.ColorPrint("\n Session Security", Beaprint.LBLUE);
|
|
|
|
if (info.NTLMMinClientSec != null)
|
|
{
|
|
var clientSessionSecurity = (SessionSecurity)info.NTLMMinClientSec;
|
|
var clientSessionSecurityDescription = clientSessionSecurity.GetDescription();
|
|
var color = !clientSessionSecurity.HasFlag(SessionSecurity.NTLMv2) && !clientSessionSecurity.HasFlag(SessionSecurity.Require128BitKey) ?
|
|
Beaprint.ansi_color_bad :
|
|
Beaprint.ansi_color_good;
|
|
Beaprint.ColorPrint($" NTLMMinClientSec : {info.NTLMMinClientSec} ({clientSessionSecurityDescription})", color);
|
|
|
|
if (info.LanmanCompatibilityLevel < 3 && !clientSessionSecurity.HasFlag(SessionSecurity.NTLMv2))
|
|
{
|
|
Beaprint.BadPrint(" [!] NTLM clients support NTLMv1!");
|
|
}
|
|
}
|
|
|
|
if (info.NTLMMinServerSec != null)
|
|
{
|
|
var serverSessionSecurity = (SessionSecurity)info.NTLMMinServerSec;
|
|
var serverSessionSecurityDescription = serverSessionSecurity.GetDescription();
|
|
var color = !serverSessionSecurity.HasFlag(SessionSecurity.NTLMv2) && !serverSessionSecurity.HasFlag(SessionSecurity.Require128BitKey) ?
|
|
Beaprint.ansi_color_bad :
|
|
Beaprint.ansi_color_good;
|
|
Beaprint.ColorPrint($" NTLMMinServerSec : {info.NTLMMinServerSec} ({serverSessionSecurityDescription})\n", color);
|
|
|
|
if (info.LanmanCompatibilityLevel < 3 && !serverSessionSecurity.HasFlag(SessionSecurity.NTLMv2))
|
|
{
|
|
Beaprint.BadPrint(" [!] NTLM services on this machine support NTLMv1!");
|
|
}
|
|
}
|
|
|
|
var ntlmOutboundRestrictionsColor = info.OutboundRestrictions == 2 ? Beaprint.ansi_color_good : Beaprint.ansi_color_bad;
|
|
|
|
Beaprint.ColorPrint("\n NTLM Auditing and Restrictions", Beaprint.LBLUE);
|
|
Beaprint.NoColorPrint($" InboundRestrictions : {info.InboundRestrictions} ({info.InboundRestrictionsString})");
|
|
Beaprint.ColorPrint($" OutboundRestrictions : {info.OutboundRestrictions} ({info.OutboundRestrictionsString})", ntlmOutboundRestrictionsColor);
|
|
Beaprint.NoColorPrint($" InboundAuditing : {info.InboundAuditing} ({info.InboundRestrictionsString})");
|
|
Beaprint.NoColorPrint($" OutboundExceptions : {info.OutboundExceptions}");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Beaprint.PrintException(ex.Message);
|
|
}
|
|
}
|
|
|
|
private static void PrintPrintNightmarePointAndPrint()
|
|
{
|
|
Beaprint.MainPrint("PrintNightmare PointAndPrint Policies", "T1068");
|
|
Beaprint.LinkPrint("https://itm4n.github.io/printnightmare-exploitation/", "Check PointAndPrint policy hardening");
|
|
|
|
try
|
|
{
|
|
string key = @"Software\\Policies\\Microsoft\\Windows NT\\Printers\\PointAndPrint";
|
|
var restrict = RegistryHelper.GetDwordValue("HKLM", key, "RestrictDriverInstallationToAdministrators");
|
|
var noWarn = RegistryHelper.GetDwordValue("HKLM", key, "NoWarningNoElevationOnInstall");
|
|
var updatePrompt = RegistryHelper.GetDwordValue("HKLM", key, "UpdatePromptSettings");
|
|
|
|
if (restrict == null && noWarn == null && updatePrompt == null)
|
|
{
|
|
Beaprint.NotFoundPrint();
|
|
return;
|
|
}
|
|
|
|
Beaprint.NoColorPrint($" RestrictDriverInstallationToAdministrators: {restrict}\n" +
|
|
$" NoWarningNoElevationOnInstall: {noWarn}\n" +
|
|
$" UpdatePromptSettings: {updatePrompt}");
|
|
|
|
if (restrict == 0 && noWarn == 1 && updatePrompt == 2)
|
|
{
|
|
Beaprint.BadPrint(" [!] Potentially vulnerable to PrintNightmare misconfiguration");
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Beaprint.PrintException(ex.Message);
|
|
}
|
|
}
|
|
|
|
private static void PrintPrintersWMIInfo()
|
|
{
|
|
Beaprint.MainPrint("Enumerating Printers (WMI)", "T1082");
|
|
|
|
try
|
|
{
|
|
foreach (var printer in Printers.GetPrinterWMIInfos())
|
|
{
|
|
Beaprint.NoColorPrint($" Name: {printer.Name}\n" +
|
|
$" Status: {printer.Status}\n" +
|
|
$" Sddl: {printer.Sddl}\n" +
|
|
$" Is default: {printer.IsDefault}\n" +
|
|
$" Is network printer: {printer.IsNetworkPrinter}\n");
|
|
Beaprint.PrintLineSeparator();
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
//Beaprint.PrintException(ex.Message);
|
|
}
|
|
}
|
|
|
|
private static void PrintNamedPipes()
|
|
{
|
|
Beaprint.MainPrint("Enumerating Named Pipes", "T1559");
|
|
|
|
try
|
|
{
|
|
string formatString = " {0,-100} {1,-70} {2}\n";
|
|
|
|
Beaprint.NoColorPrint(string.Format($"{formatString}", "Name", "CurrentUserPerms", "Sddl"));
|
|
|
|
foreach (var namedPipe in NamedPipes.GetNamedPipeInfos())
|
|
{
|
|
var colors = new Dictionary<string, string>
|
|
{
|
|
{namedPipe.CurrentUserPerms.Replace("[","\\[").Replace("]","\\]"), Beaprint.ansi_color_bad },
|
|
};
|
|
|
|
Beaprint.AnsiPrint(string.Format(formatString, namedPipe.Name, namedPipe.CurrentUserPerms, namedPipe.Sddl), colors);
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
//Beaprint.PrintException(ex.Message);
|
|
}
|
|
}
|
|
|
|
|
|
private static void PrintNamedPipeAbuseCandidates()
|
|
{
|
|
Beaprint.MainPrint("Named Pipes with Low-Priv Write Access to Privileged Servers", "T1134.001,T1559");
|
|
|
|
try
|
|
{
|
|
var candidates = NamedPipeSecurityAnalyzer.GetNamedPipeAbuseCandidates().ToList();
|
|
|
|
if (!candidates.Any())
|
|
{
|
|
Beaprint.NoColorPrint(" No risky named pipe ACLs were found.\n");
|
|
return;
|
|
}
|
|
|
|
foreach (var candidate in candidates)
|
|
{
|
|
var aclSummary = candidate.LowPrivilegeAces.Any()
|
|
? string.Join("; ", candidate.LowPrivilegeAces.Select(ace =>
|
|
$"{ace.Principal} [{ace.RightsDescription}]").Where(s => !string.IsNullOrEmpty(s)))
|
|
: "Unknown";
|
|
|
|
var serverSummary = candidate.Processes.Any()
|
|
? string.Join("; ", candidate.Processes.Select(proc =>
|
|
$"{proc.ProcessName} (PID {proc.Pid}, {proc.UserName ?? proc.UserSid})"))
|
|
: "No privileged handles observed (service idle or access denied)";
|
|
|
|
var color = candidate.HasPrivilegedServer ? Beaprint.ansi_color_bad : Beaprint.ansi_color_yellow;
|
|
|
|
Beaprint.ColorPrint($" \\\\.\\pipe\\{candidate.Name}", color);
|
|
Beaprint.NoColorPrint($" Low-priv ACLs : {aclSummary}");
|
|
Beaprint.NoColorPrint($" Observed owners: {serverSummary}");
|
|
Beaprint.NoColorPrint($" SDDL : {candidate.Sddl}");
|
|
Beaprint.PrintLineSeparator();
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Beaprint.PrintException(ex.Message);
|
|
}
|
|
}
|
|
|
|
private void PrintAMSIProviders()
|
|
{
|
|
Beaprint.MainPrint("Enumerating AMSI registered providers", "T1562.001");
|
|
|
|
try
|
|
{
|
|
var providers = RegistryHelper.GetRegSubkeys("HKLM", @"SOFTWARE\Microsoft\AMSI\Providers") ?? new string[] { };
|
|
|
|
foreach (var provider in providers)
|
|
{
|
|
var providerPath = RegistryHelper.GetRegValue("HKLM", $"SOFTWARE\\Classes\\CLSID\\{provider}\\InprocServer32", "");
|
|
|
|
Beaprint.NoColorPrint($" Provider: {provider}\n" +
|
|
$" Path: {providerPath}\n");
|
|
|
|
Beaprint.PrintLineSeparator();
|
|
}
|
|
}
|
|
catch (Exception)
|
|
{
|
|
}
|
|
}
|
|
|
|
private void PrintSysmon()
|
|
{
|
|
PrintSysmonConfiguration();
|
|
PrintSysmonEventLogs();
|
|
}
|
|
|
|
private void PrintSysmonConfiguration()
|
|
{
|
|
Beaprint.MainPrint("Enumerating Sysmon configuration", "T1518.001");
|
|
|
|
Dictionary<string, string> colors = new Dictionary<string, string>
|
|
{
|
|
{ SysMon.NotDefined, Beaprint.ansi_color_bad },
|
|
{ "False", Beaprint.ansi_color_bad },
|
|
};
|
|
|
|
try
|
|
{
|
|
if (!MyUtils.IsHighIntegrity())
|
|
{
|
|
Beaprint.NoColorPrint(" You must be an administrator to run this check");
|
|
return;
|
|
}
|
|
|
|
foreach (var item in SysMon.GetSysMonInfos())
|
|
{
|
|
Beaprint.AnsiPrint($" Installed: {item.Installed}\n" +
|
|
$" Hashing Algorithm: {item.HashingAlgorithm.GetDescription()}\n" +
|
|
$" Options: {item.Options.GetDescription()}\n" +
|
|
$" Rules: {item.Rules}\n",
|
|
colors);
|
|
Beaprint.PrintLineSeparator();
|
|
}
|
|
}
|
|
catch (Exception)
|
|
{
|
|
}
|
|
}
|
|
|
|
private void PrintSysmonEventLogs()
|
|
{
|
|
Beaprint.MainPrint("Enumerating Sysmon process creation logs (1)", "T1654");
|
|
|
|
try
|
|
{
|
|
if (!MyUtils.IsHighIntegrity())
|
|
{
|
|
Beaprint.NoColorPrint(" You must be an administrator to run this check");
|
|
return;
|
|
}
|
|
|
|
foreach (var item in SysMon.GetSysMonEventInfos())
|
|
{
|
|
Beaprint.BadPrint($" EventID: {item.EventID}\n" +
|
|
$" User Name: {item.UserName}\n" +
|
|
$" Time Created: {item.TimeCreated}\n");
|
|
Beaprint.PrintLineSeparator();
|
|
}
|
|
|
|
}
|
|
catch (Exception)
|
|
{
|
|
}
|
|
}
|
|
|
|
private static void PrintWindowsDefenderInfo()
|
|
{
|
|
Beaprint.MainPrint("Windows Defender configuration", "T1518.001");
|
|
|
|
void DisplayDefenderSettings(WindowsDefenderSettings settings)
|
|
{
|
|
var pathExclusions = settings.PathExclusions;
|
|
var processExclusions = settings.ProcessExclusions;
|
|
var extensionExclusions = settings.ExtensionExclusions;
|
|
var asrSettings = settings.AsrSettings;
|
|
|
|
if (pathExclusions.Count != 0)
|
|
{
|
|
Beaprint.NoColorPrint("\n Path Exclusions:");
|
|
foreach (var path in pathExclusions)
|
|
{
|
|
Beaprint.NoColorPrint($" {path}");
|
|
}
|
|
}
|
|
|
|
if (pathExclusions.Count != 0)
|
|
{
|
|
Beaprint.NoColorPrint("\n PolicyManagerPathExclusions:");
|
|
foreach (var path in pathExclusions)
|
|
{
|
|
Beaprint.NoColorPrint($" {path}");
|
|
}
|
|
}
|
|
|
|
if (processExclusions.Count != 0)
|
|
{
|
|
Beaprint.NoColorPrint("\n Process Exclusions");
|
|
foreach (var process in processExclusions)
|
|
{
|
|
Beaprint.NoColorPrint($" {process}");
|
|
}
|
|
}
|
|
|
|
if (extensionExclusions.Count != 0)
|
|
{
|
|
Beaprint.NoColorPrint("\n Extension Exclusions");
|
|
foreach (var ext in extensionExclusions)
|
|
{
|
|
Beaprint.NoColorPrint($" {ext}");
|
|
}
|
|
}
|
|
|
|
if (asrSettings.Enabled)
|
|
{
|
|
Beaprint.NoColorPrint("\n Attack Surface Reduction Rules:\n");
|
|
|
|
Beaprint.NoColorPrint($" {"State",-10} Rule\n");
|
|
foreach (var rule in asrSettings.Rules)
|
|
{
|
|
string state;
|
|
if (rule.State == 0)
|
|
state = "Disabled";
|
|
else if (rule.State == 1)
|
|
state = "Blocked";
|
|
else if (rule.State == 2)
|
|
state = "Audited";
|
|
else
|
|
state = $"{rule.State} - Unknown";
|
|
|
|
var asrRule = _asrGuids.ContainsKey(rule.Rule.ToString())
|
|
? _asrGuids[rule.Rule.ToString()]
|
|
: $"{rule.Rule} - Please report this";
|
|
|
|
Beaprint.NoColorPrint($" {state,-10} {asrRule}");
|
|
}
|
|
|
|
if (asrSettings.Exclusions.Count > 0)
|
|
{
|
|
Beaprint.NoColorPrint("\n ASR Exclusions:");
|
|
foreach (var exclusion in asrSettings.Exclusions)
|
|
{
|
|
Beaprint.NoColorPrint($" {exclusion}");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
try
|
|
{
|
|
var info = WindowsDefender.GetDefenderSettingsInfo();
|
|
|
|
Beaprint.ColorPrint(" Local Settings", Beaprint.LBLUE);
|
|
DisplayDefenderSettings(info.LocalSettings);
|
|
|
|
Beaprint.ColorPrint(" Group Policy Settings", Beaprint.LBLUE);
|
|
DisplayDefenderSettings(info.GroupPolicySettings);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
}
|
|
}
|
|
|
|
private static void PrintDotNetVersions()
|
|
{
|
|
try
|
|
{
|
|
Beaprint.MainPrint("Installed .NET versions\n", "T1082");
|
|
|
|
var info = DotNet.GetDotNetInfo();
|
|
|
|
Beaprint.ColorPrint(" CLR Versions", Beaprint.LBLUE);
|
|
foreach (var version in info.ClrVersions)
|
|
{
|
|
Beaprint.NoColorPrint($" {version}");
|
|
}
|
|
|
|
Beaprint.ColorPrint("\n .NET Versions", Beaprint.LBLUE);
|
|
foreach (var version in info.DotNetVersions)
|
|
{
|
|
Beaprint.NoColorPrint($" {version}");
|
|
}
|
|
|
|
var colors = new Dictionary<string, string>
|
|
{
|
|
{ "True", Beaprint.ansi_color_good },
|
|
{ "False", Beaprint.ansi_color_bad },
|
|
};
|
|
|
|
Beaprint.ColorPrint("\n .NET & AMSI (Anti-Malware Scan Interface) support", Beaprint.LBLUE);
|
|
Beaprint.AnsiPrint($" .NET version supports AMSI : {info.IsAmsiSupportedByDotNet}\n" +
|
|
$" OS supports AMSI : {info.IsAmsiSupportedByOs}",
|
|
colors);
|
|
|
|
var highestVersion = info.HighestVersion;
|
|
var lowestVersion = info.LowestVersion;
|
|
|
|
if ((highestVersion.Major == DotNetInfo.AmsiSupportedByDotNetMinMajorVersion) && (highestVersion.Minor >= DotNetInfo.AmsiSupportedByDotNetMinMinorVersion))
|
|
{
|
|
Beaprint.NoColorPrint($" [!] The highest .NET version is enrolled in AMSI!");
|
|
}
|
|
|
|
if (
|
|
info.IsAmsiSupportedByOs &&
|
|
info.IsAmsiSupportedByDotNet &&
|
|
((
|
|
(lowestVersion.Major == DotNetInfo.AmsiSupportedByDotNetMinMajorVersion - 1)
|
|
) ||
|
|
((lowestVersion.Major == DotNetInfo.AmsiSupportedByDotNetMinMajorVersion) && (lowestVersion.Minor < DotNetInfo.AmsiSupportedByDotNetMinMinorVersion)))
|
|
)
|
|
{
|
|
Beaprint.NoColorPrint($" [-] You can invoke .NET version {lowestVersion.Major}.{lowestVersion.Minor} to bypass AMSI.");
|
|
}
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
}
|
|
}
|
|
|
|
private static void PrintSystemLastShutdownTime()
|
|
{
|
|
try
|
|
{
|
|
Beaprint.MainPrint("System Last Shutdown Date/time (from Registry)\n", "T1082");
|
|
|
|
var shutdownBytes = RegistryHelper.GetRegValueBytes("HKLM", "SYSTEM\\ControlSet001\\Control\\Windows", "ShutdownTime");
|
|
if (shutdownBytes != null)
|
|
{
|
|
var shutdownInt = BitConverter.ToInt64(shutdownBytes, 0);
|
|
var shutdownTime = DateTime.FromFileTime(shutdownInt);
|
|
|
|
Beaprint.NoColorPrint($" Last Shutdown Date/time : {shutdownTime}");
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
}
|
|
}
|
|
|
|
private static void PrintLSAInfo()
|
|
{
|
|
try
|
|
{
|
|
Beaprint.MainPrint("Enumerate LSA settings - auth packages included\n", "T1547.005");
|
|
|
|
var settings = RegistryHelper.GetRegValues("HKLM", "SYSTEM\\CurrentControlSet\\Control\\Lsa");
|
|
|
|
if ((settings != null) && (settings.Count != 0))
|
|
{
|
|
foreach (var kvp in settings)
|
|
{
|
|
var val = string.Empty;
|
|
|
|
if (kvp.Value.GetType().IsArray && (kvp.Value.GetType().GetElementType().ToString() == "System.String"))
|
|
{
|
|
val = string.Join(",", (string[])kvp.Value);
|
|
}
|
|
else if (kvp.Value.GetType().IsArray && (kvp.Value.GetType().GetElementType().ToString() == "System.Byte"))
|
|
{
|
|
val = BitConverter.ToString((byte[])kvp.Value);
|
|
}
|
|
else
|
|
{
|
|
val = kvp.Value.ToString();
|
|
}
|
|
|
|
var key = kvp.Key;
|
|
|
|
Beaprint.NoColorPrint($" {key,-30} : {val}");
|
|
|
|
if (Regex.IsMatch(key, "Security Packages") && Regex.IsMatch(val, @".*wdigest.*"))
|
|
{
|
|
Beaprint.BadPrint(" [!] WDigest is enabled - plaintext password extraction is possible!");
|
|
}
|
|
|
|
if (key.Equals("RunAsPPL", StringComparison.InvariantCultureIgnoreCase) && val == "1")
|
|
{
|
|
Beaprint.BadPrint(" [!] LSASS Protected Mode is enabled! You will not be able to access lsass.exe's memory easily.");
|
|
}
|
|
|
|
if (key.Equals("DisableRestrictedAdmin", StringComparison.InvariantCultureIgnoreCase) && val == "0")
|
|
{
|
|
Beaprint.BadPrint(" [!] RDP Restricted Admin Mode is enabled! You can use pass-the-hash to access RDP on this system.");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
}
|
|
}
|
|
|
|
private static void PrintLocalGroupPolicy()
|
|
{
|
|
try
|
|
{
|
|
Beaprint.MainPrint("Display Local Group Policy settings - local users/machine", "T1082");
|
|
|
|
var infos = GroupPolicy.GetLocalGroupPolicyInfos();
|
|
|
|
foreach (var info in infos)
|
|
{
|
|
Beaprint.NoColorPrint($" Type : {info.GPOType}\n" +
|
|
$" Display Name : {info.DisplayName}\n" +
|
|
$" Name : {info.GPOName}\n" +
|
|
$" Extensions : {info.Extensions}\n" +
|
|
$" File Sys Path : {info.FileSysPath}\n" +
|
|
$" Link : {info.Link}\n" +
|
|
$" GPO Link : {info.GPOLink.GetDescription()}\n" +
|
|
$" Options : {info.Options.GetDescription()}\n");
|
|
|
|
Beaprint.PrintLineSeparator();
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
}
|
|
}
|
|
|
|
private static void PrintPotentialGPOAbuse()
|
|
{
|
|
try
|
|
{
|
|
Beaprint.MainPrint("Potential GPO abuse vectors (applied domain GPOs writable by current user)", "T1484.001");
|
|
|
|
if (!Checks.IsPartOfDomain)
|
|
{
|
|
Beaprint.NoColorPrint(" Host is not joined to a domain or domain info is unavailable.");
|
|
return;
|
|
}
|
|
|
|
// Build a friendly group list for the current user to quickly spot interesting memberships
|
|
var currentGroups = winPEAS.Info.UserInfo.User.GetUserGroups(Checks.CurrentUserName, Checks.CurrentUserDomainName) ?? new System.Collections.Generic.List<string>();
|
|
var hasGPCO = currentGroups.Any(g => string.Equals(g, "Group Policy Creator Owners", System.StringComparison.InvariantCultureIgnoreCase));
|
|
|
|
if (hasGPCO)
|
|
{
|
|
Beaprint.BadPrint(" [!] Current user is member of 'Group Policy Creator Owners' — can create/own new GPOs. If you can link a GPO to an OU that applies here, you can execute code as SYSTEM via scheduled task/startup script.");
|
|
}
|
|
|
|
var infos = GroupPolicy.GetLocalGroupPolicyInfos();
|
|
|
|
bool anyFinding = false;
|
|
foreach (var info in infos)
|
|
{
|
|
var fileSysPath = info.FileSysPath?.ToString();
|
|
if (string.IsNullOrEmpty(fileSysPath))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// Only look at domain GPOs stored in SYSVOL
|
|
var isSysvolPath = fileSysPath.StartsWith(@"\", System.StringComparison.InvariantCultureIgnoreCase) &&
|
|
fileSysPath.IndexOf(@"\SysVol\", System.StringComparison.InvariantCultureIgnoreCase) >= 0 &&
|
|
fileSysPath.IndexOf(@"\Policies\", System.StringComparison.InvariantCultureIgnoreCase) >= 0;
|
|
|
|
if (!isSysvolPath)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// Check write/equivalent permissions on common abuse locations inside the GPO
|
|
var pathsToCheck = new System.Collections.Generic.List<string>
|
|
{
|
|
fileSysPath,
|
|
System.IO.Path.Combine(fileSysPath, @"Machine\Scripts\Startup"),
|
|
System.IO.Path.Combine(fileSysPath, @"User\Scripts\Logon"),
|
|
System.IO.Path.Combine(fileSysPath, @"Machine\Preferences\ScheduledTasks")
|
|
};
|
|
|
|
foreach (var p in pathsToCheck)
|
|
{
|
|
var perms = PermissionsHelper.GetPermissionsFolder(p, Checks.CurrentUserSiDs, PermissionType.WRITEABLE_OR_EQUIVALENT);
|
|
if (perms != null && perms.Count > 0)
|
|
{
|
|
if (!anyFinding)
|
|
{
|
|
Beaprint.LinkPrint("https://book.hacktricks.wiki/en/windows-hardening/active-directory-methodology/gpo-abuse.html", "Why it matters");
|
|
}
|
|
anyFinding = true;
|
|
Beaprint.BadPrint($" [!] Writable applied GPO detected");
|
|
Beaprint.NoColorPrint($" GPO Display Name : {info.DisplayName}");
|
|
Beaprint.NoColorPrint($" GPO Name : {info.GPOName}");
|
|
Beaprint.NoColorPrint($" GPO Link : {info.Link}");
|
|
Beaprint.NoColorPrint($" Path : {p}");
|
|
foreach (var entry in perms)
|
|
{
|
|
Beaprint.NoColorPrint($" -> {entry}");
|
|
}
|
|
Beaprint.GrayPrint(" Hint: Abuse by adding an immediate Scheduled Task or Startup script to execute as SYSTEM on gpupdate.");
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!anyFinding && !hasGPCO)
|
|
{
|
|
Beaprint.NoColorPrint(" No obvious GPO abuse via writable SYSVOL paths or GPCO membership detected.");
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
// Avoid noisy stack traces in normal runs
|
|
Beaprint.GrayPrint($" [!] Error while checking potential GPO abuse: {ex.Message}");
|
|
}
|
|
}
|
|
|
|
|
|
private static void PrintPowerShellSessionSettings()
|
|
{
|
|
try
|
|
{
|
|
Beaprint.MainPrint("Enumerating PowerShell Session Settings using the registry", "T1059.001");
|
|
|
|
if (!MyUtils.IsHighIntegrity())
|
|
{
|
|
Beaprint.NoColorPrint(" You must be an administrator to run this check");
|
|
return;
|
|
}
|
|
|
|
var infos = PowerShell.GetPowerShellSessionSettingsInfos();
|
|
|
|
foreach (var info in infos)
|
|
{
|
|
Beaprint.NoColorPrint($" {"Name",-38} {info.Plugin}");
|
|
|
|
foreach (var access in info.Permissions)
|
|
{
|
|
Beaprint.NoColorPrint($" {access.Principal,-35} {access.Permission,-22}");
|
|
}
|
|
|
|
Beaprint.PrintLineSeparator();
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
}
|
|
}
|
|
|
|
private static void PrintRegistryCreds()
|
|
{
|
|
try
|
|
{
|
|
Beaprint.MainPrint("Enumerating saved credentials in Registry (CurrentPass)", "T1552.002");
|
|
string currentPass = "CurrentPass";
|
|
var hive = "HKLM";
|
|
var path = "System";
|
|
var controlSet = "ControlSet";
|
|
|
|
var colors = new Dictionary<string, string>
|
|
{
|
|
{ currentPass, Beaprint.ansi_color_bad }
|
|
};
|
|
|
|
var subkeys = RegistryHelper.GetRegSubkeys(hive, path);
|
|
|
|
foreach (var subkey in subkeys.Where(i => i.Contains(controlSet)))
|
|
{
|
|
try
|
|
{
|
|
var subPath = @$"{path}\{subkey}\Control";
|
|
var key = $@"{hive}\{subPath}\{currentPass}";
|
|
var value = RegistryHelper.GetRegValue(hive, subPath, currentPass);
|
|
|
|
if (!string.IsNullOrWhiteSpace(value))
|
|
{
|
|
Beaprint.AnsiPrint($@" {key,-60} : {value}", colors);
|
|
}
|
|
}
|
|
catch (Exception)
|
|
{
|
|
}
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
}
|
|
}
|
|
}
|
|
}
|