mirror of
https://github.com/peass-ng/PEASS-ng.git
synced 2026-04-28 11:53:22 -07:00
Merge pull request #538 from peass-ng/update_PEASS-winpeas-Inside_Ink_Dragon__Revealing_the_Rel_20251216_185841
[WINPEAS] Add privilege escalation check: Inside Ink Dragon Revealing the Relay Ne...
This commit is contained in:
@@ -74,11 +74,6 @@ winpeas.exe -lolbas #Execute also additional LOLBAS search check
|
||||
|
||||
The goal of this project is to search for possible **Privilege Escalation Paths** in Windows environments.
|
||||
|
||||
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.
|
||||
- Flag legacy/expired-signed kernel drivers (e.g., ValleyRAT's kernelquick) and their registry-controlled stealth configuration so you can spot kernel-level persistence.
|
||||
|
||||
|
||||
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).
|
||||
|
||||
The tool is based on **[SeatBelt](https://github.com/GhostPack/Seatbelt)**.
|
||||
|
||||
@@ -156,15 +156,63 @@ namespace winPEAS.Checks
|
||||
try
|
||||
{
|
||||
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();
|
||||
if (rdp_sessions.Count > 0)
|
||||
{
|
||||
string format = " {0,-10}{1,-15}{2,-15}{3,-25}{4,-10}{5}";
|
||||
string header = string.Format(format, "SessID", "pSessionName", "pUserName", "pDomainName", "State", "SourceIP");
|
||||
string format = " {0,-8}{1,-15}{2,-20}{3,-22}{4,-15}{5,-18}{6,-10}";
|
||||
string header = string.Format(format, "SessID", "Session", "User", "Domain", "State", "SourceIP", "HighPriv");
|
||||
Beaprint.GrayPrint(header);
|
||||
var colors = ColorsU();
|
||||
List<Dictionary<string, string>> flaggedSessions = new List<Dictionary<string, string>>();
|
||||
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
|
||||
|
||||
@@ -16,6 +16,20 @@ namespace winPEAS.Info.UserInfo
|
||||
{
|
||||
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
|
||||
|
||||
|
||||
@@ -91,6 +105,65 @@ namespace winPEAS.Info.UserInfo
|
||||
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
|
||||
public enum WTS_CONNECTSTATE_CLASS
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user