PageRenderTime 56ms CodeModel.GetById 14ms RepoModel.GetById 1ms app.codeStats 0ms

/lib/core/common.py

https://github.com/todarede/sqlmap
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

  1. #!/usr/bin/env python
  2. """
  3. Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/)
  4. See the file 'doc/COPYING' for copying permission
  5. """
  6. import codecs
  7. import contextlib
  8. import cookielib
  9. import copy
  10. import httplib
  11. import inspect
  12. import logging
  13. import ntpath
  14. import os
  15. import posixpath
  16. import random
  17. import re
  18. import socket
  19. import string
  20. import sys
  21. import tempfile
  22. import time
  23. import urllib
  24. import urlparse
  25. import unicodedata
  26. from ConfigParser import DEFAULTSECT
  27. from ConfigParser import RawConfigParser
  28. from StringIO import StringIO
  29. from difflib import SequenceMatcher
  30. from math import sqrt
  31. from optparse import OptionValueError
  32. from subprocess import PIPE
  33. from subprocess import Popen as execute
  34. from xml.dom import minidom
  35. from xml.sax import parse
  36. from extra.cloak.cloak import decloak
  37. from extra.safe2bin.safe2bin import safecharencode
  38. from lib.core.bigarray import BigArray
  39. from lib.core.data import conf
  40. from lib.core.data import kb
  41. from lib.core.data import logger
  42. from lib.core.data import paths
  43. from lib.core.convert import base64pickle
  44. from lib.core.convert import base64unpickle
  45. from lib.core.convert import hexdecode
  46. from lib.core.convert import htmlunescape
  47. from lib.core.convert import stdoutencode
  48. from lib.core.convert import unicodeencode
  49. from lib.core.convert import utf8encode
  50. from lib.core.decorators import cachedmethod
  51. from lib.core.defaults import defaults
  52. from lib.core.dicts import DBMS_DICT
  53. from lib.core.dicts import DEFAULT_DOC_ROOTS
  54. from lib.core.dicts import DEPRECATED_OPTIONS
  55. from lib.core.dicts import SQL_STATEMENTS
  56. from lib.core.enums import ADJUST_TIME_DELAY
  57. from lib.core.enums import CONTENT_STATUS
  58. from lib.core.enums import CHARSET_TYPE
  59. from lib.core.enums import DBMS
  60. from lib.core.enums import EXPECTED
  61. from lib.core.enums import HEURISTIC_TEST
  62. from lib.core.enums import HTTP_HEADER
  63. from lib.core.enums import HTTPMETHOD
  64. from lib.core.enums import OS
  65. from lib.core.enums import PLACE
  66. from lib.core.enums import PAYLOAD
  67. from lib.core.enums import REFLECTIVE_COUNTER
  68. from lib.core.enums import SORT_ORDER
  69. from lib.core.exception import SqlmapDataException
  70. from lib.core.exception import SqlmapFilePathException
  71. from lib.core.exception import SqlmapGenericException
  72. from lib.core.exception import SqlmapNoneDataException
  73. from lib.core.exception import SqlmapMissingDependence
  74. from lib.core.exception import SqlmapSilentQuitException
  75. from lib.core.exception import SqlmapSyntaxException
  76. from lib.core.exception import SqlmapUserQuitException
  77. from lib.core.log import LOGGER_HANDLER
  78. from lib.core.optiondict import optDict
  79. from lib.core.settings import BOLD_PATTERNS
  80. from lib.core.settings import BRUTE_DOC_ROOT_PREFIXES
  81. from lib.core.settings import BRUTE_DOC_ROOT_SUFFIXES
  82. from lib.core.settings import BRUTE_DOC_ROOT_TARGET_MARK
  83. from lib.core.settings import CUSTOM_INJECTION_MARK_CHAR
  84. from lib.core.settings import DBMS_DIRECTORY_DICT
  85. from lib.core.settings import DEFAULT_COOKIE_DELIMITER
  86. from lib.core.settings import DEFAULT_GET_POST_DELIMITER
  87. from lib.core.settings import DEFAULT_MSSQL_SCHEMA
  88. from lib.core.settings import DESCRIPTION
  89. from lib.core.settings import DUMMY_SQL_INJECTION_CHARS
  90. from lib.core.settings import DUMMY_USER_INJECTION
  91. from lib.core.settings import DYNAMICITY_MARK_LENGTH
  92. from lib.core.settings import ERROR_PARSING_REGEXES
  93. from lib.core.settings import FORCE_COOKIE_EXPIRATION_TIME
  94. from lib.core.settings import FORM_SEARCH_REGEX
  95. from lib.core.settings import GENERIC_DOC_ROOT_DIRECTORY_NAMES
  96. from lib.core.settings import GOOGLE_ANALYTICS_COOKIE_PREFIX
  97. from lib.core.settings import HASHDB_MILESTONE_VALUE
  98. from lib.core.settings import HOST_ALIASES
  99. from lib.core.settings import INFERENCE_UNKNOWN_CHAR
  100. from lib.core.settings import INVALID_UNICODE_CHAR_FORMAT
  101. from lib.core.settings import IP_ADDRESS_REGEX
  102. from lib.core.settings import ISSUES_PAGE
  103. from lib.core.settings import IS_WIN
  104. from lib.core.settings import LARGE_OUTPUT_THRESHOLD
  105. from lib.core.settings import MIN_ENCODED_LEN_CHECK
  106. from lib.core.settings import MIN_TIME_RESPONSES
  107. from lib.core.settings import MIN_VALID_DELAYED_RESPONSE
  108. from lib.core.settings import ML
  109. from lib.core.settings import NETSCAPE_FORMAT_HEADER_COOKIES
  110. from lib.core.settings import NULL
  111. from lib.core.settings import PARAMETER_AMP_MARKER
  112. from lib.core.settings import PARAMETER_SEMICOLON_MARKER
  113. from lib.core.settings import PARTIAL_HEX_VALUE_MARKER
  114. from lib.core.settings import PARTIAL_VALUE_MARKER
  115. from lib.core.settings import PAYLOAD_DELIMITER
  116. from lib.core.settings import PLATFORM
  117. from lib.core.settings import PRINTABLE_CHAR_REGEX
  118. from lib.core.settings import PYVERSION
  119. from lib.core.settings import REFERER_ALIASES
  120. from lib.core.settings import REFLECTED_BORDER_REGEX
  121. from lib.core.settings import REFLECTED_MAX_REGEX_PARTS
  122. from lib.core.settings import REFLECTED_REPLACEMENT_REGEX
  123. from lib.core.settings import REFLECTED_VALUE_MARKER
  124. from lib.core.settings import REFLECTIVE_MISS_THRESHOLD
  125. from lib.core.settings import REVISION
  126. from lib.core.settings import SENSITIVE_DATA_REGEX
  127. from lib.core.settings import SITE
  128. from lib.core.settings import SUPPORTED_DBMS
  129. from lib.core.settings import TEXT_TAG_REGEX
  130. from lib.core.settings import TIME_STDEV_COEFF
  131. from lib.core.settings import UNICODE_ENCODING
  132. from lib.core.settings import UNKNOWN_DBMS_VERSION
  133. from lib.core.settings import URI_QUESTION_MARKER
  134. from lib.core.settings import URLENCODE_CHAR_LIMIT
  135. from lib.core.settings import URLENCODE_FAILSAFE_CHARS
  136. from lib.core.settings import USER_AGENT_ALIASES
  137. from lib.core.settings import VERSION
  138. from lib.core.settings import VERSION_STRING
  139. from lib.core.threads import getCurrentThreadData
  140. from lib.utils.sqlalchemy import _sqlalchemy
  141. from thirdparty.clientform.clientform import ParseResponse
  142. from thirdparty.clientform.clientform import ParseError
  143. from thirdparty.magic import magic
  144. from thirdparty.odict.odict import OrderedDict
  145. from thirdparty.termcolor.termcolor import colored
  146. class UnicodeRawConfigParser(RawConfigParser):
  147. """
  148. RawConfigParser with unicode writing support
  149. """
  150. def write(self, fp):
  151. """
  152. Write an .ini-format representation of the configuration state.
  153. """
  154. if self._defaults:
  155. fp.write("[%s]\n" % DEFAULTSECT)
  156. for (key, value) in self._defaults.items():
  157. fp.write("%s = %s\n" % (key, getUnicode(value, UNICODE_ENCODING).replace('\n', '\n\t')))
  158. fp.write("\n")
  159. for section in self._sections:
  160. fp.write("[%s]\n" % section)
  161. for (key, value) in self._sections[section].items():
  162. if key != "__name__":
  163. if value is None:
  164. fp.write("%s\n" % (key))
  165. else:
  166. fp.write("%s = %s\n" % (key, getUnicode(value, UNICODE_ENCODING).replace('\n', '\n\t')))
  167. fp.write("\n")
  168. class Format(object):
  169. @staticmethod
  170. def humanize(values, chain=" or "):
  171. return chain.join(values)
  172. # Get methods
  173. @staticmethod
  174. def getDbms(versions=None):
  175. """
  176. Format the back-end DBMS fingerprint value and return its
  177. values formatted as a human readable string.
  178. @return: detected back-end DBMS based upon fingerprint techniques.
  179. @rtype: C{str}
  180. """
  181. if versions is None and Backend.getVersionList():
  182. versions = Backend.getVersionList()
  183. return Backend.getDbms() if versions is None else "%s %s" % (Backend.getDbms(), " and ".join(v for v in versions))
  184. @staticmethod
  185. def getErrorParsedDBMSes():
  186. """
  187. Parses the knowledge base htmlFp list and return its values
  188. formatted as a human readable string.
  189. @return: list of possible back-end DBMS based upon error messages
  190. parsing.
  191. @rtype: C{str}
  192. """
  193. htmlParsed = None
  194. if len(kb.htmlFp) == 0 or kb.heuristicTest != HEURISTIC_TEST.POSITIVE:
  195. pass
  196. elif len(kb.htmlFp) == 1:
  197. htmlParsed = kb.htmlFp[0]
  198. elif len(kb.htmlFp) > 1:
  199. htmlParsed = " or ".join(kb.htmlFp)
  200. return htmlParsed
  201. @staticmethod
  202. def getOs(target, info):
  203. """
  204. Formats the back-end operating system fingerprint value
  205. and return its values formatted as a human readable string.
  206. Example of info (kb.headersFp) dictionary:
  207. {
  208. 'distrib': set(['Ubuntu']),
  209. 'type': set(['Linux']),
  210. 'technology': set(['PHP 5.2.6', 'Apache 2.2.9']),
  211. 'release': set(['8.10'])
  212. }
  213. Example of info (kb.bannerFp) dictionary:
  214. {
  215. 'sp': set(['Service Pack 4']),
  216. 'dbmsVersion': '8.00.194',
  217. 'dbmsServicePack': '0',
  218. 'distrib': set(['2000']),
  219. 'dbmsRelease': '2000',
  220. 'type': set(['Windows'])
  221. }
  222. @return: detected back-end operating system based upon fingerprint
  223. techniques.
  224. @rtype: C{str}
  225. """
  226. infoStr = ""
  227. infoApi = {}
  228. if info and "type" in info:
  229. if hasattr(conf, "api"):
  230. infoApi["%s operating system" % target] = info
  231. else:
  232. infoStr += "%s operating system: %s" % (target, Format.humanize(info["type"]))
  233. if "distrib" in info:
  234. infoStr += " %s" % Format.humanize(info["distrib"])
  235. if "release" in info:
  236. infoStr += " %s" % Format.humanize(info["release"])
  237. if "sp" in info:
  238. infoStr += " %s" % Format.humanize(info["sp"])
  239. if "codename" in info:
  240. infoStr += " (%s)" % Format.humanize(info["codename"])
  241. if "technology" in info:
  242. if hasattr(conf, "api"):
  243. infoApi["web application technology"] = Format.humanize(info["technology"], ", ")
  244. else:
  245. infoStr += "\nweb application technology: %s" % Format.humanize(info["technology"], ", ")
  246. if hasattr(conf, "api"):
  247. return infoApi
  248. else:
  249. return infoStr.lstrip()
  250. class Backend:
  251. # Set methods
  252. @staticmethod
  253. def setDbms(dbms):
  254. dbms = aliasToDbmsEnum(dbms)
  255. if dbms is None:
  256. return None
  257. # Little precaution, in theory this condition should always be false
  258. elif kb.dbms is not None and kb.dbms != dbms:
  259. warnMsg = "there seems to be a high probability that "
  260. warnMsg += "this could be a false positive case"
  261. logger.warn(warnMsg)
  262. msg = "sqlmap previously fingerprinted back-end DBMS as "
  263. msg += "%s. However now it has been fingerprinted " % kb.dbms
  264. msg += "as %s. " % dbms
  265. msg += "Please, specify which DBMS should be "
  266. msg += "correct [%s (default)/%s] " % (kb.dbms, dbms)
  267. while True:
  268. _ = readInput(msg, default=kb.dbms)
  269. if aliasToDbmsEnum(_) == kb.dbms:
  270. break
  271. elif aliasToDbmsEnum(_) == dbms:
  272. kb.dbms = aliasToDbmsEnum(_)
  273. break
  274. else:
  275. warnMsg = "invalid value"
  276. logger.warn(warnMsg)
  277. elif kb.dbms is None:
  278. kb.dbms = aliasToDbmsEnum(dbms)
  279. return kb.dbms
  280. @staticmethod
  281. def setVersion(version):
  282. if isinstance(version, basestring):
  283. kb.dbmsVersion = [version]
  284. return kb.dbmsVersion
  285. @staticmethod
  286. def setVersionList(versionsList):
  287. if isinstance(versionsList, list):
  288. kb.dbmsVersion = versionsList
  289. elif isinstance(versionsList, basestring):
  290. Backend.setVersion(versionsList)
  291. else:
  292. logger.error("invalid format of versionsList")
  293. @staticmethod
  294. def forceDbms(dbms, sticky=False):
  295. if not kb.stickyDBMS:
  296. kb.forcedDbms = aliasToDbmsEnum(dbms)
  297. kb.stickyDBMS = sticky
  298. @staticmethod
  299. def flushForcedDbms(force=False):
  300. if not kb.stickyDBMS or force:
  301. kb.forcedDbms = None
  302. kb.stickyDBMS = False
  303. @staticmethod
  304. def setOs(os):
  305. if os is None:
  306. return None
  307. # Little precaution, in theory this condition should always be false
  308. elif kb.os is not None and isinstance(os, basestring) and kb.os.lower() != os.lower():
  309. msg = "sqlmap previously fingerprinted back-end DBMS "
  310. msg += "operating system %s. However now it has " % kb.os
  311. msg += "been fingerprinted to be %s. " % os
  312. msg += "Please, specify which OS is "
  313. msg += "correct [%s (default)/%s] " % (kb.os, os)
  314. while True:
  315. _ = readInput(msg, default=kb.os)
  316. if _ == kb.os:
  317. break
  318. elif _ == os:
  319. kb.os = _.capitalize()
  320. break
  321. else:
  322. warnMsg = "invalid value"
  323. logger.warn(warnMsg)
  324. elif kb.os is None and isinstance(os, basestring):
  325. kb.os = os.capitalize()
  326. return kb.os
  327. @staticmethod
  328. def setOsVersion(version):
  329. if version is None:
  330. return None
  331. elif kb.osVersion is None and isinstance(version, basestring):
  332. kb.osVersion = version
  333. @staticmethod
  334. def setOsServicePack(sp):
  335. if sp is None:
  336. return None
  337. elif kb.osSP is None and isinstance(sp, int):
  338. kb.osSP = sp
  339. @staticmethod
  340. def setArch():
  341. msg = "what is the back-end database management system architecture?"
  342. msg += "\n[1] 32-bit (default)"
  343. msg += "\n[2] 64-bit"
  344. while True:
  345. _ = readInput(msg, default='1')
  346. if isinstance(_, basestring) and _.isdigit() and int(_) in (1, 2):
  347. kb.arch = 32 if int(_) == 1 else 64
  348. break
  349. else:
  350. warnMsg = "invalid value. Valid values are 1 and 2"
  351. logger.warn(warnMsg)
  352. return kb.arch
  353. # Get methods
  354. @staticmethod
  355. def getForcedDbms():
  356. return aliasToDbmsEnum(kb.get("forcedDbms"))
  357. @staticmethod
  358. def getDbms():
  359. return aliasToDbmsEnum(kb.get("dbms"))
  360. @staticmethod
  361. def getErrorParsedDBMSes():
  362. """
  363. Returns array with parsed DBMS names till now
  364. This functions is called to:
  365. 1. Sort the tests, getSortedInjectionTests() - detection phase.
  366. 2. Ask user whether or not skip specific DBMS tests in detection phase,
  367. lib/controller/checks.py - detection phase.
  368. 3. Sort the fingerprint of the DBMS, lib/controller/handler.py -
  369. fingerprint phase.
  370. """
  371. return kb.htmlFp if kb.get("heuristicTest") == HEURISTIC_TEST.POSITIVE else []
  372. @staticmethod
  373. def getIdentifiedDbms():
  374. dbms = None
  375. if not kb:
  376. pass
  377. elif Backend.getForcedDbms() is not None:
  378. dbms = Backend.getForcedDbms()
  379. elif Backend.getDbms() is not None:
  380. dbms = kb.dbms
  381. elif conf.get("dbms"):
  382. dbms = conf.dbms
  383. elif Backend.getErrorParsedDBMSes():
  384. dbms = unArrayizeValue(Backend.getErrorParsedDBMSes())
  385. elif kb.get("injection") and kb.injection.dbms:
  386. dbms = unArrayizeValue(kb.injection.dbms)
  387. return aliasToDbmsEnum(dbms)
  388. @staticmethod
  389. def getVersion():
  390. if len(kb.dbmsVersion) > 0:
  391. return kb.dbmsVersion[0]
  392. else:
  393. return None
  394. @staticmethod
  395. def getVersionList():
  396. if len(kb.dbmsVersion) > 0:
  397. return kb.dbmsVersion
  398. else:
  399. return None
  400. @staticmethod
  401. def getOs():
  402. return kb.os
  403. @staticmethod
  404. def getOsVersion():
  405. return kb.osVersion
  406. @staticmethod
  407. def getOsServicePack():
  408. return kb.osSP
  409. @staticmethod
  410. def getArch():
  411. if kb.arch is None:
  412. Backend.setArch()
  413. return kb.arch
  414. # Comparison methods
  415. @staticmethod
  416. def isDbms(dbms):
  417. if Backend.getDbms() is not None:
  418. return Backend.getDbms() == aliasToDbmsEnum(dbms)
  419. else:
  420. return Backend.getIdentifiedDbms() == aliasToDbmsEnum(dbms)
  421. @staticmethod
  422. def isDbmsWithin(aliases):
  423. return Backend.getDbms() is not None and Backend.getDbms().lower() in aliases
  424. @staticmethod
  425. def isVersion(version):
  426. return Backend.getVersion() is not None and Backend.getVersion() == version
  427. @staticmethod
  428. def isVersionWithin(versionList):
  429. if Backend.getVersionList() is None:
  430. return False
  431. for _ in Backend.getVersionList():
  432. if _ != UNKNOWN_DBMS_VERSION and _ in versionList:
  433. return True
  434. return False
  435. @staticmethod
  436. def isVersionGreaterOrEqualThan(version):
  437. return Backend.getVersion() is not None and str(Backend.getVersion()) >= str(version)
  438. @staticmethod
  439. def isOs(os):
  440. return Backend.getOs() is not None and Backend.getOs().lower() == os.lower()
  441. def paramToDict(place, parameters=None):
  442. """
  443. Split the parameters into names and values, check if these parameters
  444. are within the testable parameters and return in a dictionary.
  445. """
  446. testableParameters = OrderedDict()
  447. if place in conf.parameters and not parameters:
  448. parameters = conf.parameters[place]
  449. parameters = parameters.replace(", ", ",")
  450. parameters = re.sub(r"&(\w{1,4});", r"%s\g<1>%s" % (PARAMETER_AMP_MARKER, PARAMETER_SEMICOLON_MARKER), parameters)
  451. if place == PLACE.COOKIE:
  452. splitParams = parameters.split(conf.cookieDel or DEFAULT_COOKIE_DELIMITER)
  453. else:
  454. splitParams = parameters.split(conf.paramDel or DEFAULT_GET_POST_DELIMITER)
  455. for element in splitParams:
  456. element = re.sub(r"%s(.+?)%s" % (PARAMETER_AMP_MARKER, PARAMETER_SEMICOLON_MARKER), r"&\g<1>;", element)
  457. parts = element.split("=")
  458. if len(parts) >= 2:
  459. parameter = parts[0].replace(" ", "")
  460. if conf.paramDel and conf.paramDel == '\n':
  461. parts[-1] = parts[-1].rstrip()
  462. condition = not conf.testParameter
  463. condition |= parameter in conf.testParameter
  464. condition |= place == PLACE.COOKIE and len(intersect((PLACE.COOKIE,), conf.testParameter, True)) > 0
  465. if condition:
  466. testableParameters[parameter] = "=".join(parts[1:])
  467. if not conf.multipleTargets:
  468. _ = urldecode(testableParameters[parameter], convall=True)
  469. if (_.strip(DUMMY_SQL_INJECTION_CHARS) != _\
  470. or re.search(r'\A9{3,}', _) or re.search(DUMMY_USER_INJECTION, _))\
  471. and not parameter.upper().startswith(GOOGLE_ANALYTICS_COOKIE_PREFIX):
  472. warnMsg = "it appears that you have provided tainted parameter values "
  473. warnMsg += "('%s') with most probably leftover " % element
  474. warnMsg += "chars/statements from manual SQL injection test(s). "
  475. warnMsg += "Please, always use only valid parameter values "
  476. warnMsg += "so sqlmap could be able to run properly"
  477. logger.warn(warnMsg)
  478. message = "are you sure you want to continue? [y/N] "
  479. test = readInput(message, default="N")
  480. if test[0] not in ("y", "Y"):
  481. raise SqlmapSilentQuitException
  482. if conf.testParameter and not testableParameters:
  483. paramStr = ", ".join(test for test in conf.testParameter)
  484. if len(conf.testParameter) > 1:
  485. warnMsg = "provided parameters '%s' " % paramStr
  486. warnMsg += "are not inside the %s" % place
  487. logger.warn(warnMsg)
  488. else:
  489. parameter = conf.testParameter[0]
  490. if not intersect(USER_AGENT_ALIASES + REFERER_ALIASES + HOST_ALIASES, parameter, True):
  491. debugMsg = "provided parameter '%s' " % paramStr
  492. debugMsg += "is not inside the %s" % place
  493. logger.debug(debugMsg)
  494. elif len(conf.testParameter) != len(testableParameters.keys()):
  495. for parameter in conf.testParameter:
  496. if parameter not in testableParameters:
  497. debugMsg = "provided parameter '%s' " % parameter
  498. debugMsg += "is not inside the %s" % place
  499. logger.debug(debugMsg)
  500. if testableParameters:
  501. for parameter, value in testableParameters.items():
  502. if value and not value.isdigit():
  503. for encoding in ("hex", "base64"):
  504. try:
  505. decoded = value.decode(encoding)
  506. if len(decoded) > MIN_ENCODED_LEN_CHECK and all(_ in string.printable for _ in decoded):
  507. warnMsg = "provided parameter '%s' " % parameter
  508. warnMsg += "seems to be '%s' encoded" % encoding
  509. logger.warn(warnMsg)
  510. break
  511. except:
  512. pass
  513. return testableParameters
  514. def getManualDirectories():
  515. directories = None
  516. pagePath = directoryPath(conf.path)
  517. defaultDocRoot = DEFAULT_DOC_ROOTS.get(Backend.getOs(), DEFAULT_DOC_ROOTS[OS.LINUX])
  518. if kb.absFilePaths:
  519. for absFilePath in kb.absFilePaths:
  520. if directories:
  521. break
  522. if directoryPath(absFilePath) == '/':
  523. continue
  524. absFilePath = normalizePath(absFilePath)
  525. windowsDriveLetter = None
  526. if isWindowsDriveLetterPath(absFilePath):
  527. windowsDriveLetter, absFilePath = absFilePath[:2], absFilePath[2:]
  528. absFilePath = ntToPosixSlashes(posixToNtSlashes(absFilePath))
  529. if any("/%s/" % _ in absFilePath for _ in GENERIC_DOC_ROOT_DIRECTORY_NAMES):
  530. for _ in GENERIC_DOC_ROOT_DIRECTORY_NAMES:
  531. _ = "/%s/" % _
  532. if _ in absFilePath:
  533. directories = "%s%s" % (absFilePath.split(_)[0], _)
  534. break
  535. if pagePath and pagePath in absFilePath:
  536. directories = absFilePath.split(pagePath)[0]
  537. if windowsDriveLetter:
  538. directories = "%s/%s" % (windowsDriveLetter, ntToPosixSlashes(directories))
  539. directories = normalizePath(directories)
  540. if directories:
  541. infoMsg = "retrieved the web server document root: '%s'" % directories
  542. logger.info(infoMsg)
  543. else:
  544. warnMsg = "unable to retrieve automatically the web server "
  545. warnMsg += "document root"
  546. logger.warn(warnMsg)
  547. directories = []
  548. message = "what do you want to use for writable directory?\n"
  549. message += "[1] common location(s) '%s' (default)\n" % ", ".join(root for root in defaultDocRoot)
  550. message += "[2] custom location(s)\n"
  551. message += "[3] custom directory list file\n"
  552. message += "[4] brute force search\n"
  553. choice = readInput(message, default="1").strip()
  554. if choice == "2":
  555. message = "please provide a comma separate list of absolute directory paths: "
  556. directories = readInput(message, default="").split(',')
  557. elif choice == "3":
  558. message = "what's the list file location?\n"
  559. listPath = readInput(message, default="")
  560. checkFile(listPath)
  561. directories = getFileItems(listPath)
  562. elif choice == "4":
  563. targets = set([conf.hostname])
  564. _ = conf.hostname.split('.')
  565. if _[0] == "www":
  566. targets.add('.'.join(_[1:]))
  567. targets.add('.'.join(_[1:-1]))
  568. else:
  569. targets.add('.'.join(_[:-1]))
  570. targets = filter(None, targets)
  571. for prefix in BRUTE_DOC_ROOT_PREFIXES.get(Backend.getOs(), DEFAULT_DOC_ROOTS[OS.LINUX]):
  572. if BRUTE_DOC_ROOT_TARGET_MARK in prefix and re.match(IP_ADDRESS_REGEX, conf.hostname):
  573. continue
  574. for suffix in BRUTE_DOC_ROOT_SUFFIXES:
  575. for target in targets:
  576. item = "%s/%s" % (prefix, suffix)
  577. item = item.replace(BRUTE_DOC_ROOT_TARGET_MARK, target).replace("//", '/').rstrip('/')
  578. directories.append(item)
  579. if BRUTE_DOC_ROOT_TARGET_MARK not in prefix:
  580. break
  581. infoMsg = "using common directories: %s" % ','.join(directories)
  582. logger.info(infoMsg)
  583. msg = "use additional custom directories [Enter for None]: "
  584. answer = readInput(msg)
  585. if answer:
  586. directories.extend(answer.split(','))
  587. else:
  588. directories = defaultDocRoot
  589. return directories
  590. def getAutoDirectories():
  591. retVal = set("/")
  592. if kb.absFilePaths:
  593. infoMsg = "retrieved web server absolute paths: "
  594. infoMsg += "'%s'" % ", ".join(ntToPosixSlashes(path) for path in kb.absFilePaths)
  595. logger.info(infoMsg)
  596. for absFilePath in kb.absFilePaths:
  597. if absFilePath:
  598. directory = directoryPath(absFilePath)
  599. directory = ntToPosixSlashes(directory)
  600. retVal.add(directory)
  601. else:
  602. warnMsg = "unable to retrieve automatically any web server path"
  603. logger.warn(warnMsg)
  604. _ = extractRegexResult(r"//[^/]+?(?P<result>/.*)/", conf.url) # web directory
  605. if _:
  606. retVal.add(_)
  607. return list(retVal)
  608. def filePathToSafeString(filePath):
  609. """
  610. Returns string representation of a given filepath safe for a single filename usage
  611. >>> filePathToSafeString('C:/Windows/system32')
  612. 'C__Windows_system32'
  613. """
  614. retVal = filePath.replace("/", "_").replace("\\", "_")
  615. retVal = retVal.replace(" ", "_").replace(":", "_")
  616. return retVal
  617. def singleTimeDebugMessage(message):
  618. singleTimeLogMessage(message, logging.DEBUG)
  619. def singleTimeWarnMessage(message):
  620. singleTimeLogMessage(message, logging.WARN)
  621. def singleTimeLogMessage(message, level=logging.INFO, flag=None):
  622. if flag is None:
  623. flag = hash(message)
  624. if not conf.smokeTest and flag not in kb.singleLogFlags:
  625. kb.singleLogFlags.add(flag)
  626. logger.log(level, message)
  627. def boldifyMessage(message):
  628. retVal = message
  629. if any(_ in message for _ in BOLD_PATTERNS):
  630. retVal = setColor(message, True)
  631. return retVal
  632. def setColor(message, bold=False):
  633. retVal = message
  634. level = extractRegexResult(r"\[(?P<result>[A-Z ]+)\]", message) or kb.get("stickyLevel")
  635. if message and getattr(LOGGER_HANDLER, "is_tty", False): # colorizing handler
  636. if bold:
  637. retVal = colored(message, color=None, on_color=None, attrs=("bold",))
  638. elif level:
  639. level = getattr(logging, level, None) if isinstance(level, basestring) else level
  640. _ = LOGGER_HANDLER.level_map.get(level)
  641. if _:
  642. background, foreground, bold = _
  643. retVal = colored(message, color=foreground, on_color="on_%s" % background if background else None, attrs=("bold",) if bold else None)
  644. kb.stickyLevel = level if message and message[-1] != "\n" else None
  645. return retVal
  646. def dataToStdout(data, forceOutput=False, bold=False, content_type=None, status=CONTENT_STATUS.IN_PROGRESS):
  647. """
  648. Writes text to the stdout (console) stream
  649. """
  650. message = ""
  651. if not kb.get("threadException"):
  652. if forceOutput or not getCurrentThreadData().disableStdOut:
  653. if kb.get("multiThreadMode"):
  654. logging._acquireLock()
  655. if isinstance(data, unicode):
  656. message = stdoutencode(data)
  657. else:
  658. message = data
  659. if hasattr(conf, "api"):
  660. sys.stdout.write(message, status, content_type)
  661. else:
  662. sys.stdout.write(setColor(message, bold))
  663. try:
  664. sys.stdout.flush()
  665. except IOError:
  666. pass
  667. if kb.get("multiThreadMode"):
  668. logging._releaseLock()
  669. 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')
  670. def dataToTrafficFile(data):
  671. if not conf.trafficFile:
  672. return
  673. try:
  674. conf.trafficFP.write(data)
  675. conf.trafficFP.flush()
  676. except IOError, ex:
  677. errMsg = "something went wrong while trying "
  678. errMsg += "to write to the traffic file '%s' ('%s')" % (conf.trafficFile, ex)
  679. raise SqlmapGenericException(errMsg)
  680. def dataToDumpFile(dumpFile, data):
  681. dumpFile.write(data)
  682. dumpFile.flush()
  683. def dataToOutFile(filename, data):
  684. retVal = None
  685. if data:
  686. retVal = os.path.join(conf.filePath, filePathToSafeString(filename))
  687. with codecs.open(retVal, "wb", UNICODE_ENCODING) as f:
  688. f.write(data)
  689. return retVal
  690. def readInput(message, default=None, checkBatch=True):
  691. """
  692. Reads input from terminal
  693. """
  694. retVal = None
  695. kb.stickyLevel = None
  696. message = getUnicode(message)
  697. if "\n" in message:
  698. message += "%s> " % ("\n" if message.count("\n") > 1 else "")
  699. elif message[-1] == ']':
  700. message += " "
  701. if kb.prependFlag:
  702. message = "\n%s" % message
  703. kb.prependFlag = False
  704. if conf.answers:
  705. for item in conf.answers.split(','):
  706. question = item.split('=')[0].strip()
  707. answer = item.split('=')[1] if len(item.split('=')) > 1 else None
  708. if answer and question.lower() in message.lower():
  709. retVal = getUnicode(answer, UNICODE_ENCODING)
  710. infoMsg = "%s%s" % (message, retVal)
  711. logger.info(infoMsg)
  712. debugMsg = "used the given answer"
  713. logger.debug(debugMsg)
  714. break
  715. if retVal is None:
  716. if checkBatch and conf.batch:
  717. if isListLike(default):
  718. options = ",".join(getUnicode(opt, UNICODE_ENCODING) for opt in default)
  719. elif default:
  720. options = getUnicode(default, UNICODE_ENCODING)
  721. else:
  722. options = unicode()
  723. dataToStdout("\r%s%s\n" % (message, options), forceOutput=True, bold=True)
  724. debugMsg = "used the default behaviour, running in batch mode"
  725. logger.debug(debugMsg)
  726. retVal = default
  727. else:
  728. logging._acquireLock()
  729. dataToStdout("\r%s" % message, forceOutput=True, bold=True)
  730. kb.prependFlag = False
  731. try:
  732. retVal = raw_input() or default
  733. retVal = getUnicode(retVal, system=True) if retVal else retVal
  734. except:
  735. time.sleep(0.05) # Reference: http://www.gossamer-threads.com/lists/python/python/781893
  736. kb.prependFlag = True
  737. raise SqlmapUserQuitException
  738. finally:
  739. logging._releaseLock()
  740. return retVal
  741. def randomRange(start=0, stop=1000, seed=None):
  742. """
  743. Returns random integer value in given range
  744. >>> random.seed(0)
  745. >>> randomRange(1, 500)
  746. 423
  747. """
  748. randint = random.WichmannHill(seed).randint if seed is not None else random.randint
  749. return int(randint(start, stop))
  750. def randomInt(length=4, seed=None):
  751. """
  752. Returns random integer value with provided number of digits
  753. >>> random.seed(0)
  754. >>> randomInt(6)
  755. 874254
  756. """
  757. choice = random.WichmannHill(seed).choice if seed is not None else random.choice
  758. return int("".join(choice(string.digits if _ != 0 else string.digits.replace('0', '')) for _ in xrange(0, length)))
  759. def randomStr(length=4, lowercase=False, alphabet=None, seed=None):
  760. """
  761. Returns random string value with provided number of characters
  762. >>> random.seed(0)
  763. >>> randomStr(6)
  764. 'RNvnAv'
  765. """
  766. choice = random.WichmannHill(seed).choice if seed is not None else random.choice
  767. if alphabet:
  768. retVal = "".join(choice(alphabet) for _ in xrange(0, length))
  769. elif lowercase:
  770. retVal = "".join(choice(string.ascii_lowercase) for _ in xrange(0, length))
  771. else:
  772. retVal = "".join(choice(string.ascii_letters) for _ in xrange(0, length))
  773. return retVal
  774. def sanitizeStr(value):
  775. """
  776. Sanitizes string value in respect to newline and line-feed characters
  777. >>> sanitizeStr('foo\\n\\rbar')
  778. u'foo bar'
  779. """
  780. return getUnicode(value).replace("\n", " ").replace("\r", "")
  781. def checkFile(filename):
  782. """
  783. Checks for file existence
  784. """
  785. if not os.path.isfile(filename):
  786. raise SqlmapFilePathException("unable to read file '%s'" % filename)
  787. def banner():
  788. """
  789. This function prints sqlmap banner with its version
  790. """
  791. _ = """\n %s - %s\n %s\n\n""" % (VERSION_STRING, DESCRIPTION, SITE)
  792. dataToStdout(_, forceOutput=True)
  793. def parsePasswordHash(password):
  794. """
  795. In case of Microsoft SQL Server password hash value is expanded to its components
  796. """
  797. blank = " " * 8
  798. if not password or password == " ":
  799. password = NULL
  800. if Backend.isDbms(DBMS.MSSQL) and password != NULL and isHexEncodedString(password):
  801. hexPassword = password
  802. password = "%s\n" % hexPassword
  803. password += "%sheader: %s\n" % (blank, hexPassword[:6])
  804. password += "%ssalt: %s\n" % (blank, hexPassword[6:14])
  805. password += "%smixedcase: %s\n" % (blank, hexPassword[14:54])
  806. if not Backend.isVersionWithin(("2005", "2008")):
  807. password += "%suppercase: %s" % (blank, hexPassword[54:])
  808. return password
  809. def cleanQuery(query):
  810. """
  811. Switch all SQL statement (alike) keywords to upper case
  812. """
  813. retVal = query
  814. for sqlStatements in SQL_STATEMENTS.values():
  815. for sqlStatement in sqlStatements:
  816. sqlStatementEsc = sqlStatement.replace("(", "\\(")
  817. queryMatch = re.search("(%s)" % sqlStatementEsc, query, re.I)
  818. if queryMatch and "sys_exec" not in query:
  819. retVal = retVal.replace(queryMatch.group(1), sqlStatement.upper())
  820. return retVal
  821. def setPaths():
  822. """
  823. Sets absolute paths for project directories and files
  824. """
  825. # sqlmap paths
  826. paths.SQLMAP_EXTRAS_PATH = os.path.join(paths.SQLMAP_ROOT_PATH, "extra")
  827. paths.SQLMAP_PROCS_PATH = os.path.join(paths.SQLMAP_ROOT_PATH, "procs")
  828. paths.SQLMAP_SHELL_PATH = os.path.join(paths.SQLMAP_ROOT_PATH, "shell")
  829. paths.SQLMAP_TAMPER_PATH = os.path.join(paths.SQLMAP_ROOT_PATH, "tamper")
  830. paths.SQLMAP_WAF_PATH = os.path.join(paths.SQLMAP_ROOT_PATH, "waf")
  831. paths.SQLMAP_TXT_PATH = os.path.join(paths.SQLMAP_ROOT_PATH, "txt")
  832. paths.SQLMAP_UDF_PATH = os.path.join(paths.SQLMAP_ROOT_PATH, "udf")
  833. paths.SQLMAP_XML_PATH = os.path.join(paths.SQLMAP_ROOT_PATH, "xml")
  834. paths.SQLMAP_XML_BANNER_PATH = os.path.join(paths.SQLMAP_XML_PATH, "banner")
  835. paths.SQLMAP_OUTPUT_PATH = paths.get("SQLMAP_OUTPUT_PATH", os.path.join(paths.SQLMAP_ROOT_PATH, "output"))
  836. paths.SQLMAP_DUMP_PATH = os.path.join(paths.SQLMAP_OUTPUT_PATH, "%s", "dump")
  837. paths.SQLMAP_FILES_PATH = os.path.join(paths.SQLMAP_OUTPUT_PATH, "%s", "files")
  838. # sqlmap files
  839. paths.SQLMAP_HISTORY = os.path.join(os.path.expanduser('~'), ".sqlmap_history")
  840. paths.SQLMAP_CONFIG = os.path.join(paths.SQLMAP_ROOT_PATH, "sqlmap-%s.conf" % randomStr())
  841. paths.COMMON_COLUMNS = os.path.join(paths.SQLMAP_TXT_PATH, "common-columns.txt")
  842. paths.COMMON_TABLES = os.path.join(paths.SQLMAP_TXT_PATH, "common-tables.txt")
  843. paths.COMMON_OUTPUTS = os.path.join(paths.SQLMAP_TXT_PATH, 'common-outputs.txt')
  844. paths.SQL_KEYWORDS = os.path.join(paths.SQLMAP_TXT_PATH, "keywords.txt")
  845. paths.SMALL_DICT = os.path.join(paths.SQLMAP_TXT_PATH, "smalldict.txt")
  846. paths.USER_AGENTS = os.path.join(paths.SQLMAP_TXT_PATH, "user-agents.txt")
  847. paths.WORDLIST = os.path.join(paths.SQLMAP_TXT_PATH, "wordlist.zip")
  848. paths.ERRORS_XML = os.path.join(paths.SQLMAP_XML_PATH, "errors.xml")
  849. paths.PAYLOADS_XML = os.path.join(paths.SQLMAP_XML_PATH, "payloads.xml")
  850. paths.INJECTIONS_XML = os.path.join(paths.SQLMAP_XML_PATH, "injections.xml")
  851. paths.LIVE_TESTS_XML = os.path.join(paths.SQLMAP_XML_PATH, "livetests.xml")
  852. paths.QUERIES_XML = os.path.join(paths.SQLMAP_XML_PATH, "queries.xml")
  853. paths.GENERIC_XML = os.path.join(paths.SQLMAP_XML_BANNER_PATH, "generic.xml")
  854. paths.MSSQL_XML = os.path.join(paths.SQLMAP_XML_BANNER_PATH, "mssql.xml")
  855. paths.MYSQL_XML = os.path.join(paths.SQLMAP_XML_BANNER_PATH, "mysql.xml")
  856. paths.ORACLE_XML = os.path.join(paths.SQLMAP_XML_BANNER_PATH, "oracle.xml")
  857. paths.PGSQL_XML = os.path.join(paths.SQLMAP_XML_BANNER_PATH, "postgresql.xml")
  858. def weAreFrozen():
  859. """
  860. Returns whether we are frozen via py2exe.
  861. This will affect how we find out where we are located.
  862. Reference: http://www.py2exe.org/index.cgi/WhereAmI
  863. """
  864. return hasattr(sys, "frozen")
  865. def parseTargetDirect():
  866. """
  867. Parse target dbms and set some attributes into the configuration singleton.
  868. """
  869. if not conf.direct:
  870. return
  871. details = None
  872. remote = False
  873. for dbms in SUPPORTED_DBMS:
  874. 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)
  875. if details:
  876. conf.dbms = details.group("dbms")
  877. if details.group('credentials'):
  878. conf.dbmsUser = details.group("user")
  879. conf.dbmsPass = details.group("pass")
  880. else:
  881. if conf.dbmsCred:
  882. conf.dbmsUser, conf.dbmsPass = conf.dbmsCred.split(':')
  883. else:
  884. conf.dbmsUser = unicode()
  885. conf.dbmsPass = unicode()
  886. if not conf.dbmsPass:
  887. conf.dbmsPass = None
  888. if details.group("remote"):
  889. remote = True
  890. conf.hostname = details.group("hostname").strip()
  891. conf.port = int(details.group("port"))
  892. else:
  893. conf.hostname = "localhost"
  894. conf.port = 0
  895. conf.dbmsDb = details.group("db")
  896. conf.parameters[None] = "direct connection"
  897. break
  898. if not details:
  899. errMsg = "invalid target details, valid syntax is for instance "
  900. errMsg += "'mysql://USER:PASSWORD@DBMS_IP:DBMS_PORT/DATABASE_NAME' "
  901. errMsg += "or 'access://DATABASE_FILEPATH'"
  902. raise SqlmapSyntaxException(errMsg)
  903. for dbmsName, data in DBMS_DICT.items():
  904. if conf.dbms in data[0]:
  905. try:
  906. if dbmsName in (DBMS.ACCESS, DBMS.SQLITE, DBMS.FIREBIRD):
  907. if remote:
  908. warnMsg = "direct connection over the network for "
  909. warnMsg += "%s DBMS is not supported" % dbmsName
  910. logger.warn(warnMsg)
  911. conf.hostname = "localhost"
  912. conf.port = 0
  913. elif not remote:
  914. errMsg = "missing remote connection details"
  915. raise SqlmapSyntaxException(errMsg)
  916. if dbmsName in (DBMS.MSSQL, DBMS.SYBASE):
  917. import _mssql
  918. import pymssql
  919. if not hasattr(pymssql, "__version__") or pymssql.__version__ < "1.0.2":
  920. errMsg = "'%s' third-party library must be " % data[1]
  921. errMsg += "version >= 1.0.2 to work properly. "
  922. errMsg += "Download from '%s'" % data[2]
  923. raise SqlmapMissingDependence(errMsg)
  924. elif dbmsName == DBMS.MYSQL:
  925. import pymysql
  926. elif dbmsName == DBMS.PGSQL:
  927. import psycopg2
  928. elif dbmsName == DBMS.ORACLE:
  929. import cx_Oracle
  930. elif dbmsName == DBMS.SQLITE:
  931. import sqlite3
  932. elif dbmsName == DBMS.ACCESS:
  933. import pyodbc
  934. elif dbmsName == DBMS.FIREBIRD:
  935. import kinterbasdb
  936. except ImportError:
  937. if _sqlalchemy and data[3] in _sqlalchemy.dialects.__all__:
  938. pass
  939. else:
  940. errMsg = "sqlmap requires '%s' third-party library " % data[1]
  941. errMsg += "in order to directly connect to the database "
  942. errMsg += "%s. You can download it from '%s'" % (dbmsName, data[2])
  943. errMsg += ". Alternative is to use a package 'python-sqlalchemy' "
  944. errMsg += "with support for dialect '%s' installed" % data[3]
  945. raise SqlmapMissingDependence(errMsg)
  946. def parseTargetUrl():
  947. """
  948. Parse target URL and set some attributes into the configuration singleton.
  949. """
  950. if not conf.url:
  951. return
  952. originalUrl = conf.url
  953. if re.search("\[.+\]", conf.url) and not socket.has_ipv6:
  954. errMsg = "IPv6 addressing is not supported "
  955. errMsg += "on this platform"
  956. raise SqlmapGenericException(errMsg)
  957. if not re.search("^http[s]*://", conf.url, re.I):
  958. if ":443/" in conf.url:
  959. conf.url = "https://" + conf.url
  960. else:
  961. conf.url = "http://" + conf.url
  962. if CUSTOM_INJECTION_MARK_CHAR in conf.url:
  963. conf.url = conf.url.replace('?', URI_QUESTION_MARKER)
  964. urlSplit = urlparse.urlsplit(conf.url)
  965. 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")))
  966. conf.scheme = urlSplit.scheme.strip().lower() if not conf.forceSSL else "https"
  967. conf.path = urlSplit.path.strip()
  968. conf.hostname = hostnamePort[0].strip()
  969. conf.ipv6 = conf.hostname != conf.hostname.strip("[]")
  970. conf.hostname = conf.hostname.strip("[]").replace(CUSTOM_INJECTION_MARK_CHAR, "")
  971. try:
  972. _ = conf.hostname.encode("idna")
  973. except UnicodeError:
  974. _ = None
  975. if any((_ is None, re.search(r'\s', conf.hostname), '..' in conf.hostname, conf.hostname.startswith('.'))):
  976. errMsg = "invalid target URL"
  977. raise SqlmapSyntaxException(errMsg)
  978. if len(hostnamePort) == 2:
  979. try:
  980. conf.port = int(hostnamePort[1])
  981. except:
  982. errMsg = "invalid target URL"
  983. raise SqlmapSyntaxException(errMsg)
  984. elif conf.scheme == "https":
  985. conf.port = 443
  986. else:
  987. conf.port = 80
  988. if urlSplit.query:
  989. conf.parameters[PLACE.GET] = urldecode(urlSplit.query) if urlSplit.query and urlencode(DEFAULT_GET_POST_DELIMITER, None) not in urlSplit.query else urlSplit.query
  990. conf.url = getUnicode("%s://%s:%d%s" % (conf.scheme, ("[%s]" % conf.hostname) if conf.ipv6 else conf.hostname, conf.port, conf.path))
  991. conf.url = conf.url.replace(URI_QUESTION_MARKER, '?')
  992. if not conf.referer and intersect(REFERER_ALIASES, conf.testParameter, True):
  993. debugMsg = "setting the HTTP Referer header to the target URL"
  994. logger.debug(debugMsg)
  995. conf.httpHeaders = filter(lambda (key, value): key != HTTP_HEADER.REFERER, conf.httpHeaders)
  996. conf.httpHeaders.append((HTTP_HEADER.REFERER, conf.url))
  997. if not conf.host and intersect(HOST_ALIASES, conf.testParameter, True):
  998. debugMsg = "setting the HTTP Host header to the target URL"
  999. logger.debug(debugMsg)
  1000. conf.httpHeaders = filter(lambda (key, value): key != HTTP_HEADER.HOST, conf.httpHeaders)
  1001. conf.httpHeaders.append((HTTP_HEADER.HOST, getHostHeader(conf.url)))
  1002. if conf.url != originalUrl:
  1003. kb.originalUrls[conf.url] = originalUrl
  1004. def expandAsteriskForColumns(expression):
  1005. """
  1006. If the user provided an asterisk rather than the column(s)
  1007. name, sqlmap will retrieve the columns itself and reprocess
  1008. the SQL query string (expression)
  1009. """
  1010. asterisk = re.search("^SELECT(\s+TOP\s+[\d]+)?\s+\*\s+FROM\s+([\w\.\_]+)\s*", expression, re.I)
  1011. if asterisk:
  1012. infoMsg = "you did not provide the fields in your query. "
  1013. infoMsg += "sqlmap will retrieve the column names itself"
  1014. logger.info(infoMsg)
  1015. _ = asterisk.group(2).replace("..", ".")
  1016. conf.db, conf.tbl = _.split(".", 1) if '.' in _ else (None, _)
  1017. conf.db = safeSQLIdentificatorNaming(conf.db)
  1018. conf.tbl = safeSQLIdentificatorNaming(conf.tbl, True)
  1019. columnsDict = conf.dbmsHandler.getColumns(onlyColNames=True)
  1020. if columnsDict and conf.db in columnsDict and conf.tbl in columnsDict[conf.db]:
  1021. columns = columnsDict[conf.db][conf.tbl].keys()
  1022. columns.sort()
  1023. columnsStr = ", ".join(column for column in columns)
  1024. expression = expression.replace("*", columnsStr, 1)
  1025. infoMsg = "the query with expanded column name(s) is: "
  1026. infoMsg += "%s" % expression
  1027. logger.info(infoMsg)
  1028. return expression
  1029. def getLimitRange(count, dump=False, plusOne=False):
  1030. """
  1031. Returns range of values used in limit/offset constructs
  1032. >>> [_ for _ in getLimitRange(10)]
  1033. [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
  1034. """
  1035. retVal = None
  1036. count = int(count)
  1037. limitStart, limitStop = 1, count
  1038. if dump:
  1039. if isinstance(conf.limitStop, int) and conf.limitStop > 0 and conf.limitStop < limitStop:
  1040. limitStop = conf.limitStop
  1041. if isinstance(conf.limitStart, int) and conf.limitStart > 0 and conf.limitStart <= limitStop:
  1042. limitStart = conf.limitStart
  1043. retVal = xrange(limitStart, limitStop + 1) if plusOne else xrange(limitStart - 1, limitStop)
  1044. return retVal
  1045. def parseUnionPage(page):
  1046. """
  1047. Returns resulting items from UNION query inside provided page content
  1048. """
  1049. if page is None:
  1050. return None
  1051. if page.startswith(kb.chars.start) and page.endswith(kb.chars.stop):
  1052. if len(page) > LARGE_OUTPUT_THRESHOLD:
  1053. warnMsg = "large output detected. This might take a while"
  1054. logger.warn(warnMsg)
  1055. data = BigArray()
  1056. keys = set()
  1057. for match in re.finditer("%s(.*?)%s" % (kb.chars.start, kb.chars.stop), page, re.DOTALL | re.IGNORECASE):
  1058. entry = match.group(1)
  1059. if kb.chars.start in entry:
  1060. entry = entry.split(kb.chars.start)[-1]
  1061. if kb.unionDuplicates:
  1062. key = entry.lower()
  1063. if key not in keys:
  1064. keys.add(key)
  1065. else:
  1066. continue
  1067. entry = entry.split(kb.chars.delimiter)
  1068. if conf.hexConvert:
  1069. entry = applyFunctionRecursively(entry, decodeHexValue)
  1070. if kb.safeCharEncode:
  1071. entry = applyFunctionRecursively(entry, safecharencode)
  1072. data.append(entry[0] if len(entry) == 1 else entry)
  1073. else:
  1074. data = page
  1075. if len(data) == 1 and isinstance(data[0], basestring):
  1076. data = data[0]
  1077. return data
  1078. def parseFilePaths(page):
  1079. """
  1080. Detects (possible) absolute system paths inside the provided page content
  1081. """
  1082. if page:
  1083. 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.]+)"):
  1084. for match in re.finditer(regex, page):
  1085. absFilePath = match.group("result").strip()
  1086. page = page.replace(absFilePath, "")
  1087. if isWindowsDriveLetterPath(absFilePath):
  1088. absFilePath = posixToNtSlashes(absFilePath)
  1089. if absFilePath not in kb.absFilePaths:
  1090. kb.absFilePaths.add(absFilePath)
  1091. def getLocalIP():
  1092. """
  1093. Get local IP address (exposed to the remote/target)
  1094. """
  1095. retVal = None
  1096. try:
  1097. s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  1098. s.connect((conf.hostname, conf.port))
  1099. retVal, _ = s.getsockname()
  1100. s.close()
  1101. except:
  1102. debugMsg = "there was an error in opening socket "
  1103. debugMsg += "connection toward '%s'" % conf.hostname
  1104. logger.debug(debugMsg)
  1105. return retVal
  1106. def getRemoteIP():
  1107. """
  1108. Get remote/target IP address
  1109. """
  1110. retVal = None
  1111. try:
  1112. retVal = socket.gethostbyname(conf.hostname)
  1113. except socket.gaierror:
  1114. errMsg = "address resolution problem "
  1115. errMsg += "occurred for hostname '%s'" % conf.hostname
  1116. singleTimeLogMessage(errMsg, logging.ERROR)
  1117. return retVal
  1118. def getFileType(filePath):
  1119. try:
  1120. _ = magic.from_file(filePath)
  1121. except:
  1122. return "unknown"
  1123. return "text" if "ASCII" in _ or "text" in _ else "binary"
  1124. def getCharset(charsetType=None):
  1125. """
  1126. Returns list with integers representing characters of a given
  1127. charset type appropriate for inference techniques
  1128. >>> getCharset(CHARSET_TYPE.BINARY)
  1129. [0, 1, 47, 48, 49]
  1130. """
  1131. asciiTbl = []
  1132. if charsetType is None:
  1133. asciiTbl.extend(xrange(0, 128))
  1134. # 0 or 1
  1135. elif charsetType == CHARSET_TYPE.BINARY:
  1136. asciiTbl.extend([0, 1])
  1137. asciiTbl.extend(xrange(47, 50))
  1138. # Digits
  1139. elif charsetType == CHARSET_TYPE.DIGITS:
  1140. asciiTbl.extend([0, 1])
  1141. asciiTbl.extend(xrange(47, 58))
  1142. # Hexadecimal
  1143. elif charsetType == CHARSET_TYPE.HEXADECIMAL:
  1144. asciiTbl.extend([

Large files files are truncated, but you can click here to view the full file