next up previous
Next: 我要如何得知有哪些 process 開了某一檔案,或者某一 process 正在使用哪一個 fileystem(以至於我無法 unmount 這個 filesystem)? Up: 自以為已經知道所有答案的人可能會問的高級問題 Previous: 在 C 程式中要怎麼用 sleep() 才能夠 sleep 小於一秒?

如何讓 setuid 的 shell script 可以使用?

[ 這個問題的回答很長,但是這是一個複雜又常問的問題。在此要謝謝 Maarten Litmaath 所提供的答案和以下所提到的 "indir" 程式。]

先假設你所用的 UNIX 是能認得「可執行的 shell script」的變異過的 UNIX (如 4.3BSD 或 SunOS)。這類 script 的第一行一定是如以下一般:

		#!/bin/sh
這樣的 script 就是所謂可執行的 script,因為它和一般可執行的 binary 檔 一樣有 magic number 做開頭。在我們所用的例子中, magic number 為 '#!', OS 會把這行接下來的部份當作這整個 script 的解譯程式,其後可能還 會有一些 option 如:
		#!/bin/sed -f
假設這個 script 的名字叫做 'foo',並且放在 /bin 下,那麼如果你用:
		foo arg1 arg2 arg3
那麼 OS 實際在執行時會把它看成是:
		/bin/sed -f foo arg1 arg2 arg3
有一點不同的是:如果 'foo' 設定成 setuid,那麼 OS 會把它以第一種格 式來解釋;如果你硬是以第二種格式輸入,那麼 OS 會以 /bin/sed 的 permission 為準,而它當然不會是 setuid。

-----

好吧,那如果我的 shell script 並不是以 '#!' 做開頭, 或是我的 OS 根本就不認得它呢?

嗯,如果這個 shell(或是其他的解譯程式)試著要去執行它,那麼 OS 會傳回 一個錯誤訊息,表示這個檔案不是以合法的 magic number 做開頭。收到這個錯 誤訊息後, shell 會把這個檔案認定成是 shell script,並以另一種方式來執行

		/bin/sh shell_script arguments
但是我們在前面已經看到了,在這樣的情形下,設成為 setuid 的  shell_script 並不會發生作用。

-----

那麼,設成 setuid 的 shell script 到底有什麼安全上的問題呢?

嗯,假設這個 script 叫做 '/etc/shell_script',它的開頭是:

		#!/bin/sh
現在我們來看看以下的命令會發生什麼事:
		$ cd /tmp
	        $ ln /etc/setuid_script -i
        	$ PATH=.
	        $ -i
我們可以看出來,以上的最後一個命令會被解釋成:
		#!/bin/sh -i
而這樣的命令會讓我們得到一個可以輸入命令的 shell,並且會被 setuid 成 這個 script 的擁有者。 幸好,這樣的安全漏洞可以很輕易地防止,只需要把第一行改成:
		#!/bin/sh -
'-' 這個符號代表著它是整個 option list 的結尾:所以如果再用前述的方法 的話,'-i' 就會如本來所期望的被解釋成 script 檔案的名字。

-----

然而,還有更嚴重的問題:

		$ cd /tmp
	        $ ln /etc/setuid_script temp
        	$ nice -20 temp &
	        $ mv my_script temp
第三個命令會被解釋成:
		nice -20 /bin/sh - temp
而因為這個命令的優先權被設得很低,那麼第四個命令可能就有機會搶先在  shell 開啟 'temp' 之前就用 'my_script' 把 'temp' 給蓋掉!有四種方法 可以修補這個安全上的漏洞:

1) 讓 OS 用另一個比較安全的方式執行 setuid script。如 System V R4 和  4.4BSD 利用 /dev/fd 來把該 script 的 file descriptor 傳給解譯程式。

2) 透過一個前端程式來間接解譯要執行的 script,以確定在真正的解譯程式 啟動前一切正常。例如,你可以用 comp.sources.unix 中的 'indir' 程 式,那麼你的 script 開頭就會像這樣:

			#!/bin/indir -u
	                #?/bin/sh /etc/setuid_script
3) 造一個 'binary wrapper':一個真正的 setuid 可執行程式,這個程式的 唯一功能就是用來執行 script 中所指定的解譯程式,並以該 script 的檔 名為參數傳給解譯程式。

4) 造一個 'setuid script server' ,並把一些要用到、檢查過的 setuid  script 存放在 database 中。當成功地被呼叫後,會去執行正確的解譯程 式及正確的 script。

-----

現在我們已經能確定所解譯到的 script 是正確的,那麼還有其他的危險嗎?

很抱歉,當然還有!在使用 shell scipt 的時候,你一定不能忘記要把 PATH 這個變數很明確地設到正確的路徑去。你能夠指出這是為什麼嗎?除此之外, 還有 IFS 這個變數如果沒設好也可能會造成麻煩。其他的環境變數也可能會造 成安全上的問題,如 SHELL... 更重要的,你必須要確定在 script 中沒有命 令會讓它產生出可下命令的 shell(interactive shell escape)!還有就是,  umask 可能被設成奇怪的值等等...

除此之外,你應該要知道 setuid script 會「繼承」所有它所用到的命令的  bug 及安全問題。

總而言之,你應該知道 setuid shell script 真的是件非常危險的事吧! 最好還是寫 C 程式啦。


next up previous
Next: 我要如何得知有哪些 process 開了某一檔案,或者某一 process 正在使用哪一個 fileystem(以至於我無法 unmount 這個 filesystem)? Up: 自以為已經知道所有答案的人可能會問的高級問題 Previous: 在 C 程式中要怎麼用 sleep() 才能夠 sleep 小於一秒?
Tan Koan-Sin
1999-03-02