Add winpeas privilege escalation checks from: Inside Ink Dragon: Revealing the Relay Network and Inner Workings of a Stealthy

This commit is contained in:
HackTricks News Bot
2025-12-16 19:11:20 +00:00
parent b4a1382e8a
commit 85aa98a841
3 changed files with 125 additions and 3 deletions

View File

@@ -76,6 +76,7 @@ The goal of this project is to search for possible **Privilege Escalation Paths*
New in this version: New in this version:
- Detect potential GPO abuse by flagging writable SYSVOL paths for GPOs applied to the current host and by highlighting membership in the "Group Policy Creator Owners" group. - Detect potential GPO abuse by flagging writable SYSVOL paths for GPOs applied to the current host and by highlighting membership in the "Group Policy Creator Owners" group.
- Highlight disconnected high-privilege RDP sessions so you can plan LSASS/token theft when admins leave idle sessions (Ink Dragon-style escalation).
It should take only a **few seconds** to execute almost all the checks and **some seconds/minutes during the lasts checks searching for known filenames** that could contain passwords (the time depened on the number of files in your home folder). By default only **some** filenames that could contain credentials are searched, you can use the **searchall** parameter to search all the list (this could will add some minutes). It should take only a **few seconds** to execute almost all the checks and **some seconds/minutes during the lasts checks searching for known filenames** that could contain passwords (the time depened on the number of files in your home folder). By default only **some** filenames that could contain credentials are searched, you can use the **searchall** parameter to search all the list (this could will add some minutes).

View File

