mirror of
https://github.com/sqlmapproject/sqlmap.git
synced 2026-08-04 06:50:14 +00:00
sqlmap 0.6.3-rc4:
Minor enhancement to be able to specify the number of seconds before timeout the connection, default is set to 10 seconds. Minor improvement to retry the HTTP request up to three times in case an exception is raised during the connection to the target url. Minor bug fix to correctly catch connection exceptions and notify to the user also if they occur within a thread. Minor code restyling. Updated documentation.
This commit is contained in:
parent
0f07e33e1a
commit
7f055924a7
16 changed files with 748 additions and 571 deletions
|
|
@ -74,6 +74,10 @@ class sqlmapNotVulnerableException(Exception):
|
|||
pass
|
||||
|
||||
|
||||
class sqlmapThreadException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class sqlmapUnsupportedDBMSException(Exception):
|
||||
pass
|
||||
|
||||
|
|
@ -108,6 +112,7 @@ exceptionsTuple = (
|
|||
sqlmapUndefinedMethod,
|
||||
sqlmapMissingPrivileges,
|
||||
sqlmapNotVulnerableException,
|
||||
sqlmapThreadException,
|
||||
sqlmapUnsupportedDBMSException,
|
||||
sqlmapUnsupportedFeatureException,
|
||||
sqlmapValueException,
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ import cookielib
|
|||
import logging
|
||||
import os
|
||||
import re
|
||||
import socket
|
||||
import time
|
||||
import urllib2
|
||||
import urlparse
|
||||
|
|
@ -264,7 +265,7 @@ def __setRemoteDBMS():
|
|||
|
||||
|
||||
def __setThreads():
|
||||
if conf.threads <= 0:
|
||||
if not isinstance(conf.threads, int) or conf.threads <= 0:
|
||||
conf.threads = 1
|
||||
|
||||
|
||||
|
|
@ -488,6 +489,29 @@ def __setHTTPCookies():
|
|||
conf.httpHeaders.append(("Cookie", conf.cookie))
|
||||
|
||||
|
||||
def __setHTTPTimeout():
|
||||
"""
|
||||
Set the HTTP timeout
|
||||
"""
|
||||
|
||||
if conf.timeout:
|
||||
debugMsg = "setting the HTTP timeout"
|
||||
logger.debug(debugMsg)
|
||||
|
||||
conf.timeout = float(conf.timeout)
|
||||
|
||||
if conf.timeout < 3.0:
|
||||
warnMsg = "the minimum HTTP timeout is 3 seconds, sqlmap "
|
||||
warnMsg += "will going to reset it"
|
||||
logger.warn(warnMsg)
|
||||
|
||||
conf.timeout = 3.0
|
||||
else:
|
||||
conf.timeout = 10.0
|
||||
|
||||
socket.setdefaulttimeout(conf.timeout)
|
||||
|
||||
|
||||
def __cleanupOptions():
|
||||
"""
|
||||
Cleanup configuration attributes.
|
||||
|
|
@ -543,9 +567,11 @@ def __setConfAttributes():
|
|||
conf.paramNegative = False
|
||||
conf.path = None
|
||||
conf.port = None
|
||||
conf.retries = 0
|
||||
conf.scheme = None
|
||||
conf.sessionFP = None
|
||||
conf.start = True
|
||||
conf.threadException = False
|
||||
|
||||
|
||||
def __setKnowledgeBaseAttributes():
|
||||
|
|
@ -682,6 +708,7 @@ def init(inputOptions=advancedDict()):
|
|||
__setConfAttributes()
|
||||
__setKnowledgeBaseAttributes()
|
||||
__cleanupOptions()
|
||||
__setHTTPTimeout()
|
||||
__setHTTPCookies()
|
||||
__setHTTPReferer()
|
||||
__setHTTPUserAgent()
|
||||
|
|
|
|||
|
|
@ -25,12 +25,14 @@ Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|||
|
||||
|
||||
optDict = {
|
||||
# Family: { "parameter_name": "parameter_datatype",
|
||||
"Request": {
|
||||
# Family: { "parameter_name": "parameter_datatype" },
|
||||
"Target": {
|
||||
"url": "string",
|
||||
"list": "string",
|
||||
"googleDork": "string",
|
||||
"testParameter": "string",
|
||||
},
|
||||
|
||||
"Request": {
|
||||
"method": "string",
|
||||
"data": "string",
|
||||
"cookie": "string",
|
||||
|
|
@ -42,18 +44,20 @@ optDict = {
|
|||
"proxy": "string",
|
||||
"threads": "integer",
|
||||
"delay": "float",
|
||||
"timeout": "int",
|
||||
},
|
||||
|
||||
"Injection": {
|
||||
"testParameter": "string",
|
||||
"string": "string",
|
||||
"dbms": "string",
|
||||
},
|
||||
|
||||
"Techniques": {
|
||||
"Techniques": {
|
||||
"timeTest": "boolean",
|
||||
"unionTest": "boolean",
|
||||
"unionUse": "boolean",
|
||||
},
|
||||
},
|
||||
|
||||
"Fingerprint": {
|
||||
"extensiveFp": "boolean",
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ import sys
|
|||
|
||||
|
||||
# sqlmap version and site
|
||||
VERSION = "0.6.3-rc3"
|
||||
VERSION = "0.6.3-rc4"
|
||||
VERSION_STRING = "sqlmap/%s" % VERSION
|
||||
SITE = "http://sqlmap.sourceforge.net"
|
||||
|
||||
|
|
@ -65,4 +65,6 @@ ORACLE_ALIASES = [ "oracle", "orcl", "ora", "or" ]
|
|||
|
||||
SUPPORTED_DBMS = MSSQL_ALIASES + MYSQL_ALIASES + PGSQL_ALIASES + ORACLE_ALIASES
|
||||
|
||||
TIME_DELAY = 5
|
||||
# TODO: port to command line/configuration file options?
|
||||
SECONDS = 5
|
||||
RETRIES = 3
|
||||
|
|
|
|||
|
|
@ -41,21 +41,25 @@ def cmdLineParser():
|
|||
parser = OptionParser(usage=usage, version=VERSION_STRING)
|
||||
|
||||
try:
|
||||
# Target options
|
||||
target = OptionGroup(parser, "Target", "At least one of these "
|
||||
"options has to be specified to set the source "
|
||||
"to get target urls from.")
|
||||
|
||||
target.add_option("-u", "--url", dest="url", help="Target url")
|
||||
|
||||
target.add_option("-l", dest="list", help="Parse targets from Burp "
|
||||
"or WebScarab logs")
|
||||
|
||||
target.add_option("-g", dest="googleDork",
|
||||
help="Process Google dork results as target urls")
|
||||
|
||||
target.add_option("-c", dest="configFile",
|
||||
help="Load options from a configuration INI file")
|
||||
|
||||
# Request options
|
||||
request = OptionGroup(parser, "Request", "These options have to "
|
||||
"be specified to set the target url, HTTP "
|
||||
"method, how to connect to the target url "
|
||||
"or Google dorking results in general.")
|
||||
|
||||
request.add_option("-u", "--url", dest="url", help="Target url")
|
||||
|
||||
request.add_option("-l", dest="list", help="List of targets")
|
||||
|
||||
request.add_option("-g", dest="googleDork",
|
||||
help="Process Google dork results as target urls")
|
||||
|
||||
request.add_option("-p", dest="testParameter",
|
||||
help="Testable parameter(s)")
|
||||
request = OptionGroup(parser, "Request", "These options can be used "
|
||||
"to specify how to connect to the target url.")
|
||||
|
||||
request.add_option("--method", dest="method", default="GET",
|
||||
help="HTTP method, GET or POST (default: GET)")
|
||||
|
|
@ -94,10 +98,17 @@ def cmdLineParser():
|
|||
request.add_option("--delay", dest="delay", type="float",
|
||||
help="Delay in seconds between each HTTP request")
|
||||
|
||||
request.add_option("--timeout", dest="timeout", type="float",
|
||||
help="Seconds to wait before timeout connection "
|
||||
"(default 10)")
|
||||
|
||||
|
||||
# Injection options
|
||||
injection = OptionGroup(parser, "Injection")
|
||||
|
||||
injection.add_option("-p", dest="testParameter",
|
||||
help="Testable parameter(s)")
|
||||
|
||||
injection.add_option("--string", dest="string",
|
||||
help="String to match in page when the "
|
||||
"query is valid")
|
||||
|
|
@ -253,15 +264,13 @@ def cmdLineParser():
|
|||
help="Save and resume all data retrieved "
|
||||
"on a session file")
|
||||
|
||||
miscellaneous.add_option("-c", dest="configFile",
|
||||
help="Load options from a configuration INI file")
|
||||
|
||||
miscellaneous.add_option("--save", dest="saveCmdline", action="store_true",
|
||||
help="Save options on a configuration INI file")
|
||||
|
||||
miscellaneous.add_option("--batch", dest="batch", action="store_true",
|
||||
help="Never ask for user input, use the default behaviour")
|
||||
|
||||
parser.add_option_group(target)
|
||||
parser.add_option_group(request)
|
||||
parser.add_option_group(injection)
|
||||
parser.add_option_group(techniques)
|
||||
|
|
|
|||
|
|
@ -79,12 +79,16 @@ def configFileParser(configFile):
|
|||
config = ConfigParser()
|
||||
config.read(configFile)
|
||||
|
||||
if not config.has_section("Request"):
|
||||
raise NoSectionError, "Request in the configuration file is mandatory"
|
||||
if not config.has_section("Target"):
|
||||
raise NoSectionError, "Target in the configuration file is mandatory"
|
||||
|
||||
if not config.has_option("Request", "url") and not config.has_option("Request", "googleDork"):
|
||||
condition = not config.has_option("Target", "url")
|
||||
condition &= not config.has_option("Target", "list")
|
||||
condition &= not config.has_option("Target", "googleDork")
|
||||
|
||||
if condition:
|
||||
errMsg = "missing a mandatory option in the configuration "
|
||||
errMsg += "file (url or googleDork)"
|
||||
errMsg += "file (url, list or googleDork)"
|
||||
raise sqlmapMissingMandatoryOptionException, errMsg
|
||||
|
||||
for family, optionData in optDict.items():
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ import socket
|
|||
import time
|
||||
import urllib2
|
||||
import urlparse
|
||||
import traceback
|
||||
|
||||
from lib.contrib import multipartpost
|
||||
from lib.core.convert import urlencode
|
||||
|
|
@ -38,6 +39,7 @@ from lib.core.data import conf
|
|||
from lib.core.data import kb
|
||||
from lib.core.data import logger
|
||||
from lib.core.exception import sqlmapConnectionException
|
||||
from lib.core.settings import RETRIES
|
||||
from lib.request.basic import forgeHeaders
|
||||
from lib.request.basic import parseResponse
|
||||
|
||||
|
|
@ -48,6 +50,12 @@ class Connect:
|
|||
This class defines methods used to perform HTTP requests
|
||||
"""
|
||||
|
||||
|
||||
@staticmethod
|
||||
def __getPageProxy(**kwargs):
|
||||
return Connect.getPage(**kwargs)
|
||||
|
||||
|
||||
@staticmethod
|
||||
def getPage(**kwargs):
|
||||
"""
|
||||
|
|
@ -55,6 +63,9 @@ class Connect:
|
|||
the target url page content
|
||||
"""
|
||||
|
||||
if conf.delay != None and isinstance(conf.delay, (int, float)) and conf.delay > 0:
|
||||
time.sleep(conf.delay)
|
||||
|
||||
url = kwargs.get('url', conf.url).replace(" ", "%20")
|
||||
get = kwargs.get('get', None)
|
||||
post = kwargs.get('post', None)
|
||||
|
|
@ -63,6 +74,7 @@ class Connect:
|
|||
direct = kwargs.get('direct', False)
|
||||
multipart = kwargs.get('multipart', False)
|
||||
|
||||
page = ""
|
||||
cookieStr = ""
|
||||
requestMsg = "HTTP request:\n%s " % conf.method
|
||||
responseMsg = "HTTP response "
|
||||
|
|
@ -115,6 +127,9 @@ class Connect:
|
|||
req = urllib2.Request(url, post, headers)
|
||||
conn = urllib2.urlopen(req)
|
||||
|
||||
# Reset the number of connection retries
|
||||
conf.retries = 0
|
||||
|
||||
if not req.has_header("Accept-Encoding"):
|
||||
requestHeaders += "\nAccept-Encoding: identity"
|
||||
|
||||
|
|
@ -161,40 +176,37 @@ class Connect:
|
|||
status = e.msg
|
||||
responseHeaders = e.info()
|
||||
|
||||
except (urllib2.URLError, socket.error), _:
|
||||
warnMsg = "unable to connect to the target url"
|
||||
except (urllib2.URLError, socket.error, socket.timeout, httplib.BadStatusLine), _:
|
||||
tbMsg = traceback.format_exc()
|
||||
|
||||
if "URLError" in tbMsg or "error" in tbMsg:
|
||||
warnMsg = "unable to connect to the target url"
|
||||
|
||||
elif "timeout" in tbMsg:
|
||||
warnMsg = "connection timed out to the target url"
|
||||
|
||||
elif "BadStatusLine" in tbMsg:
|
||||
warnMsg = "the target url responded with an unknown HTTP "
|
||||
warnMsg += "status code, try to force the HTTP User-Agent "
|
||||
warnMsg += "header with option --user-agent or -a"
|
||||
|
||||
if conf.multipleTargets:
|
||||
warnMsg += ", skipping to next url"
|
||||
logger.warn(warnMsg)
|
||||
|
||||
return None
|
||||
else:
|
||||
|
||||
if "BadStatusLine" not in tbMsg:
|
||||
warnMsg += " or proxy"
|
||||
raise sqlmapConnectionException, warnMsg
|
||||
|
||||
except socket.timeout, _:
|
||||
warnMsg = "connection timed out to the target url"
|
||||
if conf.retries < RETRIES:
|
||||
conf.retries += 1
|
||||
|
||||
if conf.multipleTargets:
|
||||
warnMsg += ", skipping to next url"
|
||||
warnMsg += ", sqlmap is going to retry the request"
|
||||
logger.warn(warnMsg)
|
||||
|
||||
return None
|
||||
else:
|
||||
warnMsg += " or proxy"
|
||||
raise sqlmapConnectionException, warnMsg
|
||||
|
||||
except httplib.BadStatusLine, _:
|
||||
warnMsg = "the target url responded with an unknown HTTP "
|
||||
warnMsg += "status code, try to force the HTTP User-Agent "
|
||||
warnMsg += "header with option --user-agent or -a"
|
||||
|
||||
if conf.multipleTargets:
|
||||
warnMsg += ", skipping to next url"
|
||||
logger.warn(warnMsg)
|
||||
|
||||
return None
|
||||
time.sleep(1)
|
||||
return Connect.__getPageProxy(get=get, post=post, cookie=cookie, ua=ua, direct=direct, multipart=multipart)
|
||||
else:
|
||||
raise sqlmapConnectionException, warnMsg
|
||||
|
||||
|
|
@ -208,9 +220,6 @@ class Connect:
|
|||
|
||||
logger.log(8, responseMsg)
|
||||
|
||||
if conf.delay != None and isinstance(conf.delay, (int, float)) and conf.delay > 0:
|
||||
time.sleep(conf.delay)
|
||||
|
||||
return page
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ from lib.core.data import kb
|
|||
from lib.core.data import logger
|
||||
from lib.core.data import queries
|
||||
from lib.core.data import temp
|
||||
from lib.core.settings import TIME_DELAY
|
||||
from lib.core.settings import SECONDS
|
||||
from lib.request.connect import Connect as Request
|
||||
from lib.techniques.inband.union.use import unionUse
|
||||
from lib.techniques.blind.inference import bisection
|
||||
|
|
@ -394,6 +394,6 @@ def goStacked(expression, timeTest=False):
|
|||
duration = int(time.time() - start)
|
||||
|
||||
if timeTest:
|
||||
return (duration >= TIME_DELAY, payload)
|
||||
return (duration >= SECONDS, payload)
|
||||
else:
|
||||
return duration >= TIME_DELAY
|
||||
return duration >= SECONDS
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|||
|
||||
import threading
|
||||
import time
|
||||
import traceback
|
||||
|
||||
from lib.core.agent import agent
|
||||
from lib.core.common import dataToSessionFile
|
||||
|
|
@ -34,7 +35,10 @@ from lib.core.common import replaceNewlineTabs
|
|||
from lib.core.data import conf
|
||||
from lib.core.data import kb
|
||||
from lib.core.data import logger
|
||||
from lib.core.exception import sqlmapConnectionException
|
||||
from lib.core.exception import sqlmapValueException
|
||||
from lib.core.exception import sqlmapThreadException
|
||||
from lib.core.exception import unhandledException
|
||||
from lib.core.progress import ProgressBar
|
||||
from lib.core.unescaper import unescaper
|
||||
from lib.request.connect import Connect as Request
|
||||
|
|
@ -46,6 +50,9 @@ def bisection(payload, expression, length=None):
|
|||
on an affected host
|
||||
"""
|
||||
|
||||
partialValue = ""
|
||||
finalValue = ""
|
||||
|
||||
if kb.dbmsDetected:
|
||||
_, _, _, _, fieldToCastStr = agent.getFields(expression)
|
||||
nulledCastedField = agent.nullAndCastField(fieldToCastStr)
|
||||
|
|
@ -102,6 +109,7 @@ def bisection(payload, expression, length=None):
|
|||
maxValue = limit
|
||||
|
||||
if (maxValue - minValue) == 1:
|
||||
# NOTE: this first condition should never occur
|
||||
if maxValue == 1:
|
||||
return None
|
||||
else:
|
||||
|
|
@ -145,7 +153,7 @@ def bisection(payload, expression, length=None):
|
|||
val = getChar(curidx)
|
||||
|
||||
if val == None:
|
||||
raise sqlmapValueException, "Failed to get character at index %d (expected %d total)" % (curidx, length)
|
||||
raise sqlmapValueException, "failed to get character at index %d (expected %d total)" % (curidx, length)
|
||||
|
||||
value[curidx-1] = val
|
||||
|
||||
|
|
@ -157,9 +165,38 @@ def bisection(payload, expression, length=None):
|
|||
dataToStdout("\r[%s] [INFO] retrieved: %s" % (time.strftime("%X"), s))
|
||||
iolock.release()
|
||||
|
||||
|
||||
def downloadThreadProxy(numThread):
|
||||
try:
|
||||
downloadThread()
|
||||
|
||||
except (sqlmapConnectionException, sqlmapValueException), errMsg:
|
||||
conf.threadException = True
|
||||
logger.error("thread %d: %s" % (numThread + 1, errMsg))
|
||||
|
||||
except KeyboardInterrupt:
|
||||
conf.threadException = True
|
||||
|
||||
print
|
||||
logger.debug("waiting for threads to finish")
|
||||
|
||||
try:
|
||||
while (threading.activeCount() > 1):
|
||||
pass
|
||||
|
||||
except KeyboardInterrupt:
|
||||
raise sqlmapThreadException, "user aborted"
|
||||
|
||||
except:
|
||||
conf.threadException = True
|
||||
errMsg = unhandledException()
|
||||
logger.error("thread %d: %s" % (numThread + 1, errMsg))
|
||||
traceback.print_exc()
|
||||
|
||||
|
||||
# Start the threads
|
||||
for _ in range(numThreads):
|
||||
thread = threading.Thread(target=downloadThread)
|
||||
for numThread in range(numThreads):
|
||||
thread = threading.Thread(target=downloadThreadProxy(numThread))
|
||||
thread.start()
|
||||
threads.append(thread)
|
||||
|
||||
|
|
@ -167,19 +204,27 @@ def bisection(payload, expression, length=None):
|
|||
for thread in threads:
|
||||
thread.join()
|
||||
|
||||
assert None not in value
|
||||
# If we have got one single character not correctly fetched it
|
||||
# can mean that the connection to the target url was lost
|
||||
if None in value:
|
||||
for v in value:
|
||||
if isinstance(v, str) and v != None:
|
||||
partialValue += v
|
||||
|
||||
value = "".join(value)
|
||||
if partialValue:
|
||||
finalValue = partialValue
|
||||
infoMsg = "\r[%s] [INFO] partially retrieved: %s" % (time.strftime("%X"), finalValue)
|
||||
else:
|
||||
finalValue = "".join(value)
|
||||
infoMsg = "\r[%s] [INFO] retrieved: %s" % (time.strftime("%X"), finalValue)
|
||||
|
||||
assert index[0] == length
|
||||
if isinstance(finalValue, str) and len(finalValue) > 0:
|
||||
dataToSessionFile(replaceNewlineTabs(finalValue))
|
||||
|
||||
dataToSessionFile(replaceNewlineTabs(value))
|
||||
|
||||
if conf.verbose in ( 1, 2 ) and not showEta:
|
||||
dataToStdout("\r[%s] [INFO] retrieved: %s" % (time.strftime("%X"), value))
|
||||
if conf.verbose in ( 1, 2 ) and not showEta and infoMsg:
|
||||
dataToStdout(infoMsg)
|
||||
|
||||
else:
|
||||
value = ""
|
||||
index = 0
|
||||
|
||||
while True:
|
||||
|
|
@ -190,7 +235,7 @@ def bisection(payload, expression, length=None):
|
|||
if val == None:
|
||||
break
|
||||
|
||||
value += val
|
||||
finalValue += val
|
||||
|
||||
dataToSessionFile(replaceNewlineTabs(val))
|
||||
|
||||
|
|
@ -203,9 +248,13 @@ def bisection(payload, expression, length=None):
|
|||
dataToStdout("\n")
|
||||
|
||||
if ( conf.verbose in ( 1, 2 ) and showEta and len(str(progress)) >= 64 ) or conf.verbose >= 3:
|
||||
infoMsg = "retrieved: %s" % value
|
||||
infoMsg = "retrieved: %s" % finalValue
|
||||
logger.info(infoMsg)
|
||||
|
||||
dataToSessionFile("]\n")
|
||||
if not partialValue:
|
||||
dataToSessionFile("]\n")
|
||||
|
||||
return queriesCount[0], value
|
||||
if conf.threadException:
|
||||
raise sqlmapThreadException, "something unexpected happen into the threads"
|
||||
|
||||
return queriesCount[0], finalValue
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|||
from lib.core.data import kb
|
||||
from lib.core.data import logger
|
||||
from lib.core.data import queries
|
||||
from lib.core.settings import TIME_DELAY
|
||||
from lib.core.settings import SECONDS
|
||||
from lib.request import inject
|
||||
|
||||
|
||||
|
|
@ -36,7 +36,7 @@ def timeTest():
|
|||
infoMsg += "'%s'" % kb.injParameter
|
||||
logger.info(infoMsg)
|
||||
|
||||
query = queries[kb.dbms].timedelay % TIME_DELAY
|
||||
query = queries[kb.dbms].timedelay % SECONDS
|
||||
timeTest = inject.goStacked(query, timeTest=True)
|
||||
|
||||
if timeTest[0] == True:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue