sqlmap/lib/core/dump.py

537 lines
18 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
2011-12-28 19:59:30 +04:00
import cStringIO
import re
import os
2011-12-28 19:59:30 +04:00
import threading
2008-10-15 19:38:22 +04:00
2011-02-21 01:41:42 +03:00
from lib.core.common import Backend
2008-10-15 19:38:22 +04:00
from lib.core.common import dataToDumpFile
from lib.core.common import dataToStdout
2010-06-02 16:31:36 +04:00
from lib.core.common import getUnicode
from lib.core.common import normalizeUnicode
from lib.core.common import openFile
2010-10-21 13:51:07 +04:00
from lib.core.common import restoreDumpMarkedChars
2011-07-03 02:48:56 +04:00
from lib.core.common import safeCSValue
from lib.core.common import unsafeSQLIdentificatorNaming
2008-10-15 19:38:22 +04:00
from lib.core.data import conf
2010-09-24 17:19:35 +04:00
from lib.core.data import kb
2008-10-15 19:38:22 +04:00
from lib.core.data import logger
2011-02-21 01:41:42 +03:00
from lib.core.enums import DBMS
2011-12-27 15:25:40 +04:00
from lib.core.exception import sqlmapValueException
2010-09-24 17:19:35 +04:00
from lib.core.replication import Replication
2011-12-28 19:59:30 +04:00
from lib.core.settings import BUFFERED_LOG_SIZE
from lib.core.settings import TRIM_STDOUT_DUMP_SIZE
2011-01-30 14:36:03 +03:00
from lib.core.settings import UNICODE_ENCODING
2008-10-15 19:38:22 +04:00
class Dump:
"""
This class defines methods used to parse and output the results
of SQL injection actions
"""
def __init__(self):
self.__outputFile = None
2011-04-30 17:20:05 +04:00
self.__outputFP = None
2011-12-28 19:59:30 +04:00
self.__outputBP = None
self.__lock = threading.Lock()
def __write(self, data, n=True, console=True):
2010-10-15 14:28:06 +04:00
text = "%s%s" % (data, "\n" if n else " ")
if console:
dataToStdout(text)
2010-10-15 14:28:06 +04:00
2011-12-28 19:59:30 +04:00
if kb.get("multiThreadMode"):
self.__lock.acquire()
self.__outputBP.write(text)
if self.__outputBP.tell() > BUFFERED_LOG_SIZE:
self.flush()
if kb.get("multiThreadMode"):
self.__lock.release()
2008-10-15 19:38:22 +04:00
2011-05-11 00:44:36 +04:00
kb.dataOutputFlag = True
2011-12-28 19:59:30 +04:00
def flush(self):
if self.__outputBP and self.__outputFP and self.__outputBP.tell() > 0:
_ = self.__outputBP.getvalue()
self.__outputBP.reset()
self.__outputFP.write(_)
2010-10-21 13:51:07 +04:00
def __formatString(self, inpStr):
return restoreDumpMarkedChars(getUnicode(inpStr))
2008-10-15 19:38:22 +04:00
def setOutputFile(self):
self.__outputFile = "%s%slog" % (conf.outputPath, os.sep)
2011-01-30 14:36:03 +03:00
self.__outputFP = codecs.open(self.__outputFile, "ab", UNICODE_ENCODING)
2011-12-28 19:59:30 +04:00
self.__outputBP = cStringIO.StringIO()
2010-09-26 18:56:55 +04:00
def getOutputFile(self):
return self.__outputFile
def string(self, header, data, sort=True):
2008-10-15 19:38:22 +04:00
if isinstance(data, (list, tuple, set)):
self.lister(header, data, sort)
2008-10-15 19:38:22 +04:00
return
2010-06-02 16:31:36 +04:00
data = getUnicode(data)
2008-10-15 19:38:22 +04:00
if data:
data = self.__formatString(data)
2008-10-15 19:38:22 +04:00
2011-05-23 18:28:05 +04:00
if data[-1] == '\n':
data = data[:-1]
2008-10-15 19:38:22 +04:00
if "\n" in data:
self.__write("%s:\n---\n%s\n---\n" % (header, data))
2008-10-15 19:38:22 +04:00
else:
self.__write("%s: '%s'\n" % (header, data))
else:
self.__write("%s:\tNone\n" % header)
2010-09-24 17:19:35 +04:00
def lister(self, header, elements, sort=True):
if elements:
self.__write("%s [%d]:" % (header, len(elements)))
2008-10-15 19:38:22 +04:00
if sort:
try:
elements = set(elements)
elements = list(elements)
2011-03-18 19:52:46 +03:00
elements.sort(key=lambda x: x.lower() if isinstance(x, basestring) else x)
except:
pass
2008-10-15 19:38:22 +04:00
for element in elements:
if isinstance(element, basestring):
2008-10-15 19:38:22 +04:00
self.__write("[*] %s" % element)
elif isinstance(element, (list, tuple, set)):
2010-06-02 16:31:36 +04:00
self.__write("[*] " + ", ".join(getUnicode(e) for e in element))
2008-10-15 19:38:22 +04:00
if elements:
self.__write("")
def technic(self, header, data):
self.string(header, data)
def banner(self,data):
self.string("banner", data)
def currentUser(self,data):
self.string("current user", data)
def currentDb(self,data):
if Backend.isDbms(DBMS.MAXDB):
2011-02-21 01:41:42 +03:00
self.string("current database (no practical usage on %s)" % Backend.getIdentifiedDbms(), data)
elif Backend.isDbms(DBMS.ORACLE):
self.string("current schema (equivalent to database on %s)" % Backend.getIdentifiedDbms(), data)
2011-02-21 01:41:42 +03:00
else:
self.string("current database", data)
def dba(self,data):
self.string("current user is DBA", data)
def users(self,users):
self.lister("database management system users", users)
2008-10-15 19:38:22 +04:00
def userSettings(self, header, userSettings, subHeader):
self.__areAdmins = set()
if userSettings:
self.__write("%s:" % header)
2008-10-15 19:38:22 +04:00
if isinstance(userSettings, (tuple, list, set)):
self.__areAdmins = userSettings[1]
userSettings = userSettings[0]
users = userSettings.keys()
2011-03-18 19:52:46 +03:00
users.sort(key=lambda x: x.lower() if isinstance(x, basestring) else x)
2008-10-15 19:38:22 +04:00
for user in users:
settings = userSettings[user]
if settings is None:
stringSettings = ""
else:
stringSettings = " [%d]:" % len(settings)
2008-10-15 19:38:22 +04:00
if user in self.__areAdmins:
self.__write("[*] %s (administrator)%s" % (user, stringSettings))
2008-10-15 19:38:22 +04:00
else:
self.__write("[*] %s%s" % (user, stringSettings))
2008-10-15 19:38:22 +04:00
if settings:
settings.sort()
2008-10-15 19:38:22 +04:00
for setting in settings:
self.__write(" %s: %s" % (subHeader, setting))
2008-10-15 19:38:22 +04:00
print
def dbs(self,dbs):
self.lister("available databases", dbs)
2008-10-15 19:38:22 +04:00
def dbTables(self, dbTables):
if isinstance(dbTables, dict) and len(dbTables) > 0:
2010-09-30 16:35:45 +04:00
maxlength = 0
for tables in dbTables.values():
for table in tables:
if isinstance(table, (list, tuple, set)):
table = table[0]
maxlength = max(maxlength, len(normalizeUnicode(table) or str(table)))
2010-09-30 16:35:45 +04:00
lines = "-" * (int(maxlength) + 2)
for db, tables in dbTables.items():
tables.sort()
2010-09-30 16:35:45 +04:00
2011-06-18 16:27:12 +04:00
self.__write("Database: %s" % db if db else "Current database")
2010-09-30 16:35:45 +04:00
if len(tables) == 1:
self.__write("[1 table]")
else:
self.__write("[%d tables]" % len(tables))
self.__write("+%s+" % lines)
for table in tables:
if isinstance(table, (list, tuple, set)):
table = table[0]
blank = " " * (maxlength - len(normalizeUnicode(table) or str(table)))
2010-09-30 16:35:45 +04:00
self.__write("| %s%s |" % (table, blank))
self.__write("+%s+\n" % lines)
else:
self.string("tables", dbTables)
2008-10-15 19:38:22 +04:00
def dbTableColumns(self, tableColumns):
2011-09-19 23:08:08 +04:00
if isinstance(tableColumns, dict) and len(tableColumns) > 0:
for db, tables in tableColumns.items():
if not db:
db = "All"
2008-10-15 19:38:22 +04:00
2011-09-19 23:08:08 +04:00
for table, columns in tables.items():
maxlength1 = 0
maxlength2 = 0
2008-10-15 19:38:22 +04:00
2011-09-19 23:08:08 +04:00
colType = None
2011-09-19 23:08:08 +04:00
colList = columns.keys()
colList.sort(key=lambda x: x.lower() if isinstance(x, basestring) else x)
2008-10-15 19:38:22 +04:00
2011-09-19 23:08:08 +04:00
for column in colList:
colType = columns[column]
2008-10-15 19:38:22 +04:00
2011-09-19 23:08:08 +04:00
maxlength1 = max(maxlength1, len(column or ""))
maxlength2 = max(maxlength2, len(colType or ""))
2011-09-19 23:08:08 +04:00
maxlength1 = max(maxlength1, len("COLUMN"))
lines1 = "-" * (maxlength1 + 2)
2011-09-19 23:08:08 +04:00
if colType is not None:
maxlength2 = max(maxlength2, len("TYPE"))
lines2 = "-" * (maxlength2 + 2)
2011-09-19 23:08:08 +04:00
self.__write("Database: %s\nTable: %s" % (db if db else "Current database", table))
2011-09-19 23:08:08 +04:00
if len(columns) == 1:
self.__write("[1 column]")
else:
self.__write("[%d columns]" % len(columns))
2008-10-15 19:38:22 +04:00
2011-09-19 23:08:08 +04:00
if colType is not None:
self.__write("+%s+%s+" % (lines1, lines2))
else:
self.__write("+%s+" % lines1)
2011-09-19 23:08:08 +04:00
blank1 = " " * (maxlength1 - len("COLUMN"))
2011-09-19 23:08:08 +04:00
if colType is not None:
blank2 = " " * (maxlength2 - len("TYPE"))
2008-10-15 19:38:22 +04:00
2011-09-19 23:08:08 +04:00
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)
2011-09-19 23:08:08 +04:00
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:
2011-09-19 23:08:08 +04:00
self.__write("+%s+%s+\n" % (lines1, lines2))
else:
2011-09-19 23:08:08 +04:00
self.__write("+%s+\n" % lines1)
def dbTablesCount(self, dbTables):
if isinstance(dbTables, dict) and len(dbTables) > 0:
maxlength1 = len("Table")
maxlength2 = len("Entries")
for ctables in dbTables.values():
for tables in ctables.values():
for table in tables:
maxlength1 = max(maxlength1, len(normalizeUnicode(table) or str(table)))
for db, counts in dbTables.items():
2011-06-18 16:27:12 +04:00
self.__write("Database: %s" % db if db else "Current database")
lines1 = "-" * (maxlength1 + 2)
blank1 = " " * (maxlength1 - len("Table"))
lines2 = "-" * (maxlength2 + 2)
blank2 = " " * (maxlength2 - len("Entries"))
self.__write("+%s+%s+" % (lines1, lines2))
self.__write("| Table%s | Entries%s |" % (blank1, blank2))
self.__write("+%s+%s+" % (lines1, lines2))
sortedCounts = counts.keys()
sortedCounts.sort(reverse=True)
for count in sortedCounts:
tables = counts[count]
if count is None:
count = "Unknown"
tables.sort(key=lambda x: x.lower() if isinstance(x, basestring) else x)
for table in tables:
blank1 = " " * (maxlength1 - len(normalizeUnicode(table) or str(table)))
blank2 = " " * (maxlength2 - len(str(count)))
self.__write("| %s%s | %d%s |" % (table, blank1, count, blank2))
self.__write("+%s+%s+\n" % (lines1, lines2))
else:
logger.error("unable to retrieve the number of entries for any table")
2008-10-15 19:38:22 +04:00
def dbTableValues(self, tableValues):
2010-09-24 17:34:46 +04:00
replication = None
2011-04-30 17:20:05 +04:00
rtable = None
2010-09-24 17:34:46 +04:00
if tableValues is None:
return
2008-10-15 19:38:22 +04:00
db = tableValues["__infos__"]["db"]
if not db:
db = "All"
table = tableValues["__infos__"]["table"]
2010-09-24 17:34:46 +04:00
if conf.replicate:
replication = Replication("%s%s%s.sqlite3" % (conf.dumpPath, os.sep, unsafeSQLIdentificatorNaming(db)))
else:
dumpDbPath = "%s%s%s" % (conf.dumpPath, os.sep, unsafeSQLIdentificatorNaming(db))
2008-10-15 19:38:22 +04:00
if not os.path.isdir(dumpDbPath):
os.makedirs(dumpDbPath, 0755)
dumpFileName = "%s%s%s.csv" % (dumpDbPath, os.sep, unsafeSQLIdentificatorNaming(table))
dumpFP = openFile(dumpFileName, "wb")
2008-10-15 19:38:22 +04:00
2011-04-30 17:20:05 +04:00
count = int(tableValues["__infos__"]["count"])
separator = str()
field = 1
fields = len(tableValues) - 1
2008-10-15 19:38:22 +04:00
columns = tableValues.keys()
2011-03-18 19:52:46 +03:00
columns.sort(key=lambda x: x.lower() if isinstance(x, basestring) else x)
2008-10-15 19:38:22 +04:00
for column in columns:
if column != "__infos__":
2011-04-30 17:20:05 +04:00
info = tableValues[column]
lines = "-" * (int(info["length"]) + 2)
2008-10-15 19:38:22 +04:00
separator += "+%s" % lines
separator += "+"
2011-06-18 16:27:12 +04:00
self.__write("Database: %s\nTable: %s" % (db if db else "Current database", table))
2008-10-15 19:38:22 +04:00
2010-09-24 17:19:35 +04:00
if conf.replicate:
2010-09-24 18:34:05 +04:00
cols = []
2010-11-04 15:21:06 +03:00
2010-09-24 18:34:05 +04:00
for column in columns:
if column != "__infos__":
colType = Replication.INTEGER
2010-11-04 15:21:06 +03:00
2010-09-24 18:34:05 +04:00
for value in tableValues[column]['values']:
try:
if not value or re.search("^[\ *]*$", value): #NULL
2010-09-24 18:34:05 +04:00
continue
2010-11-04 15:21:06 +03:00
_ = int(value)
2010-09-24 18:34:05 +04:00
except ValueError:
colType = None
break
2010-11-04 15:21:06 +03:00
2010-09-24 18:34:05 +04:00
if colType is None:
colType = Replication.REAL
2010-11-04 15:21:06 +03:00
2010-09-24 18:34:05 +04:00
for value in tableValues[column]['values']:
try:
if not value or re.search("^[\ *]*$", value): #NULL
2010-09-24 18:34:05 +04:00
continue
2010-11-04 15:21:06 +03:00
_ = float(value)
2010-09-24 18:34:05 +04:00
except ValueError:
colType = None
break
2010-11-04 15:21:06 +03:00
2010-09-24 18:34:05 +04:00
cols.append((column, colType if colType else Replication.TEXT))
2010-11-04 15:21:06 +03:00
2010-09-24 18:34:05 +04:00
rtable = replication.createTable(table, cols)
2010-09-24 17:19:35 +04:00
2008-10-15 19:38:22 +04:00
if count == 1:
self.__write("[1 entry]")
else:
self.__write("[%d entries]" % count)
self.__write(separator)
for column in columns:
if column != "__infos__":
2011-04-30 17:20:05 +04:00
info = tableValues[column]
2008-10-15 19:38:22 +04:00
maxlength = int(info["length"])
2011-04-30 17:20:05 +04:00
blank = " " * (maxlength - len(column))
2008-10-15 19:38:22 +04:00
self.__write("| %s%s" % (column, blank), n=False)
2010-09-24 17:34:46 +04:00
if not conf.replicate:
if field == fields:
2011-07-03 02:48:56 +04:00
dataToDumpFile(dumpFP, "%s" % safeCSValue(column))
else:
2011-11-30 21:39:41 +04:00
dataToDumpFile(dumpFP, "%s%s" % (safeCSValue(column), conf.csvDel))
2008-10-15 19:38:22 +04:00
field += 1
self.__write("|\n%s" % separator)
if not conf.replicate:
2008-10-15 19:38:22 +04:00
dataToDumpFile(dumpFP, "\n")
if conf.replicate:
rtable.beginTransaction()
if count > TRIM_STDOUT_DUMP_SIZE:
2011-12-23 00:54:20 +04:00
warnMsg = "console output will be trimmed to "
warnMsg += "last %d rows due to " % TRIM_STDOUT_DUMP_SIZE
warnMsg += "large table size"
logger.warning(warnMsg)
for i in xrange(count):
console = (i >= count - TRIM_STDOUT_DUMP_SIZE)
2008-10-15 19:38:22 +04:00
field = 1
2010-09-24 17:19:35 +04:00
values = []
2008-10-15 19:38:22 +04:00
for column in columns:
if column != "__infos__":
info = tableValues[column]
if len(info["values"]) <= i:
continue
if info["values"][i] is None:
value = u''
else:
value = getUnicode(info["values"][i])
if re.search("^[\ *]*$", value):
value = "NULL"
2008-10-15 19:38:22 +04:00
2010-09-24 17:19:35 +04:00
values.append(value)
2008-10-15 19:38:22 +04:00
maxlength = int(info["length"])
blank = " " * (maxlength - len(value))
self.__write("| %s%s" % (value, blank), n=False, console=console)
2008-10-15 19:38:22 +04:00
2010-09-24 17:34:46 +04:00
if not conf.replicate:
if field == fields:
2011-07-03 02:48:56 +04:00
dataToDumpFile(dumpFP, "%s" % safeCSValue(value))
else:
2011-11-30 21:39:41 +04:00
dataToDumpFile(dumpFP, "%s%s" % (safeCSValue(value), conf.csvDel))
2008-10-15 19:38:22 +04:00
field += 1
2010-09-24 17:19:35 +04:00
if conf.replicate:
2011-12-27 15:25:40 +04:00
try:
rtable.insert(values)
except sqlmapValueException:
pass
2010-09-24 17:19:35 +04:00
self.__write("|", console=console)
if not conf.replicate:
2008-10-15 19:38:22 +04:00
dataToDumpFile(dumpFP, "\n")
self.__write("%s\n" % separator)
2010-09-24 17:34:46 +04:00
if conf.replicate:
rtable.endTransaction()
2010-09-24 17:34:46 +04:00
logger.info("Table '%s.%s' dumped to sqlite3 file '%s'" % (db, table, replication.dbpath))
else:
2008-10-15 19:38:22 +04:00
dataToDumpFile(dumpFP, "\n")
dumpFP.close()
logger.info("Table '%s.%s' dumped to CSV file '%s'" % (db, table, dumpFileName))
def dbColumns(self, dbColumnsDict, colConsider, dbs):
for column in dbColumnsDict.keys():
if colConsider == "1":
colConsiderStr = "s like '" + column + "' were"
else:
colConsiderStr = " '%s' was" % column
2011-04-30 17:20:05 +04:00
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, dataType in colData.items():
if column.lower() in col.lower():
if db in printDbs:
if tbl in printDbs[db]:
printDbs[db][tbl][col] = dataType
else:
printDbs[db][tbl] = { col: dataType }
else:
printDbs[db] = {}
printDbs[db][tbl] = { col: dataType }
continue
self.dbTableColumns(printDbs)
def query(self, query, queryRes):
self.string(query, queryRes)
def rFile(self,filePath,fileData):
self.string("%s file saved to" % filePath,fileData,sort=False)
def registerValue(self,registerData):
self.string("Registry key value data", registerData,sort=False)
2008-10-15 19:38:22 +04:00
# object to manage how to print the retrieved queries output to
# standard output and sessions file
dumper = Dump()