Compare commits

..

1 Commits

26 changed files with 1821 additions and 2863 deletions

View File

@@ -212,14 +212,15 @@ jobs:
steps:
# Download repo
- uses: actions/checkout@v5
- uses: actions/checkout@v2
with:
ref: ${{ github.head_ref }}
# Setup go
- uses: actions/setup-go@v6
- uses: actions/setup-go@v2
with:
go-version: '1.23'
go-version: 1.17.0-rc1
stable: false
- run: go version
# Build linpeas

View File

@@ -895,14 +895,6 @@ search:
type: f
search_in:
- common
- name: "credentials.tfrc.json"
value:
type: f
bad_regex: ".*"
search_in:
- common
- name: Racoon
value:
@@ -1273,7 +1265,7 @@ search:
type: f
bad_regex: ".*"
search_in:
- common
- common
- name: Cloud Credentials
value:
@@ -2067,11 +2059,6 @@ search:
type: f
search_in:
- common
- name: "private-keys-v1.d/*.key"
value:
type: f
search_in:
- common
- name: "*.gnupg"
value:
@@ -3954,24 +3941,3 @@ search:
type: f
search_in:
- common
- name: Crontab-UI
value:
config:
auto_check: True
files:
- name: "crontab.db"
value:
bad_regex: "-P[[:space:]]+\\S+|--password[[:space:]]+\\S+|[Pp]ass(word)?|[Tt]oken|[Ss]ecret"
only_bad_lines: True
type: f
search_in:
- common
- name: "crontab-ui.service"
value:
just_list_file: True
type: f
search_in:
- common

View File

