sqlmap/lib/core/target.py

387 lines
13 KiB
Python
Raw Normal View History

2008-10-15 19:38:22 +04:00
#!/usr/bin/env python
"""
2008-10-15 19:56:32 +04:00
$Id$
2008-10-15 19:38:22 +04:00
2011-07-08 00:10:03 +04:00
Copyright (c) 2006-2011 sqlmap developers (http://www.sqlmap.org/)
2010-10-15 03:18:29 +04:00
See the file 'doc/COPYING' for copying permission
2008-10-15 19:38:22 +04:00
"""
import codecs
2008-10-15 19:38:22 +04:00
import os
import re
import tempfile
2008-10-15 19:38:22 +04:00
import time
from lib.core.common import dataToSessionFile
from lib.core.common import intersect
2008-10-15 19:38:22 +04:00
from lib.core.common import paramToDict
2010-10-10 22:56:43 +04:00
from lib.core.common import readInput
from lib.core.convert import urldecode
from lib.core.data import cmdLineOptions
2008-10-15 19:38:22 +04:00
from lib.core.data import conf
from lib.core.data import kb
from lib.core.data import logger
2008-10-15 19:38:22 +04:00
from lib.core.data import paths
from lib.core.dump import dumper
2010-11-08 12:44:32 +03:00
from lib.core.enums import HTTPMETHOD
from lib.core.enums import PLACE
2008-10-15 19:38:22 +04:00
from lib.core.exception import sqlmapFilePathException
from lib.core.exception import sqlmapGenericException
from lib.core.exception import sqlmapSyntaxException
2011-05-08 10:17:43 +04:00
from lib.core.exception import sqlmapUserQuitException
2010-12-18 13:02:01 +03:00
from lib.core.option import __setDBMS
from lib.core.option import __setKnowledgeBaseAttributes
2008-10-15 19:38:22 +04:00
from lib.core.session import resumeConfKb
from lib.core.settings import REFERER_ALIASES
from lib.core.settings import RESULTS_FILE_FORMAT
from lib.core.settings import SOAP_REGEX
from lib.core.settings import UNENCODED_ORIGINAL_VALUE
2011-01-30 14:36:03 +03:00
from lib.core.settings import UNICODE_ENCODING
from lib.core.settings import URI_INJECTABLE_REGEX
2011-02-04 15:25:14 +03:00
from lib.core.settings import URI_INJECTION_MARK_CHAR
from lib.core.settings import USER_AGENT_ALIASES
from lib.utils.hashdb import HashDB
from lib.core.xmldump import dumper as xmldumper
2010-10-10 22:56:43 +04:00
from lib.request.connect import Connect as Request
2008-10-15 19:38:22 +04:00
def __setRequestParams():
"""
Check and set the parameters and perform checks on 'data' option for
HTTP method POST.
"""
if conf.direct:
conf.parameters[None] = "direct connection"
return
2008-10-15 19:38:22 +04:00
__testableParameters = False
# Perform checks on GET parameters
2010-11-08 12:44:32 +03:00
if conf.parameters.has_key(PLACE.GET) and conf.parameters[PLACE.GET]:
parameters = conf.parameters[PLACE.GET]
__paramDict = paramToDict(PLACE.GET, parameters)
2008-10-15 19:38:22 +04:00
if __paramDict:
2010-11-08 12:44:32 +03:00
conf.paramDict[PLACE.GET] = __paramDict
2008-10-15 19:38:22 +04:00
__testableParameters = True
# Perform checks on POST parameters
2010-11-08 12:44:32 +03:00
if conf.method == HTTPMETHOD.POST and not conf.data:
2008-10-15 19:38:22 +04:00
errMsg = "HTTP POST method depends on HTTP data value to be posted"
raise sqlmapSyntaxException, errMsg
if conf.data:
if hasattr(conf.data, UNENCODED_ORIGINAL_VALUE):
original = getattr(conf.data, UNENCODED_ORIGINAL_VALUE)
conf.data = type(conf.data)(conf.data.replace("\n", " "))
setattr(conf.data, UNENCODED_ORIGINAL_VALUE, original)
else:
conf.data = conf.data.replace("\n", " ")
# Check if POST data is in xml syntax
if re.match(SOAP_REGEX, conf.data, re.I | re.M):
2011-04-18 01:39:00 +04:00
place = PLACE.SOAP
else:
2011-04-18 01:39:00 +04:00
place = PLACE.POST
conf.parameters[place] = conf.data
__paramDict = paramToDict(place, conf.data)
2008-10-15 19:38:22 +04:00
if __paramDict:
2011-04-18 01:39:00 +04:00
conf.paramDict[place] = __paramDict
2008-10-15 19:38:22 +04:00
__testableParameters = True
2010-11-08 12:44:32 +03:00
conf.method = HTTPMETHOD.POST
if re.search(URI_INJECTABLE_REGEX, conf.url, re.I) and not any(map(lambda place: place in conf.parameters, [PLACE.GET, PLACE.POST])):
warnMsg = "you've provided target url without any GET "
warnMsg += "parameters (e.g. www.site.com/article.php?id=1) "
warnMsg += "and without providing any POST parameters "
warnMsg += "through --data option"
2011-05-08 10:17:43 +04:00
logger.warn(warnMsg)
message = "do you want to try URI injections "
message += "in the target url itself? [Y/n/q] "
test = readInput(message, default="Y")
if not test or test[0] in ("y", "Y"):
conf.url = "%s%s" % (conf.url, URI_INJECTION_MARK_CHAR)
elif test[0] in ("n", "N"):
pass
elif test[0] in ("q", "Q"):
raise sqlmapUserQuitException
2011-02-04 16:04:19 +03:00
if URI_INJECTION_MARK_CHAR in conf.url:
2010-11-08 12:44:32 +03:00
conf.parameters[PLACE.URI] = conf.url
conf.paramDict[PLACE.URI] = {}
2011-02-04 16:04:19 +03:00
parts = conf.url.split(URI_INJECTION_MARK_CHAR)
2011-04-22 01:15:23 +04:00
for i in xrange(len(parts)-1):
result = str()
2011-04-22 01:15:23 +04:00
for j in xrange(len(parts)):
result += parts[j]
2011-04-22 01:15:23 +04:00
if i == j:
2011-02-04 16:04:19 +03:00
result += URI_INJECTION_MARK_CHAR
2011-04-22 01:15:23 +04:00
2011-02-04 16:04:19 +03:00
conf.paramDict[PLACE.URI]["#%d%s" % (i+1, URI_INJECTION_MARK_CHAR)] = result
2011-04-22 01:15:23 +04:00
2011-02-04 16:04:19 +03:00
conf.url = conf.url.replace(URI_INJECTION_MARK_CHAR, str())
2010-09-22 15:56:35 +04:00
__testableParameters = True
2008-10-15 19:38:22 +04:00
# Perform checks on Cookie parameters
if conf.cookie:
2010-11-08 12:44:32 +03:00
conf.parameters[PLACE.COOKIE] = conf.cookie
__paramDict = paramToDict(PLACE.COOKIE, conf.cookie)
2008-10-15 19:38:22 +04:00
if __paramDict:
2010-11-08 12:44:32 +03:00
conf.paramDict[PLACE.COOKIE] = __paramDict
2008-10-15 19:38:22 +04:00
__testableParameters = True
# Perform checks on User-Agent header value
if conf.httpHeaders:
for httpHeader, headerValue in conf.httpHeaders:
if httpHeader == PLACE.UA:
# No need for url encoding/decoding the user agent
conf.parameters[PLACE.UA] = urldecode(headerValue)
2008-10-15 19:38:22 +04:00
condition = any([not conf.testParameter, intersect(conf.testParameter, USER_AGENT_ALIASES)])
2008-10-15 19:38:22 +04:00
if condition:
2010-11-08 12:44:32 +03:00
conf.paramDict[PLACE.UA] = { PLACE.UA: headerValue }
2008-10-15 19:38:22 +04:00
__testableParameters = True
2011-02-12 02:07:03 +03:00
elif httpHeader == PLACE.REFERER:
# No need for url encoding/decoding the referer
conf.parameters[PLACE.REFERER] = urldecode(headerValue)
condition = any([not conf.testParameter, intersect(conf.testParameter, REFERER_ALIASES)])
2011-02-12 02:07:03 +03:00
if condition:
conf.paramDict[PLACE.REFERER] = { PLACE.REFERER: headerValue }
__testableParameters = True
2008-10-15 19:38:22 +04:00
if not conf.parameters:
2011-04-30 17:20:05 +04:00
errMsg = "you did not provide any GET, POST and Cookie "
2011-02-14 01:02:47 +03:00
errMsg += "parameter, neither an User-Agent or Referer header"
2008-10-15 19:38:22 +04:00
raise sqlmapGenericException, errMsg
elif not __testableParameters:
2011-04-30 17:20:05 +04:00
errMsg = "all testable parameters you provided are not present "
2008-10-15 19:38:22 +04:00
errMsg += "within the GET, POST and Cookie parameters"
raise sqlmapGenericException, errMsg
2011-09-26 17:01:43 +04:00
def __setHashDB():
"""
Check and set the HashDB SQLite file for query resume functionality.
"""
if not conf.hashDBFile:
conf.hashDBFile = "%s%shashdb" % (conf.outputPath, os.sep)
if os.path.exists(conf.hashDBFile):
if conf.flushSession:
try:
os.remove(conf.hashDBFile)
logger.info("flushing query storage file")
except OSError, msg:
errMsg = "unable to flush the hashdb file (%s)" % msg
raise sqlmapFilePathException, errMsg
conf.hashDB = HashDB(conf.hashDBFile)
2008-10-15 19:38:22 +04:00
def __setOutputResume():
"""
Check and set the output text file and the resume functionality.
"""
if not conf.sessionFile:
conf.sessionFile = "%s%ssession" % (conf.outputPath, os.sep)
logger.info("using '%s' as session file" % conf.sessionFile)
if os.path.exists(conf.sessionFile):
2010-03-04 16:01:18 +03:00
if not conf.flushSession:
2011-01-30 14:36:03 +03:00
readSessionFP = codecs.open(conf.sessionFile, "r", UNICODE_ENCODING, 'replace')
__url_cache = set()
__expression_cache = {}
2010-05-30 18:53:13 +04:00
for line in readSessionFP.readlines(): # xreadlines doesn't return unicode strings when codec.open() is used
2010-03-04 16:01:18 +03:00
if line.count("][") == 4:
line = line.split("][")
2010-03-04 16:01:18 +03:00
if len(line) != 5:
continue
2010-03-04 16:01:18 +03:00
url, _, _, expression, value = line
2010-03-04 16:01:18 +03:00
if not value:
continue
2010-03-04 16:01:18 +03:00
if url[0] == "[":
url = url[1:]
2010-05-30 18:53:13 +04:00
value = value.rstrip('\r\n') # Strips both chars independently
if url not in ( conf.url, conf.hostname ):
2010-03-04 16:01:18 +03:00
continue
if url not in __url_cache:
2010-03-04 16:01:18 +03:00
kb.resumedQueries[url] = {}
kb.resumedQueries[url][expression] = value
__url_cache.add(url)
2010-05-11 17:55:30 +04:00
__expression_cache[url] = set(expression)
2010-03-04 16:01:18 +03:00
resumeConfKb(expression, url, value)
if expression not in __expression_cache[url]:
2010-03-04 16:01:18 +03:00
kb.resumedQueries[url][expression] = value
__expression_cache[url].add(value)
2010-03-04 16:01:18 +03:00
elif len(value) >= len(kb.resumedQueries[url][expression]):
kb.resumedQueries[url][expression] = value
if kb.injection.place is not None and kb.injection.parameter is not None:
kb.injections.append(kb.injection)
2010-03-04 16:01:18 +03:00
readSessionFP.close()
else:
try:
os.remove(conf.sessionFile)
logger.info("flushing session file")
except OSError, msg:
errMsg = "unable to flush the session file (%s)" % msg
raise sqlmapFilePathException, errMsg
2008-10-15 19:38:22 +04:00
try:
2011-01-30 14:36:03 +03:00
conf.sessionFP = codecs.open(conf.sessionFile, "a", UNICODE_ENCODING)
dataToSessionFile("\n[%s]\n" % time.strftime("%X %x"))
except IOError:
errMsg = "unable to write on the session file specified"
raise sqlmapFilePathException, errMsg
2008-10-15 19:38:22 +04:00
def __setResultsFile():
"""
Create results file for storing results of running in a
multiple target mode.
"""
if not conf.multipleTargets:
return
if not conf.resultsFP:
conf.resultsFilename = "%s%s%s" % (paths.SQLMAP_OUTPUT_PATH, os.sep, time.strftime(RESULTS_FILE_FORMAT).lower())
conf.resultsFP = codecs.open(conf.resultsFilename, "w+", UNICODE_ENCODING)
conf.resultsFP.writelines("Target url,Place,Parameter,Techniques%s" % os.linesep)
logger.info("using '%s' as results file" % conf.resultsFilename)
2008-10-15 19:38:22 +04:00
def __createFilesDir():
"""
Create the file directory.
"""
if not conf.rFile:
return
conf.filePath = paths.SQLMAP_FILES_PATH % conf.hostname
if not os.path.isdir(conf.filePath):
os.makedirs(conf.filePath, 0755)
def __createDumpDir():
"""
Create the dump directory.
"""
2010-06-02 15:01:41 +04:00
if not conf.dumpTable and not conf.dumpAll and not conf.search:
2008-10-15 19:38:22 +04:00
return
conf.dumpPath = paths.SQLMAP_DUMP_PATH % conf.hostname
if not os.path.isdir(conf.dumpPath):
os.makedirs(conf.dumpPath, 0755)
def __configureDumper():
if hasattr(conf, 'xmlFile') and conf.xmlFile:
conf.dumper = xmldumper
else:
conf.dumper = dumper
conf.dumper.setOutputFile()
2010-03-15 14:55:13 +03:00
def __createTargetDirs():
"""
Create the output directory.
"""
if not os.path.isdir(paths.SQLMAP_OUTPUT_PATH):
try:
os.makedirs(paths.SQLMAP_OUTPUT_PATH, 0755)
2010-12-09 12:24:20 +03:00
except OSError, msg:
tempDir = tempfile.mkdtemp(prefix='output')
2010-12-09 12:24:20 +03:00
warnMsg = "unable to create default root output directory "
warnMsg += "'%s' (%s). " % (paths.SQLMAP_OUTPUT_PATH, msg)
warnMsg += "using temporary directory '%s' instead" % tempDir
logger.warn(warnMsg)
paths.SQLMAP_OUTPUT_PATH = tempDir
conf.outputPath = "%s%s%s" % (paths.SQLMAP_OUTPUT_PATH, os.sep, conf.hostname)
if not os.path.isdir(conf.outputPath):
try:
os.makedirs(conf.outputPath, 0755)
2010-12-09 12:24:20 +03:00
except OSError, msg:
tempDir = tempfile.mkdtemp(prefix='output')
2010-12-09 12:24:20 +03:00
warnMsg = "unable to create output directory "
warnMsg += "'%s' (%s). " % (conf.outputPath, msg)
warnMsg += "using temporary directory '%s' instead" % tempDir
logger.warn(warnMsg)
conf.outputPath = tempDir
__createDumpDir()
__createFilesDir()
__configureDumper()
def __restoreCmdLineOptions():
"""
Restore command line options that could be possibly
changed during the testing of previous target.
"""
conf.regexp = cmdLineOptions.regexp
conf.string = cmdLineOptions.string
conf.textOnly = cmdLineOptions.textOnly
2008-10-15 19:38:22 +04:00
def initTargetEnv():
"""
Initialize target environment.
"""
if conf.multipleTargets:
if conf.sessionFP:
conf.sessionFP.close()
if conf.hashDB:
conf.hashDB.close()
2010-04-06 14:15:19 +04:00
if conf.cj:
conf.cj.clear()
2010-04-06 14:15:19 +04:00
2011-04-30 17:20:05 +04:00
conf.paramDict = {}
conf.parameters = {}
conf.sessionFile = None
conf.hashDBFile = None
2010-12-18 13:02:01 +03:00
__setKnowledgeBaseAttributes(False)
__restoreCmdLineOptions()
2010-12-18 13:02:01 +03:00
__setDBMS()
2010-03-15 14:33:34 +03:00
def setupTargetEnv():
2010-03-15 14:55:13 +03:00
__createTargetDirs()
2008-10-15 19:38:22 +04:00
__setRequestParams()
__setOutputResume()
2011-09-26 17:01:43 +04:00
__setHashDB()
__setResultsFile()