当前位置:七道奇文章资讯安全技术网络技术
日期:2010-08-25 01:33:00  来源:本站整理

Perl脚本安全问题研究[网络技术]

赞助商链接



  本文“Perl脚本安全问题研究[网络技术]”是由七道奇为您精心收集,来源于网络转载,文章版权归文章作者所有,本站不对其观点以及内容做任何评价,请读者自行判断,以下是其具体内容:

本篇文章源自《黑客防线》2007年10月刊
转载请注明版权
===============================
        程序语言正常情形下并不构成安全风险,风险是伴随着程序开辟者而产生的.几近每一种语言都有这样那样的瑕疵,而这些瑕疵常常招致不少安全性不足的软件呈现.但是整个软件的安全性在很大程度上还是取决于作者的知识、对程序的理解以及安全意识.在安全方面,Perl有它自己的让人意料不到的特点,可大大都Perl程序员却没有意识到它的存在.
        下面,我们将从Perl脚本的遍及特点及其误用问题着眼,看看这些不当的利用将会带来何种的系统安全隐患,包含那些缺陷程序的广大用户正在利用的系统.同时,我们将会介绍若何操纵这些缺陷以及若何避免和修补这些漏洞.
        基本的用户输入漏洞
        Perl脚本安全问题之一就是对用户的输入过滤不严(未经考证的用户input).很多时刻,你的程序会直接大概间接地承受一些来自未被信任的用户的输入,在这个问题上,程序员更应当谨严.比方,你正在用Perl脚本编写一个CGI程序,那你就应当要考虑到会有恶意的用户给你的CGI程序发送假造的输入.
        假如用户输入未经过考证便予以信任和利用,那么输入会招致该程序呈现各种各样的错误.最明显的一点就是程序履行的时刻才知道我们履行的是别的用户供应的程序,而这种程序没有经过任何查抄和考证.
        system()和exec()函数
        Perl以复合语言著称,实际表现为可以很好地调用其他程序来为它协同工作,通过它们的输出来调和运行情形,通过一种分外的方法把它们作为另一种情势的输入,传送给别的程序,从而确保每个环节可以顺利运行.正如Perl所奉告我们的,实现的办法不止一个.
        履行外部程序大概履行一个系统号令我们只需求调用exec()函数便可.当Perl碰到exec()表达式的时刻,便去探求函数调用的参数,然后成立一个新的历程来履行指定的号令.Perl不会把掌握权交还给原先调用exec()函数的程序,而是直接进入函数的历程.
        别的还有一个近似的函数是system()函数.system()和exe()历程非常类似,它们之间唯一的辨别就是,perl首先从初处理历程中别离出一个子程序来.这个子程序就是被送入系统的参数,然后初处理等候直至子程序运行,再履行剩余的其他程序.下面我们来进一步谈论system()函数的调用,但绝大部份的结论一样可以利用到exe()函数中.
        被输入到系统的参数是一个列表情势,列表的第一项是被调用履行的程序名称,而剩下的几项将作为参数被传送给该程序.固然如此,假如只有一个参数的话,system()会非常运行.在这种情形下,Perl会扫描这个参数能否包含Shell元字符,假若有的话,它会把这些字符通过Shell来注释.因此Perl便产生了大量的内部号令来完成工作,不然Perl将拆分这些字符并调用更有效的C程序库,但是这些库也不能很好地辨认这些内部号令字符.
        目前假定我们有一个CGI表单需求用户名来列出一些文件(包含用户的统计数据表),我们便可以通过system()来调用CAT来实现我们的目的,代码以下:

system ("cat /usr/stats/$username")

而用户名$username则来自下面这个表单项里:

$username = param ("username");

用户填写表单用户名,比方jdimov,然后提交它.Perl却找不到任何元字符在"cat /usr/stats/jdimov"的字符串中,所以Perl就调用execvp()来运行"cat",再返回到我们的脚本程序里来.这段脚本看上去没什么,其实是可以被心胸叵测的攻击者操纵的.问题在于攻击者可以在表单里面利用特别字符来实现肆意Shell号令的履行.我们假定攻击者在用户名的地方输入以下字符串:"jdimov; cat /etc/passwd"字符串,Perl把分号作为元字符发送到内部号令Shell中,像下面这样:

cat /usr/stats/jdimov; cat /etc/passwd

入侵者将同时得到虚假的状况文件和密码文件,假如是对比有破坏性的,便大概仅仅发送"; rm rf /*".
        开始我们提到system()函数包含一个参数的列表,并且以号令的情势来履行列表的第一个元素,其他的元素作为参数来传送.所以,我们略微窜改脚本,便可以得到我们想要履行的号令后果.
        
