Browse Source

fix cve CVE Request -- XXE part

main
Mathieu Le Marec - Pasquet 10 years ago
parent
commit
a38656817c
10 changed files with 185 additions and 49 deletions
  1. +5
    -0
      .gitignore
  2. +2
    -0
      CHANGES.txt
  3. +10
    -0
      MANIFEST.in
  4. +31
    -19
      src/SOAPpy/Parser.py
  5. +39
    -30
      src/SOAPpy/Server.py
  6. +11
    -0
      tests/testsclient.py
  7. +13
    -0
      tests/testserver.py
  8. +21
    -0
      tests/vul_etcpasswd.txt
  9. +32
    -0
      vul_lol.txt
  10. +21
    -0
      vul_ok.txt

+ 5
- 0
.gitignore View File

@@ -1,2 +1,7 @@
*.egg-info
MANIFEST.in
build/
dist/
setup.cfg
venv/
*.pyc

+ 2
- 0
CHANGES.txt View File

@@ -3,6 +3,8 @@ CHANGELOG

0.12.6 (unreleased)
-----------------------
- fix cve CVE Request ---- SOAPpy 0.12.5 Multiple Vulnerabilities -- XXE part
[kiorky]
- Remove dependency on fpconst.
- adding maptype [Sandro Knauß]
- Support / (and other reserved characters) in the password. [Ionut Turturica]


+ 10
- 0
MANIFEST.in View File

@@ -0,0 +1,10 @@
include *.txt *.cfg *.rst
recursive-include validate *
recursive-include contrib *
recursive-include src *
recursive-include tests *
recursive-include tools *
recursive-include zope *
recursive-include docs *
recursive-include bid *
global-exclude *pyc

+ 31
- 19
src/SOAPpy/Parser.py View File

@@ -1,4 +1,5 @@
# SOAPpy modules
import traceback
from Config import Config
from Types import *
from NS import NS
@@ -7,6 +8,10 @@ from Utilities import *
import string
import xml.sax
from wstools.XMLname import fromXMLname
try:
from cStringIO import StringIO
except ImportError:
from StringIO import StringIO

try: from M2Crypto import SSL
except: pass
@@ -93,7 +98,7 @@ class SOAPParser(xml.sax.handler.ContentHandler):
elif prefix:
tag = prefix + ":" + tag
return tag
# Workaround two sax bugs
if name[0] == None and name[1][0] == ' ':
name = (None, name[1][1:])
@@ -127,7 +132,7 @@ class SOAPParser(xml.sax.handler.ContentHandler):
elif self._next == "":
raise Error, "expected nothing, " \
"got `%s'" % toStr( name )

if len(self._stack) == 2:
rules = self._rules
@@ -275,7 +280,7 @@ class SOAPParser(xml.sax.handler.ContentHandler):
null = 1

# check for nil=1, but watch out for string values
try:
try:
null = int(null)
except ValueError, e:
if not e[0].startswith("invalid literal for int()"):
@@ -312,7 +317,7 @@ class SOAPParser(xml.sax.handler.ContentHandler):
#print "cur.kind=", cur.kind
#print "cur.rules=", cur.rules
#print "\n"

if cur.rules != None:
rule = cur.rules
@@ -374,7 +379,7 @@ class SOAPParser(xml.sax.handler.ContentHandler):
# print "ns:", ns
# print "attrs:", attrs
# print "kind:", kind

if kind == None:
# If the current item's container is an array, it will
@@ -863,7 +868,7 @@ class SOAPParser(xml.sax.handler.ContentHandler):
# print " attrs=", attrs
# print " t[0]=", t[0]
# print " t[1]=", t[1]
# print " in?", t[0] in NS.EXSD_L

