mirror of
https://github.com/sqlmapproject/sqlmap.git
synced 2024-11-22 09:36:35 +03:00
727664aea7
the web application technology by parsing also HTTP response Server header. Refactor libraries and plugins that parses XML to fingerprint and show on standard output the information. Updated changelog.
590 lines
16 KiB
Python
590 lines
16 KiB
Python
#!/usr/bin/env python
|
|
|
|
"""
|
|
$Id$
|
|
|
|
This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
|
|
|
|
Copyright (c) 2006-2008 Bernardo Damele A. G. <bernardo.damele@gmail.com>
|
|
and Daniele Bellucci <daniele.bellucci@gmail.com>
|
|
|
|
sqlmap is free software; you can redistribute it and/or modify it under
|
|
the terms of the GNU General Public License as published by the Free
|
|
Software Foundation version 2 of the License.
|
|
|
|
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
|
details.
|
|
|
|
You should have received a copy of the GNU General Public License along
|
|
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
|
|
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
"""
|
|
|
|
|
|
|
|
import cookielib
|
|
import logging
|
|
import os
|
|
import re
|
|
import time
|
|
import urllib2
|
|
import urlparse
|
|
|
|
from lib.core.common import parseTargetUrl
|
|
from lib.core.common import paths
|
|
from lib.core.common import randomRange
|
|
from lib.core.common import randomStr
|
|
from lib.core.common import readInput
|
|
from lib.core.common import sanitizeStr
|
|
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.datatype import advancedDict
|
|
from lib.core.exception import sqlmapFilePathException
|
|
from lib.core.exception import sqlmapGenericException
|
|
from lib.core.exception import sqlmapSyntaxException
|
|
from lib.core.exception import sqlmapUnsupportedDBMSException
|
|
from lib.core.optiondict import optDict
|
|
from lib.core.settings import MSSQL_ALIASES
|
|
from lib.core.settings import MYSQL_ALIASES
|
|
from lib.core.settings import SITE
|
|
from lib.core.settings import SUPPORTED_DBMS
|
|
from lib.core.settings import VERSION_STRING
|
|
from lib.core.update import update
|
|
from lib.parse.configfile import configFileParser
|
|
from lib.parse.queriesfile import queriesParser
|
|
from lib.request.proxy import ProxyHTTPSHandler
|
|
from lib.utils.google import Google
|
|
|
|
|
|
authHandler = urllib2.BaseHandler()
|
|
proxyHandler = urllib2.BaseHandler()
|
|
|
|
|
|
def __urllib2Opener():
|
|
"""
|
|
This function creates the urllib2 OpenerDirector.
|
|
"""
|
|
|
|
global authHandler
|
|
global proxyHandler
|
|
|
|
debugMsg = "creating HTTP requests opener object"
|
|
logger.debug(debugMsg)
|
|
|
|
conf.cj = cookielib.LWPCookieJar()
|
|
opener = urllib2.build_opener(proxyHandler, authHandler, urllib2.HTTPCookieProcessor(conf.cj))
|
|
|
|
urllib2.install_opener(opener)
|
|
|
|
|
|
def __setGoogleDorking():
|
|
"""
|
|
This function checks if the way to request testable hosts is through
|
|
Google dorking then requests to Google the search parameter, parses
|
|
the results and save the testable hosts into the knowledge base.
|
|
"""
|
|
|
|
global proxyHandler
|
|
|
|
if not conf.googleDork:
|
|
return
|
|
|
|
debugMsg = "initializing Google dorking requests"
|
|
logger.debug(debugMsg)
|
|
|
|
logMsg = "first request to Google to get the session cookie"
|
|
logger.info(logMsg)
|
|
|
|
googleObj = Google(proxyHandler)
|
|
googleObj.getCookie()
|
|
|
|
matches = googleObj.search(conf.googleDork)
|
|
|
|
if not matches:
|
|
errMsg = "unable to find results for your "
|
|
errMsg += "Google dork expression"
|
|
raise sqlmapGenericException, errMsg
|
|
|
|
kb.targetUrls = googleObj.getTargetUrls()
|
|
|
|
if kb.targetUrls:
|
|
logMsg = "sqlmap got %d results for your " % len(matches)
|
|
logMsg += "Google dork expression, "
|
|
|
|
if len(matches) == len(kb.targetUrls):
|
|
logMsg += "all "
|
|
else:
|
|
logMsg += "%d " % len(kb.targetUrls)
|
|
|
|
logMsg += "of them are testable hosts"
|
|
logger.info(logMsg)
|
|
else:
|
|
errMsg = "sqlmap got %d results " % len(matches)
|
|
errMsg += "for your Google dork expression, but none of them "
|
|
errMsg += "have GET parameters to test for SQL injection"
|
|
raise sqlmapGenericException, errMsg
|
|
|
|
|
|
def __setRemoteDBMS():
|
|
"""
|
|
Checks and set the back-end DBMS option.
|
|
"""
|
|
|
|
if not conf.dbms:
|
|
return
|
|
|
|
debugMsg = "forcing back-end DBMS to user defined value"
|
|
logger.debug(debugMsg)
|
|
|
|
conf.dbms = conf.dbms.lower()
|
|
firstRegExp = "(%s|%s)" % ("|".join([alias for alias in MSSQL_ALIASES]),
|
|
"|".join([alias for alias in MYSQL_ALIASES]))
|
|
dbmsRegExp = re.search("%s ([\d\.]+)" % firstRegExp, conf.dbms)
|
|
|
|
if dbmsRegExp:
|
|
conf.dbms = dbmsRegExp.group(1)
|
|
kb.dbmsVersion = [dbmsRegExp.group(2)]
|
|
|
|
if conf.dbms not in SUPPORTED_DBMS:
|
|
errMsg = "you provided an unsupported back-end database management "
|
|
errMsg += "system. The supported DBMS are MySQL, PostgreSQL, "
|
|
errMsg += "Microsoft SQL Server and Oracle. If you do not know "
|
|
errMsg += "the back-end DBMS, do not provide it and sqlmap will "
|
|
errMsg += "fingerprint it for you."
|
|
raise sqlmapUnsupportedDBMSException, errMsg
|
|
|
|
|
|
def __setThreads():
|
|
if conf.threads <= 0:
|
|
conf.threads = 1
|
|
|
|
|
|
def __setHTTPProxy():
|
|
"""
|
|
Check and set the HTTP proxy to pass by all HTTP requests.
|
|
"""
|
|
|
|
global proxyHandler
|
|
|
|
if not conf.proxy:
|
|
return
|
|
|
|
parseTargetUrl()
|
|
|
|
debugMsg = "setting the HTTP proxy to pass by all HTTP requests"
|
|
logger.debug(debugMsg)
|
|
|
|
__proxySplit = urlparse.urlsplit(conf.proxy)
|
|
__hostnamePort = __proxySplit[1].split(":")
|
|
|
|
__scheme = __proxySplit[0]
|
|
__hostname = __hostnamePort[0]
|
|
__port = None
|
|
|
|
if len(__hostnamePort) == 2:
|
|
__port = int(__hostnamePort[1])
|
|
|
|
if not __scheme or not __hostname or not __port:
|
|
errMsg = "proxy value must be in format 'http://url:port'"
|
|
raise sqlmapSyntaxException, errMsg
|
|
|
|
__proxyString = "%s:%d" % (__hostname, __port)
|
|
|
|
# Workaround for http://bugs.python.org/issue1424152 (urllib/urllib2:
|
|
# HTTPS over (Squid) Proxy fails) as long as HTTP over SSL requests
|
|
# can't be tunneled over an HTTP proxy natively by Python urllib2
|
|
# standard library
|
|
if conf.scheme == "https":
|
|
proxyHandler = ProxyHTTPSHandler(__proxyString)
|
|
else:
|
|
proxyHandler = urllib2.ProxyHandler({"http": __proxyString})
|
|
|
|
|
|
def __setHTTPAuthentication():
|
|
"""
|
|
Check and set the HTTP authentication method (Basic or Digest),
|
|
username and password to perform HTTP requests with.
|
|
"""
|
|
|
|
global authHandler
|
|
|
|
if not conf.aType and not conf.aCred:
|
|
return
|
|
|
|
elif conf.aType and not conf.aCred:
|
|
errMsg = "you specified the HTTP Authentication type, but "
|
|
errMsg += "did not provide the credentials"
|
|
raise sqlmapSyntaxException, errMsg
|
|
|
|
elif not conf.aType and conf.aCred:
|
|
errMsg = "you specified the HTTP Authentication credentials, "
|
|
errMsg += "but did not provide the type"
|
|
raise sqlmapSyntaxException, errMsg
|
|
|
|
parseTargetUrl()
|
|
|
|
debugMsg = "setting the HTTP Authentication type and credentials"
|
|
logger.debug(debugMsg)
|
|
|
|
aTypeLower = conf.aType.lower()
|
|
|
|
if aTypeLower not in ( "basic", "digest" ):
|
|
errMsg = "HTTP Authentication type value must be "
|
|
errMsg += "Basic or Digest"
|
|
raise sqlmapSyntaxException, errMsg
|
|
|
|
aCredRegExp = re.search("^(.*?)\:(.*?)$", conf.aCred)
|
|
|
|
if not aCredRegExp:
|
|
errMsg = "HTTP Authentication credentials value must be "
|
|
errMsg += "in format username:password"
|
|
raise sqlmapSyntaxException, errMsg
|
|
|
|
authUsername = aCredRegExp.group(1)
|
|
authPassword = aCredRegExp.group(2)
|
|
|
|
passwordMgr = urllib2.HTTPPasswordMgrWithDefaultRealm()
|
|
passwordMgr.add_password(None, "%s://%s" % (conf.scheme, conf.hostname), authUsername, authPassword)
|
|
|
|
if aTypeLower == "basic":
|
|
authHandler = urllib2.HTTPBasicAuthHandler(passwordMgr)
|
|
elif aTypeLower == "digest":
|
|
authHandler = urllib2.HTTPDigestAuthHandler(passwordMgr)
|
|
|
|
|
|
def __setHTTPMethod():
|
|
"""
|
|
Check and set the HTTP method to perform HTTP requests through.
|
|
"""
|
|
|
|
if conf.method:
|
|
debugMsg = "setting the HTTP method to perform HTTP requests through"
|
|
logger.debug(debugMsg)
|
|
|
|
conf.method = conf.method.upper()
|
|
|
|
if conf.method not in ("GET", "POST"):
|
|
warnMsg = "'%s' " % conf.method
|
|
warnMsg += "is an unsupported HTTP method, "
|
|
warnMsg += "setting to default method, GET"
|
|
logger.warn(warnMsg)
|
|
|
|
conf.method = "GET"
|
|
else:
|
|
conf.method = "GET"
|
|
|
|
|
|
def __setHTTPStandardHeaders():
|
|
conf.httpHeaders.append(("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"))
|
|
conf.httpHeaders.append(("Accept-Language", "en-us,en;q=0.5"))
|
|
conf.httpHeaders.append(("Accept-Encoding", "gzip,deflate"))
|
|
conf.httpHeaders.append(("Accept-Charset", "ISO-8859-15,utf-8;q=0.7,*;q=0.7"))
|
|
|
|
|
|
def __defaultHTTPUserAgent():
|
|
"""
|
|
@return: default sqlmap HTTP User-Agent header
|
|
@rtype: C{str}
|
|
"""
|
|
|
|
return "%s (%s)" % (VERSION_STRING, SITE)
|
|
|
|
|
|
def __setHTTPUserAgent():
|
|
"""
|
|
Set the HTTP User-Agent header.
|
|
Depending on the user options it can be:
|
|
|
|
* The default sqlmap string
|
|
* A default value read as user option
|
|
* A random value read from a list of User-Agent headers from a
|
|
file choosed as user option
|
|
"""
|
|
|
|
if conf.agent:
|
|
debugMsg = "setting the HTTP User-Agent header"
|
|
logger.debug(debugMsg)
|
|
|
|
conf.httpHeaders.append(("User-Agent", conf.agent))
|
|
return
|
|
|
|
if not conf.userAgentsFile:
|
|
conf.httpHeaders.append(("User-Agent", __defaultHTTPUserAgent()))
|
|
return
|
|
|
|
debugMsg = "fetching random HTTP User-Agent header from "
|
|
debugMsg += "file '%s'" % conf.userAgentsFile
|
|
logger.debug(debugMsg)
|
|
|
|
try:
|
|
fd = open(conf.userAgentsFile, "r")
|
|
except IOError:
|
|
warnMsg = "unable to read HTTP User-Agent header "
|
|
warnMsg += "file '%s'" % conf.userAgentsFile
|
|
logger.warn(warnMsg)
|
|
|
|
conf.httpHeaders.append(("User-Agent", __defaultHTTPUserAgent()))
|
|
|
|
return
|
|
|
|
__count = 0
|
|
__userAgents = []
|
|
|
|
while True:
|
|
line = fd.readline()
|
|
|
|
if not line:
|
|
break
|
|
|
|
__userAgents.append(line)
|
|
__count += 1
|
|
|
|
fd.close()
|
|
|
|
if __count == 1:
|
|
__userAgent = __userAgents[0]
|
|
else:
|
|
__userAgent = __userAgents[randomRange(stop=__count)]
|
|
|
|
__userAgent = sanitizeStr(__userAgent)
|
|
conf.httpHeaders.append(("User-Agent", __userAgent))
|
|
|
|
logMsg = "fetched random HTTP User-Agent header from "
|
|
logMsg += "file '%s': %s" % (conf.userAgentsFile, __userAgent)
|
|
logger.info(logMsg)
|
|
|
|
|
|
def __setHTTPReferer():
|
|
"""
|
|
Set the HTTP Referer
|
|
"""
|
|
|
|
if conf.referer:
|
|
debugMsg = "setting the HTTP Referer header"
|
|
logger.debug(debugMsg)
|
|
|
|
conf.httpHeaders.append(("Referer", conf.referer))
|
|
|
|
|
|
def __setHTTPCookies():
|
|
"""
|
|
Set the HTTP Cookie header
|
|
"""
|
|
|
|
if conf.cookie:
|
|
debugMsg = "setting the HTTP Cookie header"
|
|
logger.debug(debugMsg)
|
|
|
|
conf.httpHeaders.append(("Connection", "Keep-Alive"))
|
|
conf.httpHeaders.append(("Cookie", conf.cookie))
|
|
|
|
|
|
def __cleanupOptions():
|
|
"""
|
|
Cleanup configuration attributes.
|
|
"""
|
|
|
|
debugMsg = "cleaning up configuration parameters"
|
|
logger.debug(debugMsg)
|
|
|
|
if conf.testParameter:
|
|
conf.testParameter = conf.testParameter.replace(" ", "")
|
|
conf.testParameter = conf.testParameter.split(",")
|
|
else:
|
|
conf.testParameter = []
|
|
|
|
if conf.db:
|
|
conf.db = conf.db.replace(" ", "")
|
|
|
|
if conf.tbl:
|
|
conf.tbl = conf.tbl.replace(" ", "")
|
|
|
|
if conf.col:
|
|
conf.col = conf.col.replace(" ", "")
|
|
|
|
if conf.user:
|
|
conf.user = conf.user.replace(" ", "")
|
|
|
|
if conf.delay:
|
|
conf.delay = float(conf.delay)
|
|
|
|
|
|
def __setConfAttributes():
|
|
"""
|
|
This function set some needed attributes into the configuration
|
|
singleton.
|
|
"""
|
|
|
|
debugMsg = "initializing the configuration"
|
|
logger.debug(debugMsg)
|
|
|
|
conf.cj = None
|
|
conf.dbmsHandler = None
|
|
conf.dumpPath = None
|
|
conf.httpHeaders = []
|
|
conf.hostname = None
|
|
conf.loggedToOut = None
|
|
conf.outputPath = None
|
|
conf.paramDict = {}
|
|
conf.parameters = {}
|
|
conf.path = None
|
|
conf.port = None
|
|
conf.scheme = None
|
|
conf.sessionFP = None
|
|
conf.start = True
|
|
|
|
|
|
def __setKnowledgeBaseAttributes():
|
|
"""
|
|
This function set some needed attributes into the knowledge base
|
|
singleton.
|
|
"""
|
|
|
|
debugMsg = "initializing the knowledge base"
|
|
logger.debug(debugMsg)
|
|
|
|
kb.absFilePaths = set()
|
|
kb.defaultResult = None
|
|
kb.docRoot = None
|
|
kb.dbms = None
|
|
kb.dbmsDetected = False
|
|
kb.dbmsVersion = None
|
|
kb.bannerFp = {}
|
|
kb.headersCount = 0
|
|
kb.headersFp = {}
|
|
kb.htmlFp = []
|
|
kb.injParameter = None
|
|
kb.injPlace = None
|
|
kb.injType = None
|
|
kb.parenthesis = None
|
|
kb.resumedQueries = {}
|
|
kb.targetUrls = set()
|
|
kb.timeTest = None
|
|
kb.unionComment = ""
|
|
kb.unionCount = None
|
|
kb.unionPosition = None
|
|
|
|
|
|
def __saveCmdline():
|
|
"""
|
|
Saves the command line options on a sqlmap configuration INI file
|
|
format.
|
|
"""
|
|
|
|
if not conf.saveCmdline:
|
|
return
|
|
|
|
debugMsg = "saving command line options on a sqlmap configuration INI file"
|
|
logger.debug(debugMsg)
|
|
|
|
userOpts = {}
|
|
|
|
for family in optDict.keys():
|
|
userOpts[family] = []
|
|
|
|
for option, value in conf.items():
|
|
for family, optionData in optDict.items():
|
|
if option in optionData:
|
|
userOpts[family].append((option, value, optionData[option]))
|
|
|
|
confFP = open(paths.SQLMAP_CONFIG, "w")
|
|
|
|
for family, optionData in userOpts.items():
|
|
confFP.write("[%s]\n" % family)
|
|
|
|
optionData.sort()
|
|
|
|
for option, value, datatype in optionData:
|
|
if value == None:
|
|
if datatype == "boolean":
|
|
value = "False"
|
|
elif datatype in ( "integer", "float" ):
|
|
if option == "threads":
|
|
value = "1"
|
|
else:
|
|
value = "0"
|
|
elif datatype == "string":
|
|
value = ""
|
|
|
|
confFP.write("%s = %s\n" % (option, value))
|
|
|
|
confFP.write("\n")
|
|
|
|
confFP.flush()
|
|
confFP.close()
|
|
|
|
infoMsg = "saved command line options on '%s' configuration file" % paths.SQLMAP_CONFIG
|
|
logger.info(infoMsg)
|
|
|
|
|
|
def __setVerbosity():
|
|
"""
|
|
This function set the verbosity of sqlmap output messages.
|
|
"""
|
|
|
|
if not conf.verbose:
|
|
conf.verbose = 0
|
|
return
|
|
|
|
conf.verbose = int(conf.verbose)
|
|
|
|
if conf.verbose <= 1:
|
|
logger.setLevel(logging.INFO)
|
|
elif conf.verbose > 1 and conf.eta:
|
|
conf.verbose = 1
|
|
logger.setLevel(logging.INFO)
|
|
elif conf.verbose == 2:
|
|
logger.setLevel(logging.DEBUG)
|
|
elif conf.verbose == 3:
|
|
logger.setLevel(9)
|
|
elif conf.verbose >= 4:
|
|
logger.setLevel(8)
|
|
|
|
|
|
def __mergeOptions(inputOptions):
|
|
"""
|
|
Merge command line options with configuration file options.
|
|
|
|
@param inputOptions: optparse object with command line options.
|
|
@type inputOptions: C{instance}
|
|
"""
|
|
|
|
if inputOptions.configFile:
|
|
configFileParser(inputOptions.configFile)
|
|
|
|
for key, value in inputOptions.__dict__.items():
|
|
if not conf.has_key(key) or conf[key] == None or value != None:
|
|
conf[key] = value
|
|
|
|
|
|
def init(inputOptions=advancedDict()):
|
|
"""
|
|
Set attributes into both configuration and knowledge base singletons
|
|
based upon command line and configuration file options.
|
|
"""
|
|
|
|
__mergeOptions(inputOptions)
|
|
__setVerbosity()
|
|
__saveCmdline()
|
|
__setConfAttributes()
|
|
__setKnowledgeBaseAttributes()
|
|
__cleanupOptions()
|
|
__setHTTPCookies()
|
|
__setHTTPReferer()
|
|
__setHTTPUserAgent()
|
|
__setHTTPStandardHeaders()
|
|
__setHTTPMethod()
|
|
__setHTTPAuthentication()
|
|
__setHTTPProxy()
|
|
__setThreads()
|
|
__setRemoteDBMS()
|
|
__setGoogleDorking()
|
|
__urllib2Opener()
|
|
|
|
update()
|
|
queriesParser()
|