动网论坛,站长建站首选,国内使用量最多的论坛软件 动网论坛官方技术讨论区 站长工具 申请属于您自己的免费论坛
首页 | 新闻资讯 | 网站运营 | 网络编程 | 数据库 | 服务器 | 网页设计 | 图像媒体 | 网络应用 | 搜索优化 | 资源下载 | 动网主机 | DVBOX
    本站内  互联网 ASP论坛  ASP.Net论坛  PHP论坛
  
   Cgi/Perl → 阅读文章

 Perl CGI编程安全点滴

作者来源: 
阅读 1538 人次 , 2006-3-29 4:01:00 

◆perl cgi编程安全点滴(作者:backend)

  cgi在现在的互联网应用越来越广泛,cgi编程的安全问题也得到越来越多的
重视。perl作为cgi编程的主要语言之一,其安全性也受到很大的关注。在 w3c
组织的 "www security faq" 之 "cgi scripts"一章中,perl安全编程就整整占
了一节。由此可见 perl cgi 安全编程的重要性。
   这里我不会重复 "www security faq" 的内容,而是根据一直以来对 perl
免费/商业程序包的源代码的研究,得出一些较易被忽略的编程漏洞,在此将几个
最常见的(主要是变量字符过滤方面)写出来,供广大程序员作为参考。
   如果你对本文的内容有不同意见或任何建议,请告诉我。如果你发现了其它
perl cgi 编程漏洞,也请告诉我。如果你也发表了关于这方面的文章,当然也
请告诉我。:-)

---------------------
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)

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

 本文Tags安全  
 收藏本文  打印本文  论坛讨论  关闭窗口
· 上一篇:CGI 安全问题
· 下一篇:windows下Perl开发环境的安装和配置
· Cgi入门教程之:12 代码详解
· Cgi入门教程之:1 Unix环境
· CGI动态创建图象
· 在windowsnt上实现cgi
· 响应头204的应用


关于本站 | 联系我们 | 业务合作 | 客户案例 | 诚聘英才 | 广告合作 | 收藏本站
海口动网先锋网络科技有限公司版权所有
Copyright © 2000 - 2006 Cndw.Com
中华人民共和国电信与信息服务业务经营许可证编号 琼 ICP 020077