[Rt-commit] rt branch, 4.0-trunk, updated. rt-4.0.13-37-ge3511b8
Thomas Sibley
trs at bestpractical.com
Wed May 22 16:47:53 EDT 2013
The branch, 4.0-trunk has been updated
via e3511b8e05675c1f5fb097ad7a867fd1183c278c (commit)
via 063e1f308ed0a2a2a099a1cc8687fc4a81f3a358 (commit)
from b8105f1750064fd5d52be2a4e4810bd471c7e443 (commit)
Summary of changes:
t/security/CVE-2012-4730-email-header-injection.t | 103 +++++++++++++++++++++
t/security/CVE-2012-4731-create-article.t | 53 +++++++++++
t/security/CVE-2012-4732-csrf-blacklist.t | 42 +++++++++
t/security/CVE-2012-4734-login-warning.t | 30 ++++++
.../CVE-2012-4735-incoming-encryption-header.t | 85 +++++++++++++++++
t/security/CVE-2012-4735-sign-any-key.t | 94 +++++++++++++++++++
t/security/CVE-2012-4735-sign-encrypt-header.t | 55 +++++++++++
7 files changed, 462 insertions(+)
create mode 100644 t/security/CVE-2012-4730-email-header-injection.t
create mode 100644 t/security/CVE-2012-4731-create-article.t
create mode 100644 t/security/CVE-2012-4732-csrf-blacklist.t
create mode 100644 t/security/CVE-2012-4734-login-warning.t
create mode 100644 t/security/CVE-2012-4735-incoming-encryption-header.t
create mode 100644 t/security/CVE-2012-4735-sign-any-key.t
create mode 100644 t/security/CVE-2012-4735-sign-encrypt-header.t
- Log -----------------------------------------------------------------
commit 063e1f308ed0a2a2a099a1cc8687fc4a81f3a358
Author: Alex Vandiver <alexmv at bestpractical.com>
Date: Thu May 2 15:38:11 2013 -0400
Add security tests for vulnerabilities released 2012-10-25
This includes tests for:
CVE-2012-4730 Email header injection
CVE-2012-4731 Create Article without proper rights
CVE-2012-4732 CSRF blacklist evasion
CVE-2012-4734 Confused deputy during login
CVE-2012-4735 Improper signing or encryption of GnuPG messages
diff --git a/t/security/CVE-2012-4730-email-header-injection.t b/t/security/CVE-2012-4730-email-header-injection.t
new file mode 100644
index 0000000..893d111
--- /dev/null
+++ b/t/security/CVE-2012-4730-email-header-injection.t
@@ -0,0 +1,103 @@
+use strict;
+use warnings;
+
+use RT::Test tests => undef;
+use Email::Abstract;
+
+# Multiple email addresses for a single user are supported in 3.8 and 4.0 via
+# commas, so we really only care if you can inject other headers. It doesn't
+# much matter if you can inject other recipients via your user email address or
+# other values since you can do that in many visible and not-so-visible supported (!)
+# ways.
+#
+# HEADER INJECTION VIA USER EMAIL ADDRESS
+#
+# 4.0-trunk security/4.0/email-header-injection
+# -------------------------------------------------
+# Via recipient headers w/ RecordOutgoingEmail ON | No | No |
+# Via recipient headers w/ RecordOutgoingEmail OFF | Yes | No |
+# Via RT-Originator | Yes | No |
+# Via other default headers | No | No |
+# -------------------------------------------------
+#
+# With RecordOutgoingEmail ON, your recipient headers are filtered through
+# Email::Address, potentially mangling addresses if you've inserted newlines or
+# other funky chars that run together.
+
+my ($ok, $msg);
+
+# Create evil user
+my $user = RT::User->new( RT->SystemUser );
+($ok, $msg) = $user->Create(
+ Name => "eviluser",
+ EmailAddress => "foo\@example.com\nEvil: yes\n\nMalicious",
+);
+ok $user->id, "Created user with evil email address: $msg";
+like $user->EmailAddress, qr/\nEvil: yes/, "Email address still evil";
+like $user->EmailAddress, qr/\n\nMalicious/, "Email address still malicious";
+
+($ok, $msg) = $user->PrincipalObj->GrantRight(
+ Right => 'CreateTicket',
+ Object => RT->System,
+);
+ok $ok, "Granted CreateTicket to evil user: $msg";
+
+note "To: header (any recipient header)";
+{
+ for my $record_outgoing (1, 0) {
+ RT->Config->Set( RecordOutgoingEmail => $record_outgoing );
+ note "RecordOutgoingEmail is " . ($record_outgoing ? "ON" : "OFF");
+
+ # Create ticket which will...
+ my $ticket = RT::Ticket->new( RT->SystemUser );
+ ($ok, $msg) = $ticket->Create(
+ Queue => 1,
+ Subject => "test recipient ticket",
+ Requestor => $user->PrincipalId,
+ );
+ ok $ticket->id, "Created ticket: $msg";
+
+ # ... send an autoreply to said user, putting their email address in the To: header
+ my @email = RT::Test->fetch_caught_mails;
+ is @email, 1, "Caught one email";
+
+ my $entity = Email::Abstract->new($email[0])->cast('MIME::Entity');
+ my $head = $entity->head;
+ like $head->get("Subject"), qr/autoreply/i, "Looks like autoreply";
+ like $head->get("To"), qr/foo\@example\.com/, "To: contains foo\@example.com";
+ ok !$head->get("Evil"), "No Evil header";
+ unlike $entity->stringify_body, qr/Malicious/, "No Malicious body";
+ }
+}
+
+note "RT-Originator header";
+{
+ for my $originator (1, 0) {
+ RT->Config->Set( UseOriginatorHeader => $originator );
+ note "UseOriginatorHeader is " . ($originator ? "ON" : "OFF");
+
+ # Create ticket as evil user
+ my $ticket = RT::Ticket->new( RT::CurrentUser->new($user) );
+ ($ok, $msg) = $ticket->Create(
+ Queue => 1,
+ Subject => "test originator ticket",
+ Requestor => 'unsuspecting at example.com', # provide any recipient
+ );
+ ok $ticket->id, "Created ticket: $msg";
+
+ # ... sends an email
+ my @email = RT::Test->fetch_caught_mails;
+ is @email, 1, "Caught one email";
+
+ my $entity = Email::Abstract->new($email[0])->cast('MIME::Entity');
+ my $head = $entity->head;
+ if ($originator) {
+ like $head->get("RT-Originator"), qr/foo\@example\.com/, "RT-Originator contains email";
+ like $head->get("RT-Originator"), qr/Evil: yes/, "Evil didn't leak out of RT-Originator";
+ }
+ ok !$head->get("Evil"), "No Evil header";
+ unlike $entity->stringify_body, qr/Malicious/, "No Malicious body";
+ }
+}
+
+done_testing;
diff --git a/t/security/CVE-2012-4731-create-article.t b/t/security/CVE-2012-4731-create-article.t
new file mode 100644
index 0000000..2797fbc
--- /dev/null
+++ b/t/security/CVE-2012-4731-create-article.t
@@ -0,0 +1,53 @@
+use strict;
+use warnings;
+
+use RT::Test tests => undef;
+
+my $user = RT::Test->load_or_create_user( Name => 'testuser' );
+ok $user->id, "Created user: ".$user->id;
+
+my $class = RT::Class->new( RT->SystemUser );
+my ($ok, $msg) = $class->Create( Name => 'recipes' );
+ok $ok, "Created class: $msg";
+
+ok(
+ RT::Test->set_rights(
+ Principal => 'Privileged',
+ Right => [qw/SeeClass/],
+ Object => $class,
+ ),
+ "Granted SeeClass on the Class"
+);
+
+my $article = RT::Article->new( RT::CurrentUser->new($user) );
+($ok, $msg) = $article->Create( Class => 'recipes', Name => 'ricotta raspberry buttermilk scones' );
+ok !$ok, "Can't create article: $msg";
+
+ok(
+ RT::Test->add_rights(
+ Principal => 'Privileged',
+ Right => [qw/CreateArticle/],
+ Object => $class,
+ ),
+ "Granted CreateArticle on the Class"
+);
+
+($ok, $msg) = $article->Create( Class => 'recipes', Name => 'ricotta raspberry buttermilk scones' );
+ok $ok, "Created article: $msg";
+
+($ok, $msg) = $article->SetSummary("a tasty morning treat");
+ok !$ok, "Can't update Summary: $msg";
+
+ok(
+ RT::Test->add_rights(
+ Principal => 'Privileged',
+ Right => [qw/ModifyArticle/],
+ Object => $class,
+ ),
+ "Granted ModifyArticle on the Class"
+);
+
+($ok, $msg) = $article->SetSummary("a tasty morning treat");
+ok $ok, "Updated Summary: $msg";
+
+done_testing;
diff --git a/t/security/CVE-2012-4732-csrf-blacklist.t b/t/security/CVE-2012-4732-csrf-blacklist.t
new file mode 100644
index 0000000..b20da3b
--- /dev/null
+++ b/t/security/CVE-2012-4732-csrf-blacklist.t
@@ -0,0 +1,42 @@
+use strict;
+use warnings;
+
+use RT::Test tests => undef;
+
+my $ticket = RT::Ticket->new(RT::CurrentUser->new('root'));
+my ($ok, $msg) = $ticket->Create(Queue => 1, Owner => 'nobody', Subject => 'bad music');
+ok($ok);
+
+my ($baseurl, $m) = RT::Test->started_ok;
+
+my $test_page = "/Helpers/Toggle/TicketBookmark?id=1";
+my $test_path = "/Helpers/Toggle/TicketBookmark";
+
+ok $m->login, 'logged in';
+
+# valid referer
+$m->add_header(Referer => $baseurl);
+$m->get_ok($test_page);
+$m->content_lacks("Possible cross-site request forgery");
+$m->content_contains("star.gif");
+
+# come from an external source
+$m->add_header(Referer => 'http://example.com');
+$m->get_ok($test_page);
+$m->content_contains("Possible cross-site request forgery");
+$m->title_is('Possible cross-site request forgery');
+
+# come with no referer
+$m->add_header(Referer => undef);
+$m->get_ok($test_page);
+$m->content_contains("Possible cross-site request forgery");
+$m->title_is('Possible cross-site request forgery');
+
+# clicking the resume request button gets us to the test page
+$m->follow_link(text_regex => qr{resume your request});
+$m->content_lacks("Possible cross-site request forgery");
+like($m->response->request->uri, qr{^http://[^/]+\Q$test_path\E\?CSRF_Token=\w+$});
+$m->content_contains("star.gif");
+
+undef $m;
+done_testing;
diff --git a/t/security/CVE-2012-4734-login-warning.t b/t/security/CVE-2012-4734-login-warning.t
new file mode 100644
index 0000000..3dd21cd
--- /dev/null
+++ b/t/security/CVE-2012-4734-login-warning.t
@@ -0,0 +1,30 @@
+use strict;
+use warnings;
+
+use RT::Test tests => undef;
+
+my $ticket = RT::Ticket->new(RT::CurrentUser->new('root'));
+my ($ok, $msg) = $ticket->Create(Queue => 1, Owner => 'nobody', Subject => 'bad music');
+ok($ok);
+
+my ($baseurl, $m) = RT::Test->started_ok;
+
+my $test_page = "/Ticket/Display.html?id=$ok;Action=Take";
+
+$m->get_ok($test_page);
+$m->content_contains("update a ticket");
+ok($m->form_name('login'),"Found the login form");
+$m->submit_form(
+ form_name => 'login',
+ fields => {
+ user => 'root',
+ pass => 'password'
+ }
+);
+$m->content_contains('Owner changed from Nobody to root');
+
+$ticket->Load($ticket->Id);
+is($ticket->OwnerObj->Name,'root',"Ticket was assigned to root");
+
+undef $m;
+done_testing;
diff --git a/t/security/CVE-2012-4735-incoming-encryption-header.t b/t/security/CVE-2012-4735-incoming-encryption-header.t
new file mode 100644
index 0000000..d58c1c5
--- /dev/null
+++ b/t/security/CVE-2012-4735-incoming-encryption-header.t
@@ -0,0 +1,85 @@
+use strict;
+use warnings;
+
+use RT::Test::GnuPG tests => undef;
+use Test::Warn;
+
+{
+ my $mail = <<EOF;
+From: root\@localhost
+Subject: a ticket
+X-RT-Incoming-Encryption: Success
+X-RT-Incoming-Signature: Success
+
+This was _totally_ encrypted.
+EOF
+
+ my ($status, $id) = RT::Test->send_via_mailgate($mail);
+ ok $id, "created a ticket";
+
+ my $ticket = RT::Ticket->new( RT->SystemUser );
+ $ticket->Load( $id );
+ ok $ticket->id, "loaded ticket";
+
+ my $txn = $ticket->Transactions->First;
+ my ($msg, $attach, $orig) = @{$txn->Attachments->ItemsArrayRef};
+
+ like( $msg->Content, qr/This was _totally_ encrypted/, "Found the right attachment" );
+
+ is( $msg->GetHeader('X-RT-Incoming-Encryption'),
+ 'Not encrypted',
+ 'Incoming encryption header is removed'
+ );
+
+ is( $msg->GetHeader('X-RT-Incoming-Signature'),
+ undef,
+ 'Incoming signature header is removed'
+ );
+}
+
+
+
+{
+ my $mail = <<EOF;
+From: root\@localhost
+Subject: a ticket
+X-RT-Incoming-Encryption: Success
+X-RT-Incoming-Signature: Success
+
+-----BEGIN PGP MESSAGE-----
+Version: GnuPG v1.4.11 (GNU/Linux)
+
+This was _totally_ encrypted.
+-----END PGP MESSAGE-----
+EOF
+
+ my ($status, $id);
+ warnings_like {
+ ($status, $id) = RT::Test->send_via_mailgate($mail);
+ ok $id, "created a ticket";
+ } [qr/keyring .* created/,
+ qr/Had a problem during decrypting and verifying/,
+ qr/Couldn't process a message/,
+ ];
+
+ my $ticket = RT::Ticket->new( RT->SystemUser );
+ $ticket->Load( $id );
+ ok $ticket->id, "loaded ticket";
+
+ my $txn = $ticket->Transactions->First;
+ my ($msg, $attach, $orig) = @{$txn->Attachments->ItemsArrayRef};
+
+ like( $msg->Content, qr/This was _totally_ encrypted/, "Found the right attachment" );
+
+ is( $msg->GetHeader('X-RT-Incoming-Encryption'),
+ undef,
+ 'Incoming encryption header is removed'
+ );
+
+ is( $msg->GetHeader('X-RT-Incoming-Signature'),
+ undef,
+ 'Incoming signature header is removed'
+ );
+}
+
+done_testing;
diff --git a/t/security/CVE-2012-4735-sign-any-key.t b/t/security/CVE-2012-4735-sign-any-key.t
new file mode 100644
index 0000000..290c7a8
--- /dev/null
+++ b/t/security/CVE-2012-4735-sign-any-key.t
@@ -0,0 +1,94 @@
+use strict;
+use warnings;
+
+use RT::Test::GnuPG
+ tests => undef,
+ gnupg_options => {
+ passphrase => 'rt-test',
+ 'trust-model' => 'always',
+ };
+
+RT::Test->import_gnupg_key('rt-recipient at example.com');
+RT::Test->import_gnupg_key('general at example.com');
+
+# Determine the key IDs of the newly-loaded keys
+my %secret_keys;
+{
+ my %info = RT::Crypt::GnuPG::GetKeysInfo(undef, 'secret', 1);
+ for my $key (@{$info{info}}) {
+ my $user = $key->{User}[0]{String};
+ $user = (Email::Address->parse( $user ))[0]->address;
+ $secret_keys{$user} = $key->{Key};
+ }
+}
+
+my $queue = RT::Test->load_or_create_queue(
+ Name => 'Signing',
+ CorrespondAddress => 'rt-recipient at example.com',
+ CommentAddress => 'rt-recipient at example.com',
+);
+ok $queue && $queue->id, 'loaded or created queue';
+
+my ( $baseurl, $m ) = RT::Test->started_ok;
+ok $m->login, 'logged in';
+
+sub create_signed {
+ my $key = shift;
+ diag( "Attempting to sign using $key" );
+
+ $m->goto_create_ticket( $queue->id );
+ $m->form_name('TicketCreate');
+ unless ($m->current_form->find_input("SignUsing")) {
+ my $content = $m->content;
+ $content =~ s!using Queue(?:'|')s key!using <input type="text" name="SignUsing" />!;
+ $m->update_html( $content );
+ }
+
+ $m->form_name('TicketCreate');
+ $m->field( Subject => 'test' );
+ $m->field( Requestors => 'rt-test at example.com' );
+ $m->field( Content => 'Some content' );
+ $m->tick( Sign => 1 );
+ $m->field( SignUsing => $key );
+ $m->submit;
+}
+
+create_signed( '' );
+$m->content_lacks("unable to sign outgoing email messages");
+my @mail = RT::Test->fetch_caught_mails;
+is( scalar @mail, 1, "Got a mail" );
+like( $mail[0], qr/BEGIN PGP SIGNATURE/, "Is signed" );
+
+
+create_signed( 'rt-recipient at example.com' );
+$m->content_lacks("unable to sign outgoing email messages");
+ at mail = RT::Test->fetch_caught_mails;
+is( scalar @mail, 1, "Got a mail" );
+like( $mail[0], qr/BEGIN PGP SIGNATURE/, "Is signed" );
+
+
+create_signed( 'general at example.com' );
+$m->content_contains("unable to sign outgoing email messages");
+ at mail = RT::Test->fetch_caught_mails;
+is( scalar @mail, 0, "Sent no mail" );
+
+create_signed( $secret_keys{'general at example.com'} );
+$m->content_contains("unable to sign outgoing email messages");
+ at mail = RT::Test->fetch_caught_mails;
+is( scalar @mail, 0, "Sent no mail" );
+
+
+my $user = RT::User->new( RT->SystemUser );
+$user->Load( 'root' );
+my ($ok, $msg) = $user->SetPrivateKey( $secret_keys{'general at example.com'} );
+ok($ok, "Set private key to $secret_keys{'general at example.com'}: $msg" );
+
+
+create_signed( $secret_keys{'general at example.com'} );
+$m->content_lacks("unable to sign outgoing email messages");
+ at mail = RT::Test->fetch_caught_mails;
+is( scalar @mail, 1, "Sent a mail" );
+like( $mail[0], qr/BEGIN PGP SIGNATURE/, "Is signed" );
+
+undef $m;
+done_testing;
diff --git a/t/security/CVE-2012-4735-sign-encrypt-header.t b/t/security/CVE-2012-4735-sign-encrypt-header.t
new file mode 100644
index 0000000..4cf8e00
--- /dev/null
+++ b/t/security/CVE-2012-4735-sign-encrypt-header.t
@@ -0,0 +1,55 @@
+use strict;
+use warnings;
+
+use RT::Test::GnuPG
+ tests => undef,
+ gnupg_options => {
+ passphrase => 'rt-test',
+ 'trust-model' => 'always',
+ };
+
+RT::Test->import_gnupg_key('rt-recipient at example.com');
+RT::Test->import_gnupg_key( 'rt-test at example.com', 'public' );
+
+my $queue = RT::Test->load_or_create_queue(
+ Name => 'GPG',
+ CorrespondAddress => 'rt-recipient at example.com',
+ CommentAddress => 'rt-recipient at example.com',
+ Sign => 0,
+);
+ok $queue && $queue->id, 'loaded or created queue';
+
+{
+ my $mail = <<EOF;
+From: root\@localhost
+Subject: a ticket
+
+That sounds like a great idea!
+EOF
+
+ my ($status, $id) = RT::Test->send_via_mailgate($mail, queue => "GPG");
+ ok $id, "created a ticket";
+
+ my ($reply) = RT::Test->fetch_caught_mails;
+ ok( defined $reply, "Got a message" );
+ unlike( $reply, qr/BEGIN PGP SIGNATURE/, "It is not signed");
+}
+
+{
+ my $mail = <<EOF;
+From: root\@localhost
+Subject: a ticket
+X-RT-Sign: 1
+
+That sounds like a great idea!
+EOF
+
+ my ($status, $id) = RT::Test->send_via_mailgate($mail, queue => "GPG");
+ ok $id, "created a ticket";
+
+ my ($reply) = RT::Test->fetch_caught_mails;
+ ok( defined $reply, "Got a message" );
+ unlike( $reply, qr/BEGIN PGP SIGNATURE/, "It is not signed");
+}
+
+done_testing;
commit e3511b8e05675c1f5fb097ad7a867fd1183c278c
Merge: b8105f1 063e1f3
Author: Thomas Sibley <trs at bestpractical.com>
Date: Wed May 22 13:37:03 2013 -0700
Merge branch 'security/4.0/tests' into 4.0-trunk
-----------------------------------------------------------------------
More information about the Rt-commit
mailing list