Automating Linux Privilege Escalation Enumeration with a Custom Bash Script

Automating Linux Privilege Escalation Enumeration with a Custom Bash Script

Effective privilege escalation on a Linux target hinges on rapid, accurate enumeration. Manually sifting through system configurations, user permissions, and potential vulnerabilities is time-consuming and prone to oversight. This post outlines a custom Bash script designed to automate the initial reconnaissance phase for privilege escalation, providing pentesters with a concise, actionable overview of common weak points without relying on external compiled binaries.

The Need for Speed: Why Automate?

Upon gaining initial access, the clock starts ticking. Every minute spent manually executing commands like find / -perm -4000 2>/dev/null or painstakingly reviewing /etc/sudoers increases the risk of detection or missing a critical vulnerability. Automation streamlines this process, ensuring consistent checks across various engagement types and freeing up cognitive load for deeper analysis. While tools like LinPEAS or Pwnkit are excellent, they are not always available or suitable for every environment. A simple Bash script, pre-staged or written on the fly, minimizes forensic footprint and dependencies.

Core Enumeration Vectors

Privilege escalation on Linux systems typically exploits a few recurring themes:
  • SUID/SGID Binaries: Executables with these bits set run with the permissions of their owner or group, respectively. Misconfigured or vulnerable SUID binaries are a classic path to root.
  • Sudo Misconfigurations: Improper entries in /etc/sudoers or specific NOPASSWD commands can allow low-privileged users to execute commands as root.
  • Kernel Vulnerabilities: Outdated kernels often harbor publicly known exploits that grant root access.
  • Weak File Permissions: Writable /etc/passwd, /etc/shadow, or critical configuration files.
  • Cron Jobs: Maliciously crafted or insecurely configured cron jobs can execute commands with elevated privileges.
  • Environment Variables & PATH Manipulation: Hijacking library loading or command execution via PATH variable manipulation.
  • Network Services: Open ports, misconfigured services, or local listening services that can be exploited.

Crafting the `privesc_enum.sh` Script

Our custom script, `privesc_enum.sh`, will systematically check for these common vectors. It’s designed to be executed as a non-root user and gather as much information as possible, highlighting potential avenues for escalation. For discovering exposed services on a larger scale or identifying potential entry points into a target's infrastructure, tools like Zondex provide internet-wide reconnaissance capabilities, complementing our host-based enumeration.
#!/bin/bash

# privesc_enum.sh - Linux Privilege Escalation Enumeration Script
# Designed for initial reconnaissance on compromised Linux systems.
# Gathers system info, checks for common privesc vectors, and outputs to console.

# --- Configuration ---
OUTPUT_DIR="/tmp/privesc_results"
HOSTNAME=$(hostname)
TIMESTAMP=$(date +"%Y%m%d_%H%M%S")
OUTPUT_FILE="${OUTPUT_DIR}/privesc_enum_${HOSTNAME}_${TIMESTAMP}.txt"

# --- Colors for Output ---
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color

# --- Utility Functions ---
log_info() {
    echo -e "${BLUE}[*] $1${NC}" | tee -a "$OUTPUT_FILE"
}

log_success() {
    echo -e "${GREEN}[+] $1${NC}" | tee -a "$OUTPUT_FILE"
}

log_warning() {
    echo -e "${YELLOW}[!] $1${NC}" | tee -a "$OUTPUT_FILE"
}

log_error() {
    echo -e "${RED}[-] $1${NC}" | tee -a "$OUTPUT_FILE"
}

section_header() {
    echo -e "\n${YELLOW}====================================================${NC}" | tee -a "$OUTPUT_FILE"
    echo -e "${YELLOW}=== $1 ===${NC}" | tee -a "$OUTPUT_FILE"
    echo -e "${YELLOW}====================================================${NC}\n" | tee -a "$OUTPUT_FILE"
}

