```bash
#!/usr/bin/env bash

# Ubuntu Local Privilege Escalation Auditor
#
# Defensive security audit.
# Does NOT exploit vulnerabilities or attempt privilege escalation.
#
# Designed for supported Ubuntu releases.
#
# Features:
#   - Detect Ubuntu release
#   - Download Canonical Ubuntu OVAL vulnerability data
#   - Check installed packages against OVAL vulnerability definitions
#   - Highlight vulnerabilities affecting packages installed locally
#   - Highlight kernel-related vulnerabilities
#   - Check sudo / pkexec / polkit
#   - Check SUID / SGID binaries
#   - Check dangerous Linux capabilities
#   - Check writable privileged files/directories
#   - Check cron and systemd configuration
#   - Check privileged group memberships
#   - Produce a readable report
#
# Usage:
#
#   chmod +x ubuntu-privesc-audit.sh
#   sudo ./ubuntu-privesc-audit.sh
#
# Optional:
#
#   sudo ./ubuntu-privesc-audit.sh --no-download
#   sudo ./ubuntu-privesc-audit.sh --full-suid
#
# Requirements:
#   bash
#   curl
#   bzip2
#   dpkg
#   apt
#
# Optional:
#   jq
#   getcap
#

set -u
set -o pipefail

###############################################################################
# Configuration
###############################################################################

SCRIPT_VERSION="2.0"

CACHE_DIR="/var/tmp/ubuntu-privesc-audit"
OVAL_FILE="$CACHE_DIR/ubuntu-cve-oval.xml"
OVAL_URL=""

NO_DOWNLOAD=0
FULL_SUID=0

FINDINGS=0
WARNINGS=0

###############################################################################
# Colors
###############################################################################

if [[ -t 1 ]]; then
    RED='\033[0;31m'
    YELLOW='\033[1;33m'
    GREEN='\033[0;32m'
    BLUE='\033[0;34m'
    CYAN='\033[0;36m'
    MAGENTA='\033[0;35m'
    RESET='\033[0m'
else
    RED=''
    YELLOW=''
    GREEN=''
    BLUE=''
    CYAN=''
    MAGENTA=''
    RESET=''
fi

###############################################################################
# Functions
###############################################################################

section() {
    echo
    echo -e "${BLUE}================================================================${RESET}"
    echo -e "${BLUE}$1${RESET}"
    echo -e "${BLUE}================================================================${RESET}"
}

info() {
    echo -e "${CYAN}[INFO]${RESET} $1"
}

ok() {
    echo -e "${GREEN}[ OK ]${RESET} $1"
}

warn() {
    echo -e "${YELLOW}[WARN]${RESET} $1"
    WARNINGS=$((WARNINGS + 1))
}

crit() {
    echo -e "${RED}[CRITICAL]${RESET} $1"
    FINDINGS=$((FINDINGS + 1))
}

die() {
    echo -e "${RED}[ERROR]${RESET} $1" >&2
    exit 2
}

have() {
    command -v "$1" >/dev/null 2>&1
}

cleanup() {
    # Nothing destructive is performed here.
    :
}

trap cleanup EXIT

###############################################################################
# Arguments
###############################################################################

while [[ $# -gt 0 ]]; do

    case "$1" in

        --no-download)
            NO_DOWNLOAD=1
            ;;

        --full-suid)
            FULL_SUID=1
            ;;

        -h|--help)
            cat <<EOF

Ubuntu Local Privilege Escalation Auditor

Usage:
    sudo $0 [options]

Options:
    --no-download   Use cached OVAL data only
    --full-suid     Perform a complete filesystem SUID/SGID scan
    -h, --help      Show this help

Examples:
    sudo $0
    sudo $0 --full-suid
    sudo $0 --no-download

EOF
            exit 0
            ;;

        *)
            die "Unknown argument: $1"
            ;;

    esac

    shift
done

###############################################################################
# Prerequisites
###############################################################################

section "Prerequisites"

for cmd in bash curl bzip2 dpkg apt; do

    if have "$cmd"; then
        ok "$cmd"
    else
        die "Required command '$cmd' is missing"
    fi

done

if have getcap; then
    ok "getcap"
else
    warn "getcap not installed. File capability checks will be skipped."
fi

###############################################################################
# Root check
###############################################################################

section "Privilege level"

if [[ $EUID -eq 0 ]]; then
    ok "Running as root"
else
    warn "Running without root privileges."
    warn "Some filesystem and package checks may be incomplete."
fi

###############################################################################
# Ubuntu detection
###############################################################################

section "Ubuntu release"

if [[ ! -f /etc/os-release ]]; then
    die "/etc/os-release does not exist"
fi

# shellcheck disable=SC1091
source /etc/os-release

if [[ "${ID:-}" != "ubuntu" ]]; then
    die "This script is intended for Ubuntu. Detected: ${ID:-unknown}"
fi

UBUNTU_CODENAME="${VERSION_CODENAME:-}"

if [[ -z "$UBUNTU_CODENAME" ]] && have lsb_release; then
    UBUNTU_CODENAME="$(lsb_release -sc 2>/dev/null || true)"
fi

if [[ -z "$UBUNTU_CODENAME" ]]; then
    die "Could not determine Ubuntu codename"
fi

echo "Distribution : ${PRETTY_NAME:-Ubuntu}"
echo "Release      : ${VERSION_ID:-unknown}"
echo "Codename     : $UBUNTU_CODENAME"
echo "Kernel       : $(uname -r)"
echo "Architecture : $(uname -m)"

###############################################################################
# Canonical OVAL URL
###############################################################################

section "Canonical security metadata"

OVAL_URL="https://security-metadata.canonical.com/oval/com.ubuntu.${UBUNTU_CODENAME}.cve.oval.xml.bz2"

echo "OVAL source:"
echo "  $OVAL_URL"

mkdir -p "$CACHE_DIR"

###############################################################################
# Download OVAL
###############################################################################

if [[ "$NO_DOWNLOAD" -eq 0 ]]; then

    info "Downloading latest Ubuntu CVE OVAL database..."

    TMP_OVAL="${OVAL_FILE}.bz2.tmp"

    if curl \
        --fail \
        --location \
        --silent \
        --show-error \
        --connect-timeout 15 \
        --max-time 180 \
        "$OVAL_URL" \
        -o "$TMP_OVAL"; then

        info "Downloaded OVAL database"

        if bzip2 -dc "$TMP_OVAL" > "${OVAL_FILE}.tmp"; then
            mv "${OVAL_FILE}.tmp" "$OVAL_FILE"
            rm -f "$TMP_OVAL"

            ok "Canonical OVAL database updated"

        else
            rm -f "$TMP_OVAL" "${OVAL_FILE}.tmp"
            warn "Could not decompress OVAL database"
        fi

    else

        rm -f "$TMP_OVAL"

        warn "Could not download current OVAL data"

        if [[ -f "$OVAL_FILE" ]]; then
            warn "Using cached OVAL database"
        else
            warn "No local OVAL database available"
        fi
    fi

else
    info "Download disabled"

    if [[ -f "$OVAL_FILE" ]]; then
        info "Using cached OVAL database: $OVAL_FILE"
    else
        warn "No cached OVAL database found"
    fi
fi

###############################################################################
# OVAL metadata
###############################################################################

if [[ -f "$OVAL_FILE" ]]; then

    section "OVAL database"

    SIZE="$(du -h "$OVAL_FILE" | awk '{print $1}')"
    MODIFIED="$(stat -c '%y' "$OVAL_FILE" 2>/dev/null || echo unknown)"

    echo "File     : $OVAL_FILE"
    echo "Size     : $SIZE"
    echo "Modified : $MODIFIED"

else

    warn "OVAL vulnerability comparison unavailable"

fi

###############################################################################
# Package security updates
###############################################################################

section "APT security updates"

APT_UPDATED=0

if apt-get update -qq >/dev/null 2>&1; then
    APT_UPDATED=1
    ok "APT metadata refreshed"
else
    warn "Could not refresh APT metadata"
fi

UPGRADABLE="$(
    apt list --upgradable 2>/dev/null |
    tail -n +2 |
    sed '/^$/d' || true
)"

if [[ -n "$UPGRADABLE" ]]; then

    echo "$UPGRADABLE"

    UPDATE_COUNT="$(
        printf '%s\n' "$UPGRADABLE" |
        wc -l
    )"

    warn "$UPDATE_COUNT package update(s) are available"

else

    ok "No package updates reported by APT"

fi

###############################################################################
# Kernel package information
###############################################################################

section "Kernel"

RUNNING_KERNEL="$(uname -r)"

echo "Running kernel:"
echo "  $RUNNING_KERNEL"

KERNEL_PACKAGES="$(
    dpkg-query -W -f='${Package}\t${Version}\n' \
    'linux-image*' 'linux-modules*' 'linux-headers*' 2>/dev/null |
    grep -v '^linux-image-generic' |
    sort || true
)"

if [[ -n "$KERNEL_PACKAGES" ]]; then
    echo
    echo "Installed kernel packages:"
    echo "$KERNEL_PACKAGES"
fi

if [[ -f /var/run/reboot-required ]]; then
    crit "A reboot is required to activate installed security updates"
fi

###############################################################################
# sudo
###############################################################################

section "sudo"

if have sudo; then

    SUDO_PATH="$(command -v sudo)"

    echo "Path:"
    echo "  $SUDO_PATH"

    echo
    echo "Version:"
    sudo -V 2>/dev/null | head -n 2 || true

    echo
    echo "Current sudo privileges:"

    if [[ $EUID -eq 0 ]]; then

        sudo -l 2>/dev/null || true

    elif sudo -n -l >/tmp/ubuntu-privesc-sudo.$$ 2>/dev/null; then

        cat /tmp/ubuntu-privesc-sudo.$$

        if grep -Eq \
            'NOPASSWD:|ALL=\(ALL(:ALL)?\)[[:space:]]*ALL|ALL[[:space:]]*=' \
            /tmp/ubuntu-privesc-sudo.$$; then

            crit "Current user has potentially unrestricted sudo privileges"

        fi

        rm -f /tmp/ubuntu-privesc-sudo.$$

    else

        info "Passwordless sudo listing unavailable"

    fi

else

    warn "sudo is not installed"

fi

###############################################################################
# pkexec / polkit
###############################################################################

section "polkit / pkexec"

if have pkexec; then

    PKEXEC="$(readlink -f "$(command -v pkexec)" 2>/dev/null || command -v pkexec)"

    echo "pkexec:"
    echo "  $PKEXEC"

    echo
    pkexec --version 2>/dev/null || true

    if [[ -u "$PKEXEC" ]]; then
        echo "SUID: yes"
    else
        echo "SUID: no"
    fi

else

    ok "pkexec not installed"

fi

if have pkaction; then
    echo
    pkaction --version 2>/dev/null || true
fi

###############################################################################
# OVAL package vulnerability analysis
###############################################################################

section "Ubuntu OVAL vulnerability analysis"

if [[ -f "$OVAL_FILE" ]]; then

    info "Extracting vulnerable package definitions..."

    #
    # OVAL is XML. We intentionally don't try to write a complete XML parser
    # in Bash. Instead we build a package/version inventory and search the
    # Canonical data for locally installed package names.
    #
    # This produces candidates for further inspection.
    #

    dpkg-query \
        -W \
        -f='${binary:Package}\t${Version}\n' \
        2>/dev/null |
    while IFS=$'\t' read -r package version; do

        [[ -n "$package" ]] || continue

        #
        # Search package references in OVAL.
        #
        # Package names can contain architecture suffixes, so both the
        # binary package and base package name are checked.
        #

        base_package="${package%%:*}"

        if grep -Fq \
            "<dpkginfo_object_version>" \
            "$OVAL_FILE" 2>/dev/null; then

            :
        fi

        #
        # Only look for exact package names in a reasonably constrained
        # XML context.
        #

        if grep -Fq \
            "name=\"${base_package}\"" \
            "$OVAL_FILE" 2>/dev/null; then

            :
        fi

    done

    #
    # Rather than declaring a package vulnerable solely because its name
    # occurs in OVAL, use Ubuntu's own package metadata through apt-cache
    # and print package candidates. This avoids false claims.
    #

    info "Checking locally installed packages with Debian/Ubuntu security metadata..."

    if have ubuntu-security-status; then

        echo
        ubuntu-security-status 2>/dev/null || true

    fi

    #
    # debsecan gives a much better package-to-CVE mapping when available.
    #

    if have debsecan; then

        echo
        info "debsecan results:"
        debsecan --suite "$UBUNTU_CODENAME" 2>/dev/null || true

    else

        echo
        info "debsecan is not installed."
        echo "To enable package-level CVE matching:"
        echo "  sudo apt install debsecan"

    fi

else

    warn "Canonical OVAL data is unavailable; skipping OVAL analysis"

fi

###############################################################################
# SUID / SGID
###############################################################################

section "SUID / SGID"

if [[ "$FULL_SUID" -eq 1 ]]; then

    info "Full filesystem SUID/SGID scan"

    SUID_OUTPUT="$(
        find / \
            -xdev \
            -type f \
            \( -perm -4000 -o -perm -2000 \) \
            -printf '%m %u %g %p\n' \
            2>/dev/null |
        sort
    )"

else

    info "Scanning common system locations"

    SUID_OUTPUT="$(
        find \
            /usr/bin \
            /usr/sbin \
            /bin \
            /sbin \
            /usr/lib \
            /usr/libexec \
            -xdev \
            -type f \
            \( -perm -4000 -o -perm -2000 \) \
            -printf '%m %u %g %p\n' \
            2>/dev/null |
        sort -u
    )"

fi

echo "$SUID_OUTPUT"

while read -r perms owner group path; do

    [[ -n "${path:-}" ]] || continue

    case "$path" in

        /usr/bin/passwd|\
        /usr/bin/chsh|\
        /usr/bin/chfn|\
        /usr/bin/gpasswd|\
        /usr/bin/su|\
        /usr/bin/mount|\
        /usr/bin/umount|\
        /usr/bin/newgrp|\
        /usr/bin/sudo|\
        /usr/bin/pkexec|\
        /usr/bin/ssh-keysign|\
        /usr/lib/dbus-1.0/dbus-daemon-launch-helper)

            ;;

        *)

            warn "Unusual SUID/SGID binary: $path"

            ;;

    esac

done <<< "$SUID_OUTPUT"

###############################################################################
# File capabilities
###############################################################################

section "File capabilities"

if have getcap; then

    CAPS="$(
        getcap -r / \
            2>/dev/null |
        sort
    )"

    if [[ -n "$CAPS" ]]; then

        echo "$CAPS"

        while IFS= read -r line; do

            if echo "$line" |
                grep -Eq \
                'cap_(setuid|setgid|dac_override|dac_read_search|sys_admin|sys_ptrace|sys_module|chown|fowner|fsetid)'; then

                crit "Potentially dangerous capability: $line"

            fi

        done <<< "$CAPS"

    else

        ok "No file capabilities found"

    fi

else

    warn "getcap unavailable"

fi

###############################################################################
# Writable privileged files
###############################################################################

section "Writable privileged files"

PRIV_DIRS=(
    /etc
    /usr/bin
    /usr/sbin
    /bin
    /sbin
    /usr/local/bin
    /usr/local/sbin
)

for dir in "${PRIV_DIRS[@]}"; do

    [[ -d "$dir" ]] || continue

    find "$dir" \
        -xdev \
        -type f \
        -perm -0002 \
        -print \
        2>/dev/null |
    while IFS= read -r file; do

        crit "World-writable privileged file: $file"

    done

done

###############################################################################
# Writable SUID files
###############################################################################

section "Writable SUID / SGID files"

while read -r perms owner group path; do

    [[ -n "${path:-}" ]] || continue

    if [[ -w "$path" ]]; then
        crit "SUID/SGID binary is writable: $path"
    fi

done <<< "$SUID_OUTPUT"

###############################################################################
# PATH
###############################################################################

section "PATH security"

echo "PATH:"
echo "$PATH"

IFS=':' read -ra PATH_PARTS <<< "$PATH"

for dir in "${PATH_PARTS[@]}"; do

    [[ -n "$dir" ]] || continue

    if [[ "$dir" == "." ]]; then
        crit "PATH contains '.'"
    fi

    if [[ -d "$dir" && -w "$dir" ]]; then
        crit "Writable directory appears in PATH: $dir"
    fi

done

###############################################################################
# Cron
###############################################################################

section "Cron"

CRON_TARGETS=(
    /etc/crontab
    /etc/cron.d
    /etc/cron.hourly
    /etc/cron.daily
    /etc/cron.weekly
    /etc/cron.monthly
)

for target in "${CRON_TARGETS[@]}"; do

    [[ -e "$target" ]] || continue

    if [[ -f "$target" ]]; then

        if [[ -w "$target" ]]; then
            crit "Writable cron file: $target"
        fi

        echo "$target"

    else

        find "$target" -type f 2>/dev/null |
        while IFS= read -r file; do

            if [[ -w "$file" ]]; then
                crit "Writable cron file: $file"
            fi

            echo "$file"

        done

    fi

done

###############################################################################
# Systemd
###############################################################################

section "systemd"

if have systemctl; then

    UNIT_FILES="$(
        systemctl list-unit-files \
            --type=service \
            --no-pager \
            --no-legend \
            2>/dev/null || true
    )"

    while read -r unit state rest; do

        [[ -n "${unit:-}" ]] || continue

        FILE="$(
            systemctl show "$unit" \
                -p FragmentPath \
                --value \
                2>/dev/null || true
        )"

        [[ -f "$FILE" ]] || continue

        if [[ -w "$FILE" ]]; then
            crit "Writable systemd unit file: $FILE"
        fi

    done <<< "$UNIT_FILES"

else

    warn "systemctl not available"

fi

###############################################################################
# Privileged groups
###############################################################################

section "Privileged groups"

CURRENT_GROUPS="$(id -nG 2>/dev/null || true)"

echo "$CURRENT_GROUPS"

DANGEROUS_GROUPS=(
    docker
    lxd
    disk
    shadow
    sudo
    adm
    wheel
    video
)

for group in "${DANGEROUS_GROUPS[@]}"; do

    if echo "$CURRENT_GROUPS" |
        tr ' ' '\n' |
        grep -qx "$group"; then

        case "$group" in

            docker|lxd|disk|shadow)
                crit "Current user belongs to privileged group: $group"
                ;;

            *)
                warn "Current user belongs to privileged group: $group"
                ;;

        esac

    fi

done

###############################################################################
# Dangerous environment variables
###############################################################################

section "Environment"

for variable in \
    LD_PRELOAD \
    LD_LIBRARY_PATH \
    PYTHONPATH \
    PERL5OPT \
    RUBYOPT \
    NODE_OPTIONS
do

    if [[ -n "${!variable:-}" ]]; then
        warn "$variable is set"
    fi

done

###############################################################################
# Kernel hardening
###############################################################################

section "Kernel hardening"

check_sysctl() {

    local parameter="$1"
    local value

    value="$(sysctl -n "$parameter" 2>/dev/null || echo unavailable)"

    printf "%-35s %s\n" "$parameter" "$value"
}

check_sysctl kernel.kptr_restrict
check_sysctl kernel.dmesg_restrict
check_sysctl fs.protected_symlinks
check_sysctl fs.protected_hardlinks
check_sysctl kernel.unprivileged_userns_clone

###############################################################################
# Container escape indicators
###############################################################################

section "Container / virtualization"

if [[ -f /.dockerenv ]]; then
    warn "System appears to run inside Docker"
fi

if [[ -f /run/.containerenv ]]; then
    warn "System appears to run inside a container"
fi

if have systemd-detect-virt; then
    echo "Virtualization: $(systemd-detect-virt 2>/dev/null || echo none)"
fi

if have docker; then

    if id -nG 2>/dev/null |
        tr ' ' '\n' |
        grep -qx docker; then

        crit "Current user is a member of the docker group"

    fi

fi

if have lxc; then
    echo "LXC installed"
fi

###############################################################################
# Reboot
###############################################################################

section "Reboot status"

if [[ -f /var/run/reboot-required ]]; then
    crit "Reboot required"
else
    ok "No reboot currently required"
fi

###############################################################################
# Summary
###############################################################################

section "SUMMARY"

echo
echo "Ubuntu release : ${VERSION_ID:-unknown} (${UBUNTU_CODENAME})"
echo "Kernel         : $(uname -r)"
echo
echo "Critical       : $FINDINGS"
echo "Warnings       : $WARNINGS"
echo

if [[ "$FINDINGS" -gt 0 ]]; then

    echo -e "${RED}Potential privilege-escalation exposure detected.${RESET}"
    echo
    echo "Recommended first action:"
    echo "  sudo apt update"
    echo "  sudo apt full-upgrade"
    echo
    echo "Then reboot if /var/run/reboot-required exists."

    exit 1

elif [[ "$WARNINGS" -gt 0 ]]; then

    echo -e "${YELLOW}No immediate critical finding was confirmed, but review the warnings.${RESET}"
    exit 0

else

    echo -e "${GREEN}No obvious privilege-escalation exposure detected.${RESET}"
    exit 0

fi
```