system ("cat", "/usr/stats/$username");

即便我们单独指定每个参数到程序中,Shell也不会被调用.发送";rm -rf /*"将不起任何作用,因为这个攻击性的字符串将仅仅作为一个文件名而被禁止.
        这种办法比单参数的办法好得多,固然它避免利用Shell,却仍旧有潜在的缺陷.分外地,我们需求关心的是用户名的字段值可否被操纵来攻击现行程序的弱点(以CAT为例),比方入侵者仍旧可以操纵我们重新写的编码版本去显示系统密码文件,那就是通过设置$username成字符串:"../../etc/passwd".运行这个程序,会有很多地方出错.比方一些利用程序采取特别的字符次序作为履行Shell号令的恳求,一个常见的问题就是一些UNIX MAIL工具的版本,当碰到"~"Esc掌握序列时(在分外匹配的上下文中)会履行Shell号令,这样在特定的环境下,用户输入包含"~!rm -rf *"的字符将大概惹起错误.
        
        open()函数
        open()函数的功效是翻开文件夹,用得最多的也是广泛的格局为"open (FILEHANDLE, "filename");".在这个利用中,filename是以只读的方法翻开,假如filename为">"前缀方法,那翻开时就是输出.假如文件已经存在,则覆盖该文件,假如是字符">>",它将采纳追加的情势翻开.前缀"<"以输入的方法翻开文件,假如没有前缀的时刻,就是默许情势.未考证用户输入的问题应当已经显而易见,就像背面将要介绍的目录摆列本领在这里一样有效.
        隐患仍然存在,我们用open()来替换CAT,比方"open (STATFILE, "/usr/stats/$username");".我们要读取某个文件里面的代码并显示它.Perl文档奉告我们,假如文件名以"|"开首,则对该文件名的注释就变成了重定向输出的管道号令.假如文件名以"|"末尾 ,该文件名的注释就是作为管道输出.
        用户在/usr/stats目录下将可以履行任何号令,仅仅是通过改正一个"|".若向后遍历目录,则可以让用户履行肆意程序.
        要办理这个问题,一个办法是老是具体指出需求翻开的文件并且加上"<"的标志作为前缀,比方"open (STATFILE, "</usr/stats/$username");".
        有些时刻,我们确切需求调用一些程序.比方,我们要改变脚本让它可以读取纯文本文件/usr/stas/username,然后把它传送到一个html过滤,然后显示给用户.比方,我们有一个简便实用工具作此用处.实现的办法是这样的:
        
open(HTML,"/usr/bin/txt2html/usr/stats/$username|")
print while <HTML>;

不幸的是,这仍旧是通过内部号令实现的.不过,我们可以利用复合情势的open()函数调用,这样就可以避免产生内部号令.

open (HTML, "-|") 大概
exec("/usr/bin/txt2html", "/usr/stats/$username");
print while <HTML>;

当我们为了读取("-|")大概写入("-|")而翻开一个以"-"开始的管道,Perl会先从当行进程里面分出一个子进程,并返回一个子程序的PID给父进程,而返回0给子进程."or"表达式决意我们是在父程序还是子程序中.假如我们处于父进程中(open()的返回值非零),则我们持续print()语句.假如我们处于子进程,则通过利用安全的exec()函数来履行txt2html程序,可以避免传参的时刻通过Shell.这模样程序打印输出txt2html的后果指向stdout,然后终止(记着exec()没有返回值),同时父进程从stdin读取后果.这样类似的同一技术可用于管道输出到外部程序:
open (PROGRAM, "|-")
or exec ("/usr/bin/progname", "$userinput");
print PROGRAM, "This is piped to /usr/bin/progname";