@@ -156,15 +156,63 @@ namespace winPEAS.Checks
try try
{ {
Beaprint.MainPrint("RDP Sessions"); Beaprint.MainPrint("RDP Sessions");
Beaprint.LinkPrint("https://book.hacktricks.wiki/en/windows-hardening/windows-local-privilege-escalation/credentials-mgmt/rdp-sessions", "Disconnected high-privilege RDP sessions keep reusable tokens inside LSASS.");
List<Dictionary<string, string>> rdp_sessions = UserInfoHelper.GetRDPSessions(); List<Dictionary<string, string>> rdp_sessions = UserInfoHelper.GetRDPSessions();
if (rdp_sessions.Count > 0) if (rdp_sessions.Count > 0)
{ {
string format = " {0,-10}{1,-15}{2,-15}{3,-25}{4,-10}{5}"; string format = " {0,-8}{1,-15}{2,-20}{3,-22}{4,-15}{5,-18}{6,-10}";
string header = string.Format(format, "SessID", "pSessionName", "pUserName", "pDomainName", "State", "SourceIP"); string header = string.Format(format, "SessID", "Session", "User", "Domain", "State", "SourceIP", "HighPriv");
Beaprint.GrayPrint(header); Beaprint.GrayPrint(header);
var colors = ColorsU();
List<Dictionary<string, string>> flaggedSessions = new List<Dictionary<string, string>>();
foreach (Dictionary<string, string> rdpSes in rdp_sessions) foreach (Dictionary<string, string> rdpSes in rdp_sessions)
{ {
Beaprint.AnsiPrint(string.Format(format, rdpSes["SessionID"], rdpSes["pSessionName"], rdpSes["pUserName"], rdpSes["pDomainName"], rdpSes["State"], rdpSes["SourceIP"]), ColorsU()); rdpSes.TryGetValue("SessionID", out string sessionId);
rdpSes.TryGetValue("pSessionName", out string sessionName);
rdpSes.TryGetValue("pUserName", out string userName);
rdpSes.TryGetValue("pDomainName", out string domainName);
rdpSes.TryGetValue("State", out string state);
rdpSes.TryGetValue("SourceIP", out string sourceIp);
sessionId = sessionId ?? string.Empty;
sessionName = sessionName ?? string.Empty;
userName = userName ?? string.Empty;
domainName = domainName ?? string.Empty;
state = state ?? string.Empty;
sourceIp = sourceIp ?? string.Empty;
bool isHighPriv = UserInfoHelper.IsHighPrivilegeAccount(userName, domainName);
string highPrivLabel = isHighPriv ? "Yes" : "No";
rdpSes["HighPriv"] = highPrivLabel;
if (isHighPriv && string.Equals(state, "Disconnected", StringComparison.OrdinalIgnoreCase))
{
flaggedSessions.Add(rdpSes);
}
Beaprint.AnsiPrint(string.Format(format, sessionId, sessionName, userName, domainName, state, sourceIp, highPrivLabel), colors);
}
if (flaggedSessions.Count > 0)
{
Beaprint.BadPrint(" [!] Disconnected high-privilege RDP sessions detected. Their credentials/tokens stay in LSASS until the user signs out.");
foreach (Dictionary<string, string> session in flaggedSessions)
{
session.TryGetValue("pDomainName", out string flaggedDomain);
session.TryGetValue("pUserName", out string flaggedUser);
session.TryGetValue("SessionID", out string flaggedSessionId);
session.TryGetValue("SourceIP", out string flaggedIp);
flaggedDomain = flaggedDomain ?? string.Empty;
flaggedUser = flaggedUser ?? string.Empty;
flaggedSessionId = flaggedSessionId ?? string.Empty;
flaggedIp = flaggedIp ?? string.Empty;
string userDisplay = string.Format("{0}\\{1}", flaggedDomain, flaggedUser).Trim('\\');
string source = string.IsNullOrEmpty(flaggedIp) ? "local" : flaggedIp;
Beaprint.BadPrint(string.Format(" -> Session {0} ({1}) from {2}", flaggedSessionId, userDisplay, source));
}
Beaprint.LinkPrint("https://book.hacktricks.wiki/en/windows-hardening/windows-local-privilege-escalation/credentials-mgmt/rdp-sessions", "Dump LSASS / steal tokens (e.g., comsvcs.dll, LsaLogonSessions, custom SSPs) to reuse those privileges.");
} }
} }
else else

View File

@@ -16,6 +16,20 @@ namespace winPEAS.Info.UserInfo
{ {
class UserInfoHelper class UserInfoHelper
{ {
private static readonly Dictionary<string, bool> _highPrivAccountCache = new Dictionary<string, bool>(StringComparer.OrdinalIgnoreCase);
private static readonly string[] _highPrivGroupIndicators = new string[]
{
"administrators",
"domain admins",
"enterprise admins",
"schema admins",
"server operators",
"account operators",
"backup operators",
"dnsadmins",
"hyper-v administrators"
};
// https://stackoverflow.com/questions/5247798/get-list-of-local-computer-usernames-in-windows // https://stackoverflow.com/questions/5247798/get-list-of-local-computer-usernames-in-windows
@@ -91,6 +105,65 @@ namespace winPEAS.Info.UserInfo
return oPrincipalContext; return oPrincipalContext;
} }
public static bool IsHighPrivilegeAccount(string userName, string domain)
{
if (string.IsNullOrWhiteSpace(userName))
{
return false;
}
string cacheKey = ($"{domain}\\{userName}").Trim('\\');
if (_highPrivAccountCache.TryGetValue(cacheKey, out bool cached))
{
return cached;
}
bool isHighPriv = false;
try
{
string resolvedDomain = string.IsNullOrWhiteSpace(domain) ? Checks.Checks.CurrentUserDomainName : domain;
List<string> groups = User.GetUserGroups(userName, resolvedDomain);
foreach (string group in groups)
{
if (IsHighPrivilegeGroup(group))
{
isHighPriv = true;
break;
}
}
}
catch (Exception ex)
{
Beaprint.GrayPrint(string.Format(" [-] Unable to resolve groups for {0}\\{1}: {2}", domain, userName, ex.Message));
}
if (!isHighPriv)
{
isHighPriv = string.Equals(userName, "administrator", StringComparison.OrdinalIgnoreCase) || userName.StartsWith("admin", StringComparison.OrdinalIgnoreCase);
}
_highPrivAccountCache[cacheKey] = isHighPriv;
return isHighPriv;
}
private static bool IsHighPrivilegeGroup(string groupName)
{
if (string.IsNullOrWhiteSpace(groupName))
{
return false;
}
foreach (string indicator in _highPrivGroupIndicators)
{
if (groupName.IndexOf(indicator, StringComparison.OrdinalIgnoreCase) >= 0)
{
return true;
}
}
return false;
}
//From Seatbelt //From Seatbelt
public enum WTS_CONNECTSTATE_CLASS public enum WTS_CONNECTSTATE_CLASS
{ {