文章会有【原创】或者【转载】标示,部分来自Google Baidu的学习结果 {Java/PHP/Python/Ruby/Go}

如何让setuid的shellscript可以使用?  


 这个问题的回答很长,但是这是一个复杂又常问的问题。在此要谢谢 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 程式啦。