mirror of
https://github.com/carlospolop/privilege-escalation-awesome-scripts-suite.git
synced 2025-12-12 11:49:00 +00:00
Compare commits
67 Commits
codex/find
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8f017f98d3 | ||
|
|
17cfc6c56e | ||
|
|
7e0f678f33 | ||
|
|
595e021864 | ||
|
|
94e84dec91 | ||
|
|
ac80ce3a9a | ||
|
|
313fe6bef5 | ||
|
|
11c0d14561 | ||
|
|
49db1df468 | ||
|
|
80318c5005 | ||
|
|
7af6c33d39 | ||
|
|
336c53a163 | ||
|
|
6877f39193 | ||
|
|
d75525ebbc | ||
|
|
29d8132d93 | ||
|
|
c16c5de36f | ||
|
|
be3fe91da4 | ||
|
|
b8b4a0fc14 | ||
|
|
7042a182df | ||
|
|
c83eef9cd8 | ||
|
|
e15a1f2e12 | ||
|
|
24e9c54290 | ||
|
|
bdb5c61dad | ||
|
|
ee83c23a74 | ||
|
|
7b36014699 | ||
|
|
6fe8304783 | ||
|
|
262feb9896 | ||
|
|
40cf08af85 | ||
|
|
7c9f431649 | ||
|
|
31bdb339d7 | ||
|
|
bdcebadde0 | ||
|
|
4b3f4aa19e | ||
|
|
7c7884fb72 | ||
|
|
35300e499b | ||
|
|
147de0fc88 | ||
|
|
afaf596342 | ||
|
|
215c5d074e | ||
|
|
ca383a4548 | ||
|
|
46264bf239 | ||
|
|
642c33304f | ||
|
|
54d861ab04 | ||
|
|
bbb932d6d3 | ||
|
|
626ea2d298 | ||
|
|
ed01b32a95 | ||
|
|
c314cfd23d | ||
|
|
cc5ab76991 | ||
|
|
36001d644e | ||
|
|
fdd414f4aa | ||
|
|
c3e50dbdbf | ||
|
|
41128808a6 | ||
|
|
6fd96f4bdb | ||
|
|
a745f00dd7 | ||
|
|
933e12d7f1 | ||
|
|
4061cef7e8 | ||
|
|
b66ced3c63 | ||
|
|
cde725dacc | ||
|
|
f0f829890c | ||
|
|
99c36b8562 | ||
|
|
a74c6c820f | ||
|
|
53fd4d8dc8 | ||
|
|
9b37fd4ef4 | ||
|
|
f27b1d4816 | ||
|
|
d335b9254f | ||
|
|
d5e3c2a885 | ||
|
|
4af321d138 | ||
|
|
4e556fd594 | ||
|
|
859a44230d |
201
.github/workflows/PR-tests.yml
vendored
Normal file
201
.github/workflows/PR-tests.yml
vendored
Normal file
@@ -0,0 +1,201 @@
|
|||||||
|
name: PR-tests
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
- main
|
||||||
|
paths-ignore:
|
||||||
|
- '.github/**'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
Build_and_test_winpeas_pr:
|
||||||
|
runs-on: windows-latest
|
||||||
|
|
||||||
|
# environment variables
|
||||||
|
env:
|
||||||
|
Solution_Path: 'winPEAS\winPEASexe\winPEAS.sln'
|
||||||
|
Configuration: 'Release'
|
||||||
|
|
||||||
|
steps:
|
||||||
|
# checkout
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@master
|
||||||
|
with:
|
||||||
|
ref: ${{ github.head_ref }}
|
||||||
|
|
||||||
|
- name: Download regexes
|
||||||
|
run: |
|
||||||
|
powershell.exe -ExecutionPolicy Bypass -File build_lists/download_regexes.ps1
|
||||||
|
|
||||||
|
# Add MSBuild to the PATH
|
||||||
|
- name: Setup MSBuild.exe
|
||||||
|
uses: microsoft/setup-msbuild@v1.0.2
|
||||||
|
|
||||||
|
# Setup NuGet
|
||||||
|
- name: Setup NuGet.exe
|
||||||
|
uses: nuget/setup-nuget@v1
|
||||||
|
|
||||||
|
# Restore the packages for testing
|
||||||
|
- name: Restore the application
|
||||||
|
run: nuget restore $env:Solution_Path
|
||||||
|
|
||||||
|
# build
|
||||||
|
- name: run MSBuild
|
||||||
|
run: msbuild $env:Solution_Path
|
||||||
|
|
||||||
|
# Build all versions
|
||||||
|
- name: Build all versions
|
||||||
|
run: |
|
||||||
|
echo "build x64"
|
||||||
|
msbuild -m $env:Solution_Path /t:Rebuild /p:Configuration=$env:Configuration /p:Platform="x64"
|
||||||
|
|
||||||
|
echo "build x86"
|
||||||
|
msbuild -m $env:Solution_Path /t:Rebuild /p:Configuration=$env:Configuration /p:Platform="x86"
|
||||||
|
|
||||||
|
echo "build Any CPU"
|
||||||
|
msbuild -m $env:Solution_Path /t:Rebuild /p:Configuration=$env:Configuration /p:Platform="Any CPU"
|
||||||
|
|
||||||
|
- name: Execute winPEAS -h
|
||||||
|
shell: pwsh
|
||||||
|
run: |
|
||||||
|
$Configuration = "Release"
|
||||||
|
$exePath = "winPEAS/winPEASexe/winPEAS/bin/$Configuration/winPEAS.exe"
|
||||||
|
if (Test-Path $exePath) {
|
||||||
|
& $exePath -h
|
||||||
|
} else {
|
||||||
|
Write-Error "winPEAS.exe not found at $exePath"
|
||||||
|
}
|
||||||
|
|
||||||
|
- name: Execute winPEAS cloudinfo
|
||||||
|
shell: pwsh
|
||||||
|
run: |
|
||||||
|
$Configuration = "Release"
|
||||||
|
$exePath = "winPEAS/winPEASexe/winPEAS/bin/$Configuration/winPEAS.exe"
|
||||||
|
if (Test-Path $exePath) {
|
||||||
|
& $exePath cloudinfo
|
||||||
|
} else {
|
||||||
|
Write-Error "winPEAS.exe not found at $exePath"
|
||||||
|
}
|
||||||
|
|
||||||
|
- name: Execute winPEAS systeminfo
|
||||||
|
shell: pwsh
|
||||||
|
run: |
|
||||||
|
$Configuration = "Release"
|
||||||
|
$exePath = "winPEAS/winPEASexe/winPEAS/bin/$Configuration/winPEAS.exe"
|
||||||
|
if (Test-Path $exePath) {
|
||||||
|
& $exePath systeminfo
|
||||||
|
} else {
|
||||||
|
Write-Error "winPEAS.exe not found at $exePath"
|
||||||
|
}
|
||||||
|
|
||||||
|
- name: Execute winPEAS networkinfo
|
||||||
|
shell: pwsh
|
||||||
|
run: |
|
||||||
|
$Configuration = "Release"
|
||||||
|
$exePath = "winPEAS/winPEASexe/winPEAS/bin/$Configuration/winPEAS.exe"
|
||||||
|
if (Test-Path $exePath) {
|
||||||
|
& $exePath networkinfo
|
||||||
|
} else {
|
||||||
|
Write-Error "winPEAS.exe not found at $exePath"
|
||||||
|
}
|
||||||
|
|
||||||
|
Build_and_test_linpeas_pr:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
# Download repo
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
with:
|
||||||
|
ref: ${{ github.head_ref }}
|
||||||
|
|
||||||
|
# Setup go
|
||||||
|
- uses: actions/setup-go@v2
|
||||||
|
with:
|
||||||
|
go-version: 1.17.0-rc1
|
||||||
|
stable: false
|
||||||
|
- run: go version
|
||||||
|
|
||||||
|
# Build linpeas
|
||||||
|
- name: Build linpeas
|
||||||
|
run: |
|
||||||
|
python3 -m pip install PyYAML
|
||||||
|
cd linPEAS
|
||||||
|
python3 -m builder.linpeas_builder --all --output linpeas_fat.sh
|
||||||
|
python3 -m builder.linpeas_builder --all-no-fat --output linpeas.sh
|
||||||
|
python3 -m builder.linpeas_builder --small --output linpeas_small.sh
|
||||||
|
|
||||||
|
# Run linpeas help as quick test
|
||||||
|
- name: Run linpeas help
|
||||||
|
run: linPEAS/linpeas_fat.sh -h && linPEAS/linpeas.sh -h && linPEAS/linpeas_small.sh -h
|
||||||
|
|
||||||
|
# Run linpeas as a test
|
||||||
|
- name: Run linpeas system_information
|
||||||
|
run: linPEAS/linpeas_fat.sh -o system_information -a
|
||||||
|
|
||||||
|
- name: Run linpeas container
|
||||||
|
run: linPEAS/linpeas_fat.sh -o container -a
|
||||||
|
|
||||||
|
- name: Run linpeas cloud
|
||||||
|
run: linPEAS/linpeas_fat.sh -o cloud -a
|
||||||
|
|
||||||
|
- name: Run linpeas procs_crons_timers_srvcs_sockets
|
||||||
|
run: linPEAS/linpeas_fat.sh -o procs_crons_timers_srvcs_sockets -a
|
||||||
|
|
||||||
|
- name: Run linpeas network_information
|
||||||
|
run: linPEAS/linpeas_fat.sh -o network_information -t -a
|
||||||
|
|
||||||
|
- name: Run linpeas users_information
|
||||||
|
run: linPEAS/linpeas_fat.sh -o users_information -a
|
||||||
|
|
||||||
|
- name: Run linpeas software_information
|
||||||
|
run: linPEAS/linpeas_fat.sh -o software_information -a
|
||||||
|
|
||||||
|
- name: Run linpeas interesting_perms_files
|
||||||
|
run: linPEAS/linpeas_fat.sh -o interesting_perms_files -a
|
||||||
|
|
||||||
|
- name: Run linpeas interesting_files
|
||||||
|
run: linPEAS/linpeas_fat.sh -o interesting_files -a
|
||||||
|
|
||||||
|
Build_and_test_macpeas_pr:
|
||||||
|
runs-on: macos-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
# Download repo
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
with:
|
||||||
|
ref: ${{ github.head_ref }}
|
||||||
|
|
||||||
|
# Build linpeas (macpeas)
|
||||||
|
- name: Build macpeas
|
||||||
|
run: |
|
||||||
|
python3 -m pip install PyYAML --break-system-packages
|
||||||
|
python3 -m pip install requests --break-system-packages
|
||||||
|
cd linPEAS
|
||||||
|
python3 -m builder.linpeas_builder --all --output linpeas_fat.sh
|
||||||
|
|
||||||
|
# Run linpeas help as quick test
|
||||||
|
- name: Run macpeas help
|
||||||
|
run: linPEAS/linpeas_fat.sh -h
|
||||||
|
|
||||||
|
# Run macpeas parts to test it
|
||||||
|
- name: Run macpeas system_information
|
||||||
|
run: linPEAS/linpeas_fat.sh -o system_information -a
|
||||||
|
|
||||||
|
- name: Run macpeas container
|
||||||
|
run: linPEAS/linpeas_fat.sh -o container -a
|
||||||
|
|
||||||
|
- name: Run macpeas cloud
|
||||||
|
run: linPEAS/linpeas_fat.sh -o cloud -a
|
||||||
|
|
||||||
|
- name: Run macpeas procs_crons_timers_srvcs_sockets
|
||||||
|
run: linPEAS/linpeas_fat.sh -o procs_crons_timers_srvcs_sockets -a
|
||||||
|
|
||||||
|
- name: Run macpeas network_information
|
||||||
|
run: linPEAS/linpeas_fat.sh -o network_information -t -a
|
||||||
|
|
||||||
|
- name: Run macpeas users_information
|
||||||
|
run: linPEAS/linpeas_fat.sh -o users_information -a
|
||||||
|
|
||||||
|
- name: Run macpeas software_information
|
||||||
|
run: linPEAS/linpeas_fat.sh -o software_information -a
|
||||||
@@ -896,6 +896,14 @@ search:
|
|||||||
search_in:
|
search_in:
|
||||||
- common
|
- common
|
||||||
|
|
||||||
|
- name: "credentials.tfrc.json"
|
||||||
|
value:
|
||||||
|
type: f
|
||||||
|
bad_regex: ".*"
|
||||||
|
search_in:
|
||||||
|
- common
|
||||||
|
|
||||||
|
|
||||||
- name: Racoon
|
- name: Racoon
|
||||||
value:
|
value:
|
||||||
config:
|
config:
|
||||||
@@ -2059,6 +2067,11 @@ search:
|
|||||||
type: f
|
type: f
|
||||||
search_in:
|
search_in:
|
||||||
- common
|
- common
|
||||||
|
- name: "private-keys-v1.d/*.key"
|
||||||
|
value:
|
||||||
|
type: f
|
||||||
|
search_in:
|
||||||
|
- common
|
||||||
|
|
||||||
- name: "*.gnupg"
|
- name: "*.gnupg"
|
||||||
value:
|
value:
|
||||||
@@ -3941,3 +3954,24 @@ search:
|
|||||||
type: f
|
type: f
|
||||||
search_in:
|
search_in:
|
||||||
- common
|
- 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
|
||||||
|
|||||||
@@ -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  **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 conten!)
|
- The  **Green** color is used for known good configurations (based on the name not on the content!)
|
||||||
|
|
||||||
- The  **Blue** color is used for: Users without shell & Mounted devices
|
- The  **Blue** color is used for: Users without shell & Mounted devices
|
||||||
|
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ if __name__ == "__main__":
|
|||||||
parser.add_argument('--small', action='store_true', help='Build small version of linpeas.')
|
parser.add_argument('--small', action='store_true', help='Build small version of linpeas.')
|
||||||
parser.add_argument('--include', type=str, help='Build linpeas only with the modules indicated you can indicate section names or module IDs).')
|
parser.add_argument('--include', type=str, help='Build linpeas only with the modules indicated you can indicate section names or module IDs).')
|
||||||
parser.add_argument('--exclude', type=str, help='Exclude the given modules (you can indicate section names or module IDs).')
|
parser.add_argument('--exclude', type=str, help='Exclude the given modules (you can indicate section names or module IDs).')
|
||||||
parser.add_argument('--output', required=True, type=str, help='Parth to write the final linpeas file to.')
|
parser.add_argument('--output', required=True, type=str, help='Path to write the final linpeas file to.')
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
all_modules = args.all
|
all_modules = args.all
|
||||||
|
|||||||
@@ -0,0 +1,46 @@
|
|||||||
|
# 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
|
||||||
@@ -0,0 +1,139 @@
|
|||||||
|
# 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
|
||||||
|
|
||||||
@@ -0,0 +1,126 @@
|
|||||||
|
# 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
|
||||||
|
|
||||||
@@ -8,7 +8,7 @@
|
|||||||
# Functions Used: print_2title
|
# Functions Used: print_2title
|
||||||
# Global Variables: $DEBUG, $knw_usrs, $nosh_usrs, $sh_usrs, $DEBUG, $USER, $STRINGS
|
# Global Variables: $DEBUG, $knw_usrs, $nosh_usrs, $sh_usrs, $DEBUG, $USER, $STRINGS
|
||||||
# Initial Functions:
|
# Initial Functions:
|
||||||
# Generated Global Variables: $mysqluser, $mysqlexec, $mysqlconnect, $mysqlconnectnopass
|
# Generated Global Variables: $mysqluser, $mysqlexec, $mysqlconnect, $mysqlconnectnopass, $mysqluser, $version_output, $major_version, $version, $process_info
|
||||||
# Fat linpeas: 0
|
# Fat linpeas: 0
|
||||||
# Small linpeas: 1
|
# Small linpeas: 1
|
||||||
|
|
||||||
@@ -103,3 +103,41 @@ if [ "$(command -v mysql || echo -n '')" ] || [ "$(command -v mysqladmin || echo
|
|||||||
fi
|
fi
|
||||||
echo ""
|
echo ""
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
### This section checks if MySQL (mysqld) is running as root and if its version is 4.x or 5.x to refer a known local privilege escalation exploit! ###
|
||||||
|
|
||||||
|
# Find the mysqld process
|
||||||
|
process_info=$(ps aux | grep '[m]ysqld' | head -n1)
|
||||||
|
|
||||||
|
if [ -z "$process_info" ]; then
|
||||||
|
echo "MySQL process not found." | sed -${E} "s,.*,${SED_GREEN},"
|
||||||
|
else
|
||||||
|
|
||||||
|
# Extract the process user
|
||||||
|
mysqluser=$(echo "$process_info" | awk '{print $1}')
|
||||||
|
|
||||||
|
# Get the MySQL version string
|
||||||
|
version_output=$(mysqld --version 2>&1)
|
||||||
|
|
||||||
|
# Extract the version number (expects format like X.Y.Z)
|
||||||
|
version=$(echo "$version_output" | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -n1)
|
||||||
|
|
||||||
|
if [ -z "$version" ]; then
|
||||||
|
echo "Unable to determine MySQL version." | sed -${E} "s,.*,${SED_GREEN},"
|
||||||
|
else
|
||||||
|
|
||||||
|
# Extract the major version number (X from X.Y.Z)
|
||||||
|
major_version=$(echo "$version" | cut -d. -f1)
|
||||||
|
|
||||||
|
# Check if MySQL is running as root and if the version is either 4.x or 5.x
|
||||||
|
if [ "$mysqluser" = "root" ] && { [ "$major_version" -eq 4 ] || [ "$major_version" -eq 5 ]; }; then
|
||||||
|
echo "MySQL is running as root with version $version. This is a potential local privilege escalation vulnerability!" | sed -${E} "s,.*,${SED_RED},"
|
||||||
|
echo "\tRefer to: https://www.exploit-db.com/exploits/1518" | sed -${E} "s,.*,${SED_YELLOW},"
|
||||||
|
echo "\tRefer to: https://medium.com/r3d-buck3t/privilege-escalation-with-mysql-user-defined-functions-996ef7d5ceaf" | sed -${E} "s,.*,${SED_YELLOW},"
|
||||||
|
else
|
||||||
|
echo "MySQL is running as user '$mysqluser' with version $version." | sed -${E} "s,.*,${SED_GREEN},"
|
||||||
|
fi
|
||||||
|
### ------------------------------------------------------------------------------------------------------------------------------------------------ ###
|
||||||
|
|
||||||
|
fi
|
||||||
|
fi
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
# 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 ""
|
||||||
@@ -29,7 +29,7 @@ fi
|
|||||||
|
|
||||||
peass{SSH}
|
peass{SSH}
|
||||||
|
|
||||||
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},"
|
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},"
|
||||||
|
|
||||||
if ! [ "$SEARCH_IN_FOLDER" ]; then
|
if ! [ "$SEARCH_IN_FOLDER" ]; then
|
||||||
if [ "$TIMEOUT" ]; then
|
if [ "$TIMEOUT" ]; then
|
||||||
|
|||||||
@@ -13,5 +13,5 @@
|
|||||||
# Small linpeas: 1
|
# Small linpeas: 1
|
||||||
|
|
||||||
|
|
||||||
sudoVB1=" \*|env_keep\W*\+=.*LD_PRELOAD|env_keep\W*\+=.*LD_LIBRARY_PATH|peass{SUDOVB1_HERE}"
|
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}"
|
sudoVB2="peass{SUDOVB2_HERE}"
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
# Title: Variables - sudovB
|
# Title: Variables - sudovB
|
||||||
# ID: sudovB
|
# ID: sudovB
|
||||||
# Author: Carlos Polop
|
# Author: Carlos Polop
|
||||||
# Last Update: 22-08-2023
|
# Last Update: 04-10-2025
|
||||||
# Description: Sudo version bad regex
|
# Description: Sudo version bad regex
|
||||||
# License: GNU GPL
|
# License: GNU GPL
|
||||||
# Version: 1.0
|
# Version: 1.0
|
||||||
@@ -13,4 +13,4 @@
|
|||||||
# Small linpeas: 1
|
# Small linpeas: 1
|
||||||
|
|
||||||
|
|
||||||
sudovB="[01].[012345678].[0-9]+|1.9.[01234][^0-9]|1.9.[01234]$|1.9.5p1"
|
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]"
|
||||||
|
|||||||
@@ -292,9 +292,12 @@ class LinpeasBaseBuilder:
|
|||||||
all_module_paths += self.enumerate_directory(LINPEAS_PARTS["variables"])
|
all_module_paths += self.enumerate_directory(LINPEAS_PARTS["variables"])
|
||||||
|
|
||||||
for module in LINPEAS_PARTS["modules"]:
|
for module in LINPEAS_PARTS["modules"]:
|
||||||
|
exclude = False
|
||||||
for ex_module in exclude_modules:
|
for ex_module in exclude_modules:
|
||||||
if ex_module in module["folder_path"] or ex_module in [module["name"], module["name_check"]]:
|
if ex_module in module["folder_path"] or ex_module in [module["name"], module["name_check"]]:
|
||||||
continue
|
exclude = True
|
||||||
|
break
|
||||||
|
if exclude: continue
|
||||||
all_module_paths += self.enumerate_directory(module["folder_path"])
|
all_module_paths += self.enumerate_directory(module["folder_path"])
|
||||||
|
|
||||||
for module in all_module_paths:
|
for module in all_module_paths:
|
||||||
|
|||||||
@@ -97,7 +97,7 @@ class LinpeasBuilder:
|
|||||||
for orig_url in urls:
|
for orig_url in urls:
|
||||||
tar_gz_bin_name = ""
|
tar_gz_bin_name = ""
|
||||||
if ",,," in orig_url:
|
if ",,," in orig_url:
|
||||||
tar_gz_bin_name = url.split(",,,")[1]
|
tar_gz_bin_name = orig_url.split(",,,")[1]
|
||||||
url = orig_url.split(",,,")[0]
|
url = orig_url.split(",,,")[0]
|
||||||
else:
|
else:
|
||||||
url = orig_url
|
url = orig_url
|
||||||
@@ -402,9 +402,9 @@ class LinpeasBuilder:
|
|||||||
|
|
||||||
|
|
||||||
def __replace_mark(self, mark: str, find_calls: list, join_char: str):
|
def __replace_mark(self, mark: str, find_calls: list, join_char: str):
|
||||||
"""Substitude the markup with the actual code"""
|
"""Substitute the markup with the actual code"""
|
||||||
|
|
||||||
self.linpeas_sh = self.linpeas_sh.replace(mark, join_char.join(find_calls)) #New line char is't needed
|
self.linpeas_sh = self.linpeas_sh.replace(mark, join_char.join(find_calls)) #New line char isn't needed
|
||||||
|
|
||||||
def write_linpeas(self, path):
|
def write_linpeas(self, path):
|
||||||
"""Write on disk the final linpeas"""
|
"""Write on disk the final linpeas"""
|
||||||
|
|||||||
@@ -270,7 +270,7 @@ class MetasploitModule < Msf::Post
|
|||||||
if datastore['CUSTOM_URL'] != ""
|
if datastore['CUSTOM_URL'] != ""
|
||||||
url_peass = datastore['CUSTOM_URL']
|
url_peass = datastore['CUSTOM_URL']
|
||||||
else
|
else
|
||||||
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"
|
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"
|
||||||
end
|
end
|
||||||
# If URL is set, check if it is a valid URL or local file
|
# If URL is set, check if it is a valid URL or local file
|
||||||
if url_peass.include?("http://") || url_peass.include?("https://")
|
if url_peass.include?("http://") || url_peass.include?("https://")
|
||||||
|
|||||||
@@ -69,57 +69,62 @@ ECHO.
|
|||||||
CALL :T_Progress 2
|
CALL :T_Progress 2
|
||||||
|
|
||||||
:ListHotFixes
|
:ListHotFixes
|
||||||
|
where wmic >nul 2>&1
|
||||||
|
if %errorlevel% equ 0 (
|
||||||
wmic qfe get Caption,Description,HotFixID,InstalledOn | more
|
wmic qfe get Caption,Description,HotFixID,InstalledOn | more
|
||||||
|
) else (
|
||||||
|
powershell -command "Get-HotFix | Format-Table -AutoSize"
|
||||||
|
)
|
||||||
set expl=no
|
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)
|
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" 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 | findstr /C:"KB2592799" 1>NUL
|
IF "%expl%" == "yes" wmic qfe get Caption,Description,HotFixID,InstalledOn 2>nul | 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" 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 | findstr /C:"KB3143141" 1>NUL
|
IF "%expl%" == "yes" wmic qfe get Caption,Description,HotFixID,InstalledOn 2>nul | 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" 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 | findstr /C:"KB2393802" 1>NUL
|
IF "%expl%" == "yes" wmic qfe get Caption,Description,HotFixID,InstalledOn 2>nul | 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" 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 | findstr /C:"KB982799" 1>NUL
|
IF "%expl%" == "yes" wmic qfe get Caption,Description,HotFixID,InstalledOn 2>nul | 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" 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 | findstr /C:"KB979683" 1>NUL
|
IF "%expl%" == "yes" wmic qfe get Caption,Description,HotFixID,InstalledOn 2>nul | 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" 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 | findstr /C:"KB2305420" 1>NUL
|
IF "%expl%" == "yes" wmic qfe get Caption,Description,HotFixID,InstalledOn 2>nul | 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" 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 | findstr /C:"KB981957" 1>NUL
|
IF "%expl%" == "yes" wmic qfe get Caption,Description,HotFixID,InstalledOn 2>nul | 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" 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 | findstr /C:"KB4013081" 1>NUL
|
IF "%expl%" == "yes" wmic qfe get Caption,Description,HotFixID,InstalledOn 2>nul | 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" 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 | findstr /C:"KB977165" 1>NUL
|
IF "%expl%" == "yes" wmic qfe get Caption,Description,HotFixID,InstalledOn 2>nul | 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" 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 | findstr /C:"KB941693" 1>NUL
|
IF "%expl%" == "yes" wmic qfe get Caption,Description,HotFixID,InstalledOn 2>nul | 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" 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 | findstr /C:"KB920958" 1>NUL
|
IF "%expl%" == "yes" wmic qfe get Caption,Description,HotFixID,InstalledOn 2>nul | 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" IF errorlevel 1 ECHO.MS06-049 patch is NOT installed! (Vulns: 2K/SP4-ZwQuerySysInfo)
|
||||||
IF "%expl%" == "yes" wmic qfe get Caption,Description,HotFixID,InstalledOn | findstr /C:"KB914389" 1>NUL
|
IF "%expl%" == "yes" wmic qfe get Caption,Description,HotFixID,InstalledOn 2>nul | 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" 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 | findstr /C:"KB908523" 1>NUL
|
IF "%expl%" == "yes" wmic qfe get Caption,Description,HotFixID,InstalledOn 2>nul | 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" 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 | findstr /C:"KB890859" 1>NUL
|
IF "%expl%" == "yes" wmic qfe get Caption,Description,HotFixID,InstalledOn 2>nul | 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" 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 | findstr /C:"KB842526" 1>NUL
|
IF "%expl%" == "yes" wmic qfe get Caption,Description,HotFixID,InstalledOn 2>nul | 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" 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 | findstr /C:"KB835732" 1>NUL
|
IF "%expl%" == "yes" wmic qfe get Caption,Description,HotFixID,InstalledOn 2>nul | 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" 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 | findstr /C:"KB841872" 1>NUL
|
IF "%expl%" == "yes" wmic qfe get Caption,Description,HotFixID,InstalledOn 2>nul | 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" IF errorlevel 1 ECHO.MS04-020 patch is NOT installed! (Vulns: 2K/SP4-POSIX)
|
||||||
IF "%expl%" == "yes" wmic qfe get Caption,Description,HotFixID,InstalledOn | findstr /C:"KB2975684" 1>NUL
|
IF "%expl%" == "yes" wmic qfe get Caption,Description,HotFixID,InstalledOn 2>nul | 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" 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 | findstr /C:"KB3136041" 1>NUL
|
IF "%expl%" == "yes" wmic qfe get Caption,Description,HotFixID,InstalledOn 2>nul | 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" 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 | findstr /C:"KB3057191" 1>NUL
|
IF "%expl%" == "yes" wmic qfe get Caption,Description,HotFixID,InstalledOn 2>nul | 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" 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 | findstr /C:"KB2989935" 1>NUL
|
IF "%expl%" == "yes" wmic qfe get Caption,Description,HotFixID,InstalledOn 2>nul | 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" 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 | findstr /C:"KB2778930" 1>NUL
|
IF "%expl%" == "yes" wmic qfe get Caption,Description,HotFixID,InstalledOn 2>nul | 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" 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 | findstr /C:"KB2850851" 1>NUL
|
IF "%expl%" == "yes" wmic qfe get Caption,Description,HotFixID,InstalledOn 2>nul | 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" 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 | findstr /C:"KB2870008" 1>NUL
|
IF "%expl%" == "yes" wmic qfe get Caption,Description,HotFixID,InstalledOn 2>nul | 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)
|
IF "%expl%" == "yes" IF errorlevel 1 ECHO.MS13-081 patch is NOT installed! (Vulns: 7SP0/SP1_x86-track_popup_menu)
|
||||||
ECHO.
|
ECHO.
|
||||||
CALL :T_Progress 2
|
CALL :T_Progress 2
|
||||||
@@ -197,7 +202,12 @@ CALL :T_Progress 1
|
|||||||
|
|
||||||
:AVSettings
|
:AVSettings
|
||||||
CALL :ColorLine " %E%33m[+]%E%97m Registered Anti-Virus(AV)"
|
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
|
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"
|
||||||
|
)
|
||||||
ECHO.Checking for defender whitelisted PATHS
|
ECHO.Checking for defender whitelisted PATHS
|
||||||
reg query "HKLM\SOFTWARE\Microsoft\Windows Defender\Exclusions\Paths" 2>nul
|
reg query "HKLM\SOFTWARE\Microsoft\Windows Defender\Exclusions\Paths" 2>nul
|
||||||
CALL :T_Progress 1
|
CALL :T_Progress 1
|
||||||
@@ -226,7 +236,12 @@ CALL :T_Progress 3
|
|||||||
:MountedDisks
|
:MountedDisks
|
||||||
CALL :ColorLine " %E%33m[+]%E%97m MOUNTED DISKS"
|
CALL :ColorLine " %E%33m[+]%E%97m MOUNTED DISKS"
|
||||||
ECHO. [i] Maybe you find something interesting
|
ECHO. [i] Maybe you find something interesting
|
||||||
(wmic logicaldisk get caption 2>nul | more) || (fsutil fsinfo drives 2>nul)
|
where wmic >nul 2>&1
|
||||||
|
if %errorlevel% equ 0 (
|
||||||
|
wmic logicaldisk get caption | more
|
||||||
|
) else (
|
||||||
|
fsutil fsinfo drives
|
||||||
|
)
|
||||||
ECHO.
|
ECHO.
|
||||||
CALL :T_Progress 1
|
CALL :T_Progress 1
|
||||||
|
|
||||||
@@ -273,16 +288,30 @@ tasklist /SVC
|
|||||||
ECHO.
|
ECHO.
|
||||||
CALL :T_Progress 2
|
CALL :T_Progress 2
|
||||||
ECHO. [i] Checking file permissions of running processes (File backdooring - maybe the same files start automatically when Administrator logs in)
|
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 "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 (
|
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.
|
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.
|
||||||
|
)
|
||||||
|
)
|
||||||
ECHO.
|
ECHO.
|
||||||
ECHO. [i] Checking directory permissions of running processes (DLL injection)
|
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 (
|
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.
|
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.
|
||||||
|
)
|
||||||
|
)
|
||||||
ECHO.
|
ECHO.
|
||||||
CALL :T_Progress 3
|
CALL :T_Progress 3
|
||||||
|
|
||||||
@@ -376,7 +405,7 @@ CALL :T_Progress 1
|
|||||||
|
|
||||||
:BasicUserInfo
|
:BasicUserInfo
|
||||||
CALL :ColorLine "%E%32m[*]%E%97m BASIC USER INFO
|
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, SeDebbugPrivilege
|
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. [?] https://book.hacktricks.wiki/en/windows-hardening/windows-local-privilege-escalation/index.html#users--groups
|
ECHO. [?] https://book.hacktricks.wiki/en/windows-hardening/windows-local-privilege-escalation/index.html#users--groups
|
||||||
ECHO.
|
ECHO.
|
||||||
CALL :ColorLine " %E%33m[+]%E%97m CURRENT USER"
|
CALL :ColorLine " %E%33m[+]%E%97m CURRENT USER"
|
||||||
@@ -452,9 +481,20 @@ ECHO.
|
|||||||
:ServiceBinaryPermissions
|
:ServiceBinaryPermissions
|
||||||
CALL :ColorLine " %E%33m[+]%E%97m SERVICE BINARY PERMISSIONS WITH WMIC and ICACLS"
|
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
|
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 "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.
|
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.
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
ECHO.
|
ECHO.
|
||||||
CALL :T_Progress 1
|
CALL :T_Progress 1
|
||||||
|
|
||||||
@@ -628,6 +668,8 @@ if "%long%" == "true" (
|
|||||||
ECHO.
|
ECHO.
|
||||||
ECHO. [i] Iterating through the drives
|
ECHO. [i] Iterating through the drives
|
||||||
ECHO.
|
ECHO.
|
||||||
|
where wmic >nul 2>&1
|
||||||
|
if !errorlevel! equ 0 (
|
||||||
for /f %%x in ('wmic logicaldisk get name ^| more') do (
|
for /f %%x in ('wmic logicaldisk get name ^| more') do (
|
||||||
set tdrive=%%x
|
set tdrive=%%x
|
||||||
if "!tdrive:~1,2!" == ":" (
|
if "!tdrive:~1,2!" == ":" (
|
||||||
@@ -640,6 +682,17 @@ if "%long%" == "true" (
|
|||||||
ECHO.
|
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.
|
||||||
|
)
|
||||||
|
)
|
||||||
CALL :T_Progress 2
|
CALL :T_Progress 2
|
||||||
) ELSE (
|
) ELSE (
|
||||||
CALL :T_Progress 2
|
CALL :T_Progress 2
|
||||||
@@ -654,7 +707,8 @@ EXIT /B
|
|||||||
|
|
||||||
:SetOnce
|
:SetOnce
|
||||||
REM :: ANSI escape character is set once below - for ColorLine Subroutine
|
REM :: ANSI escape character is set once below - for ColorLine Subroutine
|
||||||
SET "E=0x1B["
|
for /F %%a in ('echo prompt $E ^| cmd') do set "ESC=%%a"
|
||||||
|
SET "E=%ESC%["
|
||||||
SET "PercentageTrack=0"
|
SET "PercentageTrack=0"
|
||||||
EXIT /B
|
EXIT /B
|
||||||
|
|
||||||
@@ -666,5 +720,5 @@ EXIT /B
|
|||||||
|
|
||||||
:ColorLine
|
:ColorLine
|
||||||
SET "CurrentLine=%~1"
|
SET "CurrentLine=%~1"
|
||||||
FOR /F "delims=" %%A IN ('FORFILES.EXE /P %~dp0 /M %~nx0 /C "CMD /C ECHO.!CurrentLine!"') DO ECHO.%%A
|
ECHO.!CurrentLine!
|
||||||
EXIT /B
|
EXIT /B
|
||||||
|
|||||||
@@ -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
|
# 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("")
|
$wp=[System.Reflection.Assembly]::Load([byte[]](Invoke-WebRequest "$url" -UseBasicParsing | Select-Object -ExpandProperty Content)); [winPEAS.Program]::Main("")
|
||||||
|
|
||||||
# The cprevios cmd in 2 lines
|
# The previous cmd in 2 lines
|
||||||
$wp=[System.Reflection.Assembly]::Load([byte[]](Invoke-WebRequest "$url" -UseBasicParsing | Select-Object -ExpandProperty Content));
|
$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
|
[winPEAS.Program]::Main("") #Put inside the quotes the winpeas parameters you want to use
|
||||||
|
|
||||||
@@ -74,10 +74,23 @@ 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.
|
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).
|
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)**.
|
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?!?!?!
|
## 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):
|
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>
|
||||||
<dependentAssembly>
|
<dependentAssembly>
|
||||||
<assemblyIdentity name="System.Text.RegularExpressions" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
|
<assemblyIdentity name="System.Text.RegularExpressions" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
|
||||||
<bindingRedirect oldVersion="0.0.0.0-4.1.1.0" newVersion="4.1.1.0" />
|
<bindingRedirect oldVersion="0.0.0.0-4.1.0.0" newVersion="4.1.0.0" />
|
||||||
</dependentAssembly>
|
</dependentAssembly>
|
||||||
<dependentAssembly>
|
<dependentAssembly>
|
||||||
<assemblyIdentity name="System.Linq" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
|
<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.Runtime.Numerics" version="4.3.0" targetFramework="net452" />
|
||||||
<package id="System.Text.Encoding" 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.Encoding.Extensions" version="4.3.0" targetFramework="net452" />
|
||||||
<package id="System.Text.RegularExpressions" version="4.3.0" targetFramework="net452" requireReinstallation="true" />
|
<package id="System.Text.RegularExpressions" version="4.3.1" targetFramework="net48" />
|
||||||
<package id="System.Threading" version="4.3.0" targetFramework="net452" />
|
<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.Tasks" version="4.3.0" targetFramework="net452" />
|
||||||
<package id="System.Threading.Timer" version="4.3.0" targetFramework="net452" />
|
<package id="System.Threading.Timer" version="4.3.0" targetFramework="net452" />
|
||||||
|
|||||||
@@ -83,6 +83,10 @@
|
|||||||
<Reference Include="System.Runtime.InteropServices.RuntimeInformation, Version=4.0.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
<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>
|
<HintPath>..\packages\System.Runtime.InteropServices.RuntimeInformation.4.3.0\lib\net45\System.Runtime.InteropServices.RuntimeInformation.dll</HintPath>
|
||||||
</Reference>
|
</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.Xml.Linq" />
|
||||||
<Reference Include="System.Data.DataSetExtensions" />
|
<Reference Include="System.Data.DataSetExtensions" />
|
||||||
<Reference Include="Microsoft.CSharp" />
|
<Reference Include="Microsoft.CSharp" />
|
||||||
|
|||||||
@@ -32,7 +32,7 @@
|
|||||||
</dependentAssembly>
|
</dependentAssembly>
|
||||||
<dependentAssembly>
|
<dependentAssembly>
|
||||||
<assemblyIdentity name="Microsoft.Bcl.AsyncInterfaces" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
|
<assemblyIdentity name="Microsoft.Bcl.AsyncInterfaces" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
|
||||||
<bindingRedirect oldVersion="0.0.0.0-8.0.0.0" newVersion="8.0.0.0" />
|
<bindingRedirect oldVersion="0.0.0.0-9.0.0.1" newVersion="9.0.0.1" />
|
||||||
</dependentAssembly>
|
</dependentAssembly>
|
||||||
<dependentAssembly>
|
<dependentAssembly>
|
||||||
<assemblyIdentity name="Microsoft.Win32.Primitives" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
|
<assemblyIdentity name="Microsoft.Win32.Primitives" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
|
||||||
@@ -156,7 +156,7 @@
|
|||||||
</dependentAssembly>
|
</dependentAssembly>
|
||||||
<dependentAssembly>
|
<dependentAssembly>
|
||||||
<assemblyIdentity name="System.Text.Encodings.Web" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
|
<assemblyIdentity name="System.Text.Encodings.Web" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
|
||||||
<bindingRedirect oldVersion="0.0.0.0-8.0.0.0" newVersion="8.0.0.0" />
|
<bindingRedirect oldVersion="0.0.0.0-9.0.0.1" newVersion="9.0.0.1" />
|
||||||
</dependentAssembly>
|
</dependentAssembly>
|
||||||
<dependentAssembly>
|
<dependentAssembly>
|
||||||
<assemblyIdentity name="System.Text.RegularExpressions" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
|
<assemblyIdentity name="System.Text.RegularExpressions" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
|
||||||
|
|||||||
346
winPEAS/winPEASexe/winPEAS/Checks/ActiveDirectoryInfo.cs
Normal file
346
winPEAS/winPEASexe/winPEAS/Checks/ActiveDirectoryInfo.cs
Normal file
@@ -0,0 +1,346 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -90,6 +90,7 @@ namespace winPEAS.Checks
|
|||||||
new SystemCheck("servicesinfo", new ServicesInfo()),
|
new SystemCheck("servicesinfo", new ServicesInfo()),
|
||||||
new SystemCheck("applicationsinfo", new ApplicationsInfo()),
|
new SystemCheck("applicationsinfo", new ApplicationsInfo()),
|
||||||
new SystemCheck("networkinfo", new NetworkInfo()),
|
new SystemCheck("networkinfo", new NetworkInfo()),
|
||||||
|
new SystemCheck("activedirectoryinfo", new ActiveDirectoryInfo()),
|
||||||
new SystemCheck("cloudinfo", new CloudInfo()),
|
new SystemCheck("cloudinfo", new CloudInfo()),
|
||||||
new SystemCheck("windowscreds", new WindowsCreds()),
|
new SystemCheck("windowscreds", new WindowsCreds()),
|
||||||
new SystemCheck("browserinfo", new BrowserInfo()),
|
new SystemCheck("browserinfo", new BrowserInfo()),
|
||||||
|
|||||||
@@ -102,17 +102,15 @@ namespace winPEAS.Checks
|
|||||||
{
|
{
|
||||||
vulnHandlers = ProcessesInfo.GetVulnHandlers(progress);
|
vulnHandlers = ProcessesInfo.GetVulnHandlers(progress);
|
||||||
}
|
}
|
||||||
|
Dictionary<string, string> colors = new Dictionary<string, string>();
|
||||||
|
colors[Checks.CurrentUserName] = Beaprint.ansi_color_bad;
|
||||||
|
colors[HandlesHelper.elevatedProcess] = Beaprint.ansi_color_bad;
|
||||||
|
|
||||||
foreach (Dictionary<string, string> handler in vulnHandlers)
|
foreach (Dictionary<string, string> handler in vulnHandlers)
|
||||||
{
|
{
|
||||||
Dictionary<string, string> colors = new Dictionary<string, string>()
|
colors[handler["Reason"]] = Beaprint.ansi_color_bad;
|
||||||
{
|
|
||||||
{ Checks.CurrentUserName, Beaprint.ansi_color_bad },
|
|
||||||
{ handler["Reason"], Beaprint.ansi_color_bad },
|
|
||||||
};
|
|
||||||
|
|
||||||
Beaprint.DictPrint(vulnHandlers, colors, true);
|
|
||||||
}
|
}
|
||||||
|
Beaprint.DictPrint(vulnHandlers, colors, true);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -84,6 +84,7 @@ namespace winPEAS.Checks
|
|||||||
PrintLSAInfo,
|
PrintLSAInfo,
|
||||||
PrintNtlmSettings,
|
PrintNtlmSettings,
|
||||||
PrintLocalGroupPolicy,
|
PrintLocalGroupPolicy,
|
||||||
|
PrintPotentialGPOAbuse,
|
||||||
AppLockerHelper.PrintAppLockerPolicy,
|
AppLockerHelper.PrintAppLockerPolicy,
|
||||||
PrintPrintersWMIInfo,
|
PrintPrintersWMIInfo,
|
||||||
PrintNamedPipes,
|
PrintNamedPipes,
|
||||||
@@ -1131,6 +1132,94 @@ 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()
|
private static void PrintPowerShellSessionSettings()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
|||||||
@@ -129,6 +129,7 @@ namespace winPEAS.Helpers
|
|||||||
Console.WriteLine(LCYAN + " servicesinfo" + GRAY + " Search services information" + NOCOLOR);
|
Console.WriteLine(LCYAN + " servicesinfo" + GRAY + " Search services information" + NOCOLOR);
|
||||||
Console.WriteLine(LCYAN + " applicationsinfo" + GRAY + " Search installed applications information" + NOCOLOR);
|
Console.WriteLine(LCYAN + " applicationsinfo" + GRAY + " Search installed applications information" + NOCOLOR);
|
||||||
Console.WriteLine(LCYAN + " networkinfo" + GRAY + " Search network 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 + " cloudinfo" + GRAY + " Enumerate cloud information" + NOCOLOR);
|
||||||
Console.WriteLine(LCYAN + " windowscreds" + GRAY + " Search windows credentials" + NOCOLOR);
|
Console.WriteLine(LCYAN + " windowscreds" + GRAY + " Search windows credentials" + NOCOLOR);
|
||||||
Console.WriteLine(LCYAN + " browserinfo" + GRAY + " Search browser information" + NOCOLOR);
|
Console.WriteLine(LCYAN + " browserinfo" + GRAY + " Search browser information" + NOCOLOR);
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ namespace winPEAS.Helpers
|
|||||||
private const int CNST_SYSTEM_EXTENDED_HANDLE_INFORMATION = 64;
|
private const int CNST_SYSTEM_EXTENDED_HANDLE_INFORMATION = 64;
|
||||||
public const uint STATUS_INFO_LENGTH_MISMATCH = 0xC0000004;
|
public const uint STATUS_INFO_LENGTH_MISMATCH = 0xC0000004;
|
||||||
public const int DUPLICATE_SAME_ACCESS = 0x2;
|
public const int DUPLICATE_SAME_ACCESS = 0x2;
|
||||||
|
public const string elevatedProcess = "Access denied, process is probably elevated";
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
||||||
public struct FILE_NAME_INFO
|
public struct FILE_NAME_INFO
|
||||||
@@ -171,7 +172,7 @@ namespace winPEAS.Helpers
|
|||||||
// Hex perms from https://docs.microsoft.com/en-us/windows/win32/procthread/process-security-and-access-rights and https://github.com/buffer/maltracer/blob/master/defines.py
|
// Hex perms from https://docs.microsoft.com/en-us/windows/win32/procthread/process-security-and-access-rights and https://github.com/buffer/maltracer/blob/master/defines.py
|
||||||
|
|
||||||
//PROCESS_ALL_ACCESS
|
//PROCESS_ALL_ACCESS
|
||||||
if ((h.GrantedAccess & 0x001F0FFF) == h.GrantedAccess)
|
if ((h.GrantedAccess & 0x001F0FFF) == h.GrantedAccess || (h.GrantedAccess & 0x1FFFFF) == h.GrantedAccess)
|
||||||
{
|
{
|
||||||
vulnHandler.isVuln = true;
|
vulnHandler.isVuln = true;
|
||||||
vulnHandler.reason = "PROCESS_ALL_ACCESS";
|
vulnHandler.reason = "PROCESS_ALL_ACCESS";
|
||||||
@@ -454,6 +455,8 @@ namespace winPEAS.Helpers
|
|||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
|
data["name"] = elevatedProcess;
|
||||||
|
data["sid"] = elevatedProcess;
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
@@ -469,12 +472,32 @@ namespace winPEAS.Helpers
|
|||||||
public static PT_RELEVANT_INFO getProcInfoById(int pid)
|
public static PT_RELEVANT_INFO getProcInfoById(int pid)
|
||||||
{
|
{
|
||||||
PT_RELEVANT_INFO pri = new PT_RELEVANT_INFO();
|
PT_RELEVANT_INFO pri = new PT_RELEVANT_INFO();
|
||||||
|
Process proc;
|
||||||
|
|
||||||
Process proc = Process.GetProcessById(pid);
|
try
|
||||||
|
{
|
||||||
|
proc = Process.GetProcessById(pid);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
pri.pid = pid;
|
||||||
|
pri.name = "Error, process may not exist";
|
||||||
|
pri.userName = "Error, process may not exist";
|
||||||
|
pri.userSid = "Error, process may not exist";
|
||||||
|
pri.imagePath = "Error, process may not exist";
|
||||||
|
return pri;
|
||||||
|
}
|
||||||
Dictionary<string, string> user = GetProcU(proc);
|
Dictionary<string, string> user = GetProcU(proc);
|
||||||
|
|
||||||
StringBuilder fileName = new StringBuilder(2000);
|
StringBuilder fileName = new StringBuilder(2000);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
Native.Psapi.GetProcessImageFileName(proc.Handle, fileName, 2000);
|
Native.Psapi.GetProcessImageFileName(proc.Handle, fileName, 2000);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
fileName = new StringBuilder(elevatedProcess);
|
||||||
|
}
|
||||||
|
|
||||||
pri.pid = pid;
|
pri.pid = pid;
|
||||||
pri.name = proc.ProcessName;
|
pri.name = proc.ProcessName;
|
||||||
|
|||||||
@@ -172,6 +172,7 @@
|
|||||||
<Private>True</Private>
|
<Private>True</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="System.DirectoryServices.AccountManagement" />
|
<Reference Include="System.DirectoryServices.AccountManagement" />
|
||||||
|
<Reference Include="System.DirectoryServices" />
|
||||||
<Reference Include="System.Globalization.Calendars, Version=4.0.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
<Reference Include="System.Globalization.Calendars, Version=4.0.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||||
<HintPath>..\packages\System.Globalization.Calendars.4.3.0\lib\net46\System.Globalization.Calendars.dll</HintPath>
|
<HintPath>..\packages\System.Globalization.Calendars.4.3.0\lib\net46\System.Globalization.Calendars.dll</HintPath>
|
||||||
<Private>True</Private>
|
<Private>True</Private>
|
||||||
@@ -1186,6 +1187,7 @@
|
|||||||
<Compile Include="3rdParty\YamlSerializer\YamlTagValidator.cs" />
|
<Compile Include="3rdParty\YamlSerializer\YamlTagValidator.cs" />
|
||||||
<Compile Include="Checks\ApplicationsInfo.cs" />
|
<Compile Include="Checks\ApplicationsInfo.cs" />
|
||||||
<Compile Include="Checks\BrowserInfo.cs" />
|
<Compile Include="Checks\BrowserInfo.cs" />
|
||||||
|
<Compile Include="Checks\ActiveDirectoryInfo.cs" />
|
||||||
<Compile Include="Checks\CloudInfo.cs" />
|
<Compile Include="Checks\CloudInfo.cs" />
|
||||||
<Compile Include="Checks\FileAnalysis.cs" />
|
<Compile Include="Checks\FileAnalysis.cs" />
|
||||||
<Compile Include="Checks\FilesInfo.cs" />
|
<Compile Include="Checks\FilesInfo.cs" />
|
||||||
|
|||||||
@@ -19,6 +19,14 @@ 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')"
|
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
|
## 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.
|
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.
|
||||||
|
|||||||
@@ -148,6 +148,244 @@ 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 {
|
function Search-Excel {
|
||||||
[cmdletbinding()]
|
[cmdletbinding()]
|
||||||
Param (
|
Param (
|
||||||
@@ -1226,6 +1464,95 @@ Write-Host -ForegroundColor Blue "=========|| LISTENING PORTS"
|
|||||||
Start-Process NETSTAT.EXE -ArgumentList "-ano" -Wait -NoNewWindow
|
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 ""
|
Write-Host ""
|
||||||
if ($TimeStamp) { TimeElapsed }
|
if ($TimeStamp) { TimeElapsed }
|
||||||
Write-Host -ForegroundColor Blue "=========|| ARP Table"
|
Write-Host -ForegroundColor Blue "=========|| ARP Table"
|
||||||
@@ -1323,7 +1650,7 @@ Write-Host -ForegroundColor Blue "=========|| WHOAMI INFO"
|
|||||||
Write-Host ""
|
Write-Host ""
|
||||||
if ($TimeStamp) { TimeElapsed }
|
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 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, SeDebbugPrivilege"
|
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 "https://book.hacktricks.wiki/en/windows-hardening/windows-local-privilege-escalation/index.html#users--groups" -ForegroundColor Yellow
|
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
|
Start-Process whoami.exe -ArgumentList "/all" -Wait -NoNewWindow
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user