Compare commits

..

20 Commits

Author SHA1 Message Date
HackTricks News Bot
91278660b8 Add linpeas privilege escalation checks from: HTB Build: Rsync’d Jenkins backup → Jenkinsfile RCE → DNS-assisted rlogin to hos 2025-08-25 13:05:55 +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
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
d5e3c2a885 Fix typo in linpeas builder output argument 2025-06-06 00:38:05 +02:00
SirBroccoli
4af321d138 Fix docstring and comment typo 2025-06-06 00:01:29 +02:00
SirBroccoli
39066f6867 Fix leftover debug code and reset state in parser 2025-06-06 00:00:39 +02:00
10 changed files with 399 additions and 26 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

@@ -33,7 +33,7 @@ if __name__ == "__main__":
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('--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()
all_modules = args.all

View File

@@ -0,0 +1,103 @@
# Title: Processes & Cron & Services & Timers - r-commands trust (rsh/rlogin/rexec)
# ID: PR_RCommands_trust
# Author: HT Bot
# Last Update: 25-08-2025
# Description: Detect hostname-based trust for Berkeley r-commands and active listeners; warn about DNS-assisted abuse.
# License: GNU GPL
# Version: 1.0
# Functions Used: print_2title, print_3title, print_list, print_info, echo_no
# Global Variables: $SEARCH_IN_FOLDER, $E, $SED_RED, $SED_RED_YELLOW
# Initial Functions:
# Generated Global Variables: $rhosts_found, $rsvc_listeners, $homes, $h, $f, $found
# Fat linpeas: 0
# Small linpeas: 1
if ! [ "$SEARCH_IN_FOLDER" ]; then
print_2title "Berkeley r-commands trust (rsh/rlogin/rexec)"
print_info "https://book.hacktricks.wiki/en/linux-hardening/privilege-escalation/index.html#r-commands-rlogin-rsh-rexec"
rhosts_found=""
# 1) Trust files: /etc/hosts.equiv and per-user ~/.rhosts
print_list "Trust files (.rhosts / hosts.equiv)? ... "
(
# /etc/hosts.equiv
if [ -r "/etc/hosts.equiv" ]; then
printf "\n/etc/hosts.equiv (perm: %s)\n" "$(stat -c %a /etc/hosts.equiv 2>/dev/null || stat -f %p /etc/hosts.equiv 2>/dev/null)"
# highlight risky entries: '+' or hosts granting any user
sed -n "1,200p" /etc/hosts.equiv 2>/dev/null | sed -${E} "s,^\s*\+.*$,${SED_RED},; s,\s+\s*$,${SED_RED},"
rhosts_found=1
fi
# Per-user .rhosts from passwd
# Use getent if available, else parse /etc/passwd
homes=$( (getent passwd 2>/dev/null || cat /etc/passwd 2>/dev/null) | awk -F: '{print $6}' | sort -u )
for h in $homes; do
f="$h/.rhosts"
if [ -r "$f" ]; then
printf "\n%s (perm: %s)\n" "$f" "$(stat -c %a "$f" 2>/dev/null || stat -f %p "$f" 2>/dev/null)"
sed -n "1,200p" "$f" 2>/dev/null | sed -${E} "s,^\s*\+.*$,${SED_RED},; s,\s+\s*$,${SED_RED},"
rhosts_found=1
fi
done
# Common root path fallback
if [ -r "/root/.rhosts" ] && ! echo "$homes" | grep -q "^/root$"; then
printf "\n/root/.rhosts (perm: %s)\n" "$(stat -c %a /root/.rhosts 2>/dev/null || stat -f %p /root/.rhosts 2>/dev/null)"
sed -n "1,200p" /root/.rhosts 2>/dev/null | sed -${E} "s,^\s*\+.*$,${SED_RED},; s,\s+\s*$,${SED_RED},"
rhosts_found=1
fi
[ "$rhosts_found" ] || echo_no
) 2>/dev/null
# 2) r-commands listeners (512 exec/rexec, 513 rlogin, 514 rsh)
print_list "Are r-commands listening? ............ "
rsvc_listeners=""
if command -v ss >/dev/null 2>&1; then
ss -tlpn 2>/dev/null | awk 'NR==1 || $4 ~ /:(512|513|514)$/ {print}' | sed -n '2,200p' | sed -${E} "s,.*,${SED_RED_YELLOW}," && rsvc_listeners=1
elif command -v netstat >/dev/null 2>&1; then
netstat -tlpn 2>/dev/null | awk 'NR==1 || $4 ~ /:(512|513|514)$/ {print}' | sed -n '2,200p' | sed -${E} "s,.*,${SED_RED_YELLOW}," && rsvc_listeners=1
fi
[ "$rsvc_listeners" ] || echo_no
# 3) inetd/xinetd/systemd configuration hints
print_list "rsh/rlogin/rexec enabled in inetd/xinetd? "
(
found=""
[ -r /etc/inetd.conf ] && grep -E "(^|\s)(rsh|rlogin|rexec)(\s|$)" /etc/inetd.conf 2>/dev/null && found=1
if ls /etc/xinetd.d/* >/dev/null 2>&1; then
grep -E "(rsh|rlogin|rexec)" /etc/xinetd.d/* 2>/dev/null && found=1
fi
[ "$found" ] || echo_no
)
print_list "rsh/rlogin/rexec sockets in systemd? .. "
(
found=""
if command -v systemctl >/dev/null 2>&1; then
systemctl list-unit-files --type=socket --no-pager 2>/dev/null | grep -E "(rlogin|rsh|rexec)" && found=1
systemctl list-sockets --no-pager 2>/dev/null | grep -E "(rlogin|rsh|rexec)" && found=1
fi
[ "$found" ] || echo_no
)
# 4) PAM rhosts trust
print_list "PAM rhosts trust enabled? ............ "
(
found=""
for p in /etc/pam.d/rlogin /etc/pam.d/rsh /etc/pam.d/rexec; do
[ -r "$p" ] && grep -E "pam_rhosts|pam_rhosts_auth" "$p" 2>/dev/null && found=1
done
[ "$found" ] || echo_no
)
# 5) Container-to-host hint
if [ -f "/.dockerenv" ]; then
print_info "Running inside a container. If host runs r-commands and root/.rhosts trusts hostnames, aligning A+PTR DNS may allow passwordless rlogin/rsh to the host."
fi
# 6) Actionable guidance
print_3title "Why risky and how to abuse"
echo "- If a trusted entry is a hostname (not an IP) and r-services are listening, an attacker controlling DNS can set matching forward (A) and reverse (PTR) records so their IP resolves to the trusted name and reverse to the same name, passing hostname checks for passwordless access (even root if in /root/.rhosts or hosts.equiv)." | sed -${E} "s,passwordless access,${SED_RED_YELLOW},"
fi

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

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

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

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

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

@@ -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.";

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;