Skip to content

Instantly share code, notes, and snippets.

@hed0rah
Last active June 14, 2026 19:36
Show Gist options
  • Select an option

  • Save hed0rah/a6e16941b73b7182ad7c71699aaf4869 to your computer and use it in GitHub Desktop.

Select an option

Save hed0rah/a6e16941b73b7182ad7c71699aaf4869 to your computer and use it in GitHub Desktop.
hand rolled namespace container
#!/usr/bin/env bash
#
# hovercraft.sh - Build an ultra-stripped BusyBox from scratch,
# populate a minimal rootfs, and pivot into an isolated namespace.
#
set -euo pipefail
ROOT="${1:-/tmp/handroll-rootfs}"
HOSTNAME_IN="earth"
CR=$'\r'
if [[ $EUID -ne 0 ]]; then
echo "must run as root (sudo)" >&2
exit 1
fi
# ---------------------------------------------------------------------------
# 1. Fetch and compile an ultra-stripped static BusyBox
# ---------------------------------------------------------------------------
echo "[*] Downloading BusyBox source via OpenSSL..."
openssl s_client -connect busybox.net:443 -quiet 2>/dev/null <<EOF | sed '1,/^\r$/d' > busybox-1.38.0.tar.bz2
GET /downloads/busybox-1.38.0.tar.bz2 HTTP/1.1${CR}
Host: busybox.net${CR}
Connection: close${CR}
${CR}
EOF
echo "[*] Extracting source..."
tar -xjf busybox-1.38.0.tar.bz2
cd busybox-1.38.0
echo "[*] Configuring minimal applet profile..."
make allnoconfig
for applet in ASH SH_IS_ASH LS CAT ECHO PWD \
MOUNT UMOUNT HOSTNAME IFCONFIG ROUTE \
MKDIR LN INIT HALT REBOOT POWEROFF; do
sed -i "s/# CONFIG_$applet is not set/CONFIG_$applet=y/" .config
done
sed -i 's/# CONFIG_STATIC is not set/CONFIG_STATIC=y/' .config
sed -i 's/# CONFIG_STRIP is not set/CONFIG_STRIP=y/' .config
sed -i 's/# CONFIG_BUILD_OPTIMIZED_FOR_SIZE is not set/CONFIG_BUILD_OPTIMIZED_FOR_SIZE=y/' .config
make silentoldconfig
make -j"$(nproc)"
BUSYBOX="$(pwd)/busybox"
cd ..
# ---------------------------------------------------------------------------
# 2. Directory skeleton creation
# ---------------------------------------------------------------------------
echo "[*] building rootfs skeleton at $ROOT"
rm -rf "$ROOT"
mkdir -p "$ROOT"/{bin,sbin,etc,proc,sys,dev,tmp,root,run,var,usr/bin,usr/sbin,home}
mkdir -p "$ROOT"/dev/{pts,shm}
mkdir -p "$ROOT"/var/{log,tmp}
mkdir -p "$ROOT"/.oldroot
chmod 1777 "$ROOT"/tmp "$ROOT"/var/tmp "$ROOT"/dev/shm
# ---------------------------------------------------------------------------
# 3. Populate applet symlinks
# ---------------------------------------------------------------------------
echo "[*] installing busybox and applet symlinks"
cp "$BUSYBOX" "$ROOT/bin/busybox"
chmod 755 "$ROOT/bin/busybox"
while read -r applet; do
[[ -z "$applet" ]] && continue
ln -sf busybox "$ROOT/bin/$applet"
done < <("$ROOT/bin/busybox" --list 2>/dev/null)
ln -sf busybox "$ROOT/bin/sh"
for s in init halt reboot poweroff ip ifconfig route; do
ln -sf ../bin/busybox "$ROOT/sbin/$s" 2>/dev/null || true
done
# ---------------------------------------------------------------------------
# 4. /etc account database setup
# ---------------------------------------------------------------------------
echo "[*] writing accounts database"
cat > "$ROOT/etc/passwd" <<'EOF'
root:x:0:0:root:/root:/bin/sh
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/run/ircd:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
EOF
cat > "$ROOT/etc/group" <<'EOF'
root:x:0:
daemon:x:1:
bin:x:2:
sys:x:3:
adm:x:4:
tty:x:5:
disk:x:6:
lp:x:7:
mail:x:8:
news:x:9:
uucp:x:10:
man:x:12:
proxy:x:13:
kmem:x:15:
dialout:x:20:
fax:x:21:
voice:x:22:
cdrom:x:24:
floppy:x:25:
tape:x:26:
sudo:x:27:
audio:x:29:
games:x:60:
users:x:100:
www-data:x:33:
backup:x:34:
list:x:38:
irc:x:39:
nogroup:x:65534:
EOF
# ---------------------------------------------------------------------------
# 5. Hostname & Network Configuration
# ---------------------------------------------------------------------------
echo "[*] writing minimal network configs"
echo "$HOSTNAME_IN" > "$ROOT/etc/hostname"
cat > "$ROOT/etc/hosts" <<EOF
127.0.0.1 localhost
::1 localhost ip6-localhost ip6-loopback
127.1.1.1 $HOSTNAME_IN
EOF
# ---------------------------------------------------------------------------
# 6. The Execution Payload (Runs INSIDE the new namespaces)
# ---------------------------------------------------------------------------
cat > "$ROOT/rc.container" <<'EOF'
#!/bin/sh
set -eu
mount -t proc proc /proc
mount -t sysfs sysfs /sys
mount -t devtmpfs devtmpfs /dev 2>/dev/null || mount -t tmpfs devtmpfs /dev
mkdir -p /dev/pts /dev/shm
mount -t devpts devpts /dev/pts
mount -t tmpfs shm /dev/shm
ifconfig lo 127.0.0.1 up
export PATH=/bin:/sbin:/usr/bin:/usr/sbin
export HOME=/root
export TERM=xterm-256color
# Fix terminal environment and inject raw colors for the screenshot
alias ls='ls --color=auto'
export PS1='\[\033[38;5;36m\]04:20:00 \[\033[38;5;36m\]earth\[\033[38;5;202m\]☠\[\033[38;5;36m\][\w]\[\033[38;5;202m\] # \[\033[0m\]'
echo "===================================================="
echo " Welcome to the Panopticon. Everyone is Root. "
echo "===================================================="
exec /bin/sh
EOF
chmod +x "$ROOT/rc.container"
# ---------------------------------------------------------------------------
# 7. Unshare, Pivot, and Execute
# ---------------------------------------------------------------------------
echo "[*] launching container via unshare + pivot_root..."
unshare -m -u -i -p -f bash -c "
mount --bind '$ROOT' '$ROOT'
cd '$ROOT'
pivot_root . .oldroot
umount -l /.oldroot
hostname '$HOSTNAME_IN'
exec /rc.container
"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment