现在的位置: 首页 > 综合 > 正文

Perl CGI编程安全点滴

2013年10月14日 ⁄ 综合 ⁄ 共 5837字 ⁄ 字号 评论关闭

修正时间 2006-06-04

---------------------
1、“有毒”的NULL字符
---------------------

  如果我说:"root"=="root",相信没有什么人反对。但同时我也这样说:
"root"!="root"!还有多少人会认为我是个“正常人”?:)
  但在各种不同的编程语言中,确实存在着这种情况。
  对于每一个希望发现CGI漏洞安全专家或黑客来说,最常用的方法之一是
通过传递特殊字符(串),绕过CGI限制以执行系统级调用或程序。如果你仔细
留意的话,或许也会发现NULL字符确实有它的“妙用”。:)
  阅读以下例子:

# parse $user_input
$database="$user_input.db";
open(FILE "<$database");

这个例子用于打开客户端指定的数据库文件。例如客户端输入"backend",则系
统将打开"backend.db"文件考只读方式)。(注:在这里我们暂且不讨论"../"
的安全问题。)这种处理方式在互联网中是很常见的。
  现在,让我们在客户端输入"backend%00",在该PERL程序中$database=
"backend.db",然后调用open函数打开该文件。但结果是什么呢?系统会打
开"backend"文件(,如果该文件存在)!
  出现这种情况的原因是由于PERL允许在字符串变量中使用NULL空字符,而
在C语言中字符串则不允许包含空字符。因此,也就有了"root"!="root"(在
PERL中)和"root"="root"(在C语言中)。由于系统内核/调用等都是使用C
语言编写,因此当PERL将"backend.db"字符串传递到(C语言的)链接库/程序
时,空字符以后的字符将被忽略?(或许还有利用价值?我还没发现。:))
  这种编程缺陷的影响可大可小。试想一下,如果利用以上编程原理编写一个
给系统其他管理员修改除了root外的其他用户口令的PERL程序:

$user=$ARGV[1] # user the jr admin wants to change
if ($user ne "root"){
# do whatever needs to be done for this user }

那么,聪明的你应该知道如何绕过这个限制修改root用户口令了吧?对了,只要
使 $user="root",则PERL会执行上面程序中花括号内的语句。除非所有处理
过程均使用PERL,否则一旦该变量传递给系统,则会造成安全问题。如修改root
用户口令等。
  也许你认为很难遇到这种会造成严重安全问题的情况,那么我们能否将它作
为一种寻找网站源程序漏洞的间接手段呢?;-)
  不知你有没有经常遇到这种类型的CGI程序,该程序用于打开客户端(提交
的表单中)要求的页面?如:

page.cgi?page=1

然后网站是否返回页面"1.html"呢?;-) 好,现在将其改为:

page.cgi?page=page.cgi%00 (%00 == '' escaped)

这样,我们就可以得到我们感兴趣的文件内容了!这种方法连PERL的"-e"参数也
可绕过:

$file="/etc/passwd.txt.whatever.we.want";
die("hahaha! Caught you!) if($file eq "/etc/passwd");
if (-e $file){
open (FILE, ">$file");}

绕过这段程序的后果你应该想像得到吧?:)
  解决方法?最简单地,过滤NULL空字符。在PERL程序中,

$insecure_data=~s///g;

------------------------
2、漏网之鱼--反斜杠()
------------------------

  对于每一个关心CGI安全的人,也许都看过 W3C 的 WWW Security FAQ 中关
于CGI安全编程一节。其中列出了建议过滤的字符:

&;`'" *?~<>^()[]{}$nr

但我在很多时候发现反斜杠()往往被遗忘了。以下是正确的过滤表达式:

s/([&;`'/ "*?~<>^()[]{}$nr])//$1/g;

但在很多商业的CGI程序中反斜杠却没有被包含进去,这可能是程序员们写程序
时被这些过滤用的匹配表达式搞迷糊了?
  那么,没有过滤反斜杠会造成安全问题吗?试想一下,如果向你的程序中发
送如下一行内容:

user data `rm -rf /`

大多数情况下,程序员编写的程序会将以上内容过滤为:

user data `rm -rf /`

从而保护了系统。但如果PERL程序中忘记过滤了反斜杠,当客户端向该程序提交
如下内容时:

user data `rm -rf / `

经过匹配表达式后为:

user data /`rm -rf / /`

怎么样,看出危险了吗?由于两个反斜杠经系统解释后为一个字符"",但`字符
却因此没有被过滤掉,`rm -rf / `将被系统执行!不过,由于其中还含有一个
反斜杠字符,执行时系统会出错。你自己想办法绕过这个限制吧?;-)
  利用反斜杠的另一个应用--绕过系统目录进入限制。请看以下表达式:

s/..//g;

这个匹配表达式的作用非常简单,就是过滤字符串中的".."。当输入为:

/usr/tmp/../../etc/passwd

将被过滤为:

/usr/tmp///etc/passwd

这样,你将无法访问/etc/passwd文件。(注:*nix系统允许///,试一下'ls -l
/etc////passwd'命令就知道了。)
  现在,让我们的“好伙伴”反斜杠来帮忙。将输入改为:

/usr/tmp/../../etc/passwd

则由于反斜杠的存在而不符合过滤表达式。当PERL中存在如下程序段时,

$file="/usr/tmp/././././etc/passwd";
$file=s/..//g;
system("ls -l $file");

当运行到执行系统调用时,执行的命令会是"ls -l /usr/tmp/../../etc/
passwd"。想知道会得到什么输出吗?自己在机器上试试吧。;-)
  然而,以上方法只适用于系统调用或``命令中。无法绕过PERL中的'-e'命令
和open函数(非管道)。如下程序:

$file="/usr/tmp/././././etc/passwd";
open(FILE, "<$file") or die("No such file");

执行时将显示"No such file"并退出。我还没有找出绕过这个限制的方法。:(

  解决方法:只要别忘了过滤反斜杠字符(),就已足够了。

--------------------------------
3、畅通无阻的“管道”--字符" "
--------------------------------

  在PERL的open函数中,如果在文件名后加上" ",则PERL将会执行这个文件,
而不是打开它。即:

open(FILE, "/bin/ls")

将打开并得到/bin/ls的二进制代码,但

open(FILE, "/bin/ls ")

将执行/bin/ls命令!
  以下过滤表达式

s/( )//$1/g

可以限制这个方法。PERL会提示"unexpected end of file"。如果你找到绕过这
个限制的方法,请告诉我。:-)

综合应用

  现在让我们综合以上几种编程安全漏洞加以利用。先举个例子,$FORM是客
户端需要提交给CGI程序的变量。而在CGI程序中有如下语句:

open(FILE, "$FORM")

那我们可以将"ls "传递给$FORM变量来获得当前目录列表。现在让我们考虑如下
程序段:

$filename="/safe/dir/to/read/$FORM"
open(FILE, $filename)

如何再执行"ls"命令呢?只要能使$FORM="../../../../bin/ls "即可。如果系
统对目录操作加入了".."过滤,则可利用反斜杠的漏洞绕过它。
  在这段程序中,我们还可以在命令中加入参数。如"touch /backend ",将
建立/backend文件。(但我不会使用这个文件名,因为它是我的名字。:-))
  现在,让我们在程序段中加入更多的安全限制:

$filename="safe/dir/to/read/$FORM"
if(!(-e $filename)) die("I don't think so!")
open(FILE, $filename)

这样我们还需要绕过"-e"的限制。由于我们在$FORM变量中使用了" "字符,当
"-e"运算符检查"ls "文件时,因为不存在此文件而退出程序。如何当"-e"检查
时去掉管道符,而调用open函数时又含有管道符呢?回忆一下在前面谈到的NULL
字符的利用,我们就知道应该如何做了。只要使$FORM="ls "(注:在客户端
提交的表单中为"ls%00 ")即可。其中的原理复习一下前面提到的内容就会明白
了。
  需要说明的是,以上程序段中,我们无法象再上一段程序那样执行带参数的
命令,这是因为"-e"运算符的限制所致。举例如下:

$filename="/bin/ls /etc "
open(FILE, $filename)

将显示/etc目录下文件列表。

$filename="/bin/ls /etc "
if(!(-e $filename)) exit;
open(FILE, $filename)

将导致因不存在文件而退出。

$filename="/bin/ls /etc "
if(!(-e $filename)) exit;
open(FILE, $filename)

将只显示当前目录下文件列表。 

4 使用他人CGI脚本时的注意事项

关于CGI,可以从很多地方获得信息——从Internet上,从学校图书馆中,从像本书这样的书中,UseNet组中以及朋友和同事中。从这些地方不仅可以获得信息,还可以得到实际的程序和库。有些程序和库如果已经有人做过了为什么自己还要从头再做一遍呢?但就像不能盲目听从别人的意见一样,关于如何理财,如何驾车或者生活中的别的方面,同样,也不能在自己的服务器上盲目地运行另从的代码。从Net上得到的脚本也可能真正是很好的脚本。但也许并不是。花些时间考察一下脚本的来源以及获取它的站点的可靠性是值得的。

4.1 追根求源

某些Web拥有者。如果不能看到并研究源代码的话,他们甚至都不会运行一个公共的、免费的或商业性的脚本。这可能有点偏激。如果某个声誉很好的公司销售一个文档详细且广为使用的脚本,该脚本应该比自己写的脚本更安全一些。原因有二。首先,专业人才知道并能避免一些常见的安全漏洞;其次,公司是为了嫌钱而做生意,如果他们以次充好或销售那些恶意的产品就不能再做生意赚钱了。

从另一方面来看,如果UseNet组中看到一个编译好的可执行文件出自一个从没听说过的人,没有什么文档可以看,也没有该程序的用户可以交流交流,那么在将它放入自己的服务器之前一定要仔细考虑。也有可能这是来自一个像自己一样的另一个CGI编程者的完全合法的贡献,目的是想让全世界共享他的编程成果。但它也可能来自某个恶意的,具有变态幽默感的,只想看到自己能使多少人清盘的人。

在评价公共的免费软件或商业性软件时,应考虑下面这些方面:

该脚本来自一个声誉好的站点吗?该站点存在很长一段时间了吗?它维护得好吗?Web拥有者在发布文件前进行检查吗?

有没有足够的文档说明该程序如何工作以及用户如何使用等信息?

有多少人已经下载了该脚本?该站点愿意提供顾客名单吗?(仅在有疑问时才去询问;Web拥有者不会整天去回答这类问题。)

有人在UseNet上讨论该脚本吗?如果有,他们说好还是不好?如果没人提到该脚本可以进一步请求别人的见解。一般总会有人响应的。

提示

在评价脚本时检查下面这些useNet组: comp.security.announce,comp.securiy.unix,以及comp.infosystem.www.authority.cgi。另外还可以访问位于ftp.cert.org的Computer

Emergency Response Team,以了解安全问题的历史及有关工作以及安全保护的软件。

5)该脚本的作者有没有一些别的好名声的脚本?

6)源代码能得到吗?免费的或有价的都行。

7)该程序是不是过份宣传它的能力?如果是,这可能是一个编程新手。

8)该站点自己运行了该脚本吗?如果没有,为什么?能找到别的站点运行该脚本吗?过分偏激以及时间限制

尽管游览取自Web的所有代码是个好主意,但要花费很多时间,特别是当代码比较复杂时更是如此。

例如,NCSA HTTPd就太大了,一般用户不可能一行行去读,但是从它的主站点http://www.ncsa.uiuc.edu下载它却能保证极好的完整性,满足任何用户的需要。实际上,任何从NCSA下载的东西都是有保障的。

实际上,Web上的许多著名的站点已经为用户做了大部分的几乎偏激的代码检查工作。从它闪中下载代码是可能利用的另一层另一层保护。这些站点包括:

ftp://ftp.ncsa.uiuc.edu/Web/httpd/Unix/ncsa_httpd/cgi(the NCSA Archive)
http://www.novia.net/~geewhiz(Virtual Webwerx Division Zero-CGI Land)
http://www.lpage.com/cgi(the World-Famous Guestbook Server)
http://sweetbay.will.uiuc.edu/cgi++(cgi++)
http://www.aee.com/wdw(the Web Developers Warehouse)

4.2 注意礼貌

最后,如果确实希望从Web上下载一些CGI代码,或者完整地使用它,或者用作自己编写的更大程序的一部分,还应了解一些事情。

代码是兔费的并不意味着可以自由地用它作自己想做的任何事情。通常程序和库是禁止拷贝的,如果原始作者没有放弃这个权力,他即能限制如何使用该程序。例如,作者可能禁止拆散该脚本,及禁止用作别的脚本的一部分。

一跟来说,在使用别人的代码之前(即使已经确定它是安全的),最好与作者进行联系取得许可。至少这样做比较有礼貌。而大部分情况下,作者会很高兴他的代码能被别人利用。当然,如果在自己程序某个片段处注明原始作者将是很礼貌的。

抱歉!评论已关闭.