2019-05-08 13:47:52 +03:00
|
|
|
#!/usr/bin/env python
|
2010-12-24 15:13:48 +03:00
|
|
|
|
|
|
|
"""
|
2021-09-08 22:01:41 +03:00
|
|
|
Copyright (c) 2006-2021 sqlmap developers (https://sqlmap.org/)
|
2017-10-11 15:50:46 +03:00
|
|
|
See the file 'LICENSE' for copying permission
|
2010-12-24 15:13:48 +03:00
|
|
|
"""
|
|
|
|
|
2019-01-22 03:28:24 +03:00
|
|
|
from __future__ import print_function
|
|
|
|
|
2011-01-16 13:52:42 +03:00
|
|
|
import difflib
|
2010-12-24 15:13:48 +03:00
|
|
|
import threading
|
2011-06-17 16:50:28 +04:00
|
|
|
import time
|
2011-10-20 01:35:01 +04:00
|
|
|
import traceback
|
2010-12-24 15:13:48 +03:00
|
|
|
|
2019-03-27 15:33:46 +03:00
|
|
|
from lib.core.compat import WichmannHill
|
2019-03-28 18:04:38 +03:00
|
|
|
from lib.core.compat import xrange
|
2011-11-22 14:41:56 +04:00
|
|
|
from lib.core.data import conf
|
2010-12-24 15:13:48 +03:00
|
|
|
from lib.core.data import kb
|
2011-05-30 03:17:50 +04:00
|
|
|
from lib.core.data import logger
|
2011-07-08 10:02:31 +04:00
|
|
|
from lib.core.datatype import AttribDict
|
2011-06-28 01:48:26 +04:00
|
|
|
from lib.core.enums import PAYLOAD
|
2019-02-12 12:42:32 +03:00
|
|
|
from lib.core.exception import SqlmapBaseException
|
2012-12-06 17:14:19 +04:00
|
|
|
from lib.core.exception import SqlmapConnectionException
|
2020-07-28 12:10:15 +03:00
|
|
|
from lib.core.exception import SqlmapSkipTargetException
|
2012-12-06 17:14:19 +04:00
|
|
|
from lib.core.exception import SqlmapThreadException
|
2016-10-18 14:37:36 +03:00
|
|
|
from lib.core.exception import SqlmapUserQuitException
|
2012-12-06 17:14:19 +04:00
|
|
|
from lib.core.exception import SqlmapValueException
|
2011-06-07 13:50:00 +04:00
|
|
|
from lib.core.settings import MAX_NUMBER_OF_THREADS
|
2011-06-17 16:50:28 +04:00
|
|
|
from lib.core.settings import PYVERSION
|
2011-05-30 03:17:50 +04:00
|
|
|
|
2011-07-08 10:02:31 +04:00
|
|
|
shared = AttribDict()
|
2010-12-24 15:13:48 +03:00
|
|
|
|
2011-07-04 23:58:41 +04:00
|
|
|
class _ThreadData(threading.local):
|
2010-12-24 15:13:48 +03:00
|
|
|
"""
|
|
|
|
Represents thread independent data
|
|
|
|
"""
|
|
|
|
|
|
|
|
def __init__(self):
|
2011-11-23 18:26:40 +04:00
|
|
|
self.reset()
|
|
|
|
|
|
|
|
def reset(self):
|
|
|
|
"""
|
|
|
|
Resets thread data model
|
|
|
|
"""
|
2011-05-30 03:17:50 +04:00
|
|
|
|
2011-04-30 17:20:05 +04:00
|
|
|
self.disableStdOut = False
|
2011-09-26 17:36:08 +04:00
|
|
|
self.hashDBCursor = None
|
2011-11-23 18:26:40 +04:00
|
|
|
self.inTransaction = False
|
2016-09-27 11:20:36 +03:00
|
|
|
self.lastCode = None
|
2013-01-18 19:49:35 +04:00
|
|
|
self.lastComparisonPage = None
|
2013-01-18 20:00:11 +04:00
|
|
|
self.lastComparisonHeaders = None
|
2016-06-03 15:29:32 +03:00
|
|
|
self.lastComparisonCode = None
|
2017-02-06 15:28:33 +03:00
|
|
|
self.lastComparisonRatio = None
|
2019-06-01 14:42:57 +03:00
|
|
|
self.lastErrorPage = tuple()
|
2011-04-30 17:20:05 +04:00
|
|
|
self.lastHTTPError = None
|
|
|
|
self.lastRedirectMsg = None
|
|
|
|
self.lastQueryDuration = 0
|
2015-08-15 00:29:31 +03:00
|
|
|
self.lastPage = None
|
2011-09-28 12:13:46 +04:00
|
|
|
self.lastRequestMsg = None
|
2011-04-30 17:20:05 +04:00
|
|
|
self.lastRequestUID = 0
|
2019-06-01 14:42:57 +03:00
|
|
|
self.lastRedirectURL = tuple()
|
2019-03-27 15:33:46 +03:00
|
|
|
self.random = WichmannHill()
|
2011-10-12 02:40:00 +04:00
|
|
|
self.resumed = False
|
2011-05-11 16:54:33 +04:00
|
|
|
self.retriesCount = 0
|
2011-04-30 17:20:05 +04:00
|
|
|
self.seqMatcher = difflib.SequenceMatcher(None)
|
2011-05-30 03:17:50 +04:00
|
|
|
self.shared = shared
|
2019-07-01 11:43:05 +03:00
|
|
|
self.technique = None
|
2016-09-27 15:03:59 +03:00
|
|
|
self.validationRun = 0
|
2011-04-30 17:20:05 +04:00
|
|
|
self.valueStack = []
|
2010-12-24 15:13:48 +03:00
|
|
|
|
2011-07-04 23:58:41 +04:00
|
|
|
ThreadData = _ThreadData()
|
|
|
|
|
2017-04-18 16:48:05 +03:00
|
|
|
def readInput(message, default=None, checkBatch=True, boolean=False):
|
2012-02-16 13:54:29 +04:00
|
|
|
# It will be overwritten by original from lib.core.common
|
2011-06-07 13:50:00 +04:00
|
|
|
pass
|
|
|
|
|
2019-10-07 15:20:18 +03:00
|
|
|
def isDigit(value):
|
|
|
|
# It will be overwritten by original from lib.core.common
|
|
|
|
pass
|
|
|
|
|
2010-12-24 15:13:48 +03:00
|
|
|
def getCurrentThreadData():
|
|
|
|
"""
|
2011-07-04 23:58:41 +04:00
|
|
|
Returns current thread's local data
|
2010-12-24 15:13:48 +03:00
|
|
|
"""
|
|
|
|
|
2011-07-04 23:58:41 +04:00
|
|
|
return ThreadData
|
2011-05-30 03:17:50 +04:00
|
|
|
|
2011-11-22 14:09:35 +04:00
|
|
|
def getCurrentThreadName():
|
|
|
|
"""
|
|
|
|
Returns current's thread name
|
|
|
|
"""
|
|
|
|
|
|
|
|
return threading.current_thread().getName()
|
|
|
|
|
2017-04-19 14:35:36 +03:00
|
|
|
def exceptionHandledFunction(threadFunction, silent=False):
|
2011-06-07 13:50:00 +04:00
|
|
|
try:
|
|
|
|
threadFunction()
|
|
|
|
except KeyboardInterrupt:
|
|
|
|
kb.threadContinue = False
|
|
|
|
kb.threadException = True
|
|
|
|
raise
|
2019-01-22 02:40:48 +03:00
|
|
|
except Exception as ex:
|
2019-07-12 14:41:07 +03:00
|
|
|
from lib.core.common import getSafeExString
|
|
|
|
|
2020-07-28 12:10:15 +03:00
|
|
|
if not silent and kb.get("threadContinue") and not kb.get("multipleCtrlC") and not isinstance(ex, (SqlmapUserQuitException, SqlmapSkipTargetException)):
|
2019-07-12 14:41:07 +03:00
|
|
|
errMsg = getSafeExString(ex) if isinstance(ex, SqlmapBaseException) else "%s: %s" % (type(ex).__name__, getSafeExString(ex))
|
2019-02-12 12:42:32 +03:00
|
|
|
logger.error("thread %s: '%s'" % (threading.currentThread().getName(), errMsg))
|
2011-06-07 13:50:00 +04:00
|
|
|
|
2019-08-13 12:58:33 +03:00
|
|
|
if conf.get("verbose") > 1 and not isinstance(ex, SqlmapConnectionException):
|
2017-12-29 15:52:15 +03:00
|
|
|
traceback.print_exc()
|
|
|
|
|
2012-04-03 13:18:30 +04:00
|
|
|
def setDaemon(thread):
|
|
|
|
# Reference: http://stackoverflow.com/questions/190010/daemon-threads-explanation
|
|
|
|
if PYVERSION >= "2.6":
|
|
|
|
thread.daemon = True
|
|
|
|
else:
|
|
|
|
thread.setDaemon(True)
|
|
|
|
|
2011-07-03 02:48:56 +04:00
|
|
|
def runThreads(numThreads, threadFunction, cleanupFunction=None, forwardException=True, threadChoice=False, startThreadMsg=True):
|
2011-05-30 03:17:50 +04:00
|
|
|
threads = []
|
|
|
|
|
2019-08-06 03:54:18 +03:00
|
|
|
kb.multipleCtrlC = False
|
2011-05-30 03:17:50 +04:00
|
|
|
kb.threadContinue = True
|
|
|
|
kb.threadException = False
|
2019-07-01 11:43:05 +03:00
|
|
|
kb.technique = ThreadData.technique
|
2021-09-29 17:13:13 +03:00
|
|
|
kb.multiThreadMode = False
|
2011-06-07 13:50:00 +04:00
|
|
|
|
2011-08-18 02:31:33 +04:00
|
|
|
try:
|
2021-09-29 17:13:13 +03:00
|
|
|
if threadChoice and conf.threads == numThreads == 1 and not (kb.injection.data and not any(_ not in (PAYLOAD.TECHNIQUE.TIME, PAYLOAD.TECHNIQUE.STACKED) for _ in kb.injection.data)):
|
|
|
|
while True:
|
|
|
|
message = "please enter number of threads? [Enter for %d (current)] " % numThreads
|
|
|
|
choice = readInput(message, default=str(numThreads))
|
|
|
|
if choice:
|
|
|
|
skipThreadCheck = False
|
|
|
|
|
|
|
|
if choice.endswith('!'):
|
|
|
|
choice = choice[:-1]
|
|
|
|
skipThreadCheck = True
|
|
|
|
|
|
|
|
if isDigit(choice):
|
|
|
|
if int(choice) > MAX_NUMBER_OF_THREADS and not skipThreadCheck:
|
|
|
|
errMsg = "maximum number of used threads is %d avoiding potential connection issues" % MAX_NUMBER_OF_THREADS
|
|
|
|
logger.critical(errMsg)
|
|
|
|
else:
|
|
|
|
conf.threads = numThreads = int(choice)
|
|
|
|
break
|
|
|
|
|
|
|
|
if numThreads == 1:
|
|
|
|
warnMsg = "running in a single-thread mode. This could take a while"
|
|
|
|
logger.warn(warnMsg)
|
|
|
|
|
2011-08-18 02:31:33 +04:00
|
|
|
if numThreads > 1:
|
|
|
|
if startThreadMsg:
|
|
|
|
infoMsg = "starting %d threads" % numThreads
|
|
|
|
logger.info(infoMsg)
|
|
|
|
else:
|
|
|
|
threadFunction()
|
|
|
|
return
|
2011-05-30 03:17:50 +04:00
|
|
|
|
2021-09-29 17:13:13 +03:00
|
|
|
kb.multiThreadMode = True
|
|
|
|
|
2011-08-18 02:31:33 +04:00
|
|
|
# Start the threads
|
2011-10-22 02:34:27 +04:00
|
|
|
for numThread in xrange(numThreads):
|
2011-08-18 02:31:33 +04:00
|
|
|
thread = threading.Thread(target=exceptionHandledFunction, name=str(numThread), args=[threadFunction])
|
2011-06-17 16:50:28 +04:00
|
|
|
|
2012-04-03 13:18:30 +04:00
|
|
|
setDaemon(thread)
|
2011-06-17 16:50:28 +04:00
|
|
|
|
2011-08-18 02:31:33 +04:00
|
|
|
try:
|
|
|
|
thread.start()
|
2019-01-22 02:40:48 +03:00
|
|
|
except Exception as ex:
|
2019-05-03 00:51:54 +03:00
|
|
|
errMsg = "error occurred while starting new thread ('%s')" % ex
|
2011-08-18 02:31:33 +04:00
|
|
|
logger.critical(errMsg)
|
|
|
|
break
|
2011-07-03 02:48:56 +04:00
|
|
|
|
2011-08-18 02:31:33 +04:00
|
|
|
threads.append(thread)
|
2011-05-30 03:17:50 +04:00
|
|
|
|
2011-08-18 02:31:33 +04:00
|
|
|
# And wait for them to all finish
|
2011-05-30 03:17:50 +04:00
|
|
|
alive = True
|
|
|
|
while alive:
|
|
|
|
alive = False
|
|
|
|
for thread in threads:
|
2020-02-28 16:44:27 +03:00
|
|
|
if thread.is_alive():
|
2011-05-30 03:17:50 +04:00
|
|
|
alive = True
|
2011-10-10 18:47:48 +04:00
|
|
|
time.sleep(0.1)
|
2011-05-30 03:17:50 +04:00
|
|
|
|
2019-01-22 02:40:48 +03:00
|
|
|
except (KeyboardInterrupt, SqlmapUserQuitException) as ex:
|
2019-01-22 03:28:24 +03:00
|
|
|
print()
|
2018-07-18 17:30:59 +03:00
|
|
|
kb.prependFlag = False
|
2011-05-30 03:17:50 +04:00
|
|
|
kb.threadContinue = False
|
|
|
|
kb.threadException = True
|
|
|
|
|
2019-11-09 03:01:19 +03:00
|
|
|
if kb.lastCtrlCTime and (time.time() - kb.lastCtrlCTime < 1):
|
|
|
|
kb.multipleCtrlC = True
|
|
|
|
raise SqlmapUserQuitException("user aborted (Ctrl+C was pressed multiple times)")
|
|
|
|
|
|
|
|
kb.lastCtrlCTime = time.time()
|
|
|
|
|
2011-12-26 18:31:59 +04:00
|
|
|
if numThreads > 1:
|
2016-10-18 14:37:36 +03:00
|
|
|
logger.info("waiting for threads to finish%s" % (" (Ctrl+C was pressed)" if isinstance(ex, KeyboardInterrupt) else ""))
|
2011-05-30 03:17:50 +04:00
|
|
|
try:
|
2021-10-07 01:45:55 +03:00
|
|
|
while (threading.active_count() > 1):
|
2011-05-30 03:17:50 +04:00
|
|
|
pass
|
|
|
|
|
|
|
|
except KeyboardInterrupt:
|
2019-08-06 03:54:18 +03:00
|
|
|
kb.multipleCtrlC = True
|
2013-01-04 02:20:55 +04:00
|
|
|
raise SqlmapThreadException("user aborted (Ctrl+C was pressed multiple times)")
|
2011-05-30 03:17:50 +04:00
|
|
|
|
2011-05-30 03:56:41 +04:00
|
|
|
if forwardException:
|
|
|
|
raise
|
|
|
|
|
2019-01-22 02:40:48 +03:00
|
|
|
except (SqlmapConnectionException, SqlmapValueException) as ex:
|
2019-01-22 03:28:24 +03:00
|
|
|
print()
|
2012-01-13 19:56:50 +04:00
|
|
|
kb.threadException = True
|
2019-05-03 00:51:54 +03:00
|
|
|
logger.error("thread %s: '%s'" % (threading.currentThread().getName(), ex))
|
2012-01-13 19:56:50 +04:00
|
|
|
|
2019-07-11 12:13:49 +03:00
|
|
|
if conf.get("verbose") > 1 and isinstance(ex, SqlmapValueException):
|
2017-12-30 18:25:19 +03:00
|
|
|
traceback.print_exc()
|
|
|
|
|
2012-01-13 19:56:50 +04:00
|
|
|
except:
|
2019-01-22 03:28:24 +03:00
|
|
|
print()
|
2019-08-06 03:54:18 +03:00
|
|
|
|
|
|
|
if not kb.multipleCtrlC:
|
|
|
|
from lib.core.common import unhandledExceptionMessage
|
|
|
|
|
|
|
|
kb.threadException = True
|
|
|
|
errMsg = unhandledExceptionMessage()
|
|
|
|
logger.error("thread %s: %s" % (threading.currentThread().getName(), errMsg))
|
|
|
|
traceback.print_exc()
|
2011-07-03 02:48:56 +04:00
|
|
|
|
2011-05-30 03:17:50 +04:00
|
|
|
finally:
|
2021-09-29 17:13:13 +03:00
|
|
|
kb.multiThreadMode = False
|
2011-05-30 03:17:50 +04:00
|
|
|
kb.threadContinue = True
|
|
|
|
kb.threadException = False
|
2019-07-01 11:43:05 +03:00
|
|
|
kb.technique = None
|
2011-05-30 03:17:50 +04:00
|
|
|
|
2011-12-28 20:27:17 +04:00
|
|
|
for lock in kb.locks.values():
|
2016-06-10 19:02:24 +03:00
|
|
|
if lock.locked():
|
2016-03-22 15:24:54 +03:00
|
|
|
try:
|
|
|
|
lock.release()
|
2017-04-14 14:14:53 +03:00
|
|
|
except:
|
2016-03-22 15:24:54 +03:00
|
|
|
pass
|
2011-12-28 20:27:17 +04:00
|
|
|
|
2012-08-21 12:28:25 +04:00
|
|
|
if conf.get("hashDB"):
|
2011-11-23 07:03:31 +04:00
|
|
|
conf.hashDB.flush(True)
|
2011-11-22 14:41:56 +04:00
|
|
|
|
2011-05-30 03:17:50 +04:00
|
|
|
if cleanupFunction:
|
|
|
|
cleanupFunction()
|