From 0b71c85d9585cf580704892b01f79b2b485a0ac9 Mon Sep 17 00:00:00 2001 From: Bernardo Damele Date: Fri, 14 Dec 2012 13:40:25 +0000 Subject: [PATCH] refactoring, code cleanup, more security-related headers and first /scan method implementation (issue #297) --- lib/utils/restapi.py | 152 +++++++++++++++++++++++++------------------ 1 file changed, 89 insertions(+), 63 deletions(-) diff --git a/lib/utils/restapi.py b/lib/utils/restapi.py index 2ff4a7289..49b183714 100644 --- a/lib/utils/restapi.py +++ b/lib/utils/restapi.py @@ -8,6 +8,7 @@ See the file 'doc/COPYING' for copying permission import argparse import os import sys +import threading try: import simplejson as json @@ -39,135 +40,160 @@ from lib.core.option import init from lib.core.settings import UNICODE_ENCODING from lib.core.settings import RESTAPI_SERVER_PORT - -# local global variables -session_ids = [] -admin_id = "" - +# Local global variables +options = AttribDict() +adminid = "" +tasks = [] # Generic functions def jsonize(data): - #return json.dumps(data, sort_keys=False, indent=4) return json.dumps(data, sort_keys=False) - -def is_admin(session_id): - global admin_id - #print "[INFO] Admin ID: %s" % admin_id - #print "[INFO] Session ID: %s" % session_id - if admin_id != session_id: +def is_admin(taskid): + global adminid + #print "[INFO] Admin ID: %s" % adminid + #print "[INFO] Task ID: %s" % taskid + if adminid != taskid: return False else: return True - @hook('after_request') def security_headers(): """ Set some headers across all HTTP responses """ response.headers["Server"] = "Server" - response.headers["X-Frame-Options"] = "sameorigin" + response.headers["X-Content-Type-Options"] = "nosniff" + response.headers["X-Frame-Options"] = "DENY" response.headers["X-XSS-Protection"] = "1; mode=block" + response.headers["Pragma"] = "no-cache" + response.headers["Cache-Control"] = "no-cache" + response.headers["Expires"] = "0" + response.content_type = "application/json; charset=UTF-8" +############################## +# HTTP Status Code functions # +############################## -# HTTP Status Code functions @error(401) # Access Denied -def error401(error): +def error401(error=None): return "Access denied" - @error(404) # Not Found -def error404(error): +def error404(error=None): return "Nothing here" - @error(405) # Method Not Allowed (e.g. when requesting a POST method via GET) -def error405(error): +def error405(error=None): return "Method not allowed" - @error(500) # Internal Server Error -def error500(error): +def error500(error=None): return "Internal server error" - -################################ -# Session management functions # -################################ +############################# +# Task management functions # +############################# # Users' methods -@get("/session/new") -def session_new(): +@get("/task/new") +def task_new(): """ - Create new session token + Create new task ID """ - global session_ids - session_id = hexencode(os.urandom(32)) - session_ids.append(session_id) - response.content_type = "application/json; charset=UTF-8" - return jsonize({"sessionid": session_id}) + global tasks + taskid = hexencode(os.urandom(32)) + tasks.append(taskid) + return jsonize({"taskid": taskid}) - -@post("/session/destroy") -def session_destroy(): +@get("/task//destroy") +def task_destroy(taskid): """ - Destroy own session token + Destroy own task ID """ - session_id = request.json.get("sessionid", "") - if session_id in session_ids: - session_ids.remove(session_id) + if taskid in tasks: + tasks.remove(taskid) return jsonize({"success": True}) else: - abort(500) + abort(500, "Invalid task ID") # Admin's methods -@post("/session/list") -def session_list(): +@get("/task//list") +def task_list(taskid): """ - List all active sessions + List all active tasks """ - if is_admin(request.json.get("sessionid", "")): - response.content_type = "application/json; charset=UTF-8" - return jsonize({"sessions": session_ids}) + if is_admin(taskid): + return jsonize({"tasks": tasks}) else: abort(401) - -@post("/session/flush") -def session_flush(): +@get("/task//flush") +def task_flush(taskid): """ - Flush session spool (destroy all sessions) + Flush task spool (destroy all tasks) """ - global session_ids - if is_admin(request.json.get("sessionid", "")): - session_ids = [] + global tasks + if is_admin(taskid): + tasks = [] return jsonize({"success": True}) else: abort(401) +################################## +# sqlmap core interact functions # +################################## +@post("/scan/") +def scan(taskid): + """ + Mount a scan with sqlmap + """ + global options -@post("/download//") -def download(target, filename): + if taskid not in tasks: + abort(500, "Invalid task ID") + + # Initialize sqlmap engine's options with user's provided options + # within the JSON request + for key, value in request.json.items(): + if key != "taskid": + options[key] = value + init(options, True) + + # Launch sqlmap engine in a separate thread + thread = threading.Thread(target=start) + thread.daemon = True + thread.start() + + return jsonize({"success": True}) + +@post("/download///") +def download(taskid, target, filename): """ Download a certain file from the file system """ + if taskid not in tasks: + abort(500, "Invalid task ID") + path = os.path.join(paths.SQLMAP_OUTPUT_PATH, target) if os.path.exists(path): return static_file(filename, root=path) else: abort(500) - def restAPIrun(host="0.0.0.0", port=RESTAPI_SERVER_PORT): """ Initiate REST-JSON API """ - global admin_id - admin_id = hexencode(os.urandom(32)) + global adminid + global options + global tasks + adminid = hexencode(os.urandom(32)) + tasks.append(adminid) options = AttribDict(cmdLineOptions) logger.info("Running REST-JSON API server at '%s:%d'.." % (host, port)) - logger.info("The admin session ID is: %s" % admin_id) + logger.info("The admin task ID is: %s" % adminid) run(host=host, port=port) def client(host, port): @@ -176,8 +202,8 @@ def client(host, port): # TODO: write a simple client with urllib2, for now use curl from command line print "[ERROR] Not yet implemented, use curl from command line instead for now, for example:" - print "\n\t$ curl --proxy http://127.0.0.1:8080 http://%s:%s/session/new" % (host, port) - print "\t$ curl --proxy http://127.0.0.1:8080 -H \"Content-Type: application/json\" -X POST -d '{\"sessionid\": \"\"}' http://%s:%d/session/list\n" % (host, port) + print "\n\t$ curl --proxy http://127.0.0.1:8080 http://127.0.0.1:%s/task/new" % port + print "\t$ curl --proxy http://127.0.0.1:8080 -H \"Content-Type: application/json\" -X POST -d '{\"targetUrl\": \"\"}' http://127.0.0.1:%d/scan/\n" % port if __name__ == "__main__": """