1
From: Niko Tyni <ntyni@debian.org>
2
Subject: [CVE-2010-2761 CVE-2010-4410 CVE-2010-4411] CGI.pm MIME boundary and multiline header vulnerabilities
4
Bug-Debian: http://bugs.debian.org/606995
6
CVE-2010-2761 hardcoded MIME boundary, fixed in CGI.pm-3.50
7
CVE-2010-4410 CRLF injection vulnerability, fixed in CGI.pm-3.50
8
CVE-2010-4411 double CR/LF injection vulnerability, fixed in CGI.pm-3.51
13
lib/CGI.pm | 26 +++++++++++++++++++++++-
14
lib/CGI/t/headers.t | 47 ++++++++++++++++++++++++++++++++++++++++++++
15
lib/CGI/t/multipart_init.t | 20 ++++++++++++++++++
16
4 files changed, 94 insertions(+), 1 deletions(-)
18
diff --git a/MANIFEST b/MANIFEST
19
index 2b5a968..e0e950f 100644
22
@@ -1935,7 +1935,9 @@ lib/CGI/t/cookie.t See if CGI::Cookie works
23
lib/CGI/t/fast.t See if CGI::Fast works (if FCGI is installed)
24
lib/CGI/t/form.t See if CGI.pm works
25
lib/CGI/t/function.t See if CGI.pm works
26
+lib/CGI/t/headers.t See if CGI.pm works
27
lib/CGI/t/html.t See if CGI.pm works
28
+lib/CGI/t/multipart_init.t See if CGI.pm works
29
lib/CGI/t/no_tabindex.t See if CGI.pm works
30
lib/CGI/t/pretty.t See if CGI.pm works
31
lib/CGI/t/push.t See if CGI::Push works
32
diff --git a/lib/CGI.pm b/lib/CGI.pm
33
index 008bc7b..d859e76 100644
36
@@ -1382,7 +1382,14 @@ END_OF_FUNC
38
my($self,@p) = self_or_default(@_);
39
my($boundary,@other) = rearrange_header([BOUNDARY],@p);
40
- $boundary = $boundary || '------- =_aaaaaaaaaa0';
42
+ $boundary = '------- =_';
43
+ my @chrs = ('0'..'9', 'A'..'Z', 'a'..'z');
45
+ $boundary .= $chrs[rand(scalar @chrs)];
49
$self->{'separator'} = "$CRLF--$boundary$CRLF";
50
$self->{'final_separator'} = "$CRLF--$boundary--$CRLF";
51
$type = SERVER_PUSH($boundary);
52
@@ -1467,6 +1474,23 @@ sub header {
53
'EXPIRES','NPH','CHARSET',
54
'ATTACHMENT','P3P'],@p);
56
+ # CR escaping for values, per RFC 822
57
+ for my $header ($type,$status,$cookie,$target,$expires,$nph,$charset,$attachment,$p3p,@other) {
58
+ if (defined $header) {
60
+ # Unfolding is accomplished by regarding CRLF immediately
61
+ # followed by a LWSP-char as equivalent to the LWSP-char.
62
+ $header =~ s/$CRLF(\s)/$1/g;
64
+ # All other uses of newlines are invalid input.
65
+ if ($header =~ m/$CRLF|\015|\012/) {
66
+ # shorten very long values in the diagnostic
67
+ $header = substr($header,0,72).'...' if (length $header > 72);
68
+ die "Invalid header value contains a newline not followed by whitespace: $header";
75
$type ||= 'text/html' unless defined($type);
76
diff --git a/lib/CGI/t/headers.t b/lib/CGI/t/headers.t
78
index 0000000..661b74b
80
+++ b/lib/CGI/t/headers.t
83
+# Test that header generation is spec compliant.
85
+# http://www.w3.org/Protocols/rfc2616/rfc2616.html
86
+# http://www.w3.org/Protocols/rfc822/3_Lexical.html
91
+use Test::More 'no_plan';
97
+like $cgi->header( -type => "text/html" ),
98
+ qr#Type: text/html#, 'known header, basic case: type => "text/html"';
100
+eval { $cgi->header( -type => "text/html".$CGI::CRLF."evil: stuff" ) };
101
+like($@,qr/contains a newline/,'invalid header blows up');
103
+like $cgi->header( -type => "text/html".$CGI::CRLF." evil: stuff " ),
104
+ qr#Content-Type: text/html evil: stuff#, 'known header, with leading and trailing whitespace on the continuation line';
106
+eval { $cgi->header( -foobar => "text/html".$CGI::CRLF."evil: stuff" ) };
107
+like($@,qr/contains a newline/,'unknown header with CRLF embedded blows up');
109
+eval { $cgi->header( -foobar => $CGI::CRLF."Content-type: evil/header" ) };
110
+like($@,qr/contains a newline/, 'unknown header with leading newlines blows up');
112
+eval { $cgi->redirect( -type => "text/html".$CGI::CRLF."evil: stuff" ) };
113
+like($@,qr/contains a newline/,'redirect with known header with CRLF embedded blows up');
115
+eval { $cgi->redirect( -foobar => "text/html".$CGI::CRLF."evil: stuff" ) };
116
+like($@,qr/contains a newline/,'redirect with unknown header with CRLF embedded blows up');
118
+eval { $cgi->redirect( $CGI::CRLF.$CGI::CRLF."Content-Type: text/html") };
119
+like($@,qr/contains a newline/,'redirect with leading newlines blows up');
122
+ my $cgi = CGI->new('t=bogus%0A%0A<html>');
124
+ eval { $out = $cgi->redirect( $cgi->param('t') ) };
125
+ like($@,qr/contains a newline/, "redirect does not allow double-newline injection");
129
diff --git a/lib/CGI/t/multipart_init.t b/lib/CGI/t/multipart_init.t
131
index 0000000..f0a05e0
133
+++ b/lib/CGI/t/multipart_init.t
135
+use Test::More 'no_plan';
141
+my $sv = $q->multipart_init;
142
+like( $sv, qr|Content-Type: multipart/x-mixed-replace;boundary="------- =|, 'multipart_init(), basic');
144
+like( $sv, qr/$CGI::CRLF$/, 'multipart_init(), ends in CRLF' );
146
+$sv = $q->multipart_init( 'this_is_the_boundary' );
147
+like( $sv, qr/boundary="this_is_the_boundary"/, 'multipart_init("simple_boundary")' );
148
+$sv = $q->multipart_init( -boundary => 'this_is_another_boundary' );
150
+ qr/boundary="this_is_another_boundary"/, "multipart_init( -boundary => 'this_is_another_boundary')");
152
+$sv = $q->multipart_init;
153
+my $sv2 = $q->multipart_init;
154
+isnt($sv,$sv2,"due to random boundaries, multiple calls produce different results");
156
tg: (daf8b46..) fixes/cgi-multiline-header (depends on: upstream)