mirror of
https://github.com/peass-ng/PEASS-ng.git
synced 2025-12-12 07:40:39 -08:00
Add winpeas privilege escalation checks from: pipetap – A Windows Named Pipe Multi-tool and Proxy for Intercepting and Replayi
This commit is contained in:
@@ -153,6 +153,7 @@ Once you have installed and activated it you need to:
|
|||||||
- [x] Applocker Configuration & bypass suggestions
|
- [x] Applocker Configuration & bypass suggestions
|
||||||
- [x] Printers
|
- [x] Printers
|
||||||
- [x] Named Pipes
|
- [x] Named Pipes
|
||||||
|
- [x] Named Pipe ACL abuse candidates
|
||||||
- [x] AMSI Providers
|
- [x] AMSI Providers
|
||||||
- [x] SysMon
|
- [x] SysMon
|
||||||
- [x] .NET Versions
|
- [x] .NET Versions
|
||||||
|
|||||||
@@ -88,6 +88,7 @@ namespace winPEAS.Checks
|
|||||||
AppLockerHelper.PrintAppLockerPolicy,
|
AppLockerHelper.PrintAppLockerPolicy,
|
||||||
PrintPrintersWMIInfo,
|
PrintPrintersWMIInfo,
|
||||||
PrintNamedPipes,
|
PrintNamedPipes,
|
||||||
|
PrintNamedPipeAbuseCandidates,
|
||||||
PrintAMSIProviders,
|
PrintAMSIProviders,
|
||||||
PrintSysmon,
|
PrintSysmon,
|
||||||
PrintDotNetVersions
|
PrintDotNetVersions
|
||||||
@@ -791,6 +792,48 @@ namespace winPEAS.Checks
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static void PrintNamedPipeAbuseCandidates()
|
||||||
|
{
|
||||||
|
Beaprint.MainPrint("Named Pipes with Low-Priv Write Access to Privileged Servers");
|
||||||
|
|
||||||
|
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()
|
private void PrintAMSIProviders()
|
||||||
{
|
{
|
||||||
Beaprint.MainPrint("Enumerating AMSI registered providers");
|
Beaprint.MainPrint("Enumerating AMSI registered providers");
|
||||||
|
|||||||
@@ -0,0 +1,508 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Security.AccessControl;
|
||||||
|
using System.Security.Principal;
|
||||||
|
using winPEAS.Helpers;
|
||||||
|
using winPEAS.Native;
|
||||||
|
|
||||||
|
namespace winPEAS.Info.SystemInfo.NamedPipes
|
||||||
|
{
|
||||||
|
internal static class NamedPipeSecurityAnalyzer
|
||||||
|
{
|
||||||
|
private const string DeviceNamedPipePrefix = @"\Device\NamedPipe\";
|
||||||
|
private static readonly char[] CandidateSeparators = { '\\', '/', '-', ':', '(' };
|
||||||
|
|
||||||
|
private static readonly HashSet<string> LowPrivSidSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
|
||||||
|
{
|
||||||
|
"S-1-1-0", // Everyone
|
||||||
|
"S-1-5-11", // Authenticated Users
|
||||||
|
"S-1-5-32-545", // Users
|
||||||
|
"S-1-5-32-546", // Guests
|
||||||
|
"S-1-5-32-547", // Power Users
|
||||||
|
"S-1-5-32-554", // Pre-Windows 2000 Compatible Access
|
||||||
|
"S-1-5-32-555", // Remote Desktop Users
|
||||||
|
"S-1-5-32-558", // Performance Log Users
|
||||||
|
"S-1-5-32-559", // Performance Monitor Users
|
||||||
|
"S-1-5-32-562", // Distributed COM Users
|
||||||
|
"S-1-5-32-569", // Remote Management Users
|
||||||
|
"S-1-5-4", // Interactive
|
||||||
|
"S-1-5-2", // Network
|
||||||
|
"S-1-5-1", // Dialup
|
||||||
|
"S-1-5-7" // Anonymous Logon
|
||||||
|
};
|
||||||
|
|
||||||
|
private static readonly HashSet<string> LowPrivPrincipalKeywords = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
|
||||||
|
{
|
||||||
|
"everyone",
|
||||||
|
"authenticated users",
|
||||||
|
"users",
|
||||||
|
"guests",
|
||||||
|
"power users",
|
||||||
|
"remote desktop users",
|
||||||
|
"remote management users",
|
||||||
|
"distributed com users",
|
||||||
|
"anonymous logon",
|
||||||
|
"interactive",
|
||||||
|
"network",
|
||||||
|
"local",
|
||||||
|
"batch",
|
||||||
|
"iis_iusrs"
|
||||||
|
};
|
||||||
|
|
||||||
|
private static readonly HashSet<string> PrivilegedSidSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
|
||||||
|
{
|
||||||
|
"S-1-5-18", // SYSTEM
|
||||||
|
"S-1-5-19", // LOCAL SERVICE
|
||||||
|
"S-1-5-20", // NETWORK SERVICE
|
||||||
|
"S-1-5-32-544" // Administrators
|
||||||
|
};
|
||||||
|
|
||||||
|
private static readonly (string Label, FileSystemRights Right)[] DangerousRightsMap = new[]
|
||||||
|
{
|
||||||
|
("FullControl", FileSystemRights.FullControl),
|
||||||
|
("Modify", FileSystemRights.Modify),
|
||||||
|
("Write", FileSystemRights.Write),
|
||||||
|
("WriteData", FileSystemRights.WriteData),
|
||||||
|
("AppendData", FileSystemRights.AppendData),
|
||||||
|
("CreateFiles", FileSystemRights.CreateFiles),
|
||||||
|
("CreateDirectories", FileSystemRights.CreateDirectories),
|
||||||
|
("WriteAttributes", FileSystemRights.WriteAttributes),
|
||||||
|
("WriteExtendedAttributes", FileSystemRights.WriteExtendedAttributes),
|
||||||
|
("Delete", FileSystemRights.Delete),
|
||||||
|
("ChangePermissions", FileSystemRights.ChangePermissions),
|
||||||
|
("TakeOwnership", FileSystemRights.TakeOwnership)
|
||||||
|
};
|
||||||
|
|
||||||
|
public static IEnumerable<NamedPipeSecurityIssue> GetNamedPipeAbuseCandidates()
|
||||||
|
{
|
||||||
|
var insecurePipes = DiscoverInsecurePipes();
|
||||||
|
if (!insecurePipes.Any())
|
||||||
|
{
|
||||||
|
return Enumerable.Empty<NamedPipeSecurityIssue>();
|
||||||
|
}
|
||||||
|
|
||||||
|
AttachProcesses(insecurePipes);
|
||||||
|
|
||||||
|
return insecurePipes.Values
|
||||||
|
.Where(issue => issue.LowPrivilegeAces.Any())
|
||||||
|
.OrderByDescending(issue => issue.HasPrivilegedServer)
|
||||||
|
.ThenBy(issue => issue.Name)
|
||||||
|
.ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Dictionary<string, NamedPipeSecurityIssue> DiscoverInsecurePipes()
|
||||||
|
{
|
||||||
|
var result = new Dictionary<string, NamedPipeSecurityIssue>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
|
foreach (var pipe in NamedPipes.GetNamedPipeInfos())
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(pipe.Sddl) || pipe.Sddl.Equals("ERROR", StringComparison.OrdinalIgnoreCase))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var descriptor = new RawSecurityDescriptor(pipe.Sddl);
|
||||||
|
if (descriptor.DiscretionaryAcl == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
foreach (GenericAce ace in descriptor.DiscretionaryAcl)
|
||||||
|
{
|
||||||
|
if (!(ace is CommonAce commonAce))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var sid = commonAce.SecurityIdentifier;
|
||||||
|
if (sid == null || !IsLowPrivilegePrincipal(sid))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!HasDangerousWriteRights(commonAce.AccessMask))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var rights = DescribeRights(commonAce.AccessMask).ToList();
|
||||||
|
if (!rights.Any())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!result.TryGetValue(pipe.Name, out var issue))
|
||||||
|
{
|
||||||
|
issue = new NamedPipeSecurityIssue(pipe.Name, pipe.Sddl, NormalizePipeName(pipe.Name));
|
||||||
|
result[pipe.Name] = issue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var account = ResolveSidToName(sid);
|
||||||
|
issue.AddLowPrivPrincipal(account, sid.Value, rights);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// Ignore malformed SDDL strings
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void AttachProcesses(Dictionary<string, NamedPipeSecurityIssue> insecurePipes)
|
||||||
|
{
|
||||||
|
if (!insecurePipes.Any())
|
||||||
|
return;
|
||||||
|
|
||||||
|
var lookup = BuildLookup(insecurePipes.Values);
|
||||||
|
if (!lookup.Any())
|
||||||
|
return;
|
||||||
|
|
||||||
|
List<HandlesHelper.SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX> handles;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
handles = HandlesHelper.GetAllHandlers();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var currentProcess = Kernel32.GetCurrentProcess();
|
||||||
|
var processCache = new Dictionary<int, NamedPipeProcessInfo>();
|
||||||
|
|
||||||
|
foreach (var handle in handles)
|
||||||
|
{
|
||||||
|
IntPtr processHandle = IntPtr.Zero;
|
||||||
|
IntPtr duplicatedHandle = IntPtr.Zero;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
int pid = GetPid(handle);
|
||||||
|
if (pid <= 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
processHandle = Kernel32.OpenProcess(
|
||||||
|
HandlesHelper.ProcessAccessFlags.DupHandle | HandlesHelper.ProcessAccessFlags.QueryLimitedInformation,
|
||||||
|
false,
|
||||||
|
pid);
|
||||||
|
|
||||||
|
if (processHandle == IntPtr.Zero)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!Kernel32.DuplicateHandle(processHandle, handle.HandleValue, currentProcess, out duplicatedHandle, 0, false, HandlesHelper.DUPLICATE_SAME_ACCESS))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var typeName = HandlesHelper.GetObjectType(duplicatedHandle);
|
||||||
|
if (!string.Equals(typeName, "File", StringComparison.OrdinalIgnoreCase))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var objectName = HandlesHelper.GetObjectName(duplicatedHandle);
|
||||||
|
if (string.IsNullOrEmpty(objectName) || !objectName.StartsWith(DeviceNamedPipePrefix, StringComparison.OrdinalIgnoreCase))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var normalizedHandleName = NormalizePipeName(objectName.Substring(DeviceNamedPipePrefix.Length));
|
||||||
|
var candidates = GetCandidateKeys(normalizedHandleName);
|
||||||
|
|
||||||
|
bool matched = false;
|
||||||
|
|
||||||
|
foreach (var candidate in candidates)
|
||||||
|
{
|
||||||
|
if (!lookup.TryGetValue(candidate, out var matchedIssues))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!processCache.TryGetValue(pid, out var processInfo))
|
||||||
|
{
|
||||||
|
var raw = HandlesHelper.getProcInfoById(pid);
|
||||||
|
processInfo = new NamedPipeProcessInfo(raw.pid, raw.name, raw.userName, raw.userSid, IsHighPrivilegeAccount(raw.userSid, raw.userName));
|
||||||
|
processCache[pid] = processInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var issue in matchedIssues)
|
||||||
|
{
|
||||||
|
issue.AddProcess(processInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
matched = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!matched)
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// Ignore per-handle failures
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (duplicatedHandle != IntPtr.Zero)
|
||||||
|
{
|
||||||
|
Kernel32.CloseHandle(duplicatedHandle);
|
||||||
|
}
|
||||||
|
if (processHandle != IntPtr.Zero)
|
||||||
|
{
|
||||||
|
Kernel32.CloseHandle(processHandle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Dictionary<string, List<NamedPipeSecurityIssue>> BuildLookup(IEnumerable<NamedPipeSecurityIssue> issues)
|
||||||
|
{
|
||||||
|
var lookup = new Dictionary<string, List<NamedPipeSecurityIssue>>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
|
foreach (var issue in issues)
|
||||||
|
{
|
||||||
|
foreach (var key in GetCandidateKeys(issue.NormalizedName))
|
||||||
|
{
|
||||||
|
if (!lookup.TryGetValue(key, out var list))
|
||||||
|
{
|
||||||
|
list = new List<NamedPipeSecurityIssue>();
|
||||||
|
lookup[key] = list;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!list.Contains(issue))
|
||||||
|
{
|
||||||
|
list.Add(issue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return lookup;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IEnumerable<string> GetCandidateKeys(string normalizedName)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(normalizedName))
|
||||||
|
return Array.Empty<string>();
|
||||||
|
|
||||||
|
var candidates = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
|
||||||
|
{
|
||||||
|
normalizedName
|
||||||
|
};
|
||||||
|
|
||||||
|
foreach (var separator in CandidateSeparators)
|
||||||
|
{
|
||||||
|
var idx = normalizedName.IndexOf(separator);
|
||||||
|
if (idx > 0)
|
||||||
|
{
|
||||||
|
candidates.Add(normalizedName.Substring(0, idx));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return candidates;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string NormalizePipeName(string rawName)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(rawName))
|
||||||
|
return string.Empty;
|
||||||
|
|
||||||
|
var normalized = rawName.Replace('/', '\\').Trim();
|
||||||
|
while (normalized.StartsWith("\\", StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
normalized = normalized.Substring(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return normalized.ToLowerInvariant();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool HasDangerousWriteRights(int accessMask)
|
||||||
|
{
|
||||||
|
var rights = (FileSystemRights)accessMask;
|
||||||
|
foreach (var entry in DangerousRightsMap)
|
||||||
|
{
|
||||||
|
if ((rights & entry.Right) == entry.Right)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IEnumerable<string> DescribeRights(int accessMask)
|
||||||
|
{
|
||||||
|
var rights = (FileSystemRights)accessMask;
|
||||||
|
var descriptions = new List<string>();
|
||||||
|
|
||||||
|
foreach (var entry in DangerousRightsMap)
|
||||||
|
{
|
||||||
|
if ((rights & entry.Right) == entry.Right)
|
||||||
|
{
|
||||||
|
descriptions.Add(entry.Label);
|
||||||
|
if (entry.Right == FileSystemRights.FullControl)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!descriptions.Any())
|
||||||
|
{
|
||||||
|
descriptions.Add($"0x{accessMask:x}");
|
||||||
|
}
|
||||||
|
|
||||||
|
return descriptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsLowPrivilegePrincipal(SecurityIdentifier sid)
|
||||||
|
{
|
||||||
|
if (sid == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (LowPrivSidSet.Contains(sid.Value))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
var accountName = ResolveSidToName(sid);
|
||||||
|
if (string.IsNullOrEmpty(accountName))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return LowPrivPrincipalKeywords.Any(keyword => accountName.IndexOf(keyword, StringComparison.OrdinalIgnoreCase) >= 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string ResolveSidToName(SecurityIdentifier sid)
|
||||||
|
{
|
||||||
|
if (sid == null)
|
||||||
|
return string.Empty;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return sid.Translate(typeof(NTAccount)).Value;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return sid.Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsHighPrivilegeAccount(string sid, string userName)
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(sid))
|
||||||
|
{
|
||||||
|
if (PrivilegedSidSet.Contains(sid))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (sid.StartsWith("S-1-5-80-", StringComparison.OrdinalIgnoreCase)) // Service SID
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (sid.StartsWith("S-1-5-82-", StringComparison.OrdinalIgnoreCase)) // AppPool / service-like SIDs
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(userName))
|
||||||
|
{
|
||||||
|
if (string.Equals(userName, HandlesHelper.elevatedProcess, StringComparison.OrdinalIgnoreCase))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
var normalized = userName.ToUpperInvariant();
|
||||||
|
if (normalized.Contains("SYSTEM") || normalized.Contains("LOCAL SERVICE") || normalized.Contains("NETWORK SERVICE"))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (normalized.StartsWith("NT SERVICE\\", StringComparison.Ordinal))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (normalized.EndsWith("$", StringComparison.Ordinal) && normalized.Contains("\\"))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int GetPid(HandlesHelper.SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX handle)
|
||||||
|
{
|
||||||
|
unchecked
|
||||||
|
{
|
||||||
|
if (IntPtr.Size == 4)
|
||||||
|
{
|
||||||
|
return (int)handle.UniqueProcessId.ToUInt32();
|
||||||
|
}
|
||||||
|
|
||||||
|
return (int)handle.UniqueProcessId.ToUInt64();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class NamedPipeSecurityIssue
|
||||||
|
{
|
||||||
|
private readonly Dictionary<string, NamedPipePrincipalAccess> _principalAccess = new Dictionary<string, NamedPipePrincipalAccess>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
private readonly Dictionary<int, NamedPipeProcessInfo> _processes = new Dictionary<int, NamedPipeProcessInfo>();
|
||||||
|
|
||||||
|
public NamedPipeSecurityIssue(string name, string sddl, string normalizedName)
|
||||||
|
{
|
||||||
|
Name = name;
|
||||||
|
Sddl = sddl;
|
||||||
|
NormalizedName = normalizedName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Name { get; }
|
||||||
|
public string Sddl { get; }
|
||||||
|
public string NormalizedName { get; }
|
||||||
|
|
||||||
|
public IReadOnlyCollection<NamedPipePrincipalAccess> LowPrivilegeAces => _principalAccess.Values;
|
||||||
|
public IReadOnlyCollection<NamedPipeProcessInfo> Processes => _processes.Values;
|
||||||
|
public bool HasPrivilegedServer => _processes.Values.Any(process => process.IsHighPrivilege);
|
||||||
|
|
||||||
|
public void AddLowPrivPrincipal(string principal, string sid, IEnumerable<string> rights)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(sid))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!_principalAccess.TryGetValue(sid, out var access))
|
||||||
|
{
|
||||||
|
access = new NamedPipePrincipalAccess(principal, sid);
|
||||||
|
_principalAccess[sid] = access;
|
||||||
|
}
|
||||||
|
|
||||||
|
access.AddRights(rights);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddProcess(NamedPipeProcessInfo process)
|
||||||
|
{
|
||||||
|
if (process == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!_processes.ContainsKey(process.Pid))
|
||||||
|
{
|
||||||
|
_processes[process.Pid] = process;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class NamedPipePrincipalAccess
|
||||||
|
{
|
||||||
|
private readonly HashSet<string> _rights = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
|
public NamedPipePrincipalAccess(string principal, string sid)
|
||||||
|
{
|
||||||
|
Principal = principal;
|
||||||
|
Sid = sid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Principal { get; }
|
||||||
|
public string Sid { get; }
|
||||||
|
public string RightsDescription => _rights.Count == 0 ? string.Empty : string.Join("|", _rights.OrderBy(r => r));
|
||||||
|
public IEnumerable<string> Rights => _rights;
|
||||||
|
|
||||||
|
public void AddRights(IEnumerable<string> rights)
|
||||||
|
{
|
||||||
|
if (rights == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
foreach (var right in rights)
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrWhiteSpace(right))
|
||||||
|
{
|
||||||
|
_rights.Add(right.Trim());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class NamedPipeProcessInfo
|
||||||
|
{
|
||||||
|
public NamedPipeProcessInfo(int pid, string processName, string userName, string userSid, bool isHighPrivilege)
|
||||||
|
{
|
||||||
|
Pid = pid;
|
||||||
|
ProcessName = processName;
|
||||||
|
UserName = userName;
|
||||||
|
UserSid = userSid;
|
||||||
|
IsHighPrivilege = isHighPrivilege;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Pid { get; }
|
||||||
|
public string ProcessName { get; }
|
||||||
|
public string UserName { get; }
|
||||||
|
public string UserSid { get; }
|
||||||
|
public bool IsHighPrivilege { get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1291,6 +1291,7 @@
|
|||||||
<Compile Include="Info\SystemInfo\GroupPolicy\GroupPolicy.cs" />
|
<Compile Include="Info\SystemInfo\GroupPolicy\GroupPolicy.cs" />
|
||||||
<Compile Include="Info\SystemInfo\GroupPolicy\LocalGroupPolicyInfo.cs" />
|
<Compile Include="Info\SystemInfo\GroupPolicy\LocalGroupPolicyInfo.cs" />
|
||||||
<Compile Include="Info\SystemInfo\NamedPipes\NamedPipeInfo.cs" />
|
<Compile Include="Info\SystemInfo\NamedPipes\NamedPipeInfo.cs" />
|
||||||
|
<Compile Include="Info\SystemInfo\NamedPipes\NamedPipeSecurityAnalyzer.cs" />
|
||||||
<Compile Include="Info\SystemInfo\NamedPipes\NamedPipes.cs" />
|
<Compile Include="Info\SystemInfo\NamedPipes\NamedPipes.cs" />
|
||||||
<Compile Include="Info\SystemInfo\Ntlm\Ntlm.cs" />
|
<Compile Include="Info\SystemInfo\Ntlm\Ntlm.cs" />
|
||||||
<Compile Include="Info\SystemInfo\Ntlm\NtlmSettingsInfo.cs" />
|
<Compile Include="Info\SystemInfo\Ntlm\NtlmSettingsInfo.cs" />
|
||||||
|
|||||||
Reference in New Issue
Block a user