在需求的时刻,这些情势的open()更始终偏向于直接纳道的open(),因为它们不经内部号令.
目前假定我们转换统计表文件的数据经过格局化的html网页,为便利起见,决意把它们存放在与用于显示它们的脚本文件同一目录中.那么我们的open()的表达式将是这样的:
open (STATFILE, "<$username.html");
当用户从表单输入username= jdimov,脚本就会显示jdimov.html,这里就有一个攻击大概性.不像C和C++语言,Perl不利用null终止字符串.因此字符串"jdimov\0blah"在C程序库里注释为"jdimov",但在Perl里面仍旧是"jdimov\0blah".Perl传送含有一个null的字符串到一些C语言编写的程序时,问题就会呈现.Unix内核和大大都Unix内部号令都是纯C语言,而perl本身主要就是C语言编写的.当用户这样调用脚本:"statscript.pl?username=jdimov\%00"时,会发生什么呢?我们的脚本传送字符串"jdimov\%00.html"到呼应的系统调用历程顶用以翻开,但由于这些系统调用是由C编码的,并且等候null终结的字符串部份——".html".后果若何?脚本只会显示文件"jdimov"(假如存在的话).它大概不存在,即便存在,也没有什么很大的作用.但是,假如我们这样来构造脚本:"statscript.pl?username=statscript.pl%00",若脚本文件和html文件位于同一目录的话,我们便可以拐骗这个脚本程序来显示它的源代码.在这种情形下,固然它关于程序本身意义不大,但是这个办法假使利用到别的脚本,便可以帮忙攻击者解析全部Perl代码,找到别的大概存在的操纵漏洞.
        
        反引号
        在Perl中,还有另一种方法来读取一个外部程序的输出,那就是把号令封装入backticks.因此,假如我们想要存储统计文件数据的内容到标量$stats里面,我们可以这样:"$stats = `cat /usr/stats/$username`;",这样确切要经过Shell,任何触及到用户输入backtick的脚本都存在着我们上文已经介绍过的各种安全隐患.固然目前有几种差别的办法可以令Shell不注释元字符,但是最安全的办法就是不利用backticks,而翻开通往STDIN的管道,然后像我们在先前部份的末尾一样,用open()别离和履行外部程序.
        
        eval()函数和/e regex正则表达式修饰符
        eval()函数在运行时可以履行整块Perl的代码并返回最后一个表达式的值.这种功效常用于配置文件,这些文件用Perl代码来编写.除非你绝对信任传送给eval()参数的根源,不然不要写近似"eval $userinput"的程序.这也实用于"/e"正则表达式修饰符,来使Perl先诠释表达,然后再处理.
        
        用户输入的过滤
        我们目前要谈论的是过滤一切不必要的输入和可疑数据,这是个很简单的办法,却能办理最多的问题.举例来说,我们可以过滤掉全部时段,以避免向后遍历目录.一样,每当我们看到无效字符则可以舍弃之.
        这就是"黑名单"战略.也就是说,一个东西假如不是明确禁止的,那它就是公道的,但是"白名单"更好些,就是说,东西假如没有明确指出是答应的,那么它就是禁止的.
        黑名单战略的问题在于难以保持完好和更新.你大概忘掉过滤出某种字符,大概你的程序大概要切换到差别的内部号令环境下,而这种环境用的是一套完好差别的元字符.
        差别于过滤不必要的元字符和其他危险输入,白名单战略只需求肯定哪些输入是合理的.下面片断中,比方假如用户输入包含任何除字母、数字、圆点或@以外的标记时,将终止履行一个关键安全操作.
unless ($useraddress =~ /^([-\@\w.]+)$/) {
print "Security error.\n";
exit (1);
}
基本思绪不是尝试编辑一份列表式的特别原则去防备,而是要拿出一个可以安全承受的法则列表.挑选承受的输入原则自然是每一种程序会有所差别.可承受的原则应挑选以尽大概削减其丧失潜在大概性这样的一种方法.

        避免内部号令:shell
        当然,你也应当尽大概避免内部号令.但是,这一技术有更遍及的实用性.假如调用此中有特别序列编辑器,你必须确保这些序列是不答应的.
        普通,我们可以操纵现存的Perl模块来避免利用外部程序来履行一个函数.综合Perl存档网络(CPAN)是一个宏大的功效测试模块资源库,几近任何一个尺度的UNIX工具都可以利用到它.和调用一个外部程序相比,包含并调用一个模块大概会多花点工夫,但是大致上看,模块化的办法更具有安全性和机动性.为了阐明这一点,我们操纵"NET::smtp"替换exec()函数便可以办理必须经过内部指令的问题,可以避免你的用户在endmail的代理设置里操纵已知的漏洞.
        
        其他根源的安全问题
        1)不安全的环境变量
        用户输入的确是Perl程序安全问题的主要根源,但在编写Perl的安全代码时也有其他需求考虑的影响因素.一个广泛的缺陷就是,脚本Shell环境下或网络服务器环境下的不安全环境变量,最常见的就是途径变量.当你从指定的一个相对途径的代码进入一个外部程序时,你赌上了整个利用程序和系统的安全.假定你有一个system()调用是这样的:"system ("txt2html", "/usr/stats/jdimov");",关于这个运行的调用,你假定txt2html文件是存放在这样的一个目录,这个目录包含途径变量的某个位置.攻击者会改变你的途径指向其他一些拥有一样文件名的恶意程序,这样你的系统的安全便没有了保障.为了避免这样的情形发生,每个需求加以远程安全器重的程序都应当按以下的方法举行:
        
