Add winpeas privilege escalation checks from: pipetap – A Windows Named Pipe Multi-tool and Proxy for Intercepting and Replayi

This commit is contained in:
HackTricks News Bot
2025-12-09 02:07:57 +00:00
parent 94e84dec91
commit b7b7aebf1c
4 changed files with 553 additions and 0 deletions

View File

@@ -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

View File

@@ -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");

View File

@@ -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; }
}
}

View File

@@ -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" />