From 27906f388f17afdfd5cdc27ece2b0a71f2c39e72 Mon Sep 17 00:00:00 2001 From: Bernardo Damele Date: Fri, 14 Dec 2012 14:51:01 +0000 Subject: [PATCH] added first methods to interact with sqlmap core, it is now possible to launch a scan from the API, hurray! (issue #297) --- _sqlmap.py | 6 ++- lib/utils/restapi.py | 92 +++++++++++++++++++++++++++++++++++++------- 2 files changed, 82 insertions(+), 16 deletions(-) diff --git a/_sqlmap.py b/_sqlmap.py index 8b3917614..b9db12a86 100755 --- a/_sqlmap.py +++ b/_sqlmap.py @@ -45,6 +45,7 @@ from lib.core.testing import smokeTest from lib.core.testing import liveTest from lib.parse.cmdline import cmdLineParser from lib.utils.restapi import restAPIrun +from lib.utils.restapi import restAPIsetup def modulePath(): """ @@ -58,13 +59,14 @@ def restApiServe(): logger.setLevel(logging.INFO) cmdLineOptions.batch = True cmdLineOptions.disableColoring = True - restAPIrun(port=cmdLineOptions.restApiPort or RESTAPI_SERVER_PORT) + restAPIsetup(port=cmdLineOptions.restApiPort or RESTAPI_SERVER_PORT) def emit(self, record): message = stdoutencode(FORMATTER.format(record)) sys.stdout.write("%s\n" % message.strip('\r')) LOGGER_HANDLER.emit = types.MethodType(emit, LOGGER_HANDLER, type(LOGGER_HANDLER)) sys.stdout = StringIO.StringIO() - sys.stderr = StringIO.StringIO() + #sys.stderr = StringIO.StringIO() + restAPIrun(port=cmdLineOptions.restApiPort or RESTAPI_SERVER_PORT) def main(): """ diff --git a/lib/utils/restapi.py b/lib/utils/restapi.py index 98bcb7d0d..a433a11c9 100644 --- a/lib/utils/restapi.py +++ b/lib/utils/restapi.py @@ -18,7 +18,6 @@ except ImportError: sys.path.append(os.path.join(os.path.abspath(os.path.dirname(__file__)), "..", "..")) from extra.bottle.bottle import abort -from extra.bottle.bottle import debug from extra.bottle.bottle import error from extra.bottle.bottle import get from extra.bottle.bottle import hook @@ -41,7 +40,8 @@ from lib.core.settings import UNICODE_ENCODING from lib.core.settings import RESTAPI_SERVER_PORT # Local global variables -options = AttribDict() +options = {} +output = "" adminid = "" tasks = [] @@ -51,8 +51,6 @@ def jsonize(data): def is_admin(taskid): global adminid - #print "[INFO] Admin ID: %s" % adminid - #print "[INFO] Task ID: %s" % taskid if adminid != taskid: return False else: @@ -103,7 +101,8 @@ def task_new(): Create new task ID """ global tasks - taskid = hexencode(os.urandom(32)) + taskid = hexencode(os.urandom(16)) + options[taskid] = AttribDict(cmdLineOptions) tasks.append(taskid) return jsonize({"taskid": taskid}) @@ -144,22 +143,63 @@ def task_flush(taskid): ################################## # sqlmap core interact functions # ################################## + +@get("/option//list") +def option_list(taskid): + """ + List options for a certain task ID + """ + global options + if taskid not in tasks: + abort(500, "Invalid task ID") + + return jsonize(options[taskid]) + +@post("/option//get") +def option_get(taskid): + """ + Get the value of an option (command line switch) for a certain task ID + """ + global options + if taskid not in tasks: + abort(500, "Invalid task ID") + + option = request.json.get("option", "") + + if option in options[taskid]: + print {option: options[taskid][option]} + return jsonize({option: options[taskid][option]}) + else: + return jsonize({option: None}) + +@post("/option//set") +def option_set(taskid): + """ + Set an option (command line switch) for a certain task ID + """ + global options + if taskid not in tasks: + abort(500, "Invalid task ID") + + for key, value in request.json.items(): + options[taskid][key] = value + + return jsonize({"success": True}) + @post("/scan/") def scan(taskid): """ - Mount a scan with sqlmap + Launch a scan """ global options - 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) + options[taskid][key] = value + init(options[taskid], True) # Launch sqlmap engine in a separate thread thread = threading.Thread(target=start) @@ -168,6 +208,29 @@ def scan(taskid): return jsonize({"success": True}) +@get("/scan//status") +def scan_status(taskid): + """ + Verify if sqlmap core is currently running + """ + if taskid not in tasks: + abort(500, "Invalid task ID") + + return jsonize({"busy": kb.get("busyFlag")}) + +@get("/scan//output") +def scan_output(taskid): + """ + Read the standard output of sqlmap core execution + """ + if taskid not in tasks: + abort(500, "Invalid task ID") + + global output + sys.stdout.seek(len(output)) + output = sys.stdout.read() + return jsonize({"output": output}) + @post("/download///") def download(taskid, target, filename): """ @@ -182,18 +245,19 @@ def download(taskid, target, filename): else: abort(500) -def restAPIrun(host="0.0.0.0", port=RESTAPI_SERVER_PORT): +def restAPIsetup(host="0.0.0.0", port=RESTAPI_SERVER_PORT): """ Initiate REST-JSON API """ global adminid - global options global tasks - adminid = hexencode(os.urandom(32)) + adminid = hexencode(os.urandom(16)) + options[adminid] = AttribDict(cmdLineOptions) tasks.append(adminid) - options = AttribDict(cmdLineOptions) logger.info("Running REST-JSON API server at '%s:%d'.." % (host, port)) logger.info("The admin task ID is: %s" % adminid) + +def restAPIrun(host="0.0.0.0", port=RESTAPI_SERVER_PORT): run(host=host, port=port) def client(host, port):