We did a Bugzilla security release today, to fix some holes responsibly disclosed to us by Check Point Vulnerability Research, to whom we are very grateful. The most serious of them would allow someone to create and control an account for an arbitrary email address they don’t own. If your Bugzilla gives group permissions based on someone’s email domain, as some do, this could be a privilege escalation.
(Update 2014-10-07 05:42 BST: to be clear, this pattern is most commonly used to add “all people in a particular company” to a group, using an email address regexp like .*@mozilla.com$
. It is used this way on bugzilla.mozilla.org to allow Mozilla Corporation employees access to e.g. Human Resources bugs. Membership of the Mozilla security group, which has access to unfixed vulnerabilities, is done on an individual basis and could not be obtained using this bug. The same is true of BMO admin privileges.)
These bugs are actually quite interesting, because they seem to represent a new Perl-specific security problem. (At least, as far as I’m aware it’s new, but perhaps we are about to find that everyone knows about it but us. Update 2014-10-08 09:20 BST: everything old is new again; but the level of response, including changes to CGI.pm, suggest that this had mostly faded from collective memory.) This is how it works. I’m using the most serious bug as my example. The somewhat less serious bugs caused by this pattern were XSS holes. (Check Point are going to be presenting on this vulnerability at the 31st Chaos Communications Congress in December in Hamburg, so check their analysis out too.)
Here’s the vulnerable code:
my $otheruser = Bugzilla::User->create({
login_name => $login_name,
realname => $cgi->param('realname'),
cryptpassword => $password});
This code creates a new Bugzilla user in the database when someone signs up. $cgi
is an object representing the HTTP request made to the page.
The issue is a combination of two things. Firstly, the $cgi->param()
call is context-sensitive – it can return a scalar or an array, depending on the context in which you call it – i.e. the type of the variable you assign the return value to. The ability for functions to do this is a Perl “do what I mean” feature.
Let’s say you called a page as follows, with 3 instances of the same parameter:
index.cgi?foo=bar&foo=baz&foo=quux
If you call param()
in an array context (the @ sigil represents a variable which is an array), you get an array of values:
@values = $cgi->param('foo');
-->
['bar', 'baz', 'quux']
If you call it in a scalar context (the $ sigil represents a variable which is a scalar), you get a single value, probably the first one:
$value = $cgi->param('foo');
-->
'bar'
So what context is it being called in, in the code under suspicion? Well, that’s exactly the problem. It turns out that functions called during hash value assignment are evaluated in a list context. However, when the result comes back, that value or those values are assigned to be part of uthe hash as if they were a set of individual, comma-separated scalars. I suspect this behaviour exists because of the close relationship of lists and hashes; it allows you to do stuff like:
my @array = ("foo", 3, "bar", 6);
my %hash = @array;
-->
{ "foo" => 3, "bar" => 6 }
Therefore, when assigning the result of a function call as a hash value, if the return value is a single scalar, all goes as you would expect, but if it’s an array, the second and subsequent values end up being added as key/value pairs in the hash as well. This allows an attacker to override values already in the hash (specified earlier), which may have already been validated, with values controlled by them. In our case, real_name
can be any string, so doesn’t need validation, but login_name
most definitely does, and it already has been by the time this code is called.
So, in the case of the problematic code above, something like:
index.cgi?realname=JRandomUser&realname=login_name&realname=admin@mozilla.com
would end up overriding the already-validated login_name
variable, giving the attacker control of the value used in the call to Bugzilla::User->create()
. Oops.
We found 15 instances of this pattern in our code, four of which were exploitable to some degree. If you maintain a Perl web application, you may want to audit it for this pattern. Clearly, CGI.pm param()
calls are the first thing to look for, but it’s possible that this pattern could occur with other modules which use the same context-sensitive return feature. The generic fix is to require the function call to be evaluated in scalar context:
my $otheruser = Bugzilla::User->create({
login_name => $login_name,
realname => scalar $cgi->param('realname'),
cryptpassword => $password});
I’d say it might be wise to not ever allow hash values to be assigned directly from functions without a call to scalar
.