1#!/usr/bin/env python
2
3"""
4Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/)
5See the file 'LICENSE' for copying permission
6"""
7
8from __future__ import division
9
10import binascii
11import codecs
12import collections
13import contextlib
14import copy
15import functools
16import getpass
17import hashlib
18import inspect
19import io
20import json
21import keyword
22import locale
23import logging
24import ntpath
25import os
26import platform
27import posixpath
28import random
29import re
30import socket
31import string
32import subprocess
33import sys
34import tempfile
35import threading
36import time
37import types
38import unicodedata
39
40from difflib import SequenceMatcher
41from math import sqrt
42from optparse import OptionValueError
43from xml.dom import minidom
44from xml.sax import parse
45from xml.sax import SAXParseException
46
47from extra.beep.beep import beep
48from extra.cloak.cloak import decloak
49from lib.core.bigarray import BigArray
50from lib.core.compat import cmp
51from lib.core.compat import round
52from lib.core.compat import xrange
53from lib.core.convert import base64pickle
54from lib.core.convert import base64unpickle
55from lib.core.convert import decodeBase64
56from lib.core.convert import decodeHex
57from lib.core.convert import getBytes
58from lib.core.convert import getText
59from lib.core.convert import getUnicode
60from lib.core.convert import htmlUnescape
61from lib.core.convert import stdoutEncode
62from lib.core.data import conf
63from lib.core.data import kb
64from lib.core.data import logger
65from lib.core.data import paths
66from lib.core.datatype import OrderedSet
67from lib.core.decorators import cachedmethod
68from lib.core.defaults import defaults
69from lib.core.dicts import DBMS_DICT
70from lib.core.dicts import DEFAULT_DOC_ROOTS
71from lib.core.dicts import DEPRECATED_OPTIONS
72from lib.core.dicts import OBSOLETE_OPTIONS
73from lib.core.dicts import SQL_STATEMENTS
74from lib.core.enums import ADJUST_TIME_DELAY
75from lib.core.enums import CHARSET_TYPE
76from lib.core.enums import CONTENT_STATUS
77from lib.core.enums import DBMS
78from lib.core.enums import EXPECTED
79from lib.core.enums import HEURISTIC_TEST
80from lib.core.enums import HTTP_HEADER
81from lib.core.enums import HTTPMETHOD
82from lib.core.enums import LOGGING_LEVELS
83from lib.core.enums import MKSTEMP_PREFIX
84from lib.core.enums import OPTION_TYPE
85from lib.core.enums import OS
86from lib.core.enums import PAYLOAD
87from lib.core.enums import PLACE
88from lib.core.enums import POST_HINT
89from lib.core.enums import REFLECTIVE_COUNTER
90from lib.core.enums import SORT_ORDER
91from lib.core.exception import SqlmapBaseException
92from lib.core.exception import SqlmapDataException
93from lib.core.exception import SqlmapGenericException
94from lib.core.exception import SqlmapInstallationException
95from lib.core.exception import SqlmapMissingDependence
96from lib.core.exception import SqlmapNoneDataException
97from lib.core.exception import SqlmapSilentQuitException
98from lib.core.exception import SqlmapSyntaxException
99from lib.core.exception import SqlmapSystemException
100from lib.core.exception import SqlmapUserQuitException
101from lib.core.exception import SqlmapValueException
102from lib.core.log import LOGGER_HANDLER
103from lib.core.optiondict import optDict
104from lib.core.settings import BANNER
105from lib.core.settings import BOLD_PATTERNS
106from lib.core.settings import BOUNDED_INJECTION_MARKER
107from lib.core.settings import BRUTE_DOC_ROOT_PREFIXES
108from lib.core.settings import BRUTE_DOC_ROOT_SUFFIXES
109from lib.core.settings import BRUTE_DOC_ROOT_TARGET_MARK
110from lib.core.settings import BURP_REQUEST_REGEX
111from lib.core.settings import BURP_XML_HISTORY_REGEX
112from lib.core.settings import CRAWL_EXCLUDE_EXTENSIONS
113from lib.core.settings import CUSTOM_INJECTION_MARK_CHAR
114from lib.core.settings import DBMS_DIRECTORY_DICT
115from lib.core.settings import DEFAULT_COOKIE_DELIMITER
116from lib.core.settings import DEFAULT_GET_POST_DELIMITER
117from lib.core.settings import DEFAULT_MSSQL_SCHEMA
118from lib.core.settings import DEV_EMAIL_ADDRESS
119from lib.core.settings import DUMMY_USER_INJECTION
120from lib.core.settings import DYNAMICITY_BOUNDARY_LENGTH
121from lib.core.settings import ERROR_PARSING_REGEXES
122from lib.core.settings import EVALCODE_ENCODED_PREFIX
123from lib.core.settings import FILE_PATH_REGEXES
124from lib.core.settings import FORCE_COOKIE_EXPIRATION_TIME
125from lib.core.settings import FORM_SEARCH_REGEX
126from lib.core.settings import GENERIC_DOC_ROOT_DIRECTORY_NAMES
127from lib.core.settings import GIT_PAGE
128from lib.core.settings import GITHUB_REPORT_OAUTH_TOKEN
129from lib.core.settings import GOOGLE_ANALYTICS_COOKIE_PREFIX
130from lib.core.settings import HASHDB_MILESTONE_VALUE
131from lib.core.settings import HOST_ALIASES
132from lib.core.settings import HTTP_CHUNKED_SPLIT_KEYWORDS
133from lib.core.settings import IGNORE_PARAMETERS
134from lib.core.settings import IGNORE_SAVE_OPTIONS
135from lib.core.settings import INFERENCE_UNKNOWN_CHAR
136from lib.core.settings import IP_ADDRESS_REGEX
137from lib.core.settings import ISSUES_PAGE
138from lib.core.settings import IS_TTY
139from lib.core.settings import IS_WIN
140from lib.core.settings import LARGE_OUTPUT_THRESHOLD
141from lib.core.settings import LOCALHOST
142from lib.core.settings import MIN_ENCODED_LEN_CHECK
143from lib.core.settings import MIN_ERROR_PARSING_NON_WRITING_RATIO
144from lib.core.settings import MIN_TIME_RESPONSES
145from lib.core.settings import MIN_VALID_DELAYED_RESPONSE
146from lib.core.settings import NETSCAPE_FORMAT_HEADER_COOKIES
147from lib.core.settings import NULL
148from lib.core.settings import PARAMETER_AMP_MARKER
149from lib.core.settings import PARAMETER_SEMICOLON_MARKER
150from lib.core.settings import PARTIAL_HEX_VALUE_MARKER
151from lib.core.settings import PARTIAL_VALUE_MARKER
152from lib.core.settings import PAYLOAD_DELIMITER
153from lib.core.settings import PLATFORM
154from lib.core.settings import PRINTABLE_CHAR_REGEX
155from lib.core.settings import PROBLEMATIC_CUSTOM_INJECTION_PATTERNS
156from lib.core.settings import PUSH_VALUE_EXCEPTION_RETRY_COUNT
157from lib.core.settings import PYVERSION
158from lib.core.settings import RANDOMIZATION_TLDS
159from lib.core.settings import REFERER_ALIASES
160from lib.core.settings import REFLECTED_BORDER_REGEX
161from lib.core.settings import REFLECTED_MAX_REGEX_PARTS
162from lib.core.settings import REFLECTED_REPLACEMENT_REGEX
163from lib.core.settings import REFLECTED_REPLACEMENT_TIMEOUT
164from lib.core.settings import REFLECTED_VALUE_MARKER
165from lib.core.settings import REFLECTIVE_MISS_THRESHOLD
166from lib.core.settings import SENSITIVE_DATA_REGEX
167from lib.core.settings import SENSITIVE_OPTIONS
168from lib.core.settings import STDIN_PIPE_DASH
169from lib.core.settings import SUPPORTED_DBMS
170from lib.core.settings import TEXT_TAG_REGEX
171from lib.core.settings import TIME_STDEV_COEFF
172from lib.core.settings import UNICODE_ENCODING
173from lib.core.settings import UNKNOWN_DBMS_VERSION
174from lib.core.settings import URI_QUESTION_MARKER
175from lib.core.settings import URLENCODE_CHAR_LIMIT
176from lib.core.settings import URLENCODE_FAILSAFE_CHARS
177from lib.core.settings import USER_AGENT_ALIASES
178from lib.core.settings import VERSION_COMPARISON_CORRECTION
179from lib.core.settings import VERSION_STRING
180from lib.core.settings import ZIP_HEADER
181from lib.core.settings import WEBSCARAB_SPLITTER
182from lib.core.threads import getCurrentThreadData
183from lib.utils.safe2bin import safecharencode
184from lib.utils.sqlalchemy import _sqlalchemy
185from thirdparty import six
186from thirdparty.clientform.clientform import ParseResponse
187from thirdparty.clientform.clientform import ParseError
188from thirdparty.colorama.initialise import init as coloramainit
189from thirdparty.magic import magic
190from thirdparty.odict import OrderedDict
191from thirdparty.six import unichr as _unichr
192from thirdparty.six.moves import configparser as _configparser
193from thirdparty.six.moves import http_client as _http_client
194from thirdparty.six.moves import input as _input
195from thirdparty.six.moves import reload_module as _reload_module
196from thirdparty.six.moves import urllib as _urllib
197from thirdparty.six.moves import zip as _zip
198from thirdparty.termcolor.termcolor import colored
199
200class UnicodeRawConfigParser(_configparser.RawConfigParser):
201    """
202    RawConfigParser with unicode writing support
203    """
204
205    def write(self, fp):
206        """
207        Write an .ini-format representation of the configuration state.
208        """
209
210        if self._defaults:
211            fp.write("[%s]\n" % _configparser.DEFAULTSECT)
212
213            for (key, value) in self._defaults.items():
214                fp.write("%s = %s\n" % (key, getUnicode(value, UNICODE_ENCODING).replace('\n', '\n\t')))
215
216            fp.write("\n")
217
218        for section in self._sections:
219            fp.write("[%s]\n" % section)
220
221            for (key, value) in self._sections[section].items():
222                if key != "__name__":
223                    if value is None:
224                        fp.write("%s\n" % (key))
225                    else:
226                        fp.write("%s = %s\n" % (key, getUnicode(value, UNICODE_ENCODING).replace('\n', '\n\t')))
227
228            fp.write("\n")
229
230class Format(object):
231    @staticmethod
232    def humanize(values, chain=" or "):
233        return chain.join(values)
234
235    # Get methods
236    @staticmethod
237    def getDbms(versions=None):
238        """
239        Format the back-end DBMS fingerprint value and return its
240        values formatted as a human readable string.
241
242        @return: detected back-end DBMS based upon fingerprint techniques.
243        @rtype: C{str}
244        """
245
246        if versions is None and Backend.getVersionList():
247            versions = Backend.getVersionList()
248
249        return Backend.getDbms() if versions is None else "%s %s" % (Backend.getDbms(), " and ".join(filterNone(versions)))
250
251    @staticmethod
252    def getErrorParsedDBMSes():
253        """
254        Parses the knowledge base htmlFp list and return its values
255        formatted as a human readable string.
256
257        @return: list of possible back-end DBMS based upon error messages
258        parsing.
259        @rtype: C{str}
260        """
261
262        htmlParsed = None
263
264        if len(kb.htmlFp) == 0 or kb.heuristicTest != HEURISTIC_TEST.POSITIVE:
265            pass
266        elif len(kb.htmlFp) == 1:
267            htmlParsed = kb.htmlFp[0]
268        elif len(kb.htmlFp) > 1:
269            htmlParsed = " or ".join(kb.htmlFp)
270
271        return htmlParsed
272
273    @staticmethod
274    def getOs(target, info):
275        """
276        Formats the back-end operating system fingerprint value
277        and return its values formatted as a human readable string.
278
279        Example of info (kb.headersFp) dictionary:
280
281        {
282          'distrib': set(['Ubuntu']),
283          'type': set(['Linux']),
284          'technology': set(['PHP 5.2.6', 'Apache 2.2.9']),
285          'release': set(['8.10'])
286        }
287
288        Example of info (kb.bannerFp) dictionary:
289
290        {
291          'sp': set(['Service Pack 4']),
292          'dbmsVersion': '8.00.194',
293          'dbmsServicePack': '0',
294          'distrib': set(['2000']),
295          'dbmsRelease': '2000',
296          'type': set(['Windows'])
297        }
298
299        @return: detected back-end operating system based upon fingerprint
300        techniques.
301        @rtype: C{str}
302        """
303
304        infoStr = ""
305        infoApi = {}
306
307        if info and "type" in info:
308            if conf.api:
309                infoApi["%s operating system" % target] = info
310            else:
311                infoStr += "%s operating system: %s" % (target, Format.humanize(info["type"]))
312
313                if "distrib" in info:
314                    infoStr += " %s" % Format.humanize(info["distrib"])
315
316                if "release" in info:
317                    infoStr += " %s" % Format.humanize(info["release"])
318
319                if "sp" in info:
320                    infoStr += " %s" % Format.humanize(info["sp"])
321
322                if "codename" in info:
323                    infoStr += " (%s)" % Format.humanize(info["codename"])
324
325        if "technology" in info:
326            if conf.api:
327                infoApi["web application technology"] = Format.humanize(info["technology"], ", ")
328            else:
329                infoStr += "\nweb application technology: %s" % Format.humanize(info["technology"], ", ")
330
331        if conf.api:
332            return infoApi
333        else:
334            return infoStr.lstrip()
335
336class Backend(object):
337    @staticmethod
338    def setDbms(dbms):
339        dbms = aliasToDbmsEnum(dbms)
340
341        if dbms is None:
342            return None
343
344        # Little precaution, in theory this condition should always be false
345        elif kb.dbms is not None and kb.dbms != dbms:
346            warnMsg = "there appears to be a high probability that "
347            warnMsg += "this could be a false positive case"
348            logger.warn(warnMsg)
349
350            msg = "sqlmap previously fingerprinted back-end DBMS as "
351            msg += "%s. However now it has been fingerprinted " % kb.dbms
352            msg += "as %s. " % dbms
353            msg += "Please, specify which DBMS should be "
354            msg += "correct [%s (default)/%s] " % (kb.dbms, dbms)
355
356            while True:
357                choice = readInput(msg, default=kb.dbms)
358
359                if aliasToDbmsEnum(choice) == kb.dbms:
360                    kb.dbmsVersion = []
361                    kb.resolutionDbms = kb.dbms
362                    break
363                elif aliasToDbmsEnum(choice) == dbms:
364                    kb.dbms = aliasToDbmsEnum(choice)
365                    break
366                else:
367                    warnMsg = "invalid value"
368                    logger.warn(warnMsg)
369
370        elif kb.dbms is None:
371            kb.dbms = aliasToDbmsEnum(dbms)
372
373        return kb.dbms
374
375    @staticmethod
376    def setVersion(version):
377        if isinstance(version, six.string_types):
378            kb.dbmsVersion = [version]
379
380        return kb.dbmsVersion
381
382    @staticmethod
383    def setVersionList(versionsList):
384        if isinstance(versionsList, list):
385            kb.dbmsVersion = versionsList
386        elif isinstance(versionsList, six.string_types):
387            Backend.setVersion(versionsList)
388        else:
389            logger.error("invalid format of versionsList")
390
391    @staticmethod
392    def forceDbms(dbms, sticky=False):
393        if not kb.stickyDBMS:
394            kb.forcedDbms = aliasToDbmsEnum(dbms)
395            kb.stickyDBMS = sticky
396
397    @staticmethod
398    def flushForcedDbms(force=False):
399        if not kb.stickyDBMS or force:
400            kb.forcedDbms = None
401            kb.stickyDBMS = False
402
403    @staticmethod
404    def setOs(os):
405        if os is None:
406            return None
407
408        # Little precaution, in theory this condition should always be false
409        elif kb.os is not None and isinstance(os, six.string_types) and kb.os.lower() != os.lower():
410            msg = "sqlmap previously fingerprinted back-end DBMS "
411            msg += "operating system %s. However now it has " % kb.os
412            msg += "been fingerprinted to be %s. " % os
413            msg += "Please, specify which OS is "
414            msg += "correct [%s (default)/%s] " % (kb.os, os)
415
416            while True:
417                choice = readInput(msg, default=kb.os)
418
419                if choice == kb.os:
420                    break
421                elif choice == os:
422                    kb.os = choice.capitalize()
423                    break
424                else:
425                    warnMsg = "invalid value"
426                    logger.warn(warnMsg)
427
428        elif kb.os is None and isinstance(os, six.string_types):
429            kb.os = os.capitalize()
430
431        return kb.os
432
433    @staticmethod
434    def setOsVersion(version):
435        if version is None:
436            return None
437
438        elif kb.osVersion is None and isinstance(version, six.string_types):
439            kb.osVersion = version
440
441    @staticmethod
442    def setOsServicePack(sp):
443        if sp is None:
444            return None
445
446        elif kb.osSP is None and isinstance(sp, int):
447            kb.osSP = sp
448
449    @staticmethod
450    def setArch():
451        msg = "what is the back-end database management system architecture?"
452        msg += "\n[1] 32-bit (default)"
453        msg += "\n[2] 64-bit"
454
455        while True:
456            choice = readInput(msg, default='1')
457
458            if hasattr(choice, "isdigit") and choice.isdigit() and int(choice) in (1, 2):
459                kb.arch = 32 if int(choice) == 1 else 64
460                break
461            else:
462                warnMsg = "invalid value. Valid values are 1 and 2"
463                logger.warn(warnMsg)
464
465        return kb.arch
466
467    # Get methods
468    @staticmethod
469    def getForcedDbms():
470        return aliasToDbmsEnum(conf.get("forceDbms")) or aliasToDbmsEnum(kb.get("forcedDbms"))
471
472    @staticmethod
473    def getDbms():
474        return aliasToDbmsEnum(kb.get("dbms"))
475
476    @staticmethod
477    def getErrorParsedDBMSes():
478        """
479        Returns array with parsed DBMS names till now
480
481        This functions is called to:
482
483        1. Ask user whether or not skip specific DBMS tests in detection phase,
484           lib/controller/checks.py - detection phase.
485        2. Sort the fingerprint of the DBMS, lib/controller/handler.py -
486           fingerprint phase.
487        """
488
489        return kb.htmlFp if kb.get("heuristicTest") == HEURISTIC_TEST.POSITIVE else []
490
491    @staticmethod
492    def getIdentifiedDbms():
493        """
494        This functions is called to:
495
496        1. Sort the tests, getSortedInjectionTests() - detection phase.
497        2. Etc.
498        """
499
500        dbms = None
501
502        if not kb:
503            pass
504        elif not kb.get("testMode") and conf.get("dbmsHandler") and getattr(conf.dbmsHandler, "_dbms", None):
505            dbms = conf.dbmsHandler._dbms
506        elif Backend.getForcedDbms() is not None:
507            dbms = Backend.getForcedDbms()
508        elif Backend.getDbms() is not None:
509            dbms = Backend.getDbms()
510        elif kb.get("injection") and kb.injection.dbms:
511            dbms = unArrayizeValue(kb.injection.dbms)
512        elif Backend.getErrorParsedDBMSes():
513            dbms = unArrayizeValue(Backend.getErrorParsedDBMSes())
514        elif conf.get("dbms"):
515            dbms = conf.get("dbms")
516
517        return aliasToDbmsEnum(dbms)
518
519    @staticmethod
520    def getVersion():
521        versions = filterNone(flattenValue(kb.dbmsVersion)) if not isinstance(kb.dbmsVersion, six.string_types) else [kb.dbmsVersion]
522        if not isNoneValue(versions):
523            return versions[0]
524        else:
525            return None
526
527    @staticmethod
528    def getVersionList():
529        versions = filterNone(flattenValue(kb.dbmsVersion)) if not isinstance(kb.dbmsVersion, six.string_types) else [kb.dbmsVersion]
530        if not isNoneValue(versions):
531            return versions
532        else:
533            return None
534
535    @staticmethod
536    def getOs():
537        return kb.os
538
539    @staticmethod
540    def getOsVersion():
541        return kb.osVersion
542
543    @staticmethod
544    def getOsServicePack():
545        return kb.osSP
546
547    @staticmethod
548    def getArch():
549        if kb.arch is None:
550            Backend.setArch()
551        return kb.arch
552
553    # Comparison methods
554    @staticmethod
555    def isDbms(dbms):
556        if not kb.get("testMode") and all((Backend.getDbms(), Backend.getIdentifiedDbms())) and Backend.getDbms() != Backend.getIdentifiedDbms():
557            singleTimeWarnMessage("identified ('%s') and fingerprinted ('%s') DBMSes differ. If you experience problems in enumeration phase please rerun with '--flush-session'" % (Backend.getIdentifiedDbms(), Backend.getDbms()))
558        return Backend.getIdentifiedDbms() == aliasToDbmsEnum(dbms)
559
560    @staticmethod
561    def isDbmsWithin(aliases):
562        return Backend.getDbms() is not None and Backend.getDbms().lower() in aliases
563
564    @staticmethod
565    def isVersion(version):
566        return Backend.getVersion() is not None and Backend.getVersion() == version
567
568    @staticmethod
569    def isVersionWithin(versionList):
570        if Backend.getVersionList() is None:
571            return False
572
573        for _ in Backend.getVersionList():
574            if _ != UNKNOWN_DBMS_VERSION and _ in versionList:
575                return True
576
577        return False
578
579    @staticmethod
580    def isVersionGreaterOrEqualThan(version):
581        return Backend.getVersion() is not None and str(Backend.getVersion()) >= str(version)
582
583    @staticmethod
584    def isOs(os):
585        return Backend.getOs() is not None and Backend.getOs().lower() == os.lower()
586
587def paramToDict(place, parameters=None):
588    """
589    Split the parameters into names and values, check if these parameters
590    are within the testable parameters and return in a dictionary.
591    """
592
593    testableParameters = OrderedDict()
594
595    if place in conf.parameters and not parameters:
596        parameters = conf.parameters[place]
597
598    parameters = re.sub(r"&(\w{1,4});", r"%s\g<1>%s" % (PARAMETER_AMP_MARKER, PARAMETER_SEMICOLON_MARKER), parameters)
599    if place == PLACE.COOKIE:
600        splitParams = parameters.split(conf.cookieDel or DEFAULT_COOKIE_DELIMITER)
601    else:
602        splitParams = parameters.split(conf.paramDel or DEFAULT_GET_POST_DELIMITER)
603
604    for element in splitParams:
605        element = re.sub(r"%s(.+?)%s" % (PARAMETER_AMP_MARKER, PARAMETER_SEMICOLON_MARKER), r"&\g<1>;", element)
606        parts = element.split("=")
607
608        if len(parts) >= 2:
609            parameter = urldecode(parts[0].replace(" ", ""))
610
611            if not parameter:
612                continue
613
614            if conf.paramDel and conf.paramDel == '\n':
615                parts[-1] = parts[-1].rstrip()
616
617            condition = not conf.testParameter
618            condition |= conf.testParameter is not None and parameter in conf.testParameter
619            condition |= place == PLACE.COOKIE and len(intersect((PLACE.COOKIE,), conf.testParameter, True)) > 0
620
621            if condition:
622                value = "=".join(parts[1:])
623
624                if parameter in (conf.base64Parameter or []):
625                    try:
626                        oldValue = value
627                        value = decodeBase64(value, binary=False, encoding=conf.encoding or UNICODE_ENCODING)
628                        parameters = re.sub(r"\b%s(\b|\Z)" % re.escape(oldValue), value, parameters)
629                    except:
630                        errMsg = "parameter '%s' does not contain " % parameter
631                        errMsg += "valid Base64 encoded value ('%s')" % value
632                        raise SqlmapValueException(errMsg)
633
634                testableParameters[parameter] = value
635
636                if not conf.multipleTargets and not (conf.csrfToken and re.search(conf.csrfToken, parameter, re.I)):
637                    _ = urldecode(testableParameters[parameter], convall=True)
638                    if (_.endswith("'") and _.count("'") == 1 or re.search(r'\A9{3,}', _) or re.search(r'\A-\d+\Z', _) or re.search(DUMMY_USER_INJECTION, _)) and not parameter.upper().startswith(GOOGLE_ANALYTICS_COOKIE_PREFIX):
639                        warnMsg = "it appears that you have provided tainted parameter values "
640                        warnMsg += "('%s') with most likely leftover " % element
641                        warnMsg += "chars/statements from manual SQL injection test(s). "
642                        warnMsg += "Please, always use only valid parameter values "
643                        warnMsg += "so sqlmap could be able to run properly"
644                        logger.warn(warnMsg)
645
646                        message = "are you really sure that you want to continue (sqlmap could have problems)? [y/N] "
647
648                        if not readInput(message, default='N', boolean=True):
649                            raise SqlmapSilentQuitException
650                    elif not _:
651                        warnMsg = "provided value for parameter '%s' is empty. " % parameter
652                        warnMsg += "Please, always use only valid parameter values "
653                        warnMsg += "so sqlmap could be able to run properly"
654                        logger.warn(warnMsg)
655
656                if place in (PLACE.POST, PLACE.GET):
657                    for regex in (r"\A((?:<[^>]+>)+\w+)((?:<[^>]+>)+)\Z", r"\A([^\w]+.*\w+)([^\w]+)\Z"):
658                        match = re.search(regex, testableParameters[parameter])
659                        if match:
660                            try:
661                                candidates = OrderedDict()
662
663                                def walk(head, current=None):
664                                    if current is None:
665                                        current = head
666                                    if isListLike(current):
667                                        for _ in current:
668                                            walk(head, _)
669                                    elif isinstance(current, dict):
670                                        for key in current.keys():
671                                            value = current[key]
672                                            if isinstance(value, (list, tuple, set, dict)):
673                                                if value:
674                                                    walk(head, value)
675                                            elif isinstance(value, (bool, int, float, six.string_types)):
676                                                original = current[key]
677                                                if isinstance(value, bool):
678                                                    current[key] = "%s%s" % (getUnicode(value).lower(), BOUNDED_INJECTION_MARKER)
679                                                else:
680                                                    current[key] = "%s%s" % (value, BOUNDED_INJECTION_MARKER)
681                                                candidates["%s (%s)" % (parameter, key)] = re.sub(r"\b(%s\s*=\s*)%s" % (re.escape(parameter), re.escape(testableParameters[parameter])), r"\g<1>%s" % json.dumps(deserialized, separators=(',', ':') if ", " not in testableParameters[parameter] else None), parameters)
682                                                current[key] = original
683
684                                deserialized = json.loads(testableParameters[parameter])
685                                walk(deserialized)
686
687                                if candidates:
688                                    message = "it appears that provided value for %sparameter '%s' " % ("%s " % place if place != parameter else "", parameter)
689                                    message += "is JSON deserializable. Do you want to inject inside? [y/N] "
690
691                                    if readInput(message, default='N', boolean=True):
692                                        del testableParameters[parameter]
693                                        testableParameters.update(candidates)
694                                    break
695                            except (KeyboardInterrupt, SqlmapUserQuitException):
696                                raise
697                            except Exception:
698                                pass
699
700                            _ = re.sub(regex, r"\g<1>%s\g<%d>" % (kb.customInjectionMark, len(match.groups())), testableParameters[parameter])
701                            message = "it appears that provided value for %sparameter '%s' " % ("%s " % place if place != parameter else "", parameter)
702                            message += "has boundaries. Do you want to inject inside? ('%s') [y/N] " % getUnicode(_)
703
704                            if readInput(message, default='N', boolean=True):
705                                testableParameters[parameter] = re.sub(r"\b(%s\s*=\s*)%s" % (re.escape(parameter), re.escape(testableParameters[parameter])), (r"\g<1>%s" % re.sub(regex, r"\g<1>%s\g<2>" % BOUNDED_INJECTION_MARKER, testableParameters[parameter].replace("\\", r"\\"))), parameters)
706                            break
707
708    if conf.testParameter:
709        if not testableParameters:
710            paramStr = ", ".join(test for test in conf.testParameter)
711
712            if len(conf.testParameter) > 1:
713                warnMsg = "provided parameters '%s' " % paramStr
714                warnMsg += "are not inside the %s" % place
715                logger.warn(warnMsg)
716            else:
717                parameter = conf.testParameter[0]
718
719                if not intersect(USER_AGENT_ALIASES + REFERER_ALIASES + HOST_ALIASES, parameter, True):
720                    debugMsg = "provided parameter '%s' " % paramStr
721                    debugMsg += "is not inside the %s" % place
722                    logger.debug(debugMsg)
723
724        elif len(conf.testParameter) != len(testableParameters):
725            for parameter in conf.testParameter:
726                if parameter not in testableParameters:
727                    debugMsg = "provided parameter '%s' " % parameter
728                    debugMsg += "is not inside the %s" % place
729                    logger.debug(debugMsg)
730
731    if testableParameters:
732        for parameter, value in testableParameters.items():
733            if value and not value.isdigit():
734                for encoding in ("hex", "base64"):
735                    try:
736                        decoded = codecs.decode(value, encoding)
737                        if len(decoded) > MIN_ENCODED_LEN_CHECK and all(_ in getBytes(string.printable) for _ in decoded):
738                            warnMsg = "provided parameter '%s' " % parameter
739                            warnMsg += "appears to be '%s' encoded" % encoding
740                            logger.warn(warnMsg)
741                            break
742                    except:
743                        pass
744
745    return testableParameters
746
747def getManualDirectories():
748    directories = None
749    defaultDocRoot = DEFAULT_DOC_ROOTS.get(Backend.getOs(), DEFAULT_DOC_ROOTS[OS.LINUX])
750
751    if kb.absFilePaths:
752        for absFilePath in kb.absFilePaths:
753            if directories:
754                break
755
756            if directoryPath(absFilePath) == '/':
757                continue
758
759            absFilePath = normalizePath(absFilePath)
760            windowsDriveLetter = None
761
762            if isWindowsDriveLetterPath(absFilePath):
763                windowsDriveLetter, absFilePath = absFilePath[:2], absFilePath[2:]
764                absFilePath = ntToPosixSlashes(posixToNtSlashes(absFilePath))
765
766            for _ in list(GENERIC_DOC_ROOT_DIRECTORY_NAMES) + [conf.hostname]:
767                _ = "/%s/" % _
768
769                if _ in absFilePath:
770                    directories = "%s%s" % (absFilePath.split(_)[0], _)
771                    break
772
773            if not directories and conf.path.strip('/') and conf.path in absFilePath:
774                directories = absFilePath.split(conf.path)[0]
775
776            if directories and windowsDriveLetter:
777                directories = "%s/%s" % (windowsDriveLetter, ntToPosixSlashes(directories))
778
779    directories = normalizePath(directories)
780
781    if conf.webRoot:
782        directories = [conf.webRoot]
783        infoMsg = "using '%s' as web server document root" % conf.webRoot
784        logger.info(infoMsg)
785    elif directories:
786        infoMsg = "retrieved the web server document root: '%s'" % directories
787        logger.info(infoMsg)
788    else:
789        warnMsg = "unable to automatically retrieve the web server "
790        warnMsg += "document root"
791        logger.warn(warnMsg)
792
793        directories = []
794
795        message = "what do you want to use for writable directory?\n"
796        message += "[1] common location(s) ('%s') (default)\n" % ", ".join(root for root in defaultDocRoot)
797        message += "[2] custom location(s)\n"
798        message += "[3] custom directory list file\n"
799        message += "[4] brute force search"
800        choice = readInput(message, default='1')
801
802        if choice == '2':
803            message = "please provide a comma separate list of absolute directory paths: "
804            directories = readInput(message, default="").split(',')
805        elif choice == '3':
806            message = "what's the list file location?\n"
807            listPath = readInput(message, default="")
808            checkFile(listPath)
809            directories = getFileItems(listPath)
810        elif choice == '4':
811            targets = set([conf.hostname])
812            _ = conf.hostname.split('.')
813
814            if _[0] == "www":
815                targets.add('.'.join(_[1:]))
816                targets.add('.'.join(_[1:-1]))
817            else:
818                targets.add('.'.join(_[:-1]))
819
820            targets = filterNone(targets)
821
822            for prefix in BRUTE_DOC_ROOT_PREFIXES.get(Backend.getOs(), DEFAULT_DOC_ROOTS[OS.LINUX]):
823                if BRUTE_DOC_ROOT_TARGET_MARK in prefix and re.match(IP_ADDRESS_REGEX, conf.hostname):
824                    continue
825
826                for suffix in BRUTE_DOC_ROOT_SUFFIXES:
827                    for target in targets:
828                        if not prefix.endswith("/%s" % suffix):
829                            item = "%s/%s" % (prefix, suffix)
830                        else:
831                            item = prefix
832
833                        item = item.replace(BRUTE_DOC_ROOT_TARGET_MARK, target).replace("//", '/').rstrip('/')
834                        if item not in directories:
835                            directories.append(item)
836
837                        if BRUTE_DOC_ROOT_TARGET_MARK not in prefix:
838                            break
839
840            infoMsg = "using generated directory list: %s" % ','.join(directories)
841            logger.info(infoMsg)
842
843            msg = "use any additional custom directories [Enter for None]: "
844            answer = readInput(msg)
845
846            if answer:
847                directories.extend(answer.split(','))
848
849        else:
850            directories = defaultDocRoot
851
852    return directories
853
854def getAutoDirectories():
855    """
856    >>> pushValue(kb.absFilePaths)
857    >>> kb.absFilePaths = ["C:\\inetpub\\wwwroot\\index.asp", "/var/www/html"]
858    >>> getAutoDirectories()
859    ['C:/inetpub/wwwroot', '/var/www/html']
860    >>> kb.absFilePaths = popValue()
861    """
862
863    retVal = OrderedSet()
864
865    if kb.absFilePaths:
866        infoMsg = "retrieved web server absolute paths: "
867        infoMsg += "'%s'" % ", ".join(ntToPosixSlashes(path) for path in kb.absFilePaths)
868        logger.info(infoMsg)
869
870        for absFilePath in kb.absFilePaths:
871            if absFilePath:
872                directory = directoryPath(absFilePath)
873                directory = ntToPosixSlashes(directory)
874                retVal.add(directory)
875    else:
876        warnMsg = "unable to automatically parse any web server path"
877        logger.warn(warnMsg)
878
879    return list(retVal)
880
881def filePathToSafeString(filePath):
882    """
883    Returns string representation of a given filepath safe for a single filename usage
884
885    >>> filePathToSafeString('C:/Windows/system32')
886    'C__Windows_system32'
887    """
888
889    retVal = filePath.replace("/", "_").replace("\\", "_")
890    retVal = retVal.replace(" ", "_").replace(":", "_")
891
892    return retVal
893
894def singleTimeDebugMessage(message):
895    singleTimeLogMessage(message, logging.DEBUG)
896
897def singleTimeWarnMessage(message):
898    singleTimeLogMessage(message, logging.WARN)
899
900def singleTimeLogMessage(message, level=logging.INFO, flag=None):
901    if flag is None:
902        flag = hash(message)
903
904    if not conf.smokeTest and flag not in kb.singleLogFlags:
905        kb.singleLogFlags.add(flag)
906        logger.log(level, message)
907
908def boldifyMessage(message, istty=None):
909    """
910    Sets ANSI bold marking on entire message if parts found in predefined BOLD_PATTERNS
911
912    >>> boldifyMessage("Hello World", istty=True)
913    'Hello World'
914
915    >>> boldifyMessage("GET parameter id is not injectable", istty=True)
916    '\\x1b[1mGET parameter id is not injectable\\x1b[0m'
917    """
918
919    retVal = message
920
921    if any(_ in message for _ in BOLD_PATTERNS):
922        retVal = setColor(message, bold=True, istty=istty)
923
924    return retVal
925
926def setColor(message, color=None, bold=False, level=None, istty=None):
927    """
928    Sets ANSI color codes
929
930    >>> setColor("Hello World", color="red", istty=True)
931    '\\x1b[31mHello World\\x1b[0m'
932    """
933
934    retVal = message
935    level = level or extractRegexResult(r"\[(?P<result>%s)\]" % '|'.join(_[0] for _ in getPublicTypeMembers(LOGGING_LEVELS)), message)
936
937    if message and (IS_TTY or istty) and not conf.get("disableColoring"):  # colorizing handler
938        if bold or color:
939            retVal = colored(message, color=color, on_color=None, attrs=("bold",) if bold else None)
940        elif level:
941            try:
942                level = getattr(logging, level, None)
943            except:
944                level = None
945            retVal = LOGGER_HANDLER.colorize(message, level)
946
947    return retVal
948
949def clearColors(message):
950    """
951    Clears ANSI color codes
952
953    >>> clearColors("\x1b[38;5;82mHello \x1b[38;5;198mWorld")
954    'Hello World'
955    """
956
957    retVal = message
958
959    if isinstance(message, str):
960        retVal = re.sub(r"\x1b\[[\d;]+m", "", message)
961
962    return retVal
963
964def dataToStdout(data, forceOutput=False, bold=False, content_type=None, status=CONTENT_STATUS.IN_PROGRESS):
965    """
966    Writes text to the stdout (console) stream
967    """
968
969    if not kb.get("threadException"):
970        if forceOutput or not (getCurrentThreadData().disableStdOut or kb.get("wizardMode")):
971            multiThreadMode = isMultiThreadMode()
972            if multiThreadMode:
973                logging._acquireLock()
974
975            try:
976                if conf.get("api"):
977                    sys.stdout.write(stdoutEncode(clearColors(data)), status, content_type)
978                else:
979                    sys.stdout.write(stdoutEncode(setColor(data, bold=bold)))
980
981                sys.stdout.flush()
982            except IOError:
983                pass
984
985            if multiThreadMode:
986                logging._releaseLock()
987
988            kb.prependFlag = isinstance(data, six.string_types) and (len(data) == 1 and data not in ('\n', '\r') or len(data) > 2 and data[0] == '\r' and data[-1] != '\n')
989
990def dataToTrafficFile(data):
991    if not conf.trafficFile:
992        return
993
994    try:
995        conf.trafficFP.write(data)
996        conf.trafficFP.flush()
997    except IOError as ex:
998        errMsg = "something went wrong while trying "
999        errMsg += "to write to the traffic file '%s' ('%s')" % (conf.trafficFile, getSafeExString(ex))
1000        raise SqlmapSystemException(errMsg)
1001
1002def dataToDumpFile(dumpFile, data):
1003    try:
1004        dumpFile.write(data)
1005        dumpFile.flush()
1006    except IOError as ex:
1007        if "No space left" in getUnicode(ex):
1008            errMsg = "no space left on output device"
1009            logger.error(errMsg)
1010        elif "Permission denied" in getUnicode(ex):
1011            errMsg = "permission denied when flushing dump data"
1012            logger.error(errMsg)
1013        else:
1014            raise
1015
1016def dataToOutFile(filename, data):
1017    retVal = None
1018
1019    if data:
1020        while True:
1021            retVal = os.path.join(conf.filePath, filePathToSafeString(filename))
1022
1023            try:
1024                with open(retVal, "w+b") as f:  # has to stay as non-codecs because data is raw ASCII encoded data
1025                    f.write(getBytes(data))
1026            except UnicodeEncodeError as ex:
1027                _ = normalizeUnicode(filename)
1028                if filename != _:
1029                    filename = _
1030                else:
1031                    errMsg = "couldn't write to the "
1032                    errMsg += "output file ('%s')" % getSafeExString(ex)
1033                    raise SqlmapGenericException(errMsg)
1034            except IOError as ex:
1035                errMsg = "something went wrong while trying to write "
1036                errMsg += "to the output file ('%s')" % getSafeExString(ex)
1037                raise SqlmapGenericException(errMsg)
1038            else:
1039                break
1040
1041    return retVal
1042
1043def readInput(message, default=None, checkBatch=True, boolean=False):
1044    """
1045    Reads input from terminal
1046    """
1047
1048    retVal = None
1049
1050    message = getUnicode(message)
1051
1052    if "\n" in message:
1053        message += "%s> " % ("\n" if message.count("\n") > 1 else "")
1054    elif message[-1] == ']':
1055        message += " "
1056
1057    if kb.get("prependFlag"):
1058        message = "\n%s" % message
1059        kb.prependFlag = False
1060
1061    if conf.get("answers"):
1062        if not any(_ in conf.answers for _ in ",="):
1063            return conf.answers
1064
1065        for item in conf.answers.split(','):
1066            question = item.split('=')[0].strip()
1067            answer = item.split('=')[1] if len(item.split('=')) > 1 else None
1068            if answer and question.lower() in message.lower():
1069                retVal = getUnicode(answer, UNICODE_ENCODING)
1070            elif answer is None and retVal:
1071                retVal = "%s,%s" % (retVal, getUnicode(item, UNICODE_ENCODING))
1072
1073    if message and IS_TTY:
1074        message = "\r%s" % message
1075
1076    if retVal:
1077        dataToStdout("%s%s\n" % (message, retVal), forceOutput=not kb.wizardMode, bold=True)
1078
1079        debugMsg = "used the given answer"
1080        logger.debug(debugMsg)
1081
1082    if retVal is None:
1083        if checkBatch and conf.get("batch") or conf.get("api"):
1084            if isListLike(default):
1085                options = ','.join(getUnicode(opt, UNICODE_ENCODING) for opt in default)
1086            elif default:
1087                options = getUnicode(default, UNICODE_ENCODING)
1088            else:
1089                options = six.text_type()
1090
1091            dataToStdout("%s%s\n" % (message, options), forceOutput=not kb.wizardMode, bold=True)
1092
1093            debugMsg = "used the default behavior, running in batch mode"
1094            logger.debug(debugMsg)
1095
1096            retVal = default
1097        else:
1098            try:
1099                logging._acquireLock()
1100
1101                if conf.get("beep"):
1102                    beep()
1103
1104                dataToStdout("%s" % message, forceOutput=not kb.wizardMode, bold=True)
1105                kb.prependFlag = False
1106
1107                retVal = _input().strip() or default
1108                retVal = getUnicode(retVal, encoding=sys.stdin.encoding) if retVal else retVal
1109            except:
1110                try:
1111                    time.sleep(0.05)  # Reference: http://www.gossamer-threads.com/lists/python/python/781893
1112                except:
1113                    pass
1114                finally:
1115                    kb.prependFlag = True
1116                    raise SqlmapUserQuitException
1117
1118            finally:
1119                logging._releaseLock()
1120
1121    if retVal and default and isinstance(default, six.string_types) and len(default) == 1:
1122        retVal = retVal.strip()
1123
1124    if boolean:
1125        retVal = retVal.strip().upper() == 'Y'
1126
1127    return retVal or ""
1128
1129def setTechnique(technique):
1130    """
1131    Thread-safe setting of currently used technique (Note: dealing with cases of per-thread technique switching)
1132    """
1133
1134    getCurrentThreadData().technique = technique
1135
1136def getTechnique():
1137    """
1138    Thread-safe getting of currently used technique
1139    """
1140
1141    return getCurrentThreadData().technique or kb.get("technique")
1142
1143def randomRange(start=0, stop=1000, seed=None):
1144    """
1145    Returns random integer value in given range
1146
1147    >>> random.seed(0)
1148    >>> randomRange(1, 500)
1149    152
1150    """
1151
1152    if seed is not None:
1153        _ = getCurrentThreadData().random
1154        _.seed(seed)
1155        randint = _.randint
1156    else:
1157        randint = random.randint
1158
1159    return int(randint(start, stop))
1160
1161def randomInt(length=4, seed=None):
1162    """
1163    Returns random integer value with provided number of digits
1164
1165    >>> random.seed(0)
1166    >>> randomInt(6)
1167    963638
1168    """
1169
1170    if seed is not None:
1171        _ = getCurrentThreadData().random
1172        _.seed(seed)
1173        choice = _.choice
1174    else:
1175        choice = random.choice
1176
1177    return int("".join(choice(string.digits if _ != 0 else string.digits.replace('0', '')) for _ in xrange(0, length)))
1178
1179def randomStr(length=4, lowercase=False, alphabet=None, seed=None):
1180    """
1181    Returns random string value with provided number of characters
1182
1183    >>> random.seed(0)
1184    >>> randomStr(6)
1185    'FUPGpY'
1186    """
1187
1188    if seed is not None:
1189        _ = getCurrentThreadData().random
1190        _.seed(seed)
1191        choice = _.choice
1192    else:
1193        choice = random.choice
1194
1195    if alphabet:
1196        retVal = "".join(choice(alphabet) for _ in xrange(0, length))
1197    elif lowercase:
1198        retVal = "".join(choice(string.ascii_lowercase) for _ in xrange(0, length))
1199    else:
1200        retVal = "".join(choice(string.ascii_letters) for _ in xrange(0, length))
1201
1202    return retVal
1203
1204def sanitizeStr(value):
1205    """
1206    Sanitizes string value in respect to newline and line-feed characters
1207
1208    >>> sanitizeStr('foo\\n\\rbar') == 'foo bar'
1209    True
1210    """
1211
1212    return getUnicode(value).replace("\n", " ").replace("\r", "")
1213
1214def getHeader(headers, key):
1215    """
1216    Returns header value ignoring the letter case
1217
1218    >>> getHeader({"Foo": "bar"}, "foo")
1219    'bar'
1220    """
1221
1222    retVal = None
1223    for _ in (headers or {}):
1224        if _.upper() == key.upper():
1225            retVal = headers[_]
1226            break
1227    return retVal
1228
1229def checkPipedInput():
1230    """
1231    Checks whether input to program has been provided via standard input (e.g. cat /tmp/req.txt | python sqlmap.py -r -)
1232    # Reference: https://stackoverflow.com/a/33873570
1233    """
1234
1235    return not os.isatty(sys.stdin.fileno()) if hasattr(sys.stdin, "fileno") else False
1236
1237def isZipFile(filename):
1238    """
1239    Checks if file contains zip compressed content
1240
1241    >>> isZipFile(paths.WORDLIST)
1242    True
1243    """
1244
1245    checkFile(filename)
1246
1247    return openFile(filename, "rb", encoding=None).read(len(ZIP_HEADER)) == ZIP_HEADER
1248
1249def isDigit(value):
1250    """
1251    Checks if provided (string) value consists of digits (Note: Python's isdigit() is problematic)
1252
1253    >>> u'\xb2'.isdigit()
1254    True
1255    >>> isDigit(u'\xb2')
1256    False
1257    >>> isDigit('123456')
1258    True
1259    >>> isDigit('3b3')
1260    False
1261    """
1262
1263    return re.search(r"\A[0-9]+\Z", value or "") is not None
1264
1265def checkFile(filename, raiseOnError=True):
1266    """
1267    Checks for file existence and readability
1268
1269    >>> checkFile(__file__)
1270    True
1271    """
1272
1273    valid = True
1274
1275    if filename:
1276        filename = filename.strip('"\'')
1277
1278    if filename == STDIN_PIPE_DASH:
1279        return checkPipedInput()
1280    else:
1281        try:
1282            if filename is None or not os.path.isfile(filename):
1283                valid = False
1284        except:
1285            valid = False
1286
1287        if valid:
1288            try:
1289                with open(filename, "rb"):
1290                    pass
1291            except:
1292                valid = False
1293
1294    if not valid and raiseOnError:
1295        raise SqlmapSystemException("unable to read file '%s'" % filename)
1296
1297    return valid
1298
1299def banner():
1300    """
1301    This function prints sqlmap banner with its version
1302    """
1303
1304    if not any(_ in sys.argv for _ in ("--version", "--api")) and not conf.get("disableBanner"):
1305        result = BANNER
1306
1307        if not IS_TTY or "--disable-coloring" in sys.argv:
1308            result = clearColors(result)
1309        elif IS_WIN:
1310            coloramainit()
1311
1312        dataToStdout(result, forceOutput=True)
1313
1314def parsePasswordHash(password):
1315    """
1316    In case of Microsoft SQL Server password hash value is expanded to its components
1317
1318    >>> pushValue(kb.forcedDbms)
1319    >>> kb.forcedDbms = DBMS.MSSQL
1320    >>> "salt: 4086ceb6" in parsePasswordHash("0x01004086ceb60c90646a8ab9889fe3ed8e5c150b5460ece8425a")
1321    True
1322    >>> kb.forcedDbms = popValue()
1323    """
1324
1325    blank = " " * 8
1326
1327    if isNoneValue(password) or password == " ":
1328        retVal = NULL
1329    else:
1330        retVal = password
1331
1332    if Backend.isDbms(DBMS.MSSQL) and retVal != NULL and isHexEncodedString(password):
1333        retVal = "%s\n" % password
1334        retVal += "%sheader: %s\n" % (blank, password[:6])
1335        retVal += "%ssalt: %s\n" % (blank, password[6:14])
1336        retVal += "%smixedcase: %s\n" % (blank, password[14:54])
1337
1338        if password[54:]:
1339            retVal += "%suppercase: %s" % (blank, password[54:])
1340
1341    return retVal
1342
1343def cleanQuery(query):
1344    """
1345    Switch all SQL statement (alike) keywords to upper case
1346
1347    >>> cleanQuery("select id from users")
1348    'SELECT id FROM users'
1349    """
1350
1351    retVal = query
1352
1353    for sqlStatements in SQL_STATEMENTS.values():
1354        for sqlStatement in sqlStatements:
1355            candidate = sqlStatement.replace("(", "").replace(")", "").strip()
1356            queryMatch = re.search(r"(?i)\b(%s)\b" % candidate, query)
1357
1358            if queryMatch and "sys_exec" not in query:
1359                retVal = retVal.replace(queryMatch.group(1), candidate.upper())
1360
1361    return retVal
1362
1363def setPaths(rootPath):
1364    """
1365    Sets absolute paths for project directories and files
1366    """
1367
1368    paths.SQLMAP_ROOT_PATH = rootPath
1369
1370    # sqlmap paths
1371    paths.SQLMAP_DATA_PATH = os.path.join(paths.SQLMAP_ROOT_PATH, "data")
1372    paths.SQLMAP_EXTRAS_PATH = os.path.join(paths.SQLMAP_ROOT_PATH, "extra")
1373    paths.SQLMAP_SETTINGS_PATH = os.path.join(paths.SQLMAP_ROOT_PATH, "lib", "core", "settings.py")
1374    paths.SQLMAP_TAMPER_PATH = os.path.join(paths.SQLMAP_ROOT_PATH, "tamper")
1375    paths.SQLMAP_WAF_PATH = os.path.join(paths.SQLMAP_ROOT_PATH, "waf")
1376
1377    paths.SQLMAP_PROCS_PATH = os.path.join(paths.SQLMAP_DATA_PATH, "procs")
1378    paths.SQLMAP_SHELL_PATH = os.path.join(paths.SQLMAP_DATA_PATH, "shell")
1379    paths.SQLMAP_TXT_PATH = os.path.join(paths.SQLMAP_DATA_PATH, "txt")
1380    paths.SQLMAP_UDF_PATH = os.path.join(paths.SQLMAP_DATA_PATH, "udf")
1381    paths.SQLMAP_XML_PATH = os.path.join(paths.SQLMAP_DATA_PATH, "xml")
1382    paths.SQLMAP_XML_BANNER_PATH = os.path.join(paths.SQLMAP_XML_PATH, "banner")
1383    paths.SQLMAP_XML_PAYLOADS_PATH = os.path.join(paths.SQLMAP_XML_PATH, "payloads")
1384
1385    # sqlmap files
1386    paths.COMMON_COLUMNS = os.path.join(paths.SQLMAP_TXT_PATH, "common-columns.txt")
1387    paths.COMMON_FILES = os.path.join(paths.SQLMAP_TXT_PATH, "common-files.txt")
1388    paths.COMMON_TABLES = os.path.join(paths.SQLMAP_TXT_PATH, "common-tables.txt")
1389    paths.COMMON_OUTPUTS = os.path.join(paths.SQLMAP_TXT_PATH, 'common-outputs.txt')
1390    paths.SQL_KEYWORDS = os.path.join(paths.SQLMAP_TXT_PATH, "keywords.txt")
1391    paths.SMALL_DICT = os.path.join(paths.SQLMAP_TXT_PATH, "smalldict.txt")
1392    paths.USER_AGENTS = os.path.join(paths.SQLMAP_TXT_PATH, "user-agents.txt")
1393    paths.WORDLIST = os.path.join(paths.SQLMAP_TXT_PATH, "wordlist.tx_")
1394    paths.ERRORS_XML = os.path.join(paths.SQLMAP_XML_PATH, "errors.xml")
1395    paths.BOUNDARIES_XML = os.path.join(paths.SQLMAP_XML_PATH, "boundaries.xml")
1396    paths.LIVE_TESTS_XML = os.path.join(paths.SQLMAP_XML_PATH, "livetests.xml")
1397    paths.QUERIES_XML = os.path.join(paths.SQLMAP_XML_PATH, "queries.xml")
1398    paths.GENERIC_XML = os.path.join(paths.SQLMAP_XML_BANNER_PATH, "generic.xml")
1399    paths.MSSQL_XML = os.path.join(paths.SQLMAP_XML_BANNER_PATH, "mssql.xml")
1400    paths.MYSQL_XML = os.path.join(paths.SQLMAP_XML_BANNER_PATH, "mysql.xml")
1401    paths.ORACLE_XML = os.path.join(paths.SQLMAP_XML_BANNER_PATH, "oracle.xml")
1402    paths.PGSQL_XML = os.path.join(paths.SQLMAP_XML_BANNER_PATH, "postgresql.xml")
1403
1404    for path in paths.values():
1405        if any(path.endswith(_) for _ in (".txt", ".xml", ".tx_")):
1406            checkFile(path)
1407
1408    if IS_WIN:
1409        if os.getenv("LOCALAPPDATA"):
1410            paths.SQLMAP_HOME_PATH = os.path.expandvars("%LOCALAPPDATA%\\sqlmap")
1411        elif os.getenv("USERPROFILE"):
1412            paths.SQLMAP_HOME_PATH = os.path.expandvars("%USERPROFILE%\\Local Settings\\sqlmap")
1413        else:
1414            paths.SQLMAP_HOME_PATH = os.path.join(os.path.expandvars(os.path.expanduser("~")), "sqlmap")
1415    else:
1416        paths.SQLMAP_HOME_PATH = os.path.join(os.path.expandvars(os.path.expanduser("~")), ".sqlmap")
1417
1418    paths.SQLMAP_OUTPUT_PATH = getUnicode(paths.get("SQLMAP_OUTPUT_PATH", os.path.join(paths.SQLMAP_HOME_PATH, "output")), encoding=sys.getfilesystemencoding() or UNICODE_ENCODING)
1419    paths.SQLMAP_DUMP_PATH = os.path.join(paths.SQLMAP_OUTPUT_PATH, "%s", "dump")
1420    paths.SQLMAP_FILES_PATH = os.path.join(paths.SQLMAP_OUTPUT_PATH, "%s", "files")
1421
1422    # history files
1423    paths.SQLMAP_HISTORY_PATH = getUnicode(os.path.join(paths.SQLMAP_HOME_PATH, "history"), encoding=sys.getfilesystemencoding() or UNICODE_ENCODING)
1424    paths.API_SHELL_HISTORY = os.path.join(paths.SQLMAP_HISTORY_PATH, "api.hst")
1425    paths.OS_SHELL_HISTORY = os.path.join(paths.SQLMAP_HISTORY_PATH, "os.hst")
1426    paths.SQL_SHELL_HISTORY = os.path.join(paths.SQLMAP_HISTORY_PATH, "sql.hst")
1427    paths.SQLMAP_SHELL_HISTORY = os.path.join(paths.SQLMAP_HISTORY_PATH, "sqlmap.hst")
1428    paths.GITHUB_HISTORY = os.path.join(paths.SQLMAP_HISTORY_PATH, "github.hst")
1429
1430def weAreFrozen():
1431    """
1432    Returns whether we are frozen via py2exe.
1433    This will affect how we find out where we are located.
1434
1435    # Reference: http://www.py2exe.org/index.cgi/WhereAmI
1436    """
1437
1438    return hasattr(sys, "frozen")
1439
1440def parseTargetDirect():
1441    """
1442    Parse target dbms and set some attributes into the configuration singleton
1443
1444    >>> pushValue(conf.direct)
1445    >>> conf.direct = "mysql://root:testpass@127.0.0.1:3306/testdb"
1446    >>> parseTargetDirect()
1447    >>> conf.dbmsDb
1448    'testdb'
1449    >>> conf.dbmsPass
1450    'testpass'
1451    >>> conf.direct = popValue()
1452    """
1453
1454    if not conf.direct:
1455        return
1456
1457    details = None
1458    remote = False
1459
1460    for dbms in SUPPORTED_DBMS:
1461        details = re.search(r"^(?P<dbms>%s)://(?P<credentials>(?P<user>.+?)\:(?P<pass>.*)\@)?(?P<remote>(?P<hostname>[\w.-]+?)\:(?P<port>[\d]+)\/)?(?P<db>[\w\d\ \:\.\_\-\/\\]+?)$" % dbms, conf.direct, re.I)
1462
1463        if details:
1464            conf.dbms = details.group("dbms")
1465
1466            if details.group('credentials'):
1467                conf.dbmsUser = details.group("user")
1468                conf.dbmsPass = details.group("pass")
1469            else:
1470                if conf.dbmsCred:
1471                    conf.dbmsUser, conf.dbmsPass = conf.dbmsCred.split(':')
1472                else:
1473                    conf.dbmsUser = ""
1474                    conf.dbmsPass = ""
1475
1476            if not conf.dbmsPass:
1477                conf.dbmsPass = None
1478
1479            if details.group("remote"):
1480                remote = True
1481                conf.hostname = details.group("hostname").strip()
1482                conf.port = int(details.group("port"))
1483            else:
1484                conf.hostname = "localhost"
1485                conf.port = 0
1486
1487            conf.dbmsDb = details.group("db").strip() if details.group("db") is not None else None
1488            conf.parameters[None] = "direct connection"
1489
1490            break
1491
1492    if kb.smokeMode:
1493        return
1494
1495    if not details:
1496        errMsg = "invalid target details, valid syntax is for instance "
1497        errMsg += "'mysql://USER:PASSWORD@DBMS_IP:DBMS_PORT/DATABASE_NAME' "
1498        errMsg += "or 'access://DATABASE_FILEPATH'"
1499        raise SqlmapSyntaxException(errMsg)
1500
1501    for dbmsName, data in DBMS_DICT.items():
1502        if dbmsName == conf.dbms or conf.dbms.lower() in data[0]:
1503            try:
1504                conf.dbms = dbmsName
1505
1506                if dbmsName in (DBMS.ACCESS, DBMS.SQLITE, DBMS.FIREBIRD):
1507                    if remote:
1508                        warnMsg = "direct connection over the network for "
1509                        warnMsg += "%s DBMS is not supported" % dbmsName
1510                        logger.warn(warnMsg)
1511
1512                        conf.hostname = "localhost"
1513                        conf.port = 0
1514                elif not remote:
1515                    errMsg = "missing remote connection details (e.g. "
1516                    errMsg += "'mysql://USER:PASSWORD@DBMS_IP:DBMS_PORT/DATABASE_NAME' "
1517                    errMsg += "or 'access://DATABASE_FILEPATH')"
1518                    raise SqlmapSyntaxException(errMsg)
1519
1520                if dbmsName in (DBMS.MSSQL, DBMS.SYBASE):
1521                    __import__("_mssql")
1522                    pymssql = __import__("pymssql")
1523
1524                    if not hasattr(pymssql, "__version__") or pymssql.__version__ < "1.0.2":
1525                        errMsg = "'%s' third-party library must be " % data[1]
1526                        errMsg += "version >= 1.0.2 to work properly. "
1527                        errMsg += "Download from '%s'" % data[2]
1528                        raise SqlmapMissingDependence(errMsg)
1529
1530                elif dbmsName == DBMS.MYSQL:
1531                    __import__("pymysql")
1532                elif dbmsName == DBMS.PGSQL:
1533                    __import__("psycopg2")
1534                elif dbmsName == DBMS.ORACLE:
1535                    __import__("cx_Oracle")
1536
1537                    # Reference: http://itsiti.com/ora-28009-connection-sys-sysdba-sysoper
1538                    if (conf.dbmsUser or "").upper() == "SYS":
1539                        conf.direct = "%s?mode=SYSDBA" % conf.direct
1540                elif dbmsName == DBMS.SQLITE:
1541                    __import__("sqlite3")
1542                elif dbmsName == DBMS.ACCESS:
1543                    __import__("pyodbc")
1544                elif dbmsName == DBMS.FIREBIRD:
1545                    __import__("kinterbasdb")
1546            except (SqlmapSyntaxException, SqlmapMissingDependence):
1547                raise
1548            except:
1549                if _sqlalchemy and data[3] and any(_ in _sqlalchemy.dialects.__all__ for _ in (data[3], data[3].split('+')[0])):
1550                    pass
1551                else:
1552                    errMsg = "sqlmap requires '%s' third-party library " % data[1]
1553                    errMsg += "in order to directly connect to the DBMS "
1554                    errMsg += "'%s'. You can download it from '%s'" % (dbmsName, data[2])
1555                    errMsg += ". Alternative is to use a package 'python-sqlalchemy' "
1556                    errMsg += "with support for dialect '%s' installed" % data[3]
1557                    raise SqlmapMissingDependence(errMsg)
1558
1559def parseTargetUrl():
1560    """
1561    Parse target URL and set some attributes into the configuration singleton
1562
1563    >>> pushValue(conf.url)
1564    >>> conf.url = "https://www.test.com/?id=1"
1565    >>> parseTargetUrl()
1566    >>> conf.hostname
1567    'www.test.com'
1568    >>> conf.scheme
1569    'https'
1570    >>> conf.url = popValue()
1571    """
1572
1573    if not conf.url:
1574        return
1575
1576    originalUrl = conf.url
1577
1578    if re.search(r"\[.+\]", conf.url) and not socket.has_ipv6:
1579        errMsg = "IPv6 addressing is not supported "
1580        errMsg += "on this platform"
1581        raise SqlmapGenericException(errMsg)
1582
1583    if not re.search(r"^(http|ws)s?://", conf.url, re.I):
1584        if re.search(r":443\b", conf.url):
1585            conf.url = "https://%s" % conf.url
1586        else:
1587            conf.url = "http://%s" % conf.url
1588
1589    if kb.customInjectionMark in conf.url:
1590        conf.url = conf.url.replace('?', URI_QUESTION_MARKER)
1591
1592    try:
1593        urlSplit = _urllib.parse.urlsplit(conf.url)
1594    except ValueError as ex:
1595        errMsg = "invalid URL '%s' has been given ('%s'). " % (conf.url, getSafeExString(ex))
1596        errMsg += "Please be sure that you don't have any leftover characters (e.g. '[' or ']') "
1597        errMsg += "in the hostname part"
1598        raise SqlmapGenericException(errMsg)
1599
1600    hostnamePort = urlSplit.netloc.split(":") if not re.search(r"\[.+\]", urlSplit.netloc) else filterNone((re.search(r"\[.+\]", urlSplit.netloc).group(0), re.search(r"\](:(?P<port>\d+))?", urlSplit.netloc).group("port")))
1601
1602    conf.scheme = (urlSplit.scheme.strip().lower() or "http")
1603    conf.path = urlSplit.path.strip()
1604    conf.hostname = hostnamePort[0].strip()
1605
1606    if conf.forceSSL:
1607        conf.scheme = re.sub(r"(?i)\A(http|ws)\Z", r"\g<1>s", conf.scheme)
1608
1609    conf.ipv6 = conf.hostname != conf.hostname.strip("[]")
1610    conf.hostname = conf.hostname.strip("[]").replace(kb.customInjectionMark, "")
1611
1612    try:
1613        conf.hostname.encode("idna")
1614        conf.hostname.encode(UNICODE_ENCODING)
1615    except (LookupError, UnicodeError):
1616        invalid = True
1617    else:
1618        invalid = False
1619
1620    if any((invalid, re.search(r"\s", conf.hostname), '..' in conf.hostname, conf.hostname.startswith('.'), '\n' in originalUrl)):
1621        errMsg = "invalid target URL ('%s')" % originalUrl
1622        raise SqlmapSyntaxException(errMsg)
1623
1624    if len(hostnamePort) == 2:
1625        try:
1626            conf.port = int(hostnamePort[1])
1627        except:
1628            errMsg = "invalid target URL"
1629            raise SqlmapSyntaxException(errMsg)
1630    elif conf.scheme in ("https", "wss"):
1631        conf.port = 443
1632    else:
1633        conf.port = 80
1634
1635    if conf.port < 1 or conf.port > 65535:
1636        errMsg = "invalid target URL's port (%d)" % conf.port
1637        raise SqlmapSyntaxException(errMsg)
1638
1639    conf.url = getUnicode("%s://%s:%d%s" % (conf.scheme, ("[%s]" % conf.hostname) if conf.ipv6 else conf.hostname, conf.port, conf.path))
1640    conf.url = conf.url.replace(URI_QUESTION_MARKER, '?')
1641
1642    if urlSplit.query:
1643        if '=' not in urlSplit.query:
1644            conf.url = "%s?%s" % (conf.url, getUnicode(urlSplit.query))
1645        else:
1646            conf.parameters[PLACE.GET] = urldecode(urlSplit.query) if urlSplit.query and urlencode(DEFAULT_GET_POST_DELIMITER, None) not in urlSplit.query else urlSplit.query
1647
1648    if not conf.referer and (intersect(REFERER_ALIASES, conf.testParameter, True) or conf.level >= 3):
1649        debugMsg = "setting the HTTP Referer header to the target URL"
1650        logger.debug(debugMsg)
1651        conf.httpHeaders = [_ for _ in conf.httpHeaders if _[0] != HTTP_HEADER.REFERER]
1652        conf.httpHeaders.append((HTTP_HEADER.REFERER, conf.url.replace(kb.customInjectionMark, "")))
1653
1654    if not conf.host and (intersect(HOST_ALIASES, conf.testParameter, True) or conf.level >= 5):
1655        debugMsg = "setting the HTTP Host header to the target URL"
1656        logger.debug(debugMsg)
1657        conf.httpHeaders = [_ for _ in conf.httpHeaders if _[0] != HTTP_HEADER.HOST]
1658        conf.httpHeaders.append((HTTP_HEADER.HOST, getHostHeader(conf.url)))
1659
1660    if conf.url != originalUrl:
1661        kb.originalUrls[conf.url] = originalUrl
1662
1663def escapeJsonValue(value):
1664    """
1665    Escapes JSON value (used in payloads)
1666
1667    # Reference: https://stackoverflow.com/a/16652683
1668    """
1669
1670    retVal = ""
1671
1672    for char in value:
1673        if char < ' ' or char == '"':
1674            retVal += json.dumps(char)[1:-1]
1675        else:
1676            retVal += char
1677
1678    return retVal
1679
1680def expandAsteriskForColumns(expression):
1681    """
1682    If the user provided an asterisk rather than the column(s)
1683    name, sqlmap will retrieve the columns itself and reprocess
1684    the SQL query string (expression)
1685    """
1686
1687    match = re.search(r"(?i)\ASELECT(\s+TOP\s+[\d]+)?\s+\*\s+FROM\s+`?([^`\s()]+)", expression)
1688
1689    if match:
1690        infoMsg = "you did not provide the fields in your query. "
1691        infoMsg += "sqlmap will retrieve the column names itself"
1692        logger.info(infoMsg)
1693
1694        _ = match.group(2).replace("..", '.').replace(".dbo.", '.')
1695        db, conf.tbl = _.split('.', 1) if '.' in _ else (None, _)
1696
1697        if db is None:
1698            if expression != conf.sqlQuery:
1699                conf.db = db
1700            elif conf.db:
1701                expression = re.sub(r"([^\w])%s" % re.escape(conf.tbl), r"\g<1>%s.%s" % (conf.db, conf.tbl), expression)
1702        else:
1703            conf.db = db
1704
1705        conf.db = safeSQLIdentificatorNaming(conf.db)
1706        conf.tbl = safeSQLIdentificatorNaming(conf.tbl, True)
1707
1708        columnsDict = conf.dbmsHandler.getColumns(onlyColNames=True)
1709
1710        if columnsDict and conf.db in columnsDict and conf.tbl in columnsDict[conf.db]:
1711            columns = list(columnsDict[conf.db][conf.tbl].keys())
1712            columns.sort()
1713            columnsStr = ", ".join(column for column in columns)
1714            expression = expression.replace('*', columnsStr, 1)
1715
1716            infoMsg = "the query with expanded column name(s) is: "
1717            infoMsg += "%s" % expression
1718            logger.info(infoMsg)
1719
1720    return expression
1721
1722def getLimitRange(count, plusOne=False):
1723    """
1724    Returns range of values used in limit/offset constructs
1725
1726    >>> [_ for _ in getLimitRange(10)]
1727    [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
1728    """
1729
1730    retVal = None
1731    count = int(count)
1732    limitStart, limitStop = 1, count
1733    reverse = False
1734
1735    if kb.dumpTable:
1736        if conf.limitStart and conf.limitStop and conf.limitStart > conf.limitStop:
1737            limitStop = conf.limitStart
1738            limitStart = conf.limitStop
1739            reverse = True
1740        else:
1741            if isinstance(conf.limitStop, int) and conf.limitStop > 0 and conf.limitStop < limitStop:
1742                limitStop = conf.limitStop
1743
1744            if isinstance(conf.limitStart, int) and conf.limitStart > 0 and conf.limitStart <= limitStop:
1745                limitStart = conf.limitStart
1746
1747    retVal = xrange(limitStart, limitStop + 1) if plusOne else xrange(limitStart - 1, limitStop)
1748
1749    if reverse:
1750        retVal = xrange(retVal[-1], retVal[0] - 1, -1)
1751
1752    return retVal
1753
1754def parseUnionPage(page):
1755    """
1756    Returns resulting items from UNION query inside provided page content
1757    """
1758
1759    if page is None:
1760        return None
1761
1762    if re.search(r"(?si)\A%s.*%s\Z" % (kb.chars.start, kb.chars.stop), page):
1763        if len(page) > LARGE_OUTPUT_THRESHOLD:
1764            warnMsg = "large output detected. This might take a while"
1765            logger.warn(warnMsg)
1766
1767        data = BigArray()
1768        keys = set()
1769
1770        for match in re.finditer(r"%s(.*?)%s" % (kb.chars.start, kb.chars.stop), page, re.DOTALL | re.IGNORECASE):
1771            entry = match.group(1)
1772
1773            if kb.chars.start in entry:
1774                entry = entry.split(kb.chars.start)[-1]
1775
1776            if kb.unionDuplicates:
1777                key = entry.lower()
1778                if key not in keys:
1779                    keys.add(key)
1780                else:
1781                    continue
1782
1783            entry = entry.split(kb.chars.delimiter)
1784
1785            if conf.hexConvert:
1786                entry = applyFunctionRecursively(entry, decodeDbmsHexValue)
1787
1788            if kb.safeCharEncode:
1789                entry = applyFunctionRecursively(entry, safecharencode)
1790
1791            data.append(entry[0] if len(entry) == 1 else entry)
1792    else:
1793        data = page
1794
1795    if len(data) == 1 and isinstance(data[0], six.string_types):
1796        data = data[0]
1797
1798    return data
1799
1800def parseFilePaths(page):
1801    """
1802    Detects (possible) absolute system paths inside the provided page content
1803
1804    >>> _ = "/var/www/html/index.php"; parseFilePaths("<html>Error occurred at line 207 of: %s<br>Please contact your administrator</html>" % _); _ in kb.absFilePaths
1805    True
1806    """
1807
1808    if page:
1809        for regex in FILE_PATH_REGEXES:
1810            for match in re.finditer(regex, page):
1811                absFilePath = match.group("result").strip()
1812                page = page.replace(absFilePath, "")
1813
1814                if isWindowsDriveLetterPath(absFilePath):
1815                    absFilePath = posixToNtSlashes(absFilePath)
1816
1817                if absFilePath not in kb.absFilePaths:
1818                    kb.absFilePaths.add(absFilePath)
1819
1820def getLocalIP():
1821    """
1822    Get local IP address (exposed to the remote/target)
1823    """
1824
1825    retVal = None
1826
1827    try:
1828        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
1829        s.connect((conf.hostname, conf.port))
1830        retVal, _ = s.getsockname()
1831        s.close()
1832    except:
1833        debugMsg = "there was an error in opening socket "
1834        debugMsg += "connection toward '%s'" % conf.hostname
1835        logger.debug(debugMsg)
1836
1837    return retVal
1838
1839def getRemoteIP():
1840    """
1841    Get remote/target IP address
1842    """
1843
1844    retVal = None
1845
1846    try:
1847        retVal = socket.gethostbyname(conf.hostname)
1848    except socket.gaierror:
1849        errMsg = "address resolution problem "
1850        errMsg += "occurred for hostname '%s'" % conf.hostname
1851        singleTimeLogMessage(errMsg, logging.ERROR)
1852
1853    return retVal
1854
1855def getFileType(filePath):
1856    """
1857    Returns "magic" file type for given file path
1858
1859    >>> getFileType(__file__)
1860    'text'
1861    >>> getFileType(sys.executable)
1862    'binary'
1863    """
1864
1865    try:
1866        desc = magic.from_file(filePath) or magic.MAGIC_UNKNOWN_FILETYPE
1867    except:
1868        desc = magic.MAGIC_UNKNOWN_FILETYPE
1869    finally:
1870        desc = getText(desc)
1871
1872    if desc == getText(magic.MAGIC_UNKNOWN_FILETYPE):
1873        content = openFile(filePath, "rb", encoding=None).read()
1874
1875        try:
1876            content.decode()
1877        except:
1878            pass
1879        else:
1880            desc = "ascii"
1881
1882    return "text" if any(_ in desc.lower() for _ in ("ascii", "text")) else "binary"
1883
1884def getCharset(charsetType=None):
1885    """
1886    Returns list with integers representing characters of a given
1887    charset type appropriate for inference techniques
1888
1889    >>> getCharset(CHARSET_TYPE.BINARY)
1890    [0, 1, 47, 48, 49]
1891    """
1892
1893    asciiTbl = []
1894
1895    if charsetType is None:
1896        asciiTbl.extend(xrange(0, 128))
1897
1898    # Binary
1899    elif charsetType == CHARSET_TYPE.BINARY:
1900        asciiTbl.extend((0, 1))
1901        asciiTbl.extend(xrange(47, 50))
1902
1903    # Digits
1904    elif charsetType == CHARSET_TYPE.DIGITS:
1905        asciiTbl.extend((0, 9))
1906        asciiTbl.extend(xrange(47, 58))
1907
1908    # Hexadecimal
1909    elif charsetType == CHARSET_TYPE.HEXADECIMAL:
1910        asciiTbl.extend((0, 1))
1911        asciiTbl.extend(xrange(47, 58))
1912        asciiTbl.extend(xrange(64, 71))
1913        asciiTbl.extend((87, 88))  # X
1914        asciiTbl.extend(xrange(96, 103))
1915        asciiTbl.extend((119, 120))  # x
1916
1917    # Characters
1918    elif charsetType == CHARSET_TYPE.ALPHA:
1919        asciiTbl.extend((0, 1))
1920        asciiTbl.extend(xrange(64, 91))
1921        asciiTbl.extend(xrange(96, 123))
1922
1923    # Characters and digits
1924    elif charsetType == CHARSET_TYPE.ALPHANUM:
1925        asciiTbl.extend((0, 1))
1926        asciiTbl.extend(xrange(47, 58))
1927        asciiTbl.extend(xrange(64, 91))
1928        asciiTbl.extend(xrange(96, 123))
1929
1930    return asciiTbl
1931
1932def directoryPath(filepath):
1933    """
1934    Returns directory path for a given filepath
1935
1936    >>> directoryPath('/var/log/apache.log')
1937    '/var/log'
1938    >>> directoryPath('/var/log')
1939    '/var/log'
1940    """
1941
1942    retVal = filepath
1943
1944    if filepath and os.path.splitext(filepath)[-1]:
1945        retVal = ntpath.dirname(filepath) if isWindowsDriveLetterPath(filepath) else posixpath.dirname(filepath)
1946
1947    return retVal
1948
1949def normalizePath(filepath):
1950    """
1951    Returns normalized string representation of a given filepath
1952
1953    >>> normalizePath('//var///log/apache.log')
1954    '/var/log/apache.log'
1955    """
1956
1957    retVal = filepath
1958
1959    if retVal:
1960        retVal = retVal.strip("\r\n")
1961        retVal = ntpath.normpath(retVal) if isWindowsDriveLetterPath(retVal) else re.sub(r"\A/{2,}", "/", posixpath.normpath(retVal))
1962
1963    return retVal
1964
1965def safeFilepathEncode(filepath):
1966    """
1967    Returns filepath in (ASCII) format acceptable for OS handling (e.g. reading)
1968    """
1969
1970    retVal = filepath
1971
1972    if filepath and six.PY2 and isinstance(filepath, six.text_type):
1973        retVal = getBytes(filepath, sys.getfilesystemencoding() or UNICODE_ENCODING)
1974
1975    return retVal
1976
1977
1978def safeExpandUser(filepath):
1979    """
1980    Patch for a Python Issue18171 (http://bugs.python.org/issue18171)
1981    """
1982
1983    retVal = filepath
1984
1985    try:
1986        retVal = os.path.expanduser(filepath)
1987    except UnicodeError:
1988        _ = locale.getdefaultlocale()
1989        encoding = _[1] if _ and len(_) > 1 else UNICODE_ENCODING
1990        retVal = getUnicode(os.path.expanduser(filepath.encode(encoding)), encoding=encoding)
1991
1992    return retVal
1993
1994def safeStringFormat(format_, params):
1995    """
1996    Avoids problems with inappropriate string format strings
1997
1998    >>> safeStringFormat('SELECT foo FROM %s LIMIT %d', ('bar', '1'))
1999    'SELECT foo FROM bar LIMIT 1'
2000    """
2001
2002    if format_.count(PAYLOAD_DELIMITER) == 2:
2003        _ = format_.split(PAYLOAD_DELIMITER)
2004        _[1] = re.sub(r"(\A|[^A-Za-z0-9])(%d)([^A-Za-z0-9]|\Z)", r"\g<1>%s\g<3>", _[1])
2005        retVal = PAYLOAD_DELIMITER.join(_)
2006    else:
2007        retVal = re.sub(r"(\A|[^A-Za-z0-9])(%d)([^A-Za-z0-9]|\Z)", r"\g<1>%s\g<3>", format_)
2008
2009    if isinstance(params, six.string_types):
2010        retVal = retVal.replace("%s", params, 1)
2011    elif not isListLike(params):
2012        retVal = retVal.replace("%s", getUnicode(params), 1)
2013    else:
2014        start, end = 0, len(retVal)
2015        match = re.search(r"%s(.+)%s" % (PAYLOAD_DELIMITER, PAYLOAD_DELIMITER), retVal)
2016        if match and PAYLOAD_DELIMITER not in match.group(1):
2017            start, end = match.start(), match.end()
2018        if retVal.count("%s", start, end) == len(params):
2019            for param in params:
2020                index = retVal.find("%s", start)
2021                retVal = retVal[:index] + getUnicode(param) + retVal[index + 2:]
2022        else:
2023            if any('%s' in _ for _ in conf.parameters.values()):
2024                parts = format_.split(' ')
2025                for i in xrange(len(parts)):
2026                    if PAYLOAD_DELIMITER in parts[i]:
2027                        parts[i] = parts[i].replace(PAYLOAD_DELIMITER, "")
2028                        parts[i] = "%s%s" % (parts[i], PAYLOAD_DELIMITER)
2029                        break
2030                format_ = ' '.join(parts)
2031
2032            count = 0
2033            while True:
2034                match = re.search(r"(\A|[^A-Za-z0-9])(%s)([^A-Za-z0-9]|\Z)", retVal)
2035                if match:
2036                    if count >= len(params):
2037                        warnMsg = "wrong number of parameters during string formatting. "
2038                        warnMsg += "Please report by e-mail content \"%r | %r | %r\" to '%s'" % (format_, params, retVal, DEV_EMAIL_ADDRESS)
2039                        raise SqlmapValueException(warnMsg)
2040                    else:
2041                        retVal = re.sub(r"(\A|[^A-Za-z0-9])(%s)([^A-Za-z0-9]|\Z)", r"\g<1>%s\g<3>" % params[count], retVal, 1)
2042                        count += 1
2043                else:
2044                    break
2045
2046    retVal = getText(retVal)
2047
2048    return retVal
2049
2050def getFilteredPageContent(page, onlyText=True, split=" "):
2051    """
2052    Returns filtered page content without script, style and/or comments
2053    or all HTML tags
2054
2055    >>> getFilteredPageContent(u'<html><title>foobar</title><body>test</body></html>') == "foobar test"
2056    True
2057    """
2058
2059    retVal = page
2060
2061    # only if the page's charset has been successfully identified
2062    if isinstance(page, six.text_type):
2063        retVal = re.sub(r"(?si)<script.+?</script>|<!--.+?-->|<style.+?</style>%s" % (r"|<[^>]+>|\t|\n|\r" if onlyText else ""), split, page)
2064        retVal = re.sub(r"%s{2,}" % split, split, retVal)
2065        retVal = htmlUnescape(retVal.strip().strip(split))
2066
2067    return retVal
2068
2069def getPageWordSet(page):
2070    """
2071    Returns word set used in page content
2072
2073    >>> sorted(getPageWordSet(u'<html><title>foobar</title><body>test</body></html>')) == [u'foobar', u'test']
2074    True
2075    """
2076
2077    retVal = set()
2078
2079    # only if the page's charset has been successfully identified
2080    if isinstance(page, six.string_types):
2081        retVal = set(_.group(0) for _ in re.finditer(r"\w+", getFilteredPageContent(page)))
2082
2083    return retVal
2084
2085def showStaticWords(firstPage, secondPage, minLength=3):
2086    """
2087    Prints words appearing in two different response pages
2088
2089    >>> showStaticWords("this is a test", "this is another test")
2090    ['this']
2091    """
2092
2093    infoMsg = "finding static words in longest matching part of dynamic page content"
2094    logger.info(infoMsg)
2095
2096    firstPage = getFilteredPageContent(firstPage)
2097    secondPage = getFilteredPageContent(secondPage)
2098
2099    infoMsg = "static words: "
2100
2101    if firstPage and secondPage:
2102        match = SequenceMatcher(None, firstPage, secondPage).find_longest_match(0, len(firstPage), 0, len(secondPage))
2103        commonText = firstPage[match[0]:match[0] + match[2]]
2104        commonWords = getPageWordSet(commonText)
2105    else:
2106        commonWords = None
2107
2108    if commonWords:
2109        commonWords = [_ for _ in commonWords if len(_) >= minLength]
2110        commonWords.sort(key=functools.cmp_to_key(lambda a, b: cmp(a.lower(), b.lower())))
2111
2112        for word in commonWords:
2113            infoMsg += "'%s', " % word
2114
2115        infoMsg = infoMsg.rstrip(", ")
2116    else:
2117        infoMsg += "None"
2118
2119    logger.info(infoMsg)
2120
2121    return commonWords
2122
2123def isWindowsDriveLetterPath(filepath):
2124    """
2125    Returns True if given filepath starts with a Windows drive letter
2126
2127    >>> isWindowsDriveLetterPath('C:\\boot.ini')
2128    True
2129    >>> isWindowsDriveLetterPath('/var/log/apache.log')
2130    False
2131    """
2132
2133    return re.search(r"\A[\w]\:", filepath) is not None
2134
2135def posixToNtSlashes(filepath):
2136    """
2137    Replaces all occurrences of Posix slashes in provided
2138    filepath with NT backslashes
2139
2140    >>> posixToNtSlashes('C:/Windows')
2141    'C:\\\\Windows'
2142    """
2143
2144    return filepath.replace('/', '\\') if filepath else filepath
2145
2146def ntToPosixSlashes(filepath):
2147    """
2148    Replaces all occurrences of NT backslashes in provided
2149    filepath with Posix slashes
2150
2151    >>> ntToPosixSlashes('C:\\Windows')
2152    'C:/Windows'
2153    """
2154
2155    return filepath.replace('\\', '/') if filepath else filepath
2156
2157def isHexEncodedString(subject):
2158    """
2159    Checks if the provided string is hex encoded
2160
2161    >>> isHexEncodedString('DEADBEEF')
2162    True
2163    >>> isHexEncodedString('test')
2164    False
2165    """
2166
2167    return re.match(r"\A[0-9a-fA-Fx]+\Z", subject) is not None
2168
2169def isMultiThreadMode():
2170    """
2171    Checks if running in multi-thread(ing) mode
2172    """
2173
2174    return threading.activeCount() > 1
2175
2176@cachedmethod
2177def getConsoleWidth(default=80):
2178    """
2179    Returns console width
2180    """
2181
2182    width = None
2183
2184    if os.getenv("COLUMNS", "").isdigit():
2185        width = int(os.getenv("COLUMNS"))
2186    else:
2187        try:
2188            output = shellExec("stty size")
2189            match = re.search(r"\A\d+ (\d+)", output)
2190
2191            if match:
2192                width = int(match.group(1))
2193        except (OSError, MemoryError):
2194            pass
2195
2196    if width is None:
2197        try:
2198            import curses
2199
2200            stdscr = curses.initscr()
2201            _, width = stdscr.getmaxyx()
2202            curses.endwin()
2203        except:
2204            pass
2205
2206    return width or default
2207
2208def shellExec(cmd):
2209    """
2210    Executes arbitrary shell command
2211
2212    >>> shellExec('echo 1').strip() == '1'
2213    True
2214    """
2215
2216    retVal = ""
2217
2218    try:
2219        retVal = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT).communicate()[0] or ""
2220    except Exception as ex:
2221        retVal = getSafeExString(ex)
2222    finally:
2223        retVal = getText(retVal)
2224
2225    return retVal
2226
2227def clearConsoleLine(forceOutput=False):
2228    """
2229    Clears current console line
2230    """
2231
2232    if IS_TTY:
2233        dataToStdout("\r%s\r" % (" " * (getConsoleWidth() - 1)), forceOutput)
2234
2235    kb.prependFlag = False
2236
2237def parseXmlFile(xmlFile, handler):
2238    """
2239    Parses XML file by a given handler
2240    """
2241
2242    try:
2243        with contextlib.closing(io.StringIO(readCachedFileContent(xmlFile))) as stream:
2244            parse(stream, handler)
2245    except (SAXParseException, UnicodeError) as ex:
2246        errMsg = "something appears to be wrong with "
2247        errMsg += "the file '%s' ('%s'). Please make " % (xmlFile, getSafeExString(ex))
2248        errMsg += "sure that you haven't made any changes to it"
2249        raise SqlmapInstallationException(errMsg)
2250
2251def getSQLSnippet(dbms, sfile, **variables):
2252    """
2253    Returns content of SQL snippet located inside 'procs/' directory
2254
2255    >>> 'RECONFIGURE' in getSQLSnippet(DBMS.MSSQL, "activate_sp_oacreate")
2256    True
2257    """
2258
2259    if sfile.endswith('.sql') and os.path.exists(sfile):
2260        filename = sfile
2261    elif not sfile.endswith('.sql') and os.path.exists("%s.sql" % sfile):
2262        filename = "%s.sql" % sfile
2263    else:
2264        filename = os.path.join(paths.SQLMAP_PROCS_PATH, DBMS_DIRECTORY_DICT[dbms], sfile if sfile.endswith('.sql') else "%s.sql" % sfile)
2265        checkFile(filename)
2266
2267    retVal = readCachedFileContent(filename)
2268    retVal = re.sub(r"#.+", "", retVal)
2269    retVal = re.sub(r";\s+", "; ", retVal).strip("\r\n")
2270
2271    for _ in variables:
2272        retVal = re.sub(r"%%%s%%" % _, variables[_].replace('\\', r'\\'), retVal)
2273
2274    for _ in re.findall(r"%RANDSTR\d+%", retVal, re.I):
2275        retVal = retVal.replace(_, randomStr())
2276
2277    for _ in re.findall(r"%RANDINT\d+%", retVal, re.I):
2278        retVal = retVal.replace(_, randomInt())
2279
2280    variables = re.findall(r"(?<!\bLIKE ')%(\w+)%", retVal, re.I)
2281
2282    if variables:
2283        errMsg = "unresolved variable%s '%s' in SQL file '%s'" % ("s" if len(variables) > 1 else "", ", ".join(variables), sfile)
2284        logger.error(errMsg)
2285
2286        msg = "do you want to provide the substitution values? [y/N] "
2287
2288        if readInput(msg, default='N', boolean=True):
2289            for var in variables:
2290                msg = "insert value for variable '%s': " % var
2291                val = readInput(msg, default="")
2292                retVal = retVal.replace(r"%%%s%%" % var, val)
2293
2294    return retVal
2295
2296def readCachedFileContent(filename, mode="rb"):
2297    """
2298    Cached reading of file content (avoiding multiple same file reading)
2299
2300    >>> "readCachedFileContent" in readCachedFileContent(__file__)
2301    True
2302    """
2303
2304    if filename not in kb.cache.content:
2305        with kb.locks.cache:
2306            if filename not in kb.cache.content:
2307                checkFile(filename)
2308                try:
2309                    with openFile(filename, mode) as f:
2310                        kb.cache.content[filename] = f.read()
2311                except (IOError, OSError, MemoryError) as ex:
2312                    errMsg = "something went wrong while trying "
2313                    errMsg += "to read the content of file '%s' ('%s')" % (filename, getSafeExString(ex))
2314                    raise SqlmapSystemException(errMsg)
2315
2316    return kb.cache.content[filename]
2317
2318def readXmlFile(xmlFile):
2319    """
2320    Reads XML file content and returns its DOM representation
2321    """
2322
2323    checkFile(xmlFile)
2324    retVal = minidom.parse(xmlFile).documentElement
2325
2326    return retVal
2327
2328def average(values):
2329    """
2330    Computes the arithmetic mean of a list of numbers.
2331
2332    >>> "%.1f" % average([0.9, 0.9, 0.9, 1.0, 0.8, 0.9])
2333    '0.9'
2334    """
2335
2336    return (1.0 * sum(values) / len(values)) if values else None
2337
2338@cachedmethod
2339def stdev(values):
2340    """
2341    Computes standard deviation of a list of numbers.
2342
2343    # Reference: http://www.goldb.org/corestats.html
2344
2345    >>> "%.3f" % stdev([0.9, 0.9, 0.9, 1.0, 0.8, 0.9])
2346    '0.063'
2347    """
2348
2349    if not values or len(values) < 2:
2350        return None
2351    else:
2352        avg = average(values)
2353        _ = 1.0 * sum(pow((_ or 0) - avg, 2) for _ in values)
2354        return sqrt(_ / (len(values) - 1))
2355
2356def calculateDeltaSeconds(start):
2357    """
2358    Returns elapsed time from start till now
2359
2360    >>> calculateDeltaSeconds(0) > 1151721660
2361    True
2362    """
2363
2364    return time.time() - start
2365
2366def initCommonOutputs():
2367    """
2368    Initializes dictionary containing common output values used by "good samaritan" feature
2369
2370    >>> initCommonOutputs(); "information_schema" in kb.commonOutputs["Databases"]
2371    True
2372    """
2373
2374    kb.commonOutputs = {}
2375    key = None
2376
2377    for line in openFile(paths.COMMON_OUTPUTS, 'r'):
2378        if line.find('#') != -1:
2379            line = line[:line.find('#')]
2380
2381        line = line.strip()
2382
2383        if len(line) > 1:
2384            if line.startswith('[') and line.endswith(']'):
2385                key = line[1:-1]
2386            elif key:
2387                if key not in kb.commonOutputs:
2388                    kb.commonOutputs[key] = set()
2389
2390                if line not in kb.commonOutputs[key]:
2391                    kb.commonOutputs[key].add(line)
2392
2393def getFileItems(filename, commentPrefix='#', unicoded=True, lowercase=False, unique=False):
2394    """
2395    Returns newline delimited items contained inside file
2396    """
2397
2398    retVal = list() if not unique else OrderedDict()
2399
2400    if filename:
2401        filename = filename.strip('"\'')
2402
2403    checkFile(filename)
2404
2405    try:
2406        with openFile(filename, 'r', errors="ignore") if unicoded else open(filename, 'r') as f:
2407            for line in f:
2408                if commentPrefix:
2409                    if line.find(commentPrefix) != -1:
2410                        line = line[:line.find(commentPrefix)]
2411
2412                line = line.strip()
2413
2414                if line:
2415                    if lowercase:
2416                        line = line.lower()
2417
2418                    if unique and line in retVal:
2419                        continue
2420
2421                    if unique:
2422                        retVal[line] = True
2423                    else:
2424                        retVal.append(line)
2425    except (IOError, OSError, MemoryError) as ex:
2426        errMsg = "something went wrong while trying "
2427        errMsg += "to read the content of file '%s' ('%s')" % (filename, getSafeExString(ex))
2428        raise SqlmapSystemException(errMsg)
2429
2430    return retVal if not unique else list(retVal.keys())
2431
2432def goGoodSamaritan(prevValue, originalCharset):
2433    """
2434    Function for retrieving parameters needed for common prediction (good
2435    samaritan) feature.
2436
2437    prevValue: retrieved query output so far (e.g. 'i').
2438
2439    Returns commonValue if there is a complete single match (in kb.partRun
2440    of txt/common-outputs.txt under kb.partRun) regarding parameter
2441    prevValue. If there is no single value match, but multiple, commonCharset is
2442    returned containing more probable characters (retrieved from matched
2443    values in txt/common-outputs.txt) together with the rest of charset as
2444    otherCharset.
2445    """
2446
2447    if kb.commonOutputs is None:
2448        initCommonOutputs()
2449
2450    predictionSet = set()
2451    commonValue = None
2452    commonPattern = None
2453    countCommonValue = 0
2454
2455    # If the header (e.g. Databases) we are looking for has common
2456    # outputs defined
2457    if kb.partRun in kb.commonOutputs:
2458        commonPartOutputs = kb.commonOutputs[kb.partRun]
2459        commonPattern = commonFinderOnly(prevValue, commonPartOutputs)
2460
2461        # If the longest common prefix is the same as previous value then
2462        # do not consider it
2463        if commonPattern and commonPattern == prevValue:
2464            commonPattern = None
2465
2466        # For each common output
2467        for item in commonPartOutputs:
2468            # Check if the common output (item) starts with prevValue
2469            # where prevValue is the enumerated character(s) so far
2470            if item.startswith(prevValue):
2471                commonValue = item
2472                countCommonValue += 1
2473
2474                if len(item) > len(prevValue):
2475                    char = item[len(prevValue)]
2476                    predictionSet.add(char)
2477
2478        # Reset single value if there is more than one possible common
2479        # output
2480        if countCommonValue > 1:
2481            commonValue = None
2482
2483        commonCharset = []
2484        otherCharset = []
2485
2486        # Split the original charset into common chars (commonCharset)
2487        # and other chars (otherCharset)
2488        for ordChar in originalCharset:
2489            if _unichr(ordChar) not in predictionSet:
2490                otherCharset.append(ordChar)
2491            else:
2492                commonCharset.append(ordChar)
2493
2494        commonCharset.sort()
2495
2496        return commonValue, commonPattern, commonCharset, originalCharset
2497    else:
2498        return None, None, None, originalCharset
2499
2500def getPartRun(alias=True):
2501    """
2502    Goes through call stack and finds constructs matching conf.dbmsHandler.*.
2503    Returns it or its alias used in 'txt/common-outputs.txt'
2504    """
2505
2506    retVal = None
2507    commonPartsDict = optDict["Enumeration"]
2508
2509    try:
2510        stack = [item[4][0] if isinstance(item[4], list) else '' for item in inspect.stack()]
2511
2512        # Goes backwards through the stack to find the conf.dbmsHandler method
2513        # calling this function
2514        for i in xrange(0, len(stack) - 1):
2515            for regex in (r"self\.(get[^(]+)\(\)", r"conf\.dbmsHandler\.([^(]+)\(\)"):
2516                match = re.search(regex, stack[i])
2517
2518                if match:
2519                    # This is the calling conf.dbmsHandler or self method
2520                    # (e.g. 'getDbms')
2521                    retVal = match.groups()[0]
2522                    break
2523
2524            if retVal is not None:
2525                break
2526
2527    # Reference: http://coding.derkeiler.com/Archive/Python/comp.lang.python/2004-06/2267.html
2528    except TypeError:
2529        pass
2530
2531    # Return the INI tag to consider for common outputs (e.g. 'Databases')
2532    if alias:
2533        return commonPartsDict[retVal][1] if isinstance(commonPartsDict.get(retVal), tuple) else retVal
2534    else:
2535        return retVal
2536
2537def longestCommonPrefix(*sequences):
2538    """
2539    Returns longest common prefix occuring in given sequences
2540
2541    # Reference: http://boredzo.org/blog/archives/2007-01-06/longest-common-prefix-in-python-2
2542
2543    >>> longestCommonPrefix('foobar', 'fobar')
2544    'fo'
2545    """
2546
2547    if len(sequences) == 1:
2548        return sequences[0]
2549
2550    sequences = [pair[1] for pair in sorted((len(fi), fi) for fi in sequences)]
2551
2552    if not sequences:
2553        return None
2554
2555    for i, comparison_ch in enumerate(sequences[0]):
2556        for fi in sequences[1:]:
2557            ch = fi[i]
2558
2559            if ch != comparison_ch:
2560                return fi[:i]
2561
2562    return sequences[0]
2563
2564def commonFinderOnly(initial, sequence):
2565    """
2566    Returns parts of sequence which start with the given initial string
2567
2568    >>> commonFinderOnly("abcd", ["abcdefg", "foobar", "abcde"])
2569    'abcde'
2570    """
2571
2572    return longestCommonPrefix(*[_ for _ in sequence if _.startswith(initial)])
2573
2574def pushValue(value):
2575    """
2576    Push value to the stack (thread dependent)
2577    """
2578
2579    exception = None
2580    success = False
2581
2582    for i in xrange(PUSH_VALUE_EXCEPTION_RETRY_COUNT):
2583        try:
2584            getCurrentThreadData().valueStack.append(copy.deepcopy(value))
2585            success = True
2586            break
2587        except Exception as ex:
2588            exception = ex
2589
2590    if not success:
2591        getCurrentThreadData().valueStack.append(None)
2592
2593        if exception:
2594            raise exception
2595
2596def popValue():
2597    """
2598    Pop value from the stack (thread dependent)
2599
2600    >>> pushValue('foobar')
2601    >>> popValue()
2602    'foobar'
2603    """
2604
2605    return getCurrentThreadData().valueStack.pop()
2606
2607def wasLastResponseDBMSError():
2608    """
2609    Returns True if the last web request resulted in a (recognized) DBMS error page
2610    """
2611
2612    threadData = getCurrentThreadData()
2613    return threadData.lastErrorPage and threadData.lastErrorPage[0] == threadData.lastRequestUID
2614
2615def wasLastResponseHTTPError():
2616    """
2617    Returns True if the last web request resulted in an erroneous HTTP code (like 500)
2618    """
2619
2620    threadData = getCurrentThreadData()
2621    return threadData.lastHTTPError and threadData.lastHTTPError[0] == threadData.lastRequestUID
2622
2623def wasLastResponseDelayed():
2624    """
2625    Returns True if the last web request resulted in a time-delay
2626    """
2627
2628    # 99.9999999997440% of all non time-based SQL injection affected
2629    # response times should be inside +-7*stdev([normal response times])
2630    # Math reference: http://www.answers.com/topic/standard-deviation
2631
2632    deviation = stdev(kb.responseTimes.get(kb.responseTimeMode, []))
2633    threadData = getCurrentThreadData()
2634
2635    if deviation and not conf.direct and not conf.disableStats:
2636        if len(kb.responseTimes[kb.responseTimeMode]) < MIN_TIME_RESPONSES:
2637            warnMsg = "time-based standard deviation method used on a model "
2638            warnMsg += "with less than %d response times" % MIN_TIME_RESPONSES
2639            logger.warn(warnMsg)
2640
2641        lowerStdLimit = average(kb.responseTimes[kb.responseTimeMode]) + TIME_STDEV_COEFF * deviation
2642        retVal = (threadData.lastQueryDuration >= max(MIN_VALID_DELAYED_RESPONSE, lowerStdLimit))
2643
2644        if not kb.testMode and retVal:
2645            if kb.adjustTimeDelay is None:
2646                msg = "do you want sqlmap to try to optimize value(s) "
2647                msg += "for DBMS delay responses (option '--time-sec')? [Y/n] "
2648
2649                kb.adjustTimeDelay = ADJUST_TIME_DELAY.DISABLE if not readInput(msg, default='Y', boolean=True) else ADJUST_TIME_DELAY.YES
2650            if kb.adjustTimeDelay is ADJUST_TIME_DELAY.YES:
2651                adjustTimeDelay(threadData.lastQueryDuration, lowerStdLimit)
2652
2653        return retVal
2654    else:
2655        delta = threadData.lastQueryDuration - conf.timeSec
2656        if Backend.getIdentifiedDbms() in (DBMS.MYSQL,):  # MySQL's SLEEP(X) lasts 0.05 seconds shorter on average
2657            delta += 0.05
2658        return delta >= 0
2659
2660def adjustTimeDelay(lastQueryDuration, lowerStdLimit):
2661    """
2662    Provides tip for adjusting time delay in time-based data retrieval
2663    """
2664
2665    candidate = (1 if not isHeavyQueryBased() else 2) + int(round(lowerStdLimit))
2666
2667    kb.delayCandidates = [candidate] + kb.delayCandidates[:-1]
2668
2669    if all((_ == candidate for _ in kb.delayCandidates)) and candidate < conf.timeSec:
2670        if lastQueryDuration / (1.0 * conf.timeSec / candidate) > MIN_VALID_DELAYED_RESPONSE:  # Note: to prevent problems with fast responses for heavy-queries like RANDOMBLOB
2671            conf.timeSec = candidate
2672
2673            infoMsg = "adjusting time delay to "
2674            infoMsg += "%d second%s due to good response times" % (conf.timeSec, 's' if conf.timeSec > 1 else '')
2675            logger.info(infoMsg)
2676
2677def getLastRequestHTTPError():
2678    """
2679    Returns last HTTP error code
2680    """
2681
2682    threadData = getCurrentThreadData()
2683    return threadData.lastHTTPError[1] if threadData.lastHTTPError else None
2684
2685def extractErrorMessage(page):
2686    """
2687    Returns reported error message from page if it founds one
2688
2689    >>> getText(extractErrorMessage(u'<html><title>Test</title>\\n<b>Warning</b>: oci_parse() [function.oci-parse]: ORA-01756: quoted string not properly terminated<br><p>Only a test page</p></html>') )
2690    'oci_parse() [function.oci-parse]: ORA-01756: quoted string not properly terminated'
2691    >>> extractErrorMessage('Warning: This is only a dummy foobar test') is None
2692    True
2693    """
2694
2695    retVal = None
2696
2697    if isinstance(page, six.string_types):
2698        if wasLastResponseDBMSError():
2699            page = re.sub(r"<[^>]+>", "", page)
2700
2701        for regex in ERROR_PARSING_REGEXES:
2702            match = re.search(regex, page, re.IGNORECASE)
2703
2704            if match:
2705                candidate = htmlUnescape(match.group("result")).replace("<br>", "\n").strip()
2706                if candidate and (1.0 * len(re.findall(r"[^A-Za-z,. ]", candidate)) / len(candidate) > MIN_ERROR_PARSING_NON_WRITING_RATIO):
2707                    retVal = candidate
2708                    break
2709
2710    return retVal
2711
2712def findLocalPort(ports):
2713    """
2714    Find the first opened localhost port from a given list of ports (e.g. for Tor port checks)
2715    """
2716
2717    retVal = None
2718
2719    for port in ports:
2720        try:
2721            try:
2722                s = socket._orig_socket(socket.AF_INET, socket.SOCK_STREAM)
2723            except AttributeError:
2724                s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
2725            s.connect((LOCALHOST, port))
2726            retVal = port
2727            break
2728        except socket.error:
2729            pass
2730        finally:
2731            try:
2732                s.close()
2733            except socket.error:
2734                pass
2735
2736    return retVal
2737
2738def findMultipartPostBoundary(post):
2739    """
2740    Finds value for a boundary parameter in given multipart POST body
2741
2742    >>> findMultipartPostBoundary("-----------------------------9051914041544843365972754266\\nContent-Disposition: form-data; name=text\\n\\ndefault")
2743    '9051914041544843365972754266'
2744    """
2745
2746    retVal = None
2747
2748    done = set()
2749    candidates = []
2750
2751    for match in re.finditer(r"(?m)^--(.+?)(--)?$", post or ""):
2752        _ = match.group(1).strip().strip('-')
2753
2754        if _ in done:
2755            continue
2756        else:
2757            candidates.append((post.count(_), _))
2758            done.add(_)
2759
2760    if candidates:
2761        candidates.sort(key=lambda _: _[0], reverse=True)
2762        retVal = candidates[0][1]
2763
2764    return retVal
2765
2766def urldecode(value, encoding=None, unsafe="%%?&=;+%s" % CUSTOM_INJECTION_MARK_CHAR, convall=False, spaceplus=True):
2767    """
2768    URL decodes given value
2769
2770    >>> urldecode('AND%201%3E%282%2B3%29%23', convall=True) == 'AND 1>(2+3)#'
2771    True
2772    >>> urldecode('AND%201%3E%282%2B3%29%23', convall=False) == 'AND 1>(2%2B3)#'
2773    True
2774    """
2775
2776    result = value
2777
2778    if value:
2779        try:
2780            # for cases like T%C3%BCrk%C3%A7e
2781            value = str(value)
2782        except ValueError:
2783            pass
2784        finally:
2785            if convall:
2786                result = _urllib.parse.unquote_plus(value) if spaceplus else _urllib.parse.unquote(value)
2787            else:
2788                result = value
2789                charset = set(string.printable) - set(unsafe)
2790
2791                def _(match):
2792                    char = decodeHex(match.group(1), binary=False)
2793                    return char if char in charset else match.group(0)
2794
2795                if spaceplus:
2796                    result = result.replace('+', ' ')  # plus sign has a special meaning in URL encoded data (hence the usage of _urllib.parse.unquote_plus in convall case)
2797
2798                result = re.sub(r"%([0-9a-fA-F]{2})", _, result)
2799
2800            result = getUnicode(result, encoding or UNICODE_ENCODING)
2801
2802    return result
2803
2804def urlencode(value, safe="%&=-_", convall=False, limit=False, spaceplus=False):
2805    """
2806    URL encodes given value
2807
2808    >>> urlencode('AND 1>(2+3)#')
2809    'AND%201%3E%282%2B3%29%23'
2810    """
2811
2812    if conf.get("direct"):
2813        return value
2814
2815    count = 0
2816    result = None if value is None else ""
2817
2818    if value:
2819        if Backend.isDbms(DBMS.MSSQL) and not kb.tamperFunctions and any(ord(_) > 255 for _ in value):
2820            warnMsg = "if you experience problems with "
2821            warnMsg += "non-ASCII identifier names "
2822            warnMsg += "you are advised to rerun with '--tamper=charunicodeencode'"
2823            singleTimeWarnMessage(warnMsg)
2824
2825        if convall or safe is None:
2826            safe = ""
2827
2828        # corner case when character % really needs to be
2829        # encoded (when not representing URL encoded char)
2830        # except in cases when tampering scripts are used
2831        if all('%' in _ for _ in (safe, value)) and not kb.tamperFunctions:
2832            value = re.sub(r"%(?![0-9a-fA-F]{2})", "%25", value)
2833
2834        while True:
2835            result = _urllib.parse.quote(getBytes(value), safe)
2836
2837            if limit and len(result) > URLENCODE_CHAR_LIMIT:
2838                if count >= len(URLENCODE_FAILSAFE_CHARS):
2839                    break
2840
2841                while count < len(URLENCODE_FAILSAFE_CHARS):
2842                    safe += URLENCODE_FAILSAFE_CHARS[count]
2843                    count += 1
2844                    if safe[-1] in value:
2845                        break
2846            else:
2847                break
2848
2849        if spaceplus:
2850            result = result.replace(_urllib.parse.quote(' '), '+')
2851
2852    return result
2853
2854def runningAsAdmin():
2855    """
2856    Returns True if the current process is run under admin privileges
2857    """
2858
2859    isAdmin = None
2860
2861    if PLATFORM in ("posix", "mac"):
2862        _ = os.geteuid()
2863
2864        isAdmin = isinstance(_, (float, six.integer_types)) and _ == 0
2865    elif IS_WIN:
2866        import ctypes
2867
2868        _ = ctypes.windll.shell32.IsUserAnAdmin()
2869
2870        isAdmin = isinstance(_, (float, six.integer_types)) and _ == 1
2871    else:
2872        errMsg = "sqlmap is not able to check if you are running it "
2873        errMsg += "as an administrator account on this platform. "
2874        errMsg += "sqlmap will assume that you are an administrator "
2875        errMsg += "which is mandatory for the requested takeover attack "
2876        errMsg += "to work properly"
2877        logger.error(errMsg)
2878
2879        isAdmin = True
2880
2881    return isAdmin
2882
2883def logHTTPTraffic(requestLogMsg, responseLogMsg, startTime=None, endTime=None):
2884    """
2885    Logs HTTP traffic to the output file
2886    """
2887
2888    if conf.harFile:
2889        conf.httpCollector.collectRequest(requestLogMsg, responseLogMsg, startTime, endTime)
2890
2891    if conf.trafficFile:
2892        with kb.locks.log:
2893            dataToTrafficFile("%s%s" % (requestLogMsg, os.linesep))
2894            dataToTrafficFile("%s%s" % (responseLogMsg, os.linesep))
2895            dataToTrafficFile("%s%s%s%s" % (os.linesep, 76 * '#', os.linesep, os.linesep))
2896
2897def getPageTemplate(payload, place):  # Cross-referenced function
2898    raise NotImplementedError
2899
2900@cachedmethod
2901def getPublicTypeMembers(type_, onlyValues=False):
2902    """
2903    Useful for getting members from types (e.g. in enums)
2904
2905    >>> [_ for _ in getPublicTypeMembers(OS, True)]
2906    ['Linux', 'Windows']
2907    """
2908
2909    retVal = []
2910
2911    for name, value in inspect.getmembers(type_):
2912        if not name.startswith("__"):
2913            if not onlyValues:
2914                retVal.append((name, value))
2915            else:
2916                retVal.append(value)
2917
2918    return retVal
2919
2920def enumValueToNameLookup(type_, value_):
2921    """
2922    Returns name of a enum member with a given value
2923
2924    >>> enumValueToNameLookup(SORT_ORDER, 100)
2925    'LAST'
2926    """
2927
2928    retVal = None
2929
2930    for name, value in getPublicTypeMembers(type_):
2931        if value == value_:
2932            retVal = name
2933            break
2934
2935    return retVal
2936
2937@cachedmethod
2938def extractRegexResult(regex, content, flags=0):
2939    """
2940    Returns 'result' group value from a possible match with regex on a given
2941    content
2942
2943    >>> extractRegexResult(r'a(?P<result>[^g]+)g', 'abcdefg')
2944    'bcdef'
2945    """
2946
2947    retVal = None
2948
2949    if regex and content and "?P<result>" in regex:
2950        if isinstance(content, six.binary_type) and isinstance(regex, six.text_type):
2951            regex = getBytes(regex)
2952
2953        match = re.search(regex, content, flags)
2954
2955        if match:
2956            retVal = match.group("result")
2957
2958    return retVal
2959
2960def extractTextTagContent(page):
2961    """
2962    Returns list containing content from "textual" tags
2963
2964    >>> extractTextTagContent('<html><head><title>Title</title></head><body><pre>foobar</pre><a href="#link">Link</a></body></html>')
2965    ['Title', 'foobar']
2966    """
2967
2968    page = page or ""
2969
2970    if REFLECTED_VALUE_MARKER in page:
2971        try:
2972            page = re.sub(r"(?i)[^\s>]*%s[^\s<]*" % REFLECTED_VALUE_MARKER, "", page)
2973        except MemoryError:
2974            page = page.replace(REFLECTED_VALUE_MARKER, "")
2975
2976    return filterNone(_.group("result").strip() for _ in re.finditer(TEXT_TAG_REGEX, page))
2977
2978def trimAlphaNum(value):
2979    """
2980    Trims alpha numeric characters from start and ending of a given value
2981
2982    >>> trimAlphaNum('AND 1>(2+3)-- foobar')
2983    ' 1>(2+3)-- '
2984    """
2985
2986    while value and value[-1].isalnum():
2987        value = value[:-1]
2988
2989    while value and value[0].isalnum():
2990        value = value[1:]
2991
2992    return value
2993
2994def isNumPosStrValue(value):
2995    """
2996    Returns True if value is a string (or integer) with a positive integer representation
2997
2998    >>> isNumPosStrValue(1)
2999    True
3000    >>> isNumPosStrValue('1')
3001    True
3002    >>> isNumPosStrValue(0)
3003    False
3004    >>> isNumPosStrValue('-2')
3005    False
3006    """
3007
3008    return (hasattr(value, "isdigit") and value.isdigit() and int(value) > 0) or (isinstance(value, int) and value > 0)
3009
3010@cachedmethod
3011def aliasToDbmsEnum(dbms):
3012    """
3013    Returns major DBMS name from a given alias
3014
3015    >>> aliasToDbmsEnum('mssql')
3016    'Microsoft SQL Server'
3017    """
3018
3019    retVal = None
3020
3021    if dbms:
3022        for key, item in DBMS_DICT.items():
3023            if dbms.lower() in item[0] or dbms.lower() == key.lower():
3024                retVal = key
3025                break
3026
3027    return retVal
3028
3029def findDynamicContent(firstPage, secondPage):
3030    """
3031    This function checks if the provided pages have dynamic content. If they
3032    are dynamic, proper markings will be made
3033
3034    >>> findDynamicContent("Lorem ipsum dolor sit amet, congue tation referrentur ei sed. Ne nec legimus habemus recusabo, natum reque et per. Facer tritani reprehendunt eos id, modus constituam est te. Usu sumo indoctum ad, pri paulo molestiae complectitur no.", "Lorem ipsum dolor sit amet, congue tation referrentur ei sed. Ne nec legimus habemus recusabo, natum reque et per. <script src='ads.js'></script>Facer tritani reprehendunt eos id, modus constituam est te. Usu sumo indoctum ad, pri paulo molestiae complectitur no.")
3035    >>> kb.dynamicMarkings
3036    [('natum reque et per. ', 'Facer tritani repreh')]
3037    """
3038
3039    if not firstPage or not secondPage:
3040        return
3041
3042    infoMsg = "searching for dynamic content"
3043    singleTimeLogMessage(infoMsg)
3044
3045    blocks = list(SequenceMatcher(None, firstPage, secondPage).get_matching_blocks())
3046    kb.dynamicMarkings = []
3047
3048    # Removing too small matching blocks
3049    for block in blocks[:]:
3050        (_, _, length) = block
3051
3052        if length <= 2 * DYNAMICITY_BOUNDARY_LENGTH:
3053            blocks.remove(block)
3054
3055    # Making of dynamic markings based on prefix/suffix principle
3056    if len(blocks) > 0:
3057        blocks.insert(0, None)
3058        blocks.append(None)
3059
3060        for i in xrange(len(blocks) - 1):
3061            prefix = firstPage[blocks[i][0]:blocks[i][0] + blocks[i][2]] if blocks[i] else None
3062            suffix = firstPage[blocks[i + 1][0]:blocks[i + 1][0] + blocks[i + 1][2]] if blocks[i + 1] else None
3063
3064            if prefix is None and blocks[i + 1][0] == 0:
3065                continue
3066
3067            if suffix is None and (blocks[i][0] + blocks[i][2] >= len(firstPage)):
3068                continue
3069
3070            if prefix and suffix:
3071                prefix = prefix[-DYNAMICITY_BOUNDARY_LENGTH:]
3072                suffix = suffix[:DYNAMICITY_BOUNDARY_LENGTH]
3073
3074                for _ in (firstPage, secondPage):
3075                    match = re.search(r"(?s)%s(.+)%s" % (re.escape(prefix), re.escape(suffix)), _)
3076                    if match:
3077                        infix = match.group(1)
3078                        if infix[0].isalnum():
3079                            prefix = trimAlphaNum(prefix)
3080                        if infix[-1].isalnum():
3081                            suffix = trimAlphaNum(suffix)
3082                        break
3083
3084            kb.dynamicMarkings.append((prefix if prefix else None, suffix if suffix else None))
3085
3086    if len(kb.dynamicMarkings) > 0:
3087        infoMsg = "dynamic content marked for removal (%d region%s)" % (len(kb.dynamicMarkings), 's' if len(kb.dynamicMarkings) > 1 else '')
3088        singleTimeLogMessage(infoMsg)
3089
3090def removeDynamicContent(page):
3091    """
3092    Removing dynamic content from supplied page basing removal on
3093    precalculated dynamic markings
3094    """
3095
3096    if page:
3097        for item in kb.dynamicMarkings:
3098            prefix, suffix = item
3099
3100            if prefix is None and suffix is None:
3101                continue
3102            elif prefix is None:
3103                page = re.sub(r"(?s)^.+%s" % re.escape(suffix), suffix.replace('\\', r'\\'), page)
3104            elif suffix is None:
3105                page = re.sub(r"(?s)%s.+$" % re.escape(prefix), prefix.replace('\\', r'\\'), page)
3106            else:
3107                page = re.sub(r"(?s)%s.+%s" % (re.escape(prefix), re.escape(suffix)), "%s%s" % (prefix.replace('\\', r'\\'), suffix.replace('\\', r'\\')), page)
3108
3109    return page
3110
3111def filterStringValue(value, charRegex, replacement=""):
3112    """
3113    Returns string value consisting only of chars satisfying supplied
3114    regular expression (note: it has to be in form [...])
3115
3116    >>> filterStringValue('wzydeadbeef0123#', r'[0-9a-f]')
3117    'deadbeef0123'
3118    """
3119
3120    retVal = value
3121
3122    if value:
3123        retVal = re.sub(charRegex.replace("[", "[^") if "[^" not in charRegex else charRegex.replace("[^", "["), replacement, value)
3124
3125    return retVal
3126
3127def filterControlChars(value, replacement=' '):
3128    """
3129    Returns string value with control chars being supstituted with replacement character
3130
3131    >>> filterControlChars('AND 1>(2+3)\\n--')
3132    'AND 1>(2+3) --'
3133    """
3134
3135    return filterStringValue(value, PRINTABLE_CHAR_REGEX, replacement)
3136
3137def filterNone(values):
3138    """
3139    Emulates filterNone([...]) functionality
3140
3141    >>> filterNone([1, 2, "", None, 3])
3142    [1, 2, 3]
3143    """
3144
3145    retVal = values
3146
3147    if isinstance(values, collections.Iterable):
3148        retVal = [_ for _ in values if _]
3149
3150    return retVal
3151
3152def isDBMSVersionAtLeast(minimum):
3153    """
3154    Checks if the recognized DBMS version is at least the version specified
3155
3156    >>> pushValue(kb.dbmsVersion)
3157    >>> kb.dbmsVersion = "2"
3158    >>> isDBMSVersionAtLeast("1.3.4.1.4")
3159    True
3160    >>> isDBMSVersionAtLeast(2.1)
3161    False
3162    >>> isDBMSVersionAtLeast(">2")
3163    False
3164    >>> isDBMSVersionAtLeast(">=2.0")
3165    True
3166    >>> kb.dbmsVersion = "<2"
3167    >>> isDBMSVersionAtLeast("2")
3168    False
3169    >>> isDBMSVersionAtLeast("1.5")
3170    True
3171    >>> kb.dbmsVersion = "MySQL 5.4.3-log4"
3172    >>> isDBMSVersionAtLeast("5")
3173    True
3174    >>> kb.dbmsVersion = popValue()
3175    """
3176
3177    retVal = None
3178
3179    if not any(isNoneValue(_) for _ in (Backend.getVersion(), minimum)) and Backend.getVersion() != UNKNOWN_DBMS_VERSION:
3180        version = Backend.getVersion().replace(" ", "").rstrip('.')
3181
3182        correction = 0.0
3183        if ">=" in version:
3184            pass
3185        elif '>' in version:
3186            correction = VERSION_COMPARISON_CORRECTION
3187        elif '<' in version:
3188            correction = -VERSION_COMPARISON_CORRECTION
3189
3190        version = extractRegexResult(r"(?P<result>[0-9][0-9.]*)", version)
3191
3192        if version:
3193            if '.' in version:
3194                parts = version.split('.', 1)
3195                parts[1] = filterStringValue(parts[1], '[0-9]')
3196                version = '.'.join(parts)
3197
3198            try:
3199                version = float(filterStringValue(version, '[0-9.]')) + correction
3200            except ValueError:
3201                return None
3202
3203            if isinstance(minimum, six.string_types):
3204                if '.' in minimum:
3205                    parts = minimum.split('.', 1)
3206                    parts[1] = filterStringValue(parts[1], '[0-9]')
3207                    minimum = '.'.join(parts)
3208
3209                correction = 0.0
3210                if minimum.startswith(">="):
3211                    pass
3212                elif minimum.startswith(">"):
3213                    correction = VERSION_COMPARISON_CORRECTION
3214
3215                minimum = float(filterStringValue(minimum, '[0-9.]')) + correction
3216
3217            retVal = version >= minimum
3218
3219    return retVal
3220
3221def parseSqliteTableSchema(value):
3222    """
3223    Parses table column names and types from specified SQLite table schema
3224
3225    >>> kb.data.cachedColumns = {}
3226    >>> parseSqliteTableSchema("CREATE TABLE users\\n\\t\\tid INTEGER\\n\\t\\tname TEXT\\n);")
3227    True
3228    >>> repr(kb.data.cachedColumns).count(',') == 1
3229    True
3230    """
3231
3232    retVal = False
3233
3234    if value:
3235        table = {}
3236        columns = {}
3237
3238        for match in re.finditer(r"(\w+)[\"'`]?\s+(INT|INTEGER|TINYINT|SMALLINT|MEDIUMINT|BIGINT|UNSIGNED BIG INT|INT2|INT8|INTEGER|CHARACTER|VARCHAR|VARYING CHARACTER|NCHAR|NATIVE CHARACTER|NVARCHAR|TEXT|CLOB|LONGTEXT|BLOB|NONE|REAL|DOUBLE|DOUBLE PRECISION|FLOAT|REAL|NUMERIC|DECIMAL|BOOLEAN|DATE|DATETIME|NUMERIC)\b", decodeStringEscape(value), re.I):
3239            retVal = True
3240            columns[match.group(1)] = match.group(2)
3241
3242        table[safeSQLIdentificatorNaming(conf.tbl, True)] = columns
3243        kb.data.cachedColumns[conf.db] = table
3244
3245    return retVal
3246
3247def getTechniqueData(technique=None):
3248    """
3249    Returns injection data for technique specified
3250    """
3251
3252    return kb.injection.data.get(technique if technique is not None else getTechnique())
3253
3254def isTechniqueAvailable(technique):
3255    """
3256    Returns True if there is injection data which sqlmap could use for technique specified
3257
3258    >>> pushValue(kb.injection.data)
3259    >>> kb.injection.data[PAYLOAD.TECHNIQUE.ERROR] = [test for test in getSortedInjectionTests() if "error" in test["title"].lower()][0]
3260    >>> isTechniqueAvailable(PAYLOAD.TECHNIQUE.ERROR)
3261    True
3262    >>> kb.injection.data = popValue()
3263    """
3264
3265    if conf.technique and isinstance(conf.technique, list) and technique not in conf.technique:
3266        return False
3267    else:
3268        return getTechniqueData(technique) is not None
3269
3270def isHeavyQueryBased(technique=None):
3271    """
3272    Returns True whether current (kb.)technique is heavy-query based
3273
3274    >>> pushValue(kb.injection.data)
3275    >>> setTechnique(PAYLOAD.TECHNIQUE.STACKED)
3276    >>> kb.injection.data[getTechnique()] = [test for test in getSortedInjectionTests() if "heavy" in test["title"].lower()][0]
3277    >>> isHeavyQueryBased()
3278    True
3279    >>> kb.injection.data = popValue()
3280    """
3281
3282    retVal = False
3283
3284    technique = technique or getTechnique()
3285
3286    if isTechniqueAvailable(technique):
3287        data = getTechniqueData(technique)
3288        if data and "heavy query" in data["title"].lower():
3289            retVal = True
3290
3291    return retVal
3292
3293def isStackingAvailable():
3294    """
3295    Returns True whether techniques using stacking are available
3296
3297    >>> pushValue(kb.injection.data)
3298    >>> kb.injection.data[PAYLOAD.TECHNIQUE.STACKED] = [test for test in getSortedInjectionTests() if "stacked" in test["title"].lower()][0]
3299    >>> isStackingAvailable()
3300    True
3301    >>> kb.injection.data = popValue()
3302    """
3303
3304    retVal = False
3305
3306    if PAYLOAD.TECHNIQUE.STACKED in kb.injection.data:
3307        retVal = True
3308    else:
3309        for technique in getPublicTypeMembers(PAYLOAD.TECHNIQUE, True):
3310            data = getTechniqueData(technique)
3311            if data and "stacked" in data["title"].lower():
3312                retVal = True
3313                break
3314
3315    return retVal
3316
3317def isInferenceAvailable():
3318    """
3319    Returns True whether techniques using inference technique are available
3320
3321    >>> pushValue(kb.injection.data)
3322    >>> kb.injection.data[PAYLOAD.TECHNIQUE.BOOLEAN] = getSortedInjectionTests()[0]
3323    >>> isInferenceAvailable()
3324    True
3325    >>> kb.injection.data = popValue()
3326    """
3327
3328    return any(isTechniqueAvailable(_) for _ in (PAYLOAD.TECHNIQUE.BOOLEAN, PAYLOAD.TECHNIQUE.STACKED, PAYLOAD.TECHNIQUE.TIME))
3329
3330def setOptimize():
3331    """
3332    Sets options turned on by switch '-o'
3333    """
3334
3335    # conf.predictOutput = True
3336    conf.keepAlive = True
3337    conf.threads = 3 if conf.threads < 3 else conf.threads
3338    conf.nullConnection = not any((conf.data, conf.textOnly, conf.titles, conf.string, conf.notString, conf.regexp, conf.tor))
3339
3340    if not conf.nullConnection:
3341        debugMsg = "turning off switch '--null-connection' used indirectly by switch '-o'"
3342        logger.debug(debugMsg)
3343
3344def saveConfig(conf, filename):
3345    """
3346    Saves conf to configuration filename
3347    """
3348
3349    config = UnicodeRawConfigParser()
3350    userOpts = {}
3351
3352    for family in optDict:
3353        userOpts[family] = []
3354
3355    for option, value in conf.items():
3356        for family, optionData in optDict.items():
3357            if option in optionData:
3358                userOpts[family].append((option, value, optionData[option]))
3359
3360    for family, optionData in userOpts.items():
3361        config.add_section(family)
3362
3363        optionData.sort()
3364
3365        for option, value, datatype in optionData:
3366            if datatype and isListLike(datatype):
3367                datatype = datatype[0]
3368
3369            if option in IGNORE_SAVE_OPTIONS:
3370                continue
3371
3372            if value is None:
3373                if datatype == OPTION_TYPE.BOOLEAN:
3374                    value = "False"
3375                elif datatype in (OPTION_TYPE.INTEGER, OPTION_TYPE.FLOAT):
3376                    if option in defaults:
3377                        value = str(defaults[option])
3378                    else:
3379                        value = '0'
3380                elif datatype == OPTION_TYPE.STRING:
3381                    value = ""
3382
3383            if isinstance(value, six.string_types):
3384                value = value.replace("\n", "\n ")
3385
3386            config.set(family, option, value)
3387
3388    with openFile(filename, "wb") as f:
3389        try:
3390            config.write(f)
3391        except IOError as ex:
3392            errMsg = "something went wrong while trying "
3393            errMsg += "to write to the configuration file '%s' ('%s')" % (filename, getSafeExString(ex))
3394            raise SqlmapSystemException(errMsg)
3395
3396def initTechnique(technique=None):
3397    """
3398    Prepares data for technique specified
3399    """
3400
3401    try:
3402        data = getTechniqueData(technique)
3403        resetCounter(technique)
3404
3405        if data:
3406            kb.pageTemplate, kb.errorIsNone = getPageTemplate(data.templatePayload, kb.injection.place)
3407            kb.matchRatio = data.matchRatio
3408            kb.negativeLogic = (technique == PAYLOAD.TECHNIQUE.BOOLEAN) and (data.where == PAYLOAD.WHERE.NEGATIVE)
3409
3410            # Restoring stored conf options
3411            for key, value in kb.injection.conf.items():
3412                if value and (not hasattr(conf, key) or (hasattr(conf, key) and not getattr(conf, key))):
3413                    setattr(conf, key, value)
3414                    debugMsg = "resuming configuration option '%s' (%s)" % (key, ("'%s'" % value) if isinstance(value, six.string_types) else value)
3415                    logger.debug(debugMsg)
3416
3417                    if value and key == "optimize":
3418                        setOptimize()
3419        else:
3420            warnMsg = "there is no injection data available for technique "
3421            warnMsg += "'%s'" % enumValueToNameLookup(PAYLOAD.TECHNIQUE, technique)
3422            logger.warn(warnMsg)
3423
3424    except SqlmapDataException:
3425        errMsg = "missing data in old session file(s). "
3426        errMsg += "Please use '--flush-session' to deal "
3427        errMsg += "with this error"
3428        raise SqlmapNoneDataException(errMsg)
3429
3430def arrayizeValue(value):
3431    """
3432    Makes a list out of value if it is not already a list or tuple itself
3433
3434    >>> arrayizeValue('1')
3435    ['1']
3436    """
3437
3438    if isinstance(value, collections.KeysView):
3439        value = [_ for _ in value]
3440    elif not isListLike(value):
3441        value = [value]
3442
3443    return value
3444
3445def unArrayizeValue(value):
3446    """
3447    Makes a value out of iterable if it is a list or tuple itself
3448
3449    >>> unArrayizeValue(['1'])
3450    '1'
3451    >>> unArrayizeValue(['1', '2'])
3452    '1'
3453    >>> unArrayizeValue([['a', 'b'], 'c'])
3454    'a'
3455    >>> unArrayizeValue(_ for _ in xrange(10))
3456    0
3457    """
3458
3459    if isListLike(value):
3460        if not value:
3461            value = None
3462        elif len(value) == 1 and not isListLike(value[0]):
3463            value = value[0]
3464        else:
3465            value = [_ for _ in flattenValue(value) if _ is not None]
3466            value = value[0] if len(value) > 0 else None
3467    elif inspect.isgenerator(value):
3468        value = unArrayizeValue([_ for _ in value])
3469
3470    return value
3471
3472def flattenValue(value):
3473    """
3474    Returns an iterator representing flat representation of a given value
3475
3476    >>> [_ for _ in flattenValue([['1'], [['2'], '3']])]
3477    ['1', '2', '3']
3478    """
3479
3480    for i in iter(value):
3481        if isListLike(i):
3482            for j in flattenValue(i):
3483                yield j
3484        else:
3485            yield i
3486
3487def joinValue(value, delimiter=','):
3488    """
3489    Returns a value consisting of joined parts of a given value
3490
3491    >>> joinValue(['1', '2'])
3492    '1,2'
3493    >>> joinValue('1')
3494    '1'
3495    """
3496
3497    if isListLike(value):
3498        retVal = delimiter.join(value)
3499    else:
3500        retVal = value
3501
3502    return retVal
3503
3504def isListLike(value):
3505    """
3506    Returns True if the given value is a list-like instance
3507
3508    >>> isListLike([1, 2, 3])
3509    True
3510    >>> isListLike('2')
3511    False
3512    """
3513
3514    return isinstance(value, (list, tuple, set, BigArray))
3515
3516def getSortedInjectionTests():
3517    """
3518    Returns prioritized test list by eventually detected DBMS from error messages
3519
3520    >>> pushValue(kb.forcedDbms)
3521    >>> kb.forcedDbms = DBMS.SQLITE
3522    >>> [test for test in getSortedInjectionTests() if hasattr(test, "details") and hasattr(test.details, "dbms")][0].details.dbms == kb.forcedDbms
3523    True
3524    >>> kb.forcedDbms = popValue()
3525    """
3526
3527    retVal = copy.deepcopy(conf.tests)
3528
3529    def priorityFunction(test):
3530        retVal = SORT_ORDER.FIRST
3531
3532        if test.stype == PAYLOAD.TECHNIQUE.UNION:
3533            retVal = SORT_ORDER.LAST
3534
3535        elif "details" in test and "dbms" in test.details:
3536            if intersect(test.details.dbms, Backend.getIdentifiedDbms()):
3537                retVal = SORT_ORDER.SECOND
3538            else:
3539                retVal = SORT_ORDER.THIRD
3540
3541        return retVal
3542
3543    if Backend.getIdentifiedDbms():
3544        retVal = sorted(retVal, key=priorityFunction)
3545
3546    return retVal
3547
3548def filterListValue(value, regex):
3549    """
3550    Returns list with items that have parts satisfying given regular expression
3551
3552    >>> filterListValue(['users', 'admins', 'logs'], r'(users|admins)')
3553    ['users', 'admins']
3554    """
3555
3556    if isinstance(value, list) and regex:
3557        retVal = [_ for _ in value if re.search(regex, _, re.I)]
3558    else:
3559        retVal = value
3560
3561    return retVal
3562
3563def showHttpErrorCodes():
3564    """
3565    Shows all HTTP error codes raised till now
3566    """
3567
3568    if kb.httpErrorCodes:
3569        warnMsg = "HTTP error codes detected during run:\n"
3570        warnMsg += ", ".join("%d (%s) - %d times" % (code, _http_client.responses[code] if code in _http_client.responses else '?', count) for code, count in kb.httpErrorCodes.items())
3571        logger.warn(warnMsg)
3572        if any((str(_).startswith('4') or str(_).startswith('5')) and _ != _http_client.INTERNAL_SERVER_ERROR and _ != kb.originalCode for _ in kb.httpErrorCodes):
3573            msg = "too many 4xx and/or 5xx HTTP error codes "
3574            msg += "could mean that some kind of protection is involved (e.g. WAF)"
3575            logger.debug(msg)
3576
3577def openFile(filename, mode='r', encoding=UNICODE_ENCODING, errors="reversible", buffering=1):  # "buffering=1" means line buffered (Reference: http://stackoverflow.com/a/3168436)
3578    """
3579    Returns file handle of a given filename
3580
3581    >>> "openFile" in openFile(__file__).read()
3582    True
3583    >>> b"openFile" in openFile(__file__, "rb", None).read()
3584    True
3585    """
3586
3587    # Reference: https://stackoverflow.com/a/37462452
3588    if 'b' in mode:
3589        buffering = 0
3590
3591    if filename == STDIN_PIPE_DASH:
3592        if filename not in kb.cache.content:
3593            kb.cache.content[filename] = sys.stdin.read()
3594
3595        return contextlib.closing(io.StringIO(readCachedFileContent(filename)))
3596    else:
3597        try:
3598            return codecs.open(filename, mode, encoding, errors, buffering)
3599        except IOError:
3600            errMsg = "there has been a file opening error for filename '%s'. " % filename
3601            errMsg += "Please check %s permissions on a file " % ("write" if mode and ('w' in mode or 'a' in mode or '+' in mode) else "read")
3602            errMsg += "and that it's not locked by another process"
3603            raise SqlmapSystemException(errMsg)
3604
3605def decodeIntToUnicode(value):
3606    """
3607    Decodes inferenced integer value to an unicode character
3608
3609    >>> decodeIntToUnicode(35) == '#'
3610    True
3611    >>> decodeIntToUnicode(64) == '@'
3612    True
3613    """
3614    retVal = value
3615
3616    if isinstance(value, int):
3617        try:
3618            if value > 255:
3619                _ = "%x" % value
3620
3621                if len(_) % 2 == 1:
3622                    _ = "0%s" % _
3623
3624                raw = decodeHex(_)
3625
3626                if Backend.isDbms(DBMS.MYSQL):
3627                    # Reference: https://dev.mysql.com/doc/refman/8.0/en/string-functions.html#function_ord
3628                    # Note: https://github.com/sqlmapproject/sqlmap/issues/1531
3629                    retVal = getUnicode(raw, conf.encoding or UNICODE_ENCODING)
3630                elif Backend.isDbms(DBMS.MSSQL):
3631                    # Reference: https://docs.microsoft.com/en-us/sql/relational-databases/collations/collation-and-unicode-support?view=sql-server-2017 and https://stackoverflow.com/a/14488478
3632                    retVal = getUnicode(raw, "UTF-16-BE")
3633                elif Backend.getIdentifiedDbms() in (DBMS.PGSQL, DBMS.ORACLE, DBMS.SQLITE):     # Note: cases with Unicode code points (e.g. http://www.postgresqltutorial.com/postgresql-ascii/)
3634                    retVal = _unichr(value)
3635                else:
3636                    retVal = getUnicode(raw, conf.encoding)
3637            else:
3638                retVal = _unichr(value)
3639        except:
3640            retVal = INFERENCE_UNKNOWN_CHAR
3641
3642    return retVal
3643
3644def checkIntegrity():
3645    """
3646    Checks integrity of code files during the unhandled exceptions
3647    """
3648
3649    if not paths:
3650        return
3651
3652    logger.debug("running code integrity check")
3653
3654    retVal = True
3655
3656    baseTime = os.path.getmtime(paths.SQLMAP_SETTINGS_PATH) + 3600  # First hour free parking :)
3657    for root, _, filenames in os.walk(paths.SQLMAP_ROOT_PATH):
3658        for filename in filenames:
3659            if re.search(r"(\.py|\.xml|_)\Z", filename):
3660                filepath = os.path.join(root, filename)
3661                if os.path.getmtime(filepath) > baseTime:
3662                    logger.error("wrong modification time of '%s'" % filepath)
3663                    retVal = False
3664
3665    return retVal
3666
3667def getDaysFromLastUpdate():
3668    """
3669    Get total number of days from last update
3670
3671    >>> getDaysFromLastUpdate() >= 0
3672    True
3673    """
3674
3675    if not paths:
3676        return
3677
3678    return int(time.time() - os.path.getmtime(paths.SQLMAP_SETTINGS_PATH)) // (3600 * 24)
3679
3680def unhandledExceptionMessage():
3681    """
3682    Returns detailed message about occurred unhandled exception
3683
3684    >>> all(_ in unhandledExceptionMessage() for _ in ("unhandled exception occurred", "Operating system", "Command line"))
3685    True
3686    """
3687
3688    errMsg = "unhandled exception occurred in %s. It is recommended to retry your " % VERSION_STRING
3689    errMsg += "run with the latest development version from official GitHub "
3690    errMsg += "repository at '%s'. If the exception persists, please open a new issue " % GIT_PAGE
3691    errMsg += "at '%s' " % ISSUES_PAGE
3692    errMsg += "with the following text and any other information required to "
3693    errMsg += "reproduce the bug. Developers will try to reproduce the bug, fix it accordingly "
3694    errMsg += "and get back to you\n"
3695    errMsg += "Running version: %s\n" % VERSION_STRING[VERSION_STRING.find('/') + 1:]
3696    errMsg += "Python version: %s\n" % PYVERSION
3697    errMsg += "Operating system: %s\n" % platform.platform()
3698    errMsg += "Command line: %s\n" % re.sub(r".+?\bsqlmap\.py\b", "sqlmap.py", getUnicode(" ".join(sys.argv), encoding=sys.stdin.encoding))
3699    errMsg += "Technique: %s\n" % (enumValueToNameLookup(PAYLOAD.TECHNIQUE, getTechnique()) if getTechnique() is not None else ("DIRECT" if conf.get("direct") else None))
3700    errMsg += "Back-end DBMS:"
3701
3702    if Backend.getDbms() is not None:
3703        errMsg += " %s (fingerprinted)" % Backend.getDbms()
3704
3705    if Backend.getIdentifiedDbms() is not None and (Backend.getDbms() is None or Backend.getIdentifiedDbms() != Backend.getDbms()):
3706        errMsg += " %s (identified)" % Backend.getIdentifiedDbms()
3707
3708    if not errMsg.endswith(')'):
3709        errMsg += " None"
3710
3711    return errMsg
3712
3713def getLatestRevision():
3714    """
3715    Retrieves latest revision from the offical repository
3716    """
3717
3718    retVal = None
3719    req = _urllib.request.Request(url="https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/lib/core/settings.py", headers={HTTP_HEADER.USER_AGENT: fetchRandomAgent()})
3720
3721    try:
3722        content = getUnicode(_urllib.request.urlopen(req).read())
3723        retVal = extractRegexResult(r"VERSION\s*=\s*[\"'](?P<result>[\d.]+)", content)
3724    except:
3725        pass
3726
3727    return retVal
3728
3729def fetchRandomAgent():
3730    """
3731    Returns random HTTP User-Agent header value
3732
3733    >>> '(' in fetchRandomAgent()
3734    True
3735    """
3736
3737    if not kb.userAgents:
3738        debugMsg = "loading random HTTP User-Agent header(s) from "
3739        debugMsg += "file '%s'" % paths.USER_AGENTS
3740        logger.debug(debugMsg)
3741
3742        try:
3743            kb.userAgents = getFileItems(paths.USER_AGENTS)
3744        except IOError:
3745            errMsg = "unable to read HTTP User-Agent header "
3746            errMsg += "file '%s'" % paths.USER_AGENTS
3747            raise SqlmapSystemException(errMsg)
3748
3749    return random.sample(kb.userAgents, 1)[0]
3750
3751def createGithubIssue(errMsg, excMsg):
3752    """
3753    Automatically create a Github issue with unhandled exception information
3754    """
3755
3756    try:
3757        issues = getFileItems(paths.GITHUB_HISTORY, unique=True)
3758    except:
3759        issues = []
3760    finally:
3761        issues = set(issues)
3762
3763    _ = re.sub(r"'[^']+'", "''", excMsg)
3764    _ = re.sub(r"\s+line \d+", "", _)
3765    _ = re.sub(r'File ".+?/(\w+\.py)', r"\g<1>", _)
3766    _ = re.sub(r".+\Z", "", _)
3767    _ = re.sub(r"(Unicode[^:]*Error:).+", r"\g<1>", _)
3768    _ = re.sub(r"= _", "= ", _)
3769
3770    key = hashlib.md5(getBytes(_)).hexdigest()[:8]
3771
3772    if key in issues:
3773        return
3774
3775    msg = "\ndo you want to automatically create a new (anonymized) issue "
3776    msg += "with the unhandled exception information at "
3777    msg += "the official Github repository? [y/N] "
3778    try:
3779        choice = readInput(msg, default='N', checkBatch=False, boolean=True)
3780    except:
3781        choice = None
3782
3783    if choice:
3784        _excMsg = None
3785        errMsg = errMsg[errMsg.find("\n"):]
3786
3787        req = _urllib.request.Request(url="https://api.github.com/search/issues?q=%s" % _urllib.parse.quote("repo:sqlmapproject/sqlmap Unhandled exception (#%s)" % key), headers={HTTP_HEADER.USER_AGENT: fetchRandomAgent()})
3788
3789        try:
3790            content = _urllib.request.urlopen(req).read()
3791            _ = json.loads(content)
3792            duplicate = _["total_count"] > 0
3793            closed = duplicate and _["items"][0]["state"] == "closed"
3794            if duplicate:
3795                warnMsg = "issue seems to be already reported"
3796                if closed:
3797                    warnMsg += " and resolved. Please update to the latest "
3798                    warnMsg += "development version from official GitHub repository at '%s'" % GIT_PAGE
3799                logger.warn(warnMsg)
3800                return
3801        except:
3802            pass
3803
3804        data = {"title": "Unhandled exception (#%s)" % key, "body": "```%s\n```\n```\n%s```" % (errMsg, excMsg)}
3805        req = _urllib.request.Request(url="https://api.github.com/repos/sqlmapproject/sqlmap/issues", data=getBytes(json.dumps(data)), headers={HTTP_HEADER.AUTHORIZATION: "token %s" % decodeBase64(GITHUB_REPORT_OAUTH_TOKEN, binary=False), HTTP_HEADER.USER_AGENT: fetchRandomAgent()})
3806
3807        try:
3808            content = getText(_urllib.request.urlopen(req).read())
3809        except Exception as ex:
3810            content = None
3811            _excMsg = getSafeExString(ex)
3812
3813        issueUrl = re.search(r"https://github.com/sqlmapproject/sqlmap/issues/\d+", content or "")
3814        if issueUrl:
3815            infoMsg = "created Github issue can been found at the address '%s'" % issueUrl.group(0)
3816            logger.info(infoMsg)
3817
3818            try:
3819                with openFile(paths.GITHUB_HISTORY, "a+b") as f:
3820                    f.write("%s\n" % key)
3821            except:
3822                pass
3823        else:
3824            warnMsg = "something went wrong while creating a Github issue"
3825            if _excMsg:
3826                warnMsg += " ('%s')" % _excMsg
3827            if "Unauthorized" in warnMsg:
3828                warnMsg += ". Please update to the latest revision"
3829            logger.warn(warnMsg)
3830
3831def maskSensitiveData(msg):
3832    """
3833    Masks sensitive data in the supplied message
3834
3835    >>> maskSensitiveData('python sqlmap.py -u "http://www.test.com/vuln.php?id=1" --banner') == 'python sqlmap.py -u *********************************** --banner'
3836    True
3837    >>> maskSensitiveData('sqlmap.py -u test.com/index.go?id=index') == 'sqlmap.py -u **************************'
3838    True
3839    """
3840
3841    retVal = getUnicode(msg)
3842
3843    for item in filterNone(conf.get(_) for _ in SENSITIVE_OPTIONS):
3844        if isListLike(item):
3845            item = listToStrValue(item)
3846
3847        regex = SENSITIVE_DATA_REGEX % re.sub(r"(\W)", r"\\\1", getUnicode(item))
3848        while extractRegexResult(regex, retVal):
3849            value = extractRegexResult(regex, retVal)
3850            retVal = retVal.replace(value, '*' * len(value))
3851
3852    # Just in case (for problematic parameters regarding user encoding)
3853    for match in re.finditer(r"(?i)[ -]-(u|url|data|cookie|auth-\w+|proxy|host|referer|headers?|H)( |=)(.*?)(?= -?-[a-z]|\Z)", retVal):
3854        retVal = retVal.replace(match.group(3), '*' * len(match.group(3)))
3855
3856    # Fail-safe substitutions
3857    retVal = re.sub(r"(?i)(Command line:.+)\b(https?://[^ ]+)", lambda match: "%s%s" % (match.group(1), '*' * len(match.group(2))), retVal)
3858    retVal = re.sub(r"(?i)(\b\w:[\\/]+Users[\\/]+|[\\/]+home[\\/]+)([^\\/]+)", lambda match: "%s%s" % (match.group(1), '*' * len(match.group(2))), retVal)
3859
3860    if getpass.getuser():
3861        retVal = re.sub(r"(?i)\b%s\b" % re.escape(getpass.getuser()), '*' * len(getpass.getuser()), retVal)
3862
3863    return retVal
3864
3865def listToStrValue(value):
3866    """
3867    Flattens list to a string value
3868
3869    >>> listToStrValue([1,2,3])
3870    '1, 2, 3'
3871    """
3872
3873    if isinstance(value, (set, tuple, types.GeneratorType)):
3874        value = list(value)
3875
3876    if isinstance(value, list):
3877        retVal = value.__str__().lstrip('[').rstrip(']')
3878    else:
3879        retVal = value
3880
3881    return retVal
3882
3883def intersect(containerA, containerB, lowerCase=False):
3884    """
3885    Returns intersection of the container-ized values
3886
3887    >>> intersect([1, 2, 3], set([1,3]))
3888    [1, 3]
3889    """
3890
3891    retVal = []
3892
3893    if containerA and containerB:
3894        containerA = arrayizeValue(containerA)
3895        containerB = arrayizeValue(containerB)
3896
3897        if lowerCase:
3898            containerA = [val.lower() if hasattr(val, "lower") else val for val in containerA]
3899            containerB = [val.lower() if hasattr(val, "lower") else val for val in containerB]
3900
3901        retVal = [val for val in containerA if val in containerB]
3902
3903    return retVal
3904
3905def decodeStringEscape(value):
3906    """
3907    Decodes escaped string values (e.g. "\\t" -> "\t")
3908    """
3909
3910    retVal = value
3911
3912    if value and '\\' in value:
3913        charset = "\\%s" % string.whitespace.replace(" ", "")
3914        for _ in charset:
3915            retVal = retVal.replace(repr(_).strip("'"), _)
3916
3917    return retVal
3918
3919def encodeStringEscape(value):
3920    """
3921    Encodes escaped string values (e.g. "\t" -> "\\t")
3922    """
3923
3924    retVal = value
3925
3926    if value:
3927        charset = "\\%s" % string.whitespace.replace(" ", "")
3928        for _ in charset:
3929            retVal = retVal.replace(_, repr(_).strip("'"))
3930
3931    return retVal
3932
3933def removeReflectiveValues(content, payload, suppressWarning=False):
3934    """
3935    Neutralizes reflective values in a given content based on a payload
3936    (e.g. ..search.php?q=1 AND 1=2 --> "...searching for <b>1%20AND%201%3D2</b>..." --> "...searching for <b>__REFLECTED_VALUE__</b>...")
3937    """
3938
3939    retVal = content
3940
3941    try:
3942        if all((content, payload)) and isinstance(content, six.text_type) and kb.reflectiveMechanism and not kb.heuristicMode:
3943            def _(value):
3944                while 2 * REFLECTED_REPLACEMENT_REGEX in value:
3945                    value = value.replace(2 * REFLECTED_REPLACEMENT_REGEX, REFLECTED_REPLACEMENT_REGEX)
3946                return value
3947
3948            payload = getUnicode(urldecode(payload.replace(PAYLOAD_DELIMITER, ""), convall=True))
3949            regex = _(filterStringValue(payload, r"[A-Za-z0-9]", encodeStringEscape(REFLECTED_REPLACEMENT_REGEX)))
3950
3951            if regex != payload:
3952                if all(part.lower() in content.lower() for part in filterNone(regex.split(REFLECTED_REPLACEMENT_REGEX))[1:]):  # fast optimization check
3953                    parts = regex.split(REFLECTED_REPLACEMENT_REGEX)
3954
3955                    # Note: naive approach
3956                    retVal = content.replace(payload, REFLECTED_VALUE_MARKER)
3957                    retVal = retVal.replace(re.sub(r"\A\w+", "", payload), REFLECTED_VALUE_MARKER)
3958
3959                    if len(parts) > REFLECTED_MAX_REGEX_PARTS:  # preventing CPU hogs
3960                        regex = _("%s%s%s" % (REFLECTED_REPLACEMENT_REGEX.join(parts[:REFLECTED_MAX_REGEX_PARTS // 2]), REFLECTED_REPLACEMENT_REGEX, REFLECTED_REPLACEMENT_REGEX.join(parts[-REFLECTED_MAX_REGEX_PARTS // 2:])))
3961
3962                    parts = filterNone(regex.split(REFLECTED_REPLACEMENT_REGEX))
3963
3964                    if regex.startswith(REFLECTED_REPLACEMENT_REGEX):
3965                        regex = r"%s%s" % (REFLECTED_BORDER_REGEX, regex[len(REFLECTED_REPLACEMENT_REGEX):])
3966                    else:
3967                        regex = r"\b%s" % regex
3968
3969                    if regex.endswith(REFLECTED_REPLACEMENT_REGEX):
3970                        regex = r"%s%s" % (regex[:-len(REFLECTED_REPLACEMENT_REGEX)], REFLECTED_BORDER_REGEX)
3971                    else:
3972                        regex = r"%s\b" % regex
3973
3974                    _retVal = [retVal]
3975
3976                    def _thread(regex):
3977                        try:
3978                            _retVal[0] = re.sub(r"(?i)%s" % regex, REFLECTED_VALUE_MARKER, _retVal[0])
3979
3980                            if len(parts) > 2:
3981                                regex = REFLECTED_REPLACEMENT_REGEX.join(parts[1:])
3982                                _retVal[0] = re.sub(r"(?i)\b%s\b" % regex, REFLECTED_VALUE_MARKER, _retVal[0])
3983                        except KeyboardInterrupt:
3984                            raise
3985                        except:
3986                            pass
3987
3988                    thread = threading.Thread(target=_thread, args=(regex,))
3989                    thread.daemon = True
3990                    thread.start()
3991                    thread.join(REFLECTED_REPLACEMENT_TIMEOUT)
3992
3993                    if thread.isAlive():
3994                        kb.reflectiveMechanism = False
3995                        retVal = content
3996                        if not suppressWarning:
3997                            debugMsg = "turning off reflection removal mechanism (because of timeouts)"
3998                            logger.debug(debugMsg)
3999                    else:
4000                        retVal = _retVal[0]
4001
4002                if retVal != content:
4003                    kb.reflectiveCounters[REFLECTIVE_COUNTER.HIT] += 1
4004                    if not suppressWarning:
4005                        warnMsg = "reflective value(s) found and filtering out"
4006                        singleTimeWarnMessage(warnMsg)
4007
4008                    if re.search(r"(?i)FRAME[^>]+src=[^>]*%s" % REFLECTED_VALUE_MARKER, retVal):
4009                        warnMsg = "frames detected containing attacked parameter values. Please be sure to "
4010                        warnMsg += "test those separately in case that attack on this page fails"
4011                        singleTimeWarnMessage(warnMsg)
4012
4013                elif not kb.testMode and not kb.reflectiveCounters[REFLECTIVE_COUNTER.HIT]:
4014                    kb.reflectiveCounters[REFLECTIVE_COUNTER.MISS] += 1
4015                    if kb.reflectiveCounters[REFLECTIVE_COUNTER.MISS] > REFLECTIVE_MISS_THRESHOLD:
4016                        kb.reflectiveMechanism = False
4017                        if not suppressWarning:
4018                            debugMsg = "turning off reflection removal mechanism (for optimization purposes)"
4019                            logger.debug(debugMsg)
4020    except MemoryError:
4021        kb.reflectiveMechanism = False
4022        if not suppressWarning:
4023            debugMsg = "turning off reflection removal mechanism (because of low memory issues)"
4024            logger.debug(debugMsg)
4025
4026    return retVal
4027
4028def normalizeUnicode(value, charset=string.printable[:string.printable.find(' ') + 1]):
4029    """
4030    Does an ASCII normalization of unicode strings
4031
4032    # Reference: http://www.peterbe.com/plog/unicode-to-ascii
4033
4034    >>> normalizeUnicode(u'\\u0161u\\u0107uraj') == u'sucuraj'
4035    True
4036    >>> normalizeUnicode(getUnicode(decodeHex("666f6f00626172"))) == u'foobar'
4037    True
4038    """
4039
4040    retVal = value
4041
4042    if isinstance(value, six.text_type):
4043        retVal = unicodedata.normalize("NFKD", value)
4044        retVal = "".join(_ for _ in retVal if _ in charset)
4045
4046    return retVal
4047
4048def safeSQLIdentificatorNaming(name, isTable=False):
4049    """
4050    Returns a safe representation of SQL identificator name (internal data format)
4051
4052    # Reference: http://stackoverflow.com/questions/954884/what-special-characters-are-allowed-in-t-sql-column-retVal
4053
4054    >>> pushValue(kb.forcedDbms)
4055    >>> kb.forcedDbms = DBMS.MSSQL
4056    >>> getText(safeSQLIdentificatorNaming("begin"))
4057    '[begin]'
4058    >>> getText(safeSQLIdentificatorNaming("foobar"))
4059    'foobar'
4060    >>> kb.forceDbms = popValue()
4061    """
4062
4063    retVal = name
4064
4065    if isinstance(name, six.string_types):
4066        retVal = getUnicode(name)
4067        _ = isTable and Backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.SYBASE)
4068
4069        if _:
4070            retVal = re.sub(r"(?i)\A\[?%s\]?\." % DEFAULT_MSSQL_SCHEMA, "%s." % DEFAULT_MSSQL_SCHEMA, retVal)
4071
4072        if retVal.upper() in kb.keywords or (retVal or " ")[0].isdigit() or not re.match(r"\A[A-Za-z0-9_@%s\$]+\Z" % ('.' if _ else ""), retVal):  # MsSQL is the only DBMS where we automatically prepend schema to table name (dot is normal)
4073            retVal = unsafeSQLIdentificatorNaming(retVal)
4074
4075            if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.ACCESS, DBMS.SQLITE):  # Note: in SQLite double-quotes are treated as string if column/identifier is non-existent (e.g. SELECT "foobar" FROM users)
4076                retVal = "`%s`" % retVal
4077            elif Backend.getIdentifiedDbms() in (DBMS.PGSQL, DBMS.DB2, DBMS.HSQLDB, DBMS.H2, DBMS.INFORMIX):
4078                retVal = "\"%s\"" % retVal
4079            elif Backend.getIdentifiedDbms() in (DBMS.ORACLE,):
4080                retVal = "\"%s\"" % retVal.upper()
4081            elif Backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.SYBASE):
4082                if isTable:
4083                    parts = retVal.split('.', 1)
4084                    for i in xrange(len(parts)):
4085                        if parts[i] and (re.search(r"\A\d|[^\w]", parts[i], re.U) or parts[i].upper() in kb.keywords):
4086                            parts[i] = "[%s]" % parts[i]
4087                    retVal = '.'.join(parts)
4088                else:
4089                    if re.search(r"\A\d|[^\w]", retVal, re.U) or retVal.upper() in kb.keywords:
4090                        retVal = "[%s]" % retVal
4091
4092        if _ and DEFAULT_MSSQL_SCHEMA not in retVal and '.' not in re.sub(r"\[[^]]+\]", "", retVal):
4093            retVal = "%s.%s" % (DEFAULT_MSSQL_SCHEMA, retVal)
4094
4095    return retVal
4096
4097def unsafeSQLIdentificatorNaming(name):
4098    """
4099    Extracts identificator's name from its safe SQL representation
4100
4101    >>> pushValue(kb.forcedDbms)
4102    >>> kb.forcedDbms = DBMS.MSSQL
4103    >>> getText(unsafeSQLIdentificatorNaming("[begin]"))
4104    'begin'
4105    >>> getText(unsafeSQLIdentificatorNaming("foobar"))
4106    'foobar'
4107    >>> kb.forceDbms = popValue()
4108    """
4109
4110    retVal = name
4111
4112    if isinstance(name, six.string_types):
4113        if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.ACCESS, DBMS.SQLITE):
4114            retVal = name.replace("`", "")
4115        elif Backend.getIdentifiedDbms() in (DBMS.PGSQL, DBMS.DB2, DBMS.INFORMIX, DBMS.HSQLDB):
4116            retVal = name.replace("\"", "")
4117        elif Backend.getIdentifiedDbms() in (DBMS.ORACLE,):
4118            retVal = name.replace("\"", "").upper()
4119        elif Backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.SYBASE):
4120            retVal = name.replace("[", "").replace("]", "")
4121
4122        if Backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.SYBASE):
4123            retVal = re.sub(r"(?i)\A\[?%s\]?\." % DEFAULT_MSSQL_SCHEMA, "", retVal)
4124
4125    return retVal
4126
4127def isNoneValue(value):
4128    """
4129    Returns whether the value is unusable (None or '')
4130
4131    >>> isNoneValue(None)
4132    True
4133    >>> isNoneValue('None')
4134    True
4135    >>> isNoneValue('')
4136    True
4137    >>> isNoneValue([])
4138    True
4139    >>> isNoneValue([2])
4140    False
4141    """
4142
4143    if isinstance(value, six.string_types):
4144        return value in ("None", "")
4145    elif isListLike(value):
4146        return all(isNoneValue(_) for _ in value)
4147    elif isinstance(value, dict):
4148        return not any(value)
4149    else:
4150        return value is None
4151
4152def isNullValue(value):
4153    """
4154    Returns whether the value contains explicit 'NULL' value
4155
4156    >>> isNullValue(u'NULL')
4157    True
4158    >>> isNullValue(u'foobar')
4159    False
4160    """
4161
4162    return hasattr(value, "upper") and value.upper() == NULL
4163
4164def expandMnemonics(mnemonics, parser, args):
4165    """
4166    Expands mnemonic options
4167    """
4168
4169    class MnemonicNode(object):
4170        def __init__(self):
4171            self.next = {}
4172            self.current = []
4173
4174    head = MnemonicNode()
4175    pointer = None
4176
4177    for group in parser.option_groups:
4178        for option in group.option_list:
4179            for opt in option._long_opts + option._short_opts:
4180                pointer = head
4181
4182                for char in opt:
4183                    if char == "-":
4184                        continue
4185                    elif char not in pointer.next:
4186                        pointer.next[char] = MnemonicNode()
4187
4188                    pointer = pointer.next[char]
4189                    pointer.current.append(option)
4190
4191    for mnemonic in (mnemonics or "").split(','):
4192        found = None
4193        name = mnemonic.split('=')[0].replace('-', "").strip()
4194        value = mnemonic.split('=')[1] if len(mnemonic.split('=')) > 1 else None
4195        pointer = head
4196
4197        for char in name:
4198            if char in pointer.next:
4199                pointer = pointer.next[char]
4200            else:
4201                pointer = None
4202                break
4203
4204        if pointer in (None, head):
4205            errMsg = "mnemonic '%s' can't be resolved to any parameter name" % name
4206            raise SqlmapSyntaxException(errMsg)
4207
4208        elif len(pointer.current) > 1:
4209            options = {}
4210
4211            for option in pointer.current:
4212                for opt in option._long_opts + option._short_opts:
4213                    opt = opt.strip('-')
4214                    if opt.startswith(name):
4215                        options[opt] = option
4216
4217            if not options:
4218                warnMsg = "mnemonic '%s' can't be resolved" % name
4219                logger.warn(warnMsg)
4220            elif name in options:
4221                found = name
4222                debugMsg = "mnemonic '%s' resolved to %s). " % (name, found)
4223                logger.debug(debugMsg)
4224            else:
4225                found = sorted(options.keys(), key=len)[0]
4226                warnMsg = "detected ambiguity (mnemonic '%s' can be resolved to any of: %s). " % (name, ", ".join("'%s'" % key for key in options))
4227                warnMsg += "Resolved to shortest of those ('%s')" % found
4228                logger.warn(warnMsg)
4229
4230            if found:
4231                found = options[found]
4232        else:
4233            found = pointer.current[0]
4234            debugMsg = "mnemonic '%s' resolved to %s). " % (name, found)
4235            logger.debug(debugMsg)
4236
4237        if found:
4238            try:
4239                value = found.convert_value(found, value)
4240            except OptionValueError:
4241                value = None
4242
4243            if value is not None:
4244                setattr(args, found.dest, value)
4245            elif not found.type:  # boolean
4246                setattr(args, found.dest, True)
4247            else:
4248                errMsg = "mnemonic '%s' requires value of type '%s'" % (name, found.type)
4249                raise SqlmapSyntaxException(errMsg)
4250
4251def safeCSValue(value):
4252    """
4253    Returns value safe for CSV dumping
4254
4255    # Reference: http://tools.ietf.org/html/rfc4180
4256
4257    >>> safeCSValue('foo, bar')
4258    '"foo, bar"'
4259    >>> safeCSValue('foobar')
4260    'foobar'
4261    """
4262
4263    retVal = value
4264
4265    if retVal and isinstance(retVal, six.string_types):
4266        if not (retVal[0] == retVal[-1] == '"'):
4267            if any(_ in retVal for _ in (conf.get("csvDel", defaults.csvDel), '"', '\n')):
4268                retVal = '"%s"' % retVal.replace('"', '""')
4269
4270    return retVal
4271
4272def filterPairValues(values):
4273    """
4274    Returns only list-like values with length 2
4275
4276    >>> filterPairValues([[1, 2], [3], 1, [4, 5]])
4277    [[1, 2], [4, 5]]
4278    """
4279
4280    retVal = []
4281
4282    if not isNoneValue(values) and hasattr(values, '__iter__'):
4283        retVal = [value for value in values if isinstance(value, (tuple, list, set)) and len(value) == 2]
4284
4285    return retVal
4286
4287def randomizeParameterValue(value):
4288    """
4289    Randomize a parameter value based on occurrences of alphanumeric characters
4290
4291    >>> random.seed(0)
4292    >>> randomizeParameterValue('foobar')
4293    'fupgpy'
4294    >>> randomizeParameterValue('17')
4295    '36'
4296    """
4297
4298    retVal = value
4299
4300    value = re.sub(r"%[0-9a-fA-F]{2}", "", value)
4301
4302    for match in re.finditer(r"[A-Z]+", value):
4303        while True:
4304            original = match.group()
4305            candidate = randomStr(len(match.group())).upper()
4306            if original != candidate:
4307                break
4308
4309        retVal = retVal.replace(original, candidate)
4310
4311    for match in re.finditer(r"[a-z]+", value):
4312        while True:
4313            original = match.group()
4314            candidate = randomStr(len(match.group())).lower()
4315            if original != candidate:
4316                break
4317
4318        retVal = retVal.replace(original, candidate)
4319
4320    for match in re.finditer(r"[0-9]+", value):
4321        while True:
4322            original = match.group()
4323            candidate = str(randomInt(len(match.group())))
4324            if original != candidate:
4325                break
4326
4327        retVal = retVal.replace(original, candidate)
4328
4329    if re.match(r"\A[^@]+@.+\.[a-z]+\Z", value):
4330        parts = retVal.split('.')
4331        parts[-1] = random.sample(RANDOMIZATION_TLDS, 1)[0]
4332        retVal = '.'.join(parts)
4333
4334    if not retVal:
4335        retVal = randomStr(lowercase=True)
4336
4337    return retVal
4338
4339@cachedmethod
4340def asciifyUrl(url, forceQuote=False):
4341    """
4342    Attempts to make a unicode URL usable with ``urllib/urllib2``.
4343
4344    More specifically, it attempts to convert the unicode object ``url``,
4345    which is meant to represent a IRI, to an unicode object that,
4346    containing only ASCII characters, is a valid URI. This involves:
4347
4348        * IDNA/Puny-encoding the domain name.
4349        * UTF8-quoting the path and querystring parts.
4350
4351    See also RFC 3987.
4352
4353    # Reference: http://blog.elsdoerfer.name/2008/12/12/opening-iris-in-python/
4354
4355    >>> asciifyUrl(u'http://www.\\u0161u\\u0107uraj.com')
4356    'http://www.xn--uuraj-gxa24d.com'
4357    """
4358
4359    parts = _urllib.parse.urlsplit(url)
4360    if not all((parts.scheme, parts.netloc, parts.hostname)):
4361        # apparently not an url
4362        return getText(url)
4363
4364    if all(char in string.printable for char in url):
4365        return getText(url)
4366
4367    hostname = parts.hostname
4368
4369    if isinstance(hostname, six.binary_type):
4370        hostname = getUnicode(hostname)
4371
4372    # idna-encode domain
4373    try:
4374        hostname = hostname.encode("idna")
4375    except:
4376        hostname = hostname.encode("punycode")
4377
4378    # UTF8-quote the other parts. We check each part individually if
4379    # if needs to be quoted - that should catch some additional user
4380    # errors, say for example an umlaut in the username even though
4381    # the path *is* already quoted.
4382    def quote(s, safe):
4383        s = s or ''
4384        # Triggers on non-ascii characters - another option would be:
4385        #     _urllib.parse.quote(s.replace('%', '')) != s.replace('%', '')
4386        # which would trigger on all %-characters, e.g. "&".
4387        if getUnicode(s).encode("ascii", "replace") != s or forceQuote:
4388            s = _urllib.parse.quote(getBytes(s), safe=safe)
4389        return s
4390
4391    username = quote(parts.username, '')
4392    password = quote(parts.password, safe='')
4393    path = quote(parts.path, safe='/')
4394    query = quote(parts.query, safe="&=")
4395
4396    # put everything back together
4397    netloc = getText(hostname)
4398    if username or password:
4399        netloc = '@' + netloc
4400        if password:
4401            netloc = ':' + password + netloc
4402        netloc = username + netloc
4403
4404    try:
4405        port = parts.port
4406    except:
4407        port = None
4408
4409    if port:
4410        netloc += ':' + str(port)
4411
4412    return getText(_urllib.parse.urlunsplit([parts.scheme, netloc, path, query, parts.fragment]) or url)
4413
4414def isAdminFromPrivileges(privileges):
4415    """
4416    Inspects privileges to see if those are coming from an admin user
4417    """
4418
4419    privileges = privileges or []
4420
4421    # In PostgreSQL the usesuper privilege means that the
4422    # user is DBA
4423    retVal = (Backend.isDbms(DBMS.PGSQL) and "super" in privileges)
4424
4425    # In Oracle the DBA privilege means that the
4426    # user is DBA
4427    retVal |= (Backend.isDbms(DBMS.ORACLE) and "DBA" in privileges)
4428
4429    # In MySQL >= 5.0 the SUPER privilege means
4430    # that the user is DBA
4431    retVal |= (Backend.isDbms(DBMS.MYSQL) and kb.data.has_information_schema and "SUPER" in privileges)
4432
4433    # In MySQL < 5.0 the super_priv privilege means
4434    # that the user is DBA
4435    retVal |= (Backend.isDbms(DBMS.MYSQL) and not kb.data.has_information_schema and "super_priv" in privileges)
4436
4437    # In Firebird there is no specific privilege that means
4438    # that the user is DBA
4439    retVal |= (Backend.isDbms(DBMS.FIREBIRD) and all(_ in privileges for _ in ("SELECT", "INSERT", "UPDATE", "DELETE", "REFERENCES", "EXECUTE")))
4440
4441    return retVal
4442
4443def findPageForms(content, url, raise_=False, addToTargets=False):
4444    """
4445    Parses given page content for possible forms (Note: still not implemented for Python3)
4446
4447    >>> findPageForms('<html><form action="/input.php" method="POST"><input type="text" name="id" value="1"><input type="submit" value="Submit"></form></html>', 'http://www.site.com') == set([('http://www.site.com/input.php', 'POST', 'id=1', None, None)])
4448    True
4449    """
4450
4451    class _(six.StringIO, object):
4452        def __init__(self, content, url):
4453            super(_, self).__init__(content)
4454            self._url = url
4455
4456        def geturl(self):
4457            return self._url
4458
4459    if not content:
4460        errMsg = "can't parse forms as the page content appears to be blank"
4461        if raise_:
4462            raise SqlmapGenericException(errMsg)
4463        else:
4464            logger.debug(errMsg)
4465
4466    forms = None
4467    retVal = set()
4468    response = _(content, url)
4469
4470    try:
4471        forms = ParseResponse(response, backwards_compat=False)
4472    except ParseError:
4473        if re.search(r"(?i)<!DOCTYPE html|<html", content or "") and not re.search(r"(?i)\.js(\?|\Z)", url):
4474            dbgMsg = "badly formed HTML at the given URL ('%s'). Going to filter it" % url
4475            logger.debug(dbgMsg)
4476            filtered = _("".join(re.findall(FORM_SEARCH_REGEX, content)), url)
4477
4478            if filtered and filtered != content:
4479                try:
4480                    forms = ParseResponse(filtered, backwards_compat=False)
4481                except ParseError:
4482                    errMsg = "no success"
4483                    if raise_:
4484                        raise SqlmapGenericException(errMsg)
4485                    else:
4486                        logger.debug(errMsg)
4487    except:
4488        pass
4489
4490    for form in forms or []:
4491        try:
4492            for control in form.controls:
4493                if hasattr(control, "items") and not any((control.disabled, control.readonly)):
4494                    # if control has selectable items select first non-disabled
4495                    for item in control.items:
4496                        if not item.disabled:
4497                            if not item.selected:
4498                                item.selected = True
4499                            break
4500
4501            if conf.crawlExclude and re.search(conf.crawlExclude, form.action or ""):
4502                dbgMsg = "skipping '%s'" % form.action
4503                logger.debug(dbgMsg)
4504                continue
4505
4506            request = form.click()
4507        except (ValueError, TypeError) as ex:
4508            errMsg = "there has been a problem while "
4509            errMsg += "processing page forms ('%s')" % getSafeExString(ex)
4510            if raise_:
4511                raise SqlmapGenericException(errMsg)
4512            else:
4513                logger.debug(errMsg)
4514        else:
4515            url = urldecode(request.get_full_url(), kb.pageEncoding)
4516            method = request.get_method()
4517            data = request.data
4518            data = urldecode(data, kb.pageEncoding, spaceplus=False)
4519
4520            if not data and method and method.upper() == HTTPMETHOD.POST:
4521                debugMsg = "invalid POST form with blank data detected"
4522                logger.debug(debugMsg)
4523                continue
4524
4525            # flag to know if we are dealing with the same target host
4526            _ = checkSameHost(response.geturl(), url)
4527
4528            if data:
4529                data = data.lstrip("&=").rstrip('&')
4530
4531            if conf.scope and not re.search(conf.scope, url, re.I):
4532                continue
4533            elif data and not re.sub(r"(%s)=[^&]*&?" % '|'.join(IGNORE_PARAMETERS), "", data):
4534                continue
4535            elif not _:
4536                continue
4537            else:
4538                target = (url, method, data, conf.cookie, None)
4539                retVal.add(target)
4540
4541    for match in re.finditer(r"\.post\(['\"]([^'\"]*)['\"],\s*\{([^}]*)\}", content):
4542        url = _urllib.parse.urljoin(url, htmlUnescape(match.group(1)))
4543        data = ""
4544
4545        for name, value in re.findall(r"['\"]?(\w+)['\"]?\s*:\s*(['\"][^'\"]+)?", match.group(2)):
4546            data += "%s=%s%s" % (name, value, DEFAULT_GET_POST_DELIMITER)
4547
4548        data = data.rstrip(DEFAULT_GET_POST_DELIMITER)
4549        retVal.add((url, HTTPMETHOD.POST, data, conf.cookie, None))
4550
4551    for match in re.finditer(r"(?s)(\w+)\.open\(['\"]POST['\"],\s*['\"]([^'\"]+)['\"]\).*?\1\.send\(([^)]+)\)", content):
4552        url = _urllib.parse.urljoin(url, htmlUnescape(match.group(2)))
4553        data = match.group(3)
4554
4555        data = re.sub(r"\s*\+\s*[^\s'\"]+|[^\s'\"]+\s*\+\s*", "", data)
4556
4557        data = data.strip("['\"]")
4558        retVal.add((url, HTTPMETHOD.POST, data, conf.cookie, None))
4559
4560    if not retVal and not conf.crawlDepth:
4561        errMsg = "there were no forms found at the given target URL"
4562        if raise_:
4563            raise SqlmapGenericException(errMsg)
4564        else:
4565            logger.debug(errMsg)
4566
4567    if addToTargets and retVal:
4568        for target in retVal:
4569            kb.targets.add(target)
4570
4571    return retVal
4572
4573def checkSameHost(*urls):
4574    """
4575    Returns True if all provided urls share that same host
4576
4577    >>> checkSameHost('http://www.target.com/page1.php?id=1', 'http://www.target.com/images/page2.php')
4578    True
4579    >>> checkSameHost('http://www.target.com/page1.php?id=1', 'http://www.target2.com/images/page2.php')
4580    False
4581    """
4582
4583    if not urls:
4584        return None
4585    elif len(urls) == 1:
4586        return True
4587    else:
4588        def _(value):
4589            if value and not re.search(r"\A\w+://", value):
4590                value = "http://%s" % value
4591            return value
4592
4593        return all(re.sub(r"(?i)\Awww\.", "", _urllib.parse.urlparse(_(url) or "").netloc.split(':')[0]) == re.sub(r"(?i)\Awww\.", "", _urllib.parse.urlparse(_(urls[0]) or "").netloc.split(':')[0]) for url in urls[1:])
4594
4595def getHostHeader(url):
4596    """
4597    Returns proper Host header value for a given target URL
4598
4599    >>> getHostHeader('http://www.target.com/vuln.php?id=1')
4600    'www.target.com'
4601    """
4602
4603    retVal = url
4604
4605    if url:
4606        retVal = _urllib.parse.urlparse(url).netloc
4607
4608        if re.search(r"http(s)?://\[.+\]", url, re.I):
4609            retVal = extractRegexResult(r"http(s)?://\[(?P<result>.+)\]", url)
4610        elif any(retVal.endswith(':%d' % _) for _ in (80, 443)):
4611            retVal = retVal.split(':')[0]
4612
4613    if retVal and retVal.count(':') > 1 and not any(_ in retVal for _ in ('[', ']')):
4614        retVal = "[%s]" % retVal
4615
4616    return retVal
4617
4618def checkOldOptions(args):
4619    """
4620    Checks for obsolete/deprecated options
4621    """
4622
4623    for _ in args:
4624        _ = _.split('=')[0].strip()
4625        if _ in OBSOLETE_OPTIONS:
4626            errMsg = "switch/option '%s' is obsolete" % _
4627            if OBSOLETE_OPTIONS[_]:
4628                errMsg += " (hint: %s)" % OBSOLETE_OPTIONS[_]
4629            raise SqlmapSyntaxException(errMsg)
4630        elif _ in DEPRECATED_OPTIONS:
4631            warnMsg = "switch/option '%s' is deprecated" % _
4632            if DEPRECATED_OPTIONS[_]:
4633                warnMsg += " (hint: %s)" % DEPRECATED_OPTIONS[_]
4634            logger.warn(warnMsg)
4635
4636def checkSystemEncoding():
4637    """
4638    Checks for problematic encodings
4639    """
4640
4641    if sys.getdefaultencoding() == "cp720":
4642        try:
4643            codecs.lookup("cp720")
4644        except LookupError:
4645            errMsg = "there is a known Python issue (#1616979) related "
4646            errMsg += "to support for charset 'cp720'. Please visit "
4647            errMsg += "'http://blog.oneortheother.info/tip/python-fix-cp720-encoding/index.html' "
4648            errMsg += "and follow the instructions to be able to fix it"
4649            logger.critical(errMsg)
4650
4651            warnMsg = "temporary switching to charset 'cp1256'"
4652            logger.warn(warnMsg)
4653
4654            _reload_module(sys)
4655            sys.setdefaultencoding("cp1256")
4656
4657def evaluateCode(code, variables=None):
4658    """
4659    Executes given python code given in a string form
4660
4661    >>> _ = {}; evaluateCode("a = 1; b = 2; c = a", _); _["c"]
4662    1
4663    """
4664
4665    try:
4666        exec(code, variables)
4667    except KeyboardInterrupt:
4668        raise
4669    except Exception as ex:
4670        errMsg = "an error occurred while evaluating provided code ('%s') " % getSafeExString(ex)
4671        raise SqlmapGenericException(errMsg)
4672
4673def serializeObject(object_):
4674    """
4675    Serializes given object
4676
4677    >>> type(serializeObject([1, 2, 3, ('a', 'b')])) == six.binary_type
4678    True
4679    """
4680
4681    return base64pickle(object_)
4682
4683def unserializeObject(value):
4684    """
4685    Unserializes object from given serialized form
4686
4687    >>> unserializeObject(serializeObject([1, 2, 3])) == [1, 2, 3]
4688    True
4689    >>> unserializeObject('gAJVBmZvb2JhcnEBLg==')
4690    'foobar'
4691    """
4692
4693    return base64unpickle(value) if value else None
4694
4695def resetCounter(technique):
4696    """
4697    Resets query counter for a given technique
4698    """
4699
4700    kb.counters[technique] = 0
4701
4702def incrementCounter(technique):
4703    """
4704    Increments query counter for a given technique
4705    """
4706
4707    kb.counters[technique] = getCounter(technique) + 1
4708
4709def getCounter(technique):
4710    """
4711    Returns query counter for a given technique
4712
4713    >>> resetCounter(PAYLOAD.TECHNIQUE.STACKED); incrementCounter(PAYLOAD.TECHNIQUE.STACKED); getCounter(PAYLOAD.TECHNIQUE.STACKED)
4714    1
4715    """
4716
4717    return kb.counters.get(technique, 0)
4718
4719def applyFunctionRecursively(value, function):
4720    """
4721    Applies function recursively through list-like structures
4722
4723    >>> applyFunctionRecursively([1, 2, [3, 4, [19]], -9], lambda _: _ > 0)
4724    [True, True, [True, True, [True]], False]
4725    """
4726
4727    if isListLike(value):
4728        retVal = [applyFunctionRecursively(_, function) for _ in value]
4729    else:
4730        retVal = function(value)
4731
4732    return retVal
4733
4734def decodeDbmsHexValue(value, raw=False):
4735    """
4736    Returns value decoded from DBMS specific hexadecimal representation
4737
4738    >>> decodeDbmsHexValue('3132332031') == u'123 1'
4739    True
4740    >>> decodeDbmsHexValue('313233203') == u'123 ?'
4741    True
4742    >>> decodeDbmsHexValue(['0x31', '0x32']) == [u'1', u'2']
4743    True
4744    >>> decodeDbmsHexValue('5.1.41') == u'5.1.41'
4745    True
4746    """
4747
4748    retVal = value
4749
4750    def _(value):
4751        retVal = value
4752        if value and isinstance(value, six.string_types):
4753            value = value.strip()
4754
4755            if len(value) % 2 != 0:
4756                retVal = (decodeHex(value[:-1]) + b'?') if len(value) > 1 else value
4757                singleTimeWarnMessage("there was a problem decoding value '%s' from expected hexadecimal form" % value)
4758            else:
4759                retVal = decodeHex(value)
4760
4761            if not raw:
4762                if not kb.binaryField:
4763                    if Backend.isDbms(DBMS.MSSQL) and value.startswith("0x"):
4764                        try:
4765                            retVal = retVal.decode("utf-16-le")
4766                        except UnicodeDecodeError:
4767                            pass
4768
4769                    elif Backend.getIdentifiedDbms() in (DBMS.HSQLDB, DBMS.H2):
4770                        try:
4771                            retVal = retVal.decode("utf-16-be")
4772                        except UnicodeDecodeError:
4773                            pass
4774
4775                if not isinstance(retVal, six.text_type):
4776                    retVal = getUnicode(retVal, conf.encoding or UNICODE_ENCODING)
4777
4778        return retVal
4779
4780    try:
4781        retVal = applyFunctionRecursively(value, _)
4782    except:
4783        singleTimeWarnMessage("there was a problem decoding value '%s' from expected hexadecimal form" % value)
4784
4785    return retVal
4786
4787def extractExpectedValue(value, expected):
4788    """
4789    Extracts and returns expected value by a given type
4790
4791    >>> extractExpectedValue(['1'], EXPECTED.BOOL)
4792    True
4793    >>> extractExpectedValue('1', EXPECTED.INT)
4794    1
4795    """
4796
4797    if expected:
4798        value = unArrayizeValue(value)
4799
4800        if isNoneValue(value):
4801            value = None
4802        elif expected == EXPECTED.BOOL:
4803            if isinstance(value, int):
4804                value = bool(value)
4805            elif isinstance(value, six.string_types):
4806                value = value.strip().lower()
4807                if value in ("true", "false"):
4808                    value = value == "true"
4809                elif value in ('t', 'f'):
4810                    value = value == 't'
4811                elif value in ("1", "-1"):
4812                    value = True
4813                elif value == '0':
4814                    value = False
4815                else:
4816                    value = None
4817        elif expected == EXPECTED.INT:
4818            if isinstance(value, six.string_types):
4819                value = int(value) if value.isdigit() else None
4820
4821    return value
4822
4823def hashDBWrite(key, value, serialize=False):
4824    """
4825    Helper function for writing session data to HashDB
4826    """
4827
4828    if conf.hashDB:
4829        _ = '|'.join((str(_) if not isinstance(_, six.string_types) else _) for _ in (conf.hostname, conf.path.strip('/') if conf.path is not None else conf.port, key, HASHDB_MILESTONE_VALUE))
4830        conf.hashDB.write(_, value, serialize)
4831
4832def hashDBRetrieve(key, unserialize=False, checkConf=False):
4833    """
4834    Helper function for restoring session data from HashDB
4835    """
4836
4837    retVal = None
4838
4839    if conf.hashDB:
4840        _ = '|'.join((str(_) if not isinstance(_, six.string_types) else _) for _ in (conf.hostname, conf.path.strip('/') if conf.path is not None else conf.port, key, HASHDB_MILESTONE_VALUE))
4841        retVal = conf.hashDB.retrieve(_, unserialize) if kb.resumeValues and not (checkConf and any((conf.flushSession, conf.freshQueries))) else None
4842
4843        if not kb.inferenceMode and not kb.fileReadMode and isinstance(retVal, six.string_types) and any(_ in retVal for _ in (PARTIAL_VALUE_MARKER, PARTIAL_HEX_VALUE_MARKER)):
4844            retVal = None
4845
4846    return retVal
4847
4848def resetCookieJar(cookieJar):
4849    """
4850    Cleans cookies from a given cookie jar
4851    """
4852
4853    if not conf.loadCookies:
4854        cookieJar.clear()
4855    else:
4856        try:
4857            if not cookieJar.filename:
4858                infoMsg = "loading cookies from '%s'" % conf.loadCookies
4859                logger.info(infoMsg)
4860
4861                content = readCachedFileContent(conf.loadCookies)
4862                lines = filterNone(line.strip() for line in content.split("\n") if not line.startswith('#'))
4863                handle, filename = tempfile.mkstemp(prefix=MKSTEMP_PREFIX.COOKIE_JAR)
4864                os.close(handle)
4865
4866                # Reference: http://www.hashbangcode.com/blog/netscape-http-cooke-file-parser-php-584.html
4867                with openFile(filename, "w+b") as f:
4868                    f.write("%s\n" % NETSCAPE_FORMAT_HEADER_COOKIES)
4869                    for line in lines:
4870                        _ = line.split("\t")
4871                        if len(_) == 7:
4872                            _[4] = FORCE_COOKIE_EXPIRATION_TIME
4873                            f.write("\n%s" % "\t".join(_))
4874
4875                cookieJar.filename = filename
4876
4877            cookieJar.load(cookieJar.filename, ignore_expires=True)
4878
4879            for cookie in cookieJar:
4880                if cookie.expires < time.time():
4881                    warnMsg = "cookie '%s' has expired" % cookie
4882                    singleTimeWarnMessage(warnMsg)
4883
4884            cookieJar.clear_expired_cookies()
4885
4886            if not cookieJar._cookies:
4887                errMsg = "no valid cookies found"
4888                raise SqlmapGenericException(errMsg)
4889
4890        except Exception as ex:
4891            errMsg = "there was a problem loading "
4892            errMsg += "cookies file ('%s')" % re.sub(r"(cookies) file '[^']+'", r"\g<1>", getSafeExString(ex))
4893            raise SqlmapGenericException(errMsg)
4894
4895def decloakToTemp(filename):
4896    """
4897    Decloaks content of a given file to a temporary file with similar name and extension
4898
4899    >>> _ = decloakToTemp(os.path.join(paths.SQLMAP_SHELL_PATH, "stagers", "stager.asp_"))
4900    >>> openFile(_, "rb", encoding=None).read().startswith(b'<%')
4901    True
4902    >>> os.remove(_)
4903    """
4904
4905    content = decloak(filename)
4906
4907    parts = os.path.split(filename[:-1])[-1].split('.')
4908    prefix, suffix = parts[0], '.' + parts[-1]
4909    handle, filename = tempfile.mkstemp(prefix=prefix, suffix=suffix)
4910    os.close(handle)
4911
4912    with openFile(filename, "w+b", encoding=None) as f:
4913        f.write(content)
4914
4915    return filename
4916
4917def prioritySortColumns(columns):
4918    """
4919    Sorts given column names by length in ascending order while those containing
4920    string 'id' go first
4921
4922    >>> prioritySortColumns(['password', 'userid', 'name'])
4923    ['userid', 'name', 'password']
4924    """
4925
4926    def _(column):
4927        return column and re.search(r"^id|id$", column, re.I) is not None
4928
4929    return sorted(sorted(columns, key=len), key=functools.cmp_to_key(lambda x, y: -1 if _(x) and not _(y) else 1 if not _(x) and _(y) else 0))
4930
4931def getRequestHeader(request, name):
4932    """
4933    Solving an issue with an urllib2 Request header case sensitivity
4934
4935    # Reference: http://bugs.python.org/issue2275
4936    """
4937
4938    retVal = None
4939
4940    if request and request.headers and name:
4941        _ = name.upper()
4942        retVal = max(getBytes(value if _ == key.upper() else "") for key, value in request.header_items()) or None
4943
4944    return retVal
4945
4946def isNumber(value):
4947    """
4948    Returns True if the given value is a number-like object
4949
4950    >>> isNumber(1)
4951    True
4952    >>> isNumber('0')
4953    True
4954    >>> isNumber('foobar')
4955    False
4956    """
4957
4958    try:
4959        float(value)
4960    except:
4961        return False
4962    else:
4963        return True
4964
4965def zeroDepthSearch(expression, value):
4966    """
4967    Searches occurrences of value inside expression at 0-depth level
4968    regarding the parentheses
4969
4970    >>> _ = "SELECT (SELECT id FROM users WHERE 2>1) AS result FROM DUAL"; _[zeroDepthSearch(_, "FROM")[0]:]
4971    'FROM DUAL'
4972    >>> _ = "a(b; c),d;e"; _[zeroDepthSearch(_, "[;, ]")[0]:]
4973    ',d;e'
4974    """
4975
4976    retVal = []
4977
4978    depth = 0
4979    for index in xrange(len(expression)):
4980        if expression[index] == '(':
4981            depth += 1
4982        elif expression[index] == ')':
4983            depth -= 1
4984        elif depth == 0:
4985            if value.startswith('[') and value.endswith(']'):
4986                if re.search(value, expression[index:index + 1]):
4987                    retVal.append(index)
4988            elif expression[index:index + len(value)] == value:
4989                retVal.append(index)
4990
4991    return retVal
4992
4993def splitFields(fields, delimiter=','):
4994    """
4995    Returns list of (0-depth) fields splitted by delimiter
4996
4997    >>> splitFields('foo, bar, max(foo, bar)')
4998    ['foo', 'bar', 'max(foo,bar)']
4999    """
5000
5001    fields = fields.replace("%s " % delimiter, delimiter)
5002    commas = [-1, len(fields)]
5003    commas.extend(zeroDepthSearch(fields, ','))
5004    commas = sorted(commas)
5005
5006    return [fields[x + 1:y] for (x, y) in _zip(commas, commas[1:])]
5007
5008def pollProcess(process, suppress_errors=False):
5009    """
5010    Checks for process status (prints . if still running)
5011    """
5012
5013    while process:
5014        dataToStdout(".")
5015        time.sleep(1)
5016
5017        returncode = process.poll()
5018
5019        if returncode is not None:
5020            if not suppress_errors:
5021                if returncode == 0:
5022                    dataToStdout(" done\n")
5023                elif returncode < 0:
5024                    dataToStdout(" process terminated by signal %d\n" % returncode)
5025                elif returncode > 0:
5026                    dataToStdout(" quit unexpectedly with return code %d\n" % returncode)
5027
5028            break
5029
5030def parseRequestFile(reqFile, checkParams=True):
5031    """
5032    Parses WebScarab and Burp logs and adds results to the target URL list
5033    """
5034
5035    def _parseWebScarabLog(content):
5036        """
5037        Parses WebScarab logs (POST method not supported)
5038        """
5039
5040        reqResList = content.split(WEBSCARAB_SPLITTER)
5041
5042        for request in reqResList:
5043            url = extractRegexResult(r"URL: (?P<result>.+?)\n", request, re.I)
5044            method = extractRegexResult(r"METHOD: (?P<result>.+?)\n", request, re.I)
5045            cookie = extractRegexResult(r"COOKIE: (?P<result>.+?)\n", request, re.I)
5046
5047            if not method or not url:
5048                logger.debug("not a valid WebScarab log data")
5049                continue
5050
5051            if method.upper() == HTTPMETHOD.POST:
5052                warnMsg = "POST requests from WebScarab logs aren't supported "
5053                warnMsg += "as their body content is stored in separate files. "
5054                warnMsg += "Nevertheless you can use -r to load them individually."
5055                logger.warning(warnMsg)
5056                continue
5057
5058            if not(conf.scope and not re.search(conf.scope, url, re.I)):
5059                yield (url, method, None, cookie, tuple())
5060
5061    def _parseBurpLog(content):
5062        """
5063        Parses Burp logs
5064        """
5065
5066        if not re.search(BURP_REQUEST_REGEX, content, re.I | re.S):
5067            if re.search(BURP_XML_HISTORY_REGEX, content, re.I | re.S):
5068                reqResList = []
5069                for match in re.finditer(BURP_XML_HISTORY_REGEX, content, re.I | re.S):
5070                    port, request = match.groups()
5071                    try:
5072                        request = decodeBase64(request, binary=False)
5073                    except (binascii.Error, TypeError):
5074                        continue
5075                    _ = re.search(r"%s:.+" % re.escape(HTTP_HEADER.HOST), request)
5076                    if _:
5077                        host = _.group(0).strip()
5078                        if not re.search(r":\d+\Z", host):
5079                            request = request.replace(host, "%s:%d" % (host, int(port)))
5080                    reqResList.append(request)
5081            else:
5082                reqResList = [content]
5083        else:
5084            reqResList = re.finditer(BURP_REQUEST_REGEX, content, re.I | re.S)
5085
5086        for match in reqResList:
5087            request = match if isinstance(match, six.string_types) else match.group(1)
5088            request = re.sub(r"\A[^\w]+", "", request)
5089            schemePort = re.search(r"(http[\w]*)\:\/\/.*?\:([\d]+).+?={10,}", request, re.I | re.S)
5090
5091            if schemePort:
5092                scheme = schemePort.group(1)
5093                port = schemePort.group(2)
5094                request = re.sub(r"\n=+\Z", "", request.split(schemePort.group(0))[-1].lstrip())
5095            else:
5096                scheme, port = None, None
5097
5098            if "HTTP/" not in request:
5099                continue
5100
5101            if re.search(r"^[\n]*%s.*?\.(%s)\sHTTP\/" % (HTTPMETHOD.GET, "|".join(CRAWL_EXCLUDE_EXTENSIONS)), request, re.I | re.M):
5102                continue
5103
5104            getPostReq = False
5105            url = None
5106            host = None
5107            method = None
5108            data = None
5109            cookie = None
5110            params = False
5111            newline = None
5112            lines = request.split('\n')
5113            headers = []
5114
5115            for index in xrange(len(lines)):
5116                line = lines[index]
5117
5118                if not line.strip() and index == len(lines) - 1:
5119                    break
5120
5121                newline = "\r\n" if line.endswith('\r') else '\n'
5122                line = line.strip('\r')
5123                match = re.search(r"\A([A-Z]+) (.+) HTTP/[\d.]+\Z", line) if not method else None
5124
5125                if len(line.strip()) == 0 and method and method != HTTPMETHOD.GET and data is None:
5126                    data = ""
5127                    params = True
5128
5129                elif match:
5130                    method = match.group(1)
5131                    url = match.group(2)
5132
5133                    if any(_ in line for _ in ('?', '=', kb.customInjectionMark)):
5134                        params = True
5135
5136                    getPostReq = True
5137
5138                # POST parameters
5139                elif data is not None and params:
5140                    data += "%s%s" % (line, newline)
5141
5142                # GET parameters
5143                elif "?" in line and "=" in line and ": " not in line:
5144                    params = True
5145
5146                # Headers
5147                elif re.search(r"\A\S+:", line):
5148                    key, value = line.split(":", 1)
5149                    value = value.strip().replace("\r", "").replace("\n", "")
5150
5151                    # Cookie and Host headers
5152                    if key.upper() == HTTP_HEADER.COOKIE.upper():
5153                        cookie = value
5154                    elif key.upper() == HTTP_HEADER.HOST.upper():
5155                        if '://' in value:
5156                            scheme, value = value.split('://')[:2]
5157                        splitValue = value.split(":")
5158                        host = splitValue[0]
5159
5160                        if len(splitValue) > 1:
5161                            port = filterStringValue(splitValue[1], "[0-9]")
5162
5163                    # Avoid to add a static content length header to
5164                    # headers and consider the following lines as
5165                    # POSTed data
5166                    if key.upper() == HTTP_HEADER.CONTENT_LENGTH.upper():
5167                        params = True
5168
5169                    # Avoid proxy and connection type related headers
5170                    elif key not in (HTTP_HEADER.PROXY_CONNECTION, HTTP_HEADER.CONNECTION):
5171                        headers.append((getUnicode(key), getUnicode(value)))
5172
5173                    if kb.customInjectionMark in re.sub(PROBLEMATIC_CUSTOM_INJECTION_PATTERNS, "", value or ""):
5174                        params = True
5175
5176            data = data.rstrip("\r\n") if data else data
5177
5178            if getPostReq and (params or cookie or not checkParams):
5179                if not port and hasattr(scheme, "lower") and scheme.lower() == "https":
5180                    port = "443"
5181                elif not scheme and port == "443":
5182                    scheme = "https"
5183
5184                if conf.forceSSL:
5185                    scheme = "https"
5186                    port = port or "443"
5187
5188                if not host:
5189                    errMsg = "invalid format of a request file"
5190                    raise SqlmapSyntaxException(errMsg)
5191
5192                if not url.startswith("http"):
5193                    url = "%s://%s:%s%s" % (scheme or "http", host, port or "80", url)
5194                    scheme = None
5195                    port = None
5196
5197                if not(conf.scope and not re.search(conf.scope, url, re.I)):
5198                    yield (url, conf.method or method, data, cookie, tuple(headers))
5199
5200    content = readCachedFileContent(reqFile)
5201
5202    if conf.scope:
5203        logger.info("using regular expression '%s' for filtering targets" % conf.scope)
5204
5205    for target in _parseBurpLog(content):
5206        yield target
5207
5208    for target in _parseWebScarabLog(content):
5209        yield target
5210
5211def getSafeExString(ex, encoding=None):
5212    """
5213    Safe way how to get the proper exception represtation as a string
5214
5215    >>> getSafeExString(SqlmapBaseException('foobar')) == 'foobar'
5216    True
5217    >>> getSafeExString(OSError(0, 'foobar')) == 'OSError: foobar'
5218    True
5219    """
5220
5221    retVal = None
5222
5223    if getattr(ex, "message", None):
5224        retVal = ex.message
5225    elif getattr(ex, "msg", None):
5226        retVal = ex.msg
5227    elif getattr(ex, "args", None):
5228        for candidate in ex.args[::-1]:
5229            if isinstance(candidate, six.string_types):
5230                retVal = candidate
5231                break
5232
5233    if retVal is None:
5234        retVal = str(ex)
5235    elif not isinstance(ex, SqlmapBaseException):
5236        retVal = "%s: %s" % (type(ex).__name__, retVal)
5237
5238    return getUnicode(retVal or "", encoding=encoding).strip()
5239
5240def safeVariableNaming(value):
5241    """
5242    Returns escaped safe-representation of a given variable name that can be used in Python evaluated code
5243
5244    >>> safeVariableNaming("class.id") == "EVAL_636c6173732e6964"
5245    True
5246    """
5247
5248    if value in keyword.kwlist or re.search(r"\A[^a-zA-Z]|[^\w]", value):
5249        value = "%s%s" % (EVALCODE_ENCODED_PREFIX, getUnicode(binascii.hexlify(getBytes(value))))
5250
5251    return value
5252
5253def unsafeVariableNaming(value):
5254    """
5255    Returns unescaped safe-representation of a given variable name
5256
5257    >>> unsafeVariableNaming("EVAL_636c6173732e6964") == "class.id"
5258    True
5259    """
5260
5261    if value.startswith(EVALCODE_ENCODED_PREFIX):
5262        value = decodeHex(value[len(EVALCODE_ENCODED_PREFIX):], binary=False)
5263
5264    return value
5265
5266def firstNotNone(*args):
5267    """
5268    Returns first not-None value from a given list of arguments
5269
5270    >>> firstNotNone(None, None, 1, 2, 3)
5271    1
5272    """
5273
5274    retVal = None
5275
5276    for _ in args:
5277        if _ is not None:
5278            retVal = _
5279            break
5280
5281    return retVal
5282
5283def removePostHintPrefix(value):
5284    """
5285    Remove POST hint prefix from a given value (name)
5286
5287    >>> removePostHintPrefix("JSON id")
5288    'id'
5289    >>> removePostHintPrefix("id")
5290    'id'
5291    """
5292
5293    return re.sub(r"\A(%s) " % '|'.join(re.escape(__) for __ in getPublicTypeMembers(POST_HINT, onlyValues=True)), "", value)
5294
5295def chunkSplitPostData(data):
5296    """
5297    Convert POST data to chunked transfer-encoded data (Note: splitting done by SQL keywords)
5298
5299    >>> random.seed(0)
5300    >>> chunkSplitPostData("SELECT username,password FROM users")
5301    '5;4Xe90\\r\\nSELEC\\r\\n3;irWlc\\r\\nT u\\r\\n1;eT4zO\\r\\ns\\r\\n5;YB4hM\\r\\nernam\\r\\n9;2pUD8\\r\\ne,passwor\\r\\n3;mp07y\\r\\nd F\\r\\n5;8RKXi\\r\\nROM u\\r\\n4;MvMhO\\r\\nsers\\r\\n0\\r\\n\\r\\n'
5302    """
5303
5304    length = len(data)
5305    retVal = ""
5306    index = 0
5307
5308    while index < length:
5309        chunkSize = randomInt(1)
5310
5311        if index + chunkSize >= length:
5312            chunkSize = length - index
5313
5314        salt = randomStr(5, alphabet=string.ascii_letters + string.digits)
5315
5316        while chunkSize:
5317            candidate = data[index:index + chunkSize]
5318
5319            if re.search(r"\b%s\b" % '|'.join(HTTP_CHUNKED_SPLIT_KEYWORDS), candidate, re.I):
5320                chunkSize -= 1
5321            else:
5322                break
5323
5324        index += chunkSize
5325        retVal += "%x;%s\r\n" % (chunkSize, salt)
5326        retVal += "%s\r\n" % candidate
5327
5328    retVal += "0\r\n\r\n"
5329
5330    return retVal
5331