Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit c4d08db

Browse files
committed
Python: Expand XML PoC with minidom/pulldom/expat
1 parent 3affa6c commit c4d08db

1 file changed

Lines changed: 201 additions & 0 deletions

File tree

  • python/ql/test/experimental/query-tests/Security/CWE-611/dont_extract

python/ql/test/experimental/query-tests/Security/CWE-611/dont_extract/PoC.py

Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@ def fast_func():
143143

144144
# ==============================================================================
145145
import xml.sax
146+
import xml.sax.handler
146147

147148
class SimpleHandler(xml.sax.ContentHandler):
148149
def __init__(self):
@@ -447,3 +448,203 @@ def test_dtd_not_possible():
447448

448449
d = xmltodict.parse(dtd_retrieval)
449450
assert hit_dtd == False
451+
452+
# ==============================================================================
453+
import xml.dom.minidom
454+
455+
class TestMinidom:
456+
@staticmethod
457+
@expects_timeout
458+
def test_billion_laughs():
459+
xml.dom.minidom.parseString(billion_laughs)
460+
461+
@staticmethod
462+
@expects_timeout
463+
def test_quardratic_blowup():
464+
xml.dom.minidom.parseString(quadratic_blowup)
465+
466+
@staticmethod
467+
def test_ok_xml():
468+
doc = xml.dom.minidom.parseString(ok_xml)
469+
assert doc.documentElement.tagName == "test"
470+
assert doc.documentElement.childNodes[0].data == "hello world"
471+
472+
@staticmethod
473+
def test_xxe():
474+
# disabled by default
475+
doc = xml.dom.minidom.parseString(local_xxe)
476+
assert doc.documentElement.tagName == "test"
477+
assert doc.documentElement.childNodes == []
478+
479+
# but can be turned on
480+
parser = xml.sax.make_parser()
481+
parser.setFeature(xml.sax.handler.feature_external_ges, True)
482+
doc = xml.dom.minidom.parseString(local_xxe, parser=parser)
483+
assert doc.documentElement.tagName == "test"
484+
assert doc.documentElement.childNodes[0].data == "SECRET_FLAG"
485+
486+
# which also works remotely
487+
global hit_xxe
488+
hit_xxe = False
489+
490+
parser = xml.sax.make_parser()
491+
parser.setFeature(xml.sax.handler.feature_external_ges, True)
492+
_doc = xml.dom.minidom.parseString(remote_xxe, parser=parser)
493+
assert hit_xxe == True
494+
495+
@staticmethod
496+
def test_dtd():
497+
# not possible by default
498+
global hit_dtd
499+
hit_dtd = False
500+
501+
_doc = xml.dom.minidom.parseString(dtd_retrieval)
502+
assert hit_dtd == False
503+
504+
# but can be turned on
505+
parser = xml.sax.make_parser()
506+
parser.setFeature(xml.sax.handler.feature_external_ges, True)
507+
_doc = xml.dom.minidom.parseString(dtd_retrieval, parser=parser)
508+
assert hit_dtd == True
509+
510+
# ==============================================================================
511+
import xml.dom.pulldom
512+
513+
class TestPulldom:
514+
@staticmethod
515+
@expects_timeout
516+
def test_billion_laughs():
517+
doc = xml.dom.pulldom.parseString(billion_laughs)
518+
# you NEED to iterate over the items for it to take long
519+
for event, node in doc:
520+
pass
521+
522+
@staticmethod
523+
@expects_timeout
524+
def test_quardratic_blowup():
525+
doc = xml.dom.pulldom.parseString(quadratic_blowup)
526+
for event, node in doc:
527+
pass
528+
529+
@staticmethod
530+
def test_ok_xml():
531+
doc = xml.dom.pulldom.parseString(ok_xml)
532+
for event, node in doc:
533+
if event == xml.dom.pulldom.START_ELEMENT:
534+
assert node.tagName == "test"
535+
elif event == xml.dom.pulldom.CHARACTERS:
536+
assert node.data == "hello world"
537+
538+
@staticmethod
539+
def test_xxe():
540+
# disabled by default
541+
doc = xml.dom.pulldom.parseString(local_xxe)
542+
found_flag = False
543+
for event, node in doc:
544+
if event == xml.dom.pulldom.START_ELEMENT:
545+
assert node.tagName == "test"
546+
elif event == xml.dom.pulldom.CHARACTERS:
547+
if node.data == "SECRET_FLAG":
548+
found_flag = True
549+
assert found_flag == False
550+
551+
# but can be turned on
552+
parser = xml.sax.make_parser()
553+
parser.setFeature(xml.sax.handler.feature_external_ges, True)
554+
doc = xml.dom.pulldom.parseString(local_xxe, parser=parser)
555+
found_flag = False
556+
for event, node in doc:
557+
if event == xml.dom.pulldom.START_ELEMENT:
558+
assert node.tagName == "test"
559+
elif event == xml.dom.pulldom.CHARACTERS:
560+
if node.data == "SECRET_FLAG":
561+
found_flag = True
562+
assert found_flag == True
563+
564+
# which also works remotely
565+
global hit_xxe
566+
hit_xxe = False
567+
parser = xml.sax.make_parser()
568+
parser.setFeature(xml.sax.handler.feature_external_ges, True)
569+
doc = xml.dom.pulldom.parseString(remote_xxe, parser=parser)
570+
assert hit_xxe == False
571+
for event, node in doc:
572+
pass
573+
assert hit_xxe == True
574+
575+
@staticmethod
576+
def test_dtd():
577+
# not possible by default
578+
global hit_dtd
579+
hit_dtd = False
580+
581+
doc = xml.dom.pulldom.parseString(dtd_retrieval)
582+
for event, node in doc:
583+
pass
584+
assert hit_dtd == False
585+
586+
# but can be turned on
587+
parser = xml.sax.make_parser()
588+
parser.setFeature(xml.sax.handler.feature_external_ges, True)
589+
doc = xml.dom.pulldom.parseString(dtd_retrieval, parser=parser)
590+
for event, node in doc:
591+
pass
592+
assert hit_dtd == True
593+
594+
# ==============================================================================
595+
import xml.parsers.expat
596+
597+
class TestExpat:
598+
# this is the underlying parser implementation used by the rest of the Python
599+
# standard library. But people are probably not using this directly.
600+
601+
@staticmethod
602+
@expects_timeout
603+
def test_billion_laughs():
604+
parser = xml.parsers.expat.ParserCreate()
605+
parser.Parse(billion_laughs, True)
606+
607+
@staticmethod
608+
@expects_timeout
609+
def test_quardratic_blowup():
610+
parser = xml.parsers.expat.ParserCreate()
611+
parser.Parse(quadratic_blowup, True)
612+
613+
@staticmethod
614+
def test_ok_xml():
615+
char_data_recv = []
616+
def char_data_handler(data):
617+
char_data_recv.append(data)
618+
619+
parser = xml.parsers.expat.ParserCreate()
620+
parser.CharacterDataHandler = char_data_handler
621+
parser.Parse(ok_xml, True)
622+
623+
assert char_data_recv == ["hello world"]
624+
625+
@staticmethod
626+
def test_xxe():
627+
# not vuln by default
628+
char_data_recv = []
629+
def char_data_handler(data):
630+
char_data_recv.append(data)
631+
632+
parser = xml.parsers.expat.ParserCreate()
633+
parser.CharacterDataHandler = char_data_handler
634+
parser.Parse(local_xxe, True)
635+
636+
assert char_data_recv == []
637+
638+
# there might be ways to make it vuln, but I did not investigate futher.
639+
640+
@staticmethod
641+
def test_dtd():
642+
# not vuln by default
643+
global hit_dtd
644+
hit_dtd = False
645+
646+
parser = xml.parsers.expat.ParserCreate()
647+
parser.Parse(dtd_retrieval, True)
648+
assert hit_dtd == False
649+
650+
# there might be ways to make it vuln, but I did not investigate futher.

0 commit comments

Comments
 (0)