From b8528da949655e726a2cc0c1aa0f50c76237cc88 Mon Sep 17 00:00:00 2001 From: SirBroccoli Date: Sun, 22 Mar 2026 21:44:38 +0100 Subject: [PATCH] Improve container and runtime enumeration (#624) * Improve container and runtime enumeration * Fix CI failures for PR #624 --------- Co-authored-by: chack-agent --- .../2_container/3_Container_details.sh | 111 +++++--- .../2_container/4_Docker_container_details.sh | 4 +- .../2_container/5_Container_breakout.sh | 239 ++++++++++-------- .../2_container/7_RW_bind_mounts_nosuid.sh | 2 +- .../7_software_information/Containerd.sh | 30 ++- .../7_software_information/Docker.sh | 4 +- .../functions/checkCreateReleaseAgent.sh | 15 +- .../functions/checkProcSysBreakouts.sh | 71 ++++-- .../linpeas_parts/functions/containerCheck.sh | 27 +- .../functions/enumerateDockerSockets.sh | 19 +- 10 files changed, 333 insertions(+), 189 deletions(-) diff --git a/linPEAS/builder/linpeas_parts/2_container/3_Container_details.sh b/linPEAS/builder/linpeas_parts/2_container/3_Container_details.sh index 5881cf0..39ddec1 100644 --- a/linPEAS/builder/linpeas_parts/2_container/3_Container_details.sh +++ b/linPEAS/builder/linpeas_parts/2_container/3_Container_details.sh @@ -1,35 +1,15 @@ # Title: Container - Container details # ID: CT_Container_details # Author: Carlos Polop -# Last Update: 07-03-2024 -# Description: Get detailed container information relevant to privilege escalation: -# - Container type and runtime -# - Running containers -# - Container configuration -# - Common vulnerable scenarios: -# * Misconfigured containers -# * Privileged containers -# * Exposed container APIs -# * Container networking -# - Exploitation methods: -# * Container breakout: Exploit container misconfigurations -# * Common attack vectors: -# - Runtime escape -# - Privilege escalation -# - Container breakout -# - Network escape -# * Exploit techniques: -# - Container misconfiguration abuse -# - Privileged container exploitation -# - Container API abuse -# - Network escape techniques +# Last Update: 21-03-2026 +# Description: Gather general container runtime context, local runtime CLIs, and visible container-management surfaces relevant to privilege escalation. # License: GNU GPL # Version: 1.0 # Mitre: T1613,T1611 -# Functions Used: containerCheck, echo_no, print_2title, print_list, warn_exec +# Functions Used: containerCheck, echo_no, enumerateDockerSockets, print_2title, print_list, warn_exec # Global Variables: $containerType # Initial Functions: containerCheck -# Generated Global Variables: $dockercontainers, $podmancontainers, $lxccontainers, $rktcontainers, $containerCounts +# Generated Global Variables: $containerCounts, $crictlcontainers, $ctrcontainers, $dockercontainers, $lxccontainers, $nerdctlcontainers, $podmancontainers, $rktcontainers # Fat linpeas: 0 # Small linpeas: 1 @@ -44,6 +24,14 @@ if [ -f "/run/systemd/container" ]; then print_list "Systemd Container ..............$NC $(cat /run/systemd/container)" fi +if [ -f "/run/.containerenv" ]; then + print_list "Podman/OCI marker ..............$NC /run/.containerenv" +fi + +if [ -f "/.dockerenv" ]; then + print_list "Docker marker ..................$NC /.dockerenv" +fi + # Get container runtime info if [ "$(command -v docker || echo -n '')" ]; then print_list "Docker version ...............$NC " @@ -66,13 +54,58 @@ if [ "$(command -v lxc || echo -n '')" ]; then warn_exec lxc info fi +if [ "$(command -v crio || echo -n '')" ]; then + print_list "CRI-O version ..............$NC " + warn_exec crio --version +fi + +if [ "$(command -v runc || echo -n '')" ]; then + print_list "runc version ...............$NC " + warn_exec runc --version +fi + +if [ "$(command -v crun || echo -n '')" ]; then + print_list "crun version ...............$NC " + warn_exec crun --version +fi + +if [ "$(command -v nerdctl || echo -n '')" ]; then + print_list "nerdctl version ............$NC " + warn_exec nerdctl version +fi + +if [ "$(command -v crictl || echo -n '')" ]; then + print_list "crictl version .............$NC " + warn_exec crictl version +fi + +if [ "$(command -v ctr || echo -n '')" ]; then + print_list "ctr version ................$NC " + warn_exec ctr version +fi + +print_list "Interesting runtime sockets ... "$NC +enumerateDockerSockets + print_list "Any running containers? ........ "$NC # Get counts of running containers for each platform -dockercontainers=$(docker ps --format "{{.Names}}" 2>/dev/null | wc -l) -podmancontainers=$(podman ps --format "{{.Names}}" 2>/dev/null | wc -l) -lxccontainers=$(lxc list -c n --format csv 2>/dev/null | wc -l) -rktcontainers=$(rkt list 2>/dev/null | tail -n +2 | wc -l) -if [ "$dockercontainers" -eq "0" ] && [ "$lxccontainers" -eq "0" ] && [ "$rktcontainers" -eq "0" ] && [ "$podmancontainers" -eq "0" ]; then +dockercontainers=0 +podmancontainers=0 +lxccontainers=0 +rktcontainers=0 +nerdctlcontainers=0 +crictlcontainers=0 +ctrcontainers=0 + +if [ "$(command -v docker || echo -n '')" ]; then dockercontainers=$(docker ps --format "{{.Names}}" 2>/dev/null | wc -l | tr -d ' '); fi +if [ "$(command -v podman || echo -n '')" ]; then podmancontainers=$(podman ps --format "{{.Names}}" 2>/dev/null | wc -l | tr -d ' '); fi +if [ "$(command -v lxc || echo -n '')" ]; then lxccontainers=$(lxc list -c n --format csv 2>/dev/null | wc -l | tr -d ' '); fi +if [ "$(command -v rkt || echo -n '')" ]; then rktcontainers=$(rkt list 2>/dev/null | tail -n +2 | wc -l | tr -d ' '); fi +if [ "$(command -v nerdctl || echo -n '')" ]; then nerdctlcontainers=$(nerdctl ps --format "{{.Names}}" 2>/dev/null | wc -l | tr -d ' '); fi +if [ "$(command -v crictl || echo -n '')" ]; then crictlcontainers=$(crictl ps -q 2>/dev/null | wc -l | tr -d ' '); fi +if [ "$(command -v ctr || echo -n '')" ]; then ctrcontainers=$(ctr -n k8s.io containers list -q 2>/dev/null | wc -l | tr -d ' '); fi + +if [ "$dockercontainers" -eq "0" ] && [ "$lxccontainers" -eq "0" ] && [ "$rktcontainers" -eq "0" ] && [ "$podmancontainers" -eq "0" ] && [ "$nerdctlcontainers" -eq "0" ] && [ "$crictlcontainers" -eq "0" ] && [ "$ctrcontainers" -eq "0" ]; then echo_no else containerCounts="" @@ -80,6 +113,9 @@ else if [ "$podmancontainers" -ne "0" ]; then containerCounts="${containerCounts}podman($podmancontainers) "; fi if [ "$lxccontainers" -ne "0" ]; then containerCounts="${containerCounts}lxc($lxccontainers) "; fi if [ "$rktcontainers" -ne "0" ]; then containerCounts="${containerCounts}rkt($rktcontainers) "; fi + if [ "$nerdctlcontainers" -ne "0" ]; then containerCounts="${containerCounts}nerdctl($nerdctlcontainers) "; fi + if [ "$crictlcontainers" -ne "0" ]; then containerCounts="${containerCounts}crictl($crictlcontainers) "; fi + if [ "$ctrcontainers" -ne "0" ]; then containerCounts="${containerCounts}ctr($ctrcontainers) "; fi echo "Yes $containerCounts" | sed -${E} "s,.*,${SED_RED}," # List any running containers with more details @@ -111,7 +147,22 @@ else #rkt status $(rkt list --format=json 2>/dev/null | jq -r '.[].id') 2>/dev/null | grep -E "privileged|capabilities|security" | sed -${E} "s,true|privileged|host,${SED_RED},g" echo "" fi + if [ "$nerdctlcontainers" -ne "0" ]; then + echo "Running nerdctl Containers" | sed -${E} "s,.*,${SED_RED}," + nerdctl ps -a 2>/dev/null + echo "" + fi + if [ "$crictlcontainers" -ne "0" ]; then + echo "Running CRI Containers" | sed -${E} "s,.*,${SED_RED}," + crictl ps -a 2>/dev/null + echo "" + fi + if [ "$ctrcontainers" -ne "0" ]; then + echo "Running ctr Containers (k8s.io namespace)" | sed -${E} "s,.*,${SED_RED}," + ctr -n k8s.io containers list 2>/dev/null + echo "" + fi fi -echo "" \ No newline at end of file +echo "" diff --git a/linPEAS/builder/linpeas_parts/2_container/4_Docker_container_details.sh b/linPEAS/builder/linpeas_parts/2_container/4_Docker_container_details.sh index a375e6b..d795587 100644 --- a/linPEAS/builder/linpeas_parts/2_container/4_Docker_container_details.sh +++ b/linPEAS/builder/linpeas_parts/2_container/4_Docker_container_details.sh @@ -19,7 +19,7 @@ if echo "$containerType" | grep -qi "docker"; then print_2title "Docker Container details" "T1613" inDockerGroup print_list "Am I inside Docker group .......$NC $DOCKER_GROUP\n" | sed -${E} "s,Yes,${SED_RED_YELLOW}," - print_list "Looking and enumerating Docker Sockets (if any):\n"$NC + print_list "Looking and enumerating runtime sockets:\n"$NC enumerateDockerSockets print_list "Docker version .................$NC$dockerVersion" checkDockerVersionExploits @@ -35,4 +35,4 @@ if echo "$containerType" | grep -qi "docker"; then print_2title "Docker Overlays" "T1613" df -h | grep docker fi -fi \ No newline at end of file +fi diff --git a/linPEAS/builder/linpeas_parts/2_container/5_Container_breakout.sh b/linPEAS/builder/linpeas_parts/2_container/5_Container_breakout.sh index 41896fe..40559b0 100644 --- a/linPEAS/builder/linpeas_parts/2_container/5_Container_breakout.sh +++ b/linPEAS/builder/linpeas_parts/2_container/5_Container_breakout.sh @@ -1,54 +1,22 @@ # Title: Container - Container & breakout enumeration # ID: CT_Container_breakout # Author: Carlos Polop -# Last Update: 07-03-2024 -# Description: Container breakout enumeration to identify potential escape vectors: -# - Container runtime vulnerabilities -# - Mount point misconfigurations -# - Capability abuse -# - Namespace escape -# - Common vulnerable scenarios: -# * Privileged containers -# * Misconfigured mounts -# * Excessive capabilities -# * Namespace isolation bypass -# * Runtime vulnerabilities -# * Container escape tools -# * Shared kernel exploits -# * Container escape CVEs -# - Exploitation methods: -# * Mount escape: Abuse mount misconfigurations -# * Capability abuse: Exploit excessive capabilities -# * Namespace escape: Break out of container namespaces -# * Runtime escape: Exploit container runtime vulnerabilities -# * Common attack vectors: -# - Mount point manipulation -# - Capability exploitation -# - Namespace breakout -# - Runtime vulnerability abuse -# - Kernel exploit abuse -# - Container escape tool usage -# * Exploit techniques: -# - Mount point abuse -# - Capability escalation -# - Namespace escape -# - Runtime exploitation -# - Kernel exploitation -# - Container escape tool execution +# Last Update: 21-03-2026 +# Description: Enumerate container hardening, breakout surfaces, runtime exposure, and high-impact escape vectors from inside a container. # License: GNU GPL # Version: 1.0 # Mitre: T1611 # Functions Used: checkContainerExploits, checkProcSysBreakouts, containerCheck, enumerateDockerSockets, print_2title, print_3title, print_info, print_list, warn_exec -# Global Variables: $binfmt_misc_breakout, $containercapsB, $containerType, $core_pattern_breakout, $dev_mounted, $efi_efivars_writable, $efi_vars_writable, $GREP_IGNORE_MOUNTS, $inContainer, $kallsyms_readable, $kcore_readable, $kmem_readable, $kmem_writable, $kmsg_readable, $mem_readable, $mem_writable, $modprobe_present, $mountinfo_readable, $panic_on_oom_dos, $panic_sys_fs_dos, $proc_configgz_readable, $proc_mounted, $run_unshare, $release_agent_breakout1, $release_agent_breakout2, $release_agent_breakout3, $sched_debug_readable, $security_present, $security_writable, $sysreq_trigger_dos, $uevent_helper_breakout, $vmcoreinfo_readable, $VULN_CVE_2019_5021, $self_mem_readable +# Global Variables: $binfmt_misc_breakout, $containercapsB, $containerType, $core_pattern_breakout, $debugfs_present, $debugfs_readable, $dev_mounted, $efi_efivars_writable, $efi_vars_writable, $GREP_IGNORE_MOUNTS, $inContainer, $kallsyms_readable, $kcore_readable, $kmem_readable, $kmem_writable, $kmsg_readable, $mem_readable, $mem_writable, $modprobe_binary, $modprobe_config_writable, $mountinfo_readable, $panic_on_oom_dos, $panic_sys_fs_dos, $proc_configgz_readable, $proc_keys_readable, $proc_mounted, $proc_timer_list_readable, $release_agent_breakout1, $release_agent_breakout2, $release_agent_breakout3, $run_unshare, $sched_debug_readable, $security_present, $security_writable, $self_mem_readable, $sys_firmware_readable, $sysreq_trigger_dos, $thermal_present, $thermal_readable, $uevent_helper_breakout, $vmcoreinfo_readable, $VULN_CVE_2019_5021 # Initial Functions: containerCheck -# Generated Global Variables: $defautl_docker_caps, $containerd_version, $runc_version, $seccomp_mode_num, $seccomp_mode_desc +# Generated Global Variables: $container_breakout_tools, $containerd_version, $defautl_docker_caps, $gid_map_value, $host_process_count, $host_process_indicators, $no_new_privs_num, $proc_comm, $root_mount_mode, $runc_version, $seccomp_mode_desc, $seccomp_mode_num, $selinux_context, $selinux_status, $setgroups_value, $tool, $uid_map_value # Fat linpeas: 0 # Small linpeas: 0 if [ "$inContainer" ]; then echo "" print_2title "Container & breakout enumeration" "T1611" - print_info "https://book.hacktricks.wiki/en/linux-hardening/privilege-escalation/docker-security/docker-breakout-privilege-escalation/index.html" + print_info "https://book.hacktricks.wiki/en/linux-hardening/privilege-escalation/container-security/index.html" # Basic container info print_list "Container ID ...................$NC $(cat /etc/hostname && echo -n '\n')" @@ -56,8 +24,8 @@ if [ "$inContainer" ]; then print_list "Container Full ID ..............$NC $(basename $(cat /proc/1/cpuset))\n" fi - # Security mechanisms - print_3title "Security Mechanisms" "T1611" + # Hardening and isolation controls + print_3title "Hardening & isolation" "T1611" seccomp_mode_num="$(awk '/^Seccomp:/{print $2}' /proc/self/status 2>/dev/null)" seccomp_mode_desc="unknown" case "$seccomp_mode_num" in @@ -74,17 +42,57 @@ if [ "$inContainer" ]; then awk '/^Seccomp_filters:/{print $2}' /proc/self/status 2>/dev/null | sed -${E} "s,^[0-9]+$,${SED_GREEN}&," fi - print_list "AppArmor profile? .............. "$NC + no_new_privs_num="$(awk '/^NoNewPrivs:/{print $2}' /proc/self/status 2>/dev/null)" + print_list "NoNewPrivs ..................... "$NC + case "$no_new_privs_num" in + 1) printf "enabled (1)\n" | sed -${E} "s,enabled,${SED_GREEN}," ;; + 0) printf "disabled (0)\n" | sed -${E} "s,disabled,${SED_RED_YELLOW}," ;; + *) printf "unknown\n" ;; + esac + + print_list "AppArmor profile ............... "$NC (cat /proc/self/attr/current 2>/dev/null || echo "disabled") | sed "s,disabled,${SED_RED}," | sed "s,kernel,${SED_GREEN}," - print_list "User proc namespace? ........... "$NC - if [ "$(cat /proc/self/uid_map 2>/dev/null)" ]; then - (printf "enabled"; cat /proc/self/uid_map) | sed "s,enabled,${SED_GREEN},"; - echo "" - echo " Mappings (Container -> Host -> Range):" - cat /proc/self/uid_map | awk '{print " " $1 " -> " $2 " -> " $3}' - else - echo "disabled" | sed "s,disabled,${SED_RED},"; + selinux_status="disabled" + if command -v getenforce >/dev/null 2>&1; then + selinux_status="$(getenforce 2>/dev/null || echo disabled)" + elif [ -r /sys/fs/selinux/enforce ]; then + if [ "$(cat /sys/fs/selinux/enforce 2>/dev/null)" = "1" ]; then + selinux_status="Enforcing" + else + selinux_status="Permissive" + fi + fi + print_list "SELinux status ................. "$NC + printf "%s\n" "$selinux_status" | sed -${E} "s,Enforcing,${SED_GREEN},g" | sed -${E} "s,Permissive,${SED_RED_YELLOW},g" | sed -${E} "s,disabled,${SED_RED},g" + + selinux_context="$(cat /proc/self/attr/current 2>/dev/null | grep -E ':' || true)" + if [ "$selinux_context" ]; then + print_list "SELinux context ................ "$NC + printf "%s\n" "$selinux_context" | sed -${E} "s,container_t|spc_t,${SED_RED_YELLOW}&,g" + fi + + uid_map_value="$(cat /proc/self/uid_map 2>/dev/null)" + gid_map_value="$(cat /proc/self/gid_map 2>/dev/null)" + setgroups_value="$(cat /proc/self/setgroups 2>/dev/null)" + print_list "User namespace mappings ....... "$NC + if echo "$uid_map_value" | grep -Eq "^[[:space:]]*0[[:space:]]+0[[:space:]]+4294967295[[:space:]]*$"; then + echo "initial user namespace" | sed -${E} "s,initial user namespace,${SED_RED_YELLOW}," + elif [ "$uid_map_value" ]; then + echo "remapped user namespace" | sed -${E} "s,remapped user namespace,${SED_GREEN}," + else + echo "unknown" + fi + if [ "$uid_map_value" ]; then + echo " UID map (container -> host -> range):" + echo "$uid_map_value" | awk '{print " " $1 " -> " $2 " -> " $3}' + fi + if [ "$gid_map_value" ]; then + echo " GID map (container -> host -> range):" + echo "$gid_map_value" | awk '{print " " $1 " -> " $2 " -> " $3}' + fi + if [ "$setgroups_value" ]; then + echo " setgroups: $setgroups_value" fi # Known vulnerabilities @@ -93,8 +101,17 @@ if [ "$inContainer" ]; then print_list "Vulnerable to CVE-2019-5021 .... $VULN_CVE_2019_5021\n"$NC | sed -${E} "s,Yes,${SED_RED_YELLOW}," # Check for container escape tools - print_list "Container escape tools present .. "$NC - (command -v nsenter || command -v unshare || command -v chroot || command -v capsh || command -v setcap || command -v getcap || command -v docker || command -v kubectl || command -v ctr || command -v runc || command -v containerd || command -v crio || command -v podman || command -v lxc || command -v rkt || command -v nerdctl || echo "No") | sed -${E} "s,nsenter|unshare|chroot|capsh|setcap|getcap|docker|kubectl|ctr|runc|containerd|crio|podman|lxc|rkt|nerdctl,${SED_RED},g" + container_breakout_tools="$( + for tool in nsenter unshare chroot capsh setcap getcap docker kubectl ctr runc containerd crio podman lxc rkt nerdctl; do + command -v "$tool" 2>/dev/null + done + )" + print_list "Container escape tools present . "$NC + if [ "$container_breakout_tools" ]; then + printf "%s\n" "$container_breakout_tools" | sed -${E} "s,.*,${SED_RED}&," + else + echo "No" + fi # Runtime vulnerabilities print_3title "Runtime Vulnerabilities" "T1611" @@ -129,30 +146,32 @@ if [ "$inContainer" ]; then fi fi - # Mount escape vectors - print_3title "Breakout via mounts" "T1611" - print_info "https://book.hacktricks.wiki/en/linux-hardening/privilege-escalation/docker-security/docker-breakout-privilege-escalation/sensitive-mounts.html" + # Mount, procfs and sysfs escape surfaces + print_3title "Mount, procfs & sysfs surfaces" "T1611" + print_info "https://book.hacktricks.wiki/en/linux-hardening/privilege-escalation/container-security/sensitive-host-mounts.html" checkProcSysBreakouts - print_list "/proc mounted? ................. $proc_mounted\n" | sed -${E} "s,Yes,${SED_RED_YELLOW}," - print_list "/dev mounted? .................. $dev_mounted\n" | sed -${E} "s,Yes,${SED_RED_YELLOW}," + root_mount_mode="$(awk '$5=="/"{print $6; exit}' /proc/self/mountinfo 2>/dev/null | cut -d',' -f1)" + print_list "/proc heavily populated ........ $proc_mounted\n" | sed -${E} "s,Yes,${SED_RED_YELLOW}," + print_list "/dev heavily populated ......... $dev_mounted\n" | sed -${E} "s,Yes,${SED_RED_YELLOW}," + print_list "Root filesystem mode ........... ${root_mount_mode:-unknown}\n" | sed -${E} "s,rw,${SED_RED_YELLOW}," | sed -${E} "s,ro,${SED_GREEN}," print_list "Run unshare .................... $run_unshare\n" | sed -${E} "s,Yes,${SED_RED}," - print_list "release_agent breakout 1........ $release_agent_breakout1\n" | sed -${E} "s,Yes,${SED_RED}," - print_list "release_agent breakout 2........ $release_agent_breakout2\n" | sed -${E} "s,Yes,${SED_RED_YELLOW}," - print_list "release_agent breakout 3........ $release_agent_breakout3\n" | sed -${E} "s,Yes,${SED_RED_YELLOW}," - print_list "core_pattern breakout .......... $core_pattern_breakout\n" | sed -${E} "s,Yes,${SED_RED_YELLOW}," - print_list "binfmt_misc breakout ........... $binfmt_misc_breakout\n" | sed -${E} "s,Yes,${SED_RED_YELLOW}," - print_list "uevent_helper breakout ......... $uevent_helper_breakout\n" | sed -${E} "s,Yes,${SED_RED_YELLOW}," + print_list "release_agent surface 1 ........ $release_agent_breakout1\n" | sed -${E} "s,Yes,${SED_RED}," + print_list "release_agent surface 2 ........ $release_agent_breakout2\n" | sed -${E} "s,Yes,${SED_RED_YELLOW}," + print_list "release_agent surface 3 ........ $release_agent_breakout3\n" | sed -${E} "s,Yes,${SED_RED_YELLOW}," + print_list "Writable core_pattern .......... $core_pattern_breakout\n" | sed -${E} "s,Yes,${SED_RED_YELLOW}," + print_list "Writable binfmt_misc/register .. $binfmt_misc_breakout\n" | sed -${E} "s,Yes,${SED_RED_YELLOW}," + print_list "Writable uevent_helper ......... $uevent_helper_breakout\n" | sed -${E} "s,Yes,${SED_RED_YELLOW}," # Additional mount checks - print_list "Docker socket mounted? ......... "$NC - (mount | grep -E "docker.sock|/var/run/docker.sock" || echo "No") | sed -${E} "s,Yes|docker.sock,${SED_RED}," + print_list "Mounted runtime sockets ........ "$NC + (mount | grep -E "docker.sock|containerd.sock|crio.sock|podman.sock|buildkitd.sock|kubelet.sock|firecracker-containerd.sock" || echo "No") | sed -${E} "s,docker.sock|containerd.sock|crio.sock|podman.sock|buildkitd.sock|kubelet.sock|firecracker-containerd.sock,${SED_RED},g" print_list "Common host filesystem mounted? "$NC - (mount | grep -E "host|/host|/mnt/host" || echo "No") | sed -${E} "s,Yes|host,${SED_RED}," + (mount | grep -E "host|/host|/mnt/host|/rootfs" || echo "No") | sed -${E} "s,host|/host|/mnt/host|/rootfs,${SED_RED},g" print_list "Interesting mounts ............. "$NC - mount | grep -E "docker|container|overlay|kubelet" | grep -v "proc" | sed -${E} "s,docker.sock|host|privileged,${SED_RED},g" + mount | grep -E "docker|container|overlay|kubelet|buildkit|crio|podman|/host|/rootfs" | grep -v "proc" | sed -${E} "s,docker.sock|containerd.sock|crio.sock|podman.sock|kubelet.sock|buildkitd.sock|host|rootfs|privileged,${SED_RED},g" # Check for writable mount points print_list "Writable mount points ......... "$NC @@ -164,7 +183,7 @@ if [ "$inContainer" ]; then # Capability checks print_3title "Capability Checks" "T1611" - print_info "https://book.hacktricks.wiki/en/linux-hardening/privilege-escalation/docker-security/docker-breakout-privilege-escalation/capabilities-abuse-escape.html" + print_info "https://book.hacktricks.wiki/en/linux-hardening/privilege-escalation/container-security/protections/capabilities.html" print_list "Dangerous capabilities ......... "$NC if [ "$(command -v capsh || echo -n '')" ]; then @@ -179,70 +198,67 @@ if [ "$inContainer" ]; then (grep "CapAmb:" /proc/self/status 2>/dev/null | grep -v "0000000000000000" | sed "s,CapAmb:.,," || echo "No") | sed -${E} "s,No,${SED_GREEN}," | sed -${E} "s,[0-9a-fA-F]\+,${SED_RED}&," # Additional capability checks - print_list "Dangerous syscalls allowed ... "$NC + print_list "ptrace_scope (host) ........... "$NC if [ -f "/proc/sys/kernel/yama/ptrace_scope" ]; then (cat /proc/sys/kernel/yama/ptrace_scope 2>/dev/null || echo "Not found") | sed -${E} "s,0,${SED_RED}," else echo "Not found" fi - # Namespace checks - print_3title "Namespace Checks" "T1611" - print_info "https://book.hacktricks.wiki/en/linux-hardening/privilege-escalation/docker-security/namespaces/index.html" + # Namespace checks. From inside a container we often cannot prove host namespace sharing directly, + # so prefer raw namespace handles and practical indicators over misleading "host namespace = yes/no" guesses. + print_3title "Namespaces & sharing indicators" "T1611" + print_info "https://book.hacktricks.wiki/en/linux-hardening/privilege-escalation/container-security/protections/namespaces/index.html" print_list "Current namespaces ............. "$NC ls -l /proc/self/ns/ - print_list "Host network namespace? ........ "$NC - if [ "$(ip netns list 2>/dev/null)" ]; then - echo "Yes - Host network namespace accessible" | sed -${E} "s,Yes,${SED_RED}," + if ps -e -o pid= >/dev/null 2>&1; then + host_process_count="$(ps -e -o pid= 2>/dev/null | wc -l | tr -d ' ')" + host_process_indicators="$(ps -eo comm= 2>/dev/null | grep -E '^(systemd|init|kthreadd|dockerd|containerd|kubelet|sshd|udevd|NetworkManager|dbus-daemon)$' | sort -u)" else - echo "No" + host_process_count="$(ls -d /proc/[0-9]* 2>/dev/null | wc -l | tr -d ' ')" + host_process_indicators="$(for proc_comm in /proc/[0-9]*/comm; do cat "$proc_comm" 2>/dev/null; done | grep -E '^(systemd|init|kthreadd|dockerd|containerd|kubelet|sshd|udevd|NetworkManager|dbus-daemon)$' | sort -u)" fi - - # Additional namespace checks - print_list "Host IPC namespace? ........... "$NC - if [ "$(ls -l /proc/self/ns/ipc 2>/dev/null)" = "$(ls -l /proc/1/ns/ipc 2>/dev/null)" ]; then - echo "Yes - Host IPC namespace shared" | sed -${E} "s,Yes,${SED_RED}," + print_list "Processes visible .............. $host_process_count\n" | sed -${E} "s,^[^0-9]*([5-9][0-9]|[1-9][0-9]{2,}).*,${SED_RED_YELLOW}&," + print_list "Host-like processes visible .... "$NC + if [ "$host_process_indicators" ]; then + printf "%s\n" "$host_process_indicators" | sed -${E} "s,.*,${SED_RED_YELLOW}&," else - echo "No" + echo "No obvious host daemons" fi - - print_list "Host PID namespace? ........... "$NC - if [ "$(ls -l /proc/self/ns/pid 2>/dev/null)" = "$(ls -l /proc/1/ns/pid 2>/dev/null)" ]; then - echo "Yes - Host PID namespace shared" | sed -${E} "s,Yes,${SED_RED}," + + print_list "Network interfaces ............. "$NC + if command -v ip >/dev/null 2>&1; then + ip -o link show 2>/dev/null | awk -F': ' '{print $2}' else - echo "No" + ls /sys/class/net 2>/dev/null fi - - print_list "Host UTS namespace? ........... "$NC - if [ "$(ls -l /proc/self/ns/uts 2>/dev/null)" = "$(ls -l /proc/1/ns/uts 2>/dev/null)" ]; then - echo "Yes - Host UTS namespace shared" | sed -${E} "s,Yes,${SED_RED}," - else - echo "No" - fi - - - print_list "Looking and enumerating Docker Sockets (if any):\n"$NC + + print_list "Namespace inode summary ........ "$NC + for ns in cgroup ipc mnt net pid time user uts; do + if [ -L "/proc/self/ns/$ns" ]; then + printf "%s -> %s\n" "$ns" "$(readlink "/proc/self/ns/$ns" 2>/dev/null)" + fi + done + + print_list "Looking and enumerating runtime sockets:\n"$NC enumerateDockerSockets # Additional breakout vectors - print_3title "Additional Breakout Vectors" "T1611" - print_list "is modprobe present ............ $modprobe_present\n" | sed -${E} "s,/.*,${SED_RED}," - print_list "DoS via panic_on_oom ........... $panic_on_oom_dos\n" | sed -${E} "s,Yes,${SED_RED}," - print_list "DoS via panic_sys_fs ........... $panic_sys_fs_dos\n" | sed -${E} "s,Yes,${SED_RED}," + print_3title "Writable kernel helper paths" "T1611" + print_list "modprobe helper binary ......... $modprobe_binary\n" | sed -${E} "s,/.*,${SED_RED}," + print_list "modprobe path writable ......... $modprobe_config_writable\n" | sed -${E} "s,Yes,${SED_RED}," + print_list "panic_on_oom writable .......... $panic_on_oom_dos\n" | sed -${E} "s,Yes,${SED_RED}," + print_list "suid_dumpable writable ......... $panic_sys_fs_dos\n" | sed -${E} "s,Yes,${SED_RED}," print_list "DoS via sysreq_trigger_dos ..... $sysreq_trigger_dos\n" | sed -${E} "s,Yes,${SED_RED}," - - # Check for container escape tools in PATH - print_list "Container escape tools in PATH . "$NC - (which nsenter 2>/dev/null || which unshare 2>/dev/null || which chroot 2>/dev/null || which capsh 2>/dev/null || which setcap 2>/dev/null || which getcap 2>/dev/null || echo "No") | sed -${E} "s,nsenter|unshare|chroot|capsh|setcap|getcap,${SED_RED},g" - print_3title "Extra Breakout Vectors" "T1611" + print_3title "Sensitive procfs/sysfs exposure" "T1611" print_list "/proc/config.gz readable ....... $proc_configgz_readable\n" | sed -${E} "s,Yes,${SED_RED}," print_list "/proc/sched_debug readable ..... $sched_debug_readable\n" | sed -${E} "s,Yes,${SED_RED}," print_list "/proc/*/mountinfo readable ..... $mountinfo_readable\n" | sed -${E} "s,Yes,${SED_RED}," - print_list "/sys/kernel/security present ... $security_present\n" | sed -${E} "s,Yes,${SED_RED}," - print_list "/sys/kernel/security writable .. $security_writable\n" | sed -${E} "s,Yes,${SED_RED}," + print_list "/proc/keys readable ............ $proc_keys_readable\n" | sed -${E} "s,Yes,${SED_RED}," + print_list "/proc/timer_list readable ...... $proc_timer_list_readable\n" | sed -${E} "s,Yes,${SED_RED}," print_list "/proc/kmsg readable ............ $kmsg_readable\n" | sed -${E} "s,Yes,${SED_RED}," print_list "/proc/kallsyms readable ........ $kallsyms_readable\n" | sed -${E} "s,Yes,${SED_RED}," print_list "/proc/self/mem readable ........ $self_mem_readable\n" | sed -${E} "s,Yes,${SED_RED}," @@ -251,6 +267,13 @@ if [ "$inContainer" ]; then print_list "/proc/kmem writable ............ $kmem_writable\n" | sed -${E} "s,Yes,${SED_RED}," print_list "/proc/mem readable ............. $mem_readable\n" | sed -${E} "s,Yes,${SED_RED}," print_list "/proc/mem writable ............. $mem_writable\n" | sed -${E} "s,Yes,${SED_RED}," + print_list "/sys/firmware readable ......... $sys_firmware_readable\n" | sed -${E} "s,Yes,${SED_RED}," + print_list "/sys/kernel/debug present ...... $debugfs_present\n" | sed -${E} "s,Yes,${SED_RED}," + print_list "/sys/kernel/debug readable ..... $debugfs_readable\n" | sed -${E} "s,Yes,${SED_RED}," + print_list "/sys/class/thermal present ..... $thermal_present\n" | sed -${E} "s,Yes,${SED_RED_YELLOW}," + print_list "/sys/class/thermal readable .... $thermal_readable\n" | sed -${E} "s,Yes,${SED_RED_YELLOW}," + print_list "/sys/kernel/security present ... $security_present\n" | sed -${E} "s,Yes,${SED_RED}," + print_list "/sys/kernel/security writable .. $security_writable\n" | sed -${E} "s,Yes,${SED_RED}," print_list "/sys/kernel/vmcoreinfo readable $vmcoreinfo_readable\n" | sed -${E} "s,Yes,${SED_RED}," print_list "/sys/firmware/efi/vars writable $efi_vars_writable\n" | sed -${E} "s,Yes,${SED_RED}," print_list "/sys/firmware/efi/efivars writable $efi_efivars_writable\n" | sed -${E} "s,Yes,${SED_RED}," @@ -270,10 +293,10 @@ if [ "$inContainer" ]; then # Additional container runtime checks print_list "Container runtime sockets .. "$NC - (find /var/run -name "*.sock" 2>/dev/null | grep -E "docker|containerd|crio|podman|lxc|rkt" || echo "No") | sed -${E} "s,docker|containerd|crio|podman|lxc|rkt,${SED_RED},g" + (find /var/run /run -name "*.sock" 2>/dev/null | grep -E "docker|containerd|crio|podman|lxc|rkt|kubelet|buildkit|firecracker" || echo "No") | sed -${E} "s,docker|containerd|crio|podman|lxc|rkt|kubelet|buildkit|firecracker,${SED_RED},g" print_list "Container runtime configs .. "$NC - (find /etc -name "*.conf" -o -name "*.json" 2>/dev/null | grep -E "docker|containerd|crio|podman|lxc|rkt" || echo "No") | sed -${E} "s,docker|containerd|crio|podman|lxc|rkt,${SED_RED},g" + (find /etc -name "*.conf" -o -name "*.json" 2>/dev/null | grep -E "docker|containerd|crio|podman|lxc|rkt|kubelet|buildkit|firecracker" || echo "No") | sed -${E} "s,docker|containerd|crio|podman|lxc|rkt|kubelet|buildkit|firecracker,${SED_RED},g" # Kubernetes specific checks if echo "$containerType" | grep -qi "kubernetes"; then diff --git a/linPEAS/builder/linpeas_parts/2_container/7_RW_bind_mounts_nosuid.sh b/linPEAS/builder/linpeas_parts/2_container/7_RW_bind_mounts_nosuid.sh index a33b5eb..59b60cf 100644 --- a/linPEAS/builder/linpeas_parts/2_container/7_RW_bind_mounts_nosuid.sh +++ b/linPEAS/builder/linpeas_parts/2_container/7_RW_bind_mounts_nosuid.sh @@ -21,7 +21,7 @@ containerCheck if [ "$inContainer" ]; then echo "" print_2title "Container - Writable bind mounts w/o nosuid (SUID persistence risk)" "T1611" - print_info "https://book.hacktricks.wiki/en/linux-hardening/privilege-escalation/docker-security/docker-breakout-privilege-escalation/index.html#writable-bind-mounts" + print_info "https://book.hacktricks.wiki/en/linux-hardening/privilege-escalation/container-security/privileged-containers.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) diff --git a/linPEAS/builder/linpeas_parts/7_software_information/Containerd.sh b/linPEAS/builder/linpeas_parts/7_software_information/Containerd.sh index dafc47a..35f50a2 100644 --- a/linPEAS/builder/linpeas_parts/7_software_information/Containerd.sh +++ b/linPEAS/builder/linpeas_parts/7_software_information/Containerd.sh @@ -1,28 +1,42 @@ # Title: Software Information - containerd installed # ID: SI_Containerd # Author: Carlos Polop -# Last Update: 22-08-2023 -# Description: containerd installed +# Last Update: 21-03-2026 +# Description: containerd and related CRI/containerd client tooling installed # License: GNU GPL # Version: 1.0 # Mitre: T1613 # Functions Used: print_2title, print_info # Global Variables: $DEBUG, $SEARCH_IN_FOLDER # Initial Functions: -# Generated Global Variables: $containerd +# Generated Global Variables: $containerd, $containerd_cli, $crictl_cli, $nerdctl_cli # Fat linpeas: 0 # Small linpeas: 1 if ! [ "$SEARCH_IN_FOLDER" ]; then - containerd=$(command -v ctr || echo -n '') - if [ "$containerd" ] || [ "$DEBUG" ]; then - print_2title "Checking if containerd(ctr) is available" "T1613" - print_info "https://book.hacktricks.wiki/en/linux-hardening/privilege-escalation/index.html#containerd-ctr-privilege-escalation" + containerd=$(command -v containerd || echo -n '') + containerd_cli=$(command -v ctr || echo -n '') + nerdctl_cli=$(command -v nerdctl || echo -n '') + crictl_cli=$(command -v crictl || echo -n '') + if [ "$containerd" ] || [ "$containerd_cli" ] || [ "$nerdctl_cli" ] || [ "$crictl_cli" ] || [ "$DEBUG" ]; then + print_2title "Checking if containerd/CRI tooling is available" "T1613" + print_info "https://book.hacktricks.wiki/en/linux-hardening/privilege-escalation/container-security/runtime-api-and-daemon-exposure.html" if [ "$containerd" ]; then - echo "ctr was found in $containerd, you may be able to escalate privileges with it" | sed -${E} "s,.*,${SED_RED}," + echo "containerd was found in $containerd" | sed -${E} "s,.*,${SED_RED}," + fi + if [ "$containerd_cli" ]; then + echo "ctr was found in $containerd_cli, you may be able to inspect or manage containerd content with it" | sed -${E} "s,.*,${SED_RED}," ctr image list 2>&1 fi + if [ "$nerdctl_cli" ]; then + echo "nerdctl was found in $nerdctl_cli, you may be able to interact with containerd namespaces and containers with it" | sed -${E} "s,.*,${SED_RED}," + nerdctl images 2>&1 + fi + if [ "$crictl_cli" ]; then + echo "crictl was found in $crictl_cli, you may be able to inspect CRI-managed containers with it" | sed -${E} "s,.*,${SED_RED}," + crictl images 2>&1 + fi echo "" fi fi diff --git a/linPEAS/builder/linpeas_parts/7_software_information/Docker.sh b/linPEAS/builder/linpeas_parts/7_software_information/Docker.sh index da32b23..44c77f8 100644 --- a/linPEAS/builder/linpeas_parts/7_software_information/Docker.sh +++ b/linPEAS/builder/linpeas_parts/7_software_information/Docker.sh @@ -16,7 +16,7 @@ if [ "$PSTORAGE_DOCKER" ] || [ "$DEBUG" ]; then print_2title "Searching docker files (limit 70)" "T1613" - print_info "https://book.hacktricks.wiki/en/linux-hardening/privilege-escalation/docker-security/index.html#docker-breakout--privilege-escalation" + print_info "https://book.hacktricks.wiki/en/linux-hardening/privilege-escalation/container-security/index.html" printf "%s\n" "$PSTORAGE_DOCKER" | head -n 70 | while read f; do ls -l "$f" 2>/dev/null if ! [ "$IAMROOT" ] && [ -S "$f" ] && [ -w "$f" ]; then @@ -24,4 +24,4 @@ if [ "$PSTORAGE_DOCKER" ] || [ "$DEBUG" ]; then fi done echo "" -fi \ No newline at end of file +fi diff --git a/linPEAS/builder/linpeas_parts/functions/checkCreateReleaseAgent.sh b/linPEAS/builder/linpeas_parts/functions/checkCreateReleaseAgent.sh index f4b0b69..522f4a5 100644 --- a/linPEAS/builder/linpeas_parts/functions/checkCreateReleaseAgent.sh +++ b/linPEAS/builder/linpeas_parts/functions/checkCreateReleaseAgent.sh @@ -14,12 +14,15 @@ checkCreateReleaseAgent(){ - cat /proc/$$/cgroup 2>/dev/null | grep -Eo '[0-9]+:[^:]+' | grep -Eo '[^:]+$' | while read -r ss - do - if unshare -UrmC --propagation=unchanged bash -c "mount -t cgroup -o $ss cgroup /tmp/cgroup_3628d4 2>&1 >/dev/null && test -w /tmp/cgroup_3628d4/release_agent" >/dev/null 2>&1 ; then - release_agent_breakout3="Yes (unshare with $ss)"; - rm -rf /tmp/cgroup_3628d4 + release_agent_breakout3="${release_agent_breakout3:-No}" + for ss in $(awk -F: '/^[0-9]+:/{print $2}' /proc/$$/cgroup 2>/dev/null); do + if unshare -UrmC --propagation=unchanged sh -c "mount -t cgroup -o $ss cgroup /tmp/cgroup_3628d4 >/dev/null 2>&1 && test -w /tmp/cgroup_3628d4/release_agent" >/dev/null 2>&1 ; then + release_agent_breakout3="Yes (unshare with $ss)" + umount /tmp/cgroup_3628d4 >/dev/null 2>&1 + rm -rf /tmp/cgroup_3628d4 >/dev/null 2>&1 break fi + umount /tmp/cgroup_3628d4 >/dev/null 2>&1 + rm -rf /tmp/cgroup_3628d4 >/dev/null 2>&1 done -} \ No newline at end of file +} diff --git a/linPEAS/builder/linpeas_parts/functions/checkProcSysBreakouts.sh b/linPEAS/builder/linpeas_parts/functions/checkProcSysBreakouts.sh index cbc8745..f78e85e 100644 --- a/linPEAS/builder/linpeas_parts/functions/checkProcSysBreakouts.sh +++ b/linPEAS/builder/linpeas_parts/functions/checkProcSysBreakouts.sh @@ -1,19 +1,27 @@ # Title: Container - checkProcSysBreakouts # ID: checkProcSysBreakouts # Author: Carlos Polop -# Last Update: 22-08-2023 -# Description: Check if the container is vulnerable to several breakouts abusing /sys and /proc folders +# Last Update: 21-03-2026 +# Description: Check whether procfs/sysfs/cgroup surfaces exposed inside a container could be used for breakout, host discovery, or high-impact abuse. # License: GNU GPL # Version: 1.0 # Functions Used: checkCreateReleaseAgent # Global Variables: # Initial Functions: -# Generated Global Variables: $dev_mounted, $proc_mounted, $run_unshare, $release_agent_breakout1, $release_agent_breakout2, $core_pattern_breakout, $modprobe_present, $panic_on_oom_dos, $panic_on_oom, $panic_on, $panic_sys_fs_dos, $binfmt_misc_breakout, $proc_configgz_readable, $sysreq_trigger_dos, $kmsg_readable, $kallsyms_readable, $self_mem_readable, $mem_readable, $kmem_readable, $kmem_writable, $mem_writable, $sched_debug_readable, $mountinfo_readable, $uevent_helper_breakout, $vmcoreinfo_readable, $security_present, $security_writable, $efi_vars_writable, $efi_efivars_writable, $kcore_readable +# Generated Global Variables: $dev_mounted, $proc_mounted, $run_unshare, $release_agent_breakout1, $release_agent_breakout2, $core_pattern_breakout, $modprobe_binary, $modprobe_config_writable, $panic_on_oom_dos, $panic_on_oom, $panic_on, $panic_sys_fs_dos, $binfmt_misc_breakout, $proc_configgz_readable, $sysreq_trigger_dos, $kmsg_readable, $kallsyms_readable, $self_mem_readable, $mem_readable, $kmem_readable, $kmem_writable, $mem_writable, $sched_debug_readable, $mountinfo_readable, $mountinfo_file, $uevent_helper_breakout, $vmcoreinfo_readable, $security_present, $security_writable, $efi_vars_writable, $efi_efivars_writable, $kcore_readable, $proc_keys_readable, $proc_timer_list_readable, $sys_firmware_readable, $debugfs_present, $debugfs_readable, $thermal_present, $thermal_readable, $thermal_file # Fat linpeas: 0 # Small linpeas: 1 checkProcSysBreakouts(){ + can_open_for_write() { + if [ -e "$1" ] && command -v dd >/dev/null 2>&1 && dd if=/dev/null of="$1" bs=1 count=0 conv=notrunc >/dev/null 2>&1; then + echo Yes + else + echo No + fi + } + dev_mounted="No" if [ $(ls -l /dev | grep -E "^c" | wc -l) -gt 50 ]; then dev_mounted="Yes"; @@ -24,7 +32,9 @@ checkProcSysBreakouts(){ proc_mounted="Yes"; fi - run_unshare=$(unshare -UrmC bash -c 'echo -n Yes' 2>/dev/null) + if command -v unshare >/dev/null 2>&1 && command -v sh >/dev/null 2>&1; then + run_unshare=$(unshare -UrmC sh -c 'echo -n Yes' 2>/dev/null) + fi if ! [ "$run_unshare" = "Yes" ]; then run_unshare="No" fi @@ -36,15 +46,17 @@ checkProcSysBreakouts(){ fi release_agent_breakout2="No" - mkdir /tmp/cgroup_3628d4 + mkdir -p /tmp/cgroup_3628d4 mount -t cgroup -o memory cgroup /tmp/cgroup_3628d4 2>/dev/null if [ $? -eq 0 ]; then release_agent_breakout2="Yes"; + umount /tmp/cgroup_3628d4 >/dev/null 2>&1 rm -rf /tmp/cgroup_3628d4 else mount -t cgroup -o rdma cgroup /tmp/cgroup_3628d4 2>/dev/null if [ $? -eq 0 ]; then release_agent_breakout2="Yes"; + umount /tmp/cgroup_3628d4 >/dev/null 2>&1 rm -rf /tmp/cgroup_3628d4 else checkCreateReleaseAgent @@ -52,27 +64,48 @@ checkProcSysBreakouts(){ fi rm -rf /tmp/cgroup_3628d4 2>/dev/null - core_pattern_breakout="$( (echo -n '' > /proc/sys/kernel/core_pattern && echo Yes) 2>/dev/null || echo No)" - modprobe_present="$(ls -l `cat /proc/sys/kernel/modprobe` 2>/dev/null || echo No)" - panic_on_oom_dos="$( (echo -n '' > /proc/sys/vm/panic_on_oom && echo Yes) 2>/dev/null || echo No)" - panic_sys_fs_dos="$( (echo -n '' > /proc/sys/fs/suid_dumpable && echo Yes) 2>/dev/null || echo No)" - binfmt_misc_breakout="$( (echo -n '' > /proc/sys/fs/binfmt_misc/register && echo Yes) 2>/dev/null || echo No)" + # Prefer zero-byte open-for-write checks here so special files are validated more accurately without trying to change their contents. + core_pattern_breakout="$(can_open_for_write /proc/sys/kernel/core_pattern)" + modprobe_binary="$(ls -l "$(cat /proc/sys/kernel/modprobe 2>/dev/null)" 2>/dev/null || echo No)" + modprobe_config_writable="$(can_open_for_write /proc/sys/kernel/modprobe)" + panic_on_oom_dos="$(can_open_for_write /proc/sys/vm/panic_on_oom)" + panic_sys_fs_dos="$(can_open_for_write /proc/sys/fs/suid_dumpable)" + binfmt_misc_breakout="$(can_open_for_write /proc/sys/fs/binfmt_misc/register)" proc_configgz_readable="$([ -r '/proc/config.gz' ] 2>/dev/null && echo Yes || echo No)" - sysreq_trigger_dos="$( (echo -n '' > /proc/sysrq-trigger && echo Yes) 2>/dev/null || echo No)" + sysreq_trigger_dos="$(can_open_for_write /proc/sysrq-trigger)" kmsg_readable="$( (dmesg > /dev/null 2>&1 && echo Yes) 2>/dev/null || echo No)" # Kernel Exploit Dev kallsyms_readable="$( (head -n 1 /proc/kallsyms > /dev/null && echo Yes )2>/dev/null || echo No)" # Kernel Exploit Dev self_mem_readable="$( (head -n 1 /proc/self/mem > /dev/null && echo Yes) 2>/dev/null || echo No)" - if [ "$(head -n 1 /tmp/kcore 2>/dev/null)" ]; then kcore_readable="Yes"; else kcore_readable="No"; fi + if [ "$(head -n 1 /proc/kcore 2>/dev/null)" ]; then kcore_readable="Yes"; else kcore_readable="No"; fi kmem_readable="$( (head -n 1 /proc/kmem > /dev/null && echo Yes) 2>/dev/null || echo No)" - kmem_writable="$( (echo -n '' > /proc/kmem > /dev/null && echo Yes) 2>/dev/null || echo No)" + kmem_writable="$(can_open_for_write /proc/kmem)" mem_readable="$( (head -n 1 /proc/mem > /dev/null && echo Yes) 2>/dev/null || echo No)" - mem_writable="$( (echo -n '' > /proc/mem > /dev/null && echo Yes) 2>/dev/null || echo No)" + mem_writable="$(can_open_for_write /proc/mem)" sched_debug_readable="$( (head -n 1 /proc/sched_debug > /dev/null && echo Yes) 2>/dev/null || echo No)" - mountinfo_readable="$( (head -n 1 /proc/*/mountinfo > /dev/null && echo Yes) 2>/dev/null || echo No)" - uevent_helper_breakout="$( (echo -n '' > /sys/kernel/uevent_helper && echo Yes) 2>/dev/null || echo No)" + mountinfo_readable="No" + for mountinfo_file in /proc/[0-9]*/mountinfo; do + if [ -r "$mountinfo_file" ]; then + mountinfo_readable="Yes" + break + fi + done + uevent_helper_breakout="$(can_open_for_write /sys/kernel/uevent_helper)" vmcoreinfo_readable="$( (head -n 1 /sys/kernel/vmcoreinfo > /dev/null && echo Yes) 2>/dev/null || echo No)" security_present="$( (ls -l /sys/kernel/security > /dev/null && echo Yes) 2>/dev/null || echo No)" - security_writable="$( (echo -n '' > /sys/kernel/security/a && echo Yes) 2>/dev/null || echo No)" - efi_vars_writable="$( (echo -n '' > /sys/firmware/efi/vars && echo Yes) 2>/dev/null || echo No)" - efi_efivars_writable="$( (echo -n '' > /sys/firmware/efi/efivars && echo Yes) 2>/dev/null || echo No)" + security_writable="$([ -w /sys/kernel/security ] 2>/dev/null && echo Yes || echo No)" + efi_vars_writable="$([ -w /sys/firmware/efi/vars ] 2>/dev/null && echo Yes || echo No)" + efi_efivars_writable="$([ -w /sys/firmware/efi/efivars ] 2>/dev/null && echo Yes || echo No)" + proc_keys_readable="$( (head -n 1 /proc/keys > /dev/null && echo Yes) 2>/dev/null || echo No)" + proc_timer_list_readable="$( (head -n 1 /proc/timer_list > /dev/null && echo Yes) 2>/dev/null || echo No)" + sys_firmware_readable="$([ -r /sys/firmware ] 2>/dev/null && echo Yes || echo No)" + debugfs_present="$([ -d /sys/kernel/debug ] 2>/dev/null && echo Yes || echo No)" + debugfs_readable="$( (ls -la /sys/kernel/debug > /dev/null && echo Yes) 2>/dev/null || echo No)" + thermal_present="$([ -d /sys/class/thermal ] 2>/dev/null && echo Yes || echo No)" + thermal_readable="No" + for thermal_file in /sys/class/thermal/*/*; do + if [ -f "$thermal_file" ] && [ -r "$thermal_file" ]; then + thermal_readable="Yes" + break + fi + done } diff --git a/linPEAS/builder/linpeas_parts/functions/containerCheck.sh b/linPEAS/builder/linpeas_parts/functions/containerCheck.sh index 02395a6..b02f8f0 100644 --- a/linPEAS/builder/linpeas_parts/functions/containerCheck.sh +++ b/linPEAS/builder/linpeas_parts/functions/containerCheck.sh @@ -1,8 +1,8 @@ # Title: Container - containerCheck # ID: containerCheck # Author: Carlos Polop -# Last Update: 22-08-2023 -# Description: Check if we are inside a container +# Last Update: 21-03-2026 +# Description: Check whether the current process appears to be running inside a Linux container and identify common runtime hints. # License: GNU GPL # Version: 1.0 # Functions Used: echo_no @@ -21,7 +21,7 @@ containerCheck() { if [ -f "/.dockerenv" ] || grep "/docker/" /proc/1/cgroup -qa 2>/dev/null || grep -qai docker /proc/self/cgroup 2>/dev/null || - [ "$(find / -maxdepth 3 -name '*dockerenv*' -exec ls -la {} \; 2>/dev/null)" ] ; then + [ -f "/run/.dockerenv" ] ; then inContainer="1" containerType="docker\n" @@ -50,22 +50,31 @@ containerCheck() { grep "/lxc/" /proc/1/cgroup -qa 2>/dev/null; then inContainer="1" - containerType="lxc\n" + if echo "$containerType" | grep -qv "lxc"; then + if [ "$containerType" ] && [ "$containerType" != "$(echo_no)" ]; then containerType="$containerType (lxc)\n" + else containerType="lxc\n" + fi + fi fi # Are we inside podman? - if env | grep -qa "container=podman" 2>/dev/null || + if [ -f "/run/.containerenv" ] || + env | grep -qa "container=podman" 2>/dev/null || grep -qa "container=podman" /proc/1/environ 2>/dev/null; then inContainer="1" - containerType="podman\n" + if echo "$containerType" | grep -qv "podman"; then + if [ "$containerType" ] && [ "$containerType" != "$(echo_no)" ]; then containerType="$containerType (podman)\n" + else containerType="podman\n" + fi + fi fi # Check for other container platforms that report themselves in PID 1 env if [ -z "$inContainer" ]; then - if grep -a 'container=' /proc/1/environ 2>/dev/null; then + if grep -qa 'container=' /proc/1/environ 2>/dev/null; then inContainer="1" - containerType="$(grep -a 'container=' /proc/1/environ | cut -d= -f2)\n" + containerType="$(tr '\000' '\n' < /proc/1/environ 2>/dev/null | awk -F= '/^container=/{print $2; exit}')\n" fi fi -} \ No newline at end of file +} diff --git a/linPEAS/builder/linpeas_parts/functions/enumerateDockerSockets.sh b/linPEAS/builder/linpeas_parts/functions/enumerateDockerSockets.sh index 8b41440..d6d7429 100644 --- a/linPEAS/builder/linpeas_parts/functions/enumerateDockerSockets.sh +++ b/linPEAS/builder/linpeas_parts/functions/enumerateDockerSockets.sh @@ -1,14 +1,14 @@ # Title: Container - enumerateDockerSockets # ID: enumerateDockerSockets # Author: Carlos Polop -# Last Update: 22-08-2023 -# Description: Search Docker Sockets +# Last Update: 21-03-2026 +# Description: Search interesting container runtime, orchestration, and build sockets that could expose high-impact APIs from inside a container. # License: GNU GPL # Version: 1.0 # Functions Used: echo_not_found # Global Variables: $GREP_DOCKER_SOCK_INFOS, $GREP_DOCKER_SOCK_INFOS_IGNORE # Initial Functions: -# Generated Global Variables: $SEARCHED_DOCKER_SOCKETS, $docker_enumerated, $dockerVersion, $int_sock, $sockInfoResponse +# Generated Global Variables: $SEARCHED_DOCKER_SOCKETS, $docker_enumerated, $dockerVersion, $int_sock, $sockInfoResponse, $IFS, $OLDIFS # Fat linpeas: 0 # Small linpeas: 1 @@ -17,6 +17,9 @@ enumerateDockerSockets() { dockerVersion="$(echo_not_found)" if ! [ "$SEARCHED_DOCKER_SOCKETS" ]; then SEARCHED_DOCKER_SOCKETS="1" + OLDIFS="$IFS" + IFS=' +' # NOTE: This is intentionally "lightweight" (checks common runtime socket names) and avoids # pseudo filesystems (/sys, /proc) to reduce noise and latency. for int_sock in $(find / \ @@ -25,9 +28,16 @@ enumerateDockerSockets() { -type s \( \ -name "docker.sock" -o \ -name "docker.socket" -o \ + -name "cri-dockerd.sock" -o \ -name "dockershim.sock" -o \ -name "containerd.sock" -o \ + -name "containerd.sock.ttrpc" -o \ -name "crio.sock" -o \ + -name "podman.sock" -o \ + -name "kubelet.sock" -o \ + -name "buildkitd.sock" -o \ + -name "buildkit.sock" -o \ + -name "firecracker-containerd.sock" -o \ -name "frakti.sock" -o \ -name "rktlet.sock" \ \) -print 2>/dev/null); do @@ -43,7 +53,7 @@ enumerateDockerSockets() { echo "You don't have write permissions over interesting socket $int_sock" | sed -${E} "s,$int_sock,${SED_GREEN},g" fi - # Validate whether this looks like a Docker Engine API socket (amicontained-style) when curl exists. + # Validate whether this looks like a Docker-compatible API socket (amicontained-style) when curl exists. docker_enumerated="" if [ "$(command -v curl 2>/dev/null || echo -n '')" ]; then sockInfoResponse="$(curl -s --max-time 2 --unix-socket "$int_sock" http://localhost/info 2>/dev/null)" @@ -67,5 +77,6 @@ enumerateDockerSockets() { fi fi done + IFS="$OLDIFS" fi }