Skip to content

Commit

Permalink
Replaced duplicate functionality by integrating the recon object from…
Browse files Browse the repository at this point in the history
… the base module into Recon-web.
  • Loading branch information
lanmaster53 committed Oct 22, 2019
1 parent 16ac9a9 commit cbad4c7
Show file tree
Hide file tree
Showing 10 changed files with 180 additions and 170 deletions.
53 changes: 15 additions & 38 deletions recon/core/web/__init__.py
Original file line number Diff line number Diff line change
@@ -1,59 +1,36 @@
from flask import Flask, g, request, abort
from recon.core.web.utils import connect_db, get_workspaces
import logging
from flask import Flask, request, abort
from recon.core import base
import os

#logging.basicConfig(level=logging.INFO)

# print welcome message
welcome = '''\
*************************************************************************
* Welcome to Recon-web, the analytics and reporting engine for Recon-ng!
* This is a web-based user interface. Open the following URL in your browser to begin.'''
* This is a web-based user interface. Open the URL below in your browser to begin.
*************************************************************************\
'''
print(welcome)

recon = base.Recon(analytics=False)
recon.start(base.Mode.CLI)

# configuration
DEBUG = False
SECRET_KEY = 'we keep no secrets here.'
HOME_DIR = os.path.join(os.path.expanduser('~'), '.recon-ng')
DATABASE = os.path.join(HOME_DIR, 'workspaces', '{}', 'data.db')
KEYS_DB = os.path.join(HOME_DIR, 'keys.db')
JSON_SORT_KEYS = False
WORKSPACE = recon.workspace.split('/')[-1]
print((f" * Workspace initialized: {WORKSPACE}"))

def create_app():

# setting the static_url_path to blank serves static files from the web root
app = Flask(__name__)#, static_url_path='')
app = Flask(__name__, static_url_path='')
app.config.from_object(__name__)

@app.before_request
def open_db():
'''Opens a new database connection if there is none yet for the
current application context.'''
if not hasattr(g, 'db'):
# parse the workspace name from the url if present
workspace = None
if request.view_args:
workspace = request.view_args.get('workspace')
if workspace:
# validate the provided workspace
if workspace not in get_workspaces():
abort(404)
# resolve the workspace's database path
database = app.config['DATABASE'].format(workspace)
# create and store the database connection
g.db = connect_db(database)
g.workspace = workspace
app.logger.info(f"Database connection created for {workspace} workspace.")

@app.teardown_appcontext
def close_db(error):
'''Closes the database connection at the end of the request of
every request.'''
if hasattr(g, 'db'):
g.db.close()
g.pop('workspace')
app.logger.info('Database connection destroyed.')
@app.after_request
def disable_cache(response):
response.headers['Cache-Control'] = 'no-cache, no-store, must-revalidate'
return response

from recon.core.web.views import core
app.register_blueprint(core)
Expand Down
16 changes: 16 additions & 0 deletions recon/core/web/constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
from recon.core.web.exports import _jsonify, csvify, xmlify, listify, xlsxify, proxify
from recon.core.web.reports import xlsx, pushpin

EXPORTS = {
'json': _jsonify,
'xml': xmlify,
'csv': csvify,
'list': listify,
'xlsx': xlsxify,
'proxy': proxify,
}

REPORTS = {
'xlsx': xlsx,
'pushpin': pushpin,
}
11 changes: 8 additions & 3 deletions recon/core/web/exports.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from dicttoxml import dicttoxml
from flask import current_app, Response, jsonify, send_file
from flask import current_app, Response, jsonify, send_file, stream_with_context
from io import StringIO
from io import BytesIO
from recon.core.web.utils import add_worksheet, is_url
Expand Down Expand Up @@ -55,10 +55,16 @@ def xlsxify(rows):
# create a single worksheet for the provided rows
add_worksheet(workbook, 'worksheet', rows)
sfp.seek(0)
return send_file(sfp, mimetype='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
return send_file(
sfp,
mimetype='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
as_attachment=True,
attachment_filename=f"export.xlsx"
)

# http://flask.pocoo.org/docs/0.12/patterns/streaming/
def proxify(rows):
@stream_with_context
def generate():
'''Expects a list of dictionaries containing URLs and requests them
through a configured proxy.'''
Expand Down Expand Up @@ -93,6 +99,5 @@ def generate():
else:
msg += 'Error: Failed URL validation.'
msg += os.linesep*2
current_app.logger.info(msg.strip())
yield msg
return Response(generate(), mimetype='text/plain')
20 changes: 14 additions & 6 deletions recon/core/web/reports.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from flask import Response, render_template, send_file
from flask import current_app, send_file, Response, render_template
from io import BytesIO
from recon.core.web.utils import get_key, add_worksheet, get_tables, query
from recon.core.web import recon
from recon.core.web.utils import columnize, add_worksheet
import xlsxwriter

def xlsx():
Expand All @@ -9,12 +10,19 @@ def xlsx():
sfp = BytesIO()
with xlsxwriter.Workbook(sfp) as workbook:
# create a worksheet for each table in the current workspace
for table in [t['name'] for t in get_tables()]:
rows = query(f"SELECT * FROM {table}")
for table in recon.get_tables():
rows = recon.query(f"SELECT * FROM {table}", include_header=True)
columns = rows.pop(0)
rows = columnize(columns, rows)
add_worksheet(workbook, table, rows)
sfp.seek(0)
return send_file(sfp, mimetype='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
return send_file(
sfp,
mimetype='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
as_attachment=True,
attachment_filename=f"{current_app.config['WORKSPACE']}.xlsx"
)

def pushpin():
google_api_key = get_key('google_api')
google_api_key = recon.get_key('google_api')
return Response(render_template('pushpin.html', api_key=google_api_key), mimetype='text/html')
4 changes: 1 addition & 3 deletions recon/core/web/static/pushpin.js
Original file line number Diff line number Diff line change
Expand Up @@ -114,12 +114,10 @@ function toggle_marker(element) {
$(document).ready(function() {
// load the map
load_map();
// build the url
var url = "/api/workspaces/"+workspace+"/tables/pushpins";
// load the pushpins
$.ajax({
type: "GET",
url: url,
url: "/api/tables/pushpins",
success: function(data) {
// declare a storage array for markers
window['markers'] = [];
Expand Down
60 changes: 25 additions & 35 deletions recon/core/web/static/recon.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,36 +6,34 @@ $("#workspace").change(function(){
$("#columns").hide();
$("#exports").hide();
$("#data").hide();
// get the data from the api
var workspace = $("#workspace option:selected").text();
var url = "/api/workspaces/"+workspace;
$.get(url, function(data, status){
// create the workspace
createWorkspace(data.tables);
// create the summary
createSummary(data.summary);
// create the reports options
createReports(data.reports);
// set the workspace and create the summary
$.get("/api/workspaces/"+workspace, function(data, status){
createSummary(data);
});
// create the workspace
$.get("/api/tables/", function(data, status){
createWorkspace(data);
});
// create the reports options
$.get("/api/reports/", function(data, status){
createReports(data);
});
// show the new elements on screen
$("#reports").show();
$("#tables").show();
$("#summary").show();
// set the width of the workspace select box
setSelectWidth($(this));
//setSelectWidth($(this));
// set the height of the summary panels
setFillHeight();
});

$("#reports-list").on("click", "li", function(e) {
// get the current workspace
var workspace = $("#workspace option:selected").text();
// get the report name
var report = $(e.target).text();
// build the url
var url = "/api/workspaces/"+workspace+"?report="+report;
// open in a new tab
window.open(url, '_blank');
window.open("/api/reports/"+report, '_blank');
// prevent the checked state
e.preventDefault();
e.stopPropagation();
Expand All @@ -48,17 +46,17 @@ $("#tables-list").on("click", "li", function() {
});
$(this).addClass("active");
// get the data from the api
var workspace = $("#workspace option:selected").text();
var table = $(this).text();
var url = "/api/workspaces/"+workspace+"/tables/"+table;
$.get(url, function(data, status) {
$.get("/api/tables/"+table, function(data, status) {
// create the column filter
createColumnFilter(data.columns);
// create the export menu
createExportMenu(data.exports);
// create the data table
createDataTable(data.rows);
});
$.get("/api/exports", function(data, status) {
// create the export menu
createExportMenu(data);
});
// show the new elements on screen
$("#summary").hide();
$("#columns").show();
Expand All @@ -73,19 +71,15 @@ $("#columns-list").on("click", "#filter", function (e) {
checked.push($(this).val());
});
var checkedStr = checked.join();
// get the current workspace
var workspace = $("#workspace option:selected").text();
// get the current table
var table = "";
$("#tables-list > li").each(function (i, e) {
if ($(e).hasClass("active")) {
table = $(e).find('a').text();
}
});
// build the url
var url = "/api/workspaces/"+workspace+"/tables/"+table+"?columns="+checkedStr;
// get the data from the api
$.get(url, function(data, status) {
$.get("/api/tables/"+table+"?columns="+checkedStr, function(data, status) {
// create the data table
createDataTable(data.rows);
});
Expand All @@ -103,8 +97,6 @@ $("#exports-list").on("click", "li", function(e) {
checked.push($(this).val());
});
var checkedStr = checked.join();
// get the current workspace
var workspace = $("#workspace option:selected").text();
// get the current table
var table = "";
$("#tables-list > li").each(function (i, e) {
Expand All @@ -114,10 +106,8 @@ $("#exports-list").on("click", "li", function(e) {
});
// get the desired format
var format = $(this).text();
// build the url
var url = "/api/workspaces/"+workspace+"/tables/"+table+"?format="+format+"&columns="+checkedStr;
// open in a new tab to download the file
window.open(url, '_blank');
window.open("/api/tables/"+table+"?format="+format+"&columns="+checkedStr, '_blank');
// prevent the checked state
e.preventDefault();
e.stopPropagation();
Expand All @@ -129,7 +119,7 @@ function createWorkspace(tables) {
// add DOM elements based on the provided table names
$.each(tables, function(i) {
var li = $("<li/>").appendTo($("#tables-list"));
var a = $("<a/>").attr("href", "javascript:void(0)").text(tables[i].name).appendTo(li);
var a = $("<a/>").attr("href", "javascript:void(0)").text(tables[i]).appendTo(li);
});
}

Expand All @@ -143,11 +133,11 @@ function createSummary(summary) {
var div2 = $("<div/>").addClass("count").appendTo($("#summary-list-l")).text(summary.records[i].count);
});
// add DOM elements to right panel based on the provided modules
if (summary.modules.length > 0) {
$.each(summary.modules, function(i) {
if (summary.activity.length > 0) {
$.each(summary.activity, function(i) {
var div0 = $("<div/>").appendTo($("#summary-list-r"))
var div1 = $("<div/>").addClass("module").appendTo(div0).text(summary.modules[i].module);
var div2 = $("<div/>").addClass("runs").appendTo(div0).text(summary.modules[i].runs);
var div1 = $("<div/>").addClass("module").appendTo(div0).text(summary.activity[i].module);
var div2 = $("<div/>").addClass("runs").appendTo(div0).text(summary.activity[i].runs);
});
} else {
$("#summary-list-r").append("<h5>No Data.</h5>");
Expand Down
2 changes: 1 addition & 1 deletion recon/core/web/templates/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
<span>[</span>
<select id="workspace">
{% for workspace in workspaces %}
<option value="{{ workspace }}"{% if workspace == g.workspace %}selected{% endif %}>{{ workspace }}</option>
<option value="{{ workspace }}"{% if workspace == config.WORKSPACE %}selected{% endif %}>{{ workspace }}</option>
{% endfor %}
</select>
<span>]</span>
Expand Down
7 changes: 1 addition & 6 deletions recon/core/web/templates/pushpin.html
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,10 @@
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='recon.css') }}">
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='pushpin.css') }}">
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<script type="text/javascript">
var workspace = "{{ g.workspace }}";
</script>
</head>
<body>
<div class="toolbar rounded shaded">
<div class="center-content">workspace</div>
<div class="filter center-content"><span class="header">{{ g.workspace }}</span></div>
<div class="center-content">sources</div>
<div class="center-content"><span class="header">{{ config.WORKSPACE }}</span></div>
<div id="filter-source" class="filter"></div>
</div>
<div id="map" class="map">Loading pushpins...</div>
Expand Down
41 changes: 3 additions & 38 deletions recon/core/web/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,50 +3,15 @@
import os
import re

def get_workspaces():
dirnames = []
path = os.path.join(current_app.config['HOME_DIR'], 'workspaces')
for name in os.listdir(path):
if os.path.isdir(os.path.join(path, name)):
dirnames.append(name)
return dirnames

def get_tables():
tables = query('SELECT name FROM sqlite_master WHERE type=\'table\'')
return sorted(tables, key=lambda t: t['name'])

def get_columns(table):
return [x[1] for x in query(f"PRAGMA table_info('{table}')")]

def connect_db(db_path):
'''Connects to the specific database.'''
rv = sqlite3.connect(db_path)
rv.row_factory = sqlite3.Row
return rv

def get_key(name):
db = connect_db(current_app.config['KEYS_DB'])
rows = db.execute('SELECT value FROM keys WHERE name=? AND value NOT NULL', (name,))
row = rows.fetchone()
value = row[0] if row else None
db.close()
return value

def query(query, values=()):
'''Queries the database and returns the results as a list.'''
current_app.logger.debug(f"Query: {query}")
if values:
cur = g.db.execute(query, values)
else:
cur = g.db.execute(query)
return cur.fetchall()
def columnize(columns, rows):
return [{columns[i]: row[i] for i in range(0, len(columns))} for row in rows]

def add_worksheet(workbook, name, rows):
'''Helper function for building xlsx files.'''
worksheet = workbook.add_worksheet(name)
# build the data set
if rows:
_rows = [rows[0].keys()]
_rows = [list(rows[0].keys())]
for row in rows:
_row = []
for key in _rows[0]:
Expand Down
Loading

0 comments on commit cbad4c7

Please sign in to comment.