Compare commits

...

58 Commits

Author SHA1 Message Date
SirBroccoli
80318c5005 Merge pull request #514 from moscowchill/bat-pr
Fix ANSI escape codes displaying as literal text in winPEAS.bat
2025-11-15 15:45:38 +01:00
SirBroccoli
7af6c33d39 Merge pull request #513 from sttlr/patch-1
Fix: LinPEASS doesn't run via metasploit module
2025-11-15 15:44:50 +01:00
moscow chill
336c53a163 Fix ANSI escape codes displaying as literal text in winPEAS.bat
The script was setting E=0x1B[ as a literal string instead of the actual
ESC character (ASCII 27), causing color codes to display as text like
"0x1B[33m[+]0x1B[97m" instead of rendering as colors.

Changed the SetOnce subroutine to properly capture the ESC character using
the 'prompt $E' technique before building the ANSI escape sequence prefix.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-29 20:16:34 +01:00
Max K.
6877f39193 Fix: LinPEASS doesn't run via metasploit module
If you set "WINPEASS" to "false" - it's a string, and therefore "true". So it would run WinPEASS anyway.

The fix converts value of the variable to string before comparing it.
2025-10-28 13:19:03 +02:00
SirBroccoli
d75525ebbc Merge pull request #512 from moscowchill/pr-bat-fix
Fix winPEAS.bat compatibility with Windows 11 and modern Windows 10
2025-10-28 01:51:48 +01:00
moscow chill
29d8132d93 Fix winPEAS.bat compatibility with Windows 11 and modern Windows 10
WMIC has been deprecated since Windows 10 20H1 and removed in Windows 11.
The script was exiting early when WMIC commands failed instead of continuing.

Changes:
- Add proper WMIC existence checks using 'where wmic' before execution
- Implement PowerShell fallbacks for all WMIC commands
- Fix hotfix enumeration (Get-HotFix)
- Fix antivirus detection (Get-CimInstance)
- Fix mounted disk enumeration (Get-PSDrive)
- Fix running process checks (Get-Process)
- Fix service binary permission checks (Get-CimInstance Win32_Service)
- Add error suppression (2>nul) to conditional WMIC exploit checks

The script now properly detects WMIC availability and falls back to
PowerShell equivalents, ensuring full functionality on modern Windows
systems while maintaining backward compatibility with older systems.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-27 16:10:42 +01:00
carlospolop
c16c5de36f f 2025-10-18 00:59:40 +02:00
SirBroccoli
be3fe91da4 Merge pull request #507 from CravateRouge/master
Add ADCS ESC DC registry checks
2025-10-07 10:50:43 +02:00
CravateRouge
b8b4a0fc14 Fix InterfaceFlags syntax 2025-10-07 11:14:45 +08:00
CravateRouge
7042a182df Add ADCS ESC DC registry checks 2025-10-06 17:18:44 +02:00
SirBroccoli
c83eef9cd8 Merge pull request #502 from peass-ng/update_PEASS-linpeas-HTB_Planning__Grafana_CVE-2024-9264__20250913_182726
[LINPEAS] Add privilege escalation check: HTB Planning Grafana CVE-2024-9264 to Co...
2025-10-04 10:38:22 +02:00
SirBroccoli
e15a1f2e12 Update 16_Crontab_UI_misconfig.sh 2025-10-04 10:38:02 +02:00
SirBroccoli
24e9c54290 Merge pull request #505 from jtothef/patch-1
Update README.md
2025-10-04 10:36:24 +02:00
SirBroccoli
bdb5c61dad Merge pull request #504 from peass-ng/update_PEASS-linpeas-Forgotten_20250917_063428
[LINPEAS] Add privilege escalation check: Forgotten
2025-10-04 10:36:09 +02:00
SirBroccoli
ee83c23a74 Update 16_Crontab_UI_misconfig.sh 2025-10-04 10:34:04 +02:00
SirBroccoli
7b36014699 Merge pull request #499 from peass-ng/update_PEASS-linpeas-HTB_Environment__Laravel_env_overrid_20250907_013120
[LINPEAS] Add privilege escalation check: HTB Environment Laravel env override (CV...
2025-10-04 10:29:32 +02:00
SirBroccoli
6fe8304783 Merge pull request #506 from tropkal/tropkal-patch-1
Update the regex for the sudo version
2025-10-04 10:29:01 +02:00
tropkal
262feb9896 Updated the sudo regex to catch 2 more CVE's. 2025-10-04 08:43:00 +02:00
tropkal
40cf08af85 Update sudovB.sh
Modified the regex that checks for vulnerable sudo versions to include sudo version 1.9.17 (not including 1.9.17p1), which is vulnerable to CVE-2025-32463 (https://www.exploit-db.com/exploits/52352).
2025-10-04 09:08:37 +03:00
jtothef
7c9f431649 Update README.md
Fix typo
2025-09-23 12:49:05 -05:00
HackTricks News Bot
31bdb339d7 Add linpeas privilege escalation checks from: Forgotten 2025-09-17 06:48:40 +00:00
HackTricks News Bot
bdcebadde0 Add linpeas privilege escalation checks from: HTB Planning: Grafana CVE-2024-9264 to Container Root, Env-Creds Pivot, Crontab 2025-09-13 18:33:45 +00:00
HackTricks News Bot
4b3f4aa19e Add linpeas privilege escalation checks from: HTB Environment: Laravel env override (CVE‑2024‑52301) → LFM upload RCE (CVE‑202 2025-09-07 01:38:03 +00:00
carlospolop
7c7884fb72 f tf 2025-09-05 01:04:53 +02:00
carlospolop
35300e499b tf 2025-09-05 01:04:18 +02:00
carlospolop
147de0fc88 f 2025-09-03 14:19:59 +02:00
carlospolop
afaf596342 f 2025-09-03 13:39:15 +02:00
SirBroccoli
215c5d074e Merge pull request #456 from peass-ng/dependabot/nuget/winPEAS/winPEASexe/Tests/System.Text.RegularExpressions-4.3.1
Bump System.Text.RegularExpressions from 4.3.0 to 4.3.1 in /winPEAS/winPEASexe/Tests
2025-09-03 13:36:40 +02:00
SirBroccoli
ca383a4548 Merge pull request #496 from peass-ng/update_PEASS-linpeas-Case_study__Backup_leak___CI_abuse___20250827_193408
[LINPEAS] Add privilege escalation check: Case study Backup leak → CI abuse → inte...
2025-09-03 13:36:13 +02:00
SirBroccoli
46264bf239 Merge pull request #497 from peass-ng/update_PEASS-winpeas-HTB_Sendai__From_password_spray_to_g_20250828_184040
[WINPEAS] Add privilege escalation check: HTB Sendai From password spray to gMSA d...
2025-09-03 13:31:10 +02:00
SirBroccoli
642c33304f Merge pull request #494 from peass-ng/update_PEASS-winpeas-HTB__TheFrizz__High-level__redacted__20250827_190719
[WINPEAS] Add privilege escalation check: HTB TheFrizz (High-level, redacted for s...
2025-09-03 13:27:06 +02:00
HackTricks News Bot
54d861ab04 Add winpeas privilege escalation checks from: HTB Sendai: From password spray to gMSA dump, then ADCS ESC4 or SQL+Silver Ticke 2025-08-28 18:51:59 +00:00
HackTricks News Bot
bbb932d6d3 feat(winpeas): add ActiveDirectoryInfo check (gMSA readable passwords, AD CS template rights) and include in project 2025-08-28 18:50:51 +00:00
HackTricks News Bot
626ea2d298 docs(usage): add activedirectoryinfo option to usage output 2025-08-28 18:50:22 +00:00
HackTricks News Bot
ed01b32a95 Add linpeas privilege escalation checks from: Case study: Backup leak → CI abuse → internal trust misconfigurations → escalati 2025-08-27 19:45:02 +00:00
HackTricks News Bot
c314cfd23d Add winpeas privilege escalation checks from: HTB: TheFrizz (High-level, redacted for safety) 2025-08-27 19:14:43 +00:00
SirBroccoli
cc5ab76991 Merge pull request #486 from soobinrho/fix-typo-on-color-explanations
docs: fix typo (conten -> content)
2025-08-27 12:12:28 +02:00
carlospolop
36001d644e Merge branch 'master' of github.com:peass-ng/PEASS-ng 2025-08-25 11:18:18 +02:00
carlospolop
fdd414f4aa new workflow 2025-08-25 11:18:16 +02:00
Soobin Rho
c3e50dbdbf docs: fix typo (conten -> content) 2025-08-08 17:56:41 -05:00
SirBroccoli
41128808a6 Merge pull request #483 from securitytime/patch-1
Update Beaprint.cs
2025-07-01 16:23:13 +02:00
carlospolop
6fd96f4bdb f 2025-07-01 12:12:01 +02:00
carlospolop
a745f00dd7 fix 2025-07-01 11:10:21 +02:00
securitytime
933e12d7f1 Update Beaprint.cs
A space character is missing here:
"... educational purposes only.Any misuse of this software  ..."
2025-06-28 09:12:40 +02:00
SirBroccoli
4061cef7e8 Merge pull request #476 from peass-ng/codex/fix-url-reference-in-linpeasbuilder.py
Fix url variable reference in linpeasBuilder
2025-06-25 01:59:43 +02:00
SirBroccoli
b66ced3c63 Merge pull request #475 from peass-ng/codex/find-and-fix-a-bug
Fix parser global state reuse
2025-06-25 01:59:03 +02:00
SirBroccoli
cde725dacc Merge pull request #477 from peass-ng/codex/update-docstring-and-fix-typo
Fix docstring and comment in linpeasBuilder
2025-06-25 01:57:58 +02:00
SirBroccoli
f0f829890c Merge pull request #479 from peass-ng/codex/replace--parth--with--path--in-argparse
Fix typo in linpeas builder arg help
2025-06-25 01:57:11 +02:00
SirBroccoli
99c36b8562 Merge pull request #480 from Signum21/master
Fixed multiple bugs in Vulnerable Leaked Handlers
2025-06-25 01:56:58 +02:00
SirBroccoli
a74c6c820f Merge pull request #482 from Aarav-Juneja/builder-exclude-fix
Fix exclude modules on linPEASS
2025-06-25 01:55:48 +02:00
SirBroccoli
53fd4d8dc8 Merge pull request #481 from ertaku12/master
Added a privilege escalation vulnerability for MySQL 4.x/5.x versions.
2025-06-25 01:55:25 +02:00
Aarav Juneja
9b37fd4ef4 Fix exclude modules on linPEASS 2025-06-24 13:05:10 -07:00
John Doe
f27b1d4816 Added a privilege escalation vulnerability for MySQL 4.x/5.x versions. 2025-06-23 22:37:44 +03:00
Signum21
d335b9254f Fixed multiple bugs in Vulnerable Leaked Handlers 2025-06-15 20:59:20 +02:00
SirBroccoli
4af321d138 Fix docstring and comment typo 2025-06-06 00:01:29 +02:00
SirBroccoli
4e556fd594 Fix variable reference when parsing URLs 2025-06-06 00:01:17 +02:00
SirBroccoli
39066f6867 Fix leftover debug code and reset state in parser 2025-06-06 00:00:39 +02:00
dependabot[bot]
859a44230d Bump System.Text.RegularExpressions in /winPEAS/winPEASexe/Tests
Bumps System.Text.RegularExpressions from 4.3.0 to 4.3.1.

---
updated-dependencies:
- dependency-name: System.Text.RegularExpressions
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-24 18:29:38 +00:00
26 changed files with 2745 additions and 1620 deletions

201
.github/workflows/PR-tests.yml vendored Normal file
View 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

View File

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

View File

@@ -170,7 +170,7 @@ LinPEAS uses colors to indicate where does each section begin. But **it also use
- The ![](https://placehold.it/15/b32400/000000?text=+) **Red** color is used for identifing suspicious configurations that could lead to privilege escalation.
- The ![](https://placehold.it/15/66ff33/000000?text=+) **Green** color is used for known good configurations (based on the name not on the conten!)
- The ![](https://placehold.it/15/66ff33/000000?text=+) **Green** color is used for known good configurations (based on the name not on the content!)
- The ![](https://placehold.it/15/0066ff/000000?text=+) **Blue** color is used for: Users without shell & Mounted devices

View File

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

View File

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

View File

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

View File

@@ -8,7 +8,7 @@
# Functions Used: print_2title
# Global Variables: $DEBUG, $knw_usrs, $nosh_usrs, $sh_usrs, $DEBUG, $USER, $STRINGS
# 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
# Small linpeas: 1
@@ -102,4 +102,42 @@ if [ "$(command -v mysql || echo -n '')" ] || [ "$(command -v mysqladmin || echo
else echo_no
fi
echo ""
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

View File

@@ -13,5 +13,5 @@
# Small linpeas: 1
sudoVB1=" \*|env_keep\W*\+=.*LD_PRELOAD|env_keep\W*\+=.*LD_LIBRARY_PATH|peass{SUDOVB1_HERE}"
sudoVB2="peass{SUDOVB2_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}"

View File

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

View File

@@ -292,9 +292,12 @@ class LinpeasBaseBuilder:
all_module_paths += self.enumerate_directory(LINPEAS_PARTS["variables"])
for module in LINPEAS_PARTS["modules"]:
exclude = False
for ex_module in exclude_modules:
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"])
for module in all_module_paths:

View File

@@ -97,7 +97,7 @@ class LinpeasBuilder:
for orig_url in urls:
tar_gz_bin_name = ""
if ",,," in orig_url:
tar_gz_bin_name = url.split(",,,")[1]
tar_gz_bin_name = orig_url.split(",,,")[1]
url = orig_url.split(",,,")[0]
else:
url = orig_url
@@ -402,9 +402,9 @@ class LinpeasBuilder:
def __replace_mark(self, mark: str, find_calls: list, join_char: str):
"""Substitude 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
"""Substitute the markup with the actual code"""
self.linpeas_sh = self.linpeas_sh.replace(mark, join_char.join(find_calls)) #New line char isn't needed
def write_linpeas(self, path):
"""Write on disk the final linpeas"""

View File

@@ -270,7 +270,7 @@ class MetasploitModule < Msf::Post
if datastore['CUSTOM_URL'] != ""
url_peass = datastore['CUSTOM_URL']
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
# If URL is set, check if it is a valid URL or local file
if url_peass.include?("http://") || url_peass.include?("https://")

View File

@@ -106,8 +106,6 @@ def parse_line(line: str):
global FINAL_JSON, C_SECTION, C_MAIN_SECTION, C_2_SECTION, C_3_SECTION
if "Cron jobs" in line:
a=1
if is_section(line, TITLE1_PATTERN):
title = parse_title(line)
@@ -145,17 +143,26 @@ def parse_line(line: str):
def parse_peass(outputpath: str, jsonpath: str = ""):
global OUTPUT_PATH, JSON_PATH
global OUTPUT_PATH, JSON_PATH, FINAL_JSON, C_SECTION, C_MAIN_SECTION, C_2_SECTION, C_3_SECTION
OUTPUT_PATH = outputpath
JSON_PATH = jsonpath
for line in open(OUTPUT_PATH, 'r', encoding="utf8").readlines():
line = line.strip()
if not line or not clean_colors(line): #Remove empty lines or lines just with colors hex
continue
# Reset globals to avoid data leaking between executions
FINAL_JSON = {}
C_SECTION = FINAL_JSON
C_MAIN_SECTION = FINAL_JSON
C_2_SECTION = FINAL_JSON
C_3_SECTION = FINAL_JSON
parse_line(line)
with open(OUTPUT_PATH, 'r', encoding="utf8") as f:
for line in f.readlines():
line = line.strip()
# Remove empty lines or lines containing only color codes
if not line or not clean_colors(line):
continue
parse_line(line)
if JSON_PATH:
with open(JSON_PATH, "w") as f:

View File

@@ -69,57 +69,62 @@ ECHO.
CALL :T_Progress 2
:ListHotFixes
wmic qfe get Caption,Description,HotFixID,InstalledOn | more
where wmic >nul 2>&1
if %errorlevel% equ 0 (
wmic qfe get Caption,Description,HotFixID,InstalledOn | more
) else (
powershell -command "Get-HotFix | Format-Table -AutoSize"
)
set expl=no
for /f "tokens=3-9" %%a in ('systeminfo') do (ECHO."%%a %%b %%c %%d %%e %%f %%g" | findstr /i "2000 XP 2003 2008 vista" && set expl=yes) & (ECHO."%%a %%b %%c %%d %%e %%f %%g" | findstr /i /C:"windows 7" && set expl=yes)
IF "%expl%" == "yes" ECHO. [i] Possible exploits (https://github.com/codingo/OSCP-2/blob/master/Windows/WinPrivCheck.bat)
IF "%expl%" == "yes" wmic qfe get Caption,Description,HotFixID,InstalledOn | 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" 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" 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" 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" 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" 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" 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" 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" 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" 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" 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" 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" 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" 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" 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" 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" 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" 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" 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" 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" 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" 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" 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" 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)
ECHO.
CALL :T_Progress 2
@@ -197,7 +202,12 @@ CALL :T_Progress 1
:AVSettings
CALL :ColorLine " %E%33m[+]%E%97m Registered Anti-Virus(AV)"
WMIC /Node:localhost /Namespace:\\root\SecurityCenter2 Path AntiVirusProduct Get displayName /Format:List | more
where wmic >nul 2>&1
if %errorlevel% equ 0 (
WMIC /Node:localhost /Namespace:\\root\SecurityCenter2 Path AntiVirusProduct Get displayName /Format:List | more
) else (
powershell -command "Get-CimInstance -Namespace root/SecurityCenter2 -ClassName AntiVirusProduct | Select-Object -ExpandProperty displayName"
)
ECHO.Checking for defender whitelisted PATHS
reg query "HKLM\SOFTWARE\Microsoft\Windows Defender\Exclusions\Paths" 2>nul
CALL :T_Progress 1
@@ -226,7 +236,12 @@ CALL :T_Progress 3
:MountedDisks
CALL :ColorLine " %E%33m[+]%E%97m MOUNTED DISKS"
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.
CALL :T_Progress 1
@@ -273,15 +288,29 @@ tasklist /SVC
ECHO.
CALL :T_Progress 2
ECHO. [i] Checking file permissions of running processes (File backdooring - maybe the same files start automatically when Administrator logs in)
for /f "tokens=2 delims='='" %%x in ('wmic process list full^|find /i "executablepath"^|find /i /v "system32"^|find ":"') do (
for /f eol^=^"^ delims^=^" %%z in ('ECHO.%%x') do (
icacls "%%z" 2>nul | findstr /i "(F) (M) (W) :\\" | findstr /i ":\\ everyone authenticated users todos %username%" && ECHO.
where wmic >nul 2>&1
if %errorlevel% equ 0 (
for /f "tokens=2 delims='='" %%x in ('wmic process list full ^|find /i "executablepath"^|find /i /v "system32"^|find ":"') do (
for /f eol^=^"^ delims^=^" %%z in ('ECHO.%%x') do (
icacls "%%z" 2>nul | findstr /i "(F) (M) (W) :\\" | findstr /i ":\\ everyone authenticated users todos %username%" && ECHO.
)
)
) else (
for /f "tokens=*" %%x in ('powershell -command "Get-Process | Where-Object {$_.Path -and $_.Path -notlike '*system32*'} | Select-Object -ExpandProperty Path -Unique"') do (
icacls "%%x" 2>nul | findstr /i "(F) (M) (W) :\\" | findstr /i ":\\ everyone authenticated users todos %username%" && ECHO.
)
)
ECHO.
ECHO. [i] Checking directory permissions of running processes (DLL injection)
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.
where wmic >nul 2>&1
if %errorlevel% equ 0 (
for /f "tokens=2 delims='='" %%x in ('wmic process list full ^|find /i "executablepath"^|find /i /v "system32"^|find ":"') do for /f eol^=^"^ delims^=^" %%y in ('ECHO.%%x') do (
icacls "%%~dpy\" 2>nul | findstr /i "(F) (M) (W) :\\" | findstr /i ":\\ everyone authenticated users todos %username%" && ECHO.
)
) else (
for /f "tokens=*" %%x in ('powershell -command "Get-Process | Where-Object {$_.Path -and $_.Path -notlike '*system32*'} | Select-Object -ExpandProperty Path -Unique"') do (
for /f "delims=" %%d in ("%%~dpx") do icacls "%%d" 2>nul | findstr /i "(F) (M) (W) :\\" | findstr /i ":\\ everyone authenticated users todos %username%" && ECHO.
)
)
ECHO.
CALL :T_Progress 3
@@ -452,8 +481,19 @@ ECHO.
:ServiceBinaryPermissions
CALL :ColorLine " %E%33m[+]%E%97m SERVICE BINARY PERMISSIONS WITH WMIC and ICACLS"
ECHO. [?] https://book.hacktricks.wiki/en/windows-hardening/windows-local-privilege-escalation/index.html#services
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.
where wmic >nul 2>&1
if %errorlevel% equ 0 (
for /f "tokens=2 delims='='" %%a in ('cmd.exe /c wmic service list full ^| findstr /i "pathname" ^|findstr /i /v "system32"') do (
for /f eol^=^"^ delims^=^" %%b in ("%%a") do icacls "%%b" 2>nul | findstr /i "(F) (M) (W) :\\" | findstr /i ":\\ everyone authenticated users todos usuarios %username%" && ECHO.
)
) else (
for /f "tokens=*" %%a in ('powershell -command "Get-CimInstance -ClassName Win32_Service | Where-Object {$_.PathName -and $_.PathName -notlike '*system32*'} | Select-Object -ExpandProperty PathName"') do (
for /f "tokens=1 delims= " %%b in ("%%a") do (
set "svcpath=%%b"
set "svcpath=!svcpath:~1,-1!"
if exist "!svcpath!" icacls "!svcpath!" 2>nul | findstr /i "(F) (M) (W) :\\" | findstr /i ":\\ everyone authenticated users todos usuarios %username%" && ECHO.
)
)
)
ECHO.
CALL :T_Progress 1
@@ -628,16 +668,29 @@ if "%long%" == "true" (
ECHO.
ECHO. [i] Iterating through the drives
ECHO.
for /f %%x in ('wmic logicaldisk get name^| more') do (
set tdrive=%%x
if "!tdrive:~1,2!" == ":" (
%%x
CALL :ColorLine " %E%33m[+]%E%97m FILES THAT CONTAINS THE WORD PASSWORD WITH EXTENSION: .xml .ini .txt *.cfg *.config"
findstr /s/n/m/i password *.xml *.ini *.txt *.cfg *.config 2>nul | findstr /v /i "\\AppData\\Local \\WinSxS ApnDatabase.xml \\UEV\\InboxTemplates \\Microsoft.Windows.Cloud \\Notepad\+\+\\ vmware cortana alphabet \\7-zip\\" 2>nul
ECHO.
CALL :ColorLine " %E%33m[+]%E%97m FILES WHOSE NAME CONTAINS THE WORD PASS CRED or .config not inside \Windows\"
dir /s/b *pass* == *cred* == *.config* == *.cfg 2>nul | findstr /v /i "\\windows\\"
ECHO.
where wmic >nul 2>&1
if !errorlevel! equ 0 (
for /f %%x in ('wmic logicaldisk get name ^| more') do (
set tdrive=%%x
if "!tdrive:~1,2!" == ":" (
%%x
CALL :ColorLine " %E%33m[+]%E%97m FILES THAT CONTAINS THE WORD PASSWORD WITH EXTENSION: .xml .ini .txt *.cfg *.config"
findstr /s/n/m/i password *.xml *.ini *.txt *.cfg *.config 2>nul | findstr /v /i "\\AppData\\Local \\WinSxS ApnDatabase.xml \\UEV\\InboxTemplates \\Microsoft.Windows.Cloud \\Notepad\+\+\\ vmware cortana alphabet \\7-zip\\" 2>nul
ECHO.
CALL :ColorLine " %E%33m[+]%E%97m FILES WHOSE NAME CONTAINS THE WORD PASS CRED or .config not inside \Windows\"
dir /s/b *pass* == *cred* == *.config* == *.cfg 2>nul | findstr /v /i "\\windows\\"
ECHO.
)
)
) else (
for /f %%x in ('powershell -command "Get-PSDrive -PSProvider FileSystem | Where-Object {$_.Root -match ':'} | Select-Object -ExpandProperty Name"') do (
%%x:
CALL :ColorLine " %E%33m[+]%E%97m FILES THAT CONTAINS THE WORD PASSWORD WITH EXTENSION: .xml .ini .txt *.cfg *.config"
findstr /s/n/m/i password *.xml *.ini *.txt *.cfg *.config 2>nul | findstr /v /i "\\AppData\\Local \\WinSxS ApnDatabase.xml \\UEV\\InboxTemplates \\Microsoft.Windows.Cloud \\Notepad\+\+\\ vmware cortana alphabet \\7-zip\\" 2>nul
ECHO.
CALL :ColorLine " %E%33m[+]%E%97m FILES WHOSE NAME CONTAINS THE WORD PASS CRED or .config not inside \Windows\"
dir /s/b *pass* == *cred* == *.config* == *.cfg 2>nul | findstr /v /i "\\windows\\"
ECHO.
)
)
CALL :T_Progress 2
@@ -654,7 +707,8 @@ EXIT /B
:SetOnce
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"
EXIT /B
@@ -666,5 +720,5 @@ EXIT /B
:ColorLine
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

View File

@@ -22,7 +22,7 @@ $url = "https://github.com/peass-ng/PEASS-ng/releases/latest/download/winPEASany
# One liner to download and execute winPEASany from memory in a PS shell
$wp=[System.Reflection.Assembly]::Load([byte[]](Invoke-WebRequest "$url" -UseBasicParsing | Select-Object -ExpandProperty Content)); [winPEAS.Program]::Main("")
# The 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));
[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.
New in this version:
- Detect potential GPO abuse by flagging writable SYSVOL paths for GPOs applied to the current host and by highlighting membership in the "Group Policy Creator Owners" group.
It should take only a **few seconds** to execute almost all the checks and **some seconds/minutes during the lasts checks searching for known filenames** that could contain passwords (the time depened on the number of files in your home folder). By default only **some** filenames that could contain credentials are searched, you can use the **searchall** parameter to search all the list (this could will add some minutes).
The tool is based on **[SeatBelt](https://github.com/GhostPack/Seatbelt)**.
### New (AD-aware) checks
- Active Directory quick checks now include:
- gMSA readable managed passwords: enumerate msDS-GroupManagedServiceAccount objects and report those where the current user/group is allowed to retrieve the managed password (PrincipalsAllowedToRetrieveManagedPassword).
- AD CS (ESC4) hygiene: enumerate published certificate templates and highlight templates where the current user/group has dangerous control rights (GenericAll/WriteDacl/WriteOwner/WriteProperty/ExtendedRight) that could allow template abuse (e.g., ESC4 -> ESC1).
These checks are lightweight, read-only, and only run when the host is domain-joined.
## Where are my COLORS?!?!?!
The **ouput will be colored** using **ansi** colors. If you are executing `winpeas.exe` **from a Windows console**, you need to set a registry value to see the colors (and open a new CMD):

View File

@@ -39,7 +39,7 @@
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Text.RegularExpressions" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.1.1.0" newVersion="4.1.1.0" />
<bindingRedirect oldVersion="0.0.0.0-4.1.0.0" newVersion="4.1.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Linq" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />

View File

@@ -39,7 +39,7 @@
<package id="System.Runtime.Numerics" version="4.3.0" targetFramework="net452" />
<package id="System.Text.Encoding" version="4.3.0" targetFramework="net452" />
<package id="System.Text.Encoding.Extensions" version="4.3.0" targetFramework="net452" />
<package id="System.Text.RegularExpressions" version="4.3.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.Tasks" version="4.3.0" targetFramework="net452" />
<package id="System.Threading.Timer" version="4.3.0" targetFramework="net452" />

View File

@@ -83,6 +83,10 @@
<Reference Include="System.Runtime.InteropServices.RuntimeInformation, Version=4.0.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Runtime.InteropServices.RuntimeInformation.4.3.0\lib\net45\System.Runtime.InteropServices.RuntimeInformation.dll</HintPath>
</Reference>
<Reference Include="System.Text.RegularExpressions, Version=4.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<HintPath>..\packages\System.Text.RegularExpressions.4.3.1\lib\net463\System.Text.RegularExpressions.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />

View File

@@ -32,7 +32,7 @@
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Bcl.AsyncInterfaces" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-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>
<assemblyIdentity name="Microsoft.Win32.Primitives" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
@@ -156,7 +156,7 @@
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Text.Encodings.Web" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-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>
<assemblyIdentity name="System.Text.RegularExpressions" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />

View 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);
}
}
}
}

View File

@@ -90,6 +90,7 @@ namespace winPEAS.Checks
new SystemCheck("servicesinfo", new ServicesInfo()),
new SystemCheck("applicationsinfo", new ApplicationsInfo()),
new SystemCheck("networkinfo", new NetworkInfo()),
new SystemCheck("activedirectoryinfo", new ActiveDirectoryInfo()),
new SystemCheck("cloudinfo", new CloudInfo()),
new SystemCheck("windowscreds", new WindowsCreds()),
new SystemCheck("browserinfo", new BrowserInfo()),

View File

@@ -102,17 +102,15 @@ namespace winPEAS.Checks
{
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)
{
Dictionary<string, string> colors = new Dictionary<string, string>()
{
{ Checks.CurrentUserName, Beaprint.ansi_color_bad },
{ handler["Reason"], Beaprint.ansi_color_bad },
};
Beaprint.DictPrint(vulnHandlers, colors, true);
colors[handler["Reason"]] = Beaprint.ansi_color_bad;
}
Beaprint.DictPrint(vulnHandlers, colors, true);
}
catch (Exception ex)
{

View File

@@ -84,6 +84,7 @@ namespace winPEAS.Checks
PrintLSAInfo,
PrintNtlmSettings,
PrintLocalGroupPolicy,
PrintPotentialGPOAbuse,
AppLockerHelper.PrintAppLockerPolicy,
PrintPrintersWMIInfo,
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()
{
try

View File

@@ -31,7 +31,7 @@ namespace winPEAS.Helpers
public static string ansi_current_user = MAGENTA;
private static string Advisory =
"winpeas should be used for authorized penetration testing and/or educational purposes only." +
"winpeas 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 devices and/or with the device owner's permission.";
@@ -129,6 +129,7 @@ namespace winPEAS.Helpers
Console.WriteLine(LCYAN + " servicesinfo" + GRAY + " Search services information" + NOCOLOR);
Console.WriteLine(LCYAN + " applicationsinfo" + GRAY + " Search installed applications information" + NOCOLOR);
Console.WriteLine(LCYAN + " networkinfo" + GRAY + " Search network information" + NOCOLOR);
Console.WriteLine(LCYAN + " activedirectoryinfo" + GRAY + " Quick AD checks (gMSA readable passwords, AD CS template rights)" + NOCOLOR);
Console.WriteLine(LCYAN + " cloudinfo" + GRAY + " Enumerate cloud information" + NOCOLOR);
Console.WriteLine(LCYAN + " windowscreds" + GRAY + " Search windows credentials" + NOCOLOR);
Console.WriteLine(LCYAN + " browserinfo" + GRAY + " Search browser information" + NOCOLOR);

View File

@@ -12,6 +12,7 @@ namespace winPEAS.Helpers
private const int CNST_SYSTEM_EXTENDED_HANDLE_INFORMATION = 64;
public const uint STATUS_INFO_LENGTH_MISMATCH = 0xC0000004;
public const int DUPLICATE_SAME_ACCESS = 0x2;
public const string elevatedProcess = "Access denied, process is probably elevated";
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
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
//PROCESS_ALL_ACCESS
if ((h.GrantedAccess & 0x001F0FFF) == h.GrantedAccess)
if ((h.GrantedAccess & 0x001F0FFF) == h.GrantedAccess || (h.GrantedAccess & 0x1FFFFF) == h.GrantedAccess)
{
vulnHandler.isVuln = true;
vulnHandler.reason = "PROCESS_ALL_ACCESS";
@@ -454,6 +455,8 @@ namespace winPEAS.Helpers
}
catch
{
data["name"] = elevatedProcess;
data["sid"] = elevatedProcess;
return data;
}
finally
@@ -469,12 +472,32 @@ namespace winPEAS.Helpers
public static PT_RELEVANT_INFO getProcInfoById(int pid)
{
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);
StringBuilder fileName = new StringBuilder(2000);
Native.Psapi.GetProcessImageFileName(proc.Handle, fileName, 2000);
try
{
Native.Psapi.GetProcessImageFileName(proc.Handle, fileName, 2000);
}
catch
{
fileName = new StringBuilder(elevatedProcess);
}
pri.pid = pid;
pri.name = proc.ProcessName;

File diff suppressed because it is too large Load Diff