#!/usr/bin/perl -wT
require 5.001;
use strict;
$ENV{PATH} = join ':' => split (" ", << '__EOPATH__');
/usr/bin
/bin
/maybe/something/else
__EOPATH__

假如程序依靠于其他环境变量,也应在利用之前重新明肯定义.
        另一个危险变量(这是一个更具体的Perl)是一个@INC数组变量.除了它分外指明Perl应当在那边探求并在程序里面包含模块以外,这个变量很像PATH变量.@INC问题和path途径问题基本上近似,它大概指向具有相同名称以及和你盼望的一样的Perl的一个模块,但它也大概在后台做一些诡计性的活动.因此,@INC和途径变量一样一样不该该信任,应当在包含任何外部模块之前完好重新定义.
        2)setuid脚本
        正常情形下,Perl程序担当了履行它的有效用户的权限.通过制作一个setuid脚本,它的有效用户ID 可设置成一个拥有"拜候实际利用者所不能获得的资源"权限的ID(即以主人身份的文件包含程序).比方密码程序通过利用setuid脚本来获得写入答应,对系统密码文件passwd举行操作,从而答应用户改正自己的密码.因为通过CGI接口履行的程序是按照Web服务器用户的特权来运行的(普通是用户"nobody",拥有非常有限的权限),CGI程序员常常不由得用setuid技术让脚本实现其他程序做不到的功效.这的确很有效,但它也是很危险的.其一,假如一个攻击者在该脚本里面找到一个可以操纵的弱点,他们不但将得到系统拜候权,同时也将得到有效的脚本uid权限(普通是ROOT的uid).
        为了避免这种情形,在任何文件操作前,Perl程序应给真正进程的uid和gid设置有效的uid和gid.

\begin{verbatim}
$> = $< # set effective user ID to real UID.
$) = $( # set effective group ID to real GID

而CGI脚本应当老是赋予最低的权限.
        要注意的是,你的setuid脚本并不老是办理问题.部份操作系统有内核缺陷,使setuid脚本更不安全.为此这样那样的缘由,当Perl运行setuid或setgid脚本的时刻,它会自动切换到一个特别的安全情势(外部指令情势).
        3)rand()函数
        rand()函数随机生成的数在特定的机械上是一个不普通的问题.在关键安全的利用上,随机数字用于很多重要任务,如密码生成大概加密学.出于这个需求,尽大概生成接近随机数至关重要,这会让攻击者非常难以(但毫不是不大概的)猜测我们将要生成的数字.Perl的rand()函数简单地从尺度C语言库调用呼应rand(3)函数,这样的程序是很不坚固的.该C语言库的rand()函数生成一个伪随机序列,这些序列是基于一些称为种子的初始值而得来的.给定同一种子,同一程序的两个差别的实例程式将操纵rand()函数产生相同的随机值.在很多C履行中,以及在全部5.004之前的Perl版本中,假如种子没有明确指明,它将按照当前计算机系统的当前时间值来举行预算,而系统时钟则是随机的.假定给定一个特定的点和充足的时间,通过获得随机函数生成值的相关信息,任何攻击者很肆意地便可获得下一个将由随机函数产生的数字序列,从而获得威胁到系统安全的相关信息.办理这一随机函数问题的办法之一,就是利用一个基于Linux系统内置的随机数发生器——/dev/random和/dev/urandom.比起尺度的随机函数库,这些都是相对对比好的随机根源.但像其他的事物一样,它们也有自己的缺陷.二者的辨别是设备/dev/random在随机池耗尽时而不再生成随机数字,而/dev/random在随机池耗尽时操纵加密的办法产生新的随机数字.另一种办理办法是利用像Yarrow这种较为复杂的加密随机数发生器来产生随机数字.
        
        竞争条件(race conditions)
        比赛条件(以及缓冲区溢出)是经验丰富的入侵者的最爱.看一下以下代码:
        
