Pregunta

this is an assignment for my computer security class, so I don't need specific answers, but I'd like some advice or at least general direction since I've been going in circles for a while.

Basically, we have an assignment where we've been given a simple cgi application (written in perl) that has a vulnerability somewhere that allows users to view private files, like /etc/shadow, that they aren't supposed to. We basically have to show that we can attack it and view the /etc/shadow file. The application is a memo viewing program, that lets users write and read memos.

Now, I'm very new to Perl. I did use it once or twice for very simple web stuff a long time ago, but that was literally the basics.

The code in question

#!/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>";

I think the key is that the program calls open(MEMO, $memo) without checking the user's input, so if you could point it to /etc/shadow the program would just print out the shadow file.

The problem is, currently it only lists the files from home/*/memo or /root/memo. I've been trying to figure out how to to point somewhere else. I think it has to do with the fact that we can create our own memos (by creating a directory called memo in our homedir). But I can't quite figure out what I need to put in the memo to get the program to open /etc/shadow instead.

Does anyone know if I'm on the right track or completely missing another obvious error?

¿Fue útil?

Solución

You're missing the obvious error.

A popup menu is created to specify a memo value, but there is nothing enforcing the user to only specify one of the prepopulated values. They could specify anything.

There isn't even any enforcement of a POST request method, so editing the form parameters in the URL would be sufficient for specifying a value:

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

Validation

To avoid the attack, one must validate that the data is within our expected range of values by either:

  1. Reusing the values used to populate the popup_menu and comparing.
  2. Use a regular expression to match expected format.

The least likely to introduce a new bug would be to reuse the original values. This is because it's very easy to not make a regex restrictive enough. For example, allowing the updir .. to be included in the path somewhere.

Additionally, the open call should use the 3 parameter form along with a lexical filehandle while we're at it. We do not want to allow the user to specify the mode of opening the file.

open my $fh, '<', $memo or die "Can't open $memo: $!";
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top