mirror of
https://github.com/peass-ng/PEASS-ng.git
synced 2026-04-28 11:53:22 -07:00
Add winpeas privilege escalation checks from: Inside Ink Dragon: Revealing the Relay Network and Inner Workings of a Stealthy
This commit is contained in:
@@ -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).
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user