unless (-e "/tmp/a_temporary_file") {
open (FH, ">/tmp/a_temporary_file");
}
        乍一看,这是一个非常公道的代码,仿佛没有造成任何危害.我们先查抄能否有暂时文件存在,假如不存在,我们就奉告Perl去成立和翻开它并写入.这里的问题是,我们假定我们的查抄在翻开文件时是精确的.当然,Perl在一个文件的存在问题上不会对我们扯谎,但在这看似不大概的情形下,文件完好有大概在这么一个时间区间内(从查抄完毕到文件被翻开这段时间内)发生改变.又比方说,一个熟习我们程序工作原理的攻击者,恰好在我们方才查抄完暂时文件的存在性之后,履行了以下的号令:"ln -s /tmp/a_temporary_file /etc/an_important_config_file".目前我们对暂时文件所做的一切操作全部被转移到了我们重要的配置文件身上了(即移花接木——译者注).因为我们认为暂时文件不存在(这就是我们的查抄后果奉告我们的),所以我们直接翻开它并对其举行写操作.后果,config文件就这样被自己抹掉了.更不幸的是,假如攻击者知道他们所做的,这乃至大概招致毁灭性的后果.
        在这种情形下,攻击者可以在程序的两个行动之间竞争和改变一些东西,从而给我们带来麻烦,我们称之为竞争条件.在这种特别的情形下,我们会有一个TOCTOU (Time-Of-Check-Time-Of-Use)的竞争条件.此外,还有几种近似的竞争条件的范例.这种程序的缺陷常常简单被富有经验的程序员所忽视,却被攻击者频繁操纵.目前还没有简单有效的办法来办理这类问题,当竞争条件大概存在的时刻,最好的一个办法是利用原子操作.这种办法仅用一个系统调用来同时实现文件的查抄和成立,这期间不给处理器任何机会去切换到另一个进程,可这并不老是可行的.在我们的例子里,还有另一件事我们可以做,就是利用sysopen()函数并指定一个只写方法,而不设定任何截断标志.
        
unless (-e "/tmp/a_temporary_file") {
#open (FH, ">/tmp/a_temporary_file");
sysopen (FH, "/tmp/a_temporary_file", O_WRONLY);  
}
        
        这样,即便我们的文件被假造了,在我们开始写入的时刻也不会删除文件.
        注:为了使sysopen()函数调用能工作,fcntl模块必须被包含,因为fcntl模块是o_rdonly、o_wronly、o_creat等常量被定义的地方.
        
        缓冲区溢出与Perl
        普通来说,Perl对缓冲区溢出并不敏感,因为在必要时Perl可以动态扩大它的数据构造.Perl保持跟踪字符大小和长度,然后分配给每个字符串.在每次要存储一个字符串之前,Perl都确保系统有充足的空间可供利用,必要的时刻会为其分配更多的存储空间.但也有在一些老Perl版本中存在的少数已知的缓冲区溢出情形.分外是在5.003版本,缓冲区溢出是可以被操纵的.全部的suidperl版本(一类针对一些内核而用setuid脚本编写的程序,环绕竞争条件而计划的)的成立都是早于5.004 版的(CERT Advisory CA--97.17).
        
        结论
        假如时间答应的话,在后续的文章中,我们将花一些时间深化探究Perl所供应的安全功效,特别是Perl的外部号令情势;然后我们将弄清楚一些问题,假如我们不加以注意的话,这些问题大概成为安全问题的漏网之鱼.在研究Perl的各个方面以及看一些典型例子的历程中,我们的目标是成立一种可以帮忙我们一眼熟习到Perl脚本安全问题的直觉,并避免在我们的程序中犯下近似的错误.


  以上是“Perl脚本安全问题研究[网络技术]”的内容,如果你对以上该文章内容感兴趣,你可以看看七道奇为您推荐以下文章:
  • linux下perl操作mysql数据库(需求安装DBI)
  • 实战Nginx与Perl、Java的安装与配置
  • <b>perl版NC(Netcat)源码</b>
  • linux下反弹shell脚本 C版和Perl版 webshell反弹回号令行
  • 配置Nginx 运行CGI(Perl-cgi)
  • Linux下安装PHP,APACHE,MYSQL,PERL的办法
  • 用perl拜候mysql数据库
  • 用Eclipse构建Perl操纵程序
  • 若何用Eclipse调试Perl操纵程序
  • MySQL 事件预编译查询和Perl DBI简化
  • <b>利用Perl语言去存取mSQL和MySQL数据库的内容</b>
  • Perl脚本安全问题研究
  • 本文地址: 与您的QQ/BBS好友分享!
    • 好的评价 如果您觉得此文章好,就请您
        0%(0)
    • 差的评价 如果您觉得此文章差,就请您
        0%(0)

    文章评论评论内容只代表网友观点,与本站立场无关!

       评论摘要(共 0 条,得分 0 分,平均 0 分) 查看完整评论
    Copyright © 2020-2022 www.xiamiku.com. All Rights Reserved .