diff --git a/.github/workflows/PR-tests.yml b/.github/workflows/PR-tests.yml index 1536b85..5a58e73 100644 --- a/.github/workflows/PR-tests.yml +++ b/.github/workflows/PR-tests.yml @@ -110,10 +110,9 @@ jobs: ref: ${{ github.head_ref }} # Setup go - - uses: actions/setup-go@v2 + - uses: actions/setup-go@v6 with: - go-version: 1.17.0-rc1 - stable: false + go-version: '1.23' - run: go version # Build linpeas diff --git a/build_lists/sensitive_files.yaml b/build_lists/sensitive_files.yaml index 0310714..5380830 100644 --- a/build_lists/sensitive_files.yaml +++ b/build_lists/sensitive_files.yaml @@ -3546,7 +3546,7 @@ search: - name: "RDCMan.settings" value: - just_list_file: True + bad_regex: "credentialsProfiles|password|encryptedPassword" type: f search_in: - common diff --git a/linPEAS/README.md b/linPEAS/README.md index 620608d..5ade213 100755 --- a/linPEAS/README.md +++ b/linPEAS/README.md @@ -102,6 +102,9 @@ It uses **/bin/sh** syntax, so can run in anything supporting `sh` (and the bina By default, **linpeas won't write anything to disk and won't try to login as any other user using `su`**. +LinPEAS keeps expanding vendor-specific coverage; as of 29-Nov-2025 it warns when IGEL OS appliances still ship the SUID `setup`/`date` helpers that allow NetworkManager/systemd configuration hijacking (Metasploit module `linux/local/igel_network_priv_esc`). + + By default linpeas takes around **4 mins** to complete, but It could take from **5 to 10 minutes** to execute all the checks using **-a** parameter *(Recommended option for CTFs)*: - From less than 1 min to 2 mins to make almost all the checks - Almost 1 min to search for possible passwords inside all the accesible files of the system diff --git a/linPEAS/builder/linpeas_parts/1_system_information/16_Protections.sh b/linPEAS/builder/linpeas_parts/1_system_information/16_Protections.sh index 50901c9..95a91b5 100644 --- a/linPEAS/builder/linpeas_parts/1_system_information/16_Protections.sh +++ b/linPEAS/builder/linpeas_parts/1_system_information/16_Protections.sh @@ -30,7 +30,7 @@ # Functions Used: echo_not_found, print_2title, print_list, warn_exec # Global Variables: # Initial Functions: -# Generated Global Variables: $ASLR, $hypervisorflag, $detectedvirt +# Generated Global Variables: $ASLR, $hypervisorflag, $detectedvirt, $unpriv_userns_clone, $perf_event_paranoid, $mmap_min_addr, $ptrace_scope, $dmesg_restrict, $kptr_restrict, $unpriv_bpf_disabled # Fat linpeas: 0 # Small linpeas: 0 @@ -80,10 +80,86 @@ print_list "Seccomp enabled? ............... "$NC print_list "User namespace? ................ "$NC if [ "$(cat /proc/self/uid_map 2>/dev/null)" ]; then echo "enabled" | sed "s,enabled,${SED_GREEN},"; else echo "disabled" | sed "s,disabled,${SED_RED},"; fi +#-- SY) Unprivileged user namespaces +print_list "unpriv_userns_clone? ........... "$NC +unpriv_userns_clone=$(cat /proc/sys/kernel/unprivileged_userns_clone 2>/dev/null) +if [ -z "$unpriv_userns_clone" ]; then + echo_not_found "/proc/sys/kernel/unprivileged_userns_clone" +else + if [ "$unpriv_userns_clone" -eq 0 ]; then echo "0" | sed -${E} "s,0,${SED_GREEN},"; else echo "$unpriv_userns_clone" | sed -${E} "s,.*,${SED_RED},g"; fi +fi + +#-- SY) Unprivileged eBPF +print_list "unpriv_bpf_disabled? ........... "$NC +unpriv_bpf_disabled=$(cat /proc/sys/kernel/unprivileged_bpf_disabled 2>/dev/null) +if [ -z "$unpriv_bpf_disabled" ]; then + echo_not_found "/proc/sys/kernel/unprivileged_bpf_disabled" +else + if [ "$unpriv_bpf_disabled" -eq 0 ]; then echo "0" | sed -${E} "s,0,${SED_RED},"; else echo "$unpriv_bpf_disabled" | sed -${E} "s,.*,${SED_GREEN},g"; fi +fi + #-- SY) cgroup2 print_list "Cgroup2 enabled? ............... "$NC ([ "$(grep cgroup2 /proc/filesystems 2>/dev/null)" ] && echo "enabled" || echo "disabled") | sed "s,disabled,${SED_RED}," | sed "s,enabled,${SED_GREEN}," +#-- SY) Kernel hardening sysctls +print_list "kptr_restrict? ................. "$NC +kptr_restrict=$(cat /proc/sys/kernel/kptr_restrict 2>/dev/null) +if [ -z "$kptr_restrict" ]; then + echo_not_found "/proc/sys/kernel/kptr_restrict" +else + if [ "$kptr_restrict" -eq 0 ]; then echo "0" | sed -${E} "s,0,${SED_RED},"; else echo "$kptr_restrict" | sed -${E} "s,.*,${SED_GREEN},g"; fi +fi + +print_list "dmesg_restrict? ................ "$NC +dmesg_restrict=$(cat /proc/sys/kernel/dmesg_restrict 2>/dev/null) +if [ -z "$dmesg_restrict" ]; then + echo_not_found "/proc/sys/kernel/dmesg_restrict" +else + if [ "$dmesg_restrict" -eq 0 ]; then echo "0" | sed -${E} "s,0,${SED_RED},"; else echo "$dmesg_restrict" | sed -${E} "s,.*,${SED_GREEN},g"; fi +fi + +print_list "ptrace_scope? .................. "$NC +ptrace_scope=$(cat /proc/sys/kernel/yama/ptrace_scope 2>/dev/null) +if [ -z "$ptrace_scope" ]; then + echo_not_found "/proc/sys/kernel/yama/ptrace_scope" +else + if [ "$ptrace_scope" -eq 0 ]; then echo "0" | sed -${E} "s,0,${SED_RED},"; else echo "$ptrace_scope" | sed -${E} "s,.*,${SED_GREEN},g"; fi +fi + +print_list "perf_event_paranoid? ........... "$NC +perf_event_paranoid=$(cat /proc/sys/kernel/perf_event_paranoid 2>/dev/null) +if [ -z "$perf_event_paranoid" ]; then + echo_not_found "/proc/sys/kernel/perf_event_paranoid" +else + if [ "$perf_event_paranoid" -le 1 ]; then echo "$perf_event_paranoid" | sed -${E} "s,.*,${SED_RED},g"; else echo "$perf_event_paranoid" | sed -${E} "s,.*,${SED_GREEN},g"; fi +fi + +print_list "mmap_min_addr? ................. "$NC +mmap_min_addr=$(cat /proc/sys/vm/mmap_min_addr 2>/dev/null) +if [ -z "$mmap_min_addr" ]; then + echo_not_found "/proc/sys/vm/mmap_min_addr" +else + if [ "$mmap_min_addr" -eq 0 ]; then echo "0" | sed -${E} "s,0,${SED_RED},"; else echo "$mmap_min_addr" | sed -${E} "s,.*,${SED_GREEN},g"; fi +fi + +print_list "lockdown mode? ................. "$NC +if [ -f "/sys/kernel/security/lockdown" ]; then + cat /sys/kernel/security/lockdown 2>/dev/null | sed -${E} "s,none,${SED_RED},g; s,integrity|confidentiality,${SED_GREEN},g" +else + echo_not_found "/sys/kernel/security/lockdown" +fi + +#-- SY) Kernel hardening config flags +print_list "Kernel hardening flags? ........ "$NC +if [ -f "/boot/config-$(uname -r)" ]; then + grep -E 'CONFIG_RANDOMIZE_BASE|CONFIG_STACKPROTECTOR|CONFIG_SLAB_FREELIST_|CONFIG_KASAN' /boot/config-$(uname -r) 2>/dev/null +elif [ -f "/proc/config.gz" ]; then + zcat /proc/config.gz 2>/dev/null | grep -E 'CONFIG_RANDOMIZE_BASE|CONFIG_STACKPROTECTOR|CONFIG_SLAB_FREELIST_|CONFIG_KASAN' +else + echo_not_found "kernel config" +fi + #-- SY) Gatekeeper if [ "$MACPEAS" ]; then print_list "Gatekeeper enabled? .......... "$NC @@ -136,4 +212,4 @@ else if [ "$hypervisorflag" ]; then printf $RED"Yes"$NC; else printf $GREEN"No"$NC; fi fi -echo "" \ No newline at end of file +echo "" diff --git a/linPEAS/builder/linpeas_parts/1_system_information/17_Kernel_Modules.sh b/linPEAS/builder/linpeas_parts/1_system_information/17_Kernel_Modules.sh index 41490d6..9beb7e0 100644 --- a/linPEAS/builder/linpeas_parts/1_system_information/17_Kernel_Modules.sh +++ b/linPEAS/builder/linpeas_parts/1_system_information/17_Kernel_Modules.sh @@ -58,5 +58,23 @@ else echo_not_found "/proc/sys/kernel/modules_disabled" fi +# Check for module signature enforcement +print_3title "Module signature enforcement? " +if [ -f "/proc/sys/kernel/module_sig_enforce" ]; then + if [ "$(cat /proc/sys/kernel/module_sig_enforce)" = "1" ]; then + echo "Enforced" | sed -${E} "s,.*,${SED_GREEN},g" + else + echo "Not enforced" | sed -${E} "s,.*,${SED_RED},g" + fi +elif [ -f "/sys/module/module/parameters/sig_enforce" ]; then + if [ "$(cat /sys/module/module/parameters/sig_enforce)" = "Y" ]; then + echo "Enforced" | sed -${E} "s,.*,${SED_GREEN},g" + else + echo "Not enforced" | sed -${E} "s,.*,${SED_RED},g" + fi +else + echo_not_found "module_sig_enforce" +fi -echo "" \ No newline at end of file + +echo "" diff --git a/linPEAS/builder/linpeas_parts/7_software_information/Ssh.sh b/linPEAS/builder/linpeas_parts/7_software_information/Ssh.sh index 7ee753c..65947a0 100644 --- a/linPEAS/builder/linpeas_parts/7_software_information/Ssh.sh +++ b/linPEAS/builder/linpeas_parts/7_software_information/Ssh.sh @@ -33,17 +33,17 @@ grep "PermitRootLogin \|ChallengeResponseAuthentication \|PasswordAuthentication if ! [ "$SEARCH_IN_FOLDER" ]; then if [ "$TIMEOUT" ]; then - privatekeyfilesetc=$(timeout 40 grep -rl '\-\-\-\-\-BEGIN .* PRIVATE KEY.*\-\-\-\-\-' /etc 2>/dev/null) - privatekeyfileshome=$(timeout 40 grep -rl '\-\-\-\-\-BEGIN .* PRIVATE KEY.*\-\-\-\-\-' $HOMESEARCH 2>/dev/null) - privatekeyfilesroot=$(timeout 40 grep -rl '\-\-\-\-\-BEGIN .* PRIVATE KEY.*\-\-\-\-\-' /root 2>/dev/null) - privatekeyfilesmnt=$(timeout 40 grep -rl '\-\-\-\-\-BEGIN .* PRIVATE KEY.*\-\-\-\-\-' /mnt 2>/dev/null) + privatekeyfilesetc=$(timeout 40 grep -rl '\-\-\-\-\-BEGIN .* PRIVATE KEY\-\-\-\-\-' /etc 2>/dev/null) + privatekeyfileshome=$(timeout 40 grep -rl '\-\-\-\-\-BEGIN .* PRIVATE KEY\-\-\-\-\-' $HOMESEARCH 2>/dev/null) + privatekeyfilesroot=$(timeout 40 grep -rl '\-\-\-\-\-BEGIN .* PRIVATE KEY\-\-\-\-\-' /root 2>/dev/null) + privatekeyfilesmnt=$(timeout 40 grep -rl '\-\-\-\-\-BEGIN .* PRIVATE KEY\-\-\-\-\-' /mnt 2>/dev/null) else - privatekeyfilesetc=$(grep -rl '\-\-\-\-\-BEGIN .* PRIVATE KEY.*\-\-\-\-\-' /etc 2>/dev/null) #If there is tons of files linpeas gets frozen here without a timeout - privatekeyfileshome=$(grep -rl '\-\-\-\-\-BEGIN .* PRIVATE KEY.*\-\-\-\-\-' $HOME/.ssh 2>/dev/null) + privatekeyfilesetc=$(grep -rl '\-\-\-\-\-BEGIN .* PRIVATE KEY\-\-\-\-\-' /etc 2>/dev/null) #If there is tons of files linpeas gets frozen here without a timeout + privatekeyfileshome=$(grep -rl '\-\-\-\-\-BEGIN .* PRIVATE KEY\-\-\-\-\-' $HOME/.ssh 2>/dev/null) fi else # If $SEARCH_IN_FOLDER lets just search for private keys in the whole firmware - privatekeyfilesetc=$(timeout 120 grep -rl '\-\-\-\-\-BEGIN .* PRIVATE KEY.*\-\-\-\-\-' "$ROOT_FOLDER" 2>/dev/null) + privatekeyfilesetc=$(timeout 120 grep -rl '\-\-\-\-\-BEGIN .* PRIVATE KEY\-\-\-\-\-' "$ROOT_FOLDER" 2>/dev/null) fi if [ "$privatekeyfilesetc" ] || [ "$privatekeyfileshome" ] || [ "$privatekeyfilesroot" ] || [ "$privatekeyfilesmnt" ] ; then diff --git a/linPEAS/builder/linpeas_parts/8_interesting_perms_files/14_Writable_files_owner_all.sh b/linPEAS/builder/linpeas_parts/8_interesting_perms_files/14_Writable_files_owner_all.sh index 894d5bf..a6c0918 100644 --- a/linPEAS/builder/linpeas_parts/8_interesting_perms_files/14_Writable_files_owner_all.sh +++ b/linPEAS/builder/linpeas_parts/8_interesting_perms_files/14_Writable_files_owner_all.sh @@ -17,7 +17,7 @@ if ! [ "$IAMROOT" ]; then print_2title "Interesting writable files owned by me or writable by everyone (not in Home) (max 200)" print_info "https://book.hacktricks.wiki/en/linux-hardening/privilege-escalation/index.html#writable-files" #In the next file, you need to specify type "d" and "f" to avoid fake link files apparently writable by all - obmowbe=$(find $ROOT_FOLDER '(' -type f -or -type d ')' '(' '(' -user $USER ')' -or '(' -perm -o=w ')' ')' ! -path "/proc/*" ! -path "/sys/*" ! -path "$HOME/*" 2>/dev/null | grep -Ev "$notExtensions" | sort | uniq | awk -F/ '{line_init=$0; if (!cont){ cont=0 }; $NF=""; act=$0; if (act == pre){(cont += 1)} else {cont=0}; if (cont < 5){ print line_init; } if (cont == "5"){print "#)You_can_write_even_more_files_inside_last_directory\n"}; pre=act }' | head -n 200) + obmowbe=$(find $ROOT_FOLDER '(' -type f -or -type d ')' '(' '(' -user $USER ')' -or '(' -perm -o=w ')' ')' ! -path "/proc/*" ! -path "/sys/*" ! -path "/dev/*" ! -path "/snap/*" ! -path "$HOME/*" 2>/dev/null | grep -Ev "$notExtensions" | sort | uniq | awk -F/ '{line_init=$0; if (!cont){ cont=0 }; $NF=""; act=$0; if (act == pre){(cont += 1)} else {cont=0}; if (cont < 5){ print line_init; } if (cont == "5"){print "#)You_can_write_even_more_files_inside_last_directory\n"}; pre=act }' | head -n 200) printf "%s\n" "$obmowbe" | while read l; do if echo "$l" | grep -q "You_can_write_even_more_files_inside_last_directory"; then printf $ITALIC"$l\n"$NC; elif echo "$l" | grep -qE "$writeVB"; then diff --git a/linPEAS/builder/linpeas_parts/8_interesting_perms_files/16_IGEL_OS_SUID.sh b/linPEAS/builder/linpeas_parts/8_interesting_perms_files/16_IGEL_OS_SUID.sh new file mode 100644 index 0000000..9a7d312 --- /dev/null +++ b/linPEAS/builder/linpeas_parts/8_interesting_perms_files/16_IGEL_OS_SUID.sh @@ -0,0 +1,80 @@ +# Title: Interesting Permissions Files - IGEL OS SUID setup/date abuse +# ID: IP_IGEL_OS_SUID +# Author: HT Bot +# Last Update: 29-11-2025 +# Description: Detect IGEL OS environments that expose the SUID-root `setup`/`date` binaries and highlight writable NetworkManager/systemd configs that enable the documented privilege escalation chain (Metasploit linux/local/igel_network_priv_esc). +# License: GNU GPL +# Version: 1.0 +# Functions Used: print_2title, print_info +# Global Variables: $ITALIC, $NC, $SED_GREEN, $SED_RED, $SED_RED_YELLOW, $SUPERFAST +# Initial Functions: +# Generated Global Variables: $igel_markers, $igel_marker_sources, $marker, $igel_suid_hits, $candidate, $writable_nm, $writable_systemd, $unitdir, $tmp_units +# Fat linpeas: 0 +# Small linpeas: 1 + +igel_markers="" +igel_marker_sources="" +if [ -f /etc/os-release ] && grep -qi "igel" /etc/os-release 2>/dev/null; then + igel_markers="Yes" + igel_marker_sources="/etc/os-release" +fi +if [ -f /etc/issue ] && grep -qi "igel" /etc/issue 2>/dev/null; then + igel_markers="Yes" + igel_marker_sources="${igel_marker_sources} /etc/issue" +fi +for marker in /etc/igel /wfs/igel /userhome/.igel /config/sessions/igel; do + if [ -e "$marker" ]; then + igel_markers="Yes" + igel_marker_sources="${igel_marker_sources} $marker" + fi +done + +igel_suid_hits="" +for candidate in /usr/bin/setup /bin/setup /usr/sbin/setup /opt/igel/bin/setup /usr/bin/date /bin/date /usr/lib/igel/date; do + if [ -u "$candidate" ]; then + igel_suid_hits="${igel_suid_hits}$(ls -lah "$candidate" 2>/dev/null)\n" + fi +done + +if [ -n "$igel_markers" ] || [ -n "$igel_suid_hits" ]; then + print_2title "IGEL OS SUID setup/date privilege escalation surface" + print_info "https://www.rapid7.com/blog/post/pt-metasploit-wrap-up-11-28-2025" + if [ -n "$igel_markers" ]; then + echo "Potential IGEL OS detected via: $igel_marker_sources" | sed -${E} "s,.*,${SED_GREEN}," + else + echo "IGEL-specific SUID helpers found but IGEL markers were not detected" | sed -${E} "s,.*,${SED_RED}," + fi + if [ -n "$igel_suid_hits" ]; then + echo "SUID-root helpers exposing configuration primitives:" | sed -${E} "s,.*,${SED_RED_YELLOW}," + printf "%b" "$igel_suid_hits" + else + echo "No SUID setup/date binaries were located (system may be patched)." + fi + + writable_nm="" + writable_systemd="" + if ! [ "$SUPERFAST" ]; then + if [ -d /etc/NetworkManager ]; then + writable_nm=$(find /etc/NetworkManager -maxdepth 3 -type f -writable 2>/dev/null | head -n 25) + fi + for unitdir in /etc/systemd/system /lib/systemd/system /usr/lib/systemd/system; do + if [ -d "$unitdir" ]; then + tmp_units=$(find "$unitdir" -maxdepth 2 -type f -writable 2>/dev/null | head -n 15) + if [ -n "$tmp_units" ]; then + writable_systemd="${writable_systemd}${tmp_units}\n" + fi + fi + done + fi + + if [ -n "$writable_nm" ]; then + echo "Writable NetworkManager profiles/hooks (swap Exec path to your payload):" | sed -${E} "s,.*,${SED_RED_YELLOW}," + echo "$writable_nm" + fi + if [ -n "$writable_systemd" ]; then + echo "Writable systemd unit files (edit ExecStart, then restart via setup/date):" | sed -${E} "s,.*,${SED_RED_YELLOW}," + printf "%b" "$writable_systemd" + fi + printf "$ITALIC Known exploitation chain: Use the SUID setup/date binaries to edit NetworkManager or systemd configs so ExecStart points to your payload, then trigger a service restart via the same helper to run as root (Metasploit linux/local/igel_network_priv_esc).$NC\n" +fi +echo "" diff --git a/linPEAS/builder/linpeas_parts/8_interesting_perms_files/16_Writable_root_execs.sh b/linPEAS/builder/linpeas_parts/8_interesting_perms_files/16_Writable_root_execs.sh new file mode 100644 index 0000000..1f86216 --- /dev/null +++ b/linPEAS/builder/linpeas_parts/8_interesting_perms_files/16_Writable_root_execs.sh @@ -0,0 +1,36 @@ +# Title: Interesting Permissions Files - Writable root-owned executables +# ID: IP_Writable_root_execs +# Author: HT Bot +# Last Update: 29-11-2025 +# Description: Locate root-owned executables outside home folders that the current user can modify +# License: GNU GPL +# Version: 1.0 +# Functions Used: print_2title, print_info, echo_not_found +# Global Variables: $DEBUG, $IAMROOT, $ROOT_FOLDER, $HOME, $writeVB +# Initial Functions: +# Generated Global Variables: $writable_root_execs +# Fat linpeas: 0 +# Small linpeas: 1 + +if ! [ "$IAMROOT" ]; then + print_2title "Writable root-owned executables I can modify (max 200)" + print_info "https://book.hacktricks.wiki/en/linux-hardening/privilege-escalation/index.html#writable-files" + + writable_root_execs=$( + find "$ROOT_FOLDER" -type f -user root -perm -u=x \ + \( -perm -g=w -o -perm -o=w \) \ + ! -path "/proc/*" ! -path "/sys/*" ! -path "/run/*" ! -path "/dev/*" ! -path "/snap/*" ! -path "$HOME/*" 2>/dev/null \ + | while IFS= read -r f; do + if [ -w "$f" ]; then + ls -l "$f" 2>/dev/null + fi + done | head -n 200 + ) + + if [ "$writable_root_execs" ] || [ "$DEBUG" ]; then + printf "%s\n" "$writable_root_execs" | sed -${E} "s,$writeVB,${SED_RED_YELLOW}," + else + echo_not_found "Writable root-owned executables" + fi + echo "" +fi diff --git a/linPEAS/builder/linpeas_parts/functions/su_try_pwd.sh b/linPEAS/builder/linpeas_parts/functions/su_try_pwd.sh index 306e0f5..f7a868d 100644 --- a/linPEAS/builder/linpeas_parts/functions/su_try_pwd.sh +++ b/linPEAS/builder/linpeas_parts/functions/su_try_pwd.sh @@ -1,7 +1,7 @@ # Title: LinPeasBase - su_try_pwd # ID: su_try_pwd # Author: Carlos Polop -# Last Update: 22-08-2023 +# Last Update: 15-12-2025 # Description: Try to login as user using a password # License: GNU GPL # Version: 1.0 @@ -17,7 +17,7 @@ su_try_pwd(){ BFUSER=$1 PASSWORDTRY=$2 trysu=$(echo "$PASSWORDTRY" | timeout 1 su $BFUSER -c whoami 2>/dev/null) - if [ "$trysu" ]; then + if [ $? -eq 0 ]; then echo " You can login as $BFUSER using password: $PASSWORDTRY" | sed -${E} "s,.*,${SED_RED_YELLOW}," fi -} \ No newline at end of file +} diff --git a/linPEAS/builder/src/linpeasBuilder.py b/linPEAS/builder/src/linpeasBuilder.py index 40c8b51..d53eec7 100644 --- a/linPEAS/builder/src/linpeasBuilder.py +++ b/linPEAS/builder/src/linpeasBuilder.py @@ -115,7 +115,7 @@ class LinpeasBuilder: suidVB, sudoVB, capsVB = self.__get_gtfobins_lists() assert len(suidVB) > 185, f"Len suidVB is {len(suidVB)}" assert len(sudoVB) > 250, f"Len sudo is {len(sudoVB)}" - assert len(capsVB) > 10, f"Len suidVB is {len(capsVB)}" + assert len(capsVB) > 2, f"Len capsVB is {len(capsVB)}" self.__replace_mark(SUIDVB1_MARKUP, suidVB[:int(len(suidVB)/2)], "|") self.__replace_mark(SUIDVB2_MARKUP, suidVB[int(len(suidVB)/2):], "|") @@ -348,8 +348,25 @@ class LinpeasBuilder: return bin_b64 def __get_gtfobins_lists(self) -> tuple: - r = requests.get("https://github.com/GTFOBins/GTFOBins.github.io/tree/master/_gtfobins") - bins = re.findall(r'_gtfobins/([a-zA-Z0-9_ \-]+).md', r.text) + bins = [] + api_url = "https://api.github.com/repos/GTFOBins/GTFOBins.github.io/contents/_gtfobins?per_page=100" + while api_url: + r = requests.get(api_url, timeout=10) + if not r.ok: + break + data = r.json() + for entry in data: + if entry.get("type") == "file" and entry.get("name"): + bins.append(entry["name"]) + api_url = None + link = r.headers.get("Link", "") + for part in link.split(","): + if 'rel="next"' in part: + api_url = part.split(";")[0].strip().strip("<>") + break + if not bins: + r = requests.get("https://github.com/GTFOBins/GTFOBins.github.io/tree/master/_gtfobins", timeout=10) + bins = re.findall(r'_gtfobins/([a-zA-Z0-9_ \-]+)(?:\\.md)?', r.text) sudoVB = [] suidVB = [] @@ -357,12 +374,12 @@ class LinpeasBuilder: for b in bins: try: - rb = requests.get(f"https://raw.githubusercontent.com/GTFOBins/GTFOBins.github.io/master/_gtfobins/{b}.md", timeout=5) + rb = requests.get(f"https://raw.githubusercontent.com/GTFOBins/GTFOBins.github.io/master/_gtfobins/{b}", timeout=5) except: try: - rb = requests.get(f"https://raw.githubusercontent.com/GTFOBins/GTFOBins.github.io/master/_gtfobins/{b}.md", timeout=5) + rb = requests.get(f"https://raw.githubusercontent.com/GTFOBins/GTFOBins.github.io/master/_gtfobins/{b}", timeout=5) except: - rb = requests.get(f"https://raw.githubusercontent.com/GTFOBins/GTFOBins.github.io/master/_gtfobins/{b}.md", timeout=5) + rb = requests.get(f"https://raw.githubusercontent.com/GTFOBins/GTFOBins.github.io/master/_gtfobins/{b}", timeout=5) if "sudo:" in rb.text: if len(b) <= 3: sudoVB.append("[^a-zA-Z0-9]"+b+"$") # Less false possitives applied to small names diff --git a/winPEAS/winPEASexe/README.md b/winPEAS/winPEASexe/README.md index 91f7ef5..7be2142 100755 --- a/winPEAS/winPEASexe/README.md +++ b/winPEAS/winPEASexe/README.md @@ -219,7 +219,7 @@ Once you have installed and activated it you need to: - [x] SCCM - [x] Security Package Credentials - [x] AlwaysInstallElevated - - [x] WSUS + - [x] WSUS (HTTP downgrade + CVE-2025-59287 exposure) - **Browser Information** - [x] Firefox DBs diff --git a/winPEAS/winPEASexe/winPEAS/Checks/SystemInfo.cs b/winPEAS/winPEASexe/winPEAS/Checks/SystemInfo.cs index e72b31d..0ace4b3 100644 --- a/winPEAS/winPEASexe/winPEAS/Checks/SystemInfo.cs +++ b/winPEAS/winPEASexe/winPEAS/Checks/SystemInfo.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using System.Management; using System.Reflection; using System.Runtime.InteropServices; using System.Text.RegularExpressions; @@ -562,27 +563,66 @@ namespace winPEAS.Checks { Beaprint.MainPrint("Checking WSUS"); Beaprint.LinkPrint("https://book.hacktricks.wiki/en/windows-hardening/windows-local-privilege-escalation/index.html#wsus"); - string path = "Software\\Policies\\Microsoft\\Windows\\WindowsUpdate"; - string path2 = "Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU"; - string HKLM_WSUS = RegistryHelper.GetRegValue("HKLM", path, "WUServer"); - string using_HKLM_WSUS = RegistryHelper.GetRegValue("HKLM", path2, "UseWUServer"); - if (HKLM_WSUS.Contains("http://")) + string policyPath = "Software\\Policies\\Microsoft\\Windows\\WindowsUpdate"; + string policyAUPath = "Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU"; + string wsusPolicyValue = RegistryHelper.GetRegValue("HKLM", policyPath, "WUServer"); + string useWUServerValue = RegistryHelper.GetRegValue("HKLM", policyAUPath, "UseWUServer"); + + if (!string.IsNullOrEmpty(wsusPolicyValue) && wsusPolicyValue.StartsWith("http://", StringComparison.OrdinalIgnoreCase)) { - Beaprint.BadPrint(" WSUS is using http: " + HKLM_WSUS); + Beaprint.BadPrint(" WSUS is using http: " + wsusPolicyValue); Beaprint.InfoPrint("You can test https://github.com/pimps/wsuxploit to escalate privileges"); - if (using_HKLM_WSUS == "1") + if (useWUServerValue == "1") Beaprint.BadPrint(" And UseWUServer is equals to 1, so it is vulnerable!"); - else if (using_HKLM_WSUS == "0") + else if (useWUServerValue == "0") Beaprint.GoodPrint(" But UseWUServer is equals to 0, so it is not vulnerable!"); else - Console.WriteLine(" But UseWUServer is equals to " + using_HKLM_WSUS + ", so it may work or not"); + Console.WriteLine(" But UseWUServer is equals to " + useWUServerValue + ", so it may work or not"); } else { - if (string.IsNullOrEmpty(HKLM_WSUS)) + if (string.IsNullOrEmpty(wsusPolicyValue)) Beaprint.NotFoundPrint(); else - Beaprint.GoodPrint(" WSUS value: " + HKLM_WSUS); + Beaprint.GoodPrint(" WSUS value: " + wsusPolicyValue); + } + + if (!string.IsNullOrEmpty(wsusPolicyValue)) + { + bool clientsForced = useWUServerValue == "1"; + if (clientsForced) + { + Beaprint.BadPrint(" CVE-2025-59287: Clients talk to WSUS at " + wsusPolicyValue + " (UseWUServer=1). Unpatched WSUS allows unauthenticated deserialization to SYSTEM."); + } + else + { + Beaprint.InfoPrint(" CVE-2025-59287: WSUS endpoint discovered at " + wsusPolicyValue + ". Confirm patch level before attempting exploitation."); + if (!string.IsNullOrEmpty(useWUServerValue)) + Beaprint.InfoPrint(" UseWUServer is set to " + useWUServerValue + ", clients may still reach Microsoft Update."); + } + } + + string wsusSetupPath = @"SOFTWARE\Microsoft\Update Services\Server\Setup"; + string wsusVersion = RegistryHelper.GetRegValue("HKLM", wsusSetupPath, "VersionString"); + string wsusInstallPath = RegistryHelper.GetRegValue("HKLM", wsusSetupPath, "InstallPath"); + bool wsusRoleDetected = !string.IsNullOrEmpty(wsusVersion) || !string.IsNullOrEmpty(wsusInstallPath); + + if (TryGetServiceStateAndAccount("WSUSService", out string wsusServiceState, out string wsusServiceAccount)) + { + wsusRoleDetected = true; + string serviceMsg = " WSUSService status: " + wsusServiceState; + if (!string.IsNullOrEmpty(wsusServiceAccount)) + serviceMsg += " (runs as " + wsusServiceAccount + ")"; + Beaprint.BadPrint(serviceMsg); + } + + if (wsusRoleDetected) + { + if (!string.IsNullOrEmpty(wsusVersion)) + Beaprint.BadPrint(" WSUS Server version: " + wsusVersion + " (verify patch level for CVE-2025-59287)."); + if (!string.IsNullOrEmpty(wsusInstallPath)) + Beaprint.InfoPrint(" WSUS install path: " + wsusInstallPath); + Beaprint.BadPrint(" CVE-2025-59287: Local WSUS server exposes an unauthenticated deserialization surface reachable over HTTP(S). Patch or restrict access."); } } catch (Exception ex) @@ -591,6 +631,32 @@ namespace winPEAS.Checks } } + private static bool TryGetServiceStateAndAccount(string serviceName, out string state, out string account) + { + state = string.Empty; + account = string.Empty; + + try + { + string query = $"SELECT Name, State, StartName FROM Win32_Service WHERE Name='{serviceName.Replace("'", "''")}'"; + using (var searcher = new ManagementObjectSearcher(@"root\cimv2", query)) + { + foreach (ManagementObject service in searcher.Get()) + { + state = service["State"]?.ToString() ?? string.Empty; + account = service["StartName"]?.ToString() ?? string.Empty; + return true; + } + } + } + catch (Exception ex) + { + Beaprint.PrintException(ex.Message); + } + + return false; + } + static void PrintKrbRelayUp() { try