Best Python code snippet using autotest_python
option.py
Source:option.py  
1#!/usr/bin/env python2"""3Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/)4See the file 'LICENSE' for copying permission5"""6from __future__ import division7import codecs8import functools9import glob10import inspect11import logging12import os13import random14import re15import socket16import sys17import tempfile18import threading19import time20import traceback21from lib.controller.checks import checkConnection22from lib.core.common import Backend23from lib.core.common import boldifyMessage24from lib.core.common import checkFile25from lib.core.common import dataToStdout26from lib.core.common import decodeStringEscape27from lib.core.common import fetchRandomAgent28from lib.core.common import filterNone29from lib.core.common import findLocalPort30from lib.core.common import findPageForms31from lib.core.common import getConsoleWidth32from lib.core.common import getFileItems33from lib.core.common import getFileType34from lib.core.common import getPublicTypeMembers35from lib.core.common import getSafeExString36from lib.core.common import intersect37from lib.core.common import normalizePath38from lib.core.common import ntToPosixSlashes39from lib.core.common import openFile40from lib.core.common import parseRequestFile41from lib.core.common import parseTargetDirect42from lib.core.common import paths43from lib.core.common import randomStr44from lib.core.common import readCachedFileContent45from lib.core.common import readInput46from lib.core.common import resetCookieJar47from lib.core.common import runningAsAdmin48from lib.core.common import safeExpandUser49from lib.core.common import safeFilepathEncode50from lib.core.common import saveConfig51from lib.core.common import setColor52from lib.core.common import setOptimize53from lib.core.common import setPaths54from lib.core.common import singleTimeWarnMessage55from lib.core.common import urldecode56from lib.core.compat import cmp57from lib.core.compat import round58from lib.core.compat import xrange59from lib.core.convert import getUnicode60from lib.core.data import conf61from lib.core.data import kb62from lib.core.data import logger63from lib.core.data import mergedOptions64from lib.core.data import queries65from lib.core.datatype import AttribDict66from lib.core.datatype import InjectionDict67from lib.core.datatype import OrderedSet68from lib.core.defaults import defaults69from lib.core.dicts import DBMS_DICT70from lib.core.dicts import DUMP_REPLACEMENTS71from lib.core.enums import ADJUST_TIME_DELAY72from lib.core.enums import AUTH_TYPE73from lib.core.enums import CUSTOM_LOGGING74from lib.core.enums import DUMP_FORMAT75from lib.core.enums import HTTP_HEADER76from lib.core.enums import HTTPMETHOD77from lib.core.enums import MKSTEMP_PREFIX78from lib.core.enums import MOBILES79from lib.core.enums import OPTION_TYPE80from lib.core.enums import PAYLOAD81from lib.core.enums import PRIORITY82from lib.core.enums import PROXY_TYPE83from lib.core.enums import REFLECTIVE_COUNTER84from lib.core.enums import WIZARD85from lib.core.exception import SqlmapConnectionException86from lib.core.exception import SqlmapDataException87from lib.core.exception import SqlmapFilePathException88from lib.core.exception import SqlmapGenericException89from lib.core.exception import SqlmapInstallationException90from lib.core.exception import SqlmapMissingDependence91from lib.core.exception import SqlmapMissingMandatoryOptionException92from lib.core.exception import SqlmapMissingPrivileges93from lib.core.exception import SqlmapSilentQuitException94from lib.core.exception import SqlmapSyntaxException95from lib.core.exception import SqlmapSystemException96from lib.core.exception import SqlmapUnsupportedDBMSException97from lib.core.exception import SqlmapUserQuitException98from lib.core.exception import SqlmapValueException99from lib.core.log import FORMATTER100from lib.core.optiondict import optDict101from lib.core.settings import CODECS_LIST_PAGE102from lib.core.settings import CUSTOM_INJECTION_MARK_CHAR103from lib.core.settings import DBMS_ALIASES104from lib.core.settings import DEFAULT_GET_POST_DELIMITER105from lib.core.settings import DEFAULT_PAGE_ENCODING106from lib.core.settings import DEFAULT_TOR_HTTP_PORTS107from lib.core.settings import DEFAULT_TOR_SOCKS_PORTS108from lib.core.settings import DEFAULT_USER_AGENT109from lib.core.settings import DUMMY_URL110from lib.core.settings import IGNORE_CODE_WILDCARD111from lib.core.settings import IS_WIN112from lib.core.settings import KB_CHARS_BOUNDARY_CHAR113from lib.core.settings import KB_CHARS_LOW_FREQUENCY_ALPHABET114from lib.core.settings import LOCALHOST115from lib.core.settings import MAX_CONNECT_RETRIES116from lib.core.settings import MAX_NUMBER_OF_THREADS117from lib.core.settings import NULL118from lib.core.settings import PARAMETER_SPLITTING_REGEX119from lib.core.settings import PRECONNECT_CANDIDATE_TIMEOUT120from lib.core.settings import PROXY_ENVIRONMENT_VARIABLES121from lib.core.settings import SOCKET_PRE_CONNECT_QUEUE_SIZE122from lib.core.settings import SQLMAP_ENVIRONMENT_PREFIX123from lib.core.settings import SUPPORTED_DBMS124from lib.core.settings import SUPPORTED_OS125from lib.core.settings import TIME_DELAY_CANDIDATES126from lib.core.settings import UNION_CHAR_REGEX127from lib.core.settings import UNKNOWN_DBMS_VERSION128from lib.core.settings import URI_INJECTABLE_REGEX129from lib.core.threads import getCurrentThreadData130from lib.core.threads import setDaemon131from lib.core.update import update132from lib.parse.configfile import configFileParser133from lib.parse.payloads import loadBoundaries134from lib.parse.payloads import loadPayloads135from lib.request.basic import checkCharEncoding136from lib.request.basicauthhandler import SmartHTTPBasicAuthHandler137from lib.request.chunkedhandler import ChunkedHandler138from lib.request.connect import Connect as Request139from lib.request.dns import DNSServer140from lib.request.httpshandler import HTTPSHandler141from lib.request.pkihandler import HTTPSPKIAuthHandler142from lib.request.rangehandler import HTTPRangeHandler143from lib.request.redirecthandler import SmartRedirectHandler144from lib.utils.crawler import crawl145from lib.utils.deps import checkDependencies146from lib.utils.har import HTTPCollectorFactory147from lib.utils.purge import purge148from lib.utils.search import search149from thirdparty import six150from thirdparty.keepalive import keepalive151from thirdparty.multipart import multipartpost152from thirdparty.six.moves import collections_abc as _collections153from thirdparty.six.moves import http_client as _http_client154from thirdparty.six.moves import http_cookiejar as _http_cookiejar155from thirdparty.six.moves import urllib as _urllib156from thirdparty.socks import socks157from xml.etree.ElementTree import ElementTree158authHandler = _urllib.request.BaseHandler()159chunkedHandler = ChunkedHandler()160httpsHandler = HTTPSHandler()161keepAliveHandler = keepalive.HTTPHandler()162proxyHandler = _urllib.request.ProxyHandler()163redirectHandler = SmartRedirectHandler()164rangeHandler = HTTPRangeHandler()165multipartPostHandler = multipartpost.MultipartPostHandler()166# Reference: https://mail.python.org/pipermail/python-list/2009-November/558615.html167try:168    WindowsError169except NameError:170    WindowsError = None171def _loadQueries():172    """173    Loads queries from 'xml/queries.xml' file.174    """175    def iterate(node, retVal=None):176        class DictObject(object):177            def __init__(self):178                self.__dict__ = {}179            def __contains__(self, name):180                return name in self.__dict__181        if retVal is None:182            retVal = DictObject()183        for child in node.findall("*"):184            instance = DictObject()185            retVal.__dict__[child.tag] = instance186            if child.attrib:187                instance.__dict__.update(child.attrib)188            else:189                iterate(child, instance)190        return retVal191    tree = ElementTree()192    try:193        tree.parse(paths.QUERIES_XML)194    except Exception as ex:195        errMsg = "something appears to be wrong with "196        errMsg += "the file '%s' ('%s'). Please make " % (paths.QUERIES_XML, getSafeExString(ex))197        errMsg += "sure that you haven't made any changes to it"198        raise SqlmapInstallationException(errMsg)199    for node in tree.findall("*"):200        queries[node.attrib['value']] = iterate(node)201def _setMultipleTargets():202    """203    Define a configuration parameter if we are running in multiple target204    mode.205    """206    initialTargetsCount = len(kb.targets)207    seen = set()208    if not conf.logFile:209        return210    debugMsg = "parsing targets list from '%s'" % conf.logFile211    logger.debug(debugMsg)212    if not os.path.exists(conf.logFile):213        errMsg = "the specified list of targets does not exist"214        raise SqlmapFilePathException(errMsg)215    if checkFile(conf.logFile, False):216        for target in parseRequestFile(conf.logFile):217            url, _, data, _, _ = target218            key = re.sub(r"(\w+=)[^%s ]*" % (conf.paramDel or DEFAULT_GET_POST_DELIMITER), r"\g<1>", "%s %s" % (url, data))219            if key not in seen:220                kb.targets.add(target)221                seen.add(key)222    elif os.path.isdir(conf.logFile):223        files = os.listdir(conf.logFile)224        files.sort()225        for reqFile in files:226            if not re.search(r"([\d]+)\-request", reqFile):227                continue228            for target in parseRequestFile(os.path.join(conf.logFile, reqFile)):229                url, _, data, _, _ = target230                key = re.sub(r"(\w+=)[^%s ]*" % (conf.paramDel or DEFAULT_GET_POST_DELIMITER), r"\g<1>", "%s %s" % (url, data))231                if key not in seen:232                    kb.targets.add(target)233                    seen.add(key)234    else:235        errMsg = "the specified list of targets is not a file "236        errMsg += "nor a directory"237        raise SqlmapFilePathException(errMsg)238    updatedTargetsCount = len(kb.targets)239    if updatedTargetsCount > initialTargetsCount:240        infoMsg = "sqlmap parsed %d " % (updatedTargetsCount - initialTargetsCount)241        infoMsg += "(parameter unique) requests from the "242        infoMsg += "targets list ready to be tested"243        logger.info(infoMsg)244def _adjustLoggingFormatter():245    """246    Solves problem of line deletition caused by overlapping logging messages247    and retrieved data info in inference mode248    """249    if hasattr(FORMATTER, '_format'):250        return251    def format(record):252        message = FORMATTER._format(record)253        message = boldifyMessage(message)254        if kb.get("prependFlag"):255            message = "\n%s" % message256            kb.prependFlag = False257        return message258    FORMATTER._format = FORMATTER.format259    FORMATTER.format = format260def _setRequestFromFile():261    """262    This function checks if the way to make a HTTP request is through supplied263    textual file, parses it and saves the information into the knowledge base.264    """265    if conf.requestFile:266        for requestFile in re.split(PARAMETER_SPLITTING_REGEX, conf.requestFile):267            requestFile = safeExpandUser(requestFile)268            url = None269            seen = set()270            if not checkFile(requestFile, False):271                errMsg = "specified HTTP request file '%s' " % requestFile272                errMsg += "does not exist"273                raise SqlmapFilePathException(errMsg)274            infoMsg = "parsing HTTP request from '%s'" % requestFile275            logger.info(infoMsg)276            for target in parseRequestFile(requestFile):277                url = target[0]278                if url not in seen:279                    kb.targets.add(target)280                    if len(kb.targets) > 1:281                        conf.multipleTargets = True282                    seen.add(url)283            if url is None:284                errMsg = "specified file '%s' " % requestFile285                errMsg += "does not contain a usable HTTP request (with parameters)"286                raise SqlmapDataException(errMsg)287    if conf.secondReq:288        conf.secondReq = safeExpandUser(conf.secondReq)289        if not checkFile(conf.secondReq, False):290            errMsg = "specified second-order HTTP request file '%s' " % conf.secondReq291            errMsg += "does not exist"292            raise SqlmapFilePathException(errMsg)293        infoMsg = "parsing second-order HTTP request from '%s'" % conf.secondReq294        logger.info(infoMsg)295        try:296            target = next(parseRequestFile(conf.secondReq, False))297            kb.secondReq = target298        except StopIteration:299            errMsg = "specified second-order HTTP request file '%s' " % conf.secondReq300            errMsg += "does not contain a valid HTTP request"301            raise SqlmapDataException(errMsg)302def _setCrawler():303    if not conf.crawlDepth:304        return305    if not conf.bulkFile:306        if conf.url:307            crawl(conf.url)308        elif conf.requestFile and kb.targets:309            target = next(iter(kb.targets))310            crawl(target[0], target[2], target[3])311def _doSearch():312    """313    This function performs search dorking, parses results314    and saves the testable hosts into the knowledge base.315    """316    if not conf.googleDork:317        return318    kb.data.onlyGETs = None319    def retrieve():320        links = search(conf.googleDork)321        if not links:322            errMsg = "unable to find results for your "323            errMsg += "search dork expression"324            raise SqlmapGenericException(errMsg)325        for link in links:326            link = urldecode(link)327            if re.search(r"(.*?)\?(.+)", link) or conf.forms:328                kb.targets.add((link, conf.method, conf.data, conf.cookie, None))329            elif re.search(URI_INJECTABLE_REGEX, link, re.I):330                if kb.data.onlyGETs is None and conf.data is None and not conf.googleDork:331                    message = "do you want to scan only results containing GET parameters? [Y/n] "332                    kb.data.onlyGETs = readInput(message, default='Y', boolean=True)333                if not kb.data.onlyGETs or conf.googleDork:334                    kb.targets.add((link, conf.method, conf.data, conf.cookie, None))335        return links336    while True:337        links = retrieve()338        if kb.targets:339            infoMsg = "found %d results for your " % len(links)340            infoMsg += "search dork expression"341            if not conf.forms:342                infoMsg += ", "343                if len(links) == len(kb.targets):344                    infoMsg += "all "345                else:346                    infoMsg += "%d " % len(kb.targets)347                infoMsg += "of them are testable targets"348            logger.info(infoMsg)349            break350        else:351            message = "found %d results " % len(links)352            message += "for your search dork expression, but none of them "353            message += "have GET parameters to test for SQL injection. "354            message += "Do you want to skip to the next result page? [Y/n]"355            if not readInput(message, default='Y', boolean=True):356                raise SqlmapSilentQuitException357            else:358                conf.googlePage += 1359def _setStdinPipeTargets():360    if isinstance(conf.stdinPipe, _collections.Iterable):361        infoMsg = "using 'STDIN' for parsing targets list"362        logger.info(infoMsg)363        class _(object):364            def __init__(self):365                self.__rest = OrderedSet()366            def __iter__(self):367                return self368            def __next__(self):369                return self.next()370            def next(self):371                try:372                    line = next(conf.stdinPipe)373                except (IOError, OSError):374                    line = None375                if line:376                    match = re.search(r"\b(https?://[^\s'\"]+|[\w.]+\.\w{2,3}[/\w+]*\?[^\s'\"]+)", line, re.I)377                    if match:378                        return (match.group(0), conf.method, conf.data, conf.cookie, None)379                elif self.__rest:380                    return self.__rest.pop()381                raise StopIteration()382            def add(self, elem):383                self.__rest.add(elem)384        kb.targets = _()385def _setBulkMultipleTargets():386    if not conf.bulkFile:387        return388    conf.bulkFile = safeExpandUser(conf.bulkFile)389    infoMsg = "parsing multiple targets list from '%s'" % conf.bulkFile390    logger.info(infoMsg)391    if not checkFile(conf.bulkFile, False):392        errMsg = "the specified bulk file "393        errMsg += "does not exist"394        raise SqlmapFilePathException(errMsg)395    found = False396    for line in getFileItems(conf.bulkFile):397        if conf.scope and not re.search(conf.scope, line, re.I):398            continue399        if re.match(r"[^ ]+\?(.+)", line, re.I) or kb.customInjectionMark in line or conf.data:400            found = True401            kb.targets.add((line.strip(), conf.method, conf.data, conf.cookie, None))402    if not found and not conf.forms and not conf.crawlDepth:403        warnMsg = "no usable links found (with GET parameters)"404        logger.warn(warnMsg)405def _findPageForms():406    if not conf.forms or conf.crawlDepth:407        return408    if conf.url and not checkConnection():409        return410    found = False411    infoMsg = "searching for forms"412    logger.info(infoMsg)413    if not any((conf.bulkFile, conf.googleDork)):414        page, _, _ = Request.queryPage(content=True, ignoreSecondOrder=True)415        if findPageForms(page, conf.url, True, True):416            found = True417    else:418        if conf.bulkFile:419            targets = getFileItems(conf.bulkFile)420        elif conf.googleDork:421            targets = [_[0] for _ in kb.targets]422            kb.targets.clear()423        else:424            targets = []425        for i in xrange(len(targets)):426            try:427                target = targets[i].strip()428                if not re.search(r"(?i)\Ahttp[s]*://", target):429                    target = "http://%s" % target430                page, _, _ = Request.getPage(url=target.strip(), cookie=conf.cookie, crawling=True, raise404=False)431                if findPageForms(page, target, False, True):432                    found = True433                if conf.verbose in (1, 2):434                    status = '%d/%d links visited (%d%%)' % (i + 1, len(targets), round(100.0 * (i + 1) / len(targets)))435                    dataToStdout("\r[%s] [INFO] %s" % (time.strftime("%X"), status), True)436            except KeyboardInterrupt:437                break438            except Exception as ex:439                errMsg = "problem occurred while searching for forms at '%s' ('%s')" % (target, getSafeExString(ex))440                logger.error(errMsg)441    if not found:442        warnMsg = "no forms found"443        logger.warn(warnMsg)444def _setDBMSAuthentication():445    """446    Check and set the DBMS authentication credentials to run statements as447    another user, not the session user448    """449    if not conf.dbmsCred:450        return451    debugMsg = "setting the DBMS authentication credentials"452    logger.debug(debugMsg)453    match = re.search(r"^(.+?):(.*?)$", conf.dbmsCred)454    if not match:455        errMsg = "DBMS authentication credentials value must be in format "456        errMsg += "username:password"457        raise SqlmapSyntaxException(errMsg)458    conf.dbmsUsername = match.group(1)459    conf.dbmsPassword = match.group(2)460def _setMetasploit():461    if not conf.osPwn and not conf.osSmb and not conf.osBof:462        return463    debugMsg = "setting the takeover out-of-band functionality"464    logger.debug(debugMsg)465    msfEnvPathExists = False466    if IS_WIN:467        try:468            __import__("win32file")469        except ImportError:470            errMsg = "sqlmap requires third-party module 'pywin32' "471            errMsg += "in order to use Metasploit functionalities on "472            errMsg += "Windows. You can download it from "473            errMsg += "'https://github.com/mhammond/pywin32'"474            raise SqlmapMissingDependence(errMsg)475        if not conf.msfPath:476            for candidate in os.environ.get("PATH", "").split(';'):477                if all(_ in candidate for _ in ("metasploit", "bin")):478                    conf.msfPath = os.path.dirname(candidate.rstrip('\\'))479                    break480    if conf.osSmb:481        isAdmin = runningAsAdmin()482        if not isAdmin:483            errMsg = "you need to run sqlmap as an administrator "484            errMsg += "if you want to perform a SMB relay attack because "485            errMsg += "it will need to listen on a user-specified SMB "486            errMsg += "TCP port for incoming connection attempts"487            raise SqlmapMissingPrivileges(errMsg)488    if conf.msfPath:489        for path in (conf.msfPath, os.path.join(conf.msfPath, "bin")):490            if any(os.path.exists(normalizePath(os.path.join(path, "%s%s" % (_, ".bat" if IS_WIN else "")))) for _ in ("msfcli", "msfconsole")):491                msfEnvPathExists = True492                if all(os.path.exists(normalizePath(os.path.join(path, "%s%s" % (_, ".bat" if IS_WIN else "")))) for _ in ("msfvenom",)):493                    kb.oldMsf = False494                elif all(os.path.exists(normalizePath(os.path.join(path, "%s%s" % (_, ".bat" if IS_WIN else "")))) for _ in ("msfencode", "msfpayload")):495                    kb.oldMsf = True496                else:497                    msfEnvPathExists = False498                conf.msfPath = path499                break500        if msfEnvPathExists:501            debugMsg = "provided Metasploit Framework path "502            debugMsg += "'%s' is valid" % conf.msfPath503            logger.debug(debugMsg)504        else:505            warnMsg = "the provided Metasploit Framework path "506            warnMsg += "'%s' is not valid. The cause could " % conf.msfPath507            warnMsg += "be that the path does not exists or that one "508            warnMsg += "or more of the needed Metasploit executables "509            warnMsg += "within msfcli, msfconsole, msfencode and "510            warnMsg += "msfpayload do not exist"511            logger.warn(warnMsg)512    else:513        warnMsg = "you did not provide the local path where Metasploit "514        warnMsg += "Framework is installed"515        logger.warn(warnMsg)516    if not msfEnvPathExists:517        warnMsg = "sqlmap is going to look for Metasploit Framework "518        warnMsg += "installation inside the environment path(s)"519        logger.warn(warnMsg)520        envPaths = os.environ.get("PATH", "").split(";" if IS_WIN else ":")521        for envPath in envPaths:522            envPath = envPath.replace(";", "")523            if any(os.path.exists(normalizePath(os.path.join(envPath, "%s%s" % (_, ".bat" if IS_WIN else "")))) for _ in ("msfcli", "msfconsole")):524                msfEnvPathExists = True525                if all(os.path.exists(normalizePath(os.path.join(envPath, "%s%s" % (_, ".bat" if IS_WIN else "")))) for _ in ("msfvenom",)):526                    kb.oldMsf = False527                elif all(os.path.exists(normalizePath(os.path.join(envPath, "%s%s" % (_, ".bat" if IS_WIN else "")))) for _ in ("msfencode", "msfpayload")):528                    kb.oldMsf = True529                else:530                    msfEnvPathExists = False531                if msfEnvPathExists:532                    infoMsg = "Metasploit Framework has been found "533                    infoMsg += "installed in the '%s' path" % envPath534                    logger.info(infoMsg)535                    conf.msfPath = envPath536                    break537    if not msfEnvPathExists:538        errMsg = "unable to locate Metasploit Framework installation. "539        errMsg += "You can get it at 'https://www.metasploit.com/download/'"540        raise SqlmapFilePathException(errMsg)541def _setWriteFile():542    if not conf.fileWrite:543        return544    debugMsg = "setting the write file functionality"545    logger.debug(debugMsg)546    if not os.path.exists(conf.fileWrite):547        errMsg = "the provided local file '%s' does not exist" % conf.fileWrite548        raise SqlmapFilePathException(errMsg)549    if not conf.fileDest:550        errMsg = "you did not provide the back-end DBMS absolute path "551        errMsg += "where you want to write the local file '%s'" % conf.fileWrite552        raise SqlmapMissingMandatoryOptionException(errMsg)553    conf.fileWriteType = getFileType(conf.fileWrite)554def _setOS():555    """556    Force the back-end DBMS operating system option.557    """558    if not conf.os:559        return560    if conf.os.lower() not in SUPPORTED_OS:561        errMsg = "you provided an unsupported back-end DBMS operating "562        errMsg += "system. The supported DBMS operating systems for OS "563        errMsg += "and file system access are %s. " % ', '.join([o.capitalize() for o in SUPPORTED_OS])564        errMsg += "If you do not know the back-end DBMS underlying OS, "565        errMsg += "do not provide it and sqlmap will fingerprint it for "566        errMsg += "you."567        raise SqlmapUnsupportedDBMSException(errMsg)568    debugMsg = "forcing back-end DBMS operating system to user defined "569    debugMsg += "value '%s'" % conf.os570    logger.debug(debugMsg)571    Backend.setOs(conf.os)572def _setTechnique():573    validTechniques = sorted(getPublicTypeMembers(PAYLOAD.TECHNIQUE), key=lambda x: x[1])574    validLetters = [_[0][0].upper() for _ in validTechniques]575    if conf.technique and isinstance(conf.technique, six.string_types):576        _ = []577        for letter in conf.technique.upper():578            if letter not in validLetters:579                errMsg = "value for --technique must be a string composed "580                errMsg += "by the letters %s. Refer to the " % ", ".join(validLetters)581                errMsg += "user's manual for details"582                raise SqlmapSyntaxException(errMsg)583            for validTech, validInt in validTechniques:584                if letter == validTech[0]:585                    _.append(validInt)586                    break587        conf.technique = _588def _setDBMS():589    """590    Force the back-end DBMS option.591    """592    if not conf.dbms:593        return594    debugMsg = "forcing back-end DBMS to user defined value"595    logger.debug(debugMsg)596    conf.dbms = conf.dbms.lower()597    regex = re.search(r"%s ([\d\.]+)" % ("(%s)" % "|".join(SUPPORTED_DBMS)), conf.dbms, re.I)598    if regex:599        conf.dbms = regex.group(1)600        Backend.setVersion(regex.group(2))601    if conf.dbms not in SUPPORTED_DBMS:602        errMsg = "you provided an unsupported back-end database management "603        errMsg += "system. Supported DBMSes are as follows: %s. " % ', '.join(sorted(_ for _ in DBMS_DICT))604        errMsg += "If you do not know the back-end DBMS, do not provide "605        errMsg += "it and sqlmap will fingerprint it for you."606        raise SqlmapUnsupportedDBMSException(errMsg)607    for dbms, aliases in DBMS_ALIASES:608        if conf.dbms in aliases:609            conf.dbms = dbms610            break611def _listTamperingFunctions():612    """613    Lists available tamper functions614    """615    if conf.listTampers:616        infoMsg = "listing available tamper scripts\n"617        logger.info(infoMsg)618        for script in sorted(glob.glob(os.path.join(paths.SQLMAP_TAMPER_PATH, "*.py"))):619            content = openFile(script, "rb").read()620            match = re.search(r'(?s)__priority__.+"""(.+)"""', content)621            if match:622                comment = match.group(1).strip()623                dataToStdout("* %s - %s\n" % (setColor(os.path.basename(script), "yellow"), re.sub(r" *\n *", " ", comment.split("\n\n")[0].strip())))624def _setTamperingFunctions():625    """626    Loads tampering functions from given script(s)627    """628    if conf.tamper:629        last_priority = PRIORITY.HIGHEST630        check_priority = True631        resolve_priorities = False632        priorities = []633        for script in re.split(PARAMETER_SPLITTING_REGEX, conf.tamper):634            found = False635            path = safeFilepathEncode(paths.SQLMAP_TAMPER_PATH)636            script = safeFilepathEncode(script.strip())637            try:638                if not script:639                    continue640                elif os.path.exists(os.path.join(path, script if script.endswith(".py") else "%s.py" % script)):641                    script = os.path.join(path, script if script.endswith(".py") else "%s.py" % script)642                elif not os.path.exists(script):643                    errMsg = "tamper script '%s' does not exist" % script644                    raise SqlmapFilePathException(errMsg)645                elif not script.endswith(".py"):646                    errMsg = "tamper script '%s' should have an extension '.py'" % script647                    raise SqlmapSyntaxException(errMsg)648            except UnicodeDecodeError:649                errMsg = "invalid character provided in option '--tamper'"650                raise SqlmapSyntaxException(errMsg)651            dirname, filename = os.path.split(script)652            dirname = os.path.abspath(dirname)653            infoMsg = "loading tamper module '%s'" % filename[:-3]654            logger.info(infoMsg)655            if not os.path.exists(os.path.join(dirname, "__init__.py")):656                errMsg = "make sure that there is an empty file '__init__.py' "657                errMsg += "inside of tamper scripts directory '%s'" % dirname658                raise SqlmapGenericException(errMsg)659            if dirname not in sys.path:660                sys.path.insert(0, dirname)661            try:662                module = __import__(safeFilepathEncode(filename[:-3]))663            except Exception as ex:664                raise SqlmapSyntaxException("cannot import tamper module '%s' (%s)" % (getUnicode(filename[:-3]), getSafeExString(ex)))665            priority = PRIORITY.NORMAL if not hasattr(module, "__priority__") else module.__priority__666            for name, function in inspect.getmembers(module, inspect.isfunction):667                if name == "tamper" and inspect.getargspec(function).args and inspect.getargspec(function).keywords == "kwargs":668                    found = True669                    kb.tamperFunctions.append(function)670                    function.__name__ = module.__name__671                    if check_priority and priority > last_priority:672                        message = "it appears that you might have mixed "673                        message += "the order of tamper scripts. "674                        message += "Do you want to auto resolve this? [Y/n/q] "675                        choice = readInput(message, default='Y').upper()676                        if choice == 'N':677                            resolve_priorities = False678                        elif choice == 'Q':679                            raise SqlmapUserQuitException680                        else:681                            resolve_priorities = True682                        check_priority = False683                    priorities.append((priority, function))684                    last_priority = priority685                    break686                elif name == "dependencies":687                    try:688                        function()689                    except Exception as ex:690                        errMsg = "error occurred while checking dependencies "691                        errMsg += "for tamper module '%s' ('%s')" % (getUnicode(filename[:-3]), getSafeExString(ex))692                        raise SqlmapGenericException(errMsg)693            if not found:694                errMsg = "missing function 'tamper(payload, **kwargs)' "695                errMsg += "in tamper script '%s'" % script696                raise SqlmapGenericException(errMsg)697        if kb.tamperFunctions and len(kb.tamperFunctions) > 3:698            warnMsg = "using too many tamper scripts is usually not "699            warnMsg += "a good idea"700            logger.warning(warnMsg)701        if resolve_priorities and priorities:702            priorities.sort(key=functools.cmp_to_key(lambda a, b: cmp(a[0], b[0])), reverse=True)703            kb.tamperFunctions = []704            for _, function in priorities:705                kb.tamperFunctions.append(function)706def _setPreprocessFunctions():707    """708    Loads preprocess function(s) from given script(s)709    """710    if conf.preprocess:711        for script in re.split(PARAMETER_SPLITTING_REGEX, conf.preprocess):712            found = False713            function = None714            script = safeFilepathEncode(script.strip())715            try:716                if not script:717                    continue718                if not os.path.exists(script):719                    errMsg = "preprocess script '%s' does not exist" % script720                    raise SqlmapFilePathException(errMsg)721                elif not script.endswith(".py"):722                    errMsg = "preprocess script '%s' should have an extension '.py'" % script723                    raise SqlmapSyntaxException(errMsg)724            except UnicodeDecodeError:725                errMsg = "invalid character provided in option '--preprocess'"726                raise SqlmapSyntaxException(errMsg)727            dirname, filename = os.path.split(script)728            dirname = os.path.abspath(dirname)729            infoMsg = "loading preprocess module '%s'" % filename[:-3]730            logger.info(infoMsg)731            if not os.path.exists(os.path.join(dirname, "__init__.py")):732                errMsg = "make sure that there is an empty file '__init__.py' "733                errMsg += "inside of preprocess scripts directory '%s'" % dirname734                raise SqlmapGenericException(errMsg)735            if dirname not in sys.path:736                sys.path.insert(0, dirname)737            try:738                module = __import__(safeFilepathEncode(filename[:-3]))739            except Exception as ex:740                raise SqlmapSyntaxException("cannot import preprocess module '%s' (%s)" % (getUnicode(filename[:-3]), getSafeExString(ex)))741            for name, function in inspect.getmembers(module, inspect.isfunction):742                try:743                    if name == "preprocess" and inspect.getargspec(function).args and all(_ in inspect.getargspec(function).args for _ in ("req",)):744                        found = True745                        kb.preprocessFunctions.append(function)746                        function.__name__ = module.__name__747                        break748                except ValueError:  # Note: https://github.com/sqlmapproject/sqlmap/issues/4357749                    pass750            if not found:751                errMsg = "missing function 'preprocess(req)' "752                errMsg += "in preprocess script '%s'" % script753                raise SqlmapGenericException(errMsg)754            else:755                try:756                    function(_urllib.request.Request("http://localhost"))757                except:758                    tbMsg = traceback.format_exc()759                    if conf.debug:760                        dataToStdout(tbMsg)761                    handle, filename = tempfile.mkstemp(prefix=MKSTEMP_PREFIX.PREPROCESS, suffix=".py")762                    os.close(handle)763                    openFile(filename, "w+b").write("#!/usr/bin/env\n\ndef preprocess(req):\n    pass\n")764                    openFile(os.path.join(os.path.dirname(filename), "__init__.py"), "w+b").write("pass")765                    errMsg = "function 'preprocess(req)' "766                    errMsg += "in preprocess script '%s' " % script767                    errMsg += "appears to be invalid "768                    errMsg += "(Note: find template script at '%s')" % filename769                    raise SqlmapGenericException(errMsg)770def _setPostprocessFunctions():771    """772    Loads postprocess function(s) from given script(s)773    """774    if conf.postprocess:775        for script in re.split(PARAMETER_SPLITTING_REGEX, conf.postprocess):776            found = False777            function = None778            script = safeFilepathEncode(script.strip())779            try:780                if not script:781                    continue782                if not os.path.exists(script):783                    errMsg = "postprocess script '%s' does not exist" % script784                    raise SqlmapFilePathException(errMsg)785                elif not script.endswith(".py"):786                    errMsg = "postprocess script '%s' should have an extension '.py'" % script787                    raise SqlmapSyntaxException(errMsg)788            except UnicodeDecodeError:789                errMsg = "invalid character provided in option '--postprocess'"790                raise SqlmapSyntaxException(errMsg)791            dirname, filename = os.path.split(script)792            dirname = os.path.abspath(dirname)793            infoMsg = "loading postprocess module '%s'" % filename[:-3]794            logger.info(infoMsg)795            if not os.path.exists(os.path.join(dirname, "__init__.py")):796                errMsg = "make sure that there is an empty file '__init__.py' "797                errMsg += "inside of postprocess scripts directory '%s'" % dirname798                raise SqlmapGenericException(errMsg)799            if dirname not in sys.path:800                sys.path.insert(0, dirname)801            try:802                module = __import__(safeFilepathEncode(filename[:-3]))803            except Exception as ex:804                raise SqlmapSyntaxException("cannot import postprocess module '%s' (%s)" % (getUnicode(filename[:-3]), getSafeExString(ex)))805            for name, function in inspect.getmembers(module, inspect.isfunction):806                if name == "postprocess" and inspect.getargspec(function).args and all(_ in inspect.getargspec(function).args for _ in ("page", "headers", "code")):807                    found = True808                    kb.postprocessFunctions.append(function)809                    function.__name__ = module.__name__810                    break811            if not found:812                errMsg = "missing function 'postprocess(page, headers=None, code=None)' "813                errMsg += "in postprocess script '%s'" % script814                raise SqlmapGenericException(errMsg)815            else:816                try:817                    _, _, _ = function("", {}, None)818                except:819                    handle, filename = tempfile.mkstemp(prefix=MKSTEMP_PREFIX.PREPROCESS, suffix=".py")820                    os.close(handle)821                    openFile(filename, "w+b").write("#!/usr/bin/env\n\ndef postprocess(page, headers=None, code=None):\n    return page, headers, code\n")822                    openFile(os.path.join(os.path.dirname(filename), "__init__.py"), "w+b").write("pass")823                    errMsg = "function 'postprocess(page, headers=None, code=None)' "824                    errMsg += "in postprocess script '%s' " % script825                    errMsg += "should return a tuple '(page, headers, code)' "826                    errMsg += "(Note: find template script at '%s')" % filename827                    raise SqlmapGenericException(errMsg)828def _setThreads():829    if not isinstance(conf.threads, int) or conf.threads <= 0:830        conf.threads = 1831def _setDNSCache():832    """833    Makes a cached version of socket._getaddrinfo to avoid subsequent DNS requests.834    """835    def _getaddrinfo(*args, **kwargs):836        if args in kb.cache.addrinfo:837            return kb.cache.addrinfo[args]838        else:839            kb.cache.addrinfo[args] = socket._getaddrinfo(*args, **kwargs)840            return kb.cache.addrinfo[args]841    if not hasattr(socket, "_getaddrinfo"):842        socket._getaddrinfo = socket.getaddrinfo843        socket.getaddrinfo = _getaddrinfo844def _setSocketPreConnect():845    """846    Makes a pre-connect version of socket.create_connection847    """848    if conf.disablePrecon:849        return850    def _thread():851        while kb.get("threadContinue") and not conf.get("disablePrecon"):852            try:853                for key in socket._ready:854                    if len(socket._ready[key]) < SOCKET_PRE_CONNECT_QUEUE_SIZE:855                        s = socket.create_connection(*key[0], **dict(key[1]))856                        with kb.locks.socket:857                            socket._ready[key].append((s, time.time()))858            except KeyboardInterrupt:859                break860            except:861                pass862            finally:863                time.sleep(0.01)864    def create_connection(*args, **kwargs):865        retVal = None866        key = (tuple(args), frozenset(kwargs.items()))867        with kb.locks.socket:868            if key not in socket._ready:869                socket._ready[key] = []870            while len(socket._ready[key]) > 0:871                candidate, created = socket._ready[key].pop(0)872                if (time.time() - created) < PRECONNECT_CANDIDATE_TIMEOUT:873                    retVal = candidate874                    break875                else:876                    try:877                        candidate.shutdown(socket.SHUT_RDWR)878                        candidate.close()879                    except socket.error:880                        pass881        if not retVal:882            retVal = socket._create_connection(*args, **kwargs)883        return retVal884    if not hasattr(socket, "_create_connection"):885        socket._ready = {}886        socket._create_connection = socket.create_connection887        socket.create_connection = create_connection888        thread = threading.Thread(target=_thread)889        setDaemon(thread)890        thread.start()891def _setHTTPHandlers():892    """893    Check and set the HTTP/SOCKS proxy for all HTTP requests.894    """895    with kb.locks.handlers:896        if conf.proxyList:897            conf.proxy = conf.proxyList[0]898            conf.proxyList = conf.proxyList[1:] + conf.proxyList[:1]899            if len(conf.proxyList) > 1:900                infoMsg = "loading proxy '%s' from a supplied proxy list file" % conf.proxy901                logger.info(infoMsg)902        elif not conf.proxy:903            if conf.hostname in ("localhost", "127.0.0.1") or conf.ignoreProxy:904                proxyHandler.proxies = {}905        if conf.proxy:906            debugMsg = "setting the HTTP/SOCKS proxy for all HTTP requests"907            logger.debug(debugMsg)908            try:909                _ = _urllib.parse.urlsplit(conf.proxy)910            except Exception as ex:911                errMsg = "invalid proxy address '%s' ('%s')" % (conf.proxy, getSafeExString(ex))912                raise SqlmapSyntaxException(errMsg)913            hostnamePort = _.netloc.rsplit(":", 1)914            scheme = _.scheme.upper()915            hostname = hostnamePort[0]916            port = None917            username = None918            password = None919            if len(hostnamePort) == 2:920                try:921                    port = int(hostnamePort[1])922                except:923                    pass  # drops into the next check block924            if not all((scheme, hasattr(PROXY_TYPE, scheme), hostname, port)):925                errMsg = "proxy value must be in format '(%s)://address:port'" % "|".join(_[0].lower() for _ in getPublicTypeMembers(PROXY_TYPE))926                raise SqlmapSyntaxException(errMsg)927            if conf.proxyCred:928                _ = re.search(r"\A(.*?):(.*?)\Z", conf.proxyCred)929                if not _:930                    errMsg = "proxy authentication credentials "931                    errMsg += "value must be in format username:password"932                    raise SqlmapSyntaxException(errMsg)933                else:934                    username = _.group(1)935                    password = _.group(2)936            if scheme in (PROXY_TYPE.SOCKS4, PROXY_TYPE.SOCKS5):937                proxyHandler.proxies = {}938                if scheme == PROXY_TYPE.SOCKS4:939                    warnMsg = "SOCKS4 does not support resolving (DNS) names (i.e. causing DNS leakage)"940                    singleTimeWarnMessage(warnMsg)941                socks.setdefaultproxy(socks.PROXY_TYPE_SOCKS5 if scheme == PROXY_TYPE.SOCKS5 else socks.PROXY_TYPE_SOCKS4, hostname, port, username=username, password=password)942                socks.wrapmodule(_http_client)943            else:944                socks.unwrapmodule(_http_client)945                if conf.proxyCred:946                    # Reference: http://stackoverflow.com/questions/34079/how-to-specify-an-authenticated-proxy-for-a-python-http-connection947                    proxyString = "%s@" % conf.proxyCred948                else:949                    proxyString = ""950                proxyString += "%s:%d" % (hostname, port)951                proxyHandler.proxies = {"http": proxyString, "https": proxyString}952            proxyHandler.__init__(proxyHandler.proxies)953        if not proxyHandler.proxies:954            for _ in ("http", "https"):955                if hasattr(proxyHandler, "%s_open" % _):956                    delattr(proxyHandler, "%s_open" % _)957        debugMsg = "creating HTTP requests opener object"958        logger.debug(debugMsg)959        handlers = filterNone([multipartPostHandler, proxyHandler if proxyHandler.proxies else None, authHandler, redirectHandler, rangeHandler, chunkedHandler if conf.chunked else None, httpsHandler])960        if not conf.dropSetCookie:961            if not conf.loadCookies:962                conf.cj = _http_cookiejar.CookieJar()963            else:964                conf.cj = _http_cookiejar.MozillaCookieJar()965                resetCookieJar(conf.cj)966            handlers.append(_urllib.request.HTTPCookieProcessor(conf.cj))967        # Reference: http://www.w3.org/Protocols/rfc2616/rfc2616-sec8.html968        if conf.keepAlive:969            warnMsg = "persistent HTTP(s) connections, Keep-Alive, has "970            warnMsg += "been disabled because of its incompatibility "971            if conf.proxy:972                warnMsg += "with HTTP(s) proxy"973                logger.warn(warnMsg)974            elif conf.authType:975                warnMsg += "with authentication methods"976                logger.warn(warnMsg)977            else:978                handlers.append(keepAliveHandler)979        opener = _urllib.request.build_opener(*handlers)980        opener.addheaders = []  # Note: clearing default "User-Agent: Python-urllib/X.Y"981        _urllib.request.install_opener(opener)982def _setSafeVisit():983    """984    Check and set the safe visit options.985    """986    if not any((conf.safeUrl, conf.safeReqFile)):987        return988    if conf.safeReqFile:989        checkFile(conf.safeReqFile)990        raw = readCachedFileContent(conf.safeReqFile)991        match = re.search(r"\A([A-Z]+) ([^ ]+) HTTP/[0-9.]+\Z", raw.split('\n')[0].strip())992        if match:993            kb.safeReq.method = match.group(1)994            kb.safeReq.url = match.group(2)995            kb.safeReq.headers = {}996            for line in raw.split('\n')[1:]:997                line = line.strip()998                if line and ':' in line:999                    key, value = line.split(':', 1)1000                    value = value.strip()1001                    kb.safeReq.headers[key] = value1002                    if key.upper() == HTTP_HEADER.HOST.upper():1003                        if not value.startswith("http"):1004                            scheme = "http"1005                            if value.endswith(":443"):1006                                scheme = "https"1007                            value = "%s://%s" % (scheme, value)1008                        kb.safeReq.url = _urllib.parse.urljoin(value, kb.safeReq.url)1009                else:1010                    break1011            post = None1012            if '\r\n\r\n' in raw:1013                post = raw[raw.find('\r\n\r\n') + 4:]1014            elif '\n\n' in raw:1015                post = raw[raw.find('\n\n') + 2:]1016            if post and post.strip():1017                kb.safeReq.post = post1018            else:1019                kb.safeReq.post = None1020        else:1021            errMsg = "invalid format of a safe request file"1022            raise SqlmapSyntaxException(errMsg)1023    else:1024        if not re.search(r"(?i)\Ahttp[s]*://", conf.safeUrl):1025            if ":443/" in conf.safeUrl:1026                conf.safeUrl = "https://%s" % conf.safeUrl1027            else:1028                conf.safeUrl = "http://%s" % conf.safeUrl1029    if (conf.safeFreq or 0) <= 0:1030        errMsg = "please provide a valid value (>0) for safe frequency ('--safe-freq') while using safe visit features"1031        raise SqlmapSyntaxException(errMsg)1032def _setPrefixSuffix():1033    if conf.prefix is not None and conf.suffix is not None:1034        # Create a custom boundary object for user's supplied prefix1035        # and suffix1036        boundary = AttribDict()1037        boundary.level = 11038        boundary.clause = [0]1039        boundary.where = [1, 2, 3]1040        boundary.prefix = conf.prefix1041        boundary.suffix = conf.suffix1042        if " like" in boundary.suffix.lower():1043            if "'" in boundary.suffix.lower():1044                boundary.ptype = 31045            elif '"' in boundary.suffix.lower():1046                boundary.ptype = 51047        elif "'" in boundary.suffix:1048            boundary.ptype = 21049        elif '"' in boundary.suffix:1050            boundary.ptype = 41051        else:1052            boundary.ptype = 11053        # user who provides --prefix/--suffix does not want other boundaries1054        # to be tested for1055        conf.boundaries = [boundary]1056def _setAuthCred():1057    """1058    Adds authentication credentials (if any) for current target to the password manager1059    (used by connection handler)1060    """1061    if kb.passwordMgr and all(_ is not None for _ in (conf.scheme, conf.hostname, conf.port, conf.authUsername, conf.authPassword)):1062        kb.passwordMgr.add_password(None, "%s://%s:%d" % (conf.scheme, conf.hostname, conf.port), conf.authUsername, conf.authPassword)1063def _setHTTPAuthentication():1064    """1065    Check and set the HTTP(s) authentication method (Basic, Digest, NTLM or PKI),1066    username and password for first three methods, or PEM private key file for1067    PKI authentication1068    """1069    global authHandler1070    if not conf.authType and not conf.authCred and not conf.authFile:1071        return1072    if conf.authFile and not conf.authType:1073        conf.authType = AUTH_TYPE.PKI1074    elif conf.authType and not conf.authCred and not conf.authFile:1075        errMsg = "you specified the HTTP authentication type, but "1076        errMsg += "did not provide the credentials"1077        raise SqlmapSyntaxException(errMsg)1078    elif not conf.authType and conf.authCred:1079        errMsg = "you specified the HTTP authentication credentials, "1080        errMsg += "but did not provide the type (e.g. --auth-type=\"basic\")"1081        raise SqlmapSyntaxException(errMsg)1082    elif (conf.authType or "").lower() not in (AUTH_TYPE.BASIC, AUTH_TYPE.DIGEST, AUTH_TYPE.NTLM, AUTH_TYPE.PKI):1083        errMsg = "HTTP authentication type value must be "1084        errMsg += "Basic, Digest, NTLM or PKI"1085        raise SqlmapSyntaxException(errMsg)1086    if not conf.authFile:1087        debugMsg = "setting the HTTP authentication type and credentials"1088        logger.debug(debugMsg)1089        authType = conf.authType.lower()1090        if authType in (AUTH_TYPE.BASIC, AUTH_TYPE.DIGEST):1091            regExp = "^(.*?):(.*?)$"1092            errMsg = "HTTP %s authentication credentials " % authType1093            errMsg += "value must be in format 'username:password'"1094        elif authType == AUTH_TYPE.NTLM:1095            regExp = "^(.*\\\\.*):(.*?)$"1096            errMsg = "HTTP NTLM authentication credentials value must "1097            errMsg += "be in format 'DOMAIN\\username:password'"1098        elif authType == AUTH_TYPE.PKI:1099            errMsg = "HTTP PKI authentication require "1100            errMsg += "usage of option `--auth-pki`"1101            raise SqlmapSyntaxException(errMsg)1102        aCredRegExp = re.search(regExp, conf.authCred)1103        if not aCredRegExp:1104            raise SqlmapSyntaxException(errMsg)1105        conf.authUsername = aCredRegExp.group(1)1106        conf.authPassword = aCredRegExp.group(2)1107        kb.passwordMgr = _urllib.request.HTTPPasswordMgrWithDefaultRealm()1108        _setAuthCred()1109        if authType == AUTH_TYPE.BASIC:1110            authHandler = SmartHTTPBasicAuthHandler(kb.passwordMgr)1111        elif authType == AUTH_TYPE.DIGEST:1112            authHandler = _urllib.request.HTTPDigestAuthHandler(kb.passwordMgr)1113        elif authType == AUTH_TYPE.NTLM:1114            try:1115                from ntlm import HTTPNtlmAuthHandler1116            except ImportError:1117                errMsg = "sqlmap requires Python NTLM third-party library "1118                errMsg += "in order to authenticate via NTLM. Download from "1119                errMsg += "'https://github.com/mullender/python-ntlm'"1120                raise SqlmapMissingDependence(errMsg)1121            authHandler = HTTPNtlmAuthHandler.HTTPNtlmAuthHandler(kb.passwordMgr)1122    else:1123        debugMsg = "setting the HTTP(s) authentication PEM private key"1124        logger.debug(debugMsg)1125        _ = safeExpandUser(conf.authFile)1126        checkFile(_)1127        authHandler = HTTPSPKIAuthHandler(_)1128def _setHTTPExtraHeaders():1129    if conf.headers:1130        debugMsg = "setting extra HTTP headers"1131        logger.debug(debugMsg)1132        conf.headers = conf.headers.split("\n") if "\n" in conf.headers else conf.headers.split("\\n")1133        for headerValue in conf.headers:1134            if not headerValue.strip():1135                continue1136            if headerValue.count(':') >= 1:1137                header, value = (_.lstrip() for _ in headerValue.split(":", 1))1138                if header and value:1139                    conf.httpHeaders.append((header, value))1140            elif headerValue.startswith('@'):1141                checkFile(headerValue[1:])1142                kb.headersFile = headerValue[1:]1143            else:1144                errMsg = "invalid header value: %s. Valid header format is 'name:value'" % repr(headerValue).lstrip('u')1145                raise SqlmapSyntaxException(errMsg)1146    elif not conf.requestFile and len(conf.httpHeaders or []) < 2:1147        if conf.encoding:1148            conf.httpHeaders.append((HTTP_HEADER.ACCEPT_CHARSET, "%s;q=0.7,*;q=0.1" % conf.encoding))1149        # Invalidating any caching mechanism in between1150        # Reference: http://stackoverflow.com/a/13833591151        conf.httpHeaders.append((HTTP_HEADER.CACHE_CONTROL, "no-cache"))1152def _setHTTPUserAgent():1153    """1154    Set the HTTP User-Agent header.1155    Depending on the user options it can be:1156        * The default sqlmap string1157        * A default value read as user option1158        * A random value read from a list of User-Agent headers from a1159          file choosed as user option1160    """1161    debugMsg = "setting the HTTP User-Agent header"1162    logger.debug(debugMsg)1163    if conf.mobile:1164        if conf.randomAgent:1165            _ = random.sample([_[1] for _ in getPublicTypeMembers(MOBILES, True)], 1)[0]1166            conf.httpHeaders.append((HTTP_HEADER.USER_AGENT, _))1167        else:1168            message = "which smartphone do you want sqlmap to imitate "1169            message += "through HTTP User-Agent header?\n"1170            items = sorted(getPublicTypeMembers(MOBILES, True))1171            for count in xrange(len(items)):1172                item = items[count]1173                message += "[%d] %s%s\n" % (count + 1, item[0], " (default)" if item == MOBILES.IPHONE else "")1174            test = readInput(message.rstrip('\n'), default=items.index(MOBILES.IPHONE) + 1)1175            try:1176                item = items[int(test) - 1]1177            except:1178                item = MOBILES.IPHONE1179            conf.httpHeaders.append((HTTP_HEADER.USER_AGENT, item[1]))1180    elif conf.agent:1181        conf.httpHeaders.append((HTTP_HEADER.USER_AGENT, conf.agent))1182    elif not conf.randomAgent:1183        _ = True1184        for header, _ in conf.httpHeaders:1185            if header.upper() == HTTP_HEADER.USER_AGENT.upper():1186                _ = False1187                break1188        if _:1189            conf.httpHeaders.append((HTTP_HEADER.USER_AGENT, DEFAULT_USER_AGENT))1190    else:1191        userAgent = fetchRandomAgent()1192        infoMsg = "fetched random HTTP User-Agent header value '%s' from " % userAgent1193        infoMsg += "file '%s'" % paths.USER_AGENTS1194        logger.info(infoMsg)1195        conf.httpHeaders.append((HTTP_HEADER.USER_AGENT, userAgent))1196def _setHTTPReferer():1197    """1198    Set the HTTP Referer1199    """1200    if conf.referer:1201        debugMsg = "setting the HTTP Referer header"1202        logger.debug(debugMsg)1203        conf.httpHeaders.append((HTTP_HEADER.REFERER, conf.referer))1204def _setHTTPHost():1205    """1206    Set the HTTP Host1207    """1208    if conf.host:1209        debugMsg = "setting the HTTP Host header"1210        logger.debug(debugMsg)1211        conf.httpHeaders.append((HTTP_HEADER.HOST, conf.host))1212def _setHTTPCookies():1213    """1214    Set the HTTP Cookie header1215    """1216    if conf.cookie:1217        debugMsg = "setting the HTTP Cookie header"1218        logger.debug(debugMsg)1219        conf.httpHeaders.append((HTTP_HEADER.COOKIE, conf.cookie))1220def _setHostname():1221    """1222    Set value conf.hostname1223    """1224    if conf.url:1225        try:1226            conf.hostname = _urllib.parse.urlsplit(conf.url).netloc.split(':')[0]1227        except ValueError as ex:1228            errMsg = "problem occurred while "1229            errMsg += "parsing an URL '%s' ('%s')" % (conf.url, getSafeExString(ex))1230            raise SqlmapDataException(errMsg)1231def _setHTTPTimeout():1232    """1233    Set the HTTP timeout1234    """1235    if conf.timeout:1236        debugMsg = "setting the HTTP timeout"1237        logger.debug(debugMsg)1238        conf.timeout = float(conf.timeout)1239        if conf.timeout < 3.0:1240            warnMsg = "the minimum HTTP timeout is 3 seconds, sqlmap "1241            warnMsg += "will going to reset it"1242            logger.warn(warnMsg)1243            conf.timeout = 3.01244    else:1245        conf.timeout = 30.01246    try:1247        socket.setdefaulttimeout(conf.timeout)1248    except OverflowError as ex:1249        raise SqlmapValueException("invalid value used for option '--timeout' ('%s')" % getSafeExString(ex))1250def _checkDependencies():1251    """1252    Checks for missing dependencies.1253    """1254    if conf.dependencies:1255        checkDependencies()1256def _createHomeDirectories():1257    """1258    Creates directories inside sqlmap's home directory1259    """1260    if conf.get("purge"):1261        return1262    for context in ("output", "history"):1263        directory = paths["SQLMAP_%s_PATH" % getUnicode(context).upper()]   # NOTE: https://github.com/sqlmapproject/sqlmap/issues/43631264        try:1265            if not os.path.isdir(directory):1266                os.makedirs(directory)1267            _ = os.path.join(directory, randomStr())1268            open(_, "w+b").close()1269            os.remove(_)1270            if conf.get("outputDir") and context == "output":1271                warnMsg = "using '%s' as the %s directory" % (directory, context)1272                logger.warn(warnMsg)1273        except (OSError, IOError) as ex:1274            tempDir = tempfile.mkdtemp(prefix="sqlmap%s" % context)1275            warnMsg = "unable to %s %s directory " % ("create" if not os.path.isdir(directory) else "write to the", context)1276            warnMsg += "'%s' (%s). " % (directory, getUnicode(ex))1277            warnMsg += "Using temporary directory '%s' instead" % getUnicode(tempDir)1278            logger.warn(warnMsg)1279            paths["SQLMAP_%s_PATH" % context.upper()] = tempDir1280def _pympTempLeakPatch(tempDir):  # Cross-referenced function1281    raise NotImplementedError1282def _createTemporaryDirectory():1283    """1284    Creates temporary directory for this run.1285    """1286    if conf.tmpDir:1287        try:1288            if not os.path.isdir(conf.tmpDir):1289                os.makedirs(conf.tmpDir)1290            _ = os.path.join(conf.tmpDir, randomStr())1291            open(_, "w+b").close()1292            os.remove(_)1293            tempfile.tempdir = conf.tmpDir1294            warnMsg = "using '%s' as the temporary directory" % conf.tmpDir1295            logger.warn(warnMsg)1296        except (OSError, IOError) as ex:1297            errMsg = "there has been a problem while accessing "1298            errMsg += "temporary directory location(s) ('%s')" % getSafeExString(ex)1299            raise SqlmapSystemException(errMsg)1300    else:1301        try:1302            if not os.path.isdir(tempfile.gettempdir()):1303                os.makedirs(tempfile.gettempdir())1304        except Exception as ex:1305            warnMsg = "there has been a problem while accessing "1306            warnMsg += "system's temporary directory location(s) ('%s'). Please " % getSafeExString(ex)1307            warnMsg += "make sure that there is enough disk space left. If problem persists, "1308            warnMsg += "try to set environment variable 'TEMP' to a location "1309            warnMsg += "writeable by the current user"1310            logger.warn(warnMsg)1311    if "sqlmap" not in (tempfile.tempdir or "") or conf.tmpDir and tempfile.tempdir == conf.tmpDir:1312        try:1313            tempfile.tempdir = tempfile.mkdtemp(prefix="sqlmap", suffix=str(os.getpid()))1314        except:1315            tempfile.tempdir = os.path.join(paths.SQLMAP_HOME_PATH, "tmp", "sqlmap%s%d" % (randomStr(6), os.getpid()))1316    kb.tempDir = tempfile.tempdir1317    if not os.path.isdir(tempfile.tempdir):1318        try:1319            os.makedirs(tempfile.tempdir)1320        except Exception as ex:1321            errMsg = "there has been a problem while setting "1322            errMsg += "temporary directory location ('%s')" % getSafeExString(ex)1323            raise SqlmapSystemException(errMsg)1324    if six.PY3:1325        _pympTempLeakPatch(kb.tempDir)1326def _cleanupOptions():1327    """1328    Cleanup configuration attributes.1329    """1330    if conf.encoding:1331        try:1332            codecs.lookup(conf.encoding)1333        except LookupError:1334            errMsg = "unknown encoding '%s'" % conf.encoding1335            raise SqlmapValueException(errMsg)1336    debugMsg = "cleaning up configuration parameters"1337    logger.debug(debugMsg)1338    width = getConsoleWidth()1339    if conf.eta:1340        conf.progressWidth = width - 261341    else:1342        conf.progressWidth = width - 461343    for key, value in conf.items():1344        if value and any(key.endswith(_) for _ in ("Path", "File", "Dir")):1345            if isinstance(value, str):1346                conf[key] = safeExpandUser(value)1347    if conf.testParameter:1348        conf.testParameter = urldecode(conf.testParameter)1349        conf.testParameter = [_.strip() for _ in re.split(PARAMETER_SPLITTING_REGEX, conf.testParameter)]1350    else:1351        conf.testParameter = []1352    if conf.ignoreCode:1353        if conf.ignoreCode == IGNORE_CODE_WILDCARD:1354            conf.ignoreCode = xrange(0, 1000)1355        else:1356            try:1357                conf.ignoreCode = [int(_) for _ in re.split(PARAMETER_SPLITTING_REGEX, conf.ignoreCode)]1358            except ValueError:1359                errMsg = "options '--ignore-code' should contain a list of integer values or a wildcard value '%s'" % IGNORE_CODE_WILDCARD1360                raise SqlmapSyntaxException(errMsg)1361    else:1362        conf.ignoreCode = []1363    if conf.paramFilter:1364        conf.paramFilter = [_.strip() for _ in re.split(PARAMETER_SPLITTING_REGEX, conf.paramFilter.upper())]1365    else:1366        conf.paramFilter = []1367    if conf.base64Parameter:1368        conf.base64Parameter = urldecode(conf.base64Parameter)1369        conf.base64Parameter = conf.base64Parameter.strip()1370        conf.base64Parameter = re.split(PARAMETER_SPLITTING_REGEX, conf.base64Parameter)1371    else:1372        conf.base64Parameter = []1373    if conf.agent:1374        conf.agent = re.sub(r"[\r\n]", "", conf.agent)1375    if conf.user:1376        conf.user = conf.user.replace(" ", "")1377    if conf.rParam:1378        if all(_ in conf.rParam for _ in ('=', ',')):1379            original = conf.rParam1380            conf.rParam = []1381            for part in original.split(';'):1382                if '=' in part:1383                    left, right = part.split('=', 1)1384                    conf.rParam.append(left)1385                    kb.randomPool[left] = filterNone(_.strip() for _ in right.split(','))1386                else:1387                    conf.rParam.append(part)1388        else:1389            conf.rParam = conf.rParam.replace(" ", "")1390            conf.rParam = re.split(PARAMETER_SPLITTING_REGEX, conf.rParam)1391    else:1392        conf.rParam = []1393    if conf.paramDel:1394        conf.paramDel = decodeStringEscape(conf.paramDel)1395    if conf.skip:1396        conf.skip = conf.skip.replace(" ", "")1397        conf.skip = re.split(PARAMETER_SPLITTING_REGEX, conf.skip)1398    else:1399        conf.skip = []1400    if conf.cookie:1401        conf.cookie = re.sub(r"[\r\n]", "", conf.cookie)1402    if conf.delay:1403        conf.delay = float(conf.delay)1404    if conf.url:1405        conf.url = conf.url.strip().lstrip('/')1406        if not re.search(r"\A\w+://", conf.url):1407            conf.url = "http://%s" % conf.url1408    if conf.fileRead:1409        conf.fileRead = ntToPosixSlashes(normalizePath(conf.fileRead))1410    if conf.fileWrite:1411        conf.fileWrite = ntToPosixSlashes(normalizePath(conf.fileWrite))1412    if conf.fileDest:1413        conf.fileDest = ntToPosixSlashes(normalizePath(conf.fileDest))1414    if conf.msfPath:1415        conf.msfPath = ntToPosixSlashes(normalizePath(conf.msfPath))1416    if conf.tmpPath:1417        conf.tmpPath = ntToPosixSlashes(normalizePath(conf.tmpPath))1418    if any((conf.googleDork, conf.logFile, conf.bulkFile, conf.forms, conf.crawlDepth, conf.stdinPipe)):1419        conf.multipleTargets = True1420    if conf.optimize:1421        setOptimize()1422    if conf.os:1423        conf.os = conf.os.capitalize()1424    if conf.forceDbms:1425        conf.dbms = conf.forceDbms1426    if conf.dbms:1427        kb.dbmsFilter = []1428        for _ in conf.dbms.split(','):1429            for dbms, aliases in DBMS_ALIASES:1430                if _.strip().lower() in aliases:1431                    kb.dbmsFilter.append(dbms)1432                    conf.dbms = dbms if conf.dbms and ',' not in conf.dbms else None1433                    break1434    if conf.testFilter:1435        conf.testFilter = conf.testFilter.strip('*+')1436        conf.testFilter = re.sub(r"([^.])([*+])", r"\g<1>.\g<2>", conf.testFilter)1437        try:1438            re.compile(conf.testFilter)1439        except re.error:1440            conf.testFilter = re.escape(conf.testFilter)1441    if conf.csrfToken:1442        original = conf.csrfToken1443        try:1444            re.compile(conf.csrfToken)1445            if re.escape(conf.csrfToken) != conf.csrfToken:1446                message = "provided value for option '--csrf-token' is a regular expression? [y/N] "1447                if not readInput(message, default='N', boolean=True):1448                    conf.csrfToken = re.escape(conf.csrfToken)1449        except re.error:1450            conf.csrfToken = re.escape(conf.csrfToken)1451        finally:1452            class _(six.text_type):1453                pass1454            conf.csrfToken = _(conf.csrfToken)1455            conf.csrfToken._original = original1456    if conf.testSkip:1457        conf.testSkip = conf.testSkip.strip('*+')1458        conf.testSkip = re.sub(r"([^.])([*+])", r"\g<1>.\g<2>", conf.testSkip)1459        try:1460            re.compile(conf.testSkip)1461        except re.error:1462            conf.testSkip = re.escape(conf.testSkip)1463    if "timeSec" not in kb.explicitSettings:1464        if conf.tor:1465            conf.timeSec = 2 * conf.timeSec1466            kb.adjustTimeDelay = ADJUST_TIME_DELAY.DISABLE1467            warnMsg = "increasing default value for "1468            warnMsg += "option '--time-sec' to %d because " % conf.timeSec1469            warnMsg += "switch '--tor' was provided"1470            logger.warn(warnMsg)1471    else:1472        kb.adjustTimeDelay = ADJUST_TIME_DELAY.DISABLE1473    if conf.retries:1474        conf.retries = min(conf.retries, MAX_CONNECT_RETRIES)1475    if conf.code:1476        conf.code = int(conf.code)1477    if conf.csvDel:1478        conf.csvDel = decodeStringEscape(conf.csvDel)1479    if conf.torPort and hasattr(conf.torPort, "isdigit") and conf.torPort.isdigit():1480        conf.torPort = int(conf.torPort)1481    if conf.torType:1482        conf.torType = conf.torType.upper()1483    if conf.outputDir:1484        paths.SQLMAP_OUTPUT_PATH = os.path.realpath(os.path.expanduser(conf.outputDir))1485        setPaths(paths.SQLMAP_ROOT_PATH)1486    if conf.string:1487        conf.string = decodeStringEscape(conf.string)1488    if conf.getAll:1489        for _ in WIZARD.ALL:1490            conf.__setitem__(_, True)1491    if conf.noCast:1492        DUMP_REPLACEMENTS.clear()1493    if conf.dumpFormat:1494        conf.dumpFormat = conf.dumpFormat.upper()1495    if conf.torType:1496        conf.torType = conf.torType.upper()1497    if conf.col:1498        conf.col = re.sub(r"\s*,\s*", ',', conf.col)1499    if conf.exclude:1500        regex = False1501        original = conf.exclude1502        if any(_ in conf.exclude for _ in ('+', '*')):1503            try:1504                re.compile(conf.exclude)1505            except re.error:1506                pass1507            else:1508                regex = True1509        if not regex:1510            conf.exclude = re.sub(r"\s*,\s*", ',', conf.exclude)1511            conf.exclude = r"\A%s\Z" % '|'.join(re.escape(_) for _ in conf.exclude.split(','))1512        else:1513            conf.exclude = re.sub(r"(\w+)\$", r"\g<1>\$", conf.exclude)1514        class _(six.text_type):1515            pass1516        conf.exclude = _(conf.exclude)1517        conf.exclude._original = original1518    if conf.binaryFields:1519        conf.binaryFields = conf.binaryFields.replace(" ", "")1520        conf.binaryFields = re.split(PARAMETER_SPLITTING_REGEX, conf.binaryFields)1521    envProxy = max(os.environ.get(_, "") for _ in PROXY_ENVIRONMENT_VARIABLES)1522    if re.search(r"\A(https?|socks[45])://.+:\d+\Z", envProxy) and conf.proxy is None:1523        debugMsg = "using environment proxy '%s'" % envProxy1524        logger.debug(debugMsg)1525        conf.proxy = envProxy1526    if any((conf.proxy, conf.proxyFile, conf.tor)):1527        conf.disablePrecon = True1528    if conf.dummy:1529        conf.batch = True1530    threadData = getCurrentThreadData()1531    threadData.reset()1532def _cleanupEnvironment():1533    """1534    Cleanup environment (e.g. from leftovers after --sqlmap-shell).1535    """1536    if issubclass(_http_client.socket.socket, socks.socksocket):1537        socks.unwrapmodule(_http_client)1538    if hasattr(socket, "_ready"):1539        socket._ready.clear()1540def _purge():1541    """1542    Safely removes (purges) sqlmap data directory.1543    """1544    if conf.purge:1545        purge(paths.SQLMAP_HOME_PATH)1546def _setConfAttributes():1547    """1548    This function set some needed attributes into the configuration1549    singleton.1550    """1551    debugMsg = "initializing the configuration"1552    logger.debug(debugMsg)1553    conf.authUsername = None1554    conf.authPassword = None1555    conf.boundaries = []1556    conf.cj = None1557    conf.dbmsConnector = None1558    conf.dbmsHandler = None1559    conf.dnsServer = None1560    conf.dumpPath = None1561    conf.hashDB = None1562    conf.hashDBFile = None1563    conf.httpCollector = None1564    conf.httpHeaders = []1565    conf.hostname = None1566    conf.ipv6 = False1567    conf.multipleTargets = False1568    conf.outputPath = None1569    conf.paramDict = {}1570    conf.parameters = {}1571    conf.path = None1572    conf.port = None1573    conf.proxyList = None1574    conf.resultsFP = None1575    conf.scheme = None1576    conf.tests = []1577    conf.trafficFP = None1578    conf.HARCollectorFactory = None1579    conf.fileWriteType = None1580def _setKnowledgeBaseAttributes(flushAll=True):1581    """1582    This function set some needed attributes into the knowledge base1583    singleton.1584    """1585    debugMsg = "initializing the knowledge base"1586    logger.debug(debugMsg)1587    kb.absFilePaths = set()1588    kb.adjustTimeDelay = None1589    kb.alerted = False1590    kb.aliasName = randomStr()1591    kb.alwaysRefresh = None1592    kb.arch = None1593    kb.authHeader = None1594    kb.bannerFp = AttribDict()1595    kb.base64Originals = {}1596    kb.binaryField = False1597    kb.browserVerification = None1598    kb.brute = AttribDict({"tables": [], "columns": []})1599    kb.bruteMode = False1600    kb.cache = AttribDict()1601    kb.cache.addrinfo = {}1602    kb.cache.content = {}1603    kb.cache.encoding = {}1604    kb.cache.alphaBoundaries = None1605    kb.cache.hashRegex = None1606    kb.cache.intBoundaries = None1607    kb.cache.parsedDbms = {}1608    kb.cache.regex = {}1609    kb.cache.stdev = {}1610    kb.captchaDetected = None1611    kb.chars = AttribDict()1612    kb.chars.delimiter = randomStr(length=6, lowercase=True)1613    kb.chars.start = "%s%s%s" % (KB_CHARS_BOUNDARY_CHAR, randomStr(length=3, alphabet=KB_CHARS_LOW_FREQUENCY_ALPHABET), KB_CHARS_BOUNDARY_CHAR)1614    kb.chars.stop = "%s%s%s" % (KB_CHARS_BOUNDARY_CHAR, randomStr(length=3, alphabet=KB_CHARS_LOW_FREQUENCY_ALPHABET), KB_CHARS_BOUNDARY_CHAR)1615    kb.chars.at, kb.chars.space, kb.chars.dollar, kb.chars.hash_ = ("%s%s%s" % (KB_CHARS_BOUNDARY_CHAR, _, KB_CHARS_BOUNDARY_CHAR) for _ in randomStr(length=4, lowercase=True))1616    kb.choices = AttribDict(keycheck=False)1617    kb.codePage = None1618    kb.commonOutputs = None1619    kb.connErrorCounter = 01620    kb.copyExecTest = None1621    kb.counters = {}1622    kb.customInjectionMark = CUSTOM_INJECTION_MARK_CHAR1623    kb.data = AttribDict()1624    kb.dataOutputFlag = False1625    # Active back-end DBMS fingerprint1626    kb.dbms = None1627    kb.dbmsFilter = []1628    kb.dbmsVersion = [UNKNOWN_DBMS_VERSION]1629    kb.delayCandidates = TIME_DELAY_CANDIDATES * [0]1630    kb.dep = None1631    kb.disableHtmlDecoding = False1632    kb.dnsMode = False1633    kb.dnsTest = None1634    kb.docRoot = None1635    kb.droppingRequests = False1636    kb.dumpColumns = None1637    kb.dumpTable = None1638    kb.dumpKeyboardInterrupt = False1639    kb.dynamicMarkings = []1640    kb.dynamicParameter = False1641    kb.endDetection = False1642    kb.explicitSettings = set()1643    kb.extendTests = None1644    kb.errorChunkLength = None1645    kb.errorIsNone = True1646    kb.falsePositives = []1647    kb.fileReadMode = False1648    kb.fingerprinted = False1649    kb.followSitemapRecursion = None1650    kb.forcedDbms = None1651    kb.forcePartialUnion = False1652    kb.forceThreads = None1653    kb.forceWhere = None1654    kb.forkNote = None1655    kb.futileUnion = None1656    kb.fuzzUnionTest = None1657    kb.heavilyDynamic = False1658    kb.headersFile = None1659    kb.headersFp = {}1660    kb.heuristicDbms = None1661    kb.heuristicExtendedDbms = None1662    kb.heuristicMode = False1663    kb.heuristicPage = False1664    kb.heuristicTest = None1665    kb.hintValue = ""1666    kb.htmlFp = []1667    kb.httpErrorCodes = {}1668    kb.inferenceMode = False1669    kb.ignoreCasted = None1670    kb.ignoreNotFound = False1671    kb.ignoreTimeout = False1672    kb.identifiedWafs = set()1673    kb.injection = InjectionDict()1674    kb.injections = []1675    kb.jsonAggMode = False1676    kb.laggingChecked = False1677    kb.lastParserStatus = None1678    kb.locks = AttribDict()1679    for _ in ("cache", "connError", "count", "handlers", "hint", "index", "io", "limit", "liveCookies", "log", "socket", "redirect", "request", "value"):1680        kb.locks[_] = threading.Lock()1681    kb.matchRatio = None1682    kb.maxConnectionsFlag = False1683    kb.mergeCookies = None1684    kb.multipleCtrlC = False1685    kb.negativeLogic = False1686    kb.nchar = True1687    kb.nullConnection = None1688    kb.oldMsf = None1689    kb.orderByColumns = None1690    kb.originalCode = None1691    kb.originalPage = None1692    kb.originalPageTime = None1693    kb.originalTimeDelay = None1694    kb.originalUrls = dict()1695    # Back-end DBMS underlying operating system fingerprint via banner (-b)1696    # parsing1697    kb.os = None1698    kb.osVersion = None1699    kb.osSP = None1700    kb.pageCompress = True1701    kb.pageTemplate = None1702    kb.pageTemplates = dict()1703    kb.pageEncoding = DEFAULT_PAGE_ENCODING1704    kb.pageStable = None1705    kb.partRun = None1706    kb.permissionFlag = False1707    kb.postHint = None1708    kb.postSpaceToPlus = False1709    kb.postUrlEncode = True1710    kb.prependFlag = False1711    kb.processResponseCounter = 01712    kb.previousMethod = None1713    kb.processUserMarks = None1714    kb.proxyAuthHeader = None1715    kb.queryCounter = 01716    kb.randomPool = {}1717    kb.reflectiveMechanism = True1718    kb.reflectiveCounters = {REFLECTIVE_COUNTER.MISS: 0, REFLECTIVE_COUNTER.HIT: 0}1719    kb.requestCounter = 01720    kb.resendPostOnRedirect = None1721    kb.resolutionDbms = None1722    kb.responseTimes = {}1723    kb.responseTimeMode = None1724    kb.responseTimePayload = None1725    kb.resumeValues = True1726    kb.safeCharEncode = False1727    kb.safeReq = AttribDict()1728    kb.secondReq = None1729    kb.serverHeader = None1730    kb.singleLogFlags = set()1731    kb.skipSeqMatcher = False1732    kb.smokeMode = False1733    kb.reduceTests = None1734    kb.sslSuccess = False1735    kb.stickyDBMS = False1736    kb.suppressResumeInfo = False1737    kb.tableFrom = None1738    kb.technique = None1739    kb.tempDir = None1740    kb.testMode = False1741    kb.testOnlyCustom = False1742    kb.testQueryCount = 01743    kb.testType = None1744    kb.threadContinue = True1745    kb.threadException = False1746    kb.tlsSNI = {}1747    kb.uChar = NULL1748    kb.udfFail = False1749    kb.unionDuplicates = False1750    kb.unionTemplate = None1751    kb.webSocketRecvCount = None1752    kb.wizardMode = False1753    kb.xpCmdshellAvailable = False1754    if flushAll:1755        kb.checkSitemap = None1756        kb.headerPaths = {}1757        kb.keywords = set(getFileItems(paths.SQL_KEYWORDS))1758        kb.lastCtrlCTime = None1759        kb.normalizeCrawlingChoice = None1760        kb.passwordMgr = None1761        kb.postprocessFunctions = []1762        kb.preprocessFunctions = []1763        kb.skipVulnHost = None1764        kb.storeCrawlingChoice = None1765        kb.tamperFunctions = []1766        kb.targets = OrderedSet()1767        kb.testedParams = set()1768        kb.userAgents = None1769        kb.vainRun = True1770        kb.vulnHosts = set()1771        kb.wafFunctions = []1772        kb.wordlists = None1773def _useWizardInterface():1774    """1775    Presents simple wizard interface for beginner users1776    """1777    if not conf.wizard:1778        return1779    logger.info("starting wizard interface")1780    while not conf.url:1781        message = "Please enter full target URL (-u): "1782        conf.url = readInput(message, default=None)1783    message = "%s data (--data) [Enter for None]: " % ((conf.method if conf.method != HTTPMETHOD.GET else None) or HTTPMETHOD.POST)1784    conf.data = readInput(message, default=None)1785    if not (any('=' in _ for _ in (conf.url, conf.data)) or '*' in conf.url):1786        warnMsg = "no GET and/or %s parameter(s) found for testing " % ((conf.method if conf.method != HTTPMETHOD.GET else None) or HTTPMETHOD.POST)1787        warnMsg += "(e.g. GET parameter 'id' in 'http://www.site.com/vuln.php?id=1'). "1788        if not conf.crawlDepth and not conf.forms:1789            warnMsg += "Will search for forms"1790            conf.forms = True1791        logger.warn(warnMsg)1792    choice = None1793    while choice is None or choice not in ("", "1", "2", "3"):1794        message = "Injection difficulty (--level/--risk). Please choose:\n"1795        message += "[1] Normal (default)\n[2] Medium\n[3] Hard"1796        choice = readInput(message, default='1')1797        if choice == '2':1798            conf.risk = 21799            conf.level = 31800        elif choice == '3':1801            conf.risk = 31802            conf.level = 51803        else:1804            conf.risk = 11805            conf.level = 11806    if not conf.getAll:1807        choice = None1808        while choice is None or choice not in ("", "1", "2", "3"):1809            message = "Enumeration (--banner/--current-user/etc). Please choose:\n"1810            message += "[1] Basic (default)\n[2] Intermediate\n[3] All"1811            choice = readInput(message, default='1')1812            if choice == '2':1813                options = WIZARD.INTERMEDIATE1814            elif choice == '3':1815                options = WIZARD.ALL1816            else:1817                options = WIZARD.BASIC1818            for _ in options:1819                conf.__setitem__(_, True)1820    logger.debug("muting sqlmap.. it will do the magic for you")1821    conf.verbose = 01822    conf.batch = True1823    conf.threads = 41824    dataToStdout("\nsqlmap is running, please wait..\n\n")1825    kb.wizardMode = True1826def _saveConfig():1827    """1828    Saves the command line options to a sqlmap configuration INI file1829    Format.1830    """1831    if not conf.saveConfig:1832        return1833    debugMsg = "saving command line options to a sqlmap configuration INI file"1834    logger.debug(debugMsg)1835    saveConfig(conf, conf.saveConfig)1836    infoMsg = "saved command line options to the configuration file '%s'" % conf.saveConfig1837    logger.info(infoMsg)1838def setVerbosity():1839    """1840    This function set the verbosity of sqlmap output messages.1841    """1842    if conf.verbose is None:1843        conf.verbose = 11844    conf.verbose = int(conf.verbose)1845    if conf.verbose == 0:1846        logger.setLevel(logging.ERROR)1847    elif conf.verbose == 1:1848        logger.setLevel(logging.INFO)1849    elif conf.verbose > 2 and conf.eta:1850        conf.verbose = 21851        logger.setLevel(logging.DEBUG)1852    elif conf.verbose == 2:1853        logger.setLevel(logging.DEBUG)1854    elif conf.verbose == 3:1855        logger.setLevel(CUSTOM_LOGGING.PAYLOAD)1856    elif conf.verbose == 4:1857        logger.setLevel(CUSTOM_LOGGING.TRAFFIC_OUT)1858    elif conf.verbose >= 5:1859        logger.setLevel(CUSTOM_LOGGING.TRAFFIC_IN)1860def _normalizeOptions(inputOptions):1861    """1862    Sets proper option types1863    """1864    types_ = {}1865    for group in optDict.keys():1866        types_.update(optDict[group])1867    for key in inputOptions:1868        if key in types_:1869            value = inputOptions[key]1870            if value is None:1871                continue1872            type_ = types_[key]1873            if type_ and isinstance(type_, tuple):1874                type_ = type_[0]1875            if type_ == OPTION_TYPE.BOOLEAN:1876                try:1877                    value = bool(value)1878                except (TypeError, ValueError):1879                    value = False1880            elif type_ == OPTION_TYPE.INTEGER:1881                try:1882                    value = int(value)1883                except (TypeError, ValueError):1884                    value = 01885            elif type_ == OPTION_TYPE.FLOAT:1886                try:1887                    value = float(value)1888                except (TypeError, ValueError):1889                    value = 0.01890            inputOptions[key] = value1891def _mergeOptions(inputOptions, overrideOptions):1892    """1893    Merge command line options with configuration file and default options.1894    @param inputOptions: optparse object with command line options.1895    @type inputOptions: C{instance}1896    """1897    if inputOptions.configFile:1898        configFileParser(inputOptions.configFile)1899    if hasattr(inputOptions, "items"):1900        inputOptionsItems = inputOptions.items()1901    else:1902        inputOptionsItems = inputOptions.__dict__.items()1903    for key, value in inputOptionsItems:1904        if key not in conf or value not in (None, False) or overrideOptions:1905            conf[key] = value1906    if not conf.api:1907        for key, value in conf.items():1908            if value is not None:1909                kb.explicitSettings.add(key)1910    for key, value in defaults.items():1911        if hasattr(conf, key) and conf[key] is None:1912            conf[key] = value1913            if conf.unstable:1914                if key in ("timeSec", "retries", "timeout"):1915                    conf[key] *= 21916    if conf.unstable:1917        conf.forcePartial = True1918    lut = {}1919    for group in optDict.keys():1920        lut.update((_.upper(), _) for _ in optDict[group])1921    envOptions = {}1922    for key, value in os.environ.items():1923        if key.upper().startswith(SQLMAP_ENVIRONMENT_PREFIX):1924            _ = key[len(SQLMAP_ENVIRONMENT_PREFIX):].upper()1925            if _ in lut:1926                envOptions[lut[_]] = value1927    if envOptions:1928        _normalizeOptions(envOptions)1929        for key, value in envOptions.items():1930            conf[key] = value1931    mergedOptions.update(conf)1932def _setTrafficOutputFP():1933    if conf.trafficFile:1934        infoMsg = "setting file for logging HTTP traffic"1935        logger.info(infoMsg)1936        conf.trafficFP = openFile(conf.trafficFile, "w+")1937def _setupHTTPCollector():1938    if not conf.harFile:1939        return1940    conf.httpCollector = HTTPCollectorFactory(conf.harFile).create()1941def _setDNSServer():1942    if not conf.dnsDomain:1943        return1944    infoMsg = "setting up DNS server instance"1945    logger.info(infoMsg)1946    isAdmin = runningAsAdmin()1947    if isAdmin:1948        try:1949            conf.dnsServer = DNSServer()1950            conf.dnsServer.run()1951        except socket.error as ex:1952            errMsg = "there was an error while setting up "1953            errMsg += "DNS server instance ('%s')" % getSafeExString(ex)1954            raise SqlmapGenericException(errMsg)1955    else:1956        errMsg = "you need to run sqlmap as an administrator "1957        errMsg += "if you want to perform a DNS data exfiltration attack "1958        errMsg += "as it will need to listen on privileged UDP port 53 "1959        errMsg += "for incoming address resolution attempts"1960        raise SqlmapMissingPrivileges(errMsg)1961def _setProxyList():1962    if not conf.proxyFile:1963        return1964    conf.proxyList = []1965    for match in re.finditer(r"(?i)((http[^:]*|socks[^:]*)://)?([\w\-.]+):(\d+)", readCachedFileContent(conf.proxyFile)):1966        _, type_, address, port = match.groups()1967        conf.proxyList.append("%s://%s:%s" % (type_ or "http", address, port))1968def _setTorProxySettings():1969    if not conf.tor:1970        return1971    if conf.torType == PROXY_TYPE.HTTP:1972        _setTorHttpProxySettings()1973    else:1974        _setTorSocksProxySettings()1975def _setTorHttpProxySettings():1976    infoMsg = "setting Tor HTTP proxy settings"1977    logger.info(infoMsg)1978    port = findLocalPort(DEFAULT_TOR_HTTP_PORTS if not conf.torPort else (conf.torPort,))1979    if port:1980        conf.proxy = "http://%s:%d" % (LOCALHOST, port)1981    else:1982        errMsg = "can't establish connection with the Tor HTTP proxy. "1983        errMsg += "Please make sure that you have Tor (bundle) installed and setup "1984        errMsg += "so you could be able to successfully use switch '--tor' "1985        raise SqlmapConnectionException(errMsg)1986    if not conf.checkTor:1987        warnMsg = "use switch '--check-tor' at "1988        warnMsg += "your own convenience when accessing "1989        warnMsg += "Tor anonymizing network because of "1990        warnMsg += "known issues with default settings of various 'bundles' "1991        warnMsg += "(e.g. Vidalia)"1992        logger.warn(warnMsg)1993def _setTorSocksProxySettings():1994    infoMsg = "setting Tor SOCKS proxy settings"1995    logger.info(infoMsg)1996    port = findLocalPort(DEFAULT_TOR_SOCKS_PORTS if not conf.torPort else (conf.torPort,))1997    if not port:1998        errMsg = "can't establish connection with the Tor SOCKS proxy. "1999        errMsg += "Please make sure that you have Tor service installed and setup "2000        errMsg += "so you could be able to successfully use switch '--tor' "2001        raise SqlmapConnectionException(errMsg)2002    # SOCKS5 to prevent DNS leaks (http://en.wikipedia.org/wiki/Tor_%28anonymity_network%29)2003    socks.setdefaultproxy(socks.PROXY_TYPE_SOCKS5 if conf.torType == PROXY_TYPE.SOCKS5 else socks.PROXY_TYPE_SOCKS4, LOCALHOST, port)2004    socks.wrapmodule(_http_client)2005def _setHttpChunked():2006    if conf.chunked and conf.data:2007        _http_client.HTTPConnection._set_content_length = lambda self, a, b: None2008def _checkWebSocket():2009    if conf.url and (conf.url.startswith("ws:/") or conf.url.startswith("wss:/")):2010        try:2011            from websocket import ABNF2012        except ImportError:2013            errMsg = "sqlmap requires third-party module 'websocket-client' "2014            errMsg += "in order to use WebSocket functionality"2015            raise SqlmapMissingDependence(errMsg)2016def _checkTor():2017    if not conf.checkTor:2018        return2019    infoMsg = "checking Tor connection"2020    logger.info(infoMsg)2021    try:2022        page, _, _ = Request.getPage(url="https://check.torproject.org/", raise404=False)2023    except SqlmapConnectionException:2024        page = None2025    if not page or "Congratulations" not in page:2026        errMsg = "it appears that Tor is not properly set. Please try using options '--tor-type' and/or '--tor-port'"2027        raise SqlmapConnectionException(errMsg)2028    else:2029        infoMsg = "Tor is properly being used"2030        logger.info(infoMsg)2031def _basicOptionValidation():2032    if conf.limitStart is not None and not (isinstance(conf.limitStart, int) and conf.limitStart > 0):2033        errMsg = "value for option '--start' (limitStart) must be an integer value greater than zero (>0)"2034        raise SqlmapSyntaxException(errMsg)2035    if conf.limitStop is not None and not (isinstance(conf.limitStop, int) and conf.limitStop > 0):2036        errMsg = "value for option '--stop' (limitStop) must be an integer value greater than zero (>0)"2037        raise SqlmapSyntaxException(errMsg)2038    if conf.level is not None and not (isinstance(conf.level, int) and conf.level >= 1 and conf.level <= 5):2039        errMsg = "value for option '--level' must be an integer value from range [1, 5]"2040        raise SqlmapSyntaxException(errMsg)2041    if conf.risk is not None and not (isinstance(conf.risk, int) and conf.risk >= 1 and conf.risk <= 3):2042        errMsg = "value for option '--risk' must be an integer value from range [1, 3]"2043        raise SqlmapSyntaxException(errMsg)2044    if isinstance(conf.limitStart, int) and conf.limitStart > 0 and \2045       isinstance(conf.limitStop, int) and conf.limitStop < conf.limitStart:2046        warnMsg = "usage of option '--start' (limitStart) which is bigger than value for --stop (limitStop) option is considered unstable"2047        logger.warn(warnMsg)2048    if isinstance(conf.firstChar, int) and conf.firstChar > 0 and \2049       isinstance(conf.lastChar, int) and conf.lastChar < conf.firstChar:2050        errMsg = "value for option '--first' (firstChar) must be smaller than or equal to value for --last (lastChar) option"2051        raise SqlmapSyntaxException(errMsg)2052    if conf.textOnly and conf.nullConnection:2053        errMsg = "switch '--text-only' is incompatible with switch '--null-connection'"2054        raise SqlmapSyntaxException(errMsg)2055    if conf.eta and conf.verbose > defaults.verbose:2056        errMsg = "switch '--eta' is incompatible with option '-v'"2057        raise SqlmapSyntaxException(errMsg)2058    if conf.secondUrl and conf.secondReq:2059        errMsg = "option '--second-url' is incompatible with option '--second-req')"2060        raise SqlmapSyntaxException(errMsg)2061    if conf.direct and conf.url:2062        errMsg = "option '-d' is incompatible with option '-u' ('--url')"2063        raise SqlmapSyntaxException(errMsg)2064    if conf.direct and conf.dbms:2065        errMsg = "option '-d' is incompatible with option '--dbms'"2066        raise SqlmapSyntaxException(errMsg)2067    if conf.titles and conf.nullConnection:2068        errMsg = "switch '--titles' is incompatible with switch '--null-connection'"2069        raise SqlmapSyntaxException(errMsg)2070    if conf.dumpTable and conf.search:2071        errMsg = "switch '--dump' is incompatible with switch '--search'"2072        raise SqlmapSyntaxException(errMsg)2073    if conf.chunked and not any((conf.data, conf.requestFile, conf.forms)):2074        errMsg = "switch '--chunked' requires usage of (POST) options/switches '--data', '-r' or '--forms'"2075        raise SqlmapSyntaxException(errMsg)2076    if conf.api and not conf.configFile:2077        errMsg = "switch '--api' requires usage of option '-c'"2078        raise SqlmapSyntaxException(errMsg)2079    if conf.data and conf.nullConnection:2080        errMsg = "option '--data' is incompatible with switch '--null-connection'"2081        raise SqlmapSyntaxException(errMsg)2082    if conf.string and conf.nullConnection:2083        errMsg = "option '--string' is incompatible with switch '--null-connection'"2084        raise SqlmapSyntaxException(errMsg)2085    if conf.notString and conf.nullConnection:2086        errMsg = "option '--not-string' is incompatible with switch '--null-connection'"2087        raise SqlmapSyntaxException(errMsg)2088    if conf.tor and conf.osPwn:2089        errMsg = "option '--tor' is incompatible with switch '--os-pwn'"2090        raise SqlmapSyntaxException(errMsg)2091    if conf.noCast and conf.hexConvert:2092        errMsg = "switch '--no-cast' is incompatible with switch '--hex'"2093        raise SqlmapSyntaxException(errMsg)2094    if conf.crawlDepth:2095        try:2096            xrange(conf.crawlDepth)2097        except OverflowError as ex:2098            errMsg = "invalid value used for option '--crawl' ('%s')" % getSafeExString(ex)2099            raise SqlmapSyntaxException(errMsg)2100    if conf.dumpAll and conf.search:2101        errMsg = "switch '--dump-all' is incompatible with switch '--search'"2102        raise SqlmapSyntaxException(errMsg)2103    if conf.string and conf.notString:2104        errMsg = "option '--string' is incompatible with switch '--not-string'"2105        raise SqlmapSyntaxException(errMsg)2106    if conf.regexp and conf.nullConnection:2107        errMsg = "option '--regexp' is incompatible with switch '--null-connection'"2108        raise SqlmapSyntaxException(errMsg)2109    if conf.regexp:2110        try:2111            re.compile(conf.regexp)2112        except Exception as ex:2113            errMsg = "invalid regular expression '%s' ('%s')" % (conf.regexp, getSafeExString(ex))2114            raise SqlmapSyntaxException(errMsg)2115    if conf.paramExclude:2116        try:2117            re.compile(conf.paramExclude)2118        except Exception as ex:2119            errMsg = "invalid regular expression '%s' ('%s')" % (conf.paramExclude, getSafeExString(ex))2120            raise SqlmapSyntaxException(errMsg)2121    if conf.cookieDel and len(conf.cookieDel):2122        errMsg = "option '--cookie-del' should contain a single character (e.g. ';')"2123        raise SqlmapSyntaxException(errMsg)2124    if conf.crawlExclude:2125        try:2126            re.compile(conf.crawlExclude)2127        except Exception as ex:2128            errMsg = "invalid regular expression '%s' ('%s')" % (conf.crawlExclude, getSafeExString(ex))2129            raise SqlmapSyntaxException(errMsg)2130    if conf.scope:2131        try:2132            re.compile(conf.scope)2133        except Exception as ex:2134            errMsg = "invalid regular expression '%s' ('%s')" % (conf.scope, getSafeExString(ex))2135            raise SqlmapSyntaxException(errMsg)2136    if conf.dumpTable and conf.dumpAll:2137        errMsg = "switch '--dump' is incompatible with switch '--dump-all'"2138        raise SqlmapSyntaxException(errMsg)2139    if conf.predictOutput and (conf.threads > 1 or conf.optimize):2140        errMsg = "switch '--predict-output' is incompatible with option '--threads' and switch '-o'"2141        raise SqlmapSyntaxException(errMsg)2142    if conf.threads > MAX_NUMBER_OF_THREADS and not conf.get("skipThreadCheck"):2143        errMsg = "maximum number of used threads is %d avoiding potential connection issues" % MAX_NUMBER_OF_THREADS2144        raise SqlmapSyntaxException(errMsg)2145    if conf.forms and not any((conf.url, conf.googleDork, conf.bulkFile)):2146        errMsg = "switch '--forms' requires usage of option '-u' ('--url'), '-g' or '-m'"2147        raise SqlmapSyntaxException(errMsg)2148    if conf.crawlExclude and not conf.crawlDepth:2149        errMsg = "option '--crawl-exclude' requires usage of switch '--crawl'"2150        raise SqlmapSyntaxException(errMsg)2151    if conf.safePost and not conf.safeUrl:2152        errMsg = "option '--safe-post' requires usage of option '--safe-url'"2153        raise SqlmapSyntaxException(errMsg)2154    if conf.safeFreq and not any((conf.safeUrl, conf.safeReqFile)):2155        errMsg = "option '--safe-freq' requires usage of option '--safe-url' or '--safe-req'"2156        raise SqlmapSyntaxException(errMsg)2157    if conf.safeReqFile and any((conf.safeUrl, conf.safePost)):2158        errMsg = "option '--safe-req' is incompatible with option '--safe-url' and option '--safe-post'"2159        raise SqlmapSyntaxException(errMsg)2160    if conf.csrfUrl and not conf.csrfToken:2161        errMsg = "option '--csrf-url' requires usage of option '--csrf-token'"2162        raise SqlmapSyntaxException(errMsg)2163    if conf.csrfMethod and not conf.csrfToken:2164        errMsg = "option '--csrf-method' requires usage of option '--csrf-token'"2165        raise SqlmapSyntaxException(errMsg)2166    if conf.csrfToken and conf.threads > 1:2167        errMsg = "option '--csrf-url' is incompatible with option '--threads'"2168        raise SqlmapSyntaxException(errMsg)2169    if conf.requestFile and conf.url and conf.url != DUMMY_URL:2170        errMsg = "option '-r' is incompatible with option '-u' ('--url')"2171        raise SqlmapSyntaxException(errMsg)2172    if conf.direct and conf.proxy:2173        errMsg = "option '-d' is incompatible with option '--proxy'"2174        raise SqlmapSyntaxException(errMsg)2175    if conf.direct and conf.tor:2176        errMsg = "option '-d' is incompatible with switch '--tor'"2177        raise SqlmapSyntaxException(errMsg)2178    if not conf.technique:2179        errMsg = "option '--technique' can't be empty"2180        raise SqlmapSyntaxException(errMsg)2181    if conf.tor and conf.ignoreProxy:2182        errMsg = "switch '--tor' is incompatible with switch '--ignore-proxy'"2183        raise SqlmapSyntaxException(errMsg)2184    if conf.tor and conf.proxy:2185        errMsg = "switch '--tor' is incompatible with option '--proxy'"2186        raise SqlmapSyntaxException(errMsg)2187    if conf.proxy and conf.proxyFile:2188        errMsg = "switch '--proxy' is incompatible with option '--proxy-file'"2189        raise SqlmapSyntaxException(errMsg)2190    if conf.proxyFreq and not conf.proxyFile:2191        errMsg = "option '--proxy-freq' requires usage of option '--proxy-file'"2192        raise SqlmapSyntaxException(errMsg)2193    if conf.checkTor and not any((conf.tor, conf.proxy)):2194        errMsg = "switch '--check-tor' requires usage of switch '--tor' (or option '--proxy' with HTTP proxy address of Tor service)"2195        raise SqlmapSyntaxException(errMsg)2196    if conf.torPort is not None and not (isinstance(conf.torPort, int) and conf.torPort >= 0 and conf.torPort <= 65535):2197        errMsg = "value for option '--tor-port' must be in range [0, 65535]"2198        raise SqlmapSyntaxException(errMsg)2199    if conf.torType not in getPublicTypeMembers(PROXY_TYPE, True):2200        errMsg = "option '--tor-type' accepts one of following values: %s" % ", ".join(getPublicTypeMembers(PROXY_TYPE, True))2201        raise SqlmapSyntaxException(errMsg)2202    if conf.dumpFormat not in getPublicTypeMembers(DUMP_FORMAT, True):2203        errMsg = "option '--dump-format' accepts one of following values: %s" % ", ".join(getPublicTypeMembers(DUMP_FORMAT, True))2204        raise SqlmapSyntaxException(errMsg)2205    if conf.skip and conf.testParameter:2206        if intersect(conf.skip, conf.testParameter):2207            errMsg = "option '--skip' is incompatible with option '-p'"2208            raise SqlmapSyntaxException(errMsg)2209    if conf.rParam and conf.testParameter:2210        if intersect(conf.rParam, conf.testParameter):2211            errMsg = "option '--randomize' is incompatible with option '-p'"2212            raise SqlmapSyntaxException(errMsg)2213    if conf.mobile and conf.agent:2214        errMsg = "switch '--mobile' is incompatible with option '--user-agent'"2215        raise SqlmapSyntaxException(errMsg)2216    if conf.proxy and conf.ignoreProxy:2217        errMsg = "option '--proxy' is incompatible with switch '--ignore-proxy'"2218        raise SqlmapSyntaxException(errMsg)2219    if conf.alert and conf.alert.startswith('-'):2220        errMsg = "value for option '--alert' must be valid operating system command(s)"2221        raise SqlmapSyntaxException(errMsg)2222    if conf.timeSec < 1:2223        errMsg = "value for option '--time-sec' must be a positive integer"2224        raise SqlmapSyntaxException(errMsg)2225    if conf.uChar and not re.match(UNION_CHAR_REGEX, conf.uChar):2226        errMsg = "value for option '--union-char' must be an alpha-numeric value (e.g. 1)"2227        raise SqlmapSyntaxException(errMsg)2228    if conf.hashFile and any((conf.direct, conf.url, conf.logFile, conf.bulkFile, conf.googleDork, conf.configFile, conf.requestFile, conf.updateAll, conf.smokeTest, conf.wizard, conf.dependencies, conf.purge, conf.listTampers)):2229        errMsg = "option '--crack' should be used as a standalone"2230        raise SqlmapSyntaxException(errMsg)2231    if isinstance(conf.uCols, six.string_types):2232        if not conf.uCols.isdigit() and ("-" not in conf.uCols or len(conf.uCols.split("-")) != 2):2233            errMsg = "value for option '--union-cols' must be a range with hyphon "2234            errMsg += "(e.g. 1-10) or integer value (e.g. 5)"2235            raise SqlmapSyntaxException(errMsg)2236    if conf.dbmsCred and ':' not in conf.dbmsCred:2237        errMsg = "value for option '--dbms-cred' must be in "2238        errMsg += "format <username>:<password> (e.g. \"root:pass\")"2239        raise SqlmapSyntaxException(errMsg)2240    if conf.encoding:2241        _ = checkCharEncoding(conf.encoding, False)2242        if _ is None:2243            errMsg = "unknown encoding '%s'. Please visit " % conf.encoding2244            errMsg += "'%s' to get the full list of " % CODECS_LIST_PAGE2245            errMsg += "supported encodings"2246            raise SqlmapSyntaxException(errMsg)2247        else:2248            conf.encoding = _2249    if conf.loadCookies:2250        if not os.path.exists(conf.loadCookies):2251            errMsg = "cookies file '%s' does not exist" % conf.loadCookies2252            raise SqlmapFilePathException(errMsg)2253def initOptions(inputOptions=AttribDict(), overrideOptions=False):2254    _setConfAttributes()2255    _setKnowledgeBaseAttributes()2256    _mergeOptions(inputOptions, overrideOptions)2257def init():2258    """2259    Set attributes into both configuration and knowledge base singletons2260    based upon command line and configuration file options.2261    """2262    _useWizardInterface()2263    setVerbosity()2264    _saveConfig()2265    _setRequestFromFile()2266    _cleanupOptions()2267    _cleanupEnvironment()2268    _purge()2269    _checkDependencies()2270    _createHomeDirectories()2271    _createTemporaryDirectory()2272    _basicOptionValidation()2273    _setProxyList()2274    _setTorProxySettings()2275    _setDNSServer()2276    _adjustLoggingFormatter()2277    _setMultipleTargets()2278    _listTamperingFunctions()2279    _setTamperingFunctions()2280    _setPreprocessFunctions()2281    _setPostprocessFunctions()2282    _setTrafficOutputFP()2283    _setupHTTPCollector()2284    _setHttpChunked()2285    _checkWebSocket()2286    parseTargetDirect()2287    if any((conf.url, conf.logFile, conf.bulkFile, conf.requestFile, conf.googleDork, conf.stdinPipe)):2288        _setHostname()2289        _setHTTPTimeout()2290        _setHTTPExtraHeaders()2291        _setHTTPCookies()2292        _setHTTPReferer()2293        _setHTTPHost()2294        _setHTTPUserAgent()2295        _setHTTPAuthentication()2296        _setHTTPHandlers()2297        _setDNSCache()2298        _setSocketPreConnect()2299        _setSafeVisit()2300        _doSearch()2301        _setStdinPipeTargets()2302        _setBulkMultipleTargets()2303        _checkTor()2304        _setCrawler()2305        _findPageForms()2306        _setDBMS()2307        _setTechnique()2308    _setThreads()2309    _setOS()2310    _setWriteFile()2311    _setMetasploit()2312    _setDBMSAuthentication()2313    loadBoundaries()2314    loadPayloads()2315    _setPrefixSuffix()2316    update()...sqlmap.py
Source:sqlmap.py  
1#!/usr/bin/env python2"""3Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/)4See the file 'LICENSE' for copying permission5"""6from __future__ import print_function7try:8    import sys9    sys.dont_write_bytecode = True10    try:11        __import__("lib.utils.versioncheck")  # this has to be the first non-standard import12    except ImportError:13        sys.exit("[!] wrong installation detected (missing modules). Visit 'https://github.com/sqlmapproject/sqlmap/#installation' for further details")14    import bdb15    import distutils16    import glob17    import inspect18    import json19    import logging20    import os21    import re22    import shutil23    import sys24    import tempfile25    import threading26    import time27    import traceback28    import warnings29    warnings.filterwarnings(action="ignore", message="Python 2 is no longer supported")30    warnings.filterwarnings(action="ignore", message=".*was already imported", category=UserWarning)31    warnings.filterwarnings(action="ignore", message=".*using a very old release", category=UserWarning)32    warnings.filterwarnings(action="ignore", message=".*default buffer size will be used", category=RuntimeWarning)33    warnings.filterwarnings(action="ignore", category=UserWarning, module="psycopg2")34    if "--deprecations" not in sys.argv:35        warnings.filterwarnings(action="ignore", category=DeprecationWarning)36    from lib.core.data import logger37    from lib.core.common import banner38    from lib.core.common import checkIntegrity39    from lib.core.common import checkPipedInput40    from lib.core.common import createGithubIssue41    from lib.core.common import dataToStdout42    from lib.core.common import filterNone43    from lib.core.common import getDaysFromLastUpdate44    from lib.core.common import getFileItems45    from lib.core.common import getSafeExString46    from lib.core.common import maskSensitiveData47    from lib.core.common import openFile48    from lib.core.common import setPaths49    from lib.core.common import weAreFrozen50    from lib.core.convert import getUnicode51    from lib.core.data import cmdLineOptions52    from lib.core.data import conf53    from lib.core.data import kb54    from lib.core.common import MKSTEMP_PREFIX55    from lib.core.common import setColor56    from lib.core.common import unhandledExceptionMessage57    from lib.core.compat import xrange58    from lib.core.exception import SqlmapBaseException59    from lib.core.exception import SqlmapShellQuitException60    from lib.core.exception import SqlmapSilentQuitException61    from lib.core.exception import SqlmapUserQuitException62    from lib.core.option import init63    from lib.core.option import initOptions64    from lib.core.patch import dirtyPatches65    from lib.core.patch import resolveCrossReferences66    from lib.core.settings import GIT_PAGE67    from lib.core.settings import IS_WIN68    from lib.core.settings import LAST_UPDATE_NAGGING_DAYS69    from lib.core.settings import LEGAL_DISCLAIMER70    from lib.core.settings import THREAD_FINALIZATION_TIMEOUT71    from lib.core.settings import UNICODE_ENCODING72    from lib.core.settings import VERSION73    from lib.parse.cmdline import cmdLineParser74    from lib.utils.crawler import crawl75except KeyboardInterrupt:76    errMsg = "user aborted"77    if "logger" in globals():78        logger.critical(errMsg)79        raise SystemExit80    else:81        import time82        sys.exit("\r[%s] [CRITICAL] %s" % (time.strftime("%X"), errMsg))83def modulePath():84    """85    This will get us the program's directory, even if we are frozen86    using py2exe87    """88    try:89        _ = sys.executable if weAreFrozen() else __file__90    except NameError:91        _ = inspect.getsourcefile(modulePath)92    return getUnicode(os.path.dirname(os.path.realpath(_)), encoding=sys.getfilesystemencoding() or UNICODE_ENCODING)93def checkEnvironment():94    try:95        os.path.isdir(modulePath())96    except UnicodeEncodeError:97        errMsg = "your system does not properly handle non-ASCII paths. "98        errMsg += "Please move the sqlmap's directory to the other location"99        logger.critical(errMsg)100        raise SystemExit101    if distutils.version.LooseVersion(VERSION) < distutils.version.LooseVersion("1.0"):102        errMsg = "your runtime environment (e.g. PYTHONPATH) is "103        errMsg += "broken. Please make sure that you are not running "104        errMsg += "newer versions of sqlmap with runtime scripts for older "105        errMsg += "versions"106        logger.critical(errMsg)107        raise SystemExit108    # Patch for pip (import) environment109    if "sqlmap.sqlmap" in sys.modules:110        for _ in ("cmdLineOptions", "conf", "kb"):111            globals()[_] = getattr(sys.modules["lib.core.data"], _)112        for _ in ("SqlmapBaseException", "SqlmapShellQuitException", "SqlmapSilentQuitException", "SqlmapUserQuitException"):113            globals()[_] = getattr(sys.modules["lib.core.exception"], _)114def main():115    """116    Main function of sqlmap when running from command line.117    """118    try:119        dirtyPatches()120        resolveCrossReferences()121        checkEnvironment()122        setPaths(modulePath())123        banner()124        # Store original command line options for possible later restoration125        args = cmdLineParser()126        cmdLineOptions.update(args.__dict__ if hasattr(args, "__dict__") else args)127        initOptions(cmdLineOptions)128        if checkPipedInput():129            conf.batch = True130        if conf.get("api"):131            # heavy imports132            from lib.utils.api import StdDbOut133            from lib.utils.api import setRestAPILog134            # Overwrite system standard output and standard error to write135            # to an IPC database136            sys.stdout = StdDbOut(conf.taskid, messagetype="stdout")137            sys.stderr = StdDbOut(conf.taskid, messagetype="stderr")138            setRestAPILog()139        conf.showTime = True140        dataToStdout("[!] legal disclaimer: %s\n\n" % LEGAL_DISCLAIMER, forceOutput=True)141        dataToStdout("[*] starting @ %s\n\n" % time.strftime("%X /%Y-%m-%d/"), forceOutput=True)142        init()143        if not conf.updateAll:144            # Postponed imports (faster start)145            if conf.smokeTest:146                from lib.core.testing import smokeTest147                os._exitcode = 1 - (smokeTest() or 0)148            elif conf.vulnTest:149                from lib.core.testing import vulnTest150                os._exitcode = 1 - (vulnTest() or 0)151            elif conf.bedTest:152                from lib.core.testing import bedTest153                os._exitcode = 1 - (bedTest() or 0)154            elif conf.fuzzTest:155                from lib.core.testing import fuzzTest156                fuzzTest()157            else:158                from lib.controller.controller import start159                if conf.profile:160                    from lib.core.profiling import profile161                    globals()["start"] = start162                    profile()163                else:164                    try:165                        if conf.crawlDepth and conf.bulkFile:166                            targets = getFileItems(conf.bulkFile)167                            for i in xrange(len(targets)):168                                try:169                                    kb.targets.clear()170                                    target = targets[i]171                                    if not re.search(r"(?i)\Ahttp[s]*://", target):172                                        target = "http://%s" % target173                                    infoMsg = "starting crawler for target URL '%s' (%d/%d)" % (target, i + 1, len(targets))174                                    logger.info(infoMsg)175                                    crawl(target)176                                except Exception as ex:177                                    if not isinstance(ex, SqlmapUserQuitException):178                                        errMsg = "problem occurred while crawling '%s' ('%s')" % (target, getSafeExString(ex))179                                        logger.error(errMsg)180                                    else:181                                        raise182                                else:183                                    if kb.targets:184                                        start()185                        else:186                            start()187                    except Exception as ex:188                        os._exitcode = 1189                        if "can't start new thread" in getSafeExString(ex):190                            errMsg = "unable to start new threads. Please check OS (u)limits"191                            logger.critical(errMsg)192                            raise SystemExit193                        else:194                            raise195    except SqlmapUserQuitException:196        if not conf.batch:197            errMsg = "user quit"198            logger.error(errMsg)199    except (SqlmapSilentQuitException, bdb.BdbQuit):200        pass201    except SqlmapShellQuitException:202        cmdLineOptions.sqlmapShell = False203    except SqlmapBaseException as ex:204        errMsg = getSafeExString(ex)205        logger.critical(errMsg)206        os._exitcode = 1207        raise SystemExit208    except KeyboardInterrupt:209        print()210    except EOFError:211        print()212        errMsg = "exit"213        logger.error(errMsg)214    except SystemExit as ex:215        os._exitcode = ex.code or 0216    except:217        print()218        errMsg = unhandledExceptionMessage()219        excMsg = traceback.format_exc()220        valid = checkIntegrity()221        os._exitcode = 255222        if any(_ in excMsg for _ in ("MemoryError", "Cannot allocate memory")):223            errMsg = "memory exhaustion detected"224            logger.critical(errMsg)225            raise SystemExit226        elif any(_ in excMsg for _ in ("No space left", "Disk quota exceeded", "Disk full while accessing")):227            errMsg = "no space left on output device"228            logger.critical(errMsg)229            raise SystemExit230        elif any(_ in excMsg for _ in ("The paging file is too small",)):231            errMsg = "no space left for paging file"232            logger.critical(errMsg)233            raise SystemExit234        elif all(_ in excMsg for _ in ("Access is denied", "subprocess", "metasploit")):235            errMsg = "permission error occurred while running Metasploit"236            logger.critical(errMsg)237            raise SystemExit238        elif all(_ in excMsg for _ in ("Permission denied", "metasploit")):239            errMsg = "permission error occurred while using Metasploit"240            logger.critical(errMsg)241            raise SystemExit242        elif "Read-only file system" in excMsg:243            errMsg = "output device is mounted as read-only"244            logger.critical(errMsg)245            raise SystemExit246        elif "Insufficient system resources" in excMsg:247            errMsg = "resource exhaustion detected"248            logger.critical(errMsg)249            raise SystemExit250        elif "OperationalError: disk I/O error" in excMsg:251            errMsg = "I/O error on output device"252            logger.critical(errMsg)253            raise SystemExit254        elif "Violation of BIDI" in excMsg:255            errMsg = "invalid URL (violation of Bidi IDNA rule - RFC 5893)"256            logger.critical(errMsg)257            raise SystemExit258        elif "Invalid IPv6 URL" in excMsg:259            errMsg = "invalid URL ('%s')" % excMsg.strip().split('\n')[-1]260            logger.critical(errMsg)261            raise SystemExit262        elif "_mkstemp_inner" in excMsg:263            errMsg = "there has been a problem while accessing temporary files"264            logger.critical(errMsg)265            raise SystemExit266        elif any(_ in excMsg for _ in ("tempfile.mkdtemp", "tempfile.mkstemp", "tempfile.py")):267            errMsg = "unable to write to the temporary directory '%s'. " % tempfile.gettempdir()268            errMsg += "Please make sure that your disk is not full and "269            errMsg += "that you have sufficient write permissions to "270            errMsg += "create temporary files and/or directories"271            logger.critical(errMsg)272            raise SystemExit273        elif "Permission denied: '" in excMsg:274            match = re.search(r"Permission denied: '([^']*)", excMsg)275            errMsg = "permission error occurred while accessing file '%s'" % match.group(1)276            logger.critical(errMsg)277            raise SystemExit278        elif all(_ in excMsg for _ in ("twophase", "sqlalchemy")):279            errMsg = "please update the 'sqlalchemy' package (>= 1.1.11) "280            errMsg += "(Reference: 'https://qiita.com/tkprof/items/7d7b2d00df9c5f16fffe')"281            logger.critical(errMsg)282            raise SystemExit283        elif all(_ in excMsg for _ in ("scramble_caching_sha2", "TypeError")):284            errMsg = "please downgrade the 'PyMySQL' package (=< 0.8.1) "285            errMsg += "(Reference: 'https://github.com/PyMySQL/PyMySQL/issues/700')"286            logger.critical(errMsg)287            raise SystemExit288        elif "must be pinned buffer, not bytearray" in excMsg:289            errMsg = "error occurred at Python interpreter which "290            errMsg += "is fixed in 2.7. Please update accordingly "291            errMsg += "(Reference: 'https://bugs.python.org/issue8104')"292            logger.critical(errMsg)293            raise SystemExit294        elif all(_ in excMsg for _ in ("Resource temporarily unavailable", "os.fork()", "dictionaryAttack")):295            errMsg = "there has been a problem while running the multiprocessing hash cracking. "296            errMsg += "Please rerun with option '--threads=1'"297            logger.critical(errMsg)298            raise SystemExit299        elif "can't start new thread" in excMsg:300            errMsg = "there has been a problem while creating new thread instance. "301            errMsg += "Please make sure that you are not running too many processes"302            if not IS_WIN:303                errMsg += " (or increase the 'ulimit -u' value)"304            logger.critical(errMsg)305            raise SystemExit306        elif "can't allocate read lock" in excMsg:307            errMsg = "there has been a problem in regular socket operation "308            errMsg += "('%s')" % excMsg.strip().split('\n')[-1]309            logger.critical(errMsg)310            raise SystemExit311        elif all(_ in excMsg for _ in ("pymysql", "configparser")):312            errMsg = "wrong initialization of pymsql detected (using Python3 dependencies)"313            logger.critical(errMsg)314            raise SystemExit315        elif all(_ in excMsg for _ in ("ntlm", "socket.error, err", "SyntaxError")):316            errMsg = "wrong initialization of python-ntlm detected (using Python2 syntax)"317            logger.critical(errMsg)318            raise SystemExit319        elif all(_ in excMsg for _ in ("drda", "to_bytes")):320            errMsg = "wrong initialization of drda detected (using Python3 syntax)"321            logger.critical(errMsg)322            raise SystemExit323        elif all(_ in excMsg for _ in ("window = tkinter.Tk()",)):324            errMsg = "there has been a problem in initialization of GUI interface "325            errMsg += "('%s')" % excMsg.strip().split('\n')[-1]326            logger.critical(errMsg)327            raise SystemExit328        elif any(_ in excMsg for _ in ("unable to access item 'liveTest'",)):329            errMsg = "detected usage of files from different versions of sqlmap"330            logger.critical(errMsg)331            raise SystemExit332        elif kb.get("dumpKeyboardInterrupt"):333            raise SystemExit334        elif any(_ in excMsg for _ in ("Broken pipe",)):335            raise SystemExit336        elif valid is False:337            errMsg = "code integrity check failed (turning off automatic issue creation). "338            errMsg += "You should retrieve the latest development version from official GitHub "339            errMsg += "repository at '%s'" % GIT_PAGE340            logger.critical(errMsg)341            print()342            dataToStdout(excMsg)343            raise SystemExit344        elif any(_ in excMsg for _ in ("tamper/", "waf/")):345            logger.critical(errMsg)346            print()347            dataToStdout(excMsg)348            raise SystemExit349        elif any(_ in excMsg for _ in ("ImportError", "ModuleNotFoundError", "Can't find file for module", "SAXReaderNotAvailable", "source code string cannot contain null bytes", "No module named", "tp_name field")):350            errMsg = "invalid runtime environment ('%s')" % excMsg.split("Error: ")[-1].strip()351            logger.critical(errMsg)352            raise SystemExit353        elif all(_ in excMsg for _ in ("SyntaxError: Non-ASCII character", ".py on line", "but no encoding declared")):354            errMsg = "invalid runtime environment ('%s')" % excMsg.split("Error: ")[-1].strip()355            logger.critical(errMsg)356            raise SystemExit357        elif all(_ in excMsg for _ in ("No such file", "_'")):358            errMsg = "corrupted installation detected ('%s'). " % excMsg.strip().split('\n')[-1]359            errMsg += "You should retrieve the latest development version from official GitHub "360            errMsg += "repository at '%s'" % GIT_PAGE361            logger.critical(errMsg)362            raise SystemExit363        elif all(_ in excMsg for _ in ("HTTPNtlmAuthHandler", "'str' object has no attribute 'decode'")):364            errMsg = "package 'python-ntlm' has a known compatibility issue with the "365            errMsg += "Python 3 (Reference: 'https://github.com/mullender/python-ntlm/pull/61')"366            logger.critical(errMsg)367            raise SystemExit368        elif "'DictObject' object has no attribute '" in excMsg and all(_ in errMsg for _ in ("(fingerprinted)", "(identified)")):369            errMsg = "there has been a problem in enumeration. "370            errMsg += "Because of a considerable chance of false-positive case "371            errMsg += "you are advised to rerun with switch '--flush-session'"372            logger.critical(errMsg)373            raise SystemExit374        elif "bad marshal data (unknown type code)" in excMsg:375            match = re.search(r"\s*(.+)\s+ValueError", excMsg)376            errMsg = "one of your .pyc files are corrupted%s" % (" ('%s')" % match.group(1) if match else "")377            errMsg += ". Please delete .pyc files on your system to fix the problem"378            logger.critical(errMsg)379            raise SystemExit380        for match in re.finditer(r'File "(.+?)", line', excMsg):381            file_ = match.group(1)382            try:383                file_ = os.path.relpath(file_, os.path.dirname(__file__))384            except ValueError:385                pass386            file_ = file_.replace("\\", '/')387            if "../" in file_:388                file_ = re.sub(r"(\.\./)+", '/', file_)389            else:390                file_ = file_.lstrip('/')391            file_ = re.sub(r"/{2,}", '/', file_)392            excMsg = excMsg.replace(match.group(1), file_)393        errMsg = maskSensitiveData(errMsg)394        excMsg = maskSensitiveData(excMsg)395        if conf.get("api") or not valid:396            logger.critical("%s\n%s" % (errMsg, excMsg))397        else:398            logger.critical(errMsg)399            dataToStdout("%s\n" % setColor(excMsg.strip(), level=logging.CRITICAL))400            createGithubIssue(errMsg, excMsg)401    finally:402        kb.threadContinue = False403        if getDaysFromLastUpdate() > LAST_UPDATE_NAGGING_DAYS:404            warnMsg = "your sqlmap version is outdated"405            logger.warn(warnMsg)406        if conf.get("showTime"):407            dataToStdout("\n[*] ending @ %s\n\n" % time.strftime("%X /%Y-%m-%d/"), forceOutput=True)408        kb.threadException = True409        if kb.get("tempDir"):410            for prefix in (MKSTEMP_PREFIX.IPC, MKSTEMP_PREFIX.TESTING, MKSTEMP_PREFIX.COOKIE_JAR, MKSTEMP_PREFIX.BIG_ARRAY):411                for filepath in glob.glob(os.path.join(kb.tempDir, "%s*" % prefix)):412                    try:413                        os.remove(filepath)414                    except OSError:415                        pass416            if not filterNone(filepath for filepath in glob.glob(os.path.join(kb.tempDir, '*')) if not any(filepath.endswith(_) for _ in (".lock", ".exe", ".so", '_'))):  # ignore junk files417                try:418                    shutil.rmtree(kb.tempDir, ignore_errors=True)419                except OSError:420                    pass421        if conf.get("hashDB"):422            conf.hashDB.flush(True)423        if conf.get("harFile"):424            try:425                with openFile(conf.harFile, "w+b") as f:426                    json.dump(conf.httpCollector.obtain(), fp=f, indent=4, separators=(',', ': '))427            except SqlmapBaseException as ex:428                errMsg = getSafeExString(ex)429                logger.critical(errMsg)430        if conf.get("api"):431            conf.databaseCursor.disconnect()432        if conf.get("dumper"):433            conf.dumper.flush()434        # short delay for thread finalization435        _ = time.time()436        while threading.activeCount() > 1 and (time.time() - _) > THREAD_FINALIZATION_TIMEOUT:437            time.sleep(0.01)438        if cmdLineOptions.get("sqlmapShell"):439            cmdLineOptions.clear()440            conf.clear()441            kb.clear()442            conf.disableBanner = True443            main()444if __name__ == "__main__":445    try:446        main()447    except KeyboardInterrupt:448        pass449    except SystemExit:450        raise451    except:452        traceback.print_exc()453    finally:454        # Reference: http://stackoverflow.com/questions/1635080/terminate-a-multi-thread-python-program455        if threading.activeCount() > 1:456            os._exit(getattr(os, "_exitcode", 0))457        else:458            sys.exit(getattr(os, "_exitcode", 0))459else:460    # cancelling postponed imports (because of Travis CI checks)...Learn to execute automation testing from scratch with LambdaTest Learning Hub. Right from setting up the prerequisites to run your first automation test, to following best practices and diving deeper into advanced test scenarios. LambdaTest Learning Hubs compile a list of step-by-step guides to help you be proficient with different test automation frameworks i.e. Selenium, Cypress, TestNG etc.
You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.
Get 100 minutes of automation test minutes FREE!!
