使用CGI的路径名攻击

时间:2014-05-14 23:27:57

标签: perl cgi pathname

这是我的计算机安全课程的作业,所以我不需要具体的答案,但我想要一些建议或至少大方向,因为我已经进入了一段时间。

基本上,我们有一个任务,我们已经给了一个简单的cgi应用程序(用perl编写),它有一个允许用户查看私有文件的漏洞,比如/ etc / shadow,它们不应该是。我们基本上必须表明我们可以攻击它并查看/ etc / shadow文件。该应用程序是一个备忘录查看程序,允许用户编写和阅读备忘录。

现在,我对Perl很新。很久以前,我曾经使用过一次或两次非常简单的网页内容,但这确实是基础知识。

有问题的代码

#!/usr/bin/perl -w

use CGI qw(:standard);
use CGI::Carp qw(fatalsToBrowser);
use strict;

my %labels; # global of pretty labels for memo pathnames

# glob through the homedirs for an array of paths to memos sorted by date
sub list_memo_selector {
        my @memos = </home/*/memo/*>; # all regular users
        push (@memos, </root/memo/*>); # special memos from root
        foreach (@memos) {
                $_ =~ m#^.+/([^/]+)$#; # regex extract filename
                my $temp = $1;
                $temp =~ s/_/ /g; # convert _ to " "
                $labels{$_} = $temp; # assign pretty label name
        }
        print popup_menu(-name=>'memo', -values=>\@memos, -labels=>\%labels);
        print submit("Read memo");
}

print header();
print "<html><head><title></title></head><body>\n";

print h1("FrobozzCo Memo Distribution Website");
print h4("Got Memo?");
print hr();

print p('Select a memo from the popup menu below and click the "Read memo" button.');
print p("<form method='post' name='main'>\n");

if (!param('memo')) {
        list_memo_selector();
} else { # there is a memo selected
        list_memo_selector();
        my $memo = param('memo');
        my $author = "root";
        my @stat = stat $memo;
        my $date = localtime $stat[9];
        if ($memo =~ m#^/home/([^/]+)/.*$#) {
                $author = $1;
        }
        print "<hr>\n";
        print "<blockquote>";
        print '<table border=1><tr><td>';
        print "<center><b>$labels{$memo}</b></center>";
        print '</td></tr>';
        print "<tr><td>\n<p>";
        print "<b>Author:</b> $author<br />\n";
        print "<b>Subject:</b> $labels{$memo}<br />";
        print "<b>Date:</b> $date<br />\n";
        print "\n</p></td></tr>\n";
        print "<tr><td><p>&nbsp;</p>\n";
        print "<blockquote><p>\n";

        open (MEMO, $memo);

        foreach (<MEMO>) {
                $_ =~ s#\n$#</p><p>#; # HTMLize newlines for formatting
                print "$_\n";
        }
        print "</p></blockquote>\n";
        print '<p>&nbsp;</p></td></tr></table>';
        print "</blockquote>";
        print "<hr>\n";
}


print h2("To publish a memo:");
print <<TEXT;

<ol>
<li>Create a directory named 'memo' in your home directory.</li>
<li>Edit text files in that directory.</li>
<li>Save the file using underscores (_) for spaces, e.g. "free_lunch".</li>
</ol>

TEXT

print p('To remove your memo from publication, simply delete the file from tme memo directory.');

print "</form>\n";
print "</body></html>";

我认为关键是程序调用open(MEMO,$ memo)而不检查用户的输入,所以如果你可以将它指向/ etc / shadow,程序就会打印出shadow文件。

问题是,目前它只列出home / * / memo或/ root / memo中的文件。我一直试图弄清楚如何指向其他地方。我认为这与我们可以创建自己的备忘录(通过在homedir中创建一个名为memo的目录)这一事实有关。但我无法弄清楚我需要在备忘录中放置什么来让程序打开/ etc / shadow。

有人知道我是在正确的轨道上还是完全错过了另一个明显的错误?

1 个答案:

答案 0 :(得分:4)

你错过了明显的错误。

创建一个弹出菜单以指定memo值,但没有任何内容强制用户仅指定其中一个预先填充的值。他们可以指定任何东西。

甚至没有执行POST请求方法,因此在URL中编辑表单参数就足以指定一个值:

http://www.yourdomain.com/form.cgi?memo=/etc/naughty/boy

<强>验证

为了避免攻击,必须通过以下任一方式验证数据是否在我们预期的值范围内:

  1. 重用用于填充popup_menu和比较的值。
  2. 使用正则表达式匹配预期格式。
  3. 引入新bug的可能性最小的是重用原始值。这是因为很容易使正则表达式变得不够严格。例如,允许updir ..包含在某个路径中。

    此外,open调用应该使用3参数形式以及词法文件句柄,而我们在此处。我们不希望允许用户指定打开文件的模式。

    open my $fh, '<', $memo or die "Can't open $memo: $!";