diff --git a/lib/core/common.py b/lib/core/common.py index e6b55e41e..2805ec9bc 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -72,6 +72,7 @@ from lib.core.enums import HEURISTIC_TEST from lib.core.enums import HTTP_HEADER from lib.core.enums import HTTPMETHOD from lib.core.enums import MKSTEMP_PREFIX +from lib.core.enums import OPTION_TYPE from lib.core.enums import OS from lib.core.enums import PLACE from lib.core.enums import PAYLOAD @@ -112,6 +113,7 @@ from lib.core.settings import GITHUB_REPORT_OAUTH_TOKEN from lib.core.settings import GOOGLE_ANALYTICS_COOKIE_PREFIX from lib.core.settings import HASHDB_MILESTONE_VALUE from lib.core.settings import HOST_ALIASES +from lib.core.settings import IGNORE_SAVE_OPTIONS from lib.core.settings import INFERENCE_UNKNOWN_CHAR from lib.core.settings import INVALID_UNICODE_CHAR_FORMAT from lib.core.settings import IP_ADDRESS_REGEX @@ -1145,7 +1147,7 @@ def banner(): This function prints sqlmap banner with its version """ - if not any(_ in sys.argv for _ in ("--version", "--pickled-options")): + if not any(_ in sys.argv for _ in ("--version", "--api")): _ = BANNER if not getattr(LOGGER_HANDLER, "is_tty", False) or "--disable-coloring" in sys.argv: @@ -2928,6 +2930,58 @@ def setOptimize(): debugMsg = "turning off switch '--null-connection' used indirectly by switch '-o'" logger.debug(debugMsg) +def saveConfig(conf, filename): + """ + Saves conf to configuration filename + """ + + config = UnicodeRawConfigParser() + userOpts = {} + + for family in optDict.keys(): + userOpts[family] = [] + + for option, value in conf.items(): + for family, optionData in optDict.items(): + if option in optionData: + userOpts[family].append((option, value, optionData[option])) + + for family, optionData in userOpts.items(): + config.add_section(family) + + optionData.sort() + + for option, value, datatype in optionData: + if datatype and isListLike(datatype): + datatype = datatype[0] + + if option in IGNORE_SAVE_OPTIONS: + continue + + if value is None: + if datatype == OPTION_TYPE.BOOLEAN: + value = "False" + elif datatype in (OPTION_TYPE.INTEGER, OPTION_TYPE.FLOAT): + if option in defaults: + value = str(defaults[option]) + else: + value = "0" + elif datatype == OPTION_TYPE.STRING: + value = "" + + if isinstance(value, basestring): + value = value.replace("\n", "\n ") + + config.set(family, option, value) + + with openFile(filename, "wb") as f: + try: + config.write(f) + except IOError, ex: + errMsg = "something went wrong while trying " + errMsg += "to write to the configuration file '%s' ('%s')" % (filename, getSafeExString(ex)) + raise SqlmapSystemException(errMsg) + def initTechnique(technique=None): """ Prepares data for technique specified diff --git a/lib/core/dicts.py b/lib/core/dicts.py index 919bf5861..dd681a09a 100644 --- a/lib/core/dicts.py +++ b/lib/core/dicts.py @@ -274,6 +274,7 @@ DEPRECATED_OPTIONS = { "--auth-private": "use '--auth-file' instead", "--check-payload": None, "--check-waf": None, + "--pickled-options": "use '--api -c ...' instead", } DUMP_DATA_PREPROCESS = { diff --git a/lib/core/enums.py b/lib/core/enums.py index fd69edcce..9339b8ed4 100644 --- a/lib/core/enums.py +++ b/lib/core/enums.py @@ -364,6 +364,7 @@ class MKSTEMP_PREFIX: HASHES = "sqlmaphashes-" CRAWLER = "sqlmapcrawler-" IPC = "sqlmapipc-" + CONFIG = "sqlmapconfig-" TESTING = "sqlmaptesting-" RESULTS = "sqlmapresults-" COOKIE_JAR = "sqlmapcookiejar-" diff --git a/lib/core/option.py b/lib/core/option.py index 0fcd0406f..0bc5c2be4 100755 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -45,7 +45,6 @@ from lib.core.common import getConsoleWidth from lib.core.common import getFileItems from lib.core.common import getFileType from lib.core.common import getUnicode -from lib.core.common import isListLike from lib.core.common import normalizePath from lib.core.common import ntToPosixSlashes from lib.core.common import openFile @@ -58,12 +57,11 @@ from lib.core.common import readInput from lib.core.common import resetCookieJar from lib.core.common import runningAsAdmin from lib.core.common import safeExpandUser +from lib.core.common import saveConfig from lib.core.common import setOptimize from lib.core.common import setPaths from lib.core.common import singleTimeWarnMessage -from lib.core.common import UnicodeRawConfigParser from lib.core.common import urldecode -from lib.core.convert import base64unpickle from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger @@ -112,7 +110,6 @@ from lib.core.settings import DEFAULT_PAGE_ENCODING from lib.core.settings import DEFAULT_TOR_HTTP_PORTS from lib.core.settings import DEFAULT_TOR_SOCKS_PORTS from lib.core.settings import DUMMY_URL -from lib.core.settings import IGNORE_SAVE_OPTIONS from lib.core.settings import INJECT_HERE_MARK from lib.core.settings import IS_WIN from lib.core.settings import KB_CHARS_BOUNDARY_CHAR @@ -2107,53 +2104,7 @@ def _saveConfig(): debugMsg = "saving command line options to a sqlmap configuration INI file" logger.debug(debugMsg) - config = UnicodeRawConfigParser() - userOpts = {} - - for family in optDict.keys(): - userOpts[family] = [] - - for option, value in conf.items(): - for family, optionData in optDict.items(): - if option in optionData: - userOpts[family].append((option, value, optionData[option])) - - for family, optionData in userOpts.items(): - config.add_section(family) - - optionData.sort() - - for option, value, datatype in optionData: - if datatype and isListLike(datatype): - datatype = datatype[0] - - if option in IGNORE_SAVE_OPTIONS: - continue - - if value is None: - if datatype == OPTION_TYPE.BOOLEAN: - value = "False" - elif datatype in (OPTION_TYPE.INTEGER, OPTION_TYPE.FLOAT): - if option in defaults: - value = str(defaults[option]) - else: - value = "0" - elif datatype == OPTION_TYPE.STRING: - value = "" - - if isinstance(value, basestring): - value = value.replace("\n", "\n ") - - config.set(family, option, value) - - confFP = openFile(conf.saveConfig, "wb") - - try: - config.write(confFP) - except IOError, ex: - errMsg = "something went wrong while trying " - errMsg += "to write to the configuration file '%s' ('%s')" % (conf.saveConfig, getSafeExString(ex)) - raise SqlmapSystemException(errMsg) + saveConfig(conf, conf.saveConfig) infoMsg = "saved command line options to the configuration file '%s'" % conf.saveConfig logger.info(infoMsg) @@ -2229,26 +2180,6 @@ def _mergeOptions(inputOptions, overrideOptions): @type inputOptions: C{instance} """ - if inputOptions.pickledOptions: - try: - unpickledOptions = base64unpickle(inputOptions.pickledOptions, unsafe=True) - - if type(unpickledOptions) == dict: - unpickledOptions = AttribDict(unpickledOptions) - - _normalizeOptions(unpickledOptions) - - unpickledOptions["pickledOptions"] = None - for key in inputOptions: - if key not in unpickledOptions: - unpickledOptions[key] = inputOptions[key] - - inputOptions = unpickledOptions - except Exception, ex: - errMsg = "provided invalid value '%s' for option '--pickled-options'" % inputOptions.pickledOptions - errMsg += " (%s)" % repr(ex) - raise SqlmapSyntaxException(errMsg) - if inputOptions.configFile: configFileParser(inputOptions.configFile) @@ -2456,6 +2387,10 @@ def _basicOptionValidation(): errMsg = "switch '--dump' is incompatible with switch '--search'" raise SqlmapSyntaxException(errMsg) + if conf.api and not conf.configFile: + errMsg = "switch '--api' requires usage of option '-c'" + raise SqlmapSyntaxException(errMsg) + if conf.data and conf.nullConnection: errMsg = "option '--data' is incompatible with switch '--null-connection'" raise SqlmapSyntaxException(errMsg) diff --git a/lib/core/optiondict.py b/lib/core/optiondict.py index 19a5fdb45..9521dd2c5 100644 --- a/lib/core/optiondict.py +++ b/lib/core/optiondict.py @@ -243,5 +243,10 @@ optDict = { "liveTest": "boolean", "stopFail": "boolean", "runCase": "string", + }, + "API": { + "api": "boolean", + "taskid": "string", + "database": "string", } } diff --git a/lib/core/settings.py b/lib/core/settings.py index de70cb4a2..297d55582 100755 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import DBMS_DIRECTORY_NAME from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.1.4.11" +VERSION = "1.1.4.12" TYPE = "dev" if VERSION.count('.') > 2 and VERSION.split('.')[-1] != '0' else "stable" TYPE_COLORS = {"dev": 33, "stable": 90, "pip": 34} VERSION_STRING = "sqlmap/%s#%s" % ('.'.join(VERSION.split('.')[:-1]) if VERSION.count('.') > 2 and VERSION.split('.')[-1] == '0' else VERSION, TYPE) diff --git a/lib/parse/cmdline.py b/lib/parse/cmdline.py index d46374eac..6e435a635 100644 --- a/lib/parse/cmdline.py +++ b/lib/parse/cmdline.py @@ -770,9 +770,6 @@ def cmdLineParser(argv=None): parser.add_option("--murphy-rate", dest="murphyRate", type="int", help=SUPPRESS_HELP) - parser.add_option("--pickled-options", dest="pickledOptions", - help=SUPPRESS_HELP) - parser.add_option("--disable-precon", dest="disablePrecon", action="store_true", help=SUPPRESS_HELP) @@ -799,6 +796,14 @@ def cmdLineParser(argv=None): parser.add_option("--run-case", dest="runCase", help=SUPPRESS_HELP) + # API options + parser.add_option("--api", dest="api", action="store_true", + help=SUPPRESS_HELP) + + parser.add_option("--taskid", dest="taskid", help=SUPPRESS_HELP) + + parser.add_option("--database", dest="database", help=SUPPRESS_HELP) + parser.add_option_group(target) parser.add_option_group(request) parser.add_option_group(optimization) @@ -963,7 +968,7 @@ def cmdLineParser(argv=None): if not any((args.direct, args.url, args.logFile, args.bulkFile, args.googleDork, args.configFile, \ args.requestFile, args.updateAll, args.smokeTest, args.liveTest, args.wizard, args.dependencies, \ - args.purgeOutput, args.pickledOptions, args.sitemapUrl)): + args.purgeOutput, args.sitemapUrl)): errMsg = "missing a mandatory option (-d, -u, -l, -m, -r, -g, -c, -x, --wizard, --update, --purge-output or --dependencies), " errMsg += "use -h for basic or -hh for advanced help\n" parser.error(errMsg) diff --git a/lib/utils/api.py b/lib/utils/api.py index 72fd0e14b..ade1a03fd 100644 --- a/lib/utils/api.py +++ b/lib/utils/api.py @@ -20,8 +20,8 @@ import urllib2 from lib.core.common import dataToStdout from lib.core.common import getSafeExString +from lib.core.common import saveConfig from lib.core.common import unArrayizeValue -from lib.core.convert import base64pickle from lib.core.convert import hexencode from lib.core.convert import dejsonize from lib.core.convert import jsonize @@ -163,12 +163,15 @@ class Task(object): self.options = AttribDict(self._original_options) def engine_start(self): + configFile = tempfile.mkstemp(prefix=MKSTEMP_PREFIX.CONFIG, text=True)[1] + saveConfig(self.options, configFile) + if os.path.exists("sqlmap.py"): - self.process = Popen(["python", "sqlmap.py", "--pickled-options", base64pickle(self.options)], shell=False, close_fds=not IS_WIN) + self.process = Popen(["python", "sqlmap.py", "--api", "-c", configFile], shell=False, close_fds=not IS_WIN) elif os.path.exists(os.path.join(os.getcwd(), "sqlmap.py")): - self.process = Popen(["python", "sqlmap.py", "--pickled-options", base64pickle(self.options)], shell=False, cwd=os.getcwd(), close_fds=not IS_WIN) + self.process = Popen(["python", "sqlmap.py", "--api", "-c", configFile], shell=False, cwd=os.getcwd(), close_fds=not IS_WIN) else: - self.process = Popen(["sqlmap", "--pickled-options", base64pickle(self.options)], shell=False, close_fds=not IS_WIN) + self.process = Popen(["sqlmap", "--api", "-c", configFile], shell=False, close_fds=not IS_WIN) def engine_stop(self): if self.process: @@ -657,7 +660,7 @@ def server(host=RESTAPI_DEFAULT_ADDRESS, port=RESTAPI_DEFAULT_PORT, adapter=REST logger.info("Running REST-JSON API server at '%s:%d'.." % (host, port)) logger.info("Admin ID: %s" % DataStore.admin_id) - logger.debug("IPC database: %s" % Database.filepath) + logger.debug("IPC database: '%s'" % Database.filepath) # Initialize IPC database DataStore.current_db = Database() diff --git a/txt/checksum.md5 b/txt/checksum.md5 index 695b19aeb..0941b7927 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -26,26 +26,26 @@ d79481ab99acd739615e747d4a79d9d0 lib/controller/handler.py 310efc965c862cfbd7b0da5150a5ad36 lib/controller/__init__.py 19905ecb4437b94512cf21d5f1720091 lib/core/agent.py 6cc95a117fbd34ef31b9aa25520f0e31 lib/core/bigarray.py -145d131884dd5401d7a52effaea2ee9e lib/core/common.py +652266ff49168dd88a9d5649003a3951 lib/core/common.py 5065a4242a8cccf72f91e22e1007ae63 lib/core/convert.py a8143dab9d3a27490f7d49b6b29ea530 lib/core/data.py 7936d78b1a7f1f008ff92bf2f88574ba lib/core/datatype.py 36c85e9ef109c5b4af3ca9bb1065ef1f lib/core/decorators.py 47eecd5499eaa15e931793e1d1ac3566 lib/core/defaults.py -4029f6869b36eb5f796c2bcc948f4fae lib/core/dicts.py +7309cf449b009723d1a4655fcf1a96d7 lib/core/dicts.py 77edcfd3d7c5522bb64baf59ac23a047 lib/core/dump.py -2acf5449c71bfae4feec8da538e70116 lib/core/enums.py +b9ff4e622c416116bee6024c0f050349 lib/core/enums.py 9381a0c7e8bc19986299e84f4edda1a0 lib/core/exception.py 310efc965c862cfbd7b0da5150a5ad36 lib/core/__init__.py 9ba39bf66e9ecd469446bdbbeda906c3 lib/core/log.py -66c9795e2e7da32f46f04497ae910070 lib/core/optiondict.py -0324fce84ef88ed0416123f73c54a6d7 lib/core/option.py +ebb778c2d26eba8b34d7d8658e4105a6 lib/core/optiondict.py +bffd3f1bffa71a3c0ffc14768631f8ed lib/core/option.py 5f2f56e6c5f274408df61943f1e080c0 lib/core/profiling.py 40be71cd774662a7b420caeb7051e7d5 lib/core/readlineng.py d8e9250f3775119df07e9070eddccd16 lib/core/replication.py 785f86e3f963fa3798f84286a4e83ff2 lib/core/revision.py 40c80b28b3a5819b737a5a17d4565ae9 lib/core/session.py -627833276af914ba8fcca145c1a22269 lib/core/settings.py +4c2f45cb5c80dba2370379d230d526f3 lib/core/settings.py d91291997d2bd2f6028aaf371bf1d3b6 lib/core/shell.py 2ad85c130cc5f2b3701ea85c2f6bbf20 lib/core/subprocessng.py afd0636d2e93c23f4f0a5c9b6023ea17 lib/core/target.py @@ -56,7 +56,7 @@ ad74fc58fc7214802fd27067bce18dd2 lib/core/unescaper.py 4d13ed693401a498b6d073a2a494bd83 lib/core/wordlist.py 310efc965c862cfbd7b0da5150a5ad36 lib/__init__.py 8c4b04062db2245d9e190b413985202a lib/parse/banner.py -54f06c50771ce894a3c6a418d545f4bf lib/parse/cmdline.py +aa89ea0c7c44eb74eaaeeccaddc94d39 lib/parse/cmdline.py 3a31657bc38f277d0016ff6d50bde61f lib/parse/configfile.py 14539f1be714d4f1ed042067d63bc50a lib/parse/handler.py 64e5bb3ecbdd75144500588b437ba8da lib/parse/headers.py @@ -99,7 +99,7 @@ d3da4c7ceaf57c4687a052d58722f6bb lib/techniques/dns/use.py 310efc965c862cfbd7b0da5150a5ad36 lib/techniques/union/__init__.py 19fd73af7a278fd72b46a5a60f5bdd09 lib/techniques/union/test.py 8cd5655c60a638caa30ca1220896aeda lib/techniques/union/use.py -7b4c2347f207f61d74135a5520690336 lib/utils/api.py +9fca8077f1ee6f701ce7b7972e05ee53 lib/utils/api.py 29e32d59fcdd63c5a13498af1f367c8c lib/utils/crawler.py ba12c69a90061aa14d848b8396e79191 lib/utils/deps.py 3b9fd519164e0bf275d5fd361c3f11ff lib/utils/getch.py