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:
Bernardo Damele 2008-12-04 17:40:03 +00:00
parent 0f07e33e1a
commit 7f055924a7
16 changed files with 748 additions and 571 deletions

View file

@ -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,

View file

@ -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()

View file

@ -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",

View file

@ -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

View file

@ -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)

View file

@ -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():

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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: