奇趣技术网 收藏本站
设为主页
商务合作
首页 新闻中心 行业动态 软件新闻 安全资讯 病毒预警 漏洞发布 操作系统 Dos Win9x Win2000 WinXP Win2003 WinVista Linux Unix
数据库 DB2 Access MSSQL MySQL Oracle Sybase 编程技术 ASP PHP JSP CGI/Perl XML .Net C/C++/C# VB VC Delphi Java 汇编
安全技术 安全教学 工具介绍 漏洞利用 病毒防范 入侵检测 防火墙 安全防范 汉化破解 攻击实例 加密解密 技术论坛
中华网络安全联盟 >> 程序开发 >> Perl >> Perl的安全性监测
程序开发
Asp
PHP
JSP
CGI/Perl
XML
.Net
C/C++/C#
Visual Basic
Visual C++
Delphi
Java
汇编语言
  • LB5000XP论坛配合SERV

  • 获取随机的由大小写字

  • Perl操作Mysql数据库

  • Perl在NT下的快速简便

  • 用Perl制作页面计数器

  • 用Perl语句来代替常用

  • 用 perl 实现文件上传

  • perl 域名查询程序

  • Perl的安全性监测
    字体:

    中华网络安全联盟    作者:佚名    来源:网络转载    时间:2006-3-21


    本月的专栏将介绍Perl的称为“tainting”的内部安全机制,它可以让Perl捕捉到任何可能导致安全性问题的系统调用。我强烈推荐在你的所有CGI程序中打开“tainting”机制。

    CGI使得互联网上的任何人都可以在你的计算机上运行程序,这就使得CGI成为世界上最流行的安全漏洞。作为程序员,我们的责任是不让坏人侵入我们的系统,对于我们所编制的程序来说,要做到没有漏洞可钻。

    例如,下面这个CGI程序,就是个坏程序:

    #!/usr/bin/perl -w

    # cgi-bad – 一个不好的cgi 脚本的例子

    ...

    $file = param("FILE")

    or die "Must fill out the FILE field\n";

    unlink("/usr/local/public/data/$file")

    or die "Can't delete $file : $!\n";

    该脚本所做的是读出在表单中所输入的文件名,并从目录/usr/local/public/data/中删除该名称的文件。错了!该脚本所做的实际上是让任何用户对在网络服务器上usercode可以删除的任何文件作删除操作。请看:

    % setuid-bad ../../etc/apache/var/userdb

    天哪!那里是用户数据库!

    我们本来要做的是检查程序的参数,以确定其是否为文件名。问题是你的程序外部所产生的数据用到了系统调用上,如nlink(), open(),和system()。而你并不打算让在你的程序之外产生的数据影响到外部世界。

    Perl有个选项,打开后,可以强迫你检查常数,环境,输入,或其它有可能被不怀好意的人利用的漏洞。该选项称为“tainting”

    打开Taint检查选项
    要打开taint检查选项,让Perl带一个 -T 选项:

    #!/usr/bin/perl -wT

    如果我们在上述程序运行时,带有 –T选项,我们会看到如下信息:

    Insecure dependency in unlink while running with

    -T switch at setuid-bad line 5.

    Perl跟踪$file中的值,它是在你的程序外部生成的,(它被称为“tainted”)。 unlink() 被认为是个不安全的操作,因为它对外部世界有影响:文件。在不安全的操作下,企图使用没有信任度的(tainted)数据是危险的。正如我们已经看到的,数据可能有诈。

    这些漏洞可以由Perl的taint检查选项在运行时捕捉到,并且使得程序停下来。

    Tainted数据

    Tainted 数据来源很多,包括:来源于你的环境散列表 (the %ENV) ,参数 (@ARGV),读入的文件和目录,来源于运行的程序中,以及一些系统调用的结果(用getpw读出口令数据库中的GECOS域)。任何对tainted值的操作(添加,合并,插入),其结果值也是tainted。这就好像是数据一旦被粘上了污点,那么无论数据传播到哪里,污点就会被带到哪里。

    仅有三种方式,可以得到“untainted”值:数据直接在程序中指明;数据来自于安全的函数(如localtime);或者使用正则表达式提取来自不安全函数的tainted 串的一部分。

    $a = 4; # untainted

    $file = $ARGV[0]; # tainted

    $file =~ m{^([^/]+)$}

    or die "$file is not a good filename.\n";

    $untainted = $1; # untainted

    通过正则表达式用括号括起来,创建了$1, $2, ... 变量。这些都是untainted数据。通过正则表达式,你可以确信它就是你所期望的值。如果匹配失败,你会得到失败信息。如果匹配成功,$1 ...变量包含了你可以使用的untainted 数据。

    如果我们已经打开tainting 选项,当我们试图做unlink()操作时,Perl 解释器会停下来,告诉你$file 中包含了tainted 数据。文件名是 tainted的,因为它来自于不信任源:使用你的程序的人。

    坏动作
    如果你所使用的数据是tainted的,你想要Perl程序所做的大多数事情会产生出错信息。如果文件名或程序名是tainted的,那么运行程序,打开文件来写入,以及删除文件,这些操作都将被禁止进行。

    这一节将演示如何在这种场合下,解除tainted状态。

    考虑:

    system("ls *.h");

    Perl 在你的串中看到了 *,并决定调用shell,这样:

    sh -c "ls *.h"

    但是,的确有人可能用假的路径环境变量来运行你的程序,从而导致调用了错误的sh或ls。所以,对于PATH变量以及SHELL中可以用来修改其行为的其他变量,应该进行 untaint操作。

    一般,运行其它程序时,你应采取三项步骤:

    明确你的环境变量,使得运行的是实际程序。

    关闭shell

    对程序的参数进行untaint操作。

    用如下的等简单方式清除你的环境变量:

    delete @ENV{"IFS", "CDPATH", "ENV", "BASH_ENV"};

    $ENV{PATH} = "/bin:/usr/bin";

    第一行删除掉可能会引起问题的环境变量,第二行给出一个确保安全的PATH。你可以添加其他的目录到PATH中,但务必确保它们同该处一样,是有确定值的。

    关闭shell也要把握好分寸。Perl 在涉及到有关shell的操作,如 open(), system(), backticks,和exec() 调用时,有自己的规则,这些规则不太容易掌握。最好的规则是:避免使用backticks 和pipe open() 调用,而是使用system() 和exec() ,并传给它们参数表。

    大多数人习惯于看到如下的写法:

    system("someprogram arg1 arg2 arg3");

    他们不知道还可这样写:

    system("someprogram", "arg1", "arg2", "arg3");

    这样的写法,可以精确地告诉Perl的各个参数是什么,Perl将不会调用shell。 exec() 也具有读参数表和不调用shell的特点。而如果要使用piped open() 和backticks,就无法保证不会用到shell。

    如果你打算使用piped open 或 backticks,你得用如下的方法重新实现:

    $pid = open(COMMAND, "-|");

    die "Couldn't fork: $!" unless defined $pid;

    if ($pid) {

    @lines = <COMMAND>;

    close(COMMAND);

    } else {

    exec("some", "program", "with", "args") or die "execing: $!";

    }

    一般来说,即使你的PATH已经作了安全处理,给出所运行的程序的完整路径是个好主意。这就会避免了错误地调用了/usr/bin/boom 而不是/home/user/bin/boom这种情况的发生,因为在PATH中 /usr/bin 位于/home/usr/bin/boom.之前。

     


     

    文件名
    对文件名进行操作时,使用unlink() 或 <*.h> ,或者用open()时,是有危险的。

    从目录中读入的文件名是tainted的。你可以打开一个tainted 文件名来读入,但你不能打开它来写入。从文件中读数据,不管文件名是否 tainted,已经是tainted的。因为用到了shell,你不能用<*.h> 来得到文件清单。

    为了检查文件名是否是好的,你得写出一个正则表达式,并同合法的文件名进行匹配。在一些场合,可以用如下的简单方法来检查你的数据:

    $file = $ARGV[0];

    ($file =~ m{^([^/]+)$} && $file ne "." && $file ne "..")

    or die "Bad filename $file\n";

    $file = $1;

    根据任何不包含斜杠的串的正则表达式来检查文件名,这就把子目录排除在外,然后排除掉“.”(当前目录)和“..”(当前目录的父目录)。如果这些测试都通过了,$1变量中存放的就是我们可以使用的文件名。

    为了得到匹配某种模式的文件名清单,你既可以从CPAN (File::KGlob 和File::BSD 是两个有用的模块)安装有关模块,也可以使用读目录操作和正则表达式:

    opendir(DH, "/path/to/directory") or die "opening directory: $!\n";

    while (defined ($thing = readdir(DH))) {

    next unless /^(.*\.h)$/;

    push(@files, $1);

    }

    closedir(DH);

    # @files is the list of untainted *.h filenames

    检查 Taintedness

    如果你需要检查 taintedness,你可以使用如下技巧:

    sub is_tainted {

    return ! eval {

    join('',@_), kill 0;

    1;

    };

    }

    你需要了解两件事情:kill 0 除了返回“true”之外,什么也不做;如果表达式的部分使用了tainted数据,那么,该表达式是tainted。所以,如果is_tainted调用时使用了tainted数据,对@_ 进行kill,就足以使得Perl 程序die。

     


     

    Untainting过了头也会有问题
    在不多的场合,盲目地untaint你的数据也产生安全漏洞。所以也此时需要Tainting的存在。如果象下面一样,盲目地对任何数据都untaint:

    $var =~ /(.*)/s; # 愚蠢

    $var = $1;

    正则表达式中的 /s 符号使得句点可以匹配串中的任何换行符。

    通过用 .* 我们匹配了串中的一切符号,并用$1存放该数据的untainted的副本。

    正如注释所说的,这样做是愚蠢的。

    总结

    -T 打开tainting选项。来自你程序之外的数据是tainted,不能使用这些数据,以免影响外部世界。

    用正则表达式和$1, $2, ... 变量进行untaint。要运行其他程序,设置好path,不要使用shell,并对参数进行untaint。

    进一步的阅读

    在perlsec manpage 中详细阐述了tainting的机制,并给出了较多的例子。Chapter Perl Cookbook的第十六章谈了进程管理,演示了non-shell 版的 piped opens和其他有趣的用法

    字体:
     
    设为主页 收藏本站 联系我们 友情连接 商务合作 网友留言
    Copyright©2006-2008 中华网络安全联盟 All rights reserved.