diff --git a/docker/tanner/Dockerfile b/docker/tanner/Dockerfile index 7d33af2d..d2fdba42 100644 --- a/docker/tanner/Dockerfile +++ b/docker/tanner/Dockerfile @@ -19,7 +19,7 @@ RUN sed -i 's/dl-cdn/dl-2/g' /etc/apk/repositories && \ musl-dev \ python3-dev && \ # Setup Tanner - git clone --depth=1 https://github.com/mushorg/tanner -b develop /opt/tanner && \ + git clone --depth=1 https://github.com/mushorg/tanner /opt/tanner && \ cp /root/dist/config.yaml /opt/tanner/tanner/data/ && \ cd /opt/tanner/ && \ pip3 install --no-cache-dir setuptools && \ diff --git a/tanner/data/config.yaml b/tanner/data/config.yaml index c34e2b8e..fecaf4ac 100644 --- a/tanner/data/config.yaml +++ b/tanner/data/config.yaml @@ -40,7 +40,8 @@ EMULATOR_ENABLED: lfi: True xss: True cmd_exec: True - php_code_injection: True + php_code_injection: True + twig_template_injection: True php_object_injection: True crlf: True xxe_injection: True @@ -100,3 +101,7 @@ REMOTE_DOCKERFILE: SESSIONS: delete_timeout: 300 analyze_timeout: 300 + +TWIG_PATH: + autoloader: "path/of/Autoloader.php" + stringloader: "path/of/StringLoader.php" diff --git a/tanner/emulators/base.py b/tanner/emulators/base.py index 09741b66..1b3e9448 100644 --- a/tanner/emulators/base.py +++ b/tanner/emulators/base.py @@ -6,7 +6,7 @@ from tanner import __version__ as tanner_version from tanner.config import TannerConfig from tanner.emulators import lfi, rfi, sqli, xss, cmd_exec, php_code_injection, php_object_injection, crlf, \ - xxe_injection, template_injection # noqa + xxe_injection, template_injection, twig_template_injection # noqa from tanner.utils import patterns @@ -22,7 +22,8 @@ def __init__(self, base_dir, db_name, loop=None): 'php_object_injection': TannerConfig.get('EMULATOR_ENABLED', 'php_object_injection'), 'crlf': TannerConfig.get('EMULATOR_ENABLED', 'crlf'), 'xxe_injection': TannerConfig.get('EMULATOR_ENABLED', 'xxe_injection'), - 'template_injection': TannerConfig.get('EMULATOR_ENABLED', 'template_injection') + 'template_injection': TannerConfig.get('EMULATOR_ENABLED', 'template_injection'), + 'twig_template_injection': TannerConfig.get('EMULATOR_ENABLED', 'twig_template_injection') } self.emulators = { @@ -39,13 +40,15 @@ def __init__(self, base_dir, db_name, loop=None): 'crlf': crlf.CRLFEmulator() if self.emulator_enabled['crlf'] else None, 'xxe_injection': xxe_injection.XXEInjection(loop) if self.emulator_enabled['xxe_injection'] else None, 'template_injection': template_injection.TemplateInjection(loop) if - self.emulator_enabled['template_injection'] else None + self.emulator_enabled['template_injection'] else None, + 'twig_template_injection': twig_template_injection.TwigTemplateInjection(loop) if + self.emulator_enabled['twig_template_injection'] else None } self.get_emulators = ['sqli', 'rfi', 'lfi', 'xss', 'php_code_injection', 'php_object_injection', - 'cmd_exec', 'crlf', 'xxe_injection', 'template_injection'] + 'cmd_exec', 'crlf', 'xxe_injection', 'template_injection', 'twig_template_injection'] self.post_emulators = ['sqli', 'rfi', 'lfi', 'xss', 'php_code_injection', 'php_object_injection', - 'cmd_exec', 'crlf', 'xxe_injection', 'template_injection'] + 'cmd_exec', 'crlf', 'xxe_injection', 'template_injection', 'twig_template_injection'] self.cookie_emulators = ['sqli', 'php_object_injection'] def extract_get_data(self, path): diff --git a/tanner/emulators/twig_template_injection.py b/tanner/emulators/twig_template_injection.py new file mode 100644 index 00000000..87dd3553 --- /dev/null +++ b/tanner/emulators/twig_template_injection.py @@ -0,0 +1,66 @@ +import asyncio +import logging + +from tanner.config import TannerConfig +from tanner.utils.php_sandbox_helper import PHPSandboxHelper +from tanner.utils import patterns + + +class TwigTemplateInjection: + def __init__(self, loop=None): + self._loop = loop if loop is not None else asyncio.get_event_loop() + self.logger = logging.getLogger("tanner.twig_template_injection") + self.helper = PHPSandboxHelper(self._loop) + self.autoloader = TannerConfig.get("TWIG_PATH", "autoloader") + self.stringloader = TannerConfig.get("TWIG_PATH", "stringloader") + + async def get_injection_result(self, code): + """ + Injects the code from attacker to vulnerable code and get emulation results from php sandbox. + :param code (str): Input payload from attacker + :return: twig_injection_result (dict): file_md5 (md5 hash), stdout (injection result) as keys. + """ + + vul_code = """ + addExtension(new \\Twig\\Extension\\StringLoaderExtension()); + $payload = "%s"; + $result = $twig->render($payload); + echo $result; + ?> + """ % ( + self.autoloader, + self.stringloader, + code, + ) + + self.logger.debug( + "Getting the twig injection results of %s from php sandbox", code + ) + twig_injection_result = await self.helper.get_result(vul_code) + + return twig_injection_result + + def scan(self, value): + """ + Scans the input payload to detect attack using regex + :param value (str): code from attacker + :return: detection (dict): name (attack name), order (attack order) as keys + """ + + detection = None + if patterns.TEMPLATE_INJECTION_TORNADO.match(value): + detection = dict(name="twig_template_injection", order=3) + return detection + + async def handle(self, attack_params, session=None): + attack_params[0]['value'] = unquote(attack_params[0]['value']) + result = await self.get_injection_result(attack_params[0]['value']) + return result