Item13125: CGI::param called in list context from package Foswiki::Engine::CGI line 222,
Priority: Security
Current State: Closed
Released In: 2.0.0
Target Release: major
Recent versions of CGI complain about use of list context.
#### Method: param / multi_param
# Returns the value(s)of a named parameter.
# If invoked in a list context, returns the
# entire list. Otherwise returns the first
# member of the list.
# If name is not provided, return a list of all
# the known parameters names available.
# If more than one argument is provided, the
# second and subsequent arguments are used to
# set the value of the parameter.
#
# note that calling param() in list context
# will raise a warning about potential bad
# things, hence the multi_param method
####
This was added in CGI version 4.08. Earlier versions do not generate the warning. See also
http://seclists.org/vulnwatch/2006/q4/6. TWiki's take on the error at
http://twiki.org/cgi-bin/view/Support/SID-01980.
[Sun Nov 30 12:55:38 2014] login: CGI::param called in list context from package Foswiki::Engine::CGI line 222, this can lead to vulnerabilities. See the warning in "Fetching the value or values of a single named parameter" at /usr/share/perl5/CGI.pm line 436.
--
GeorgeClark - 30 Nov 2014
Since it's possible for extensions to call CGI directly, rather than disable the warning, we need to:
- Review code and double check that all CGI::param in list context is acceptable.
- Test CGI version, or ->can(multi_param) and make the correct call for the CGI version
This allows the code to still catch and warn about direct CGI calls that might not be handling multi-valued parameters correctly
--
GeorgeClark - 30 Nov 2014
... and for the impatient, use this patch to quiet the noise.
diff --git a/core/lib/Foswiki/Engine.pm b/core/lib/Foswiki/Engine.pm
index a9a64a2..dac1527 100644
--- a/core/lib/Foswiki/Engine.pm
+++ b/core/lib/Foswiki/Engine.pm
@@ -19,6 +19,7 @@ use warnings;
use Error qw( :try );
use Assert;
use Scalar::Util ();
+$CGI::LIST_CONTEXT_WARN = 0;
BEGIN {
if ( $Foswiki::cfg{UseLocale} ) {
--
MichaelDaum - 01 Dec 2014
The basic coding pattern to watch out for is
my $hashref = {name => $q->param('name')};
Problems arise when
param('name')
returns a list instead of a scalar which is the case when
name
is multi-valued. As a consequence the inner expression of the hash constructor will potentially initialize additional properties besides
name
.
Example:
http://example.com/somecgi?name=1&name=2&name=evilkey&name=evilvalue
Hash:
$VAR1 = {
'evilkey' => 'evilvalue',
'name' => '2'
};
See
http://seclists.org/vulnwatch/2006/q4/6 for details.
Analyzing the Foswiki core code there are at least 4 spots matching a
grep
grep -r -- "->param([^)][^)]*) *}" lib/Foswiki
(please help improving this
grep
)
Result:
lib/Foswiki/Request.pm: if ( my $upload = $this->{uploads}{ $this->param($p) } ) {
lib/Foswiki/Request.pm: CORE::delete $this->{uploads}{ $this->param($p) };
lib/Foswiki/Request.pm: my $upload = $this->{uploads}{ $this->param($name) };
lib/Foswiki/Configure/Wizards/Save.pm: while ( my ( $k, $v ) = each %{ $this->param('set') } ) {
This with my limitted understanding of the issue.
Another spot to consider:
# Handle approver action; either approve or deny
sub _checkApproval {
...
my $data = {
EmailAddress => $session->{request}->param('email'),
Referee => $session->{request}->param('referee'),
WikiName => Foswiki::Sandbox::untaint(
$session->{request}->param('wikiname') || 'UnknownUser',
\&Foswiki::Sandbox::validateTopicName
),
Feedback => $session->{request}->param('feedback')
};
...
--
MichaelDaum - 01 Dec 2014
Discussion from IRC - Release meeting & Security channels. I redacted the discussion from the release meeting minutes.
(08:40:39 AM) MichaelDaum: http://foswiki.org/Tasks/Item13125 ... how do we deal with it during the release?
(08:41:13 AM) MichaelDaum: I am not really sure I know what the new warning messages are ... but provided a patch to quiet it ;)
..
(08:43:27 AM) gac410: Item13125 - The solution is to explicitly use multi-param if you expect multi-valued paramegers, But need to can->('multi-param') since it's a new addition.
(08:43:59 AM) gac410: Basically cgi->param in a @list context will throw the warning, since behavior is unpredictable based upon browser input.
(08:45:02 AM) gac410: So adding the workaround is certainly safe ... we will work exactly like we always worked. But going with the new call without disabling the warning might help plugin developers avoid cgi errors.
(08:45:33 AM) gac410: I wouldnt' hold up the release for a "good fix"
(08:45:39 AM) MichaelDaum: @vals = $cgi->param("multi") is unexpected with only one multi urlparam ? why's that?
(08:46:02 AM) MichaelDaum: I'd expect a list with one item in it
(08:47:01 AM) gac410: tbh I'm not really sure. Yes that's what happens. Maybe a tempest in teapot.
(08:47:26 AM) gac410: But I guess there were some security implications, and the cgi maintainers decided to change the behavior
...
(08:53:47 AM) MichaelDaum: http://seclists.org/vulnwatch/2006/q4/6 is the security report to consider while checking our code
(08:54:12 AM) MichaelDaum: ... for the cgi param thing
(08:54:46 AM) gac410: Thanks MichaelDaum Maybe a review of that and we can decide just to add the error suppress.
(08:56:14 AM) MichaelDaum: the pattern to look for is %{$cgi->param("multi")} ... that is cgi->param as a HASH constructor returning a ref to it ... wild wild assumption. never seen anywhere in the code. though not 1000% sure.
(08:57:00 AM) gac410: Okay worth a review. I agree I think we handle the multi-value parms correctly.
(08:58:03 AM) MichaelDaum: plus: their scenario is adding Data::FormValidator; to it... i.e. its _validate_ method being called in list context ... not cgi->param directly
(08:58:50 AM) MichaelDaum: this after reading the seclist entry cursory
.... Discussion moved to #Foswiki-security
(10:24:50 AM) ***MichaelDaum reviewing all use of ->param() in the core code
(10:25:11 AM) MichaelDaum: things to look for is cgi->param() being called inside a hash constructor
(10:25:22 AM) MichaelDaum: such as %{ $this->param(
(10:25:27 AM) MichaelDaum: such as %{ $this->param('set) }
(10:25:41 AM) MichaelDaum: ^set^set'
(10:26:14 AM) MichaelDaum: I've found 3 in Request.pm and one in ./Configure/Wizards/Save.pm
(10:26:37 AM) jast: or inside a function's arg list
(10:26:49 AM) gac410: wonderful That's not good news
(10:28:13 AM) MichaelDaum: jast right
(10:28:36 AM) MichaelDaum: the first is: if ( my $upload = $this->{uploads}{ $this->param($p) } ) {
(10:28:56 AM) MichaelDaum: in Request.pm
(10:29:58 AM) MichaelDaum: followed by CORE::delete $this->{uploads}{ $this->param($p) }; one line below
(10:30:07 AM) gac410: I'm glad I didn't just go with the TWiki solution - quiet the messages and ignore it.
(10:32:22 AM) MichaelDaum: jast, have you got any idea how to deal with these code spots?
(10:33:30 AM) MichaelDaum: if there is at least a 10% chance that our code indeed is affected by http://seclists.org/vulnwatch/2006/q4/6 then we have to raise http://foswiki.org/Tasks/Item13125 to security level until further clarified
(10:33:49 AM) ***MichaelDaum doing so docu'ing the findings
(10:37:37 AM) jast: actually your example from Request.pm should be "fine" – hash lookups are scalar context afaik
(10:37:50 AM) jast: you're not accessing a hash slice unless you do @myhash{...}
(10:38:43 AM) jast: it's hash literals where you run into problems
(10:42:30 AM) MichaelDaum: any code example?
(10:42:45 AM) MichaelDaum: in core?
(10:43:23 AM) jast: I haven't really looked
(10:43:23 AM) MichaelDaum: see http://foswiki.org/Tasks/Item13125
(10:43:40 AM) MichaelDaum: could you comment on the 4 code spots I listed there?
(10:44:29 AM) jast: I don't see an issue with the first three
(10:44:46 AM) jast: and I think the fourth is deliberate
(10:44:57 AM) jast: there'd be no sense in iterating over something we expect to be a single value
(10:45:15 AM) MichaelDaum: y
(10:46:00 AM) MichaelDaum: however {uploads}{ $this->param($p)} could potentially construct {upload}{lots of evil props} inside
(10:46:58 AM) MichaelDaum: the first two are part of a delete()
(10:47:37 AM) MichaelDaum: not that I understand the code at all
(10:48:29 AM) jast: as I said... @foo{list here} is a thing... $foo{list here} isn't
(10:50:17 AM) jast: perl -e '%a=(1,2,3,4); print defined($a{1,3})."\n";'
(10:50:56 AM) jast: do the same with @a{1,3} and it does give a result
(10:51:19 AM) MichaelDaum: the code pattern on seclists is my $hashref = {name => $q->param('name')};
(10:51:30 AM) MichaelDaum: which is constructing a hash ref
(10:51:31 AM) jast: yeah, and that one is definitely a problem
(10:51:43 AM) jast: it would be a problem for constructing a normal hash, too
(10:51:59 AM) jast: %h = (name => $q->param('exploitmepls'));
(10:54:35 AM) MichaelDaum: ah ok
(10:55:51 AM) MichaelDaum: greping for any "=>.*-<param"
(10:55:56 AM) MichaelDaum: greping for any "=>.*->param"
(10:57:49 AM) MichaelDaum: lib/Foswiki/UI/Register.pm: Referee => $session->{request}->param('referee'),
(10:58:20 AM) MichaelDaum: line 242ff
(10:58:27 AM) MichaelDaum: a couple of them
(11:03:55 AM) jast: yeah, that's probably a problem
(11:04:27 AM) jast: fix is straightforward: ... => scalar($session->{request}->param(...))
(11:05:09 AM) MichaelDaum: the properties of the $data hash are then used to call _sendEmail which expands macros n stuff
(11:05:12 AM) jast: in this case the impact is low because the code only renders an e-mail to be sent to the person
(11:05:35 AM) jast: well unless the code double-expands macros, I guess
(11:05:42 AM) MichaelDaum: so you could email any result of a %macro?
(11:57:11 AM) gac410: Any verdict yet? ... do we have an exposure, and if so, is it CVE-worthy?
(11:58:35 AM) MichaelDaum: dunno, it needs somebody more security savvy
(12:00:12 PM) jast: we'd need to comb through the code to be sure
(12:01:36 PM) jast: the grep only catches "likely" problems
(12:38:32 PM) MichaelDaum left the room (quit: ).
--
GeorgeClark - 01 Dec 2014
I've got a possible fix started that so far tests backwards compatible to older versions of CGI. So far running master on the latest CGI, this seems to be the only place triggering the use of param in a list context.
Rather than suppressing the errors, this lets us find questionable uses while remaining backwards compatible with older versions of CGI.
One issue I see is that our
Foswiki::Request
is how we access CGI parameters. Other than in Engine, the calls to
param()
are made against
Request::param()
, and not
CGI::param()
. So this is probably a deeper than simply handling calls to CGI. Our implementation of
Request::param()
needs to be checked against the vulnerability in
CGI::param()
to make sure we don't have a similar problem. Given that Request mirrors
CGI
, we should consider implementing
Request::multi_param()
and following the same checks done in
CGI
.
diff --git a/core/lib/Foswiki/Engine/CGI.pm b/core/lib/Foswiki/Engine/CGI.pm
index 6200f8d..69612bb 100644
--- a/core/lib/Foswiki/Engine/CGI.pm
+++ b/core/lib/Foswiki/Engine/CGI.pm
@@ -207,6 +207,12 @@ sub prepareBody {
$CONSTRUCTOR_PID = $$;
my $cgi = new CGI();
+ unless ($cgi->can('multi_param')) {
+ no warnings 'redefine';
+ print STDERR "REDEFINED CGI multi_param\n";
+ *CGI::multi_param = \&CGI::param;
+ use warnings 'redefine';
+ }
my $err = $cgi->cgi_error;
throw Foswiki::EngineException( $1, $2 )
if defined $err && $err =~ /\s*(\d{3})\s*(.*)/;
@@ -217,9 +223,9 @@ sub prepareBodyParameters {
my ( $this, $req ) = @_;
return unless $ENV{CONTENT_LENGTH};
- my @plist = $this->{cgi}->param();
+ my @plist = $this->{cgi}->multi_param();
foreach my $pname (@plist) {
- my @values = map { "$_" } $this->{cgi}->param($pname);
+ my @values = map { "$_" } $this->{cgi}->multi_param($pname);
$req->bodyParam( -name => $pname, -value => \@values );
$this->{uploads}{$pname} = 1 if scalar $this->{cgi}->upload($pname);
}
--
GeorgeClark - 06 Dec 2014
And.. This diff implements the same checks in Foswiki::Request ... Triggers a lot of warinings. This will be more reliable than code inspection:
diff --git a/core/lib/Foswiki/Request.pm b/core/lib/Foswiki/Request.pm
index f2dffd2..dab43c5 100644
--- a/core/lib/Foswiki/Request.pm
+++ b/core/lib/Foswiki/Request.pm
@@ -421,12 +421,32 @@ Resonably compatible with CGI.
=cut
+sub multi_param {
+
+ my @list_of_params = param(@_);
+ return @list_of_params;
+}
+
sub param {
- my ( $this, @p ) = @_;
+ my ( $this, @p ) = (@_);
my ( $key, @value ) = rearrange( [ 'NAME', [qw(VALUE VALUES)] ], @p );
return @{ $this->{param_list} } unless defined $key;
+
+# list context can be dangerous so warn:
+# http://blog.gerv.net/2014.10/new-class-of-vulnerability-in-perl-web-applications
+ if (wantarray) {
+ my ( $package, $filename, $line ) = caller;
+ if ( $package ne 'Foswiki::Request' ) {
+ warn
+"Foswiki::Request::param called in list context from package $package line $line, declare as scalar, or call multi_param. ";
+ }
+ }
+
if ( defined $value[0] ) {
push @{ $this->{param_list} }, $key
unless exists $this->{param}{$key};
--
GeorgeClark - 06 Dec 2014
More complete patch attached. In addition to warning if
::param()
is called in a list context, it also warns when called as a scalar and the parameter is multivalued.
Fixed errors reported for routine view, edit, save, and user registration, table sorting ...
--
GeorgeClark - 06 Dec 2014
Hi all, opinions on the attached patch? Check it in now? I don't think we have exposed any vulnerabilities but I'm not certain.
--
GeorgeClark - 09 Dec 2014
Calling
::param
in list context isn't dangerous per se. There are
three conditions that must match:
-
::param
is used in list context
- the returned list is used to initialized a hash
- hash is being read in a generic way processing all key-value pairs (e.g. while key,value = each %hash; $file .= $key . $value; ... # stupid)
These
three factors may lead to the hash being initialized and used in undesirable ways ... which is the vulnerability.
Again, the following code alone is
NOT vulnerable by any means:
@list = $cgi->param("key");
It is 100% secure and
THE most common way to read multiple params from
CGI
. Only when
@list
is then being used to initialized a HASH might it cause problems as it is not clear which key-value pairs are contained.
As such replacing any used of
::param
with
::multi_param
might not shut down this attack vector at all. While coders might now be more aware of a
@list
being returned, I doubt they were not before. Still, the returned @list of params could be used as a HASH initializer causing
exactly the same problems as using it the old way before:
@list = $cgi->multi_param("key");
What holds me back to misuse this
@list
the same way it has been constructed before using
::param
???
Frankly, I don't really understand the recommended fix in the advisory. The only thing it does is shutting down a warning by replacing
param
with
multi_param
in list context. The misuse of the returned list is
not prevented.
Wrt the patch, I am puzzled by things like
- $req->queryParam( $param, $params{$param} );
+ ( undef ) = $req->queryParam( $param, $params{$param} );
This only for the sake of a list context? Wtf.
--
MichaelDaum - 09 Dec 2014
There is an additional way
param
can become dangerous in list context: when it's used in the parameter list of a function call, like so (contrived example):
Foswiki::Func::saveTopicText($web, $topic, $query->param('newtext'), 0);
In that function, the parameter after $text is $ignorePermissions. If someone passes a POST body containing
newtext=my%20body;newtext=1
... guess what, we're now overwriting topics the user isn't actually allowed to overwrite.
@Main.MichaelDaum: I don't see any mention of
multi_param
in the advisory linked above, and you're right that blindly replacing
param
everywhere would not help. The purpose of
multi_param
is to communicate the intention of the developer. Use of
multi_param
asserts that the developer knows that multiple values may be returned. Blindly substitung
multi_param
everywhere would completely destroy that.
Our current approach is to
warn
when
param
is used in list context. This is only helpful if core/extension developers notice the warnings, figure out what they mean, and change their code. If they don't, the warning is only window dressing and there is no actual security.
A stronger protection would be to
die
in the same case. This is probably a bit extreme... especially since there are several cases in which calling
param
in list context is perfectly fine.
The way some other frameworks, e.g.
Mojolicious
, do it: make
param
return one value no matter what context it's called in, and make
multi_param
return a list no matter what context it's called in.
The impact of that approach on previously written code is that, in many cases, code that expects to get a list will now get only a single value. It will be not too enjoyable to fix all the code, but at least this is actually secure
and does not force us to make scalar context explicit all over the place.
--
JanKrueger - 09 Dec 2014
Why only
CGI::param
? Doesn't any function that behaves differently in scalar and list context cause an issue? The problem is either bigger as suggested or potemkinsh.
--
MichaelDaum - 09 Dec 2014
My fix was not blindly replacing param with multi_param. Where I changed it I:
- looked to see if a list was expected / desired, and made that multi_param.
- Looked to see if a list was NOT expected, and changed that to an explicit scalar
- And the ( wtf ) was because calling it as a null resulted in the warning,
I chose to go with the multi_param instead of just turning off warnings so that we would find any possible misuse, and fix them rather than depending on grep and code inspection. Also this finds future misuse of
Request->param()
rather than trusting that we got them all on the first time and turning off warnings. So my implementation strategy was to implement the same checks in our cloned
param()
code, test, find a "warn", review list or scalar, fix, ... repeat.
--
GeorgeClark - 09 Dec 2014
I had to make several more changes that were due to calls in a list context. While in there, I bracketed all uses of scalar() to reduce ambiguity.
--
CrawfordCurrie - 16 Jan 2015
Setting to waiting for release. Reopen if more issues arise.
--
GeorgeClark - 02 Mar 2015