X-Received: by 10.224.66.196 with SMTP id o4mr11417833qai.2.1392750309473; Tue, 18 Feb 2014 11:05:09 -0800 (PST) X-BeenThere: rubyonrails-security@googlegroups.com Received: by 10.140.81.20 with SMTP id e20ls1440222qgd.58.gmail; Tue, 18 Feb 2014 11:04:57 -0800 (PST) X-Received: by 10.236.86.77 with SMTP id v53mr12169141yhe.41.1392750297178; Tue, 18 Feb 2014 11:04:57 -0800 (PST) Return-Path: Received: from mail-pb0-f47.google.com (mail-pb0-f47.google.com [209.85.160.47]) by gmr-mx.google.com with ESMTPS id g1si3942982pbw.2.2014.02.18.11.04.57 for (version=TLSv1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Tue, 18 Feb 2014 11:04:57 -0800 (PST) Received-SPF: neutral (google.com: 209.85.160.47 is neither permitted nor denied by best guess record for domain of aa...@tenderlovemaking.com) client-ip=209.85.160.47; Authentication-Results: gmr-mx.google.com; spf=neutral (google.com: 209.85.160.47 is neither permitted nor denied by best guess record for domain of aa...@tenderlovemaking.com) smtp.mail=aa...@tenderlovemaking.com Received: by mail-pb0-f47.google.com with SMTP id rp16so17140615pbb.34 for ; Tue, 18 Feb 2014 11:04:57 -0800 (PST) X-Gm-Message-State: ALoCoQkC9EFSfStJU23P06WFiP3zTxQNBn5sBY8+bjUSSkwhKf5ihgBSAMFezVXavEKjLTFiqLE7 X-Received: by 10.68.218.3 with SMTP id pc3mr35125393pbc.71.1392750296180; Tue, 18 Feb 2014 11:04:56 -0800 (PST) Return-Path: Received: from higgins.local (c-66-235-47-187.sea.wa.customer.broadstripe.net. [66.235.47.187]) by mx.google.com with ESMTPSA id qh2sm148213879pab.13.2014.02.18.11.04.53 for (version=TLSv1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Tue, 18 Feb 2014 11:04:54 -0800 (PST) Sender: Aaron Patterson Date: Tue, 18 Feb 2014 11:04:47 -0800 From: Aaron Patterson To: rubyonrail...@googlegroups.com, oss-se...@lists.openwall.com, seca...@redhat.com Subject: Data Injection Vulnerability in Active Record (CVE-2014-0080) Message-ID: <20140218190447.GB16793@higgins.local> MIME-Version: 1.0 Content-Type: multipart/signed; micalg=pgp-sha1; protocol="application/pgp-signature"; boundary="x4pBfXISqBoDm8sr" Content-Disposition: inline User-Agent: Mutt/1.5.21 (2010-09-15) --x4pBfXISqBoDm8sr Content-Type: multipart/mixed; boundary="yVhtmJPUSI46BTXb" Content-Disposition: inline --yVhtmJPUSI46BTXb Content-Type: text/plain; charset=us-ascii Content-Disposition: inline Content-Transfer-Encoding: quoted-printable Data Injection Vulnerability in Active Record There is a data injection vulnerability in Active Record. Specially crafted strings can be used to save data in PostgreSQL array columns that m= ay not be intended. This vulnerability has been assigned the CVE identifier CVE-2014-0080. Versions Affected: 4.0.x, 4.1.0.beta1 Not affected: 3.2.x and older Fixed Versions: 4.0.3, 4.1.0.beta2 Impact ------ Specially crafted strings may be used to save data to array columns in PostgreSQL databases. This vulnerability cannot be used to delete data or execute arbitrary SQL statements, but *can* be used to add data that could have an impact on the application (such as setting an admin flag). Only arr= ay type columns in PostgreSQL are impacted. All users running an affected release should either upgrade or use one of t= he work arounds immediately. Releases -------- The FIXED releases are available at the normal locations. Workarounds ----------- To work around this issue, apply this monkey patch: ```ruby module ActiveRecord module ConnectionAdapters class PostgreSQLColumn module Cast alias :old_quote_and_escape :quote_and_escape ARRAY_ESCAPE =3D "\\" * 2 * 2 # escape the backslash twice for PG a= rrays def quote_and_escape(value) case value when "NULL", Numeric value else value =3D value.gsub(/\\/, ARRAY_ESCAPE) value.gsub!(/"/,"\\\"") "\"#{value}\"" end end end end end end ``` Patches ------- To aid users who aren't able to upgrade immediately we have provided patche= s for the two supported release series. They are in git-am format and consist of = a single changeset. * 4-1-beta-array_injection.patch - Patch for 4.1-beta series * 4-0-array_injection.patch - Patch for 4.0 series Credits ------- Thanks Godfrey Chan for reporting this! --=20 Aaron Patterson http://tenderlovemaking.com/ --yVhtmJPUSI46BTXb Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="4-0-array_injection.patch" Content-Transfer-Encoding: quoted-printable From 3eaea655a506ed035fab3d143aa918958cf52405 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 12 Feb 2014 16:22:40 -0800 Subject: [PATCH] Correctly escape PostgreSQL arrays. Thanks Godfrey Chan for reporting this! Fixes: CVE-2014-0080 --- .../lib/active_record/connection_adapters/postgresql/cast.rb | 6 ++++= +- activerecord/test/cases/adapters/postgresql/datatype_test.rb | 8 ++++= ++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/= cast.rb b/activerecord/lib/active_record/connection_adapters/postgresql/cas= t.rb index a73f0ac..eac828b 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/cast.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/cast.rb @@ -138,12 +138,16 @@ module ActiveRecord end end =20 + ARRAY_ESCAPE =3D "\\" * 2 * 2 # escape the backslash twice for P= G arrays + def quote_and_escape(value) case value when "NULL" value else - "\"#{value.gsub(/"/,"\\\"")}\"" + value =3D value.gsub(/\\/, ARRAY_ESCAPE) + value.gsub!(/"/,"\\\"") + "\"#{value}\"" end end end diff --git a/activerecord/test/cases/adapters/postgresql/datatype_test.rb b= /activerecord/test/cases/adapters/postgresql/datatype_test.rb index 1b2f5f0..6c78a51 100644 --- a/activerecord/test/cases/adapters/postgresql/datatype_test.rb +++ b/activerecord/test/cases/adapters/postgresql/datatype_test.rb @@ -184,6 +184,14 @@ _SQL assert_equal :text, @first_array.column_for_attribute(:nicknames).type end =20 + def test_array_escaping + unknown =3D %(foo\\",bar,baz,\\) + nicknames =3D ["hello_#{unknown}"] + ar =3D PostgresqlArray.create!(nicknames: nicknames, id: 100) + ar.reload + assert_equal nicknames, ar.nicknames + end + def test_data_type_of_range_types skip "PostgreSQL 9.2 required for range datatypes" unless @connection.= supports_ranges? assert_equal :daterange, @first_range.column_for_attribute(:date_range= ).type --=20 1.8.4.3 --yVhtmJPUSI46BTXb Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="4-1-beta-array_injection.patch" Content-Transfer-Encoding: quoted-printable From 6256b1de9a2d968b0d123ad6a09b33de01019ae6 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 12 Feb 2014 16:22:40 -0800 Subject: [PATCH] Correctly escape PostgreSQL arrays. Thanks Godfrey Chan for reporting this! Fixes: CVE-2014-0080 --- .../lib/active_record/connection_adapters/postgresql/cast.rb | 6 ++++= +- activerecord/test/cases/adapters/postgresql/datatype_test.rb | 8 ++++= ++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/= cast.rb b/activerecord/lib/active_record/connection_adapters/postgresql/cas= t.rb index bf34f2b..bb6ea95 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/cast.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/cast.rb @@ -142,12 +142,16 @@ module ActiveRecord end end =20 + ARRAY_ESCAPE =3D "\\" * 2 * 2 # escape the backslash twice for P= G arrays + def quote_and_escape(value) case value when "NULL" value else - "\"#{value.gsub(/"/,"\\\"")}\"" + value =3D value.gsub(/\\/, ARRAY_ESCAPE) + value.gsub!(/"/,"\\\"") + "\"#{value}\"" end end =20 diff --git a/activerecord/test/cases/adapters/postgresql/datatype_test.rb b= /activerecord/test/cases/adapters/postgresql/datatype_test.rb index 04a458f..5c3a797 100644 --- a/activerecord/test/cases/adapters/postgresql/datatype_test.rb +++ b/activerecord/test/cases/adapters/postgresql/datatype_test.rb @@ -78,6 +78,14 @@ class PostgresqlDataTypeTest < ActiveRecord::TestCase PostgresqlBitString, PostgresqlOid, PostgresqlTimestampWithZone, Post= gresqlUUID].each(&:delete_all) end =20 + def test_array_escaping + unknown =3D %(foo\\",bar,baz,\\) + nicknames =3D ["hello_#{unknown}"] + ar =3D PostgresqlArray.create!(nicknames: nicknames, id: 100) + ar.reload + assert_equal nicknames, ar.nicknames + end + def test_data_type_of_array_types assert_equal :integer, @first_array.column_for_attribute(:commission_b= y_quarter).type assert_equal :text, @first_array.column_for_attribute(:nicknames).type --=20 1.8.4.3 --yVhtmJPUSI46BTXb-- --x4pBfXISqBoDm8sr Q29udGVudC1UeXBlOiBhcHBsaWNhdGlvbi9wZ3Atc2lnbmF0dXJlDQoNCi0tLS0tQkVHSU4gUEdQ IFNJR05BVFVSRS0tLS0tDQpWZXJzaW9uOiBHbnVQRyB2MS40LjEzIChEYXJ3aW4pDQoNCmlRRWNC QUVCQWdBR0JRSlRBNjdQQUFvSkVKVXhjTHkwLzYvR2Mwc0gvMDZyRGtiNHBwUFlkazBBV0hEM2ZC QWYNCisrMkljSVhSRnNXQVR6MVQ0ZkcwTlAwRVJwWGJ0ZXNQUVVuZlBYbGxwNmJqM1o1SktBVERY VjIwZDRQZWFhdUYNClpvTVV1d0lsc1BncVNVa3EvVUt5MUFxKzhIcGtTZHlWVTBsazVrVStGaVM2 b1BvMHFjbkJjMGFRbWVFQVZlWWQNCmVEWnFBVTYzV0w4YVFyU0g4OW43YzZ3RkljYWRJVmJPWTNH cHAyRTJXVXlTVWNOUjM1Ujcrbm81dHM0YXNheG0NCjYzTDFYNUt5RWl4MnBVY0Uwcy9Oa0JOa3ho T2xZNEQ4b0k3c2YwK2crRWNZY2t5NTI2S3dremZWWkNEeUlZQXMNCjlpSVduYXpnWjZNamR0RUpl NkFWWC85MFhkTkNYN2hDb2RPdjJKQjdRSkFQZVBKNUR1SnRBS1dmV1VXY0Fmaz0NCj1RMzY2DQot LS0tLUVORCBQR1AgU0lHTkFUVVJFLS0tLS0NCg== --x4pBfXISqBoDm8sr--