1#!/usr/bin/env python 2 3""" 4Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) 5See the file 'LICENSE' for copying permission 6""" 7 8import re 9 10from lib.core.common import isListLike 11from lib.core.common import readInput 12from lib.core.common import safeSQLIdentificatorNaming 13from lib.core.common import unsafeSQLIdentificatorNaming 14from lib.core.data import conf 15from lib.core.data import kb 16from lib.core.data import logger 17from lib.core.data import paths 18from lib.core.data import queries 19from lib.core.enums import DBMS 20from lib.core.exception import SqlmapMissingMandatoryOptionException 21from lib.core.exception import SqlmapNoneDataException 22from lib.core.exception import SqlmapUserQuitException 23from lib.core.settings import CURRENT_DB 24from lib.utils.brute import columnExists 25from lib.utils.pivotdumptable import pivotDumpTable 26from plugins.generic.enumeration import Enumeration as GenericEnumeration 27from thirdparty import six 28from thirdparty.six.moves import zip as _zip 29 30class Enumeration(GenericEnumeration): 31 def __init__(self): 32 GenericEnumeration.__init__(self) 33 34 kb.data.processChar = lambda x: x.replace('_', ' ') if x else x 35 36 def getPasswordHashes(self): 37 warnMsg = "on SAP MaxDB it is not possible to enumerate the user password hashes" 38 logger.warn(warnMsg) 39 40 return {} 41 42 def getDbs(self): 43 if len(kb.data.cachedDbs) > 0: 44 return kb.data.cachedDbs 45 46 infoMsg = "fetching database names" 47 logger.info(infoMsg) 48 49 rootQuery = queries[DBMS.MAXDB].dbs 50 query = rootQuery.inband.query 51 retVal = pivotDumpTable("(%s) AS %s" % (query, kb.aliasName), ['%s.schemaname' % kb.aliasName], blind=True) 52 53 if retVal: 54 kb.data.cachedDbs = next(six.itervalues(retVal[0])) 55 56 if kb.data.cachedDbs: 57 kb.data.cachedDbs.sort() 58 59 return kb.data.cachedDbs 60 61 def getTables(self, bruteForce=None): 62 if len(kb.data.cachedTables) > 0: 63 return kb.data.cachedTables 64 65 self.forceDbmsEnum() 66 67 if conf.db == CURRENT_DB: 68 conf.db = self.getCurrentDb() 69 70 if conf.db: 71 dbs = conf.db.split(',') 72 else: 73 dbs = self.getDbs() 74 75 for db in (_ for _ in dbs if _): 76 dbs[dbs.index(db)] = safeSQLIdentificatorNaming(db) 77 78 infoMsg = "fetching tables for database" 79 infoMsg += "%s: %s" % ("s" if len(dbs) > 1 else "", ", ".join(db if isinstance(db, six.string_types) else db[0] for db in sorted(dbs))) 80 logger.info(infoMsg) 81 82 rootQuery = queries[DBMS.MAXDB].tables 83 84 for db in dbs: 85 query = rootQuery.inband.query % (("'%s'" % db) if db != "USER" else 'USER') 86 retVal = pivotDumpTable("(%s) AS %s" % (query, kb.aliasName), ['%s.tablename' % kb.aliasName], blind=True) 87 88 if retVal: 89 for table in list(retVal[0].values())[0]: 90 if db not in kb.data.cachedTables: 91 kb.data.cachedTables[db] = [table] 92 else: 93 kb.data.cachedTables[db].append(table) 94 95 for db, tables in kb.data.cachedTables.items(): 96 kb.data.cachedTables[db] = sorted(tables) if tables else tables 97 98 return kb.data.cachedTables 99 100 def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None, dumpMode=False): 101 self.forceDbmsEnum() 102 103 if conf.db is None or conf.db == CURRENT_DB: 104 if conf.db is None: 105 warnMsg = "missing database parameter. sqlmap is going " 106 warnMsg += "to use the current database to enumerate " 107 warnMsg += "table(s) columns" 108 logger.warn(warnMsg) 109 110 conf.db = self.getCurrentDb() 111 112 elif conf.db is not None: 113 if ',' in conf.db: 114 errMsg = "only one database name is allowed when enumerating " 115 errMsg += "the tables' columns" 116 raise SqlmapMissingMandatoryOptionException(errMsg) 117 118 conf.db = safeSQLIdentificatorNaming(conf.db) 119 120 if conf.col: 121 colList = conf.col.split(',') 122 else: 123 colList = [] 124 125 if conf.exclude: 126 colList = [_ for _ in colList if re.search(conf.exclude, _, re.I) is None] 127 128 for col in colList: 129 colList[colList.index(col)] = safeSQLIdentificatorNaming(col) 130 131 if conf.tbl: 132 tblList = conf.tbl.split(',') 133 else: 134 self.getTables() 135 136 if len(kb.data.cachedTables) > 0: 137 tblList = list(kb.data.cachedTables.values()) 138 139 if tblList and isListLike(tblList[0]): 140 tblList = tblList[0] 141 else: 142 errMsg = "unable to retrieve the tables " 143 errMsg += "on database '%s'" % unsafeSQLIdentificatorNaming(conf.db) 144 raise SqlmapNoneDataException(errMsg) 145 146 for tbl in tblList: 147 tblList[tblList.index(tbl)] = safeSQLIdentificatorNaming(tbl, True) 148 149 if bruteForce: 150 resumeAvailable = False 151 152 for tbl in tblList: 153 for db, table, colName, colType in kb.brute.columns: 154 if db == conf.db and table == tbl: 155 resumeAvailable = True 156 break 157 158 if resumeAvailable and not conf.freshQueries or colList: 159 columns = {} 160 161 for column in colList: 162 columns[column] = None 163 164 for tbl in tblList: 165 for db, table, colName, colType in kb.brute.columns: 166 if db == conf.db and table == tbl: 167 columns[colName] = colType 168 169 if conf.db in kb.data.cachedColumns: 170 kb.data.cachedColumns[safeSQLIdentificatorNaming(conf.db)][safeSQLIdentificatorNaming(tbl, True)] = columns 171 else: 172 kb.data.cachedColumns[safeSQLIdentificatorNaming(conf.db)] = {safeSQLIdentificatorNaming(tbl, True): columns} 173 174 return kb.data.cachedColumns 175 176 message = "do you want to use common column existence check? [y/N/q] " 177 choice = readInput(message, default='Y' if 'Y' in message else 'N').upper() 178 179 if choice == 'N': 180 return 181 elif choice == 'Q': 182 raise SqlmapUserQuitException 183 else: 184 return columnExists(paths.COMMON_COLUMNS) 185 186 rootQuery = queries[DBMS.MAXDB].columns 187 188 for tbl in tblList: 189 if conf.db is not None and len(kb.data.cachedColumns) > 0 and conf.db in kb.data.cachedColumns and tbl in kb.data.cachedColumns[conf.db]: 190 infoMsg = "fetched tables' columns on " 191 infoMsg += "database '%s'" % unsafeSQLIdentificatorNaming(conf.db) 192 logger.info(infoMsg) 193 194 return {conf.db: kb.data.cachedColumns[conf.db]} 195 196 if dumpMode and colList: 197 table = {} 198 table[safeSQLIdentificatorNaming(tbl, True)] = dict((_, None) for _ in colList) 199 kb.data.cachedColumns[safeSQLIdentificatorNaming(conf.db)] = table 200 continue 201 202 infoMsg = "fetching columns " 203 infoMsg += "for table '%s' " % unsafeSQLIdentificatorNaming(tbl) 204 infoMsg += "on database '%s'" % unsafeSQLIdentificatorNaming(conf.db) 205 logger.info(infoMsg) 206 207 query = rootQuery.inband.query % (unsafeSQLIdentificatorNaming(tbl), ("'%s'" % unsafeSQLIdentificatorNaming(conf.db)) if unsafeSQLIdentificatorNaming(conf.db) != "USER" else 'USER') 208 retVal = pivotDumpTable("(%s) AS %s" % (query, kb.aliasName), ['%s.columnname' % kb.aliasName, '%s.datatype' % kb.aliasName, '%s.len' % kb.aliasName], blind=True) 209 210 if retVal: 211 table = {} 212 columns = {} 213 214 for columnname, datatype, length in _zip(retVal[0]["%s.columnname" % kb.aliasName], retVal[0]["%s.datatype" % kb.aliasName], retVal[0]["%s.len" % kb.aliasName]): 215 columns[safeSQLIdentificatorNaming(columnname)] = "%s(%s)" % (datatype, length) 216 217 table[tbl] = columns 218 kb.data.cachedColumns[conf.db] = table 219 220 return kb.data.cachedColumns 221 222 def getPrivileges(self, *args, **kwargs): 223 warnMsg = "on SAP MaxDB it is not possible to enumerate the user privileges" 224 logger.warn(warnMsg) 225 226 return {} 227 228 def search(self): 229 warnMsg = "on SAP MaxDB search option is not available" 230 logger.warn(warnMsg) 231 232 def getHostname(self): 233 warnMsg = "on SAP MaxDB it is not possible to enumerate the hostname" 234 logger.warn(warnMsg) 235 236 def getStatements(self): 237 warnMsg = "on SAP MaxDB it is not possible to enumerate the SQL statements" 238 logger.warn(warnMsg) 239 240 return [] 241