passwordauth: prevent authentication bypass via multiple name parameters

Calling CGI::FormBuilder::field with a name argument in list context
returns zero or more user-specified values of the named field, even
if that field was not declared as supporting multiple values.
Passing the result of field as a function parameter counts as list
context. This is the same bad behaviour that is now discouraged
for CGI::param.

In this case we pass the multiple values to CGI::Session::param.
That accessor has six possible calling conventions, of which four are
documented. If an attacker passes (2*n + 1) values for the 'name'
field, for example name=a&name=b&name=c, we end up in one of the
undocumented calling conventions for param:

    # equivalent to: (name => 'a', b => 'c')
    $session->param('name', 'a', 'b', 'c')

and the 'b' session parameter is unexpectedly set to an
attacker-specified value.

In particular, if an attacker "bob" specifies
name=bob&name=name&name=alice, then authentication is carried out
for "bob" but the CGI::Session ends up containing {name => 'alice'},
an authentication bypass vulnerability.

This vulnerability is tracked as OVE-20170111-0001.

(cherry picked from commit e909eb93f4530a175d622360a8433e833ecf0254)
master
Simon McVittie 2017-01-11 13:12:50 +00:00
parent c7a4d57772
commit f357856448
1 changed files with 3 additions and 3 deletions

View File

@ -325,12 +325,13 @@ sub formbuilder (@) {
if ($form->title eq "signin" || $form->title eq "register") { if ($form->title eq "signin" || $form->title eq "register") {
if (($form->submitted && $form->validate) || $do_register) { if (($form->submitted && $form->validate) || $do_register) {
my $user_name = $form->field('name');
if ($form->submitted eq 'Login') { if ($form->submitted eq 'Login') {
$session->param("name", $form->field("name")); $session->param("name", $user_name);
IkiWiki::cgi_postsignin($cgi, $session); IkiWiki::cgi_postsignin($cgi, $session);
} }
elsif ($form->submitted eq 'Create Account') { elsif ($form->submitted eq 'Create Account') {
my $user_name=$form->field('name');
if (IkiWiki::userinfo_setall($user_name, { if (IkiWiki::userinfo_setall($user_name, {
'email' => $form->field('email'), 'email' => $form->field('email'),
'regdate' => time})) { 'regdate' => time})) {
@ -344,7 +345,6 @@ sub formbuilder (@) {
} }
} }
elsif ($form->submitted eq 'Reset Password') { elsif ($form->submitted eq 'Reset Password') {
my $user_name=$form->field("name");
my $email=IkiWiki::userinfo_get($user_name, "email"); my $email=IkiWiki::userinfo_get($user_name, "email");
if (! length $email) { if (! length $email) {
error(gettext("No email address, so cannot email password reset instructions.")); error(gettext("No email address, so cannot email password reset instructions."));