/lib/core/common.py
Python | 3761 lines | 3710 code | 30 blank | 21 comment | 58 complexity | e572b5a1c3c0ef9cfd185eac7ff847c9 MD5 | raw file
Possible License(s): GPL-2.0, BSD-3-Clause
Large files files are truncated, but you can click here to view the full file
- #!/usr/bin/env python
- """
- Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/)
- See the file 'doc/COPYING' for copying permission
- """
- import codecs
- import contextlib
- import cookielib
- import copy
- import httplib
- import inspect
- import logging
- import ntpath
- import os
- import posixpath
- import random
- import re
- import socket
- import string
- import sys
- import tempfile
- import time
- import urllib
- import urlparse
- import unicodedata
- from ConfigParser import DEFAULTSECT
- from ConfigParser import RawConfigParser
- from StringIO import StringIO
- from difflib import SequenceMatcher
- from math import sqrt
- from optparse import OptionValueError
- from subprocess import PIPE
- from subprocess import Popen as execute
- from xml.dom import minidom
- from xml.sax import parse
- from extra.cloak.cloak import decloak
- from extra.safe2bin.safe2bin import safecharencode
- from lib.core.bigarray import BigArray
- from lib.core.data import conf
- from lib.core.data import kb
- from lib.core.data import logger
- from lib.core.data import paths
- from lib.core.convert import base64pickle
- from lib.core.convert import base64unpickle
- from lib.core.convert import hexdecode
- from lib.core.convert import htmlunescape
- from lib.core.convert import stdoutencode
- from lib.core.convert import unicodeencode
- from lib.core.convert import utf8encode
- from lib.core.decorators import cachedmethod
- from lib.core.defaults import defaults
- from lib.core.dicts import DBMS_DICT
- from lib.core.dicts import DEFAULT_DOC_ROOTS
- from lib.core.dicts import DEPRECATED_OPTIONS
- from lib.core.dicts import SQL_STATEMENTS
- from lib.core.enums import ADJUST_TIME_DELAY
- from lib.core.enums import CONTENT_STATUS
- from lib.core.enums import CHARSET_TYPE
- from lib.core.enums import DBMS
- from lib.core.enums import EXPECTED
- from lib.core.enums import HEURISTIC_TEST
- from lib.core.enums import HTTP_HEADER
- from lib.core.enums import HTTPMETHOD
- from lib.core.enums import OS
- from lib.core.enums import PLACE
- from lib.core.enums import PAYLOAD
- from lib.core.enums import REFLECTIVE_COUNTER
- from lib.core.enums import SORT_ORDER
- from lib.core.exception import SqlmapDataException
- from lib.core.exception import SqlmapFilePathException
- from lib.core.exception import SqlmapGenericException
- from lib.core.exception import SqlmapNoneDataException
- from lib.core.exception import SqlmapMissingDependence
- from lib.core.exception import SqlmapSilentQuitException
- from lib.core.exception import SqlmapSyntaxException
- from lib.core.exception import SqlmapUserQuitException
- from lib.core.log import LOGGER_HANDLER
- from lib.core.optiondict import optDict
- from lib.core.settings import BOLD_PATTERNS
- from lib.core.settings import BRUTE_DOC_ROOT_PREFIXES
- from lib.core.settings import BRUTE_DOC_ROOT_SUFFIXES
- from lib.core.settings import BRUTE_DOC_ROOT_TARGET_MARK
- from lib.core.settings import CUSTOM_INJECTION_MARK_CHAR
- from lib.core.settings import DBMS_DIRECTORY_DICT
- from lib.core.settings import DEFAULT_COOKIE_DELIMITER
- from lib.core.settings import DEFAULT_GET_POST_DELIMITER
- from lib.core.settings import DEFAULT_MSSQL_SCHEMA
- from lib.core.settings import DESCRIPTION
- from lib.core.settings import DUMMY_SQL_INJECTION_CHARS
- from lib.core.settings import DUMMY_USER_INJECTION
- from lib.core.settings import DYNAMICITY_MARK_LENGTH
- from lib.core.settings import ERROR_PARSING_REGEXES
- from lib.core.settings import FORCE_COOKIE_EXPIRATION_TIME
- from lib.core.settings import FORM_SEARCH_REGEX
- from lib.core.settings import GENERIC_DOC_ROOT_DIRECTORY_NAMES
- from lib.core.settings import GOOGLE_ANALYTICS_COOKIE_PREFIX
- from lib.core.settings import HASHDB_MILESTONE_VALUE
- from lib.core.settings import HOST_ALIASES
- from lib.core.settings import INFERENCE_UNKNOWN_CHAR
- from lib.core.settings import INVALID_UNICODE_CHAR_FORMAT
- from lib.core.settings import IP_ADDRESS_REGEX
- from lib.core.settings import ISSUES_PAGE
- from lib.core.settings import IS_WIN
- from lib.core.settings import LARGE_OUTPUT_THRESHOLD
- from lib.core.settings import MIN_ENCODED_LEN_CHECK
- from lib.core.settings import MIN_TIME_RESPONSES
- from lib.core.settings import MIN_VALID_DELAYED_RESPONSE
- from lib.core.settings import ML
- from lib.core.settings import NETSCAPE_FORMAT_HEADER_COOKIES
- from lib.core.settings import NULL
- from lib.core.settings import PARAMETER_AMP_MARKER
- from lib.core.settings import PARAMETER_SEMICOLON_MARKER
- from lib.core.settings import PARTIAL_HEX_VALUE_MARKER
- from lib.core.settings import PARTIAL_VALUE_MARKER
- from lib.core.settings import PAYLOAD_DELIMITER
- from lib.core.settings import PLATFORM
- from lib.core.settings import PRINTABLE_CHAR_REGEX
- from lib.core.settings import PYVERSION
- from lib.core.settings import REFERER_ALIASES
- from lib.core.settings import REFLECTED_BORDER_REGEX
- from lib.core.settings import REFLECTED_MAX_REGEX_PARTS
- from lib.core.settings import REFLECTED_REPLACEMENT_REGEX
- from lib.core.settings import REFLECTED_VALUE_MARKER
- from lib.core.settings import REFLECTIVE_MISS_THRESHOLD
- from lib.core.settings import REVISION
- from lib.core.settings import SENSITIVE_DATA_REGEX
- from lib.core.settings import SITE
- from lib.core.settings import SUPPORTED_DBMS
- from lib.core.settings import TEXT_TAG_REGEX
- from lib.core.settings import TIME_STDEV_COEFF
- from lib.core.settings import UNICODE_ENCODING
- from lib.core.settings import UNKNOWN_DBMS_VERSION
- from lib.core.settings import URI_QUESTION_MARKER
- from lib.core.settings import URLENCODE_CHAR_LIMIT
- from lib.core.settings import URLENCODE_FAILSAFE_CHARS
- from lib.core.settings import USER_AGENT_ALIASES
- from lib.core.settings import VERSION
- from lib.core.settings import VERSION_STRING
- from lib.core.threads import getCurrentThreadData
- from lib.utils.sqlalchemy import _sqlalchemy
- from thirdparty.clientform.clientform import ParseResponse
- from thirdparty.clientform.clientform import ParseError
- from thirdparty.magic import magic
- from thirdparty.odict.odict import OrderedDict
- from thirdparty.termcolor.termcolor import colored
- class UnicodeRawConfigParser(RawConfigParser):
- """
- RawConfigParser with unicode writing support
- """
- def write(self, fp):
- """
- Write an .ini-format representation of the configuration state.
- """
- if self._defaults:
- fp.write("[%s]\n" % DEFAULTSECT)
- for (key, value) in self._defaults.items():
- fp.write("%s = %s\n" % (key, getUnicode(value, UNICODE_ENCODING).replace('\n', '\n\t')))
- fp.write("\n")
- for section in self._sections:
- fp.write("[%s]\n" % section)
- for (key, value) in self._sections[section].items():
- if key != "__name__":
- if value is None:
- fp.write("%s\n" % (key))
- else:
- fp.write("%s = %s\n" % (key, getUnicode(value, UNICODE_ENCODING).replace('\n', '\n\t')))
- fp.write("\n")
- class Format(object):
- @staticmethod
- def humanize(values, chain=" or "):
- return chain.join(values)
- # Get methods
- @staticmethod
- def getDbms(versions=None):
- """
- Format the back-end DBMS fingerprint value and return its
- values formatted as a human readable string.
- @return: detected back-end DBMS based upon fingerprint techniques.
- @rtype: C{str}
- """
- if versions is None and Backend.getVersionList():
- versions = Backend.getVersionList()
- return Backend.getDbms() if versions is None else "%s %s" % (Backend.getDbms(), " and ".join(v for v in versions))
- @staticmethod
- def getErrorParsedDBMSes():
- """
- Parses the knowledge base htmlFp list and return its values
- formatted as a human readable string.
- @return: list of possible back-end DBMS based upon error messages
- parsing.
- @rtype: C{str}
- """
- htmlParsed = None
- if len(kb.htmlFp) == 0 or kb.heuristicTest != HEURISTIC_TEST.POSITIVE:
- pass
- elif len(kb.htmlFp) == 1:
- htmlParsed = kb.htmlFp[0]
- elif len(kb.htmlFp) > 1:
- htmlParsed = " or ".join(kb.htmlFp)
- return htmlParsed
- @staticmethod
- def getOs(target, info):
- """
- Formats the back-end operating system fingerprint value
- and return its values formatted as a human readable string.
- Example of info (kb.headersFp) dictionary:
- {
- 'distrib': set(['Ubuntu']),
- 'type': set(['Linux']),
- 'technology': set(['PHP 5.2.6', 'Apache 2.2.9']),
- 'release': set(['8.10'])
- }
- Example of info (kb.bannerFp) dictionary:
- {
- 'sp': set(['Service Pack 4']),
- 'dbmsVersion': '8.00.194',
- 'dbmsServicePack': '0',
- 'distrib': set(['2000']),
- 'dbmsRelease': '2000',
- 'type': set(['Windows'])
- }
- @return: detected back-end operating system based upon fingerprint
- techniques.
- @rtype: C{str}
- """
- infoStr = ""
- infoApi = {}
- if info and "type" in info:
- if hasattr(conf, "api"):
- infoApi["%s operating system" % target] = info
- else:
- infoStr += "%s operating system: %s" % (target, Format.humanize(info["type"]))
- if "distrib" in info:
- infoStr += " %s" % Format.humanize(info["distrib"])
- if "release" in info:
- infoStr += " %s" % Format.humanize(info["release"])
- if "sp" in info:
- infoStr += " %s" % Format.humanize(info["sp"])
- if "codename" in info:
- infoStr += " (%s)" % Format.humanize(info["codename"])
- if "technology" in info:
- if hasattr(conf, "api"):
- infoApi["web application technology"] = Format.humanize(info["technology"], ", ")
- else:
- infoStr += "\nweb application technology: %s" % Format.humanize(info["technology"], ", ")
- if hasattr(conf, "api"):
- return infoApi
- else:
- return infoStr.lstrip()
- class Backend:
- # Set methods
- @staticmethod
- def setDbms(dbms):
- dbms = aliasToDbmsEnum(dbms)
- if dbms is None:
- return None
- # Little precaution, in theory this condition should always be false
- elif kb.dbms is not None and kb.dbms != dbms:
- warnMsg = "there seems to be a high probability that "
- warnMsg += "this could be a false positive case"
- logger.warn(warnMsg)
- msg = "sqlmap previously fingerprinted back-end DBMS as "
- msg += "%s. However now it has been fingerprinted " % kb.dbms
- msg += "as %s. " % dbms
- msg += "Please, specify which DBMS should be "
- msg += "correct [%s (default)/%s] " % (kb.dbms, dbms)
- while True:
- _ = readInput(msg, default=kb.dbms)
- if aliasToDbmsEnum(_) == kb.dbms:
- break
- elif aliasToDbmsEnum(_) == dbms:
- kb.dbms = aliasToDbmsEnum(_)
- break
- else:
- warnMsg = "invalid value"
- logger.warn(warnMsg)
- elif kb.dbms is None:
- kb.dbms = aliasToDbmsEnum(dbms)
- return kb.dbms
- @staticmethod
- def setVersion(version):
- if isinstance(version, basestring):
- kb.dbmsVersion = [version]
- return kb.dbmsVersion
- @staticmethod
- def setVersionList(versionsList):
- if isinstance(versionsList, list):
- kb.dbmsVersion = versionsList
- elif isinstance(versionsList, basestring):
- Backend.setVersion(versionsList)
- else:
- logger.error("invalid format of versionsList")
- @staticmethod
- def forceDbms(dbms, sticky=False):
- if not kb.stickyDBMS:
- kb.forcedDbms = aliasToDbmsEnum(dbms)
- kb.stickyDBMS = sticky
- @staticmethod
- def flushForcedDbms(force=False):
- if not kb.stickyDBMS or force:
- kb.forcedDbms = None
- kb.stickyDBMS = False
- @staticmethod
- def setOs(os):
- if os is None:
- return None
- # Little precaution, in theory this condition should always be false
- elif kb.os is not None and isinstance(os, basestring) and kb.os.lower() != os.lower():
- msg = "sqlmap previously fingerprinted back-end DBMS "
- msg += "operating system %s. However now it has " % kb.os
- msg += "been fingerprinted to be %s. " % os
- msg += "Please, specify which OS is "
- msg += "correct [%s (default)/%s] " % (kb.os, os)
- while True:
- _ = readInput(msg, default=kb.os)
- if _ == kb.os:
- break
- elif _ == os:
- kb.os = _.capitalize()
- break
- else:
- warnMsg = "invalid value"
- logger.warn(warnMsg)
- elif kb.os is None and isinstance(os, basestring):
- kb.os = os.capitalize()
- return kb.os
- @staticmethod
- def setOsVersion(version):
- if version is None:
- return None
- elif kb.osVersion is None and isinstance(version, basestring):
- kb.osVersion = version
- @staticmethod
- def setOsServicePack(sp):
- if sp is None:
- return None
- elif kb.osSP is None and isinstance(sp, int):
- kb.osSP = sp
- @staticmethod
- def setArch():
- msg = "what is the back-end database management system architecture?"
- msg += "\n[1] 32-bit (default)"
- msg += "\n[2] 64-bit"
- while True:
- _ = readInput(msg, default='1')
- if isinstance(_, basestring) and _.isdigit() and int(_) in (1, 2):
- kb.arch = 32 if int(_) == 1 else 64
- break
- else:
- warnMsg = "invalid value. Valid values are 1 and 2"
- logger.warn(warnMsg)
- return kb.arch
- # Get methods
- @staticmethod
- def getForcedDbms():
- return aliasToDbmsEnum(kb.get("forcedDbms"))
- @staticmethod
- def getDbms():
- return aliasToDbmsEnum(kb.get("dbms"))
- @staticmethod
- def getErrorParsedDBMSes():
- """
- Returns array with parsed DBMS names till now
- This functions is called to:
- 1. Sort the tests, getSortedInjectionTests() - detection phase.
- 2. Ask user whether or not skip specific DBMS tests in detection phase,
- lib/controller/checks.py - detection phase.
- 3. Sort the fingerprint of the DBMS, lib/controller/handler.py -
- fingerprint phase.
- """
- return kb.htmlFp if kb.get("heuristicTest") == HEURISTIC_TEST.POSITIVE else []
- @staticmethod
- def getIdentifiedDbms():
- dbms = None
- if not kb:
- pass
- elif Backend.getForcedDbms() is not None:
- dbms = Backend.getForcedDbms()
- elif Backend.getDbms() is not None:
- dbms = kb.dbms
- elif conf.get("dbms"):
- dbms = conf.dbms
- elif Backend.getErrorParsedDBMSes():
- dbms = unArrayizeValue(Backend.getErrorParsedDBMSes())
- elif kb.get("injection") and kb.injection.dbms:
- dbms = unArrayizeValue(kb.injection.dbms)
- return aliasToDbmsEnum(dbms)
- @staticmethod
- def getVersion():
- if len(kb.dbmsVersion) > 0:
- return kb.dbmsVersion[0]
- else:
- return None
- @staticmethod
- def getVersionList():
- if len(kb.dbmsVersion) > 0:
- return kb.dbmsVersion
- else:
- return None
- @staticmethod
- def getOs():
- return kb.os
- @staticmethod
- def getOsVersion():
- return kb.osVersion
- @staticmethod
- def getOsServicePack():
- return kb.osSP
- @staticmethod
- def getArch():
- if kb.arch is None:
- Backend.setArch()
- return kb.arch
- # Comparison methods
- @staticmethod
- def isDbms(dbms):
- if Backend.getDbms() is not None:
- return Backend.getDbms() == aliasToDbmsEnum(dbms)
- else:
- return Backend.getIdentifiedDbms() == aliasToDbmsEnum(dbms)
- @staticmethod
- def isDbmsWithin(aliases):
- return Backend.getDbms() is not None and Backend.getDbms().lower() in aliases
- @staticmethod
- def isVersion(version):
- return Backend.getVersion() is not None and Backend.getVersion() == version
- @staticmethod
- def isVersionWithin(versionList):
- if Backend.getVersionList() is None:
- return False
- for _ in Backend.getVersionList():
- if _ != UNKNOWN_DBMS_VERSION and _ in versionList:
- return True
- return False
- @staticmethod
- def isVersionGreaterOrEqualThan(version):
- return Backend.getVersion() is not None and str(Backend.getVersion()) >= str(version)
- @staticmethod
- def isOs(os):
- return Backend.getOs() is not None and Backend.getOs().lower() == os.lower()
- def paramToDict(place, parameters=None):
- """
- Split the parameters into names and values, check if these parameters
- are within the testable parameters and return in a dictionary.
- """
- testableParameters = OrderedDict()
- if place in conf.parameters and not parameters:
- parameters = conf.parameters[place]
- parameters = parameters.replace(", ", ",")
- parameters = re.sub(r"&(\w{1,4});", r"%s\g<1>%s" % (PARAMETER_AMP_MARKER, PARAMETER_SEMICOLON_MARKER), parameters)
- if place == PLACE.COOKIE:
- splitParams = parameters.split(conf.cookieDel or DEFAULT_COOKIE_DELIMITER)
- else:
- splitParams = parameters.split(conf.paramDel or DEFAULT_GET_POST_DELIMITER)
- for element in splitParams:
- element = re.sub(r"%s(.+?)%s" % (PARAMETER_AMP_MARKER, PARAMETER_SEMICOLON_MARKER), r"&\g<1>;", element)
- parts = element.split("=")
- if len(parts) >= 2:
- parameter = parts[0].replace(" ", "")
- if conf.paramDel and conf.paramDel == '\n':
- parts[-1] = parts[-1].rstrip()
- condition = not conf.testParameter
- condition |= parameter in conf.testParameter
- condition |= place == PLACE.COOKIE and len(intersect((PLACE.COOKIE,), conf.testParameter, True)) > 0
- if condition:
- testableParameters[parameter] = "=".join(parts[1:])
- if not conf.multipleTargets:
- _ = urldecode(testableParameters[parameter], convall=True)
- if (_.strip(DUMMY_SQL_INJECTION_CHARS) != _\
- or re.search(r'\A9{3,}', _) or re.search(DUMMY_USER_INJECTION, _))\
- and not parameter.upper().startswith(GOOGLE_ANALYTICS_COOKIE_PREFIX):
- warnMsg = "it appears that you have provided tainted parameter values "
- warnMsg += "('%s') with most probably leftover " % element
- warnMsg += "chars/statements from manual SQL injection test(s). "
- warnMsg += "Please, always use only valid parameter values "
- warnMsg += "so sqlmap could be able to run properly"
- logger.warn(warnMsg)
- message = "are you sure you want to continue? [y/N] "
- test = readInput(message, default="N")
- if test[0] not in ("y", "Y"):
- raise SqlmapSilentQuitException
- if conf.testParameter and not testableParameters:
- paramStr = ", ".join(test for test in conf.testParameter)
- if len(conf.testParameter) > 1:
- warnMsg = "provided parameters '%s' " % paramStr
- warnMsg += "are not inside the %s" % place
- logger.warn(warnMsg)
- else:
- parameter = conf.testParameter[0]
- if not intersect(USER_AGENT_ALIASES + REFERER_ALIASES + HOST_ALIASES, parameter, True):
- debugMsg = "provided parameter '%s' " % paramStr
- debugMsg += "is not inside the %s" % place
- logger.debug(debugMsg)
- elif len(conf.testParameter) != len(testableParameters.keys()):
- for parameter in conf.testParameter:
- if parameter not in testableParameters:
- debugMsg = "provided parameter '%s' " % parameter
- debugMsg += "is not inside the %s" % place
- logger.debug(debugMsg)
- if testableParameters:
- for parameter, value in testableParameters.items():
- if value and not value.isdigit():
- for encoding in ("hex", "base64"):
- try:
- decoded = value.decode(encoding)
- if len(decoded) > MIN_ENCODED_LEN_CHECK and all(_ in string.printable for _ in decoded):
- warnMsg = "provided parameter '%s' " % parameter
- warnMsg += "seems to be '%s' encoded" % encoding
- logger.warn(warnMsg)
- break
- except:
- pass
- return testableParameters
- def getManualDirectories():
- directories = None
- pagePath = directoryPath(conf.path)
- defaultDocRoot = DEFAULT_DOC_ROOTS.get(Backend.getOs(), DEFAULT_DOC_ROOTS[OS.LINUX])
- if kb.absFilePaths:
- for absFilePath in kb.absFilePaths:
- if directories:
- break
- if directoryPath(absFilePath) == '/':
- continue
- absFilePath = normalizePath(absFilePath)
- windowsDriveLetter = None
- if isWindowsDriveLetterPath(absFilePath):
- windowsDriveLetter, absFilePath = absFilePath[:2], absFilePath[2:]
- absFilePath = ntToPosixSlashes(posixToNtSlashes(absFilePath))
- if any("/%s/" % _ in absFilePath for _ in GENERIC_DOC_ROOT_DIRECTORY_NAMES):
- for _ in GENERIC_DOC_ROOT_DIRECTORY_NAMES:
- _ = "/%s/" % _
- if _ in absFilePath:
- directories = "%s%s" % (absFilePath.split(_)[0], _)
- break
- if pagePath and pagePath in absFilePath:
- directories = absFilePath.split(pagePath)[0]
- if windowsDriveLetter:
- directories = "%s/%s" % (windowsDriveLetter, ntToPosixSlashes(directories))
- directories = normalizePath(directories)
- if directories:
- infoMsg = "retrieved the web server document root: '%s'" % directories
- logger.info(infoMsg)
- else:
- warnMsg = "unable to retrieve automatically the web server "
- warnMsg += "document root"
- logger.warn(warnMsg)
- directories = []
- message = "what do you want to use for writable directory?\n"
- message += "[1] common location(s) '%s' (default)\n" % ", ".join(root for root in defaultDocRoot)
- message += "[2] custom location(s)\n"
- message += "[3] custom directory list file\n"
- message += "[4] brute force search\n"
- choice = readInput(message, default="1").strip()
- if choice == "2":
- message = "please provide a comma separate list of absolute directory paths: "
- directories = readInput(message, default="").split(',')
- elif choice == "3":
- message = "what's the list file location?\n"
- listPath = readInput(message, default="")
- checkFile(listPath)
- directories = getFileItems(listPath)
- elif choice == "4":
- targets = set([conf.hostname])
- _ = conf.hostname.split('.')
- if _[0] == "www":
- targets.add('.'.join(_[1:]))
- targets.add('.'.join(_[1:-1]))
- else:
- targets.add('.'.join(_[:-1]))
- targets = filter(None, targets)
- for prefix in BRUTE_DOC_ROOT_PREFIXES.get(Backend.getOs(), DEFAULT_DOC_ROOTS[OS.LINUX]):
- if BRUTE_DOC_ROOT_TARGET_MARK in prefix and re.match(IP_ADDRESS_REGEX, conf.hostname):
- continue
- for suffix in BRUTE_DOC_ROOT_SUFFIXES:
- for target in targets:
- item = "%s/%s" % (prefix, suffix)
- item = item.replace(BRUTE_DOC_ROOT_TARGET_MARK, target).replace("//", '/').rstrip('/')
- directories.append(item)
- if BRUTE_DOC_ROOT_TARGET_MARK not in prefix:
- break
- infoMsg = "using common directories: %s" % ','.join(directories)
- logger.info(infoMsg)
- msg = "use additional custom directories [Enter for None]: "
- answer = readInput(msg)
- if answer:
- directories.extend(answer.split(','))
- else:
- directories = defaultDocRoot
- return directories
- def getAutoDirectories():
- retVal = set("/")
- if kb.absFilePaths:
- infoMsg = "retrieved web server absolute paths: "
- infoMsg += "'%s'" % ", ".join(ntToPosixSlashes(path) for path in kb.absFilePaths)
- logger.info(infoMsg)
- for absFilePath in kb.absFilePaths:
- if absFilePath:
- directory = directoryPath(absFilePath)
- directory = ntToPosixSlashes(directory)
- retVal.add(directory)
- else:
- warnMsg = "unable to retrieve automatically any web server path"
- logger.warn(warnMsg)
- _ = extractRegexResult(r"//[^/]+?(?P<result>/.*)/", conf.url) # web directory
- if _:
- retVal.add(_)
- return list(retVal)
- def filePathToSafeString(filePath):
- """
- Returns string representation of a given filepath safe for a single filename usage
- >>> filePathToSafeString('C:/Windows/system32')
- 'C__Windows_system32'
- """
- retVal = filePath.replace("/", "_").replace("\\", "_")
- retVal = retVal.replace(" ", "_").replace(":", "_")
- return retVal
- def singleTimeDebugMessage(message):
- singleTimeLogMessage(message, logging.DEBUG)
- def singleTimeWarnMessage(message):
- singleTimeLogMessage(message, logging.WARN)
- def singleTimeLogMessage(message, level=logging.INFO, flag=None):
- if flag is None:
- flag = hash(message)
- if not conf.smokeTest and flag not in kb.singleLogFlags:
- kb.singleLogFlags.add(flag)
- logger.log(level, message)
- def boldifyMessage(message):
- retVal = message
- if any(_ in message for _ in BOLD_PATTERNS):
- retVal = setColor(message, True)
- return retVal
- def setColor(message, bold=False):
- retVal = message
- level = extractRegexResult(r"\[(?P<result>[A-Z ]+)\]", message) or kb.get("stickyLevel")
- if message and getattr(LOGGER_HANDLER, "is_tty", False): # colorizing handler
- if bold:
- retVal = colored(message, color=None, on_color=None, attrs=("bold",))
- elif level:
- level = getattr(logging, level, None) if isinstance(level, basestring) else level
- _ = LOGGER_HANDLER.level_map.get(level)
- if _:
- background, foreground, bold = _
- retVal = colored(message, color=foreground, on_color="on_%s" % background if background else None, attrs=("bold",) if bold else None)
- kb.stickyLevel = level if message and message[-1] != "\n" else None
- return retVal
- def dataToStdout(data, forceOutput=False, bold=False, content_type=None, status=CONTENT_STATUS.IN_PROGRESS):
- """
- Writes text to the stdout (console) stream
- """
- message = ""
- if not kb.get("threadException"):
- if forceOutput or not getCurrentThreadData().disableStdOut:
- if kb.get("multiThreadMode"):
- logging._acquireLock()
- if isinstance(data, unicode):
- message = stdoutencode(data)
- else:
- message = data
- if hasattr(conf, "api"):
- sys.stdout.write(message, status, content_type)
- else:
- sys.stdout.write(setColor(message, bold))
- try:
- sys.stdout.flush()
- except IOError:
- pass
- if kb.get("multiThreadMode"):
- logging._releaseLock()
- kb.prependFlag = isinstance(data, basestring) and (len(data) == 1 and data not in ('\n', '\r') or len(data) > 2 and data[0] == '\r' and data[-1] != '\n')
- def dataToTrafficFile(data):
- if not conf.trafficFile:
- return
- try:
- conf.trafficFP.write(data)
- conf.trafficFP.flush()
- except IOError, ex:
- errMsg = "something went wrong while trying "
- errMsg += "to write to the traffic file '%s' ('%s')" % (conf.trafficFile, ex)
- raise SqlmapGenericException(errMsg)
- def dataToDumpFile(dumpFile, data):
- dumpFile.write(data)
- dumpFile.flush()
- def dataToOutFile(filename, data):
- retVal = None
- if data:
- retVal = os.path.join(conf.filePath, filePathToSafeString(filename))
- with codecs.open(retVal, "wb", UNICODE_ENCODING) as f:
- f.write(data)
- return retVal
- def readInput(message, default=None, checkBatch=True):
- """
- Reads input from terminal
- """
- retVal = None
- kb.stickyLevel = None
- message = getUnicode(message)
- if "\n" in message:
- message += "%s> " % ("\n" if message.count("\n") > 1 else "")
- elif message[-1] == ']':
- message += " "
- if kb.prependFlag:
- message = "\n%s" % message
- kb.prependFlag = False
- if conf.answers:
- for item in conf.answers.split(','):
- question = item.split('=')[0].strip()
- answer = item.split('=')[1] if len(item.split('=')) > 1 else None
- if answer and question.lower() in message.lower():
- retVal = getUnicode(answer, UNICODE_ENCODING)
- infoMsg = "%s%s" % (message, retVal)
- logger.info(infoMsg)
- debugMsg = "used the given answer"
- logger.debug(debugMsg)
- break
- if retVal is None:
- if checkBatch and conf.batch:
- if isListLike(default):
- options = ",".join(getUnicode(opt, UNICODE_ENCODING) for opt in default)
- elif default:
- options = getUnicode(default, UNICODE_ENCODING)
- else:
- options = unicode()
- dataToStdout("\r%s%s\n" % (message, options), forceOutput=True, bold=True)
- debugMsg = "used the default behaviour, running in batch mode"
- logger.debug(debugMsg)
- retVal = default
- else:
- logging._acquireLock()
- dataToStdout("\r%s" % message, forceOutput=True, bold=True)
- kb.prependFlag = False
- try:
- retVal = raw_input() or default
- retVal = getUnicode(retVal, system=True) if retVal else retVal
- except:
- time.sleep(0.05) # Reference: http://www.gossamer-threads.com/lists/python/python/781893
- kb.prependFlag = True
- raise SqlmapUserQuitException
- finally:
- logging._releaseLock()
- return retVal
- def randomRange(start=0, stop=1000, seed=None):
- """
- Returns random integer value in given range
- >>> random.seed(0)
- >>> randomRange(1, 500)
- 423
- """
- randint = random.WichmannHill(seed).randint if seed is not None else random.randint
- return int(randint(start, stop))
- def randomInt(length=4, seed=None):
- """
- Returns random integer value with provided number of digits
- >>> random.seed(0)
- >>> randomInt(6)
- 874254
- """
- choice = random.WichmannHill(seed).choice if seed is not None else random.choice
- return int("".join(choice(string.digits if _ != 0 else string.digits.replace('0', '')) for _ in xrange(0, length)))
- def randomStr(length=4, lowercase=False, alphabet=None, seed=None):
- """
- Returns random string value with provided number of characters
- >>> random.seed(0)
- >>> randomStr(6)
- 'RNvnAv'
- """
- choice = random.WichmannHill(seed).choice if seed is not None else random.choice
- if alphabet:
- retVal = "".join(choice(alphabet) for _ in xrange(0, length))
- elif lowercase:
- retVal = "".join(choice(string.ascii_lowercase) for _ in xrange(0, length))
- else:
- retVal = "".join(choice(string.ascii_letters) for _ in xrange(0, length))
- return retVal
- def sanitizeStr(value):
- """
- Sanitizes string value in respect to newline and line-feed characters
- >>> sanitizeStr('foo\\n\\rbar')
- u'foo bar'
- """
- return getUnicode(value).replace("\n", " ").replace("\r", "")
- def checkFile(filename):
- """
- Checks for file existence
- """
- if not os.path.isfile(filename):
- raise SqlmapFilePathException("unable to read file '%s'" % filename)
- def banner():
- """
- This function prints sqlmap banner with its version
- """
- _ = """\n %s - %s\n %s\n\n""" % (VERSION_STRING, DESCRIPTION, SITE)
- dataToStdout(_, forceOutput=True)
- def parsePasswordHash(password):
- """
- In case of Microsoft SQL Server password hash value is expanded to its components
- """
- blank = " " * 8
- if not password or password == " ":
- password = NULL
- if Backend.isDbms(DBMS.MSSQL) and password != NULL and isHexEncodedString(password):
- hexPassword = password
- password = "%s\n" % hexPassword
- password += "%sheader: %s\n" % (blank, hexPassword[:6])
- password += "%ssalt: %s\n" % (blank, hexPassword[6:14])
- password += "%smixedcase: %s\n" % (blank, hexPassword[14:54])
- if not Backend.isVersionWithin(("2005", "2008")):
- password += "%suppercase: %s" % (blank, hexPassword[54:])
- return password
- def cleanQuery(query):
- """
- Switch all SQL statement (alike) keywords to upper case
- """
- retVal = query
- for sqlStatements in SQL_STATEMENTS.values():
- for sqlStatement in sqlStatements:
- sqlStatementEsc = sqlStatement.replace("(", "\\(")
- queryMatch = re.search("(%s)" % sqlStatementEsc, query, re.I)
- if queryMatch and "sys_exec" not in query:
- retVal = retVal.replace(queryMatch.group(1), sqlStatement.upper())
- return retVal
- def setPaths():
- """
- Sets absolute paths for project directories and files
- """
- # sqlmap paths
- paths.SQLMAP_EXTRAS_PATH = os.path.join(paths.SQLMAP_ROOT_PATH, "extra")
- paths.SQLMAP_PROCS_PATH = os.path.join(paths.SQLMAP_ROOT_PATH, "procs")
- paths.SQLMAP_SHELL_PATH = os.path.join(paths.SQLMAP_ROOT_PATH, "shell")
- paths.SQLMAP_TAMPER_PATH = os.path.join(paths.SQLMAP_ROOT_PATH, "tamper")
- paths.SQLMAP_WAF_PATH = os.path.join(paths.SQLMAP_ROOT_PATH, "waf")
- paths.SQLMAP_TXT_PATH = os.path.join(paths.SQLMAP_ROOT_PATH, "txt")
- paths.SQLMAP_UDF_PATH = os.path.join(paths.SQLMAP_ROOT_PATH, "udf")
- paths.SQLMAP_XML_PATH = os.path.join(paths.SQLMAP_ROOT_PATH, "xml")
- paths.SQLMAP_XML_BANNER_PATH = os.path.join(paths.SQLMAP_XML_PATH, "banner")
- paths.SQLMAP_OUTPUT_PATH = paths.get("SQLMAP_OUTPUT_PATH", os.path.join(paths.SQLMAP_ROOT_PATH, "output"))
- paths.SQLMAP_DUMP_PATH = os.path.join(paths.SQLMAP_OUTPUT_PATH, "%s", "dump")
- paths.SQLMAP_FILES_PATH = os.path.join(paths.SQLMAP_OUTPUT_PATH, "%s", "files")
- # sqlmap files
- paths.SQLMAP_HISTORY = os.path.join(os.path.expanduser('~'), ".sqlmap_history")
- paths.SQLMAP_CONFIG = os.path.join(paths.SQLMAP_ROOT_PATH, "sqlmap-%s.conf" % randomStr())
- paths.COMMON_COLUMNS = os.path.join(paths.SQLMAP_TXT_PATH, "common-columns.txt")
- paths.COMMON_TABLES = os.path.join(paths.SQLMAP_TXT_PATH, "common-tables.txt")
- paths.COMMON_OUTPUTS = os.path.join(paths.SQLMAP_TXT_PATH, 'common-outputs.txt')
- paths.SQL_KEYWORDS = os.path.join(paths.SQLMAP_TXT_PATH, "keywords.txt")
- paths.SMALL_DICT = os.path.join(paths.SQLMAP_TXT_PATH, "smalldict.txt")
- paths.USER_AGENTS = os.path.join(paths.SQLMAP_TXT_PATH, "user-agents.txt")
- paths.WORDLIST = os.path.join(paths.SQLMAP_TXT_PATH, "wordlist.zip")
- paths.ERRORS_XML = os.path.join(paths.SQLMAP_XML_PATH, "errors.xml")
- paths.PAYLOADS_XML = os.path.join(paths.SQLMAP_XML_PATH, "payloads.xml")
- paths.INJECTIONS_XML = os.path.join(paths.SQLMAP_XML_PATH, "injections.xml")
- paths.LIVE_TESTS_XML = os.path.join(paths.SQLMAP_XML_PATH, "livetests.xml")
- paths.QUERIES_XML = os.path.join(paths.SQLMAP_XML_PATH, "queries.xml")
- paths.GENERIC_XML = os.path.join(paths.SQLMAP_XML_BANNER_PATH, "generic.xml")
- paths.MSSQL_XML = os.path.join(paths.SQLMAP_XML_BANNER_PATH, "mssql.xml")
- paths.MYSQL_XML = os.path.join(paths.SQLMAP_XML_BANNER_PATH, "mysql.xml")
- paths.ORACLE_XML = os.path.join(paths.SQLMAP_XML_BANNER_PATH, "oracle.xml")
- paths.PGSQL_XML = os.path.join(paths.SQLMAP_XML_BANNER_PATH, "postgresql.xml")
- def weAreFrozen():
- """
- Returns whether we are frozen via py2exe.
- This will affect how we find out where we are located.
- Reference: http://www.py2exe.org/index.cgi/WhereAmI
- """
- return hasattr(sys, "frozen")
- def parseTargetDirect():
- """
- Parse target dbms and set some attributes into the configuration singleton.
- """
- if not conf.direct:
- return
- details = None
- remote = False
- for dbms in SUPPORTED_DBMS:
- details = re.search("^(?P<dbms>%s)://(?P<credentials>(?P<user>.+?)\:(?P<pass>.*)\@)?(?P<remote>(?P<hostname>.+?)\:(?P<port>[\d]+)\/)?(?P<db>[\w\d\ \:\.\_\-\/\\\\]+?)$" % dbms, conf.direct, re.I)
- if details:
- conf.dbms = details.group("dbms")
- if details.group('credentials'):
- conf.dbmsUser = details.group("user")
- conf.dbmsPass = details.group("pass")
- else:
- if conf.dbmsCred:
- conf.dbmsUser, conf.dbmsPass = conf.dbmsCred.split(':')
- else:
- conf.dbmsUser = unicode()
- conf.dbmsPass = unicode()
- if not conf.dbmsPass:
- conf.dbmsPass = None
- if details.group("remote"):
- remote = True
- conf.hostname = details.group("hostname").strip()
- conf.port = int(details.group("port"))
- else:
- conf.hostname = "localhost"
- conf.port = 0
- conf.dbmsDb = details.group("db")
- conf.parameters[None] = "direct connection"
- break
- if not details:
- errMsg = "invalid target details, valid syntax is for instance "
- errMsg += "'mysql://USER:PASSWORD@DBMS_IP:DBMS_PORT/DATABASE_NAME' "
- errMsg += "or 'access://DATABASE_FILEPATH'"
- raise SqlmapSyntaxException(errMsg)
- for dbmsName, data in DBMS_DICT.items():
- if conf.dbms in data[0]:
- try:
- if dbmsName in (DBMS.ACCESS, DBMS.SQLITE, DBMS.FIREBIRD):
- if remote:
- warnMsg = "direct connection over the network for "
- warnMsg += "%s DBMS is not supported" % dbmsName
- logger.warn(warnMsg)
- conf.hostname = "localhost"
- conf.port = 0
- elif not remote:
- errMsg = "missing remote connection details"
- raise SqlmapSyntaxException(errMsg)
- if dbmsName in (DBMS.MSSQL, DBMS.SYBASE):
- import _mssql
- import pymssql
- if not hasattr(pymssql, "__version__") or pymssql.__version__ < "1.0.2":
- errMsg = "'%s' third-party library must be " % data[1]
- errMsg += "version >= 1.0.2 to work properly. "
- errMsg += "Download from '%s'" % data[2]
- raise SqlmapMissingDependence(errMsg)
- elif dbmsName == DBMS.MYSQL:
- import pymysql
- elif dbmsName == DBMS.PGSQL:
- import psycopg2
- elif dbmsName == DBMS.ORACLE:
- import cx_Oracle
- elif dbmsName == DBMS.SQLITE:
- import sqlite3
- elif dbmsName == DBMS.ACCESS:
- import pyodbc
- elif dbmsName == DBMS.FIREBIRD:
- import kinterbasdb
- except ImportError:
- if _sqlalchemy and data[3] in _sqlalchemy.dialects.__all__:
- pass
- else:
- errMsg = "sqlmap requires '%s' third-party library " % data[1]
- errMsg += "in order to directly connect to the database "
- errMsg += "%s. You can download it from '%s'" % (dbmsName, data[2])
- errMsg += ". Alternative is to use a package 'python-sqlalchemy' "
- errMsg += "with support for dialect '%s' installed" % data[3]
- raise SqlmapMissingDependence(errMsg)
- def parseTargetUrl():
- """
- Parse target URL and set some attributes into the configuration singleton.
- """
- if not conf.url:
- return
- originalUrl = conf.url
- if re.search("\[.+\]", conf.url) and not socket.has_ipv6:
- errMsg = "IPv6 addressing is not supported "
- errMsg += "on this platform"
- raise SqlmapGenericException(errMsg)
- if not re.search("^http[s]*://", conf.url, re.I):
- if ":443/" in conf.url:
- conf.url = "https://" + conf.url
- else:
- conf.url = "http://" + conf.url
- if CUSTOM_INJECTION_MARK_CHAR in conf.url:
- conf.url = conf.url.replace('?', URI_QUESTION_MARKER)
- urlSplit = urlparse.urlsplit(conf.url)
- hostnamePort = urlSplit.netloc.split(":") if not re.search("\[.+\]", urlSplit.netloc) else filter(None, (re.search("\[.+\]", urlSplit.netloc).group(0), re.search("\](:(?P<port>\d+))?", urlSplit.netloc).group("port")))
- conf.scheme = urlSplit.scheme.strip().lower() if not conf.forceSSL else "https"
- conf.path = urlSplit.path.strip()
- conf.hostname = hostnamePort[0].strip()
- conf.ipv6 = conf.hostname != conf.hostname.strip("[]")
- conf.hostname = conf.hostname.strip("[]").replace(CUSTOM_INJECTION_MARK_CHAR, "")
- try:
- _ = conf.hostname.encode("idna")
- except UnicodeError:
- _ = None
- if any((_ is None, re.search(r'\s', conf.hostname), '..' in conf.hostname, conf.hostname.startswith('.'))):
- errMsg = "invalid target URL"
- raise SqlmapSyntaxException(errMsg)
- if len(hostnamePort) == 2:
- try:
- conf.port = int(hostnamePort[1])
- except:
- errMsg = "invalid target URL"
- raise SqlmapSyntaxException(errMsg)
- elif conf.scheme == "https":
- conf.port = 443
- else:
- conf.port = 80
- if urlSplit.query:
- conf.parameters[PLACE.GET] = urldecode(urlSplit.query) if urlSplit.query and urlencode(DEFAULT_GET_POST_DELIMITER, None) not in urlSplit.query else urlSplit.query
- conf.url = getUnicode("%s://%s:%d%s" % (conf.scheme, ("[%s]" % conf.hostname) if conf.ipv6 else conf.hostname, conf.port, conf.path))
- conf.url = conf.url.replace(URI_QUESTION_MARKER, '?')
- if not conf.referer and intersect(REFERER_ALIASES, conf.testParameter, True):
- debugMsg = "setting the HTTP Referer header to the target URL"
- logger.debug(debugMsg)
- conf.httpHeaders = filter(lambda (key, value): key != HTTP_HEADER.REFERER, conf.httpHeaders)
- conf.httpHeaders.append((HTTP_HEADER.REFERER, conf.url))
- if not conf.host and intersect(HOST_ALIASES, conf.testParameter, True):
- debugMsg = "setting the HTTP Host header to the target URL"
- logger.debug(debugMsg)
- conf.httpHeaders = filter(lambda (key, value): key != HTTP_HEADER.HOST, conf.httpHeaders)
- conf.httpHeaders.append((HTTP_HEADER.HOST, getHostHeader(conf.url)))
- if conf.url != originalUrl:
- kb.originalUrls[conf.url] = originalUrl
- def expandAsteriskForColumns(expression):
- """
- If the user provided an asterisk rather than the column(s)
- name, sqlmap will retrieve the columns itself and reprocess
- the SQL query string (expression)
- """
- asterisk = re.search("^SELECT(\s+TOP\s+[\d]+)?\s+\*\s+FROM\s+([\w\.\_]+)\s*", expression, re.I)
- if asterisk:
- infoMsg = "you did not provide the fields in your query. "
- infoMsg += "sqlmap will retrieve the column names itself"
- logger.info(infoMsg)
- _ = asterisk.group(2).replace("..", ".")
- conf.db, conf.tbl = _.split(".", 1) if '.' in _ else (None, _)
- conf.db = safeSQLIdentificatorNaming(conf.db)
- conf.tbl = safeSQLIdentificatorNaming(conf.tbl, True)
- columnsDict = conf.dbmsHandler.getColumns(onlyColNames=True)
- if columnsDict and conf.db in columnsDict and conf.tbl in columnsDict[conf.db]:
- columns = columnsDict[conf.db][conf.tbl].keys()
- columns.sort()
- columnsStr = ", ".join(column for column in columns)
- expression = expression.replace("*", columnsStr, 1)
- infoMsg = "the query with expanded column name(s) is: "
- infoMsg += "%s" % expression
- logger.info(infoMsg)
- return expression
- def getLimitRange(count, dump=False, plusOne=False):
- """
- Returns range of values used in limit/offset constructs
- >>> [_ for _ in getLimitRange(10)]
- [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
- """
- retVal = None
- count = int(count)
- limitStart, limitStop = 1, count
- if dump:
- if isinstance(conf.limitStop, int) and conf.limitStop > 0 and conf.limitStop < limitStop:
- limitStop = conf.limitStop
- if isinstance(conf.limitStart, int) and conf.limitStart > 0 and conf.limitStart <= limitStop:
- limitStart = conf.limitStart
- retVal = xrange(limitStart, limitStop + 1) if plusOne else xrange(limitStart - 1, limitStop)
- return retVal
- def parseUnionPage(page):
- """
- Returns resulting items from UNION query inside provided page content
- """
- if page is None:
- return None
- if page.startswith(kb.chars.start) and page.endswith(kb.chars.stop):
- if len(page) > LARGE_OUTPUT_THRESHOLD:
- warnMsg = "large output detected. This might take a while"
- logger.warn(warnMsg)
- data = BigArray()
- keys = set()
- for match in re.finditer("%s(.*?)%s" % (kb.chars.start, kb.chars.stop), page, re.DOTALL | re.IGNORECASE):
- entry = match.group(1)
- if kb.chars.start in entry:
- entry = entry.split(kb.chars.start)[-1]
- if kb.unionDuplicates:
- key = entry.lower()
- if key not in keys:
- keys.add(key)
- else:
- continue
- entry = entry.split(kb.chars.delimiter)
- if conf.hexConvert:
- entry = applyFunctionRecursively(entry, decodeHexValue)
- if kb.safeCharEncode:
- entry = applyFunctionRecursively(entry, safecharencode)
- data.append(entry[0] if len(entry) == 1 else entry)
- else:
- data = page
- if len(data) == 1 and isinstance(data[0], basestring):
- data = data[0]
- return data
- def parseFilePaths(page):
- """
- Detects (possible) absolute system paths inside the provided page content
- """
- if page:
- for regex in (r" in <b>(?P<result>.*?)</b> on line", r"(?:>|\s)(?P<result>[A-Za-z]:[\\/][\w.\\/]*)", r"(?:>|\s)(?P<result>/\w[/\w.]+)"):
- for match in re.finditer(regex, page):
- absFilePath = match.group("result").strip()
- page = page.replace(absFilePath, "")
- if isWindowsDriveLetterPath(absFilePath):
- absFilePath = posixToNtSlashes(absFilePath)
- if absFilePath not in kb.absFilePaths:
- kb.absFilePaths.add(absFilePath)
- def getLocalIP():
- """
- Get local IP address (exposed to the remote/target)
- """
- retVal = None
- try:
- s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- s.connect((conf.hostname, conf.port))
- retVal, _ = s.getsockname()
- s.close()
- except:
- debugMsg = "there was an error in opening socket "
- debugMsg += "connection toward '%s'" % conf.hostname
- logger.debug(debugMsg)
- return retVal
- def getRemoteIP():
- """
- Get remote/target IP address
- """
- retVal = None
- try:
- retVal = socket.gethostbyname(conf.hostname)
- except socket.gaierror:
- errMsg = "address resolution problem "
- errMsg += "occurred for hostname '%s'" % conf.hostname
- singleTimeLogMessage(errMsg, logging.ERROR)
- return retVal
- def getFileType(filePath):
- try:
- _ = magic.from_file(filePath)
- except:
- return "unknown"
- return "text" if "ASCII" in _ or "text" in _ else "binary"
- def getCharset(charsetType=None):
- """
- Returns list with integers representing characters of a given
- charset type appropriate for inference techniques
- >>> getCharset(CHARSET_TYPE.BINARY)
- [0, 1, 47, 48, 49]
- """
- asciiTbl = []
- if charsetType is None:
- asciiTbl.extend(xrange(0, 128))
- # 0 or 1
- elif charsetType == CHARSET_TYPE.BINARY:
- asciiTbl.extend([0, 1])
- asciiTbl.extend(xrange(47, 50))
- # Digits
- elif charsetType == CHARSET_TYPE.DIGITS:
- asciiTbl.extend([0, 1])
- asciiTbl.extend(xrange(47, 58))
- # Hexadecimal
- elif charsetType == CHARSET_TYPE.HEXADECIMAL:
- asciiTbl.extend([…
Large files files are truncated, but you can click here to view the full file