Philipp Hörist pushed to branch master at gajim / python-nbxmpp

Commits:
70de8004 by Philipp Hörist at 2018-11-12T20:22:14Z
Harden parser against xml vulnerabilities

- - - - -


5 changed files:

- nbxmpp/dispatcher_nb.py
- nbxmpp/simplexml.py
- + test/__init__.py
- test/runtests.py
- + test/unit/test_xml_vulnerability.py


Changes:

=====================================
nbxmpp/dispatcher_nb.py
=====================================
@@ -235,8 +235,9 @@ class XMPPDispatcher(PlugIn):
             if self.Stream and self.Stream.has_received_endtag():
                 self._owner.disconnect(self.Stream.streamError)
                 return 0
-        except ExpatError:
+        except ExpatError as error:
             log.error('Invalid XML received from server. Forcing disconnect.')
+            log.error(error)
             self._owner.Connection.disconnect()
             return 0
         except ValueError as e:


=====================================
nbxmpp/simplexml.py
=====================================
@@ -22,6 +22,7 @@ from __future__ import unicode_literals
 
 import sys
 import xml.parsers.expat
+from xml.parsers.expat import ExpatError
 import logging
 log = logging.getLogger('nbxmpp.simplexml')
 
@@ -566,10 +567,17 @@ class NodeBuilder(object):
         """
         log.debug("Preparing to handle incoming XML stream.")
         self._parser = xml.parsers.expat.ParserCreate()
+        self._parser.UseForeignDTD(False)
         self._parser.StartElementHandler = self.starttag
         self._parser.EndElementHandler = self.endtag
         self._parser.StartNamespaceDeclHandler = self.handle_namespace_start
         self._parser.CharacterDataHandler = self.handle_cdata
+        self._parser.StartDoctypeDeclHandler = self.handle_invalid_xmpp_element
+        self._parser.EntityDeclHandler = self.handle_invalid_xmpp_element
+        self._parser.CommentHandler = self.handle_invalid_xmpp_element
+        self._parser.ExternalEntityRefHandler = 
self.handle_invalid_xmpp_element
+        self._parser.AttlistDeclHandler = self.handle_invalid_xmpp_element
+        self._parser.ProcessingInstructionHandler = 
self.handle_invalid_xmpp_element
         self._parser.buffer_text = True
         self.Parse = self._parser.Parse
 
@@ -673,6 +681,10 @@ class NodeBuilder(object):
             self.data_buffer = [data]
             self.last_is_data = 1
 
+    @staticmethod
+    def handle_invalid_xmpp_element(*args):
+        raise ExpatError('Found invalid xmpp stream element: %s' % str(args))
+
     def handle_namespace_start(self, prefix, uri):
         """
         XML Parser callback. Used internally


=====================================
test/__init__.py
=====================================


=====================================
test/runtests.py
=====================================
@@ -38,6 +38,7 @@ modules = ( 'unit.test_xmpp_dispatcher_nb',
             'unit.test_xmpp_smacks',
             #'unit.test_xmpp_client_nb', gajim.org only supports TLS/SSL 
connections
             'unit.test_xmpp_transports_nb2',
+            'unit.test_xml_vulnerability',
           )
 
 nb_errors = 0


=====================================
test/unit/test_xml_vulnerability.py
=====================================
@@ -0,0 +1,73 @@
+import unittest
+from unittest.mock import Mock
+
+from nbxmpp import dispatcher_nb
+
+
+class XMLVulnerability(unittest.TestCase):
+
+    def setUp(self):
+        self.client = Mock()
+        self.client.Connection = Mock()
+        self.dispatcher = dispatcher_nb.XMPPDispatcher()
+        self.dispatcher.PlugIn(self.client)
+        self.dispatcher.StreamInit()
+
+    def tearDown(self):
+        self.dispatcher = None
+        self.client = None
+
+    def test_exponential_entity_expansion(self):
+        bomb = """<?xml version="1.0" encoding="utf-8"?>
+        <!DOCTYPE bomb [
+            <!ENTITY a "test">
+            <!ENTITY b "&a;&a;&a;&a;&a;&a;&a;&a;&a;&a;&a;&a;&a;&a;&a;&a;">
+            <!ENTITY c "&b;&b;&b;&b;&b;&b;&b;&b;&b;&b;&b;&b;&b;&b;&b;&b;">
+        ]>
+        <bomb>&c;</bomb>"""
+
+        self.dispatcher.ProcessNonBlocking(bomb)
+        self.client.Connection.disconnect.assert_called()
+
+    def test_quadratic_blowup(self):
+        bomb = """<?xml version="1.0" encoding="utf-8"?>
+        <!DOCTYPE bomb [
+        <!ENTITY a "xxxxxxx... a couple of ten thousand chars">
+        ]>
+        <bomb>&a;&a;&a;... repeat</bomb>"""
+
+        self.dispatcher.ProcessNonBlocking(bomb)
+        self.client.Connection.disconnect.assert_called()
+
+    def test_external_entity_expansion(self):
+        bomb = """<?xml version="1.0" encoding="utf-8"?>
+        <!DOCTYPE external [
+        <!ENTITY ee SYSTEM "http://www.python.org/some.xml";>
+        ]>
+        <root>&ee;</root>"""
+
+        self.dispatcher.ProcessNonBlocking(bomb)
+        self.client.Connection.disconnect.assert_called()
+
+    def test_external_local_entity_expansion(self):
+        bomb = """<?xml version="1.0" encoding="utf-8"?>
+        <stream:stream xmlns:stream='http://etherx.jabber.org/streams' 
xmlns='jabber:client'>
+        <!DOCTYPE external [
+        <!ENTITY ee SYSTEM "file:///PATH/TO/simple.xml">
+        ]>
+        <root>&ee;</root>"""
+
+        self.dispatcher.ProcessNonBlocking(bomb)
+        self.client.Connection.disconnect.assert_called()
+
+    def test_dtd_retrival(self):
+        bomb = """<?xml version="1.0" encoding="utf-8"?>
+        <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+          "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd";>
+        <html>
+            <head/>
+            <body>text</body>
+        </html>"""
+
+        self.dispatcher.ProcessNonBlocking(bomb)
+        self.client.Connection.disconnect.assert_called()



View it on GitLab: 
https://dev.gajim.org/gajim/python-nbxmpp/commit/70de8004a84ef62e0e2c25bc1c882e92a9a89e94

-- 
View it on GitLab: 
https://dev.gajim.org/gajim/python-nbxmpp/commit/70de8004a84ef62e0e2c25bc1c882e92a9a89e94
You're receiving this email because of your account on dev.gajim.org.
_______________________________________________
Commits mailing list
Commits@gajim.org
https://lists.gajim.org/cgi-bin/listinfo/commits

Reply via email to