Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for elasticsearch output #10

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions regrip.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@
import pkg_resources
from Registry import Registry

try:
import elasticsearch_dsl
except:pass

logging.basicConfig()
l = logging.getLogger("regrippy")
l.setLevel("ERROR")
Expand Down Expand Up @@ -189,6 +193,22 @@ def load_plugin(plugin_name):
raise ValueError(f"No such plugin: {plugin_name}")


if 'elasticsearch_dsl' in sys.modules:
def create_index(index: str, override: bool):
logger = logging.getLogger()
i = elasticsearch_dsl.Index(name=index)
if i.exists():
if override:
logger.warning("deleting index '{index}'".format(index=index))
i.delete()
else:
logger.warning("adding values to existing index '{index}'".format(index=index))
return
assert not i.exists()
logger.warning("creating index '{index}'".format(index=index))
i.create()


def main():
if os.path.basename(sys.argv[0]).lower() != "regrip.py":
# Issue #5: allow selecting plugins based on argv[0]
Expand Down Expand Up @@ -260,6 +280,13 @@ def main():
parser.add_argument(
"--bodyfile", "-b", help="Force output in Bodyfile format", action="store_true"
)
if 'elasticsearch_dsl' in sys.modules:
parser.add_argument(
"--elasticsearch", help="Name of elasticsearch index to write to", type=str
)
parser.add_argument(
"--override", help="Override existing index", action="store_true"
)
parser.add_argument(
"--list", "-l", help="List available plugins", action="store_true"
)
Expand All @@ -285,6 +312,10 @@ def main():
else:
hive_names = plugin.__REGHIVE__

if args.elasticsearch:
elasticsearch_dsl.connections.create_connection(hosts=['localhost'], timeout=20)
create_index(index=args.elasticsearch, override=args.override)

for hive_name in hive_names:
hive_paths = get_hive_paths(args, hive_name)
if not hive_paths:
Expand All @@ -305,6 +336,8 @@ def main():
for result in results:
if args.bodyfile:
p.display_machine(result)
elif args.elasticsearch:
p.display_elasticsearch(result, index=args.elasticsearch)
else:
if hive_name == "NTUSER.DAT":
print(f"[.] User: {p.guess_username()}")
Expand Down
86 changes: 61 additions & 25 deletions regrippy/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
import os
import sys
from datetime import datetime

from Registry import Registry

try:
import elasticsearch_dsl
except:pass


def mactime(
md5="0",
Expand Down Expand Up @@ -129,6 +136,17 @@ def display_machine(self, result):
"""
print(mactime(name=result.path, mtime=result.mtime))

def display_elasticsearch(self, result, index):
"""Inserts a result into an elasticsearch instance

:param result: the result to display
:type result: regrip.PluginResult
"""
if 'elasticsearch_dsl' in sys.modules:
result.save(index=index)
else:
raise NotImplementedError('module elasticsearch_dsl is required to do this')

def warning(self, msg):
"""Logs a message at WARNING level"""
self.logger.warning(msg)
Expand All @@ -142,7 +160,7 @@ def info(self, msg):
self.logger.info(msg)


class PluginResult(object):
class SimplePluginResult(object):
"""A class which holds a single result of a plugin execution

:ivar dict custom: a `dict` you can use to store custom data for your result
Expand All @@ -157,27 +175,45 @@ class PluginResult(object):
:ivar value_data: the actual value data. The variable type depends on the type of the value.
"""

def __init__(self, *, key=None, value=None):
self._key = key
self._value = value

self.custom = {}

self.path = None
self.mtime = 0
self.atime = 0
self.ctime = 0
self.btime = 0
self.value_type = None
self.value_name = None
self.value_data = None

if key:
self.path = key.path()
self.mtime = int(key.timestamp().timestamp())
self.key_name = key.name()

if value:
self.value_name = value.name()
self.value_type = value.value_type_str()
self.value_data = value.value()
def __init__(self, **kwargs):
self._key = kwargs['key']
self._value = kwargs['value']

self.custom = kwargs.get('custom') or dict()

self.path = kwargs.get('path')
self.mtime = kwargs.get('mtime') or 0
self.atime = kwargs.get('atime') or 0
self.ctime = kwargs.get('ctime') or 0
self.btime = kwargs.get('btime') or 0
self.value_type = kwargs.get('value_type')
self.value_name = kwargs.get('value_name')
self.value_data = kwargs.get('value_data')

if 'key' in kwargs:
self.path = kwargs['key'].path()
self.mtime = int(kwargs['key'].timestamp().timestamp())
self.key_name = kwargs['key'].name()

if 'value' in kwargs:
self.value_name = kwargs['value'].name()
self.value_type = kwargs['value'].value_type_str()
self.value_data = kwargs['value'].value()

class ElasticPluginResult(elasticsearch_dsl.Document):
"""A class which holds a single result of a plugin execution """

timestamp = elasticsearch_dsl.Date()
mtime = elasticsearch_dsl.Date()
atime = elasticsearch_dsl.Date()
ctime = elasticsearch_dsl.Date()
btime = elasticsearch_dsl.Date()
path = elasticsearch_dsl.Text()
key_name = elasticsearch_dsl.Text()
value_type = elasticsearch_dsl.Keyword()
value_name = elasticsearch_dsl.Text()
value_data = elasticsearch_dsl.Text()
custom = elasticsearch_dsl.Nested()
plugin = elasticsearch_dsl.Keyword()

PluginResult = ElasticPluginResult if 'elasticsearch_dsl' in sys.modules else SimplePluginResult
5 changes: 4 additions & 1 deletion regrippy/plugins/regtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@ def run(self):
yield from self.dump(key)

def dump(self, key):
res = PluginResult(key=key)
res = PluginResult(
timestamp=key.timestamp(),
key_name=key.name(),
path=key.path())
res.path = self.cleanup_path(res.path)
yield res

Expand Down
24 changes: 16 additions & 8 deletions regrippy/plugins/shimcache.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,19 +22,27 @@ def run(self):
return

for entry in read_cache(key.value("AppCompatCache").value()):
res = PluginResult(key=key, value=None)
res.custom["date"] = entry[0]
if type(entry[2]) == bytes:
res.custom["path"] = entry[2].decode("utf8")
else:
res.custom["path"] = entry[2]
timestamp = datetime.strptime(entry[0], "%Y-%m-%d %H:%M:%S")
mtime = key.timestamp()
path = entry[2].decode("utf8") if type(entry[2]) == bytes else entry[2]

res = PluginResult(
timestamp=timestamp,
plugin="shimcache",
mtime=mtime,
key_name=key.name(),
path=key.path(),
custom={
'date': timestamp,
'path': path
})
yield res

def display_human(self, result):
print(result.custom["date"] + "\t" + result.custom["path"])
print(result.custom["date"].strftime("%Y-%m-%d %H:%M:%S") + "\t" + result.custom["path"])

def display_machine(self, result):
date = datetime.strptime(result.custom["date"], "%Y-%m-%d %H:%M:%S")
date = result.custom["date"]
atime = int(date.timestamp())

print(mactime(name=result.custom["path"], mtime=result.mtime, atime=atime))
4 changes: 3 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
wheel
python-registry
python-registry

elasticsearch_dsl