if t[0] in NS.EXSD_L:
@@ -933,11 +938,11 @@ class SOAPParser(xml.sax.handler.ContentHandler):
elif d == 0:
if type(self.zerofloatre) == StringType:
self.zerofloatre = re.compile(self.zerofloatre)
if self.zerofloatre.search(s):
raise UnderflowError, "invalid %s: %s" % (t[1], s)
return d
if t[1] in ("dateTime", "date", "timeInstant", "time"):
return self.convertDateTime(d, t[1])
if t[1] == "decimal":
@@ -1031,14 +1036,17 @@ class SOAPParser(xml.sax.handler.ContentHandler):
################################################################################
# call to SOAPParser that keeps all of the info
################################################################################
def _parseSOAP(xml_str, rules = None):
try:
from cStringIO import StringIO
except ImportError:
from StringIO import StringIO
class EmptyEntityResolver(xml.sax.handler.EntityResolver):
def resolveEntity(self, publicId, systemId):
return StringIO("<?xml version='1.0' encoding='UTF-8'?>")


def _parseSOAP(xml_str, rules = None, ignore_ext=None):
if ignore_ext is None:
ignore_ext = False

parser = xml.sax.make_parser()
t = SOAPParser(rules = rules)
t = SOAPParser(rules=rules)
parser.setContentHandler(t)
e = xml.sax.handler.ErrorHandler()
parser.setErrorHandler(e)
@@ -1046,15 +1054,19 @@ def _parseSOAP(xml_str, rules = None):
inpsrc = xml.sax.xmlreader.InputSource()
inpsrc.setByteStream(StringIO(xml_str))

# disable by default entity loading on posted content
if ignore_ext:
parser.setEntityResolver(EmptyEntityResolver())
# turn on namespace mangeling
parser.setFeature(xml.sax.handler.feature_namespaces,1)
parser.setFeature(xml.sax.handler.feature_namespaces, 1)

try:
parser.parse(inpsrc)
except xml.sax.SAXParseException, e:
parser._parser = None
print traceback.format_exc()
raise e
return t

################################################################################
@@ -1068,9 +1080,9 @@ def parseSOAP(xml_str, attrs = 0):
return t.body


def parseSOAPRPC(xml_str, header = 0, body = 0, attrs = 0, rules = None):
def parseSOAPRPC(xml_str, header = 0, body = 0, attrs = 0, rules = None, ignore_ext=None):

t = _parseSOAP(xml_str, rules = rules)
t = _parseSOAP(xml_str, rules = rules, ignore_ext=ignore_ext)
p = t.body[0]

# Empty string, for RPC this translates into a void
@@ -1080,7 +1092,7 @@ def parseSOAPRPC(xml_str, header = 0, body = 0, attrs = 0, rules = None):
if k[0] != "_":
name = k
p = structType(name)
if header or body or attrs:
ret = (p,)
if header : ret += (t.header,)


+ 39
- 30
src/SOAPpy/Server.py View File

@@ -188,8 +188,9 @@ class SOAPServerBase:
if namespace[0] == ":": namespace = namespace[1:]

del self.objmap[namespace]
class SOAPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
ignore_ext = True
def version_string(self):
return '<a href="http://pywebsvcs.sf.net">' + \
'SOAPpy ' + __version__ + '</a> (Python ' + \
@@ -204,7 +205,7 @@ class SOAPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):

def do_POST(self):
global _contexts
status = 500
try:
if self.server.config.dumpHeadersIn:
@@ -226,7 +227,7 @@ class SOAPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
debugFooter(s)

(r, header, body, attrs) = \
parseSOAPRPC(data, header = 1, body = 1, attrs = 1)
parseSOAPRPC(data, header = 1, body = 1, attrs = 1, ignore_ext=self.ignore_ext)

method = r._name
args = r._aslist()
@@ -252,8 +253,8 @@ class SOAPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
ordered_args = {}
named_args = {}

if Config.specialArgs:
if Config.specialArgs:
for (k,v) in kw.items():

if k[0]=="v":
@@ -271,13 +272,13 @@ class SOAPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
# if r._ns is specified use it, if not check for
# a path, if it's specified convert it and use it as the
# namespace. If both are specified, use r._ns.
ns = r._ns

