diff --git a/tanner/config.py b/tanner/config.py index ae33ad5e..a980a700 100644 --- a/tanner/config.py +++ b/tanner/config.py @@ -18,7 +18,7 @@ 'REDIS': {'host': 'localhost', 'port': 6379, 'poolsize': 80, 'timeout': 1}, 'EMULATORS': {'root_dir': '/opt/tanner'}, 'EMULATOR_ENABLED': {'sqli': True, 'rfi': True, 'lfi': True, 'xss': True, 'cmd_exec': True, - 'php_code_injection': True, "crlf": True}, + 'php_code_injection': True, 'php_object_injection': True, "crlf": True}, 'SQLI': {'type': 'SQLITE', 'db_name': 'tanner_db', 'host': 'localhost', 'user': 'root', 'password': 'user_pass'}, 'DOCKER': {'host_image': 'busybox:latest'}, diff --git a/tanner/emulators/base.py b/tanner/emulators/base.py index 3572eded..07832dc2 100644 --- a/tanner/emulators/base.py +++ b/tanner/emulators/base.py @@ -5,7 +5,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, crlf +from tanner.emulators import lfi, rfi, sqli, xss, cmd_exec, php_code_injection, php_object_injection, crlf from tanner.utils import patterns @@ -21,12 +21,16 @@ def __init__(self, base_dir, db_name, loop=None): 'cmd_exec': cmd_exec.CmdExecEmulator() if self.emulator_enabled['cmd_exec'] else None, 'php_code_injection': php_code_injection.PHPCodeInjection(loop) if self.emulator_enabled[ 'php_code_injection'] else None, + 'php_object_injection': php_object_injection.PHPObjectInjection(loop) if self.emulator_enabled[ + 'php_object_injection'] else None, 'crlf': crlf.CRLFEmulator() if self.emulator_enabled['crlf'] else None } - self.get_emulators = ['sqli', 'rfi', 'lfi', 'xss', 'php_code_injection', 'cmd_exec', 'crlf'] - self.post_emulators = ['sqli', 'rfi', 'lfi', 'xss', 'php_code_injection', 'cmd_exec', 'crlf'] - self.cookie_emulators = ['sqli'] + self.get_emulators = ['sqli', 'rfi', 'lfi', 'xss', 'php_code_injection', 'php_object_injection', + 'cmd_exec', 'crlf'] + self.post_emulators = ['sqli', 'rfi', 'lfi', 'xss', 'php_code_injection', 'php_object_injection', + 'cmd_exec', 'crlf'] + self.cookie_emulators = ['sqli', 'php_object_injection'] def extract_get_data(self, path): """ diff --git a/tanner/emulators/php_object_injection.py b/tanner/emulators/php_object_injection.py new file mode 100644 index 00000000..9621763b --- /dev/null +++ b/tanner/emulators/php_object_injection.py @@ -0,0 +1,43 @@ +import asyncio +import logging + +from tanner.utils.php_sandbox_helper import PHPSandboxHelper +from tanner.utils import patterns + + +class PHPObjectInjection: + def __init__(self, loop=None): + self._loop = loop if loop is not None else asyncio.get_event_loop() + self.logger = logging.getLogger('tanner.php_object_injection') + self.helper = PHPSandboxHelper(self._loop) + + async def get_injection_result(self, code): + + vul_code = "insert, $ret);" \ + "print $var[0];" \ + "$this->date = date('d-m-y');" \ + "$this->filename = '/tmp/logs/' . $this->date;" \ + "file_put_contents($this->filename, $var[0], FILE_APPEND);" \ + "}} " \ + "$cmd = unserialize(\'%s\');" \ + "?>" % code + + object_injection_result = await self.helper.get_result(vul_code) + + return object_injection_result + + def scan(self, value): + detection = None + if patterns.PHP_OBJECT_INJECTION.match(value): + detection = dict(name='php_object_injection', order=3) + return detection + + async def handle(self, attack_params): + result = await self.get_injection_result(attack_params[0]['value']) + if not result or 'stdout' not in result: + return dict(status_code=504) + return dict(value=result['stdout'], page=False) diff --git a/tanner/utils/patterns.py b/tanner/utils/patterns.py index fd225178..ee424a1a 100644 --- a/tanner/utils/patterns.py +++ b/tanner/utils/patterns.py @@ -10,6 +10,7 @@ r'(alias|cat|cd|cp|echo|exec|find|for|grep|ifconfig|ls|man|mkdir|netstat|ping|ps|pwd|uname|wget|touch|while)' r'([^A-z:./]|\b)') PHP_CODE_INJECTION = re.compile(r'.*(;)*(echo|system|print|phpinfo)(\(.*\)).*') +PHP_OBJECT_INJECTION = re.compile(r'(^|;|{|})O:[0-9]+:') CRLF_ATTACK = re.compile(r'.*(\r\n).*') REMOTE_FILE_URL = re.compile(r'(.*(http(s){0,1}|ftp(s){0,1}):.*)') WORD_PRESS_CONTENT = re.compile(r'/wp-content/.*') diff --git a/tanner/utils/php_sandbox_helper.py b/tanner/utils/php_sandbox_helper.py new file mode 100644 index 00000000..63438d66 --- /dev/null +++ b/tanner/utils/php_sandbox_helper.py @@ -0,0 +1,27 @@ +import logging +import asyncio +import aiohttp +from tanner import config + + +class PHPSandboxHelper: + def __init__(self, loop): + self.logger = logging.getLogger('tanner.php_sandbox_helper.PHPSandboxHelper') + self._loop = loop if loop is not None else asyncio.get_event_loop() + + async def get_result(self, code): + result = None + + phpox_address = 'http://{host}:{port}'.format(host=config.TannerConfig.get('PHPOX', 'host'), + port=config.TannerConfig.get('PHPOX', 'port') + ) + + try: + async with aiohttp.ClientSession(loop=self._loop) as session: + async with session.post(phpox_address, data=code) as resp: + result = await resp.json() + except aiohttp.ClientError as client_error: + self.logger.error('Error during connection to php sandbox %s', client_error) + finally: + await session.close() + return result