@@ -60,6 +60,7 @@ def _addPageTextWords():
6060
6161 return wordsList
6262
63+ @stackedmethod
6364def tableExists (tableFile , regex = None ):
6465 if kb .tableExistsChoice is None and not any (_ for _ in kb .injection .data if _ not in (PAYLOAD .TECHNIQUE .TIME , PAYLOAD .TECHNIQUE .STACKED )) and not conf .direct :
6566 warnMsg = "it's not recommended to use '%s' and/or '%s' " % (PAYLOAD .SQLINJECTION [PAYLOAD .TECHNIQUE .TIME ], PAYLOAD .SQLINJECTION [PAYLOAD .TECHNIQUE .STACKED ])
@@ -74,15 +75,17 @@ def tableExists(tableFile, regex=None):
7475
7576 result = inject .checkBooleanExpression ("%s" % safeStringFormat (BRUTE_TABLE_EXISTS_TEMPLATE , (randomInt (1 ), randomStr ())))
7677
77- if conf .db and Backend .getIdentifiedDbms () in (DBMS .ORACLE , DBMS .DB2 ):
78- conf .db = conf .db .upper ()
79-
8078 if result :
8179 errMsg = "can't use table existence check because of detected invalid results "
8280 errMsg += "(most likely caused by inability of the used injection "
8381 errMsg += "to distinguish erroneous results)"
8482 raise SqlmapDataException (errMsg )
8583
84+ pushValue (conf .db )
85+
86+ if conf .db and Backend .getIdentifiedDbms () in (DBMS .ORACLE , DBMS .DB2 ):
87+ conf .db = conf .db .upper ()
88+
8689 message = "which common tables (wordlist) file do you want to use?\n "
8790 message += "[1] default '%s' (press Enter)\n " % tableFile
8891 message += "[2] custom"
@@ -92,80 +95,88 @@ def tableExists(tableFile, regex=None):
9295 message = "what's the custom common tables file location?\n "
9396 tableFile = readInput (message ) or tableFile
9497
95- infoMsg = "checking table existence using items from '%s'" % tableFile
98+ infoMsg = "performing table existence using items from '%s'" % tableFile
9699 logger .info (infoMsg )
97100
98101 tables = getFileItems (tableFile , lowercase = Backend .getIdentifiedDbms () in (DBMS .ACCESS ,), unique = True )
99102 tables .extend (_addPageTextWords ())
100103 tables = filterListValue (tables , regex )
101104
102- threadData = getCurrentThreadData ()
103- threadData .shared .count = 0
104- threadData .shared .limit = len (tables )
105- threadData .shared .files = []
106- threadData .shared .unique = set ()
105+ for conf .db in (conf .db .split (',' ) if conf .db else [conf .db ]):
106+ if conf .db :
107+ infoMsg = "checking database '%s'" % conf .db
108+ logger .info (infoMsg )
107109
108- def tableExistsThread ():
109110 threadData = getCurrentThreadData ()
110-
111- while kb .threadContinue :
112- kb .locks .count .acquire ()
113- if threadData .shared .count < threadData .shared .limit :
114- table = safeSQLIdentificatorNaming (tables [threadData .shared .count ], True )
115- threadData .shared .count += 1
116- kb .locks .count .release ()
117- else :
118- kb .locks .count .release ()
119- break
120-
121- if conf .db and METADB_SUFFIX not in conf .db and Backend .getIdentifiedDbms () not in (DBMS .SQLITE , DBMS .ACCESS , DBMS .FIREBIRD ):
122- fullTableName = "%s.%s" % (conf .db , table )
123- else :
124- fullTableName = table
125-
126- result = inject .checkBooleanExpression ("%s" % safeStringFormat (BRUTE_TABLE_EXISTS_TEMPLATE , (randomInt (1 ), fullTableName )))
127-
128- kb .locks .io .acquire ()
129-
130- if result and table .lower () not in threadData .shared .unique :
131- threadData .shared .files .append (table )
132- threadData .shared .unique .add (table .lower ())
133-
134- if conf .verbose in (1 , 2 ) and not conf .api :
135- clearConsoleLine (True )
136- infoMsg = "[%s] [INFO] retrieved: %s\n " % (time .strftime ("%X" ), unsafeSQLIdentificatorNaming (table ))
137- dataToStdout (infoMsg , True )
138-
139- if conf .verbose in (1 , 2 ):
140- status = '%d/%d items (%d%%)' % (threadData .shared .count , threadData .shared .limit , round (100.0 * threadData .shared .count / threadData .shared .limit ))
141- dataToStdout ("\r [%s] [INFO] tried %s" % (time .strftime ("%X" ), status ), True )
142-
143- kb .locks .io .release ()
144-
145- try :
146- runThreads (conf .threads , tableExistsThread , threadChoice = True )
147- except KeyboardInterrupt :
148- warnMsg = "user aborted during table existence "
149- warnMsg += "check. sqlmap will display partial output"
150- logger .warn (warnMsg )
151-
152- clearConsoleLine (True )
153- dataToStdout ("\n " )
154-
155- if not threadData .shared .files :
156- warnMsg = "no table(s) found"
157- logger .warn (warnMsg )
158- else :
159- for item in threadData .shared .files :
160- if conf .db not in kb .data .cachedTables :
161- kb .data .cachedTables [conf .db ] = [item ]
162- else :
163- kb .data .cachedTables [conf .db ].append (item )
164-
165- for _ in ((conf .db , item ) for item in threadData .shared .files ):
166- if _ not in kb .brute .tables :
167- kb .brute .tables .append (_ )
168-
111+ threadData .shared .count = 0
112+ threadData .shared .limit = len (tables )
113+ threadData .shared .files = []
114+ threadData .shared .unique = set ()
115+
116+ def tableExistsThread ():
117+ threadData = getCurrentThreadData ()
118+
119+ while kb .threadContinue :
120+ kb .locks .count .acquire ()
121+ if threadData .shared .count < threadData .shared .limit :
122+ table = safeSQLIdentificatorNaming (tables [threadData .shared .count ], True )
123+ threadData .shared .count += 1
124+ kb .locks .count .release ()
125+ else :
126+ kb .locks .count .release ()
127+ break
128+
129+ if conf .db and METADB_SUFFIX not in conf .db and Backend .getIdentifiedDbms () not in (DBMS .SQLITE , DBMS .ACCESS , DBMS .FIREBIRD ):
130+ fullTableName = "%s.%s" % (conf .db , table )
131+ else :
132+ fullTableName = table
133+
134+ result = inject .checkBooleanExpression ("%s" % safeStringFormat (BRUTE_TABLE_EXISTS_TEMPLATE , (randomInt (1 ), fullTableName )))
135+
136+ kb .locks .io .acquire ()
137+
138+ if result and table .lower () not in threadData .shared .unique :
139+ threadData .shared .files .append (table )
140+ threadData .shared .unique .add (table .lower ())
141+
142+ if conf .verbose in (1 , 2 ) and not conf .api :
143+ clearConsoleLine (True )
144+ infoMsg = "[%s] [INFO] retrieved: %s\n " % (time .strftime ("%X" ), unsafeSQLIdentificatorNaming (table ))
145+ dataToStdout (infoMsg , True )
146+
147+ if conf .verbose in (1 , 2 ):
148+ status = '%d/%d items (%d%%)' % (threadData .shared .count , threadData .shared .limit , round (100.0 * threadData .shared .count / threadData .shared .limit ))
149+ dataToStdout ("\r [%s] [INFO] tried %s" % (time .strftime ("%X" ), status ), True )
150+
151+ kb .locks .io .release ()
152+
153+ try :
154+ runThreads (conf .threads , tableExistsThread , threadChoice = True )
155+ except KeyboardInterrupt :
156+ warnMsg = "user aborted during table existence "
157+ warnMsg += "check. sqlmap will display partial output"
158+ logger .warn (warnMsg )
159+
160+ clearConsoleLine (True )
161+ dataToStdout ("\n " )
162+
163+ if not threadData .shared .files :
164+ warnMsg = "no table(s) found"
165+ if conf .db :
166+ warnMsg += "for database '%s'" % conf .db
167+ logger .warn (warnMsg )
168+ else :
169+ for item in threadData .shared .files :
170+ if conf .db not in kb .data .cachedTables :
171+ kb .data .cachedTables [conf .db ] = [item ]
172+ else :
173+ kb .data .cachedTables [conf .db ].append (item )
174+
175+ for _ in ((conf .db , item ) for item in threadData .shared .files ):
176+ if _ not in kb .brute .tables :
177+ kb .brute .tables .append (_ )
178+
179+ conf .db = popValue ()
169180 hashDBWrite (HASHDB_KEYS .KB_BRUTE_TABLES , kb .brute .tables , True )
170181
171182 return kb .data .cachedTables
0 commit comments