@@ -22,6 +22,10 @@ Check how to **select the checks you want to build [in your own linpeas followin
Note that by default, in the releases pages of this repository, you will find a **linpeas with all the checks**.
### New in Aug 2025
- Added heuristic detection for Bash arithmetic injection in root-run periodic parsers (cron/timers). LinPEAS now inspects root cron entries and systemd timers to find shell scripts that parse logs and use arithmetic contexts like (( ... )), let, or declare -i with untrusted variables. This may reveal dangerous patterns where attacker-controlled log lines can trigger command substitution inside arithmetic evaluation.
## Differences between `linpeas_fat.sh`, `linpeas.sh` and `linpeas_small.sh`:
- **linpeas_fat.sh**: Contains all checks, even third party applications in base64 embedded.

View File

@@ -1,46 +0,0 @@
# Title: Container - Writable bind mounts without nosuid (SUID risk)
# ID: CT_RW_bind_mounts_nosuid
# Author: HT Bot
# Last Update: 17-09-2025
# Description: Detect writable bind-mounted paths inside containers that are not mounted with nosuid.
# If the container user is root and the mount is a host bind mount without nosuid, an attacker may
# be able to drop a SUID binary on the shared path and execute it from the host to escalate to root
# (classic container-to-host breakout via writable bind mount).
# License: GNU GPL
# Version: 1.0
# Functions Used: containerCheck, print_2title, print_list, print_info
# Global Variables: $inContainer
# Initial Functions: containerCheck
# Generated Global Variables: $CT_RW_bind_mounts_matches
# Fat linpeas: 0
# Small linpeas: 1
containerCheck
if [ "$inContainer" ]; then
echo ""
print_2title "Container - Writable bind mounts w/o nosuid (SUID persistence risk)"
print_info "https://book.hacktricks.wiki/en/linux-hardening/privilege-escalation/docker-security/docker-breakout-privilege-escalation/index.html#writable-bind-mounts"
if [ -r /proc/self/mountinfo ]; then
CT_RW_bind_mounts_matches=$(grep -E "(^| )bind( |$)" /proc/self/mountinfo 2>/dev/null | grep -E "(^|,)rw(,|$)" | grep -v "nosuid" || true)
else
CT_RW_bind_mounts_matches=$(mount -l 2>/dev/null | grep -E "bind" | grep -E "(^|,)rw(,|$)" | grep -v "nosuid" || true)
fi
if [ -z "$CT_RW_bind_mounts_matches" ]; then
print_list "Writable bind mounts without nosuid ............ No"
else
print_list "Writable bind mounts without nosuid ............ Yes" | sed -${E} "s,Yes,${SED_RED},"
echo "$CT_RW_bind_mounts_matches" | sed -${E} "s,/proc/self/mountinfo,${SED_GREEN},"
echo ""
if [ "$(id -u 2>/dev/null)" = "0" ]; then
print_list "Note"; echo ": You are root inside a container and there are writable bind mounts without nosuid." | sed -${E} "s,.*,${SED_RED},"
echo " If the path is shared with the host and executable there, you may plant a SUID binary (e.g., copy /bin/bash and chmod 6777)"
echo " and execute it from the host to obtain root. Ensure proper authorization before testing."
else
print_list "Note"; echo ": Current user is not root; if you obtain container root, these mounts may enable host escalation via SUID planting." | sed -${E} "s,.*,${SED_RED},"
fi
fi
echo ""
fi

View File

@@ -0,0 +1,215 @@
# Title: Processes & Cron & Services & Timers - Bash arithmetic on untrusted logs
# ID: PR_Bash_arithmetic_untrusted_logs
# Author: HT Bot
# Last Update: 2025-08-30
# Description: Heuristic detection of root-run periodic scripts (cron/timers) that perform Bash arithmetic on variables sourced from logs or user-controlled files. Flags common patterns that enable command substitution inside arithmetic ((...)), let, or declare -i when parsing log lines/arguments.
# License: GNU GPL
# Version: 1.0
# Functions Used: print_2title, print_3title, print_info
# Global Variables: $SEARCH_IN_FOLDER
# Initial Functions:
# Generated Global Variables: $cron_file, $line, $tok, $script, $args, $arg, $timer_unit, $service_unit, $exec_line, $user_field, $cand, $match_lines, $severity, $msg, $file, $sev_and_lines, $candidates_tmp
# Fat linpeas: 0
# Small linpeas: 1
if ! [ "$SEARCH_IN_FOLDER" ]; then
print_2title "Potential Bash arithmetic injection in root-run parsers (cron/timers)"
print_info "https://book.hacktricks.wiki/en/linux-hardening/privilege-escalation/index.html#scheduledcron-jobs"
# Small helpers -----------------------------------------------------------
uniq_print_cand() {
# de-duplicate candidate scripts
# usage: uniq_print_cand <path>
cand="$1"
[ -z "$cand" ] && return
[ ! -f "$cand" ] && return
# only consider text-ish files to keep it fast
if grep -Iq . "$cand" 2>/dev/null; then
printf '%s\n' "$cand"
fi
}
is_shell_script() {
# returns 0 if file looks like a shell script (shebang or .sh), else 1
[ ! -f "$1" ] && return 1
head -n1 "$1" 2>/dev/null | grep -Eq '(/bash|/sh|/dash|/zsh|/ksh)' && return 0
printf '%s' "$1" | grep -qE '\\.sh(\\.|$)' && return 0
return 1
}
find_arith_patterns() {
# echo severity and matched lines for arithmetic patterns
# severity: STRONG if command substitution inside arithmetic, WEAK if variable arithmetic
file="$1"
# Strong: command substitution inside arithmetic context
if grep -nE '\\(\\(.*\\$\\(.*\\).*\\)\\)|\\blet\\b[^#]*\\$\\(.*\\)|declare[[:space:]]+-i[[:space:]]+[A-Za-z_][A-Za-z0-9_]*=[^#]*\\$\\(.*\\)' "$file" 2>/dev/null | head -n 3 | sed 's/^/ /' ; then
severity="STRONG"
match_lines=$(grep -nE '\\(\\(.*\\$\\(.*\\).*\\)\\)|\\blet\\b[^#]*\\$\\(.*\\)|declare[[:space:]]+-i[[:space:]]+[A-Za-z_][A-Za-z0-9_]*=[^#]*\\$\\(.*\\)' "$file" 2>/dev/null | head -n 3)
printf 'STRONG\n'; printf '%s\n' "$match_lines"
return 0
fi
# Weak: arithmetic with unquoted variables that could be attacker-controlled
if grep -nE '\\(\\([^#]*\\$[A-Za-z_][A-Za-z0-9_]*[^#]*\\)\\)|\\blet\\b[^#]*\\$[A-Za-z_][A-Za-z0-9_]*|declare[[:space:]]+-i[[:space:]]+[A-Za-z_][A-Za-z0-9_]*=[^#]*\\$[A-Za-z_][A-Za-z0-9_]*' "$file" 2>/dev/null | head -n 3 | sed 's/^/ /' ; then
severity="WEAK"
match_lines=$(grep -nE '\\(\\([^#]*\\$[A-Za-z_][A-Za-z0-9_]*[^#]*\\)\\)|\\blet\\b[^#]*\\$[A-Za-z_][A-Za-z0-9_]*|declare[[:space:]]+-i[[:space:]]+[A-Za-z_][A-Za-z0-9_]*=[^#]*\\$[A-Za-z_][A-Za-z0-9_]*' "$file" 2>/dev/null | head -n 3)
printf 'WEAK\n'; printf '%s\n' "$match_lines"
return 0
fi
return 1
}
looks_like_log_parsing() {
# heuristics: script references logs/WWW logs/tmp or uses variables LOG/FILE in pipelines/process substitution
file="$1"
# explicit log-like paths
if grep -qE '/var/log|/var/www/.*/log|/tmp/' "$file" 2>/dev/null; then
return 0
fi
# log-ish variables used in pipelines or redirections
if grep -qE '(LOG_FILE|LOG|FILE)=' "$file" 2>/dev/null && \
grep -qE '(grep|awk|sed|cut|tail|head)[^\n]*\\$[A-Za-z_][A-Za-z0-9_]*' "$file" 2>/dev/null; then
return 0
fi
# process substitution with grep/cat reading from variable
grep -qE '(<\\(|<\\s*[^<])' "$file" 2>/dev/null && \
grep -qE 'grep[^\n]*\\$[A-Za-z_][A-Za-z0-9_]*' "$file" 2>/dev/null && return 0
return 1
}
print_writable_arg_info() {
# Given an argument path, print if current user can write the file or its parent dir
arg="$1"
[ -z "$arg" ] && return
case "$arg" in
/*)
if [ -e "$arg" ]; then
if [ -w "$arg" ]; then
echo " Writable argument file: $arg"; ls -l "$arg" 2>/dev/null | sed 's/^/ /'
fi
else
d=$(dirname -- "$arg")
if [ -d "$d" ] && [ -w "$d" ]; then
echo " Parent dir writable: $d (arg $arg does not exist)"
ls -ld "$d" 2>/dev/null | sed 's/^/ /'
fi
fi
;;
esac
}
# Collect candidate scripts from cron ------------------------------------------------
candidates_tmp=$(mktemp 2>/dev/null || echo "/tmp/.lp.cand.$$")
: > "$candidates_tmp"
# Root crontabs and system cron files
for cron_file in /etc/crontab /var/spool/cron/crontabs/root /etc/cron.d/*; do
[ -r "$cron_file" ] || continue
# Iterate non-comment lines
while IFS= read -r line || [ -n "$line" ]; do
case "$line" in
\#*|"") continue ;;
esac
# Extract absolute paths from the line
# First token that is an absolute path is likely the script/binary
script=""
args=""
# Get all absolute-like tokens
for tok in $(printf '%s\n' "$line" | grep -oE '/[^[:space:]]+'); do
if [ -z "$script" ]; then
script="$tok"
else
args="$args $tok"
fi
done
if [ -n "$script" ]; then
uniq_print_cand "$script" >> "$candidates_tmp"
# If script is run by root (likely in these files), show writable argument hints now
if [ -n "$args" ]; then
echo "Root cron entry: $script$args"
for arg in $args; do
print_writable_arg_info "$arg"
done
fi
fi
done < "$cron_file"
done
# run-parts style cron directories (executed as root by system crond/anacron)
for d in /etc/cron.daily /etc/cron.hourly /etc/cron.weekly /etc/cron.monthly; do
[ -d "$d" ] || continue
for f in "$d"/*; do
[ -f "$f" ] || continue
uniq_print_cand "$f" >> "$candidates_tmp"
done
done
# Collect candidate scripts from systemd timers --------------------------------------
if command -v systemctl >/dev/null 2>&1; then
systemctl list-timers --all 2>/dev/null | awk 'NR>1 {print $1}' | grep -E '\\.timer$' | while read -r timer_unit; do
[ -z "$timer_unit" ] && continue
service_unit=$(systemctl show "$timer_unit" -p Unit 2>/dev/null | cut -d= -f2)
[ -z "$service_unit" ] && continue
user_field=$(systemctl show "$service_unit" -p User 2>/dev/null | cut -d= -f2)
# Default user for services without User= is root; include both empty and root
if [ -z "$user_field" ] || [ "$user_field" = "root" ]; then
exec_line=$(systemctl show "$service_unit" -p ExecStart 2>/dev/null | cut -d= -f2)
# Extract first absolute path as executable/script and any absolute path args
script=""
args=""
for tok in $(printf '%s\n' "$exec_line" | grep -oE '/[^[:space:]]+'); do
if [ -z "$script" ]; then
script="$tok"
else
args="$args $tok"
fi
done
if [ -n "$script" ]; then
uniq_print_cand "$script" >> "$candidates_tmp"
if [ -n "$args" ]; then
echo "Root timer entry: $script$args"
for arg in $args; do
print_writable_arg_info "$arg"
done
fi
fi
fi
done
fi
# Evaluate candidates ---------------------------------------------------------------
if [ -s "$candidates_tmp" ]; then
print_3title "Reviewing root-run scripts for arithmetic on untrusted input"
sort -u "$candidates_tmp" | while read -r script; do
[ -z "$script" ] && continue
[ -r "$script" ] || continue
is_shell_script "$script" || continue
sev_and_lines=$(find_arith_patterns "$script")
if [ $? -eq 0 ]; then
severity=$(printf '%s' "$sev_and_lines" | head -n1)
match_lines=$(printf '%s' "$sev_and_lines" | tail -n +2)
if looks_like_log_parsing "$script"; then
echo "[!] $severity risk: arithmetic evaluation with variables in $script"
printf '%s\n' "$match_lines" | sed 's/^/ /'
# Bonus: try to spot obvious log file variables
if grep -qE 'LOG_FILE|LOG_PATH|LOG' "$script" 2>/dev/null; then
msg=$(grep -nE 'LOG_FILE|LOG_PATH|LOG' "$script" 2>/dev/null | head -n 2)
printf '%s\n' "$msg" | sed 's/^/ hint: /'
fi
fi
fi
done
fi
rm -f "$candidates_tmp" 2>/dev/null
echo ""
else
# Folder analysis mode: just list potential log-parsing shell scripts under the target folder
print_2title "Potential Bash arithmetic/log-parsing scripts in folder"
find "$SEARCH_IN_FOLDER" -type f -name "*.sh" -maxdepth 6 2>/dev/null \
-exec sh -c 'head -n1 "$1" 2>/dev/null | grep -Eq "(/bash|/sh|/dash|/zsh|/ksh)" || exit 1' _ {} \; \
-exec grep -Ilq . {} \; \
-exec grep -qE "\\(\\(|\\blet\\b|declare[[:space:]]+-i" {} \; \
-print
echo ""
fi

View File

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

View File

@@ -1,126 +0,0 @@
# Title: Processes & Cron & Services & Timers - Crontab UI (root) Misconfiguration
# ID: PR_Crontab_UI_misconfig
# Author: HT Bot
# Last Update: 2025-09-13
# Description: Detect Crontab UI service and risky configurations that can lead to privesc:
# - Root-run Crontab UI exposed on localhost
# - Basic-Auth credentials in systemd Environment= (BASIC_AUTH_USER/PWD)
# - Cron DB path (CRON_DB_PATH) and weak permissions / embedded secrets in jobs
# License: GNU GPL
# Version: 1.0
# Functions Used: print_2title, print_info, print_list, echo_not_found
# Global Variables: $SEARCH_IN_FOLDER, $SED_RED, $SED_RED_YELLOW, $NC
# Initial Functions:
# Generated Global Variables: $svc, $state, $user, $envvals, $port, $dbpath, $dbfile, $candidates, $procs, $perms, $basic_user, $basic_pwd, $uprint, $pprint, $dir, $found
# Fat linpeas: 0
# Small linpeas: 1
if ! [ "$SEARCH_IN_FOLDER" ]; then
print_2title "Crontab UI (root) misconfiguration checks"
print_info "https://book.hacktricks.wiki/en/linux-hardening/privilege-escalation/index.html#scheduledcron-jobs"
# Collect candidate services referencing crontab-ui
candidates=""
if command -v systemctl >/dev/null 2>&1; then
candidates=$(systemctl list-units --type=service --all 2>/dev/null | awk '{print $1}' | grep -Ei '^crontab-ui\.service$' 2>/dev/null)
fi
# Fallback: grep service files for ExecStart containing crontab-ui
if [ -z "$candidates" ]; then
for dir in /etc/systemd/system /lib/systemd/system; do
[ -d "$dir" ] || continue
found=$(grep -RIl "^Exec(Start|StartPre|StartPost)=.*crontab-ui" "$dir" 2>/dev/null | xargs -r -I{} basename {} 2>/dev/null)
if [ -n "$found" ]; then
candidates=$(printf "%s\n%s" "$candidates" "$found" | sort -u)
fi
done
fi
# Also flag if the binary exists or a process seems to be running
if command -v crontab-ui >/dev/null 2>&1; then
print_list "crontab-ui binary found at: $(command -v crontab-ui)"$NC
else
echo_not_found "crontab-ui"
fi
procs=$(ps aux 2>/dev/null | grep -E "(crontab-ui|node .*crontab-ui)" | grep -v grep)
if [ -n "$procs" ]; then
print_list "Processes matching crontab-ui? ..................... "$NC
printf "%s\n" "$procs"
echo ""
fi
# If no candidates detected, exit quietly
if [ "$candidates" ]; then
# Iterate candidates and extract interesting data
printf "%s\n" "$candidates" | while read -r svc; do
[ -n "$svc" ] || continue
# Ensure suffix .service if missing
case "$svc" in
*.service) : ;;
*) svc="$svc.service" ;;
esac
state=""
user=""
if command -v systemctl >/dev/null 2>&1; then
state=$(systemctl is-active "$svc" 2>/dev/null)
user=$(systemctl show "$svc" -p User 2>/dev/null | cut -d= -f2)
fi
[ -z "$state" ] && state="unknown"
[ -z "$user" ] && user="unknown"
echo "Service: $svc (state: $state, User: $user)" | sed -${E} "s,root,${SED_RED},g"
# Read Environment from systemd (works even if file unreadable in many setups)
envvals=$(systemctl show "$svc" -p Environment 2>/dev/null | cut -d= -f2-)
if [ -n "$envvals" ]; then
basic_user=$(printf "%s\n" "$envvals" | tr ' ' '\n' | grep -E '^BASIC_AUTH_USER=' | head -n1 | cut -d= -f2-)
basic_pwd=$(printf "%s\n" "$envvals" | tr ' ' '\n' | grep -E '^BASIC_AUTH_PWD=' | head -n1 | cut -d= -f2-)
dbpath=$(printf "%s\n" "$envvals" | tr ' ' '\n' | grep -E '^CRON_DB_PATH=' | head -n1 | cut -d= -f2-)
port=$(printf "%s\n" "$envvals" | tr ' ' '\n' | grep -E '^PORT=' | head -n1 | cut -d= -f2-)
if [ -n "$basic_user" ] || [ -n "$basic_pwd" ]; then
uprint="$basic_user"
pprint="$basic_pwd"
[ -n "$basic_pwd" ] && pprint="$basic_pwd"
echo " └─ Basic-Auth credentials in Environment: user='${uprint}' pwd='${pprint}'" | sed -${E} "s,pwd='[^']*',${SED_RED_YELLOW},g"
fi
if [ -n "$dbpath" ]; then
echo " └─ CRON_DB_PATH: $dbpath"
fi
# Check listener bound to localhost
[ -z "$port" ] && port=8000
if command -v ss >/dev/null 2>&1; then
if ss -ltn 2>/dev/null | grep -qE "127\.0\.0\.1:${port}[[:space:]]"; then
echo " └─ Listener detected on 127.0.0.1:${port} (likely Crontab UI)."
fi
else
if netstat -tnl 2>/dev/null | grep -qE "127\.0\.0\.1:${port}[[:space:]]"; then
echo " └─ Listener detected on 127.0.0.1:${port} (likely Crontab UI)."
fi
fi
# If we know DB path, try to read crontab.db for obvious secrets and check perms
if [ -n "$dbpath" ] && [ -d "$dbpath" ] && [ -r "$dbpath" ]; then
dbfile="$dbpath/crontab.db"
if [ -f "$dbfile" ]; then
perms=$(ls -ld "$dbpath" 2>/dev/null | awk '{print $1, $3, $4}')
echo " └─ DB dir perms: $perms"
if [ -w "$dbpath" ] || [ -w "$dbfile" ]; then
echo " └─ Writable by current user -> potential job injection!" | sed -${E} "s,.*,${SED_RED},g"
fi
echo " └─ Inspecting $dbfile for embedded secrets in commands (zip -P / --password / pass/token/secret)..."
grep -E "-P[[:space:]]+\S+|--password[[:space:]]+\S+|[Pp]ass(word)?|[Tt]oken|[Ss]ecret" "$dbfile" 2>/dev/null | head -n 20 | sed -${E} "s,(${SED_RED_YELLOW}),\1,g"
fi
fi
fi
echo ""
done
fi
fi

View File

@@ -1,72 +0,0 @@
# Title: Software Information - PostgreSQL Event Triggers
# ID: SI_Postgresql_Event_Triggers
# Author: HT Bot
# Last Update: 19-11-2025
# Description: Detect unsafe PostgreSQL event triggers and postgres_fdw custom scripts that grant temporary SUPERUSER
# License: GNU GPL
# Version: 1.0
# Functions Used: echo_not_found, print_2title, print_info
# Global Variables: $DEBUG, $E, $SED_GREEN, $SED_RED, $SED_YELLOW, $TIMEOUT
# Initial Functions:
# Generated Global Variables: $psql_bin, $psql_evt_output, $psql_evt_status, $psql_evt_err_line, $postgres_fdw_dirs, $postgres_fdw_hits, $old_ifs, $evtname, $enabled, $owner, $owner_is_super, $func, $func_owner, $func_owner_is_super, $IFS
# Fat linpeas: 0
# Small linpeas: 1
if [ "$DEBUG" ] || { [ "$TIMEOUT" ] && [ "$(command -v psql 2>/dev/null || echo -n '')" ]; }; then
print_2title "PostgreSQL event trigger ownership & postgres_fdw hooks"
print_info "https://book.hacktricks.wiki/en/linux-hardening/privilege-escalation/index.html#postgresql-event-triggers"
psql_bin="$(command -v psql 2>/dev/null || echo -n '')"
if [ "$TIMEOUT" ] && [ "$psql_bin" ]; then
psql_evt_output="$($TIMEOUT 5 "$psql_bin" -w -X -q -A -t -d postgres -c "WITH evt AS ( SELECT e.evtname, e.evtenabled, pg_get_userbyid(e.evtowner) AS trig_owner, tr.rolsuper AS trig_owner_super, n.nspname || '.' || p.proname AS function_name, pg_get_userbyid(p.proowner) AS func_owner, fr.rolsuper AS func_owner_super FROM pg_event_trigger e JOIN pg_proc p ON e.evtfoid = p.oid JOIN pg_namespace n ON p.pronamespace = n.oid LEFT JOIN pg_roles tr ON tr.oid = e.evtowner LEFT JOIN pg_roles fr ON fr.oid = p.proowner ) SELECT evtname || '|' || evtenabled || '|' || COALESCE(trig_owner,'?') || '|' || COALESCE(CASE WHEN trig_owner_super THEN 'yes' ELSE 'no' END,'unknown') || '|' || function_name || '|' || COALESCE(func_owner,'?') || '|' || COALESCE(CASE WHEN func_owner_super THEN 'yes' ELSE 'no' END,'unknown') FROM evt WHERE COALESCE(trig_owner_super,false) = false OR COALESCE(func_owner_super,false) = false;" 2>&1)"
psql_evt_status=$?
if [ $psql_evt_status -eq 0 ]; then
if [ "$psql_evt_output" ]; then
echo "Non-superuser-owned event triggers were found (trigger|enabled?|owner|owner_is_super|function|function_owner|fn_owner_is_super):" | sed -${E} "s,.*,${SED_RED},"
printf "%s\n" "$psql_evt_output" | while IFS='|' read evtname enabled owner owner_is_super func func_owner func_owner_is_super; do
case "$enabled" in
O) enabled="enabled" ;;
D) enabled="disabled" ;;
*) enabled="status_$enabled" ;;
esac
echo " - $evtname ($enabled) uses $func owned by $func_owner (superuser:$func_owner_is_super); trigger owner: $owner (superuser:$owner_is_super)" | sed -${E} "s,superuser:no,${SED_RED},g"
done
else
echo "No event triggers owned by non-superusers were returned." | sed -${E} "s,.*,${SED_GREEN},"
fi
else
psql_evt_err_line=$(printf '%s\n' "$psql_evt_output" | head -n1)
echo "Could not query pg_event_trigger (psql exit $psql_evt_status): $psql_evt_err_line" | sed -${E} "s,.*,${SED_YELLOW},"
fi
else
if ! [ "$TIMEOUT" ]; then
echo_not_found "timeout"
fi
if ! [ "$psql_bin" ]; then
echo_not_found "psql"
fi
fi
postgres_fdw_dirs="/etc/postgresql /var/lib/postgresql /var/lib/postgres /usr/lib/postgresql /usr/local/lib/postgresql /opt/supabase /opt/postgres /srv/postgres"
postgres_fdw_hits=""
for d in $postgres_fdw_dirs; do
if [ -d "$d" ]; then
old_ifs="$IFS"
IFS="\n"
for f in $(find "$d" -maxdepth 5 -type f \( -name '*postgres_fdw*.sql' -o -name '*postgres_fdw*.psql' -o -name 'after-create.sql' \) 2>/dev/null); do
if [ -f "$f" ] && grep -qiE "alter[[:space:]]+role[[:space:]]+postgres[[:space:]]+superuser" "$f" 2>/dev/null; then
postgres_fdw_hits="$postgres_fdw_hits\n$f"
fi
done
IFS="$old_ifs"
fi
done
if [ "$postgres_fdw_hits" ]; then
echo "Detected postgres_fdw custom scripts granting postgres SUPERUSER (check for SupaPwn-style window):" | sed -${E} "s,.*,${SED_RED},"
printf "%s\n" "$postgres_fdw_hits" | sed "s,^, - ,"
fi
fi
echo ""

View File

@@ -29,7 +29,7 @@ fi
peass{SSH}
grep "PermitRootLogin \|ChallengeResponseAuthentication \|PasswordAuthentication \|UsePAM \|Port\|PermitEmptyPasswords\|PubkeyAuthentication\|ListenAddress\|ForwardAgent\|AllowAgentForwarding\|AuthorizedKeysFile" /etc/ssh/sshd_config 2>/dev/null | grep -v "#" | sed -${E} "s,PermitRootLogin.*es|PermitEmptyPasswords.*es|ChallengeResponseAuthentication.*es|FordwardAgent.*es,${SED_RED},"
grep "PermitRootLogin \|ChallengeResponseAuthentication \|PasswordAuthentication \|UsePAM \|Port\|PermitEmptyPasswords\|PubkeyAuthentication\|ListenAddress\|ForwardAgent\|AllowAgentForwarding\|AuthorizedKeysFiles" /etc/ssh/sshd_config 2>/dev/null | grep -v "#" | sed -${E} "s,PermitRootLogin.*es|PermitEmptyPasswords.*es|ChallengeResponseAuthentication.*es|FordwardAgent.*es,${SED_RED},"
if ! [ "$SEARCH_IN_FOLDER" ]; then
if [ "$TIMEOUT" ]; then

View File

@@ -371,7 +371,7 @@ echo ""
printf ${BLUE}"Linux Privesc Checklist: ${YELLOW}https://book.hacktricks.wiki/en/linux-hardening/linux-privilege-escalation-checklist.html\n"$NC
echo " LEGEND:" | sed "s,LEGEND,${C}[1;4m&${C}[0m,"
echo " RED/YELLOW: 95% a PE vector" | sed "s,RED/YELLOW,${SED_RED_YELLOW},"
echo " RED: You should take a look into it" | sed "s,RED,${SED_RED},"
echo " RED: You should take a look to it" | sed "s,RED,${SED_RED},"
echo " LightCyan: Users with console" | sed "s,LightCyan,${SED_LIGHT_CYAN},"
echo " Blue: Users without console & mounted devs" | sed "s,Blue,${SED_BLUE},"
echo " Green: Common things (users, groups, SUID/SGID, mounts, .sh scripts, cronjobs) " | sed "s,Green,${SED_GREEN},"
@@ -514,4 +514,4 @@ else
HOMESEARCH="$HOME $HOMESEARCH"
fi
fi
GREPHOMESEARCH=$(echo "$HOMESEARCH" | sed 's/ *$//g' | tr " " "|") #Remove ending spaces before putting "|"
GREPHOMESEARCH=$(echo "$HOMESEARCH" | sed 's/ *$//g' | tr " " "|") #Remove ending spaces before putting "|"

View File

@@ -13,5 +13,5 @@
# Small linpeas: 1
sudoVB1=" \*|env_keep\W*\+=.*LD_PRELOAD|env_keep\W*\+=.*LD_LIBRARY_PATH|env_keep\W*\+=.*BASH_ENV|env_keep\W*\+=.* ENV|peass{SUDOVB1_HERE}"
sudoVB2="peass{SUDOVB2_HERE}"
sudoVB1=" \*|env_keep\W*\+=.*LD_PRELOAD|env_keep\W*\+=.*LD_LIBRARY_PATH|peass{SUDOVB1_HERE}"
sudoVB2="peass{SUDOVB2_HERE}"

View File

@@ -1,7 +1,7 @@
# Title: Variables - sudovB
# ID: sudovB
# Author: Carlos Polop
# Last Update: 04-10-2025
# Last Update: 22-08-2023
# Description: Sudo version bad regex
# License: GNU GPL
# Version: 1.0
@@ -13,4 +13,4 @@
# Small linpeas: 1
sudovB="[01].[012345678].[0-9]+|1.9.[01234][^0-9]|1.9.[01234]$|1.9.5p1|1\.9\.[6-9]|1\.9\.1[0-7]"
sudovB="[01].[012345678].[0-9]+|1.9.[01234][^0-9]|1.9.[01234]$|1.9.5p1"

View File

@@ -270,7 +270,7 @@ class MetasploitModule < Msf::Post
if datastore['CUSTOM_URL'] != ""
url_peass = datastore['CUSTOM_URL']
else
url_peass = datastore['WINPEASS'].to_s.strip.downcase == 'true' ? "https://github.com/peass-ng/PEASS-ng/releases/latest/download/winPEASany_ofs.exe" : "https://github.com/peass-ng/PEASS-ng/releases/latest/download/linpeas.sh"
url_peass = datastore['WINPEASS'] ? "https://github.com/peass-ng/PEASS-ng/releases/latest/download/winPEASany_ofs.exe" : "https://github.com/peass-ng/PEASS-ng/releases/latest/download/linpeas.sh"
end
# If URL is set, check if it is a valid URL or local file
if url_peass.include?("http://") || url_peass.include?("https://")

View File

@@ -69,62 +69,57 @@ ECHO.
CALL :T_Progress 2
:ListHotFixes
where wmic >nul 2>&1
if %errorlevel% equ 0 (
wmic qfe get Caption,Description,HotFixID,InstalledOn | more
) else (
powershell -command "Get-HotFix | Format-Table -AutoSize"
)
wmic qfe get Caption,Description,HotFixID,InstalledOn | more
set expl=no
for /f "tokens=3-9" %%a in ('systeminfo') do (ECHO."%%a %%b %%c %%d %%e %%f %%g" | findstr /i "2000 XP 2003 2008 vista" && set expl=yes) & (ECHO."%%a %%b %%c %%d %%e %%f %%g" | findstr /i /C:"windows 7" && set expl=yes)
IF "%expl%" == "yes" ECHO. [i] Possible exploits (https://github.com/codingo/OSCP-2/blob/master/Windows/WinPrivCheck.bat)
IF "%expl%" == "yes" wmic qfe get Caption,Description,HotFixID,InstalledOn 2>nul | findstr /C:"KB2592799" 1>NUL
IF "%expl%" == "yes" wmic qfe get Caption,Description,HotFixID,InstalledOn | findstr /C:"KB2592799" 1>NUL
IF "%expl%" == "yes" IF errorlevel 1 ECHO.MS11-080 patch is NOT installed! (Vulns: XP/SP3,2K3/SP3-afd.sys)
IF "%expl%" == "yes" wmic qfe get Caption,Description,HotFixID,InstalledOn 2>nul | findstr /C:"KB3143141" 1>NUL
IF "%expl%" == "yes" wmic qfe get Caption,Description,HotFixID,InstalledOn | findstr /C:"KB3143141" 1>NUL
IF "%expl%" == "yes" IF errorlevel 1 ECHO.MS16-032 patch is NOT installed! (Vulns: 2K8/SP1/2,Vista/SP2,7/SP1-secondary logon)
IF "%expl%" == "yes" wmic qfe get Caption,Description,HotFixID,InstalledOn 2>nul | findstr /C:"KB2393802" 1>NUL
IF "%expl%" == "yes" wmic qfe get Caption,Description,HotFixID,InstalledOn | findstr /C:"KB2393802" 1>NUL
IF "%expl%" == "yes" IF errorlevel 1 ECHO.MS11-011 patch is NOT installed! (Vulns: XP/SP2/3,2K3/SP2,2K8/SP2,Vista/SP1/2,7/SP0-WmiTraceMessageVa)
IF "%expl%" == "yes" wmic qfe get Caption,Description,HotFixID,InstalledOn 2>nul | findstr /C:"KB982799" 1>NUL
IF "%expl%" == "yes" wmic qfe get Caption,Description,HotFixID,InstalledOn | findstr /C:"KB982799" 1>NUL
IF "%expl%" == "yes" IF errorlevel 1 ECHO.MS10-59 patch is NOT installed! (Vulns: 2K8,Vista,7/SP0-Chimichurri)
IF "%expl%" == "yes" wmic qfe get Caption,Description,HotFixID,InstalledOn 2>nul | findstr /C:"KB979683" 1>NUL
IF "%expl%" == "yes" wmic qfe get Caption,Description,HotFixID,InstalledOn | findstr /C:"KB979683" 1>NUL
IF "%expl%" == "yes" IF errorlevel 1 ECHO.MS10-21 patch is NOT installed! (Vulns: 2K/SP4,XP/SP2/3,2K3/SP2,2K8/SP2,Vista/SP0/1/2,7/SP0-Win Kernel)
IF "%expl%" == "yes" wmic qfe get Caption,Description,HotFixID,InstalledOn 2>nul | findstr /C:"KB2305420" 1>NUL
IF "%expl%" == "yes" wmic qfe get Caption,Description,HotFixID,InstalledOn | findstr /C:"KB2305420" 1>NUL
IF "%expl%" == "yes" IF errorlevel 1 ECHO.MS10-092 patch is NOT installed! (Vulns: 2K8/SP0/1/2,Vista/SP1/2,7/SP0-Task Sched)
IF "%expl%" == "yes" wmic qfe get Caption,Description,HotFixID,InstalledOn 2>nul | findstr /C:"KB981957" 1>NUL
IF "%expl%" == "yes" wmic qfe get Caption,Description,HotFixID,InstalledOn | findstr /C:"KB981957" 1>NUL
IF "%expl%" == "yes" IF errorlevel 1 ECHO.MS10-073 patch is NOT installed! (Vulns: XP/SP2/3,2K3/SP2/2K8/SP2,Vista/SP1/2,7/SP0-Keyboard Layout)
IF "%expl%" == "yes" wmic qfe get Caption,Description,HotFixID,InstalledOn 2>nul | findstr /C:"KB4013081" 1>NUL
IF "%expl%" == "yes" wmic qfe get Caption,Description,HotFixID,InstalledOn | findstr /C:"KB4013081" 1>NUL
IF "%expl%" == "yes" IF errorlevel 1 ECHO.MS17-017 patch is NOT installed! (Vulns: 2K8/SP2,Vista/SP2,7/SP1-Registry Hive Loading)
IF "%expl%" == "yes" wmic qfe get Caption,Description,HotFixID,InstalledOn 2>nul | findstr /C:"KB977165" 1>NUL
IF "%expl%" == "yes" wmic qfe get Caption,Description,HotFixID,InstalledOn | findstr /C:"KB977165" 1>NUL
IF "%expl%" == "yes" IF errorlevel 1 ECHO.MS10-015 patch is NOT installed! (Vulns: 2K,XP,2K3,2K8,Vista,7-User Mode to Ring)
IF "%expl%" == "yes" wmic qfe get Caption,Description,HotFixID,InstalledOn 2>nul | findstr /C:"KB941693" 1>NUL
IF "%expl%" == "yes" wmic qfe get Caption,Description,HotFixID,InstalledOn | findstr /C:"KB941693" 1>NUL
IF "%expl%" == "yes" IF errorlevel 1 ECHO.MS08-025 patch is NOT installed! (Vulns: 2K/SP4,XP/SP2,2K3/SP1/2,2K8/SP0,Vista/SP0/1-win32k.sys)
IF "%expl%" == "yes" wmic qfe get Caption,Description,HotFixID,InstalledOn 2>nul | findstr /C:"KB920958" 1>NUL
IF "%expl%" == "yes" wmic qfe get Caption,Description,HotFixID,InstalledOn | findstr /C:"KB920958" 1>NUL
IF "%expl%" == "yes" IF errorlevel 1 ECHO.MS06-049 patch is NOT installed! (Vulns: 2K/SP4-ZwQuerySysInfo)
IF "%expl%" == "yes" wmic qfe get Caption,Description,HotFixID,InstalledOn 2>nul | findstr /C:"KB914389" 1>NUL
IF "%expl%" == "yes" wmic qfe get Caption,Description,HotFixID,InstalledOn | findstr /C:"KB914389" 1>NUL
IF "%expl%" == "yes" IF errorlevel 1 ECHO.MS06-030 patch is NOT installed! (Vulns: 2K,XP/SP2-Mrxsmb.sys)
IF "%expl%" == "yes" wmic qfe get Caption,Description,HotFixID,InstalledOn 2>nul | findstr /C:"KB908523" 1>NUL
IF "%expl%" == "yes" wmic qfe get Caption,Description,HotFixID,InstalledOn | findstr /C:"KB908523" 1>NUL
IF "%expl%" == "yes" IF errorlevel 1 ECHO.MS05-055 patch is NOT installed! (Vulns: 2K/SP4-APC Data-Free)
IF "%expl%" == "yes" wmic qfe get Caption,Description,HotFixID,InstalledOn 2>nul | findstr /C:"KB890859" 1>NUL
IF "%expl%" == "yes" wmic qfe get Caption,Description,HotFixID,InstalledOn | findstr /C:"KB890859" 1>NUL
IF "%expl%" == "yes" IF errorlevel 1 ECHO.MS05-018 patch is NOT installed! (Vulns: 2K/SP3/4,XP/SP1/2-CSRSS)
IF "%expl%" == "yes" wmic qfe get Caption,Description,HotFixID,InstalledOn 2>nul | findstr /C:"KB842526" 1>NUL
IF "%expl%" == "yes" wmic qfe get Caption,Description,HotFixID,InstalledOn | findstr /C:"KB842526" 1>NUL
IF "%expl%" == "yes" IF errorlevel 1 ECHO.MS04-019 patch is NOT installed! (Vulns: 2K/SP2/3/4-Utility Manager)
IF "%expl%" == "yes" wmic qfe get Caption,Description,HotFixID,InstalledOn 2>nul | findstr /C:"KB835732" 1>NUL
IF "%expl%" == "yes" wmic qfe get Caption,Description,HotFixID,InstalledOn | findstr /C:"KB835732" 1>NUL
IF "%expl%" == "yes" IF errorlevel 1 ECHO.MS04-011 patch is NOT installed! (Vulns: 2K/SP2/3/4,XP/SP0/1-LSASS service BoF)
IF "%expl%" == "yes" wmic qfe get Caption,Description,HotFixID,InstalledOn 2>nul | findstr /C:"KB841872" 1>NUL
IF "%expl%" == "yes" wmic qfe get Caption,Description,HotFixID,InstalledOn | findstr /C:"KB841872" 1>NUL
IF "%expl%" == "yes" IF errorlevel 1 ECHO.MS04-020 patch is NOT installed! (Vulns: 2K/SP4-POSIX)
IF "%expl%" == "yes" wmic qfe get Caption,Description,HotFixID,InstalledOn 2>nul | findstr /C:"KB2975684" 1>NUL
IF "%expl%" == "yes" wmic qfe get Caption,Description,HotFixID,InstalledOn | findstr /C:"KB2975684" 1>NUL
IF "%expl%" == "yes" IF errorlevel 1 ECHO.MS14-040 patch is NOT installed! (Vulns: 2K3/SP2,2K8/SP2,Vista/SP2,7/SP1-afd.sys Dangling Pointer)
IF "%expl%" == "yes" wmic qfe get Caption,Description,HotFixID,InstalledOn 2>nul | findstr /C:"KB3136041" 1>NUL
IF "%expl%" == "yes" wmic qfe get Caption,Description,HotFixID,InstalledOn | findstr /C:"KB3136041" 1>NUL
IF "%expl%" == "yes" IF errorlevel 1 ECHO.MS16-016 patch is NOT installed! (Vulns: 2K8/SP1/2,Vista/SP2,7/SP1-WebDAV to Address)
IF "%expl%" == "yes" wmic qfe get Caption,Description,HotFixID,InstalledOn 2>nul | findstr /C:"KB3057191" 1>NUL
IF "%expl%" == "yes" wmic qfe get Caption,Description,HotFixID,InstalledOn | findstr /C:"KB3057191" 1>NUL
IF "%expl%" == "yes" IF errorlevel 1 ECHO.MS15-051 patch is NOT installed! (Vulns: 2K3/SP2,2K8/SP2,Vista/SP2,7/SP1-win32k.sys)
IF "%expl%" == "yes" wmic qfe get Caption,Description,HotFixID,InstalledOn 2>nul | findstr /C:"KB2989935" 1>NUL
IF "%expl%" == "yes" wmic qfe get Caption,Description,HotFixID,InstalledOn | findstr /C:"KB2989935" 1>NUL
IF "%expl%" == "yes" IF errorlevel 1 ECHO.MS14-070 patch is NOT installed! (Vulns: 2K3/SP2-TCP/IP)
IF "%expl%" == "yes" wmic qfe get Caption,Description,HotFixID,InstalledOn 2>nul | findstr /C:"KB2778930" 1>NUL
IF "%expl%" == "yes" wmic qfe get Caption,Description,HotFixID,InstalledOn | findstr /C:"KB2778930" 1>NUL
IF "%expl%" == "yes" IF errorlevel 1 ECHO.MS13-005 patch is NOT installed! (Vulns: Vista,7,8,2008,2008R2,2012,RT-hwnd_broadcast)
IF "%expl%" == "yes" wmic qfe get Caption,Description,HotFixID,InstalledOn 2>nul | findstr /C:"KB2850851" 1>NUL
IF "%expl%" == "yes" wmic qfe get Caption,Description,HotFixID,InstalledOn | findstr /C:"KB2850851" 1>NUL
IF "%expl%" == "yes" IF errorlevel 1 ECHO.MS13-053 patch is NOT installed! (Vulns: 7SP0/SP1_x86-schlamperei)
IF "%expl%" == "yes" wmic qfe get Caption,Description,HotFixID,InstalledOn 2>nul | findstr /C:"KB2870008" 1>NUL
IF "%expl%" == "yes" wmic qfe get Caption,Description,HotFixID,InstalledOn | findstr /C:"KB2870008" 1>NUL
IF "%expl%" == "yes" IF errorlevel 1 ECHO.MS13-081 patch is NOT installed! (Vulns: 7SP0/SP1_x86-track_popup_menu)
ECHO.
CALL :T_Progress 2
@@ -202,12 +197,7 @@ CALL :T_Progress 1
:AVSettings
CALL :ColorLine " %E%33m[+]%E%97m Registered Anti-Virus(AV)"
where wmic >nul 2>&1
if %errorlevel% equ 0 (
WMIC /Node:localhost /Namespace:\\root\SecurityCenter2 Path AntiVirusProduct Get displayName /Format:List | more
) else (
powershell -command "Get-CimInstance -Namespace root/SecurityCenter2 -ClassName AntiVirusProduct | Select-Object -ExpandProperty displayName"
)
WMIC /Node:localhost /Namespace:\\root\SecurityCenter2 Path AntiVirusProduct Get displayName /Format:List | more
ECHO.Checking for defender whitelisted PATHS
reg query "HKLM\SOFTWARE\Microsoft\Windows Defender\Exclusions\Paths" 2>nul
CALL :T_Progress 1
@@ -236,12 +226,7 @@ CALL :T_Progress 3
:MountedDisks
CALL :ColorLine " %E%33m[+]%E%97m MOUNTED DISKS"
ECHO. [i] Maybe you find something interesting
where wmic >nul 2>&1
if %errorlevel% equ 0 (
wmic logicaldisk get caption | more
) else (
fsutil fsinfo drives
)
(wmic logicaldisk get caption 2>nul | more) || (fsutil fsinfo drives 2>nul)
ECHO.
CALL :T_Progress 1
@@ -288,29 +273,15 @@ tasklist /SVC
ECHO.
CALL :T_Progress 2
ECHO. [i] Checking file permissions of running processes (File backdooring - maybe the same files start automatically when Administrator logs in)
where wmic >nul 2>&1
if %errorlevel% equ 0 (
for /f "tokens=2 delims='='" %%x in ('wmic process list full ^|find /i "executablepath"^|find /i /v "system32"^|find ":"') do (
for /f eol^=^"^ delims^=^" %%z in ('ECHO.%%x') do (
icacls "%%z" 2>nul | findstr /i "(F) (M) (W) :\\" | findstr /i ":\\ everyone authenticated users todos %username%" && ECHO.
)
)
) else (
for /f "tokens=*" %%x in ('powershell -command "Get-Process | Where-Object {$_.Path -and $_.Path -notlike '*system32*'} | Select-Object -ExpandProperty Path -Unique"') do (
icacls "%%x" 2>nul | findstr /i "(F) (M) (W) :\\" | findstr /i ":\\ everyone authenticated users todos %username%" && ECHO.
for /f "tokens=2 delims='='" %%x in ('wmic process list full^|find /i "executablepath"^|find /i /v "system32"^|find ":"') do (
for /f eol^=^"^ delims^=^" %%z in ('ECHO.%%x') do (
icacls "%%z" 2>nul | findstr /i "(F) (M) (W) :\\" | findstr /i ":\\ everyone authenticated users todos %username%" && ECHO.
)
)
ECHO.
ECHO. [i] Checking directory permissions of running processes (DLL injection)
where wmic >nul 2>&1
if %errorlevel% equ 0 (
for /f "tokens=2 delims='='" %%x in ('wmic process list full ^|find /i "executablepath"^|find /i /v "system32"^|find ":"') do for /f eol^=^"^ delims^=^" %%y in ('ECHO.%%x') do (
icacls "%%~dpy\" 2>nul | findstr /i "(F) (M) (W) :\\" | findstr /i ":\\ everyone authenticated users todos %username%" && ECHO.
)
) else (
for /f "tokens=*" %%x in ('powershell -command "Get-Process | Where-Object {$_.Path -and $_.Path -notlike '*system32*'} | Select-Object -ExpandProperty Path -Unique"') do (
for /f "delims=" %%d in ("%%~dpx") do icacls "%%d" 2>nul | findstr /i "(F) (M) (W) :\\" | findstr /i ":\\ everyone authenticated users todos %username%" && ECHO.
)
for /f "tokens=2 delims='='" %%x in ('wmic process list full^|find /i "executablepath"^|find /i /v "system32"^|find ":"') do for /f eol^=^"^ delims^=^" %%y in ('ECHO.%%x') do (
icacls "%%~dpy\" 2>nul | findstr /i "(F) (M) (W) :\\" | findstr /i ":\\ everyone authenticated users todos %username%" && ECHO.
)
ECHO.
CALL :T_Progress 3
@@ -405,7 +376,7 @@ CALL :T_Progress 1
:BasicUserInfo
CALL :ColorLine "%E%32m[*]%E%97m BASIC USER INFO
ECHO. [i] Check if you are inside the Administrators group or if you have enabled any token that can be use to escalate privileges like SeImpersonatePrivilege, SeAssignPrimaryPrivilege, SeTcbPrivilege, SeBackupPrivilege, SeRestorePrivilege, SeCreateTokenPrivilege, SeLoadDriverPrivilege, SeTakeOwnershipPrivilege, SeDebugPrivilege
ECHO. [i] Check if you are inside the Administrators group or if you have enabled any token that can be use to escalate privileges like SeImpersonatePrivilege, SeAssignPrimaryPrivilege, SeTcbPrivilege, SeBackupPrivilege, SeRestorePrivilege, SeCreateTokenPrivilege, SeLoadDriverPrivilege, SeTakeOwnershipPrivilege, SeDebbugPrivilege
ECHO. [?] https://book.hacktricks.wiki/en/windows-hardening/windows-local-privilege-escalation/index.html#users--groups
ECHO.
CALL :ColorLine " %E%33m[+]%E%97m CURRENT USER"
@@ -481,19 +452,8 @@ ECHO.
:ServiceBinaryPermissions
CALL :ColorLine " %E%33m[+]%E%97m SERVICE BINARY PERMISSIONS WITH WMIC and ICACLS"
ECHO. [?] https://book.hacktricks.wiki/en/windows-hardening/windows-local-privilege-escalation/index.html#services
where wmic >nul 2>&1
if %errorlevel% equ 0 (
for /f "tokens=2 delims='='" %%a in ('cmd.exe /c wmic service list full ^| findstr /i "pathname" ^|findstr /i /v "system32"') do (
for /f eol^=^"^ delims^=^" %%b in ("%%a") do icacls "%%b" 2>nul | findstr /i "(F) (M) (W) :\\" | findstr /i ":\\ everyone authenticated users todos usuarios %username%" && ECHO.
)
) else (
for /f "tokens=*" %%a in ('powershell -command "Get-CimInstance -ClassName Win32_Service | Where-Object {$_.PathName -and $_.PathName -notlike '*system32*'} | Select-Object -ExpandProperty PathName"') do (
for /f "tokens=1 delims= " %%b in ("%%a") do (
set "svcpath=%%b"
set "svcpath=!svcpath:~1,-1!"
if exist "!svcpath!" icacls "!svcpath!" 2>nul | findstr /i "(F) (M) (W) :\\" | findstr /i ":\\ everyone authenticated users todos usuarios %username%" && ECHO.
)
)
for /f "tokens=2 delims='='" %%a in ('cmd.exe /c wmic service list full ^| findstr /i "pathname" ^|findstr /i /v "system32"') do (
for /f eol^=^"^ delims^=^" %%b in ("%%a") do icacls "%%b" 2>nul | findstr /i "(F) (M) (W) :\\" | findstr /i ":\\ everyone authenticated users todos usuarios %username%" && ECHO.
)
ECHO.
CALL :T_Progress 1
@@ -668,29 +628,16 @@ if "%long%" == "true" (
ECHO.
ECHO. [i] Iterating through the drives
ECHO.
where wmic >nul 2>&1
if !errorlevel! equ 0 (
for /f %%x in ('wmic logicaldisk get name ^| more') do (
set tdrive=%%x
if "!tdrive:~1,2!" == ":" (
%%x
CALL :ColorLine " %E%33m[+]%E%97m FILES THAT CONTAINS THE WORD PASSWORD WITH EXTENSION: .xml .ini .txt *.cfg *.config"
findstr /s/n/m/i password *.xml *.ini *.txt *.cfg *.config 2>nul | findstr /v /i "\\AppData\\Local \\WinSxS ApnDatabase.xml \\UEV\\InboxTemplates \\Microsoft.Windows.Cloud \\Notepad\+\+\\ vmware cortana alphabet \\7-zip\\" 2>nul
ECHO.
CALL :ColorLine " %E%33m[+]%E%97m FILES WHOSE NAME CONTAINS THE WORD PASS CRED or .config not inside \Windows\"
dir /s/b *pass* == *cred* == *.config* == *.cfg 2>nul | findstr /v /i "\\windows\\"
ECHO.
)
)
) else (
for /f %%x in ('powershell -command "Get-PSDrive -PSProvider FileSystem | Where-Object {$_.Root -match ':'} | Select-Object -ExpandProperty Name"') do (
%%x:
CALL :ColorLine " %E%33m[+]%E%97m FILES THAT CONTAINS THE WORD PASSWORD WITH EXTENSION: .xml .ini .txt *.cfg *.config"
findstr /s/n/m/i password *.xml *.ini *.txt *.cfg *.config 2>nul | findstr /v /i "\\AppData\\Local \\WinSxS ApnDatabase.xml \\UEV\\InboxTemplates \\Microsoft.Windows.Cloud \\Notepad\+\+\\ vmware cortana alphabet \\7-zip\\" 2>nul
ECHO.
CALL :ColorLine " %E%33m[+]%E%97m FILES WHOSE NAME CONTAINS THE WORD PASS CRED or .config not inside \Windows\"
dir /s/b *pass* == *cred* == *.config* == *.cfg 2>nul | findstr /v /i "\\windows\\"
ECHO.
for /f %%x in ('wmic logicaldisk get name^| more') do (
set tdrive=%%x
if "!tdrive:~1,2!" == ":" (
%%x
CALL :ColorLine " %E%33m[+]%E%97m FILES THAT CONTAINS THE WORD PASSWORD WITH EXTENSION: .xml .ini .txt *.cfg *.config"
findstr /s/n/m/i password *.xml *.ini *.txt *.cfg *.config 2>nul | findstr /v /i "\\AppData\\Local \\WinSxS ApnDatabase.xml \\UEV\\InboxTemplates \\Microsoft.Windows.Cloud \\Notepad\+\+\\ vmware cortana alphabet \\7-zip\\" 2>nul
ECHO.
CALL :ColorLine " %E%33m[+]%E%97m FILES WHOSE NAME CONTAINS THE WORD PASS CRED or .config not inside \Windows\"
dir /s/b *pass* == *cred* == *.config* == *.cfg 2>nul | findstr /v /i "\\windows\\"
ECHO.
)
)
CALL :T_Progress 2
@@ -707,8 +654,7 @@ EXIT /B
:SetOnce
REM :: ANSI escape character is set once below - for ColorLine Subroutine
for /F %%a in ('echo prompt $E ^| cmd') do set "ESC=%%a"
SET "E=%ESC%["
SET "E=0x1B["
SET "PercentageTrack=0"
EXIT /B
@@ -720,5 +666,5 @@ EXIT /B
:ColorLine
SET "CurrentLine=%~1"
ECHO.!CurrentLine!
FOR /F "delims=" %%A IN ('FORFILES.EXE /P %~dp0 /M %~nx0 /C "CMD /C ECHO.!CurrentLine!"') DO ECHO.%%A
EXIT /B

View File

@@ -22,7 +22,7 @@ $url = "https://github.com/peass-ng/PEASS-ng/releases/latest/download/winPEASany
# One liner to download and execute winPEASany from memory in a PS shell
$wp=[System.Reflection.Assembly]::Load([byte[]](Invoke-WebRequest "$url" -UseBasicParsing | Select-Object -ExpandProperty Content)); [winPEAS.Program]::Main("")
# The previous cmd in 2 lines
# The cprevios cmd in 2 lines
$wp=[System.Reflection.Assembly]::Load([byte[]](Invoke-WebRequest "$url" -UseBasicParsing | Select-Object -ExpandProperty Content));
[winPEAS.Program]::Main("") #Put inside the quotes the winpeas parameters you want to use
@@ -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):

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,346 +0,0 @@
using System;
using System.Collections.Generic;
using System.DirectoryServices;
using System.Security.AccessControl;
using System.Security.Principal;
using winPEAS.Helpers;
using winPEAS.Helpers.Registry;
using winPEAS.Info.FilesInfo.Certificates;
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,
PrintAdcsMisconfigurations
}.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 misconfigurations
private void PrintAdcsMisconfigurations()
{
try
{
Beaprint.MainPrint("AD CS misconfigurations for ESC");
Beaprint.LinkPrint("https://book.hacktricks.wiki/en/windows-hardening/active-directory-methodology/ad-certificates.html");
if (!Checks.IsPartOfDomain)
{
Beaprint.GrayPrint(" [-] Host is not domain-joined. Skipping.");
return;
}
Beaprint.InfoPrint("Check for ADCS misconfigurations in the local DC registry");
bool IsDomainController = RegistryHelper.GetReg("HKLM", @"SYSTEM\CurrentControlSet\Services\NTDS")?.ValueCount > 0;
if (IsDomainController)
{
// For StrongBinding and CertificateMapping, More details in KB014754 - Registry key information:
// https://support.microsoft.com/en-us/topic/kb5014754-certificate-based-authentication-changes-on-windows-domain-controllers-ad2c23b0-15d8-4340-a468-4d4f3b188f16
uint? strongBinding = RegistryHelper.GetDwordValue("HKLM", @"SYSTEM\CurrentControlSet\Services\Kdc", "StrongCertificateBindingEnforcement");
switch (strongBinding)
{
case 0:
Beaprint.BadPrint(" StrongCertificateBindingEnforcement: 0 — Weak mapping allowed, vulnerable to ESC9.");
break;
case 2:
Beaprint.GoodPrint(" StrongCertificateBindingEnforcement: 2 — Prevents weak UPN/DNS mappings even if SID extension missing, not vulnerable to ESC9.");
break;
// 1 is default behavior now I think?
case 1:
default:
Beaprint.NoColorPrint($" StrongCertificateBindingEnforcement: {strongBinding} — Allow weak mapping if SID extension missing, may be vulnerable to ESC9.");
break;
}
uint? certMapping = RegistryHelper.GetDwordValue("HKLM", @"SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL", "CertificateMappingMethods");
if (certMapping.HasValue && (certMapping & 0x4) != 0)
Beaprint.BadPrint($" CertificateMappingMethods: {certMapping} — Allow UPN-based mapping, vulnerable to ESC10.");
else if(certMapping.HasValue && ((certMapping & 0x1) != 0 || (certMapping & 0x2) != 0))
Beaprint.NoColorPrint($" CertificateMappingMethods: {certMapping} — Allow weak Subject/Issuer certificate mapping.");
// 0x18 (strong mapping) is default behavior if not the flags above I think?
else
Beaprint.GoodPrint($" CertificateMappingMethods: {certMapping} — Strong Certificate mapping enabled.");
// We take the Active CA, can they be several?
string caName = RegistryHelper.GetRegValue("HKLM", $@"SYSTEM\CurrentControlSet\Services\CertSvc\Configuration", "Active");
if (!string.IsNullOrWhiteSpace(caName))
{
// Obscure Source for InterfaceFlag Enum:
// https://www.sysadmins.lv/apidocs/pki/html/T_PKI_CertificateServices_Flags_InterfaceFlagEnum.htm
uint? interfaceFlags = RegistryHelper.GetDwordValue("HKLM", $@"SYSTEM\CurrentControlSet\Services\CertSvc\Configuration\{caName}", "InterfaceFlags");
if (!interfaceFlags.HasValue || (interfaceFlags & 512) == 0)
Beaprint.BadPrint(" IF_ENFORCEENCRYPTICERTREQUEST not set in InterfaceFlags — vulnerable to ESC11.");
else
Beaprint.GoodPrint(" IF_ENFORCEENCRYPTICERTREQUEST set in InterfaceFlags — not vulnerable to ESC11.");
string policyModule = RegistryHelper.GetRegValue("HKLM", $@"SYSTEM\CurrentControlSet\Services\CertSvc\Configuration\{caName}\PolicyModules", "Active");
if (!string.IsNullOrWhiteSpace(policyModule))
{
string disableExtensionList = RegistryHelper.GetRegValue("HKLM", $@"SYSTEM\CurrentControlSet\Services\CertSvc\Configuration\{caName}\PolicyModules\{policyModule}", "DisableExtensionList");
// zOID_NTDS_CA_SECURITY_EXT (OID 1.3.6.1.4.1.311.25.2)
if (disableExtensionList?.Contains("1.3.6.1.4.1.311.25.2") == true)
Beaprint.BadPrint(" szOID_NTDS_CA_SECURITY_EXT disabled for the entire CA — vulnerable to ESC16.");
else
Beaprint.GoodPrint(" szOID_NTDS_CA_SECURITY_EXT not disabled for the CA — not vulnerable to ESC16.");
}
else
{
Beaprint.GrayPrint(" [-] Policy Module not found. Skipping.");
}
}
else
{
Beaprint.GrayPrint(" [-] Certificate Authority not found. Skipping.");
}
}
else
{
Beaprint.GrayPrint(" [-] Host is not a domain controller. Skipping ADCS Registry check");
}
// Detect AD CS certificate templates where current principal has dangerous control rights(ESC4 - style)
Beaprint.InfoPrint("\nIf you can modify a template (WriteDacl/WriteOwner/GenericAll), you can abuse ESC4");
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);
}
}
}
}

View File

@@ -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()),

View File

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

View File

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

View File

@@ -19,14 +19,6 @@ Download the **[latest releas from here](https://github.com/peass-ng/PEASS-ng/re
powershell "IEX(New-Object Net.WebClient).downloadString('https://raw.githubusercontent.com/peass-ng/PEASS-ng/master/winPEAS/winPEASps1/winPEAS.ps1')"
```
## Recent Updates
- Added Active Directory awareness checks to highlight Kerberos-only environments (NTLM restrictions) and time skew issues before attempting ticket-based attacks.
- winPEAS.ps1 now reviews AD-integrated DNS ACLs to flag zones where low-privileged users can register/modify records (dynamic DNS hijack risk).
- Enumerates high-value SPN accounts and weak gMSA password readers so you can immediately target Kerberoastable admins or abused service accounts.
- Surfaces Schannel certificate mapping settings to warn about ESC10-style certificate abuse opportunities when UPN mapping is enabled.
## Advisory
All the scripts/binaries of the PEAS Suite should be used for authorized penetration testing and/or educational purposes only. Any misuse of this software will not be the responsibility of the author or of any other collaborator. Use it at your own networks and/or with the network owner's permission.

View File

@@ -148,244 +148,6 @@ function Get-ClipBoardText {
}
}
function Get-DomainContext {
try {
return [System.DirectoryServices.ActiveDirectory.Domain]::GetComputerDomain()
}
catch {
return $null
}
}
function Convert-SidToName {
param(
$SidInput
)
if ($null -eq $SidInput) { return $null }
try {
if ($SidInput -is [System.Security.Principal.SecurityIdentifier]) {
$sidObject = $SidInput
}
else {
$sidObject = New-Object System.Security.Principal.SecurityIdentifier($SidInput)
}
return $sidObject.Translate([System.Security.Principal.NTAccount]).Value
}
catch {
try { return $sidObject.Value }
catch { return [string]$SidInput }
}
}
function Get-WeakDnsUpdateFindings {
param(
[System.DirectoryServices.ActiveDirectory.Domain]$DomainContext
)
if (-not $DomainContext) { return @() }
$domainDN = $DomainContext.GetDirectoryEntry().distinguishedName
$forestDN = $DomainContext.Forest.RootDomain.GetDirectoryEntry().distinguishedName
$paths = @(
"LDAP://CN=MicrosoftDNS,DC=DomainDnsZones,$domainDN",
"LDAP://CN=MicrosoftDNS,DC=ForestDnsZones,$forestDN",
"LDAP://CN=MicrosoftDNS,$domainDN"
)
$weakPatterns = @(
"authenticated users",
"everyone",
"domain users"
)
$dangerousRights = @("GenericAll", "GenericWrite", "CreateChild", "WriteProperty", "WriteDacl", "WriteOwner")
$findings = @()
foreach ($path in $paths) {
try {
$container = New-Object System.DirectoryServices.DirectoryEntry($path)
$null = $container.NativeGuid
}
catch { continue }
$searcher = New-Object System.DirectoryServices.DirectorySearcher($container)
$searcher.Filter = "(objectClass=dnsZone)"
$searcher.PageSize = 500
$results = $searcher.FindAll()
foreach ($result in $results) {
try {
$zoneEntry = $result.GetDirectoryEntry()
$zoneEntry.Options.SecurityMasks = [System.DirectoryServices.SecurityMasks]::Dacl
$sd = $zoneEntry.ObjectSecurity
foreach ($ace in $sd.Access) {
if ($ace.AccessControlType -ne 'Allow') { continue }
$principal = Convert-SidToName $ace.IdentityReference
if (-not $principal) { continue }
$principalLower = $principal.ToLower()
if (-not ($weakPatterns | Where-Object { $principalLower -like "*${_}*" })) { continue }
$rights = $ace.ActiveDirectoryRights.ToString()
if (-not ($dangerousRights | Where-Object { $rights -like "*${_}*" })) { continue }
$findings += [pscustomobject]@{
Zone = $zoneEntry.Properties["name"].Value
Partition = $path.Split(',')[1]
Principal = $principal
Rights = $rights
}
}
}
catch { continue }
}
}
return ($findings | Sort-Object Zone, Principal -Unique)
}
function Get-GmsaReadersReport {
param(
[System.DirectoryServices.ActiveDirectory.Domain]$DomainContext
)
if (-not $DomainContext) { return @() }
$domainDN = $DomainContext.GetDirectoryEntry().distinguishedName
try {
$searcher = New-Object System.DirectoryServices.DirectorySearcher
$searcher.SearchRoot = New-Object System.DirectoryServices.DirectoryEntry("LDAP://$domainDN")
$searcher.Filter = "(&(objectClass=msDS-GroupManagedServiceAccount))"
$searcher.PageSize = 500
[void]$searcher.PropertiesToLoad.Add("sAMAccountName")
[void]$searcher.PropertiesToLoad.Add("msDS-GroupMSAMembership")
$results = $searcher.FindAll()
}
catch { return @() }
$report = @()
foreach ($result in $results) {
$name = $result.Properties["samaccountname"]
$blobs = $result.Properties["msds-groupmsamembership"]
if (-not $blobs) { continue }
$principals = @()
foreach ($blob in $blobs) {
try {
$raw = New-Object System.Security.AccessControl.RawSecurityDescriptor (, $blob)
foreach ($ace in $raw.DiscretionaryAcl) {
$sid = Convert-SidToName $ace.SecurityIdentifier
if ($sid) { $principals += $sid }
}
}
catch { continue }
}
if ($principals.Count -eq 0) { continue }
$principals = $principals | Sort-Object -Unique
$weak = $principals | Where-Object { $_ -match 'Domain Users|Authenticated Users|Everyone' }
$report += [pscustomobject]@{
Account = ($name | Select-Object -First 1)
Allowed = ($principals -join ", ")
WeakPrincipals = if ($weak) { $weak -join ", " } else { "" }
}
}
return $report
}
function Get-PrivilegedSpnTargets {
param(
[System.DirectoryServices.ActiveDirectory.Domain]$DomainContext
)
if (-not $DomainContext) { return @() }
$domainDN = $DomainContext.GetDirectoryEntry().distinguishedName
$keywords = @(
"Domain Admin",
"Enterprise Admin",
"Administrators",
"Exchange",
"IT_",
"Schema Admin",
"Account Operator",
"Server Operator",
"Backup Operator",
"DnsAdmin"
)
try {
$searcher = New-Object System.DirectoryServices.DirectorySearcher
$searcher.SearchRoot = New-Object System.DirectoryServices.DirectoryEntry("LDAP://$domainDN")
$searcher.Filter = "(&(objectClass=user)(servicePrincipalName=*))"
$searcher.PageSize = 500
[void]$searcher.PropertiesToLoad.Add("sAMAccountName")
[void]$searcher.PropertiesToLoad.Add("memberOf")
$results = $searcher.FindAll()
}
catch { return @() }
$findings = @()
foreach ($res in $results) {
$groups = $res.Properties["memberof"]
if (-not $groups) { continue }
$matchedGroups = @()
foreach ($group in $groups) {
$cn = ($group -split ',')[0] -replace '^CN=',''
if ($keywords | Where-Object { $cn -like "*${_}*" }) {
$matchedGroups += $cn
}
}
if ($matchedGroups.Count -gt 0) {
$findings += [pscustomobject]@{
User = ($res.Properties["samaccountname"] | Select-Object -First 1)
Groups = ($matchedGroups | Sort-Object -Unique) -join ', '
}
}
}
return ($findings | Sort-Object User | Select-Object -First 12)
}
function Get-NtlmPolicySummary {
try {
$msv = Get-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\Lsa\MSV1_0' -ErrorAction Stop
}
catch { return $null }
$lsa = Get-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\Lsa' -ErrorAction SilentlyContinue
return [pscustomobject]@{
RestrictReceiving = $msv.RestrictReceivingNTLMTraffic
RestrictSending = $msv.RestrictSendingNTLMTraffic
LmCompatibility = if ($lsa) { $lsa.LmCompatibilityLevel } else { $null }
}
}
function Get-TimeSkewInfo {
param(
[System.DirectoryServices.ActiveDirectory.Domain]$DomainContext
)
if (-not $DomainContext) { return $null }
try {
$pdc = $DomainContext.PdcRoleOwner.Name
}
catch { return $null }
try {
$stripchart = w32tm /stripchart /computer:$pdc /dataonly /samples:3 2>$null
$sample = $stripchart | Where-Object { $_ -match ',' } | Select-Object -Last 1
if (-not $sample) { return $null }
$parts = $sample.Split(',')
if ($parts.Count -lt 2) { return $null }
$offsetString = $parts[1].Trim().TrimEnd('s')
[double]$offsetSeconds = 0
if (-not [double]::TryParse($offsetString, [ref]$offsetSeconds)) { return $null }
return [pscustomobject]@{
Source = $pdc
OffsetSeconds = $offsetSeconds
RawSample = $sample
}
}
catch {
return $null
}
}
function Get-AdcsSchannelInfo {
$info = [ordered]@{
MappingValue = $null
UpnMapping = $false
ServiceState = $null
}
try {
$schannel = Get-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL' -Name 'CertificateMappingMethods' -ErrorAction Stop
$info.MappingValue = $schannel.CertificateMappingMethods
if (($schannel.CertificateMappingMethods -band 0x4) -eq 0x4) { $info.UpnMapping = $true }
}
catch { }
$svc = Get-Service -Name certsrv -ErrorAction SilentlyContinue
if ($svc) { $info.ServiceState = $svc.Status }
return [pscustomobject]$info
}
function Search-Excel {
[cmdletbinding()]
Param (
@@ -1464,95 +1226,6 @@ Write-Host -ForegroundColor Blue "=========|| LISTENING PORTS"
Start-Process NETSTAT.EXE -ArgumentList "-ano" -Wait -NoNewWindow
######################## ACTIVE DIRECTORY / IDENTITY MISCONFIG CHECKS ########################
Write-Host ""
if ($TimeStamp) { TimeElapsed }
Write-Host -ForegroundColor Blue "=========|| ACTIVE DIRECTORY / IDENTITY MISCONFIG CHECKS"
$domainContext = Get-DomainContext
if (-not $domainContext) {
Write-Host "Host appears to be in a workgroup or the AD context could not be resolved. Skipping domain-specific checks." -ForegroundColor DarkGray
}
else {
$ntlmStatus = Get-NtlmPolicySummary
if ($ntlmStatus) {
$recvValue = if ($ntlmStatus.RestrictReceiving -ne $null) { [int]$ntlmStatus.RestrictReceiving } else { -1 }
$sendValue = if ($ntlmStatus.RestrictSending -ne $null) { [int]$ntlmStatus.RestrictSending } else { -1 }
$lmValue = if ($ntlmStatus.LmCompatibility -ne $null) { [int]$ntlmStatus.LmCompatibility } else { -1 }
$ntlmMsg = "Receiving:{0} Sending:{1} LMCompat:{2}" -f $recvValue, $sendValue, $lmValue
if ($recvValue -ge 1 -or $sendValue -ge 1 -or $lmValue -ge 5) {
Write-Host "[!] NTLM is restricted/disabled ($ntlmMsg). Expect Kerberos-only auth paths (sync time before Kerberoasting)." -ForegroundColor Yellow
}
else {
Write-Host "[i] NTLM restrictions appear relaxed ($ntlmMsg)."
}
}
$timeSkew = Get-TimeSkewInfo -DomainContext $domainContext
if ($timeSkew) {
$offsetAbs = [math]::Abs($timeSkew.OffsetSeconds)
$timeMsg = "Offset vs {0}: {1:N3}s (sample: {2})" -f $timeSkew.Source, $timeSkew.OffsetSeconds, $timeSkew.RawSample.Trim()
if ($offsetAbs -gt 5) {
Write-Host "[!] Significant Kerberos time skew detected - $timeMsg" -ForegroundColor Yellow
}
else {
Write-Host "[i] Kerberos time offset looks OK - $timeMsg"
}
}
$dnsFindings = @(Get-WeakDnsUpdateFindings -DomainContext $domainContext)
if ($dnsFindings.Count -gt 0) {
Write-Host "[!] AD-integrated DNS zones allow low-priv principals to write records (dynamic DNS hijack / service MITM risk)." -ForegroundColor Yellow
$dnsFindings | Format-Table Zone,Partition,Principal,Rights -AutoSize | Out-String | Write-Host
}
else {
Write-Host "[i] No obvious insecure dynamic DNS ACLs found with current privileges."
}
$spnFindings = @(Get-PrivilegedSpnTargets -DomainContext $domainContext)
if ($spnFindings.Count -gt 0) {
Write-Host "[!] High-value SPN accounts identified (prime Kerberoast targets):" -ForegroundColor Yellow
$spnFindings | Format-Table User,Groups -AutoSize | Out-String | Write-Host
}
else {
Write-Host "[i] No privileged SPN users detected via quick LDAP search."
}
$gmsaReport = @(Get-GmsaReadersReport -DomainContext $domainContext)
if ($gmsaReport.Count -gt 0) {
$weakGmsa = $gmsaReport | Where-Object { $_.WeakPrincipals -ne "" }
if ($weakGmsa) {
Write-Host "[!] gMSA passwords readable by low-priv groups/principals: " -ForegroundColor Yellow
$weakGmsa | Select-Object Account, WeakPrincipals | Format-Table -AutoSize | Out-String | Write-Host
}
else {
Write-Host "[i] gMSA accounts discovered (review allowed readers below)."
$gmsaReport | Select-Object Account, Allowed | Sort-Object Account | Select-Object -First 5 | Format-Table -Wrap | Out-String | Write-Host
}
}
else {
Write-Host "[i] No gMSA objects found via LDAP."
}
$adcsInfo = Get-AdcsSchannelInfo
if ($adcsInfo.MappingValue -ne $null) {
$hex = ('0x{0:X}' -f [int]$adcsInfo.MappingValue)
if ($adcsInfo.UpnMapping) {
Write-Host ("[!] Schannel CertificateMappingMethods={0} (UPN mapping allowed) - ESC10 certificate abuse possible if you can edit another user's UPN." -f $hex) -ForegroundColor Yellow
}
else {
Write-Host ("[i] Schannel CertificateMappingMethods={0} (UPN mapping flag not set)." -f $hex)
}
if ($adcsInfo.ServiceState) {
Write-Host ("[i] AD CS service state: {0}" -f $adcsInfo.ServiceState)
}
}
else {
Write-Host "[i] Could not read Schannel certificate mapping configuration." -ForegroundColor DarkGray
}
}
Write-Host ""
if ($TimeStamp) { TimeElapsed }
Write-Host -ForegroundColor Blue "=========|| ARP Table"
@@ -1650,7 +1323,7 @@ Write-Host -ForegroundColor Blue "=========|| WHOAMI INFO"
Write-Host ""
if ($TimeStamp) { TimeElapsed }
Write-Host -ForegroundColor Blue "=========|| Check Token access here: https://book.hacktricks.wiki/en/windows-hardening/windows-local-privilege-escalation/privilege-escalation-abusing-tokens.html#abusing-tokens" -ForegroundColor yellow
Write-Host -ForegroundColor Blue "=========|| Check if you are inside the Administrators group or if you have enabled any token that can be use to escalate privileges like SeImpersonatePrivilege, SeAssignPrimaryPrivilege, SeTcbPrivilege, SeBackupPrivilege, SeRestorePrivilege, SeCreateTokenPrivilege, SeLoadDriverPrivilege, SeTakeOwnershipPrivilege, SeDebugPrivilege"
Write-Host -ForegroundColor Blue "=========|| Check if you are inside the Administrators group or if you have enabled any token that can be use to escalate privileges like SeImpersonatePrivilege, SeAssignPrimaryPrivilege, SeTcbPrivilege, SeBackupPrivilege, SeRestorePrivilege, SeCreateTokenPrivilege, SeLoadDriverPrivilege, SeTakeOwnershipPrivilege, SeDebbugPrivilege"
Write-Host "https://book.hacktricks.wiki/en/windows-hardening/windows-local-privilege-escalation/index.html#users--groups" -ForegroundColor Yellow
Start-Process whoami.exe -ArgumentList "/all" -Wait -NoNewWindow