code refactoring and first time logger is handled by a separate file descriptor (issue #297)

This commit is contained in:
Bernardo Damele 2012-12-15 00:12:22 +00:00
parent 2f6a31605c
commit 0c3da5c7eb
3 changed files with 63 additions and 41 deletions

View File

@ -37,6 +37,7 @@ from lib.core.exception import SqlmapSilentQuitException
from lib.core.exception import SqlmapUserQuitException from lib.core.exception import SqlmapUserQuitException
from lib.core.log import FORMATTER from lib.core.log import FORMATTER
from lib.core.log import LOGGER_HANDLER from lib.core.log import LOGGER_HANDLER
from lib.core.log import LOGGER_OUTPUT
from lib.core.option import init from lib.core.option import init
from lib.core.profiling import profile from lib.core.profiling import profile
from lib.core.settings import LEGAL_DISCLAIMER from lib.core.settings import LEGAL_DISCLAIMER
@ -44,8 +45,8 @@ from lib.core.settings import RESTAPI_SERVER_PORT
from lib.core.testing import smokeTest from lib.core.testing import smokeTest
from lib.core.testing import liveTest from lib.core.testing import liveTest
from lib.parse.cmdline import cmdLineParser from lib.parse.cmdline import cmdLineParser
from lib.utils.restapi import restAPIrun from lib.utils.restapi import restAPIRun
from lib.utils.restapi import restAPIsetup from lib.utils.restapi import restAPISetup
def modulePath(): def modulePath():
""" """
@ -55,18 +56,29 @@ def modulePath():
return os.path.dirname(getUnicode(sys.executable if weAreFrozen() else __file__, sys.getfilesystemencoding())) return os.path.dirname(getUnicode(sys.executable if weAreFrozen() else __file__, sys.getfilesystemencoding()))
def restApiServe(): def restAPIServe():
logger.setLevel(logging.INFO) # Increase default logging level to debug for RESTful API
logger.setLevel(logging.DEBUG)
# Enforce batch mode and disable coloring for RESTful API
cmdLineOptions.batch = True cmdLineOptions.batch = True
cmdLineOptions.disableColoring = True cmdLineOptions.disableColoring = True
restAPIsetup(port=cmdLineOptions.restApiPort or RESTAPI_SERVER_PORT)
# Setup RESTful API
restAPISetup(port=cmdLineOptions.restApiPort or RESTAPI_SERVER_PORT)
# Wrap logger stdout onto a custom file descriptor (LOGGER_OUTPUT)
def emit(self, record): def emit(self, record):
message = stdoutencode(FORMATTER.format(record)) message = stdoutencode(FORMATTER.format(record))
sys.stdout.write("%s\n" % message.strip('\r')) print >>LOGGER_OUTPUT, message.strip('\r')
LOGGER_HANDLER.emit = types.MethodType(emit, LOGGER_HANDLER, type(LOGGER_HANDLER)) LOGGER_HANDLER.emit = types.MethodType(emit, LOGGER_HANDLER, type(LOGGER_HANDLER))
# Wrap standard output onto a custom file descriptor
sys.stdout = StringIO.StringIO() sys.stdout = StringIO.StringIO()
#sys.stderr = StringIO.StringIO() #sys.stderr = StringIO.StringIO()
restAPIrun(port=cmdLineOptions.restApiPort or RESTAPI_SERVER_PORT)
# Run RESTful API
restAPIRun(port=cmdLineOptions.restApiPort or RESTAPI_SERVER_PORT)
def main(): def main():
""" """
@ -85,7 +97,7 @@ def main():
cmdLineOptions.update(cmdLineParser().__dict__) cmdLineOptions.update(cmdLineParser().__dict__)
if cmdLineOptions.restApi: if cmdLineOptions.restApi:
restApiServe() restAPIServe()
else: else:
init(cmdLineOptions) init(cmdLineOptions)

View File

@ -6,6 +6,7 @@ See the file 'doc/COPYING' for copying permission
""" """
import logging import logging
import StringIO
import sys import sys
from lib.core.enums import CUSTOM_LOGGING from lib.core.enums import CUSTOM_LOGGING
@ -32,3 +33,6 @@ FORMATTER = logging.Formatter("\r[%(asctime)s] [%(levelname)s] %(message)s", "%H
LOGGER_HANDLER.setFormatter(FORMATTER) LOGGER_HANDLER.setFormatter(FORMATTER)
LOGGER.addHandler(LOGGER_HANDLER) LOGGER.addHandler(LOGGER_HANDLER)
LOGGER.setLevel(logging.WARN) LOGGER.setLevel(logging.WARN)
# to handle logger with the RESTful API
LOGGER_OUTPUT = StringIO.StringIO()

View File

@ -32,6 +32,7 @@ from lib.core.datatype import AttribDict
from lib.core.data import cmdLineOptions from lib.core.data import cmdLineOptions
from lib.core.data import kb from lib.core.data import kb
from lib.core.data import logger from lib.core.data import logger
from lib.core.log import LOGGER_OUTPUT
from lib.core.exception import SqlmapMissingDependence from lib.core.exception import SqlmapMissingDependence
from lib.core.option import init from lib.core.option import init
from lib.core.settings import UNICODE_ENCODING from lib.core.settings import UNICODE_ENCODING
@ -99,10 +100,7 @@ def task_new():
global tasks global tasks
taskid = hexencode(os.urandom(16)) taskid = hexencode(os.urandom(16))
tasks[taskid] = AttribDict(cmdLineOptions)
tasks[taskid] = AttribDict()
tasks[taskid].options = AttribDict(cmdLineOptions)
tasks[taskid].output = ""
return jsonize({"taskid": taskid}) return jsonize({"taskid": taskid})
@ -171,9 +169,9 @@ def cleanup(taskid):
global tasks global tasks
if is_admin(taskid): if is_admin(taskid):
for task, taskdata in tasks.items(): for task, options in tasks.items():
if "oDir" in taskdata.options and taskdata.options.oDir is not None: if "oDir" in options and options.oDir is not None:
shutil.rmtree(taskdata.options.oDir) shutil.rmtree(options.oDir)
admin_task = tasks[adminid] admin_task = tasks[adminid]
tasks = AttribDict() tasks = AttribDict()
@ -192,7 +190,7 @@ def option_list(taskid):
if taskid not in tasks: if taskid not in tasks:
abort(500, "Invalid task ID") abort(500, "Invalid task ID")
return jsonize(tasks[taskid].options) return jsonize(tasks[taskid])
@post("/option/<taskid>/get") @post("/option/<taskid>/get")
def option_get(taskid): def option_get(taskid):
@ -204,8 +202,8 @@ def option_get(taskid):
option = request.json.get("option", "") option = request.json.get("option", "")
if option in tasks[taskid].options: if option in tasks[taskid]:
return jsonize({option: tasks[taskid].options[option]}) return jsonize({option: tasks[taskid][option]})
else: else:
return jsonize({option: None}) return jsonize({option: None})
@ -220,7 +218,7 @@ def option_set(taskid):
abort(500, "Invalid task ID") abort(500, "Invalid task ID")
for key, value in request.json.items(): for key, value in request.json.items():
tasks[taskid].options[key] = value tasks[taskid][key] = value
return jsonize({"success": True}) return jsonize({"success": True})
@ -238,12 +236,12 @@ def scan(taskid):
# Initialize sqlmap engine's options with user's provided options # Initialize sqlmap engine's options with user's provided options
# within the JSON request # within the JSON request
for key, value in request.json.items(): for key, value in request.json.items():
tasks[taskid].options[key] = value tasks[taskid][key] = value
# Overwrite oDir value to a temporary directory # Overwrite output directory (oDir) value to a temporary directory
tasks[taskid].options.oDir = tempfile.mkdtemp(prefix="sqlmap-") tasks[taskid].oDir = tempfile.mkdtemp(prefix="sqlmap-")
init(tasks[taskid].options, True) init(tasks[taskid], True)
# Launch sqlmap engine in a separate thread # Launch sqlmap engine in a separate thread
thread = threading.Thread(target=start) thread = threading.Thread(target=start)
@ -262,11 +260,12 @@ def scan_output(taskid):
if taskid not in tasks: if taskid not in tasks:
abort(500, "Invalid task ID") abort(500, "Invalid task ID")
sys.stdout.seek(len(tasks[taskid]["output"])) sys.stdout.seek(0)
tasks[taskid]["output"] = sys.stdout.read() output = sys.stdout.read()
sys.stdout.flush()
sys.stdout.truncate(0) sys.stdout.truncate(0)
return jsonize({"output": tasks[taskid]["output"]}) return jsonize({"output": output})
@get("/scan/<taskid>/delete") @get("/scan/<taskid>/delete")
def scan_delete(taskid): def scan_delete(taskid):
@ -278,21 +277,26 @@ def scan_delete(taskid):
if taskid not in tasks: if taskid not in tasks:
abort(500, "Invalid task ID") abort(500, "Invalid task ID")
if "oDir" in tasks[taskid].options and tasks[taskid].options.oDir is not None: if "oDir" in tasks[taskid] and tasks[taskid].oDir is not None:
shutil.rmtree(tasks[taskid].options.oDir) shutil.rmtree(tasks[taskid].oDir)
return jsonize({"success": True}) return jsonize({"success": True})
# Function to handle scans' logs # Function to handle scans' logs
@get("/log/<taskid>/info") @get("/scan/<taskid>/log")
def log_info(taskid): def scan_log(taskid):
""" """
Read the informational log messages Read the informational log messages
""" """
if taskid not in tasks: if taskid not in tasks:
abort(500, "Invalid task ID") abort(500, "Invalid task ID")
pass LOGGER_OUTPUT.seek(0)
output = LOGGER_OUTPUT.read()
LOGGER_OUTPUT.flush()
LOGGER_OUTPUT.truncate(0)
return jsonize({"log": output})
# Function to handle files inside the output directory # Function to handle files inside the output directory
@get("/download/<taskid>/<target>/<filename:path>") @get("/download/<taskid>/<target>/<filename:path>")
@ -313,29 +317,31 @@ def download(taskid, target, filename):
else: else:
abort(500) abort(500)
def restAPIsetup(host="0.0.0.0", port=RESTAPI_SERVER_PORT): def restAPISetup(host="0.0.0.0", port=RESTAPI_SERVER_PORT):
""" """
Initiate REST-JSON API Setup REST-JSON API
""" """
global adminid global adminid
global tasks global tasks
adminid = hexencode(os.urandom(16)) adminid = hexencode(os.urandom(16))
tasks[adminid] = AttribDict() tasks[adminid] = AttribDict(cmdLineOptions)
tasks[adminid].options = AttribDict(cmdLineOptions)
tasks[adminid].output = ""
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): 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 REST-JSON API
"""
run(host=host, port=port, quiet=False, debug=False) run(host=host, port=port, quiet=False, debug=False)
def client(host, port): def client(host, port):
addr = "http://%s:%d" % (host, port) addr = "http://%s:%d" % (host, port)
print "[INFO] Starting debug REST-JSON client to '%s'..." % addr print "[*] starting debug REST-JSON client to '%s'..." % addr
# TODO: write a simple client with urllib2, for now use curl from command line # 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 "[!] 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://127.0.0.1:%s/task/new" % 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 '{\"url\": \"http://testphp.vulnweb.com/artists.php?artist=1\"}' http://127.0.0.1:%d/scan/<taskID>/start" % port print "\t$ curl --proxy http://127.0.0.1:8080 -H \"Content-Type: application/json\" -X POST -d '{\"url\": \"http://testphp.vulnweb.com/artists.php?artist=1\"}' http://127.0.0.1:%d/scan/<taskID>/start" % port
print "\t$ curl --proxy http://127.0.0.1:8080 http://127.0.0.1:8775/scan/<taskID>/output\n" print "\t$ curl --proxy http://127.0.0.1:8080 http://127.0.0.1:8775/scan/<taskID>/output\n"