mirror of
https://github.com/carlospolop/privilege-escalation-awesome-scripts-suite.git
synced 2026-01-27 07:59:02 +00:00
Compare commits
1 Commits
20250904-4
...
update_PEA
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
91278660b8 |
@@ -1265,21 +1265,7 @@ search:
|
||||
type: f
|
||||
bad_regex: ".*"
|
||||
search_in:
|
||||
- common
|
||||
|
||||
|
||||
- name: Terraform
|
||||
value:
|
||||
config:
|
||||
auto_check: True
|
||||
|
||||
files:
|
||||
- name: "credentials.tfrc.json"
|
||||
value:
|
||||
type: f
|
||||
bad_regex: ".*"
|
||||
search_in:
|
||||
- common
|
||||
- common
|
||||
|
||||
- name: Cloud Credentials
|
||||
value:
|
||||
|
||||
@@ -170,7 +170,7 @@ LinPEAS uses colors to indicate where does each section begin. But **it also use
|
||||
|
||||
- The  **Red** color is used for identifing suspicious configurations that could lead to privilege escalation.
|
||||
|
||||
- The  **Green** color is used for known good configurations (based on the name not on the content!)
|
||||
- The  **Green** color is used for known good configurations (based on the name not on the conten!)
|
||||
|
||||
- The  **Blue** color is used for: Users without shell & Mounted devices
|
||||
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
# Title: Processes & Cron & Services & Timers - r-commands trust (rsh/rlogin/rexec)
|
||||
# ID: PR_RCommands_trust
|
||||
# Author: HT Bot
|
||||
# Last Update: 25-08-2025
|
||||
# Description: Detect hostname-based trust for Berkeley r-commands and active listeners; warn about DNS-assisted abuse.
|
||||
# License: GNU GPL
|
||||
# Version: 1.0
|
||||
# Functions Used: print_2title, print_3title, print_list, print_info, echo_no
|
||||
# Global Variables: $SEARCH_IN_FOLDER, $E, $SED_RED, $SED_RED_YELLOW
|
||||
# Initial Functions:
|
||||
# Generated Global Variables: $rhosts_found, $rsvc_listeners, $homes, $h, $f, $found
|
||||
# Fat linpeas: 0
|
||||
# Small linpeas: 1
|
||||
|
||||
if ! [ "$SEARCH_IN_FOLDER" ]; then
|
||||
print_2title "Berkeley r-commands trust (rsh/rlogin/rexec)"
|
||||
print_info "https://book.hacktricks.wiki/en/linux-hardening/privilege-escalation/index.html#r-commands-rlogin-rsh-rexec"
|
||||
|
||||
rhosts_found=""
|
||||
|
||||
# 1) Trust files: /etc/hosts.equiv and per-user ~/.rhosts
|
||||
print_list "Trust files (.rhosts / hosts.equiv)? ... "
|
||||
(
|
||||
# /etc/hosts.equiv
|
||||
if [ -r "/etc/hosts.equiv" ]; then
|
||||
printf "\n/etc/hosts.equiv (perm: %s)\n" "$(stat -c %a /etc/hosts.equiv 2>/dev/null || stat -f %p /etc/hosts.equiv 2>/dev/null)"
|
||||
# highlight risky entries: '+' or hosts granting any user
|
||||
sed -n "1,200p" /etc/hosts.equiv 2>/dev/null | sed -${E} "s,^\s*\+.*$,${SED_RED},; s,\s+\s*$,${SED_RED},"
|
||||
rhosts_found=1
|
||||
fi
|
||||
|
||||
# Per-user .rhosts from passwd
|
||||
# Use getent if available, else parse /etc/passwd
|
||||
homes=$( (getent passwd 2>/dev/null || cat /etc/passwd 2>/dev/null) | awk -F: '{print $6}' | sort -u )
|
||||
for h in $homes; do
|
||||
f="$h/.rhosts"
|
||||
if [ -r "$f" ]; then
|
||||
printf "\n%s (perm: %s)\n" "$f" "$(stat -c %a "$f" 2>/dev/null || stat -f %p "$f" 2>/dev/null)"
|
||||
sed -n "1,200p" "$f" 2>/dev/null | sed -${E} "s,^\s*\+.*$,${SED_RED},; s,\s+\s*$,${SED_RED},"
|
||||
rhosts_found=1
|
||||
fi
|
||||
done
|
||||
|
||||
# Common root path fallback
|
||||
if [ -r "/root/.rhosts" ] && ! echo "$homes" | grep -q "^/root$"; then
|
||||
printf "\n/root/.rhosts (perm: %s)\n" "$(stat -c %a /root/.rhosts 2>/dev/null || stat -f %p /root/.rhosts 2>/dev/null)"
|
||||
sed -n "1,200p" /root/.rhosts 2>/dev/null | sed -${E} "s,^\s*\+.*$,${SED_RED},; s,\s+\s*$,${SED_RED},"
|
||||
rhosts_found=1
|
||||
fi
|
||||
|
||||
[ "$rhosts_found" ] || echo_no
|
||||
) 2>/dev/null
|
||||
|
||||
# 2) r-commands listeners (512 exec/rexec, 513 rlogin, 514 rsh)
|
||||
print_list "Are r-commands listening? ............ "
|
||||
rsvc_listeners=""
|
||||
if command -v ss >/dev/null 2>&1; then
|
||||
ss -tlpn 2>/dev/null | awk 'NR==1 || $4 ~ /:(512|513|514)$/ {print}' | sed -n '2,200p' | sed -${E} "s,.*,${SED_RED_YELLOW}," && rsvc_listeners=1
|
||||
elif command -v netstat >/dev/null 2>&1; then
|
||||
netstat -tlpn 2>/dev/null | awk 'NR==1 || $4 ~ /:(512|513|514)$/ {print}' | sed -n '2,200p' | sed -${E} "s,.*,${SED_RED_YELLOW}," && rsvc_listeners=1
|
||||
fi
|
||||
[ "$rsvc_listeners" ] || echo_no
|
||||
|
||||
# 3) inetd/xinetd/systemd configuration hints
|
||||
print_list "rsh/rlogin/rexec enabled in inetd/xinetd? "
|
||||
(
|
||||
found=""
|
||||
[ -r /etc/inetd.conf ] && grep -E "(^|\s)(rsh|rlogin|rexec)(\s|$)" /etc/inetd.conf 2>/dev/null && found=1
|
||||
if ls /etc/xinetd.d/* >/dev/null 2>&1; then
|
||||
grep -E "(rsh|rlogin|rexec)" /etc/xinetd.d/* 2>/dev/null && found=1
|
||||
fi
|
||||
[ "$found" ] || echo_no
|
||||
)
|
||||
|
||||
print_list "rsh/rlogin/rexec sockets in systemd? .. "
|
||||
(
|
||||
found=""
|
||||
if command -v systemctl >/dev/null 2>&1; then
|
||||
systemctl list-unit-files --type=socket --no-pager 2>/dev/null | grep -E "(rlogin|rsh|rexec)" && found=1
|
||||
systemctl list-sockets --no-pager 2>/dev/null | grep -E "(rlogin|rsh|rexec)" && found=1
|
||||
fi
|
||||
[ "$found" ] || echo_no
|
||||
)
|
||||
|
||||
# 4) PAM rhosts trust
|
||||
print_list "PAM rhosts trust enabled? ............ "
|
||||
(
|
||||
found=""
|
||||
for p in /etc/pam.d/rlogin /etc/pam.d/rsh /etc/pam.d/rexec; do
|
||||
[ -r "$p" ] && grep -E "pam_rhosts|pam_rhosts_auth" "$p" 2>/dev/null && found=1
|
||||
done
|
||||
[ "$found" ] || echo_no
|
||||
)
|
||||
|
||||
# 5) Container-to-host hint
|
||||
if [ -f "/.dockerenv" ]; then
|
||||
print_info "Running inside a container. If host runs r-commands and root/.rhosts trusts hostnames, aligning A+PTR DNS may allow passwordless rlogin/rsh to the host."
|
||||
fi
|
||||
|
||||
# 6) Actionable guidance
|
||||
print_3title "Why risky and how to abuse"
|
||||
echo "- If a trusted entry is a hostname (not an IP) and r-services are listening, an attacker controlling DNS can set matching forward (A) and reverse (PTR) records so their IP resolves to the trusted name and reverse to the same name, passing hostname checks for passwordless access (even root if in /root/.rhosts or hosts.equiv)." | sed -${E} "s,passwordless access,${SED_RED_YELLOW},"
|
||||
fi
|
||||
@@ -1,139 +0,0 @@
|
||||
# Title: Processes & Cron & Services & Timers - Legacy r-commands and host-based trust
|
||||
# ID: PR_Rcommands_trust
|
||||
# Author: HT Bot
|
||||
# Last Update: 27-08-2025
|
||||
# Description: Detect legacy r-services (rsh/rlogin/rexec) exposure and dangerous host-based trust (.rhosts/hosts.equiv),
|
||||
# which can allow passwordless root via hostname/DNS manipulation.
|
||||
# License: GNU GPL
|
||||
# Version: 1.0
|
||||
# Functions Used: print_2title, print_3title, echo_not_found
|
||||
# Global Variables:
|
||||
# Initial Functions:
|
||||
# Generated Global Variables: $rfile, $perms, $owner, $g, $o, $any_rhosts, $shown, $f, $p
|
||||
# Fat linpeas: 0
|
||||
# Small linpeas: 1
|
||||
|
||||
if ! [ "$SEARCH_IN_FOLDER" ]; then
|
||||
print_2title "Legacy r-commands (rsh/rlogin/rexec) and host-based trust"
|
||||
|
||||
echo ""
|
||||
print_3title "Listening r-services (TCP 512-514)"
|
||||
if command -v ss >/dev/null 2>&1; then
|
||||
ss -ltnp 2>/dev/null | awk '$1 ~ /^LISTEN$/ && $4 ~ /:(512|513|514)$/ {print}' || echo_not_found "ss"
|
||||
elif command -v netstat >/dev/null 2>&1; then
|
||||
netstat -ltnp 2>/dev/null | awk '$6 ~ /LISTEN/ && $4 ~ /:(512|513|514)$/ {print}' || echo_not_found "netstat"
|
||||
else
|
||||
echo_not_found "ss|netstat"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
print_3title "systemd units exposing r-services"
|
||||
if command -v systemctl >/dev/null 2>&1; then
|
||||
systemctl list-unit-files 2>/dev/null | grep -E '^(rlogin|rsh|rexec)\.(socket|service)\b' || echo_not_found "rlogin|rsh|rexec units"
|
||||
systemctl list-sockets 2>/dev/null | grep -E '\b(rlogin|rsh|rexec)\.socket\b' || true
|
||||
else
|
||||
echo_not_found "systemctl"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
print_3title "inetd/xinetd configuration for r-services"
|
||||
if [ -f /etc/inetd.conf ]; then
|
||||
grep -vE '^\s*#|^\s*$' /etc/inetd.conf 2>/dev/null | grep -Ei '\b(shell|login|exec|rsh|rlogin|rexec)\b' 2>/dev/null || echo " No r-services found in /etc/inetd.conf"
|
||||
else
|
||||
echo_not_found "/etc/inetd.conf"
|
||||
fi
|
||||
if [ -d /etc/xinetd.d ]; then
|
||||
# Print enabled r-services in xinetd
|
||||
for f in /etc/xinetd.d/*; do
|
||||
[ -f "$f" ] || continue
|
||||
if grep -qiE '\b(service|disable)\b' "$f" 2>/dev/null; then
|
||||
if grep -qiE 'service\s+(rsh|rlogin|rexec|shell|login|exec)\b' "$f" 2>/dev/null; then
|
||||
# Only warn if not disabled
|
||||
if ! grep -qiE '^\s*disable\s*=\s*yes\b' "$f" 2>/dev/null; then
|
||||
echo " $(basename "$f") may enable r-services:"; grep -iE '^(\s*service|\s*disable)' "$f" 2>/dev/null | sed 's/^/ /'
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
done
|
||||
else
|
||||
echo_not_found "/etc/xinetd.d"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
print_3title "Installed r-service server packages"
|
||||
if command -v dpkg >/dev/null 2>&1; then
|
||||
dpkg -l 2>/dev/null | grep -E '\b(rsh-server|rsh-redone-server|krb5-rsh-server|inetutils-inetd|openbsd-inetd|xinetd|netkit-rsh)\b' || echo " No related packages found via dpkg"
|
||||
elif command -v rpm >/dev/null 2>&1; then
|
||||
rpm -qa 2>/dev/null | grep -Ei '\b(rsh|rlogin|rexec|xinetd)\b' || echo " No related packages found via rpm"
|
||||
else
|
||||
echo_not_found "dpkg|rpm"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
print_3title "/etc/hosts.equiv and /etc/shosts.equiv"
|
||||
for f in /etc/hosts.equiv /etc/shosts.equiv; do
|
||||
if [ -f "$f" ]; then
|
||||
perms=$(stat -c %a "$f" 2>/dev/null)
|
||||
owner=$(stat -c %U "$f" 2>/dev/null)
|
||||
echo " $f (perm $perms, owner $owner)"
|
||||
# Print non-comment lines
|
||||
awk 'NF && $0 !~ /^\s*#/ {print " " $0}' "$f" 2>/dev/null
|
||||
if grep -qEv '^\s*#|^\s*$' "$f" 2>/dev/null; then
|
||||
if grep -qE '(^|\s)\+' "$f" 2>/dev/null; then
|
||||
echo " [!] Wildcard '+' trust found"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
echo ""
|
||||
print_3title "Per-user .rhosts files"
|
||||
any_rhosts=false
|
||||
for rfile in /root/.rhosts /home/*/.rhosts; do
|
||||
if [ -f "$rfile" ]; then
|
||||
any_rhosts=true
|
||||
perms=$(stat -c %a "$rfile" 2>/dev/null)
|
||||
owner=$(stat -c %U "$rfile" 2>/dev/null)
|
||||
echo " $rfile (perm $perms, owner $owner)"
|
||||
awk 'NF && $0 !~ /^\s*#/ {print " " $0}' "$rfile" 2>/dev/null
|
||||
# Warn on insecure perms (group/other write)
|
||||
g=$(printf "%s" "$perms" | cut -c2)
|
||||
o=$(printf "%s" "$perms" | cut -c3)
|
||||
if [ "${g:-0}" -ge 2 ] || [ "${o:-0}" -ge 2 ]; then
|
||||
echo " [!] Insecure permissions (group/other write)"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
if ! $any_rhosts; then echo_not_found ".rhosts"; fi
|
||||
|
||||
echo ""
|
||||
print_3title "PAM rhosts authentication"
|
||||
shown=false
|
||||
for p in /etc/pam.d/rlogin /etc/pam.d/rsh; do
|
||||
if [ -f "$p" ]; then
|
||||
shown=true
|
||||
echo " $p:"
|
||||
(grep -nEi 'pam_rhosts|pam_rhosts_auth' "$p" 2>/dev/null || echo " no pam_rhosts* lines") | sed 's/^/ /'
|
||||
fi
|
||||
done
|
||||
if ! $shown; then echo_not_found "/etc/pam.d/rlogin|rsh"; fi
|
||||
|
||||
echo ""
|
||||
print_3title "SSH HostbasedAuthentication"
|
||||
if [ -f /etc/ssh/sshd_config ]; then
|
||||
if grep -qiE '^[^#]*HostbasedAuthentication\s+yes' /etc/ssh/sshd_config 2>/dev/null; then
|
||||
echo " HostbasedAuthentication yes (check /etc/shosts.equiv or ~/.shosts)"
|
||||
else
|
||||
echo " HostbasedAuthentication no or not set"
|
||||
fi
|
||||
else
|
||||
echo_not_found "/etc/ssh/sshd_config"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
print_3title "Potential DNS control indicators (local)"
|
||||
(ps -eo comm,args 2>/dev/null | grep -Ei '(^|/)(pdns|pdns_server|pdns_recursor|powerdns-admin)( |$)' | grep -Ev 'grep|bash' || echo " Not detected")
|
||||
|
||||
echo ""
|
||||
fi
|
||||
|
||||
@@ -74,23 +74,10 @@ 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.
|
||||
|
||||
|
||||
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)**.
|
||||
|
||||
### New (AD-aware) checks
|
||||
|
||||
- Active Directory quick checks now include:
|
||||
- gMSA readable managed passwords: enumerate msDS-GroupManagedServiceAccount objects and report those where the current user/group is allowed to retrieve the managed password (PrincipalsAllowedToRetrieveManagedPassword).
|
||||
- AD CS (ESC4) hygiene: enumerate published certificate templates and highlight templates where the current user/group has dangerous control rights (GenericAll/WriteDacl/WriteOwner/WriteProperty/ExtendedRight) that could allow template abuse (e.g., ESC4 -> ESC1).
|
||||
|
||||
These checks are lightweight, read-only, and only run when the host is domain-joined.
|
||||
|
||||
|
||||
## Where are my COLORS?!?!?!
|
||||
|
||||
The **ouput will be colored** using **ansi** colors. If you are executing `winpeas.exe` **from a Windows console**, you need to set a registry value to see the colors (and open a new CMD):
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Text.RegularExpressions" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.1.0.0" newVersion="4.1.0.0" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.1.1.0" newVersion="4.1.1.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Linq" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
<package id="System.Runtime.Numerics" version="4.3.0" targetFramework="net452" />
|
||||
<package id="System.Text.Encoding" version="4.3.0" targetFramework="net452" />
|
||||
<package id="System.Text.Encoding.Extensions" version="4.3.0" targetFramework="net452" />
|
||||
<package id="System.Text.RegularExpressions" version="4.3.1" targetFramework="net48" />
|
||||
<package id="System.Text.RegularExpressions" version="4.3.0" targetFramework="net452" requireReinstallation="true" />
|
||||
<package id="System.Threading" version="4.3.0" targetFramework="net452" />
|
||||
<package id="System.Threading.Tasks" version="4.3.0" targetFramework="net452" />
|
||||
<package id="System.Threading.Timer" version="4.3.0" targetFramework="net452" />
|
||||
|
||||
@@ -83,10 +83,6 @@
|
||||
<Reference Include="System.Runtime.InteropServices.RuntimeInformation, Version=4.0.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Runtime.InteropServices.RuntimeInformation.4.3.0\lib\net45\System.Runtime.InteropServices.RuntimeInformation.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Text.RegularExpressions, Version=4.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<HintPath>..\packages\System.Text.RegularExpressions.4.3.1\lib\net463\System.Text.RegularExpressions.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="Microsoft.Bcl.AsyncInterfaces" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-9.0.0.1" newVersion="9.0.0.1" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-8.0.0.0" newVersion="8.0.0.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="Microsoft.Win32.Primitives" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
|
||||
@@ -156,7 +156,7 @@
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Text.Encodings.Web" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-9.0.0.1" newVersion="9.0.0.1" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-8.0.0.0" newVersion="8.0.0.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Text.RegularExpressions" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
|
||||
|
||||
@@ -1,275 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.DirectoryServices;
|
||||
using System.Security.AccessControl;
|
||||
using System.Security.Principal;
|
||||
using winPEAS.Helpers;
|
||||
|
||||
namespace winPEAS.Checks
|
||||
{
|
||||
// Lightweight AD-oriented checks for common escalation paths (gMSA readable password, AD CS template control)
|
||||
internal class ActiveDirectoryInfo : ISystemCheck
|
||||
{
|
||||
public void PrintInfo(bool isDebug)
|
||||
{
|
||||
Beaprint.GreatPrint("Active Directory Quick Checks");
|
||||
|
||||
new List<Action>
|
||||
{
|
||||
PrintGmsaReadableByCurrentPrincipal,
|
||||
PrintAdcsEsc4LikeTemplates
|
||||
}.ForEach(action => CheckRunner.Run(action, isDebug));
|
||||
}
|
||||
|
||||
private static HashSet<string> GetCurrentSidSet()
|
||||
{
|
||||
var sids = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||
try
|
||||
{
|
||||
var id = WindowsIdentity.GetCurrent();
|
||||
sids.Add(id.User.Value);
|
||||
foreach (var g in id.Groups)
|
||||
{
|
||||
sids.Add(g.Value);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Beaprint.GrayPrint(" [!] Error obtaining current SIDs: " + ex.Message);
|
||||
}
|
||||
return sids;
|
||||
}
|
||||
|
||||
private static string GetRootDseProp(string prop)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var root = new DirectoryEntry("LDAP://RootDSE"))
|
||||
{
|
||||
return root.Properties[prop]?.Value as string;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Beaprint.GrayPrint($" [!] Error accessing RootDSE ({prop}): {ex.Message}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static string GetProp(SearchResult r, string name)
|
||||
{
|
||||
return (r.Properties.Contains(name) && r.Properties[name].Count > 0)
|
||||
? r.Properties[name][0]?.ToString()
|
||||
: null;
|
||||
}
|
||||
|
||||
// Detect gMSA objects where the current principal (or one of its groups) can retrieve the managed password
|
||||
private void PrintGmsaReadableByCurrentPrincipal()
|
||||
{
|
||||
try
|
||||
{
|
||||
Beaprint.MainPrint("gMSA readable managed passwords");
|
||||
Beaprint.LinkPrint(
|
||||
"https://book.hacktricks.wiki/en/windows-hardening/active-directory-methodology/gmsa.html",
|
||||
"Look for Group Managed Service Accounts you can read (msDS-ManagedPassword)");
|
||||
|
||||
if (!Checks.IsPartOfDomain)
|
||||
{
|
||||
Beaprint.GrayPrint(" [-] Host is not domain-joined. Skipping.");
|
||||
return;
|
||||
}
|
||||
|
||||
var defaultNC = GetRootDseProp("defaultNamingContext");
|
||||
if (string.IsNullOrEmpty(defaultNC))
|
||||
{
|
||||
Beaprint.GrayPrint(" [-] Could not resolve defaultNamingContext.");
|
||||
return;
|
||||
}
|
||||
|
||||
var currentSidSet = GetCurrentSidSet();
|
||||
int total = 0, readable = 0;
|
||||
|
||||
using (var baseDe = new DirectoryEntry("LDAP://" + defaultNC))
|
||||
using (var ds = new DirectorySearcher(baseDe))
|
||||
{
|
||||
ds.PageSize = 300;
|
||||
ds.Filter = "(&(objectClass=msDS-GroupManagedServiceAccount))";
|
||||
ds.PropertiesToLoad.Add("sAMAccountName");
|
||||
ds.PropertiesToLoad.Add("distinguishedName");
|
||||
// Who can read the managed password
|
||||
ds.PropertiesToLoad.Add("PrincipalsAllowedToRetrieveManagedPassword");
|
||||
|
||||
foreach (SearchResult r in ds.FindAll())
|
||||
{
|
||||
total++;
|
||||
var name = GetProp(r, "sAMAccountName") ?? GetProp(r, "distinguishedName") ?? "<unknown>";
|
||||
var dn = GetProp(r, "distinguishedName") ?? "";
|
||||
|
||||
bool canRead = false;
|
||||
// Attribute may be absent or empty
|
||||
var allowedDns = r.Properties["principalsallowedtoretrievemanagedpassword"];
|
||||
if (allowedDns != null)
|
||||
{
|
||||
foreach (var val in allowedDns)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var de = new DirectoryEntry("LDAP://" + val.ToString()))
|
||||
{
|
||||
var sidObj = de.Properties["objectSid"]?.Value as byte[];
|
||||
if (sidObj == null) continue;
|
||||
var sid = new SecurityIdentifier(sidObj, 0).Value;
|
||||
if (currentSidSet.Contains(sid))
|
||||
{
|
||||
canRead = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch { /* ignore DN resolution issues */ }
|
||||
}
|
||||
}
|
||||
|
||||
if (canRead)
|
||||
{
|
||||
readable++;
|
||||
Beaprint.BadPrint($" You can retrieve managed password for gMSA: {name} (DN: {dn})");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (readable == 0)
|
||||
{
|
||||
Beaprint.GrayPrint($" [-] No gMSA with readable managed password found (checked {total}).");
|
||||
}
|
||||
else
|
||||
{
|
||||
Beaprint.GrayPrint($" [*] Hint: If such gMSA is member of Builtin\\Remote Management Users on a target, WinRM may be allowed.");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Beaprint.PrintException(ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
// Detect AD CS certificate templates where current principal has dangerous control rights (ESC4-style)
|
||||
private void PrintAdcsEsc4LikeTemplates()
|
||||
{
|
||||
try
|
||||
{
|
||||
Beaprint.MainPrint("AD CS templates with dangerous ACEs (ESC4)");
|
||||
Beaprint.LinkPrint(
|
||||
"https://book.hacktricks.wiki/en/windows-hardening/active-directory-methodology/ad-certificates.html#esc4",
|
||||
"If you can modify a template (WriteDacl/WriteOwner/GenericAll), you can abuse ESC4");
|
||||
|
||||
if (!Checks.IsPartOfDomain)
|
||||
{
|
||||
Beaprint.GrayPrint(" [-] Host is not domain-joined. Skipping.");
|
||||
return;
|
||||
}
|
||||
|
||||
var configNC = GetRootDseProp("configurationNamingContext");
|
||||
if (string.IsNullOrEmpty(configNC))
|
||||
{
|
||||
Beaprint.GrayPrint(" [-] Could not resolve configurationNamingContext.");
|
||||
return;
|
||||
}
|
||||
|
||||
var currentSidSet = GetCurrentSidSet();
|
||||
int checkedTemplates = 0;
|
||||
int vulnerable = 0;
|
||||
|
||||
var templatesDn = $"LDAP://CN=Certificate Templates,CN=Public Key Services,CN=Services,{configNC}";
|
||||
|
||||
using (var deBase = new DirectoryEntry(templatesDn))
|
||||
using (var ds = new DirectorySearcher(deBase))
|
||||
{
|
||||
ds.PageSize = 300;
|
||||
ds.Filter = "(objectClass=pKICertificateTemplate)";
|
||||
ds.PropertiesToLoad.Add("cn");
|
||||
|
||||
foreach (SearchResult r in ds.FindAll())
|
||||
{
|
||||
checkedTemplates++;
|
||||
string templateCn = GetProp(r, "cn") ?? "<unknown>";
|
||||
|
||||
// Fetch security descriptor (DACL)
|
||||
DirectoryEntry de = null;
|
||||
try
|
||||
{
|
||||
de = r.GetDirectoryEntry();
|
||||
de.Options.SecurityMasks = SecurityMasks.Dacl;
|
||||
de.RefreshCache(new[] { "ntSecurityDescriptor" });
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
de?.Dispose();
|
||||
continue;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var sd = de.ObjectSecurity; // ActiveDirectorySecurity
|
||||
var rules = sd.GetAccessRules(true, true, typeof(SecurityIdentifier));
|
||||
bool hit = false;
|
||||
var hitRights = new HashSet<string>();
|
||||
|
||||
foreach (ActiveDirectoryAccessRule rule in rules)
|
||||
{
|
||||
if (rule.AccessControlType != AccessControlType.Allow) continue;
|
||||
var sid = (rule.IdentityReference as SecurityIdentifier)?.Value;
|
||||
if (string.IsNullOrEmpty(sid)) continue;
|
||||
if (!currentSidSet.Contains(sid)) continue;
|
||||
|
||||
var rights = rule.ActiveDirectoryRights;
|
||||
bool dangerous =
|
||||
rights.HasFlag(ActiveDirectoryRights.GenericAll) ||
|
||||
rights.HasFlag(ActiveDirectoryRights.WriteDacl) ||
|
||||
rights.HasFlag(ActiveDirectoryRights.WriteOwner) ||
|
||||
rights.HasFlag(ActiveDirectoryRights.WriteProperty) ||
|
||||
rights.HasFlag(ActiveDirectoryRights.ExtendedRight);
|
||||
|
||||
if (dangerous)
|
||||
{
|
||||
hit = true;
|
||||
if (rights.HasFlag(ActiveDirectoryRights.GenericAll)) hitRights.Add("GenericAll");
|
||||
if (rights.HasFlag(ActiveDirectoryRights.WriteDacl)) hitRights.Add("WriteDacl");
|
||||
if (rights.HasFlag(ActiveDirectoryRights.WriteOwner)) hitRights.Add("WriteOwner");
|
||||
if (rights.HasFlag(ActiveDirectoryRights.WriteProperty)) hitRights.Add("WriteProperty");
|
||||
if (rights.HasFlag(ActiveDirectoryRights.ExtendedRight)) hitRights.Add("ExtendedRight");
|
||||
}
|
||||
}
|
||||
|
||||
if (hit)
|
||||
{
|
||||
vulnerable++;
|
||||
Beaprint.BadPrint($" Dangerous rights over template: {templateCn} (Rights: {string.Join(",", hitRights)})");
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// ignore templates we couldn't read
|
||||
}
|
||||
finally
|
||||
{
|
||||
de?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (vulnerable == 0)
|
||||
{
|
||||
Beaprint.GrayPrint($" [-] No templates with dangerous rights found (checked {checkedTemplates}).");
|
||||
}
|
||||
else
|
||||
{
|
||||
Beaprint.GrayPrint(" [*] Tip: Abuse with tools like Certipy (template write -> ESC1 -> enroll).");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Beaprint.PrintException(ex.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -90,7 +90,6 @@ namespace winPEAS.Checks
|
||||
new SystemCheck("servicesinfo", new ServicesInfo()),
|
||||
new SystemCheck("applicationsinfo", new ApplicationsInfo()),
|
||||
new SystemCheck("networkinfo", new NetworkInfo()),
|
||||
new SystemCheck("activedirectoryinfo", new ActiveDirectoryInfo()),
|
||||
new SystemCheck("cloudinfo", new CloudInfo()),
|
||||
new SystemCheck("windowscreds", new WindowsCreds()),
|
||||
new SystemCheck("browserinfo", new BrowserInfo()),
|
||||
|
||||
@@ -84,7 +84,6 @@ namespace winPEAS.Checks
|
||||
PrintLSAInfo,
|
||||
PrintNtlmSettings,
|
||||
PrintLocalGroupPolicy,
|
||||
PrintPotentialGPOAbuse,
|
||||
AppLockerHelper.PrintAppLockerPolicy,
|
||||
PrintPrintersWMIInfo,
|
||||
PrintNamedPipes,
|
||||
@@ -1132,94 +1131,6 @@ namespace winPEAS.Checks
|
||||
}
|
||||
}
|
||||
|
||||
private static void PrintPotentialGPOAbuse()
|
||||
{
|
||||
try
|
||||
{
|
||||
Beaprint.MainPrint("Potential GPO abuse vectors (applied domain GPOs writable by current user)");
|
||||
|
||||
if (!Checks.IsPartOfDomain)
|
||||
{
|
||||
Beaprint.NoColorPrint(" Host is not joined to a domain or domain info is unavailable.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Build a friendly group list for the current user to quickly spot interesting memberships
|
||||
var currentGroups = winPEAS.Info.UserInfo.User.GetUserGroups(Checks.CurrentUserName, Checks.CurrentUserDomainName) ?? new System.Collections.Generic.List<string>();
|
||||
var hasGPCO = currentGroups.Any(g => string.Equals(g, "Group Policy Creator Owners", System.StringComparison.InvariantCultureIgnoreCase));
|
||||
|
||||
if (hasGPCO)
|
||||
{
|
||||
Beaprint.BadPrint(" [!] Current user is member of 'Group Policy Creator Owners' — can create/own new GPOs. If you can link a GPO to an OU that applies here, you can execute code as SYSTEM via scheduled task/startup script.");
|
||||
}
|
||||
|
||||
var infos = GroupPolicy.GetLocalGroupPolicyInfos();
|
||||
|
||||
bool anyFinding = false;
|
||||
foreach (var info in infos)
|
||||
{
|
||||
var fileSysPath = info.FileSysPath?.ToString();
|
||||
if (string.IsNullOrEmpty(fileSysPath))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Only look at domain GPOs stored in SYSVOL
|
||||
var isSysvolPath = fileSysPath.StartsWith(@"\", System.StringComparison.InvariantCultureIgnoreCase) &&
|
||||
fileSysPath.IndexOf(@"\SysVol\", System.StringComparison.InvariantCultureIgnoreCase) >= 0 &&
|
||||
fileSysPath.IndexOf(@"\Policies\", System.StringComparison.InvariantCultureIgnoreCase) >= 0;
|
||||
|
||||
if (!isSysvolPath)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check write/equivalent permissions on common abuse locations inside the GPO
|
||||
var pathsToCheck = new System.Collections.Generic.List<string>
|
||||
{
|
||||
fileSysPath,
|
||||
System.IO.Path.Combine(fileSysPath, @"Machine\Scripts\Startup"),
|
||||
System.IO.Path.Combine(fileSysPath, @"User\Scripts\Logon"),
|
||||
System.IO.Path.Combine(fileSysPath, @"Machine\Preferences\ScheduledTasks")
|
||||
};
|
||||
|
||||
foreach (var p in pathsToCheck)
|
||||
{
|
||||
var perms = PermissionsHelper.GetPermissionsFolder(p, Checks.CurrentUserSiDs, PermissionType.WRITEABLE_OR_EQUIVALENT);
|
||||
if (perms != null && perms.Count > 0)
|
||||
{
|
||||
if (!anyFinding)
|
||||
{
|
||||
Beaprint.LinkPrint("https://book.hacktricks.wiki/en/windows-hardening/active-directory-methodology/gpo-abuse.html", "Why it matters");
|
||||
}
|
||||
anyFinding = true;
|
||||
Beaprint.BadPrint($" [!] Writable applied GPO detected");
|
||||
Beaprint.NoColorPrint($" GPO Display Name : {info.DisplayName}");
|
||||
Beaprint.NoColorPrint($" GPO Name : {info.GPOName}");
|
||||
Beaprint.NoColorPrint($" GPO Link : {info.Link}");
|
||||
Beaprint.NoColorPrint($" Path : {p}");
|
||||
foreach (var entry in perms)
|
||||
{
|
||||
Beaprint.NoColorPrint($" -> {entry}");
|
||||
}
|
||||
Beaprint.GrayPrint(" Hint: Abuse by adding an immediate Scheduled Task or Startup script to execute as SYSTEM on gpupdate.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!anyFinding && !hasGPCO)
|
||||
{
|
||||
Beaprint.NoColorPrint(" No obvious GPO abuse via writable SYSVOL paths or GPCO membership detected.");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// Avoid noisy stack traces in normal runs
|
||||
Beaprint.GrayPrint($" [!] Error while checking potential GPO abuse: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static void PrintPowerShellSessionSettings()
|
||||
{
|
||||
try
|
||||
|
||||
@@ -129,7 +129,6 @@ namespace winPEAS.Helpers
|
||||
Console.WriteLine(LCYAN + " servicesinfo" + GRAY + " Search services information" + NOCOLOR);
|
||||
Console.WriteLine(LCYAN + " applicationsinfo" + GRAY + " Search installed applications information" + NOCOLOR);
|
||||
Console.WriteLine(LCYAN + " networkinfo" + GRAY + " Search network information" + NOCOLOR);
|
||||
Console.WriteLine(LCYAN + " activedirectoryinfo" + GRAY + " Quick AD checks (gMSA readable passwords, AD CS template rights)" + NOCOLOR);
|
||||
Console.WriteLine(LCYAN + " cloudinfo" + GRAY + " Enumerate cloud information" + NOCOLOR);
|
||||
Console.WriteLine(LCYAN + " windowscreds" + GRAY + " Search windows credentials" + NOCOLOR);
|
||||
Console.WriteLine(LCYAN + " browserinfo" + GRAY + " Search browser information" + NOCOLOR);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user