* CVE-2024-32651 - Security fix - Server Side Template Injection in Jinja2 allows Remote Command Execution * use ImmutableSandboxedEnvironment also in validationpull/2337/head
parent
1ba29655f5
commit
bd6eda696c
@ -0,0 +1,18 @@
|
||||
"""
|
||||
Safe Jinja2 render with max payload sizes
|
||||
|
||||
See https://jinja.palletsprojects.com/en/3.1.x/sandbox/#security-considerations
|
||||
"""
|
||||
|
||||
import jinja2.sandbox
|
||||
import typing as t
|
||||
import os
|
||||
|
||||
JINJA2_MAX_RETURN_PAYLOAD_SIZE = 1024 * int(os.getenv("JINJA2_MAX_RETURN_PAYLOAD_SIZE_KB", 1024 * 10))
|
||||
|
||||
|
||||
def render(template_str, **args: t.Any) -> str:
|
||||
jinja2_env = jinja2.sandbox.ImmutableSandboxedEnvironment(extensions=['jinja2_time.TimeExtension'])
|
||||
output = jinja2_env.from_string(template_str).render(args)
|
||||
return output[:JINJA2_MAX_RETURN_PAYLOAD_SIZE]
|
||||
|
@ -0,0 +1,57 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
# run from dir above changedetectionio/ dir
|
||||
# python3 -m unittest changedetectionio.tests.unit.test_jinja2_security
|
||||
|
||||
import unittest
|
||||
from changedetectionio import safe_jinja
|
||||
|
||||
|
||||
# mostly
|
||||
class TestJinja2SSTI(unittest.TestCase):
|
||||
|
||||
def test_exception(self):
|
||||
import jinja2
|
||||
|
||||
# Where sandbox should kick in
|
||||
attempt_list = [
|
||||
"My name is {{ self.__init__.__globals__.__builtins__.__import__('os').system('id') }}",
|
||||
"{{ self._TemplateReference__context.cycler.__init__.__globals__.os }}",
|
||||
"{{ self.__init__.__globals__.__builtins__.__import__('os').popen('id').read() }}",
|
||||
"{{cycler.__init__.__globals__.os.popen('id').read()}}",
|
||||
"{{joiner.__init__.__globals__.os.popen('id').read()}}",
|
||||
"{{namespace.__init__.__globals__.os.popen('id').read()}}",
|
||||
"{{ ''.__class__.__mro__[2].__subclasses__()[40]('/tmp/hello.txt', 'w').write('Hello here !') }}",
|
||||
"My name is {{ self.__init__.__globals__ }}",
|
||||
"{{ dict.__base__.__subclasses__() }}"
|
||||
]
|
||||
for attempt in attempt_list:
|
||||
with self.assertRaises(jinja2.exceptions.SecurityError):
|
||||
safe_jinja.render(attempt)
|
||||
|
||||
def test_exception_debug_calls(self):
|
||||
import jinja2
|
||||
# Where sandbox should kick in - configs and debug calls
|
||||
attempt_list = [
|
||||
"{% debug %}",
|
||||
]
|
||||
for attempt in attempt_list:
|
||||
# Usually should be something like 'Encountered unknown tag 'debug'.'
|
||||
with self.assertRaises(jinja2.exceptions.TemplateSyntaxError):
|
||||
safe_jinja.render(attempt)
|
||||
|
||||
# https://book.hacktricks.xyz/pentesting-web/ssti-server-side-template-injection/jinja2-ssti#accessing-global-objects
|
||||
def test_exception_empty_calls(self):
|
||||
import jinja2
|
||||
attempt_list = [
|
||||
"{{config}}",
|
||||
"{{ debug }}"
|
||||
"{{[].__class__}}",
|
||||
]
|
||||
for attempt in attempt_list:
|
||||
self.assertEqual(len(safe_jinja.render(attempt)), 0, f"string test '{attempt}' is correctly empty")
|
||||
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
Loading…
Reference in new issue