Tai-hwa Liang Multimedia Laboratory, Dept. of Computer Science & Engineering Yuan Ze University, Taiwan 簡介 由 FreeBSD 4.0 開始,核心提供了一個類似 chroot(2) 的 system call:jail(2); 和 chroot(2) 不大一樣的,便是 jail 對於在其中執行的行程(process)做了更多的 限制,使得它比 chroot 更適合被用以產生「限制 root 權限的執行環境」。 何謂「限制 root 權限的執行環境?」簡單來說就是在該環境中執行的行程,即使具備 root 的權限,其所能對系統其他部份所造成之破壞是相當有限的。講得更明白一些,將 一個需要以 root 身份執行的程式(如 sendmail()放在該環境中執行,如果哪天 sendmail 被遠端攻擊者透過 buffer overflow 的方式取得了 root 的權限,但是因為 這個環境的保護,使得攻擊者所能破壞的不外乎是這個環境中(先把這個環境視為系統 上的一個目錄)的資料,對於該環境(目錄)以外的檔案或行程,攻擊者理論上是沒有 辦法做任何更改的(對 jail(2) 而言應是如此,但 chroot(2) 就不一定了)。 由此可見,jail 特別適合用來限制一些需以特權執行的網路服務;以下將以 BIND8 這個提供 DNS 服務的程式為例,說明如何設定 jail 的環境。 如何設定 jail 的環境 實際上,jail(2) 底層也會呼叫 chroot(2) 以改變行程對根目錄的起點,因此,設定 jail 環境和設定 chroot 環境概念上差不多,但是得注意下列幾點: a. FreeBSD 4.x 對 jail 環境的裝置檔(device files,也就是 /dev 下面 那一堆)有特別的關鍵字可以做出來。 b. 如果行程需要使用 SYSV IPC(如 shared memory、semaphore... 等等), 別忘了先使用 "sysctl -w jail.sysvipc_allowed=1"。 c. 每一個 jail 環境只能對應唯一一個 IP address,對 BIND 來說,這裡得 放棄如 127.0.0.1 這樣的位址不去 bind,因此在寫 /etc/resolv.conf 時也得注意一下,本來如果有 nameserver 指向 127.0.0.1 的,都要改掉。 至於若要在 jail 中執行 inetd ,可以用 -a 的參數限定要 bind 的網路 位址。 以下步驟以 FreeBSD 4.x 內建的 BIND8 為例,說明如何建立 jail 環境: 1. 建立 jail 的根目錄(這裡定為 /home/bind),之後該目錄便會成為 jail 後之行程的根目錄: # mkdir /home/bind 2. 建立基本系統所需的目錄: # mkdir -p /home/bind/dev # mkdir -p /home/bind/bin # mkdir -p /home/bind/etc/namedb # mkdir -p /home/bind/usr/lib # mkdir -p /home/bind/usr/libexec # mkdir -p /home/bind/usr/sbin # mkdir -p /home/bind/var/log # mkdir -p /home/bind/var/run 3. 複製/建立必需檔案: # cp /dev/MAKEDEV* /home/bind/dev # cp /usr/sbin/syslogd /home/bind/usr/sbin # (cd /bin; cp sh \[ ln rm /home/bind/bin) # (cd /etc; cp resolv.conf host.conf hosts services group \ master.passwd localtime syslog.conf /home/bind/etc) # toUCh /home/bind/var/run/utmp # touch /home/bind/var/log/messages # touch /home/bind/var/log/security * 其中像 resolv.conf、host.conf、hosts 與 service 等,都是一 些網路程式執行時會用到的檔案,而因為 jail 環境與「外界」不同, syslogd 是分開跑的,因此這邊也得準備 syslogd 的設定檔。 * 基於安全理由,master.passwd 或 group 等檔中只能留下必需的項 目,比方這個 jail 僅給 named 使用,就留下: bind:*:53:53::0:0:Bind SandBox:/:/sbin/nologin 一行在 master.passwd 中,並以: # pwd_mkdb -p -d /home/bind/etc /home/bind/etc/master.passwd 重建 passwd 資料庫即可;如此一來,即使攻擊者成功侵入 jail 環 境中,也不能藉由該檔得到系統上其他的帳號資料。 4. 以 "MAKEDEV jail" 做出適合 jail 環境的裝置檔: # (cd /home/bind/dev; sh MAKEDEV jail) 5. 複製 BIND8 執行時必需的函式庫;之後若其他的程式要放在 jail 內執行, 可先以 ldd 檢查該程式需要哪些函式庫,再複製過來: # cp /usr/libexec/ld-elf.so.1 /home/bind/usr/libexec # cp /usr/lib/libc.so.4 /home/bind/usr/lib 6. 複製 BIND8 執行時必需的檔案(BIND8 設定檔請自行複製/建立至 /home/bind/etc/namedb 下): # cp /usr/sbin/named /home/bind/usr/sbin # cp /usr/libexec/named-xfer /home/bind/usr/libexec # cp -pR /etc/namedb/* /home/bind/etc/namedb 7. 建立 jail 環境啟動檔 /home/bind/etc/rc,用來處理一些 jail 環境內 初始化的動作,比方說啟動 syslogd 與 named: /home/bind/etc/rc 內容(參考 /etc/rc): #! /bin/sh trap : 2 trap : 3 # shouldn't be needed HOME=/ PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin eXPort HOME PATH if [ ! -h /dev/log ]; then ln -sf /var/run/log /dev/log fi rm -f /var/run/log syslogd -ss named -u bind -g bind exit 0 8. 建立一個 /var/run/ndc 的鏈結,如此一來管理者可以在 jail 之外透過 ndc 來管理 jail 中的 named 行程;當然也可以把下列這個命令寫在 /etc/rc.local 中,以省去每次開機後都要重新建立鏈結的麻煩: # ln -sf /home/bind/var/run/ndc /var/run/ndc 至此一個可以執行 BIND8 的環境已經設好了,用以下命令試試看 jail 能不能正常運作: # /usr/sbin/jail /home/bind my.host.name my.ip.address \ /bin/sh /etc/rc 如果沒問題,用 "ps ax" 應該會看到類似: 35 ?? Is 0:00.00 adjkerntz -i 97 ?? Ss 0:00.17 syslogd -s 103 ?? SsJ 0:00.12 syslogd -ss 105 ?? SsJ 0:02.49 named -u bind -g bind 125 ?? Is 0:00.01 inetd -wW 127 ?? Is 0:00.19 cron 如果第三欄中有一個 'J',表示該行程正在某一 jail 中執行;再看看 /home/bind/var/log/messages,如果沒有錯誤訊息,表示差不多完成了: Dec 6 15:42:27 dns named[105]: Ready to answer queries. 最後用 nslookup 檢查一下能不能正確查詢;如果都正常,可以把前述啟動 方式加至 /etc/rc.conf 中: named_program="/usr/sbin/jail /home/bind my.host my.ip /bin/sh /etc/rc" 注意事項 * 基於系統安全理由,不同的服務應該放在不同的 jail 中,以免其中一項服務被攻陷後 影響到其他在同一 jail 中的服務程式。 * 原則上,一個 jail 中的檔案愈少,對攻擊者的挑戰性也愈高,上例還提供 sh、rm 等 程式,純粹因為作者太懶,想用 script 擺平後面的環境設定...。如果有興趣,可以將 第七步驟的工作用 C 寫成一個單一的程式(exec()、unlink()、symlink()),減少掉 bin 下一些不必要的檔案。 * jail(2) 底層還是會呼叫 chroot(2),因此千萬不要為了節省空間而用 symbolic link 的方式「建立」jail 中的檔案;如果一定要,請改用 hard link(將需要的檔案先複製 到與 jail 同一檔案系統之下,再以 hard link 鏈結至不同的 jail 之中)。比方說: # ln /home/jail_share/libc.so.4 /home/jail_1/usr/lib # ln /home/jail_share/libc.so.4 /home/jail_2/usr/lib # ln /home/jail_share/libc.so.4 /home/jail_3/usr/lib * 由於在 jail 環境中的 log 檔是各自獨立的,為管理方便起見,可以考慮將它們 symbolic link 至 /var/log 下,如: # ln -s /home/bind/var/log/messages /var/log/bind.log * 以 4.2-RELEASE 為例,和 jail 相關的 sysctl 有: jail.set_hostname_allowed jail.socket_unixiproute_only jail.sysvipc_allowed 這些在 jail( 的 manual page 中都有詳細的解釋,別忘了針對不同需要更改它們。 * 如果程式有需要存取 /proc,別忘了在進入 jail 前先將 procfs mount 上來: # mkdir /home/jail_1/proc # mount -t procfs proc /home/jail_1/proc 後記 這個 jailed named 的環境已經在我們實驗室的 DNS 上跑過幾個月了,用起來沒啥大 問題;一般而言,只要正確的設定檔與函式庫都複製到正確目錄下,大部份的程式都可 以在 jail 環境下正常工作。此外,對於一些有心發展網路程式(如 BBS)的開發者, 可以考慮在程式中呼叫 jail(2),增進程式的安全。 或許有人會問:「BIND8 不是已經提供 -t 的參數讓我們做 chroot 了,為什麼還要弄 個這麼麻煩的 jail 來保護它呢?」這裡得強調一點,chroot(2) 僅是改變一行程對根 目錄的起點(starting point of root Directory),對於其中的行程是不做任何額外 地限制的,也就是說,假使攻擊者自遠端透過 !@%#$*&_)! 的方式自 BIND8 取得 root 權限,即使被 chrootted 在一個固定的目錄(假設是 /home/bind),但理論上他/她 仍可以透過一些的 system call 對系統其他部份造成破壞,比方說: 1. 以 kill(2) 砍掉其他的行程(**)。 2. 以 mknod(2) 做出特殊裝置檔案,再透過這些檔案直接存取系統週邊(**)。 3. .... 於在 jail 環境中,1, 2 兩項行為已經被納入保護範圍了,至於傳說中的第 N 項 攻擊技術.... 呃,這裡只能說「沒有絕對安全的系統,」能做多少保護就盡力去做。 如果還有興趣往下研究,可以 grep 一下 /sys/kern,找找 PRISON_CHECK 或是 p_prison,看看 jail(2) 除了 SYSV IPC、process、sysctl() 與 mknod() 外還做了 哪些資源上的限制。 ** 當然你可以說 -u 的參數可以讓 BIND8 放棄 root 權限,但很難說未來會不會出現 哪種攻擊是用來突破這種限制的。 當然,對於某些有強烈偏執傾向的人而言,將 jail 建立在一般檔案系統中還是「太危 險」了一些,此時不妨考慮以 vn(4) 建立一個隔離開的檔案系統,但這已經超出本文 討論的範圍,請有興趣的人自己試試。 參考資料 * Poul-Henning Kamp, Robert Watson, jail(2), FreeBSD 4.2 manual page * Poul-Henning Kamp, jail(, FreeBSD 4.2 manual page