diff --git a/doc/THIRD-PARTY.md b/doc/THIRD-PARTY.md index 2e0ce9e2e..379e752f8 100644 --- a/doc/THIRD-PARTY.md +++ b/doc/THIRD-PARTY.md @@ -277,6 +277,8 @@ be bound by the terms and conditions of this License Agreement. # MIT +* The bottle web framework library located under extra/bottle/. + Copyright (C) 2012, Marcel Hellkamp. * The PageRank library located under thirdparty/pagerank/. Copyright (C) 2010, Corey Goldberg. * The PrettyPrint library located under thirdparty/prettyprint/. diff --git a/lib/parse/cmdline.py b/lib/parse/cmdline.py index 4b0f99e40..a282f2e81 100644 --- a/lib/parse/cmdline.py +++ b/lib/parse/cmdline.py @@ -685,7 +685,7 @@ def cmdLineParser(): parser.add_option("--restapi", dest="restApi", action="store_true", help=SUPPRESS_HELP) - parser.add_option("--restApi-port", dest="restApiPort", type="int", + parser.add_option("--restapi-port", dest="restApiPort", type="int", help=SUPPRESS_HELP) parser.add_option("--xmlrpc", dest="xmlRpc", action="store_true", diff --git a/lib/utils/restapi.py b/lib/utils/restapi.py index 82416ebf7..2ff4a7289 100644 --- a/lib/utils/restapi.py +++ b/lib/utils/restapi.py @@ -5,13 +5,31 @@ Copyright (c) 2006-2012 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ -import sys -import json +import argparse import os +import sys -from extra.bottle.bottle import abort, error, get, post, request, run, template, debug +try: + import simplejson as json +except ImportError: + import json + +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 +from extra.bottle.bottle import post +from extra.bottle.bottle import request +from extra.bottle.bottle import response +from extra.bottle.bottle import run +from extra.bottle.bottle import static_file +from extra.bottle.bottle import template from lib.controller.controller import start from lib.core.convert import hexencode +from lib.core.data import paths from lib.core.datatype import AttribDict from lib.core.data import cmdLineOptions from lib.core.data import kb @@ -29,17 +47,30 @@ admin_id = "" # Generic functions def jsonize(data): - return json.dumps(data, sort_keys=False, indent=4) + #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: 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-XSS-Protection"] = "1; mode=block" + + # HTTP Status Code functions @error(401) # Access Denied def error401(error): @@ -55,6 +86,7 @@ def error404(error): def error405(error): return "Method not allowed" + @error(500) # Internal Server Error def error500(error): return "Internal server error" @@ -73,6 +105,7 @@ def session_new(): 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}) @@ -81,12 +114,10 @@ def session_destroy(): """ Destroy own session token """ - # TODO: replace use of request.forms with JSON - session_id = request.forms.get("sessionid", "") - # + session_id = request.json.get("sessionid", "") if session_id in session_ids: session_ids.remove(session_id) - return "Done" + return jsonize({"success": True}) else: abort(500) @@ -96,25 +127,38 @@ def session_list(): """ List all active sessions """ - # TODO: replace use of request.forms with JSON - if is_admin(request.forms.get("sessionid", "")): + if is_admin(request.json.get("sessionid", "")): + response.content_type = "application/json; charset=UTF-8" return jsonize({"sessions": session_ids}) else: abort(401) -@get("/session/flush") +@post("/session/flush") def session_flush(): """ Flush session spool (destroy all sessions) """ global session_ids - if is_admin(request.forms.get("sessionid", "")): + if is_admin(request.json.get("sessionid", "")): session_ids = [] + return jsonize({"success": True}) else: abort(401) +@post("/download//") +def download(target, filename): + """ + Download a certain file from the file system + """ + 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 @@ -126,9 +170,28 @@ def restAPIrun(host="0.0.0.0", port=RESTAPI_SERVER_PORT): logger.info("The admin session ID is: %s" % admin_id) run(host=host, port=port) - -if __name__ == "__main__": - addr = "http://localhost:%d" % (int(sys.argv[1]) if len(sys.argv) > 1 else RESTAPI_SERVER_PORT) - print "[i] Starting debug REST-JSON client to '%s'..." % addr +def client(host, port): + addr = "http://%s:%d" % (host, port) + print "[INFO] Starting debug REST-JSON client to '%s'..." % addr # 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) + +if __name__ == "__main__": + """ + Standalone REST-JSON API wrapper function + """ + + parser = argparse.ArgumentParser() + parser.add_argument("-s", "--server", help="Act as a REST-JSON API server", default=RESTAPI_SERVER_PORT, action="store_true", required=False) + parser.add_argument("-c", "--client", help="Act as a REST-JSON API client", default=RESTAPI_SERVER_PORT, action="store_true", required=False) + parser.add_argument("-H", "--host", help="Host of the REST-JSON API server", default="0.0.0.0", action="store", required=False) + parser.add_argument("-p", "--port", help="Port of the the REST-JSON API server", default=RESTAPI_SERVER_PORT, action="store", required=False) + args = parser.parse_args() + + if args.server is True: + restAPIrun(args.host, args.port) + elif args.client is True: + client(args.host, args.port)