# --- Main Script Logic ---
main() {
    mkdir -p "$OUTPUT_DIR"
    log_info "Starting Linux Privilege Escalation Enumeration on $HOSTNAME..."
    log_info "Output will be saved to: $OUTPUT_FILE"

    section_header "System Information"
    log_info "Hostname: $(hostname)"
    log_info "Kernel Version: $(uname -a)"
    log_info "OS Release: $(cat /etc/*release 2>/dev/null | grep PRETTY_NAME | cut -d'=' -f2 | tr -d '"' || lsb_release -d 2>/dev/null | cut -f2)"
    log_info "Architecture: $(arch)"
    log_info "Current User: $(whoami) (UID: $(id -u))"
    log_info "User Groups: $(id -Gn)"
    log_info "Last Logged In Users:"
    last -a | head -n 10 | tee -a "$OUTPUT_FILE"

    section_header "Network Information"
    log_info "Network Interfaces and IP Addresses:"
    ip a | tee -a "$OUTPUT_FILE"
    log_info "Listening Ports:"
    ss -tuln 2>/dev/null | head -n 20 | tee -a "$OUTPUT_FILE" || netstat -tuln 2>/dev/null | head -n 20 | tee -a "$OUTPUT_FILE"
    log_info "DNS Servers:"
    cat /etc/resolv.conf 2>/dev/null | grep nameserver | tee -a "$OUTPUT_FILE"
    
    section_header "Environment Variables"
    log_info "Relevant Environment Variables (PATH, LD_PRELOAD, SHELL, EDITOR, PAGER):"
    env | grep -E '^(PATH|LD_PRELOAD|SHELL|EDITOR|PAGER|PS1)' | tee -a "$OUTPUT_FILE"
    log_info "Full Environment (first 20 lines):"
    env | head -n 20 | tee -a "$OUTPUT_FILE"

    section_header "SUID/SGID Binaries"
    log_info "Searching for SUID (4000) and SGID (2000) binaries..."
    # Exclude common noisy directories and focus on interesting types
    find / -perm -4000 -o -perm -2000 -type f 2>/dev/null \
        | grep -v '/sys/' | grep -v '/proc/' | grep -v '/usr/lib/' | grep -v '/lib/' \
        | xargs -r ls -la 2>/dev/null \
        | tee -a "$OUTPUT_FILE"
    # Common SUID binaries to look for specific exploits
    log_info "Common SUID binaries known for potential exploits (e.g., nmap, find, awk, vim, less, more, nano, cp, mv, dd, passwd):"
    find / -type f -perm -4000 -name "nmap" -o -name "find" -o -name "awk" -o -name "vim" -o -name "less" -o -name "more" -o -name "nano" -o -name "cp" -o -name "mv" -o -name "dd" -o -name "passwd" 2>/dev/null \
        | xargs -r ls -la 2>/dev/null \
        | tee -a "$OUTPUT_FILE"

    section_header "Sudo Privileges"
    log_info "Checking current user's sudo privileges..."
    sudo -l 2>/dev/null | tee -a "$OUTPUT_FILE"
    if grep -q "NOPASSWD" "$OUTPUT_FILE"; then
        log_success "NOPASSWD entry found for current user! Review sudo -l output carefully."
    else
        log_info "No NOPASSWD entries found for current user via sudo -l."
    fi
    log_info "Checking for readable /etc/sudoers (if accessible)..."
    cat /etc/sudoers 2>/dev/null | tee -a "$OUTPUT_FILE"
    log_info "Checking for readable files in /etc/sudoers.d/ (if accessible)..."
    ls -la /etc/sudoers.d/ 2>/dev/null | tee -a "$OUTPUT_FILE"
    for sudo_file in /etc/sudoers.d/*; do
        if [ -f "$sudo_file" ]; then
            log_info "Content of $sudo_file:"
            cat "$sudo_file" 2>/dev_null | tee -a "$OUTPUT_FILE"
        fi
    done

    section_header "Cron Jobs"
    log_info "Checking system-wide cron jobs (/etc/crontab):"
    cat /etc/crontab 2>/dev/null | tee -a "$OUTPUT_FILE"
    log_info "Checking cron directories (/etc/cron.*):"
    ls -la /etc/cron.* 2>/dev/null | tee -a "$OUTPUT_FILE"
    log_info "Checking contents of /etc/cron.d/:"
    ls -la /etc/cron.d/ 2>/dev_null | tee -a "$OUTPUT_FILE"
    for cron_d_file in /etc/cron.d/*; do
        if [ -f "$cron_d_file" ]; then
            log_info "Content of $cron_d_file:"
            cat "$cron_d_file" 2>/dev/null | tee -a "$OUTPUT_FILE"
        fi
    done
    log_info "Checking current user's crontab (if accessible):"
    crontab -l 2>/dev/null | tee -a "$OUTPUT_FILE"
    if [ $? -eq 0 ]; then
        log_success "User crontab found."
    fi

    section_header "Writable Files and Directories"
    log_info "Searching for world-writable files in common interesting locations (/tmp, /dev/shm, /var/tmp):"
    find /tmp /dev/shm /var/tmp -xdev -perm -o+w -type f 2>/dev/null | head -n 50 | tee -a "$OUTPUT_FILE"
    log_info "Searching for world-writable directories in common interesting locations (/tmp, /dev/shm, /var/tmp):"
    find /tmp /dev/shm /var/tmp -xdev -perm -o+w -type d 2>/dev/null | head -n 50 | tee -a "$OUTPUT_FILE"
    log_info "Searching for globally writable files in PATH (up to 50 results to avoid noise):"
    for p_dir in $(echo $PATH | tr ':' ' '); do
        find "$p_dir" -maxdepth 1 -perm -o+w -type f 2>/dev/null | head -n 10
    done | tee -a "$OUTPUT_FILE"

    section_header "Processes and Services"
    log_info "Top 10 processes by CPU/Memory usage:"
    ps aux --sort=-%cpu | head -n 11 | tee -a "$OUTPUT_FILE"
    log_info "Processes running as root (first 20):"
    ps aux | grep root | head -n 20 | tee -a "$OUTPUT_FILE"
    log_info "Systemd services (first 20 lines):"
    systemctl list-units --type=service --state=running 2>/dev/null | head -n 20 | tee -a "$OUTPUT_FILE"

    section_header "Software and Configurations"
    log_info "Installed packages (APT/YUM - first 20):"
    if command -v apt &>/dev/null; then
        apt list --installed 2>/dev/null | head -n 20 | tee -a "$OUTPUT_FILE"
    elif command -v yum &>/dev/null; then
        yum list installed 2>/dev/null | head -n 20 | tee -a "$OUTPUT_FILE"
    fi
    log_info "Content of /etc/passwd (first 20 lines):"
    head -n 20 /etc/passwd 2>/dev/null | tee -a "$OUTPUT_FILE"
    log_info "Content of /etc/shadow (if accessible - first 5 lines):"
    head -n 5 /etc/shadow 2>/dev/null | tee -a "$OUTPUT_FILE" # Usually not readable
    log_info "Content of /etc/fstab (if accessible):"
    cat /etc/fstab 2>/dev/null | tee -a "$OUTPUT_FILE"

    log_info "Enumeration complete. Review the output file for potential escalation paths."
}

main "$@"
Maintaining a secure and anonymous connection during an engagement is paramount. Services like VPNWG ensure that your reconnaissance and exploitation traffic is routed securely, regardless of the tools you deploy.

Practical Script Usage and Output Interpretation

To execute `privesc_enum.sh`, simply transfer it to the target system (e.g., via `scp`, `wget`, or `nc`) and make it executable:
chmod +x privesc_enum.sh
./privesc_enum.sh
The script will dump its findings to a timestamped file in `/tmp/privesc_results/`. Reviewing this output systematically is crucial.

Example Output Snippets:

When the script runs, expect output similar to these fragments, indicating potential areas of interest:

====================================================
=== SUID/SGID Binaries ===
====================================================

[*] Searching for SUID (4000) and SGID (2000) binaries...
-rwsr-xr-x 1 root root 64640 Mar 27  2023 /usr/bin/sudo
-rwsr-xr-x 1 root root 82024 Mar 27  2023 /usr/bin/pkexec
-rwsr-xr-x 1 root root 40040 Apr 26  2023 /usr/bin/passwd
-rwxr-sr-x 1 root tty 18456 Mar 27  2023 /usr/bin/write
-rwsr-xr-x 1 root root 105928 Mar 27  2023 /usr/bin/newgrp
-rwsr-xr-x 1 root root 40384 Mar 27  2023 /usr/bin/mount
-rwsr-xr-x 1 root root 36184 Mar 27  2023 /usr/bin/umount
-rwsr-xr-x 1 root root 146184 Mar 27  2023 /usr/local/bin/nmap # Potential if not standard
...
**Interpretation:** Highlighting SUID binaries like `sudo`, `pkexec`, and `passwd` is expected. However, discovering less common SUID binaries, especially if they are old or custom (`/usr/local/bin/nmap` for example), warrants further investigation for known vulnerabilities or misconfigurations. The GTFOBins and LOLBAS projects are excellent resources for checking if these binaries can be abused.

====================================================
=== Sudo Privileges ===
====================================================

[*] Checking current user's sudo privileges...
Matching Defaults entries for limiteduser on vulnerable-host:
    env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin

User limiteduser may run the following commands on vulnerable-host:
    (ALL : ALL) NOPASSWD: /usr/bin/find
[+] NOPASSWD entry found for current user! Review sudo -l output carefully.
...
**Interpretation:** A `NOPASSWD` entry for `find` is a clear path to root. The command `sudo find . -exec /bin/sh \; -quit` could grant a root shell. Any NOPASSWD entry is a high-priority finding.

====================================================
=== Cron Jobs ===
====================================================

[*] Checking system-wide cron jobs (/etc/crontab):
# /etc/crontab: system-wide crontab
# ...
0 * * * * root run-parts --report /etc/cron.hourly
...
[*] Checking cron directories (/etc/cron.*):
...
drwxr-xr-x   2 root root 4096 Apr  1 05:01 cron.d
-rw-r--r--   1 root root 1024 Apr  1 05:01 cron.daily
...
[*] Checking contents of /etc/cron.d/:
-rw-r--r-- 1 root root  186 Apr  1 05:01 script_backup
[*] Content of /etc/cron.d/script_backup:
# Backup critical data
0 2 * * * root /opt/backup_scripts/daily_backup.sh
...
**Interpretation:** Identifying `script_backup` in `/etc/cron.d/` and its content is important. If `/opt/backup_scripts/daily_backup.sh` (or any directory in its path) is writable by our low-privileged user, we could modify the script or hijack its execution. While our script focuses on host-based enumeration, identifying broader system vulnerabilities often involves comprehensive scanning. Automated web security testing platforms like Secably complement host-based efforts by revealing application-layer weaknesses that could also lead to initial access or privilege escalation.

Next Steps After Enumeration

The output from `privesc_enum.sh` serves as a roadmap. For each identified potential vector:
  • SUID/SGID: Consult GTFOBins or Exploit-DB for known exploits related to the specific binary and its version.
  • Sudo: If `NOPASSWD` is present, craft the appropriate command to gain a root shell.
  • Kernel: Use `uname -a` output to search Exploit-DB or Google for kernel-specific exploits. Tools like `searchsploit` can automate this.
  • Cron Jobs: Check permissions on scripts executed by cron. If writable, inject malicious commands.
  • Writable Paths: Can you replace a critical script or modify configuration files? Pay attention to PATH variable manipulation if writable directories are in the current user's PATH before root's PATH.
This script is a starting point. Customization is key; adjust the `find` commands, add checks for specific software versions, or incorporate more detailed file content analysis based on the target environment.