Commit 70de8004 authored by Philipp Hörist's avatar Philipp Hörist

Harden parser against xml vulnerabilities

parent 0589a264
Pipeline #2637 passed with stages
in 28 seconds
......@@ -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:
......
......@@ -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
......
......@@ -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
......
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()
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment