XML Parsing Vulnerability affecting JRuby users
There is a vulnerability in the JDOM backend to ActiveSupport's XML parser. This could allow an attacker to perform a
denial of service attack or gain access to files stored on the application server. This vulnerability has been
assigned the CVE identifier CVE-2013-1856.
Versions Affected: 3.0.0 and All Later Versions when using JRuby
Not affected: Applications not using JRuby or JRuby applications not using the JDOM backend.
Fixed Versions: 3.2.13, 3.1.12
Impact
------
The ActiveSupport XML parsing functionality supports multiple pluggable backends. One backend supported for JRuby
users is ActiveSupport::XmlMini_JDOM which makes use of the javax.xml.parsers.DocumentBuilder class.
In some JVM configurations the default settings of that class can allow an attacker to construct XML which, when
parsed, will contain the contents of arbitrary URLs including files from the application server. They may also allow
for various denial of service attacks.
If you are using JRuby and have an affected JVM, you should upgrade or use one of the work arounds immediately.
Releases
--------
The 3.2.13 and 3.1.12 releases are available at the normal locations.
Workarounds
-----------
If you are unable to upgrade, you can place this code in an application initializer to prevent this issue:
ActiveSupport::XmlMini.backend="REXML"
Patches
-------
To aid users who aren't able to upgrade immediately we have provided patches for the two supported release series.
They are in git-am format and consist of a single changeset.
* 3-2-jdom.patch - Patch for 3.2 series
* 3-1-jdom.patch - Patch for 3.1 series
* 3-0-jdom.patch - Patch for 3.0 series
Please note that only the 3.1.x and 3.2.x series are supported at present. Users of earlier unsupported releases are
advised to upgrade as soon as possible as we cannot guarantee the continued availability of security fixes for
unsupported releases.
Credits
-------
Thanks to Ben Murphy for reporting this vulnerability to us and working with us to inform other affected libraries and
programming languages.
--
Aaron Patterson
http://tenderlovemaking.com/
3-0-jdom.patch
Description:
From 6d930adfeaae6cbed22d1e97ccd401bbc61407ab Mon Sep 17 00:00:00 2001
From: Ben Murphy <benmmurphy () gmail com>
Date: Fri, 8 Feb 2013 02:48:22 +0000
Subject: [PATCH] JDOM XXE Protection
Conflicts:
activesupport/test/xml_mini/jdom_engine_test.rb
Conflicts:
activesupport/test/xml_mini/jdom_engine_test.rb
---
activesupport/lib/active_support/xml_mini/jdom.rb | 6 ++++
activesupport/test/fixtures/xml/jdom_doctype.dtd | 1 +
activesupport/test/fixtures/xml/jdom_entities.txt | 1 +
activesupport/test/fixtures/xml/jdom_include.txt | 1 +
activesupport/test/xml_mini/jdom_engine_test.rb | 40 ++++++++++++++++++++---
5 files changed, 45 insertions(+), 4 deletions(-)
create mode 100644 activesupport/test/fixtures/xml/jdom_doctype.dtd
create mode 100644 activesupport/test/fixtures/xml/jdom_entities.txt
create mode 100644 activesupport/test/fixtures/xml/jdom_include.txt
diff --git a/activesupport/lib/active_support/xml_mini/jdom.rb b/activesupport/lib/active_support/xml_mini/jdom.rb
index 102b9be..0e3522f 100644
--- a/activesupport/lib/active_support/xml_mini/jdom.rb
+++ b/activesupport/lib/active_support/xml_mini/jdom.rb
@@ -38,6 +38,12 @@ module ActiveSupport
{}
else
@dbf = DocumentBuilderFactory.new_instance
+ # secure processing of java xml
+ # http://www.ibm.com/developerworks/xml/library/x-tipcfsx/index.html
+ @dbf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false)
+ @dbf.setFeature("http://xml.org/sax/features/external-general-entities", false)
+ @dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false)
+ @dbf.setFeature(javax.xml.XMLConstants::FEATURE_SECURE_PROCESSING, true)
xml_string_reader = StringReader.new(data)
xml_input_source = InputSource.new(xml_string_reader)
doc = @dbf.new_document_builder.parse(xml_input_source)
diff --git a/activesupport/test/fixtures/xml/jdom_doctype.dtd b/activesupport/test/fixtures/xml/jdom_doctype.dtd
new file mode 100644
index 0000000..8948049
--- /dev/null
+++ b/activesupport/test/fixtures/xml/jdom_doctype.dtd
@@ -0,0 +1 @@
+<!ENTITY a "external entity">
diff --git a/activesupport/test/fixtures/xml/jdom_entities.txt b/activesupport/test/fixtures/xml/jdom_entities.txt
new file mode 100644
index 0000000..0337fda
--- /dev/null
+++ b/activesupport/test/fixtures/xml/jdom_entities.txt
@@ -0,0 +1 @@
+<!ENTITY a "hello">
diff --git a/activesupport/test/fixtures/xml/jdom_include.txt b/activesupport/test/fixtures/xml/jdom_include.txt
new file mode 100644
index 0000000..239ca3a
--- /dev/null
+++ b/activesupport/test/fixtures/xml/jdom_include.txt
@@ -0,0 +1 @@
+include me
diff --git a/activesupport/test/xml_mini/jdom_engine_test.rb b/activesupport/test/xml_mini/jdom_engine_test.rb
index ae35dbc..9a4661e 100644
--- a/activesupport/test/xml_mini/jdom_engine_test.rb
+++ b/activesupport/test/xml_mini/jdom_engine_test.rb
@@ -3,10 +3,11 @@ if RUBY_PLATFORM =~ /java/
require 'active_support/xml_mini'
require 'active_support/core_ext/hash/conversions'
-
- class JDOMEngineTest < Test::Unit::TestCase
+ class JDOMEngineTest < ActiveSupport::TestCase
include ActiveSupport
+ FILES_DIR = File.dirname(__FILE__) + '/../fixtures/xml'
+
def setup
@default_backend = XmlMini.backend
XmlMini.backend = 'JDOM'
@@ -31,10 +32,41 @@ if RUBY_PLATFORM =~ /java/
assert_equal 'image/png', file.content_type
end
+ def test_not_allowed_to_expand_entities_to_files
+ attack_xml = <<-EOT
+ <!DOCTYPE member [
+ <!ENTITY a SYSTEM "file://#{FILES_DIR}/jdom_include.txt">
+ ]>
+ <member>x&a;</member>
+ EOT
+ assert_equal 'x', Hash.from_xml(attack_xml)["member"]
+ end
+
+ def test_not_allowed_to_expand_parameter_entities_to_files
+ attack_xml = <<-EOT
+ <!DOCTYPE member [
+ <!ENTITY % b SYSTEM "file://#{FILES_DIR}/jdom_entities.txt">
+ %b;
+ ]>
+ <member>x&a;</member>
+ EOT
+ assert_raise Java::OrgXmlSax::SAXParseException do
+ assert_equal 'x', Hash.from_xml(attack_xml)["member"]
+ end
+ end
+
+
+ def test_not_allowed_to_load_external_doctypes
+ attack_xml = <<-EOT
+ <!DOCTYPE member SYSTEM "file://#{FILES_DIR}/jdom_doctype.dtd">
+ <member>x&a;</member>
+ EOT
+ assert_equal 'x', Hash.from_xml(attack_xml)["member"]
+ end
+
def test_exception_thrown_on_expansion_attack
- assert_raise NativeException do
+ assert_raise Java::OrgXmlSax::SAXParseException do
attack_xml = <<-EOT
- <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE member [
<!ENTITY a "&b;&b;&b;&b;&b;&b;&b;&b;&b;&b;">
<!ENTITY b "&c;&c;&c;&c;&c;&c;&c;&c;&c;&c;">
--
1.8.1.1
3-2-jdom.patch
Description:
From 6a3ca3601258e2c1a41a4297855c008f6ab87b44 Mon Sep 17 00:00:00 2001
From: Ben Murphy <benmmurphy () gmail com>
Date: Fri, 8 Feb 2013 02:48:22 +0000
Subject: [PATCH] JDOM XXE Protection
Conflicts:
activesupport/test/xml_mini/jdom_engine_test.rb
---
activesupport/lib/active_support/xml_mini/jdom.rb | 6 ++++
activesupport/test/fixtures/xml/jdom_doctype.dtd | 1 +
activesupport/test/fixtures/xml/jdom_entities.txt | 1 +
activesupport/test/fixtures/xml/jdom_include.txt | 1 +
activesupport/test/xml_mini/jdom_engine_test.rb | 39 +++++++++++++++++++++--
5 files changed, 45 insertions(+), 3 deletions(-)
create mode 100644 activesupport/test/fixtures/xml/jdom_doctype.dtd
create mode 100644 activesupport/test/fixtures/xml/jdom_entities.txt
create mode 100644 activesupport/test/fixtures/xml/jdom_include.txt
diff --git a/activesupport/lib/active_support/xml_mini/jdom.rb b/activesupport/lib/active_support/xml_mini/jdom.rb
index 6c222b8..8d23ce4 100644
--- a/activesupport/lib/active_support/xml_mini/jdom.rb
+++ b/activesupport/lib/active_support/xml_mini/jdom.rb
@@ -38,6 +38,12 @@ module ActiveSupport
{}
else
@dbf = DocumentBuilderFactory.new_instance
+ # secure processing of java xml
+ # http://www.ibm.com/developerworks/xml/library/x-tipcfsx/index.html
+ @dbf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false)
+ @dbf.setFeature("http://xml.org/sax/features/external-general-entities", false)
+ @dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false)
+ @dbf.setFeature(javax.xml.XMLConstants::FEATURE_SECURE_PROCESSING, true)
xml_string_reader = StringReader.new(data)
xml_input_source = InputSource.new(xml_string_reader)
doc = @dbf.new_document_builder.parse(xml_input_source)
diff --git a/activesupport/test/fixtures/xml/jdom_doctype.dtd b/activesupport/test/fixtures/xml/jdom_doctype.dtd
new file mode 100644
index 0000000..8948049
--- /dev/null
+++ b/activesupport/test/fixtures/xml/jdom_doctype.dtd
@@ -0,0 +1 @@
+<!ENTITY a "external entity">
diff --git a/activesupport/test/fixtures/xml/jdom_entities.txt b/activesupport/test/fixtures/xml/jdom_entities.txt
new file mode 100644
index 0000000..0337fda
--- /dev/null
+++ b/activesupport/test/fixtures/xml/jdom_entities.txt
@@ -0,0 +1 @@
+<!ENTITY a "hello">
diff --git a/activesupport/test/fixtures/xml/jdom_include.txt b/activesupport/test/fixtures/xml/jdom_include.txt
new file mode 100644
index 0000000..239ca3a
--- /dev/null
+++ b/activesupport/test/fixtures/xml/jdom_include.txt
@@ -0,0 +1 @@
+include me
diff --git a/activesupport/test/xml_mini/jdom_engine_test.rb b/activesupport/test/xml_mini/jdom_engine_test.rb
index 7f809e7..ec81ada 100644
--- a/activesupport/test/xml_mini/jdom_engine_test.rb
+++ b/activesupport/test/xml_mini/jdom_engine_test.rb
@@ -3,9 +3,11 @@ if RUBY_PLATFORM =~ /java/
require 'active_support/xml_mini'
require 'active_support/core_ext/hash/conversions'
- class JDOMEngineTest < Test::Unit::TestCase
+ class JDOMEngineTest < ActiveSupport::TestCase
include ActiveSupport
+ FILES_DIR = File.dirname(__FILE__) + '/../fixtures/xml'
+
def setup
@default_backend = XmlMini.backend
XmlMini.backend = 'JDOM'
@@ -30,10 +32,41 @@ if RUBY_PLATFORM =~ /java/
assert_equal 'image/png', file.content_type
end
+ def test_not_allowed_to_expand_entities_to_files
+ attack_xml = <<-EOT
+ <!DOCTYPE member [
+ <!ENTITY a SYSTEM "file://#{FILES_DIR}/jdom_include.txt">
+ ]>
+ <member>x&a;</member>
+ EOT
+ assert_equal 'x', Hash.from_xml(attack_xml)["member"]
+ end
+
+ def test_not_allowed_to_expand_parameter_entities_to_files
+ attack_xml = <<-EOT
+ <!DOCTYPE member [
+ <!ENTITY % b SYSTEM "file://#{FILES_DIR}/jdom_entities.txt">
+ %b;
+ ]>
+ <member>x&a;</member>
+ EOT
+ assert_raise Java::OrgXmlSax::SAXParseException do
+ assert_equal 'x', Hash.from_xml(attack_xml)["member"]
+ end
+ end
+
+
+ def test_not_allowed_to_load_external_doctypes
+ attack_xml = <<-EOT
+ <!DOCTYPE member SYSTEM "file://#{FILES_DIR}/jdom_doctype.dtd">
+ <member>x&a;</member>
+ EOT
+ assert_equal 'x', Hash.from_xml(attack_xml)["member"]
+ end
+
def test_exception_thrown_on_expansion_attack
- assert_raise NativeException do
+ assert_raise Java::OrgXmlSax::SAXParseException do
attack_xml = <<-EOT
- <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE member [
<!ENTITY a "&b;&b;&b;&b;&b;&b;&b;&b;&b;&b;">
<!ENTITY b "&c;&c;&c;&c;&c;&c;&c;&c;&c;&c;">
--
1.8.1.1
3-1-jdom.patch
Description:
From ccb5d375b8ca65a34bfad29227f02170564d1d83 Mon Sep 17 00:00:00 2001
From: Ben Murphy <benmmurphy () gmail com>
Date: Fri, 8 Feb 2013 02:48:22 +0000
Subject: [PATCH] JDOM XXE Protection
Conflicts:
activesupport/test/xml_mini/jdom_engine_test.rb
---
activesupport/lib/active_support/xml_mini/jdom.rb | 6 ++++
activesupport/test/fixtures/xml/jdom_doctype.dtd | 1 +
activesupport/test/fixtures/xml/jdom_entities.txt | 1 +
activesupport/test/fixtures/xml/jdom_include.txt | 1 +
activesupport/test/xml_mini/jdom_engine_test.rb | 39 +++++++++++++++++++++--
5 files changed, 45 insertions(+), 3 deletions(-)
create mode 100644 activesupport/test/fixtures/xml/jdom_doctype.dtd
create mode 100644 activesupport/test/fixtures/xml/jdom_entities.txt
create mode 100644 activesupport/test/fixtures/xml/jdom_include.txt
diff --git a/activesupport/lib/active_support/xml_mini/jdom.rb b/activesupport/lib/active_support/xml_mini/jdom.rb
index 0dee0f3..45a315e 100644
--- a/activesupport/lib/active_support/xml_mini/jdom.rb
+++ b/activesupport/lib/active_support/xml_mini/jdom.rb
@@ -38,6 +38,12 @@ module ActiveSupport
{}
else
@dbf = DocumentBuilderFactory.new_instance
+ # secure processing of java xml
+ # http://www.ibm.com/developerworks/xml/library/x-tipcfsx/index.html
+ @dbf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false)
+ @dbf.setFeature("http://xml.org/sax/features/external-general-entities", false)
+ @dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false)
+ @dbf.setFeature(javax.xml.XMLConstants::FEATURE_SECURE_PROCESSING, true)
xml_string_reader = StringReader.new(data)
xml_input_source = InputSource.new(xml_string_reader)
doc = @dbf.new_document_builder.parse(xml_input_source)
diff --git a/activesupport/test/fixtures/xml/jdom_doctype.dtd b/activesupport/test/fixtures/xml/jdom_doctype.dtd
new file mode 100644
index 0000000..8948049
--- /dev/null
+++ b/activesupport/test/fixtures/xml/jdom_doctype.dtd
@@ -0,0 +1 @@
+<!ENTITY a "external entity">
diff --git a/activesupport/test/fixtures/xml/jdom_entities.txt b/activesupport/test/fixtures/xml/jdom_entities.txt
new file mode 100644
index 0000000..0337fda
--- /dev/null
+++ b/activesupport/test/fixtures/xml/jdom_entities.txt
@@ -0,0 +1 @@
+<!ENTITY a "hello">
diff --git a/activesupport/test/fixtures/xml/jdom_include.txt b/activesupport/test/fixtures/xml/jdom_include.txt
new file mode 100644
index 0000000..239ca3a
--- /dev/null
+++ b/activesupport/test/fixtures/xml/jdom_include.txt
@@ -0,0 +1 @@
+include me
diff --git a/activesupport/test/xml_mini/jdom_engine_test.rb b/activesupport/test/xml_mini/jdom_engine_test.rb
index 7f809e7..ec81ada 100644
--- a/activesupport/test/xml_mini/jdom_engine_test.rb
+++ b/activesupport/test/xml_mini/jdom_engine_test.rb
@@ -3,9 +3,11 @@ if RUBY_PLATFORM =~ /java/
require 'active_support/xml_mini'
require 'active_support/core_ext/hash/conversions'
- class JDOMEngineTest < Test::Unit::TestCase
+ class JDOMEngineTest < ActiveSupport::TestCase
include ActiveSupport
+ FILES_DIR = File.dirname(__FILE__) + '/../fixtures/xml'
+
def setup
@default_backend = XmlMini.backend
XmlMini.backend = 'JDOM'
@@ -30,10 +32,41 @@ if RUBY_PLATFORM =~ /java/
assert_equal 'image/png', file.content_type
end
+ def test_not_allowed_to_expand_entities_to_files
+ attack_xml = <<-EOT
+ <!DOCTYPE member [
+ <!ENTITY a SYSTEM "file://#{FILES_DIR}/jdom_include.txt">
+ ]>
+ <member>x&a;</member>
+ EOT
+ assert_equal 'x', Hash.from_xml(attack_xml)["member"]
+ end
+
+ def test_not_allowed_to_expand_parameter_entities_to_files
+ attack_xml = <<-EOT
+ <!DOCTYPE member [
+ <!ENTITY % b SYSTEM "file://#{FILES_DIR}/jdom_entities.txt">
+ %b;
+ ]>
+ <member>x&a;</member>
+ EOT
+ assert_raise Java::OrgXmlSax::SAXParseException do
+ assert_equal 'x', Hash.from_xml(attack_xml)["member"]
+ end
+ end
+
+
+ def test_not_allowed_to_load_external_doctypes
+ attack_xml = <<-EOT
+ <!DOCTYPE member SYSTEM "file://#{FILES_DIR}/jdom_doctype.dtd">
+ <member>x&a;</member>
+ EOT
+ assert_equal 'x', Hash.from_xml(attack_xml)["member"]
+ end
+
def test_exception_thrown_on_expansion_attack
- assert_raise NativeException do
+ assert_raise Java::OrgXmlSax::SAXParseException do
attack_xml = <<-EOT
- <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE member [
<!ENTITY a "&b;&b;&b;&b;&b;&b;&b;&b;&b;&b;">
<!ENTITY b "&c;&c;&c;&c;&c;&c;&c;&c;&c;&c;">
--
1.8.1.1