if len(self.path) > 1 and not ns:
ns = self.path.replace("/", ":")
if ns[0] == ":": ns = ns[1:]
# authorization method
a = None

@@ -291,9 +292,9 @@ class SOAPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
#print '<-> Argument Matching Yielded:'
#print '<-> Ordered Arguments:' + str(ordered_args)
#print '<-> Named Arguments :' + str(named_args)
resp = ""
# For fault messages
if ns:
nsmethod = "%s:%s" % (ns, method)
@@ -318,7 +319,7 @@ class SOAPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
# there are none, because the split will return
# [method]
f = self.server.objmap[ns]
# Look for the authorization method
if self.server.config.authMethod != None:
authmethod = self.server.config.authMethod
@@ -359,7 +360,7 @@ class SOAPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
if "SOAPAction".lower() not in self.headers.keys() or \
self.headers["SOAPAction"] == "\"\"":
self.headers["SOAPAction"] = method
thread_id = thread.get_ident()
_contexts[thread_id] = SOAPContext(header, body,
attrs, data,
@@ -374,11 +375,11 @@ class SOAPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
raise faultType("%s:Server" % NS.ENV_T,
"Authorization failed.",
"%s" % nsmethod)
# If it's wrapped, some special action may be needed
if isinstance(f, MethodSig):
c = None
if f.context: # retrieve context object
c = _contexts[thread_id]

@@ -389,9 +390,9 @@ class SOAPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
elif f.keywords:
# This is lame, but have to de-unicode
# keywords
strkw = {}
for (k, v) in kw.items():
strkw[str(k)] = v
if c:
@@ -408,7 +409,7 @@ class SOAPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
else:
fr = apply(f, args, {})

if type(fr) == type(self) and \
isinstance(fr, voidType):
resp = buildSOAP(kw = {'%sResponse' % method: fr},
@@ -423,7 +424,7 @@ class SOAPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
# Clean up _contexts
if _contexts.has_key(thread_id):
del _contexts[thread_id]
except Exception, e:
import traceback
info = sys.exc_info()
@@ -558,7 +559,7 @@ class SOAPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
self.connection.shutdown(1)

def do_GET(self):
#print 'command ', self.command
#print 'path ', self.path
#print 'request_version', self.request_version
@@ -567,7 +568,7 @@ class SOAPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
#print ' maintype', self.headers.maintype
#print ' subtype ', self.headers.subtype
#print ' params ', self.headers.plist
path = self.path.lower()
if path.endswith('wsdl'):
method = 'wsdl'
@@ -575,13 +576,13 @@ class SOAPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
if self.server.funcmap.has_key(namespace) \
and self.server.funcmap[namespace].has_key(method):
function = self.server.funcmap[namespace][method]
else:
else:
if namespace in self.server.objmap.keys():
function = self.server.objmap[namespace]
l = method.split(".")
for i in l:
function = getattr(function, i)
if function:
self.send_response(200)
self.send_header("Content-type", 'text/plain')
@@ -589,7 +590,7 @@ class SOAPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
response = apply(function, ())
self.wfile.write(str(response))
return
# return error
self.send_response(200)
self.send_header("Content-type", 'text/html')
@@ -614,13 +615,17 @@ class SOAPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):

</body>''')

def log_message(self, format, *args):
if self.server.log:
BaseHTTPServer.BaseHTTPRequestHandler.\
log_message (self, format, *args)


class SOAPInsecureRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
'''Request handler that does load POSTed doctypes'''
ignore_ext = False


class SOAPServer(SOAPServerBase, SocketServer.TCPServer):

@@ -679,19 +684,19 @@ class ThreadingSOAPServer(SOAPServerBase, SocketServer.ThreadingTCPServer):
if hasattr(socket, "AF_UNIX"):

class SOAPUnixSocketServer(SOAPServerBase, SocketServer.UnixStreamServer):
def __init__(self, addr = 8000,
RequestHandler = SOAPRequestHandler, log = 0, encoding = 'UTF-8',
config = Config, namespace = None, ssl_context = None):
# Test the encoding, raising an exception if it's not known
if encoding != None:
''.encode(encoding)
if ssl_context != None and not config.SSLserver:
raise AttributeError, \
"SSL server not supported by this Python installation"
self.namespace = namespace
self.objmap = {}
self.funcmap = {}
@@ -699,8 +704,12 @@ if hasattr(socket, "AF_UNIX"):
self.encoding = encoding
self.config = config
self.log = log
self.allow_reuse_address= 1
SocketServer.UnixStreamServer.__init__(self, str(addr), RequestHandler)






+ 11
- 0
tests/testsclient.py View File

@@ -0,0 +1,11 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
__docformat__ = 'restructuredtext en'
#!/usr/bin/env python
# coding:utf-8

from SOAPpy import SOAPProxy
server = SOAPProxy("http://localhost:8080/")
print server.echo("Hello world")

# vim:set et sts=4 ts=4 tw=80:

+ 13
- 0
tests/testserver.py View File

@@ -0,0 +1,13 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
__docformat__ = 'restructuredtext en'
#!/usr/bin/env python
# encoding:utf-8
from SOAPpy import SOAPServer
def echo(s):
return s # repeats a string twice
server = SOAPServer(("0.0.0.0", 8080))
server.registerFunction(echo)
server.serve_forever()

# vim:set et sts=4 ts=4 tw=80:

+ 21
- 0
tests/vul_etcpasswd.txt View File

@@ -0,0 +1,21 @@
POST / HTTP/1.0
Host: localhost:8080
User-agent: SOAPpy 0.12.0 (pywebsvcs.sf.net)
Content-type: text/xml; charset="UTF-8"
Content-length: 10000000
SOAPAction: "echo"

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE v1 [ <!ENTITY xxe SYSTEM "file:///etc/passwd" > ]>
<SOAP-ENV:Envelope
SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance"
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsd="http://www.w3.org/1999/XMLSchema" >
<SOAP-ENV:Body>
<echo SOAP-ENC:root="1">
<v1 xsi:type="xsd:string">&xxe; aaa</v1>
</echo>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>

+ 32
- 0
vul_lol.txt View File

@@ -0,0 +1,32 @@
POST / HTTP/1.0
Host: localhost:8080
User-agent: SOAPpy 0.12.0 (pywebsvcs.sf.net)
Content-type: text/xml; charset="UTF-8"
Content-length: 10000000
SOAPAction: "echo"

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE v1 [
<!ENTITY lol "lol">
<!ENTITY lol2 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;">
<!ENTITY lol3 "&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;">
<!ENTITY lol4 "&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;">
<!ENTITY lol5 "&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;">
<!ENTITY lol6 "&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;">
<!ENTITY lol7 "&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;">
<!ENTITY lol8 "&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;">
<!ENTITY lol9 "&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;">
]>
<SOAP-ENV:Envelope
SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance"
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsd="http://www.w3.org/1999/XMLSchema"
>
<SOAP-ENV:Body>
<echo SOAP-ENC:root="1">
<v1 xsi:type="xsd:string">&lol9;</v1>
</echo>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>

+ 21
- 0
vul_ok.txt View File

@@ -0,0 +1,21 @@
POST / HTTP/1.0
Host: localhost:8080
User-agent: SOAPpy 0.12.0 (pywebsvcs.sf.net)
Content-type: text/xml; charset="UTF-8"
Content-length: 484
SOAPAction: "echo"

<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope
SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance"
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsd="http://www.w3.org/1999/XMLSchema"
>
<SOAP-ENV:Body>
<echo SOAP-ENC:root="1">
<v1 xsi:type="xsd:string">Hello world</v1>
</echo>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>

Loading…
Cancel
Save