Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit a5968ff

Browse files
committed
Added --count switch to count the number of entries for a specific table (when -T is provided), all database's tables (when only -D is provided) or all databases' tables when neither -D nor -T are provided
1 parent 529595f commit a5968ff

7 files changed

Lines changed: 111 additions & 3 deletions

File tree

lib/controller/action.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,9 @@ def action():
9999
if conf.getColumns:
100100
conf.dumper.dbTableColumns(conf.dbmsHandler.getColumns())
101101

102+
if conf.getCount:
103+
conf.dumper.dbTablesCount(conf.dbmsHandler.getCount())
104+
102105
if conf.commonColumns:
103106
conf.dumper.dbTableColumns(columnExists(paths.COMMON_COLUMNS))
104107

lib/core/common.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2527,17 +2527,20 @@ def safeSQLIdentificatorNaming(name, isTable=False):
25272527
"""
25282528

25292529
retVal = name
2530+
25302531
if isinstance(name, basestring):
25312532
if isTable and Backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.SYBASE) and '.' not in name:
25322533
name = "%s.%s" % (DEFAULT_MSSQL_SCHEMA, name)
25332534

25342535
parts = name.split('.')
2536+
25352537
for i in range(len(parts)):
25362538
if not re.match(r"\A[A-Za-z0-9_]+\Z", parts[i]):
25372539
if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.ACCESS):
25382540
parts[i] = "`%s`" % parts[i].strip("`")
25392541
elif Backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.ORACLE, DBMS.PGSQL):
25402542
parts[i] = "\"%s\"" % parts[i].strip("\"")
2543+
25412544
retVal = ".".join(parts)
25422545

25432546
return retVal

lib/core/dump.py

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -211,11 +211,11 @@ def dbTableColumns(self, tableColumns):
211211
maxlength2 = max(maxlength2, len(colType))
212212

213213
maxlength1 = max(maxlength1, len("COLUMN"))
214-
lines1 = "-" * (int(maxlength1) + 2)
214+
lines1 = "-" * (maxlength1 + 2)
215215

216216
if colType is not None:
217217
maxlength2 = max(maxlength2, len("TYPE"))
218-
lines2 = "-" * (int(maxlength2) + 2)
218+
lines2 = "-" * (maxlength2 + 2)
219219

220220
self.__write("Database: %s\nTable: %s" % (db, table))
221221

@@ -256,6 +256,48 @@ def dbTableColumns(self, tableColumns):
256256
else:
257257
self.__write("+%s+\n" % lines1)
258258

259+
def dbTablesCount(self, dbTables):
260+
if isinstance(dbTables, dict) and len(dbTables) > 0:
261+
maxlength1 = len("Table")
262+
maxlength2 = len("Entries")
263+
264+
for ctables in dbTables.values():
265+
for tables in ctables.values():
266+
for table in tables:
267+
maxlength1 = max(maxlength1, len(normalizeUnicode(table) or str(table)))
268+
269+
for db, counts in dbTables.items():
270+
self.__write("Database: %s" % db)
271+
272+
lines1 = "-" * (maxlength1 + 2)
273+
blank1 = " " * (maxlength1 - len("Table"))
274+
lines2 = "-" * (maxlength2 + 2)
275+
blank2 = " " * (maxlength2 - len("Entries"))
276+
277+
self.__write("+%s+%s+" % (lines1, lines2))
278+
self.__write("| Table%s | Entries%s |" % (blank1, blank2))
279+
self.__write("+%s+%s+" % (lines1, lines2))
280+
281+
sortedCounts = counts.keys()
282+
sortedCounts.sort(reverse=True)
283+
284+
for count in sortedCounts:
285+
tables = counts[count]
286+
287+
if count is None:
288+
count = "Unknown"
289+
290+
tables.sort(key=lambda x: x.lower() if isinstance(x, basestring) else x)
291+
292+
for table in tables:
293+
blank1 = " " * (maxlength1 - len(normalizeUnicode(table) or str(table)))
294+
blank2 = " " * (maxlength2 - len(str(count)))
295+
self.__write("| %s%s | %d%s |" % (table, blank1, count, blank2))
296+
297+
self.__write("+%s+%s+\n" % (lines1, lines2))
298+
else:
299+
logger.error("unable to retrieve the number of entries for any table")
300+
259301
def dbTableValues(self, tableValues):
260302
replication = None
261303
rtable = None

lib/core/optiondict.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@
9393
"getTables": ("boolean", "Tables"),
9494
"getColumns": ("boolean", "Columns"),
9595
"getSchema": "boolean",
96+
"getCount": "boolean",
9697
"dumpTable": "boolean",
9798
"dumpAll": "boolean",
9899
"search": "boolean",

lib/parse/cmdline.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,9 @@ def cmdLineParser():
279279
enumeration.add_option("--schema", dest="getSchema", action="store_true",
280280
default=False, help="Enumerate DBMS schema")
281281

282+
enumeration.add_option("--count", dest="getCount", action="store_true",
283+
default=False, help="Retrieve number of entries for table(s)")
284+
282285
enumeration.add_option("--dump", dest="dumpTable", action="store_true",
283286
default=False, help="Dump DBMS database table entries")
284287

plugins/generic/enumeration.py

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ def __init__(self):
8585
kb.data.cachedDbs = []
8686
kb.data.cachedTables = {}
8787
kb.data.cachedColumns = {}
88+
kb.data.cachedCounts = {}
8889
kb.data.dumpedTable = {}
8990
kb.data.processChar = None
9091
self.alwaysRetrieveSqlOutput = False
@@ -839,6 +840,7 @@ def getTables(self, bruteForce=None):
839840
for db, table in value:
840841
db = safeSQLIdentificatorNaming(db)
841842
table = safeSQLIdentificatorNaming(table, True)
843+
842844
if not kb.data.cachedTables.has_key(db):
843845
kb.data.cachedTables[db] = [table]
844846
else:
@@ -885,6 +887,7 @@ def getTables(self, bruteForce=None):
885887
query = rootQuery.blind.query % index
886888
else:
887889
query = rootQuery.blind.query % (unsafeSQLIdentificatorNaming(db), index)
890+
888891
table = inject.getValue(query, inband=False, error=False)
889892
kb.hintValue = table
890893
table = safeSQLIdentificatorNaming(table, True)
@@ -1174,6 +1177,56 @@ def getSchema(self):
11741177

11751178
return kb.data.cachedColumns
11761179

1180+
def __tableGetCount(self, db, table):
1181+
query = "SELECT COUNT(*) FROM %s.%s" % (safeSQLIdentificatorNaming(db), safeSQLIdentificatorNaming(table, True))
1182+
count = inject.getValue(query, expected=EXPECTED.INT, charsetType=2)
1183+
1184+
if count is not None and isinstance(count, basestring) and count.isdigit():
1185+
if unsafeSQLIdentificatorNaming(db) not in kb.data.cachedCounts:
1186+
kb.data.cachedCounts[unsafeSQLIdentificatorNaming(db)] = {}
1187+
1188+
if int(count) in kb.data.cachedCounts[unsafeSQLIdentificatorNaming(db)]:
1189+
kb.data.cachedCounts[unsafeSQLIdentificatorNaming(db)][int(count)].append(unsafeSQLIdentificatorNaming(table))
1190+
else:
1191+
kb.data.cachedCounts[unsafeSQLIdentificatorNaming(db)][int(count)] = [unsafeSQLIdentificatorNaming(table)]
1192+
1193+
def getCount(self):
1194+
if not conf.tbl:
1195+
warnMsg = "missing table parameter, sqlmap will retrieve "
1196+
warnMsg += "the number of entries for all database "
1197+
warnMsg += "management system databases' tables"
1198+
logger.warn(warnMsg)
1199+
1200+
elif "." in conf.tbl:
1201+
if not conf.db:
1202+
conf.db, conf.tbl = conf.tbl.split(".")
1203+
1204+
if conf.tbl is not None and conf.db is None:
1205+
warnMsg = "missing database parameter, sqlmap is going to "
1206+
warnMsg += "use the current database to retrieve the "
1207+
warnMsg += "number of entries for table '%s'" % conf.tbl
1208+
logger.warn(warnMsg)
1209+
1210+
conf.db = self.getCurrentDb()
1211+
1212+
self.forceDbmsEnum()
1213+
1214+
if conf.db:
1215+
conf.db = safeSQLIdentificatorNaming(conf.db)
1216+
1217+
if conf.tbl:
1218+
for table in conf.tbl.split(","):
1219+
table = safeSQLIdentificatorNaming(table, True)
1220+
self.__tableGetCount(conf.db, table)
1221+
else:
1222+
self.getTables()
1223+
1224+
for db, tables in kb.data.cachedTables.items():
1225+
for table in tables:
1226+
self.__tableGetCount(db, table)
1227+
1228+
return kb.data.cachedCounts
1229+
11771230
def __pivotDumpTable(self, table, colList, count=None, blind=True):
11781231
lengths = {}
11791232
entries = {}
@@ -1580,7 +1633,6 @@ def dumpAll(self):
15801633
infoMsg = "skipping table '%s'" % table
15811634
logger.info(infoMsg)
15821635

1583-
15841636
def dumpFoundColumn(self, dbs, foundCols, colConsider):
15851637
if not dbs:
15861638
warnMsg = "no databases have tables containing any of the "

sqlmap.conf

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,10 @@ getColumns = False
314314
# Valid: True or False
315315
getSchema = False
316316

317+
# Retrieve number of entries for table(s).
318+
# Valid: True or False
319+
getCount = False
320+
317321
# Dump back-end database management system database table entries.
318322
# Requires: tbl and/or col
319323
# Optional: db

0 commit comments

Comments
 (0)