diff --git a/README.md b/README.md index 7fd4382..322a7ee 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,7 @@ Wordpot is a Wordpress honeypot which detects probes for plugins, themes, timthu --plugins=PLUGINS Fake installed plugins --themes=THEMES Fake installed plugins --ver=VERSION Wordpress version + --server=SERVER Custom server header To configure the honeypot you can edit the config file `wordpot.conf` or provide arguments trough the command line interface as shown above. diff --git a/requirements.txt b/requirements.txt index c41fb80..3f80779 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,3 @@ Flask==0.10.1 -e git+https://github.com/threatstream/hpfeeds/#egg=hpfeeds-dev +psycopg2 diff --git a/wordpot.conf b/wordpot.conf index 827d737..91baead 100644 --- a/wordpot.conf +++ b/wordpot.conf @@ -38,3 +38,11 @@ HPFEEDS_PORT = 10000 HPFEEDS_IDENT = 'wordpot' HPFEEDS_SECRET = 'wordpot-pass' HPFEEDS_TOPIC = 'wordpot.events' + +POSTGRESQL_ENABLED = True +POSTGRESQL_DATABASE = 'database' +POSTGRESQL_USER = 'username' +POSTGRESQL_PASSWORD = 'password' +POSTGRESQL_HOST = '127.0.0.1' +POSTGRESQL_PORT = 5432 + diff --git a/wordpot/__init__.py b/wordpot/__init__.py index ea9eab0..41d2e0e 100644 --- a/wordpot/__init__.py +++ b/wordpot/__init__.py @@ -98,6 +98,109 @@ def check_options(): else: LOGGER.warn('hpfeeds is disabled') +if app.config['POSTGRESQL_ENABLED']: + import psycopg2 + print 'Connecting to postgresql {}:{}'.format(app.config['POSTGRESQL_HOST'], app.config['POSTGRESQL_PORT']) + app.config['postgresql_dbh'] = psycopg2.connect(database=app.config['POSTGRESQL_DATABASE'], user=app.config['POSTGRESQL_USER'], password=app.config['POSTGRESQL_PASSWORD'], host=app.config['POSTGRESQL_HOST'], port=app.config['POSTGRESQL_PORT']) + cursor = app.config['postgresql_dbh'].cursor() + cursor.execute("""CREATE TABLE IF NOT EXISTS + login_attempts ( + id SERIAL PRIMARY KEY, + plugin TEXT, + source_ip TEXT, + source_port INTEGER, + dest_host TEXT, + dest_port INTEGER, + username TEXT, + password TEXT, + user_agent TEXT, + url TEXT, + timestamp TIMESTAMP + );""") + cursor.execute("""CREATE TABLE IF NOT EXISTS + login_page_probes ( + id SERIAL PRIMARY KEY, + plugin TEXT, + source_ip TEXT, + source_port INTEGER, + dest_host TEXT, + dest_port INTEGER, + user_agent TEXT, + url TEXT, + timestamp TIMESTAMP + );""") + cursor.execute("""CREATE TABLE IF NOT EXISTS + file_probes ( + id SERIAL PRIMARY KEY, + plugin TEXT, + source_ip TEXT, + source_port INTEGER, + dest_host TEXT, + dest_port INTEGER, + probed_filename TEXT, + user_agent TEXT, + url TEXT, + timestamp TIMESTAMP + );""") + cursor.execute("""CREATE TABLE IF NOT EXISTS + plugins_probes ( + id SERIAL PRIMARY KEY, + plugin TEXT, + source_ip TEXT, + source_port INTEGER, + dest_host TEXT, + dest_port INTEGER, + probed_plugin TEXT, + path TEXT, + user_agent TEXT, + url TEXT, + timestamp TIMESTAMP + );""") + cursor.execute("""CREATE TABLE IF NOT EXISTS + themes_probes ( + id SERIAL PRIMARY KEY, + plugin TEXT, + source_ip TEXT, + source_port INTEGER, + dest_host TEXT, + dest_port INTEGER, + probed_theme TEXT, + path TEXT, + user_agent TEXT, + url TEXT, + timestamp TIMESTAMP + );""") + cursor.execute("""CREATE TABLE IF NOT EXISTS + author_probes ( + id SERIAL PRIMARY KEY, + plugin TEXT, + source_ip TEXT, + source_port INTEGER, + dest_host TEXT, + dest_port INTEGER, + probed_author TEXT, + user_agent TEXT, + url TEXT, + timestamp TIMESTAMP + );""") + cursor.execute("""CREATE TABLE IF NOT EXISTS + connections ( + id SERIAL PRIMARY KEY, + source_ip TEXT, + source_port INTEGER, + dest_host TEXT, + dest_port INTEGER, + user_agent TEXT, + url TEXT, + method TEXT, + path TEXT, + headers TEXT, + timestamp TIMESTAMP + );""") + app.config['postgresql_dbh'].commit() +else: + LOGGER.warn('postgresql is disabled') + # ------------------------ # Add Custom Server Header diff --git a/wordpot/plugins/badlogin.py b/wordpot/plugins/badlogin.py index a2bb04c..a43c54f 100644 --- a/wordpot/plugins/badlogin.py +++ b/wordpot/plugins/badlogin.py @@ -1,5 +1,7 @@ from wordpot.plugins_manager import BasePlugin +import datetime + class Plugin(BasePlugin): def run(self): # Initialize template vars dict @@ -17,10 +19,13 @@ def run(self): password = self.inputs['request'].form['pwd'] self.outputs['log'] = '%s tried to login with username %s and password %s' % (origin, username, password) self.outputs['log_json'] = self.to_json_log(username=username, password=password, plugin='badlogin') + self.outputs['log_postgresql_login_attempt'] = {"source_ip": self.inputs['request'].remote_addr,"source_port": self.inputs['request'].environ['REMOTE_PORT'],"dest_host": self.inputs['request'].environ['SERVER_NAME'],"dest_port": self.inputs['request'].environ['SERVER_PORT'],"username": self.inputs['request'].form['log'],"password": self.inputs['request'].form['pwd'],"user_agent": self.inputs['request'].user_agent.string,"url": self.inputs['request'].url,"timestamp": str(datetime.datetime.now())} + self.outputs['template_vars']['BADLOGIN'] = True self.outputs['template'] = 'wp-login.html' else: self.outputs['log'] = '%s probed for the login page' % origin + self.outputs['log_postgresql_login_page_probes'] = {"source_ip": self.inputs['request'].remote_addr,"source_port": self.inputs['request'].environ['REMOTE_PORT'],"dest_host": self.inputs['request'].environ['SERVER_NAME'],"dest_port": self.inputs['request'].environ['SERVER_PORT'],"user_agent": self.inputs['request'].user_agent.string,"url": self.inputs['request'].url,"timestamp": str(datetime.datetime.now())} self.outputs['template_vars']['BADLOGIN'] = False self.outputs['template'] = 'wp-login.html' diff --git a/wordpot/plugins/commonfiles.py b/wordpot/plugins/commonfiles.py index 803df1f..b1ec8a4 100644 --- a/wordpot/plugins/commonfiles.py +++ b/wordpot/plugins/commonfiles.py @@ -1,5 +1,7 @@ from wordpot.plugins_manager import BasePlugin +import datetime + class Plugin(BasePlugin): def run(self): # Initialize template vars dict @@ -20,6 +22,7 @@ def run(self): if filename in common: self.outputs['log'] = '%s probed for: %s' % (origin, filename) self.outputs['log_json'] = self.to_json_log(filename=filename, plugin='commonfiles') + self.outputs['log_postgresql_file_probes'] = {"source_ip": self.inputs['request'].remote_addr,"source_port": self.inputs['request'].environ['REMOTE_PORT'],"dest_host": self.inputs['request'].environ['SERVER_NAME'],"dest_port": self.inputs['request'].environ['SERVER_PORT'],"probed_filename": filename,"user_agent": self.inputs['request'].user_agent.string,"url": self.inputs['request'].url,"timestamp": str(datetime.datetime.now())} self.outputs['template'] = common[filename] return diff --git a/wordpot/plugins/timthumb.py b/wordpot/plugins/timthumb.py index 9f6dc17..c526b14 100644 --- a/wordpot/plugins/timthumb.py +++ b/wordpot/plugins/timthumb.py @@ -1,6 +1,8 @@ from wordpot.plugins_manager import BasePlugin import re +import datetime + TIMTHUMB_RE = re.compile('[tim]*thumb|uploadify', re.I) class Plugin(BasePlugin): @@ -11,6 +13,12 @@ def run(self): log = '%s probed for timthumb: %s' % (self.inputs['request'].remote_addr, self.inputs['subpath']) self.outputs['log'] = log self.outputs['log_json'] = self.to_json_log(filename=self.inputs['subpath'], plugin='timthumb') + if 'theme' in self.inputs: + self.outputs['log_postgresql_themes_probes'] = {"source_ip": self.inputs['request'].remote_addr,"source_port": self.inputs['request'].environ['REMOTE_PORT'],"dest_host": self.inputs['request'].environ['SERVER_NAME'],"dest_port": self.inputs['request'].environ['SERVER_PORT'],"probed_theme": self.inputs['theme'],"path": self.inputs['subpath'],"user_agent": self.inputs['request'].user_agent.string,"url": self.inputs['request'].url,"timestamp": str(datetime.datetime.now())} + + if 'plugin' in self.inputs: + self.outputs['log_postgresql_plugins_probes'] = {"source_ip": self.inputs['request'].remote_addr,"source_port": self.inputs['request'].environ['REMOTE_PORT'],"dest_host": self.inputs['request'].environ['SERVER_NAME'],"dest_port": self.inputs['request'].environ['SERVER_PORT'],"probed_plugin": self.inputs['plugin'],"path": self.inputs['subpath'],"user_agent": self.inputs['request'].user_agent.string,"url": self.inputs['request'].url,"timestamp": str(datetime.datetime.now())} + # Template to render self.outputs['template'] = 'timthumb.html' diff --git a/wordpot/plugins/userenumeration.py b/wordpot/plugins/userenumeration.py index e0b81bf..bb87070 100644 --- a/wordpot/plugins/userenumeration.py +++ b/wordpot/plugins/userenumeration.py @@ -1,6 +1,8 @@ from wordpot.plugins_manager import BasePlugin from wordpot import app +import datetime + class Plugin(BasePlugin): def run(self): # Initialize template vars dict @@ -15,6 +17,7 @@ def run(self): if (k + 1) == int(req_args['author']): self.outputs['log'] = '%s probed author page for user: %s' % (origin, a) self.outputs['log_json'] = self.to_json_log(author=a, plugin='userenumeration') + self.outputs['log_postgresql_author_probes'] = {"source_ip": self.inputs['request'].remote_addr,"source_port": self.inputs['request'].environ['REMOTE_PORT'],"dest_host": self.inputs['request'].environ['SERVER_NAME'],"dest_port": self.inputs['request'].environ['SERVER_PORT'],"probed_author": a,"user_agent": self.inputs['request'].user_agent.string,"url": self.inputs['request'].url,"timestamp": str(datetime.datetime.now())} self.outputs['template_vars']['AUTHORPAGE'] = True self.outputs['template_vars']['CURRENTAUTHOR'] = (k+1, a) self.outputs['template'] = app.config['THEME'] + '.html' diff --git a/wordpot/views.py b/wordpot/views.py index f8547c9..0104019 100644 --- a/wordpot/views.py +++ b/wordpot/views.py @@ -5,12 +5,21 @@ from wordpot.helpers import * from wordpot.logger import LOGGER +import psycopg2 +import datetime + TEMPLATE = app.config['THEME'] + '.html' @app.route('/', methods=['GET', 'POST']) @app.route('/.', methods=['GET', 'POST']) def commons(filename=None, ext=None): + if app.config['POSTGRESQL_ENABLED']: + cursor = app.config['postgresql_dbh'].cursor() + cursor.execute("INSERT INTO connections (source_ip, source_port, dest_host, dest_port, user_agent, url, method, path, headers, timestamp) VALUES (%(remote_addr)s,%(remote_port)s,%(server_name)s,%(server_port)s,%(user_agent)s,%(url)s,%(method)s,%(path)s,%(headers)s,%(timestamp)s)", {"remote_addr": request.remote_addr, "remote_port": request.environ['REMOTE_PORT'], "server_name": request.environ['SERVER_NAME'], "server_port": request.environ['SERVER_PORT'], "user_agent": request.user_agent.string, "url": request.url, "method": request.method, "path": request.path, "headers": str(request.headers), "timestamp": str(datetime.datetime.now())}) + app.config['postgresql_dbh'].commit() + + # Plugins hook for p in pm.hook('commons'): p.start(filename=filename, ext=ext, request=request) @@ -18,6 +27,34 @@ def commons(filename=None, ext=None): LOGGER.info(p.outputs['log']) if 'log_json' in p.outputs and app.config['HPFEEDS_ENABLED']: app.config['hpfeeds_client'].publish(app.config['HPFEEDS_TOPIC'], p.outputs['log_json']) + if 'log_postgresql_login_attempt' in p.outputs and app.config['POSTGRESQL_ENABLED']: + try: + cursor = app.config['postgresql_dbh'].cursor() + cursor.execute("INSERT INTO login_attempts (plugin, source_ip, source_port, dest_host, dest_port, username, password, user_agent, url, timestamp) VALUES ('badlogin',%(source_ip)s,%(source_port)s,%(dest_host)s,%(dest_port)s,%(username)s,%(password)s,%(user_agent)s,%(url)s,%(timestamp)s)", p.outputs['log_postgresql_login_attempt']) + app.config['postgresql_dbh'].commit() + except Exception as e: + print(e) + if 'log_postgresql_login_page_probes' in p.outputs and app.config['POSTGRESQL_ENABLED']: + try: + cursor = app.config['postgresql_dbh'].cursor() + cursor.execute("INSERT INTO login_page_probes (plugin, source_ip, source_port, dest_host, dest_port, user_agent, url, timestamp) VALUES ('badlogin',%(source_ip)s,%(source_port)s,%(dest_host)s,%(dest_port)s,%(user_agent)s,%(url)s,%(timestamp)s)", p.outputs['log_postgresql_login_page_probes']) + app.config['postgresql_dbh'].commit() + except Exception as e: + print(e) + if 'log_postgresql_author_probes' in p.outputs and app.config['POSTGRESQL_ENABLED']: + try: + cursor = app.config['postgresql_dbh'].cursor() + cursor.execute("INSERT INTO author_probes (plugin, source_ip, source_port, dest_host, dest_port, probed_author, user_agent, url, timestamp) VALUES ('userenumeration',%(source_ip)s,%(source_port)s,%(dest_host)s,%(dest_port)s,%(probed_author)s,%(user_agent)s,%(url)s,%(timestamp)s)", p.outputs['log_postgresql_author_probes']) + app.config['postgresql_dbh'].commit() + except Exception as e: + print(e) + if 'log_postgresql_file_probes' in p.outputs and app.config['POSTGRESQL_ENABLED']: + try: + cursor = app.config['postgresql_dbh'].cursor() + cursor.execute("INSERT INTO file_probes (plugin, source_ip, source_port, dest_host, dest_port, probed_filename, user_agent, url, timestamp) VALUES ('commonfiles',%(source_ip)s,%(source_port)s,%(dest_host)s,%(dest_port)s,%(probed_filename)s,%(user_agent)s,%(url)s,%(timestamp)s)", p.outputs['log_postgresql_file_probes']) + app.config['postgresql_dbh'].commit() + except Exception as e: + print(e) if 'template' in p.outputs: if 'template_vars' in p.outputs: return render_template(p.outputs['template'], vars=p.outputs['template_vars']) @@ -37,6 +74,11 @@ def admin(subpath='/'): origin = request.remote_addr LOGGER.info('%s probed for the admin panel with path: %s', origin, subpath) + if app.config['POSTGRESQL_ENABLED']: + cursor = app.config['postgresql_dbh'].cursor() + cursor.execute("INSERT INTO connections (source_ip, source_port, dest_host, dest_port, user_agent, url, method, path, headers, timestamp) VALUES (%(remote_addr)s,%(remote_port)s,%(server_name)s,%(server_port)s,%(user_agent)s,%(url)s,%(method)s,%(path)s,%(headers)s,%(timestamp)s)", {"remote_addr": request.remote_addr, "remote_port": request.environ['REMOTE_PORT'], "server_name": request.environ['SERVER_NAME'], "server_port": request.environ['SERVER_PORT'], "user_agent": request.user_agent.string, "url": request.url, "method": request.method, "path": request.path, "headers": str(request.headers), "timestamp": str(datetime.datetime.now())}) + app.config['postgresql_dbh'].commit() + # Plugins hook for p in pm.hook('plugins'): p.start(subpath=subpath, request=request) @@ -58,6 +100,11 @@ def plugin(plugin, subpath='/'): origin = request.remote_addr LOGGER.info('%s probed for plugin "%s" with path: %s', origin, plugin, subpath) + if app.config['POSTGRESQL_ENABLED']: + cursor = app.config['postgresql_dbh'].cursor() + cursor.execute("INSERT INTO connections (source_ip, source_port, dest_host, dest_port, user_agent, url, method, path, headers, timestamp) VALUES (%(remote_addr)s,%(remote_port)s,%(server_name)s,%(server_port)s,%(user_agent)s,%(url)s,%(method)s,%(path)s,%(headers)s,%(timestamp)s)", {"remote_addr": request.remote_addr, "remote_port": request.environ['REMOTE_PORT'], "server_name": request.environ['SERVER_NAME'], "server_port": request.environ['SERVER_PORT'], "user_agent": request.user_agent.string, "url": request.url, "method": request.method, "path": request.path, "headers": str(request.headers), "timestamp": str(datetime.datetime.now())}) + app.config['postgresql_dbh'].commit() + # Is the plugin in the whitelist? if not is_plugin_whitelisted(plugin): abort(404) @@ -69,6 +116,13 @@ def plugin(plugin, subpath='/'): LOGGER.info(p.outputs['log']) if 'log_json' in p.outputs and app.config['HPFEEDS_ENABLED']: app.config['hpfeeds_client'].publish(app.config['HPFEEDS_TOPIC'], p.outputs['log_json']) + if 'log_postgresql_plugins_probes' in p.outputs and app.config['POSTGRESQL_ENABLED']: + try: + cursor = app.config['postgresql_dbh'].cursor() + cursor.execute("INSERT INTO plugins_probes (plugin, source_ip, source_port, dest_host, dest_port, probed_plugin, path, user_agent, url, timestamp) VALUES ('timthumb',%(source_ip)s,%(source_port)s,%(dest_host)s,%(dest_port)s,%(probed_plugin)s,%(path)s,%(user_agent)s,%(url)s,%(timestamp)s)", p.outputs['log_postgresql_plugins_probes']) + app.config['postgresql_dbh'].commit() + except Exception as e: + print(e) if 'template' in p.outputs: if 'template_vars' in p.outputs: return render_template(p.outputs['template'], vars=p.outputs['template_vars']) @@ -83,6 +137,11 @@ def theme(theme, subpath='/'): origin = request.remote_addr LOGGER.info('%s probed for theme "%s" with path: %s', origin, theme, subpath) + if app.config['POSTGRESQL_ENABLED']: + cursor = app.config['postgresql_dbh'].cursor() + cursor.execute("INSERT INTO connections (source_ip, source_port, dest_host, dest_port, user_agent, url, method, path, headers, timestamp) VALUES (%(remote_addr)s,%(remote_port)s,%(server_name)s,%(server_port)s,%(user_agent)s,%(url)s,%(method)s,%(path)s,%(headers)s,%(timestamp)s)", {"remote_addr": request.remote_addr, "remote_port": request.environ['REMOTE_PORT'], "server_name": request.environ['SERVER_NAME'], "server_port": request.environ['SERVER_PORT'], "user_agent": request.user_agent.string, "url": request.url, "method": request.method, "path": request.path, "headers": str(request.headers), "timestamp": str(datetime.datetime.now())}) + app.config['postgresql_dbh'].commit() + # Is the theme whitelisted? if not is_theme_whitelisted(theme): abort(404) @@ -94,6 +153,13 @@ def theme(theme, subpath='/'): LOGGER.info(p.outputs['log']) if 'log_json' in p.outputs and app.config['HPFEEDS_ENABLED']: app.config['hpfeeds_client'].publish(app.config['HPFEEDS_TOPIC'], p.outputs['log_json']) + if 'log_postgresql_themes_probes' in p.outputs and app.config['POSTGRESQL_ENABLED']: + try: + cursor = app.config['postgresql_dbh'].cursor() + cursor.execute("INSERT INTO themes_probes (plugin, source_ip, source_port, dest_host, dest_port, probed_theme, path, user_agent, url, timestamp) VALUES ('timthumb',%(source_ip)s,%(source_port)s,%(dest_host)s,%(dest_port)s,%(probed_theme)s,%(path)s,%(user_agent)s,%(url)s,%(timestamp)s)", p.outputs['log_postgresql_themes_probes']) + app.config['postgresql_dbh'].commit() + except Exception as e: + print(e) if 'template' in p.outputs: if 'template_vars' in p.outputs: return render_template(p.outputs['template'], vars=p.outputs['template_vars']) @@ -101,3 +167,13 @@ def theme(theme, subpath='/'): return render_template(TEMPLATE, vars={}) +@app.route('/', methods=['GET', 'POST']) +def connection(path='/'): + + if app.config['POSTGRESQL_ENABLED']: + cursor = app.config['postgresql_dbh'].cursor() + cursor.execute("INSERT INTO connections (source_ip, source_port, dest_host, dest_port, user_agent, url, method, path, headers, timestamp) VALUES (%(remote_addr)s,%(remote_port)s,%(server_name)s,%(server_port)s,%(user_agent)s,%(url)s,%(method)s,%(path)s,%(headers)s,%(timestamp)s)", {"remote_addr": request.remote_addr, "remote_port": request.environ['REMOTE_PORT'], "server_name": request.environ['SERVER_NAME'], "server_port": request.environ['SERVER_PORT'], "user_agent": request.user_agent.string, "url": request.url, "method": request.method, "path": request.path, "headers": str(request.headers), "timestamp": str(datetime.datetime.now())}) + app.config['postgresql_dbh'].commit() + + abort(404) +