mirror of
https://github.com/sqlmapproject/sqlmap.git
synced 2026-07-03 15:01:13 +00:00
Minor safety mechanism for HEAD null connection
This commit is contained in:
parent
92a9446c46
commit
d6b491dec4
3 changed files with 75 additions and 29 deletions
|
|
@ -93,6 +93,8 @@ from lib.core.settings import MAX_DIFFLIB_SEQUENCE_LENGTH
|
|||
from lib.core.settings import MAX_STABILITY_DELAY
|
||||
from lib.core.settings import NON_SQLI_CHECK_PREFIX_SUFFIX_LENGTH
|
||||
from lib.core.settings import NOSQL_ERROR_REGEX
|
||||
from lib.core.settings import NULL_CONNECTION_LENGTH_TOLERANCE_HIGH
|
||||
from lib.core.settings import NULL_CONNECTION_LENGTH_TOLERANCE_LOW
|
||||
from lib.core.settings import NULL_CONNECTION_SKIP_READ_MIN_LENGTH
|
||||
from lib.core.settings import PRECONNECT_INCOMPATIBLE_SERVERS
|
||||
from lib.core.settings import SINGLE_QUOTE_MARKER
|
||||
|
|
@ -1533,44 +1535,79 @@ def checkNullConnection():
|
|||
pushValue(kb.pageCompress)
|
||||
kb.pageCompress = False
|
||||
|
||||
# A method is accepted only if the length it reports tracks the real GET response. The
|
||||
# original page length (len(kb.originalPage)) is the reference; a method whose length is
|
||||
# grossly off (e.g. HEAD returning 'Content-Length: 0', HEAD served from a different code
|
||||
# path, or sneaked-in compression) would otherwise make every page look identical and
|
||||
# silently break detection. The band is coarse on purpose (byte-vs-character size and
|
||||
# moderate page dynamism are expected); a false reject just forgoes the optimization
|
||||
def _plausibleLength(length):
|
||||
reference = len(kb.originalPage or "")
|
||||
if not reference:
|
||||
return True
|
||||
return NULL_CONNECTION_LENGTH_TOLERANCE_LOW * reference <= length <= NULL_CONNECTION_LENGTH_TOLERANCE_HIGH * reference
|
||||
|
||||
try:
|
||||
page, headers, _ = Request.getPage(method=HTTPMETHOD.HEAD, raise404=False)
|
||||
|
||||
if not page and HTTP_HEADER.CONTENT_LENGTH in (headers or {}):
|
||||
kb.nullConnection = NULLCONNECTION.HEAD
|
||||
try:
|
||||
length = int(headers[HTTP_HEADER.CONTENT_LENGTH].split(',')[0])
|
||||
except ValueError:
|
||||
length = None
|
||||
|
||||
infoMsg = "NULL connection is supported with HEAD method ('Content-Length')"
|
||||
logger.info(infoMsg)
|
||||
else:
|
||||
if length is not None and _plausibleLength(length):
|
||||
kb.nullConnection = NULLCONNECTION.HEAD
|
||||
|
||||
infoMsg = "NULL connection is supported with HEAD method ('Content-Length')"
|
||||
logger.info(infoMsg)
|
||||
elif length is not None:
|
||||
debugMsg = "HEAD method reports an implausible 'Content-Length' (%d B vs ~%d B for the original page); skipping it" % (length, len(kb.originalPage or ""))
|
||||
logger.debug(debugMsg)
|
||||
|
||||
if kb.nullConnection is None:
|
||||
page, headers, _ = Request.getPage(auxHeaders={HTTP_HEADER.RANGE: "bytes=-1"})
|
||||
|
||||
if page and len(page) == 1 and HTTP_HEADER.CONTENT_RANGE in (headers or {}):
|
||||
kb.nullConnection = NULLCONNECTION.RANGE
|
||||
try:
|
||||
length = int(headers[HTTP_HEADER.CONTENT_RANGE][headers[HTTP_HEADER.CONTENT_RANGE].find('/') + 1:])
|
||||
except ValueError:
|
||||
length = None
|
||||
|
||||
infoMsg = "NULL connection is supported with GET method ('Range')"
|
||||
logger.info(infoMsg)
|
||||
else:
|
||||
_, headers, _ = Request.getPage(skipRead=True)
|
||||
if length is not None and _plausibleLength(length):
|
||||
kb.nullConnection = NULLCONNECTION.RANGE
|
||||
|
||||
if HTTP_HEADER.CONTENT_LENGTH in (headers or {}):
|
||||
try:
|
||||
length = int(headers[HTTP_HEADER.CONTENT_LENGTH].split(',')[0])
|
||||
except ValueError:
|
||||
length = len(kb.originalPage or "")
|
||||
infoMsg = "NULL connection is supported with GET method ('Range')"
|
||||
logger.info(infoMsg)
|
||||
elif length is not None:
|
||||
debugMsg = "'Range' method reports an implausible total length (%d B vs ~%d B for the original page); skipping it" % (length, len(kb.originalPage or ""))
|
||||
logger.debug(debugMsg)
|
||||
|
||||
# Unlike HEAD/Range, 'skip-read' leaves the body unread and must close the
|
||||
# connection (an unread body cannot be reused), paying a fresh TCP/TLS handshake
|
||||
# per request. That only outweighs the avoided body transfer for large responses;
|
||||
# for small ones it is a net slowdown, so it is gated by the response size here
|
||||
if length >= NULL_CONNECTION_SKIP_READ_MIN_LENGTH:
|
||||
kb.nullConnection = NULLCONNECTION.SKIP_READ
|
||||
if kb.nullConnection is None:
|
||||
_, headers, _ = Request.getPage(skipRead=True)
|
||||
|
||||
infoMsg = "NULL connection is supported with 'skip-read' method"
|
||||
logger.info(infoMsg)
|
||||
else:
|
||||
debugMsg = "'skip-read' NULL connection method is available but skipped because the "
|
||||
debugMsg += "response (%d B) is too small for it to outweigh the per-request reconnect cost" % length
|
||||
logger.debug(debugMsg)
|
||||
if HTTP_HEADER.CONTENT_LENGTH in (headers or {}):
|
||||
try:
|
||||
length = int(headers[HTTP_HEADER.CONTENT_LENGTH].split(',')[0])
|
||||
except ValueError:
|
||||
length = len(kb.originalPage or "")
|
||||
|
||||
if not _plausibleLength(length):
|
||||
debugMsg = "'skip-read' method reports an implausible 'Content-Length' (%d B vs ~%d B for the original page); skipping it" % (length, len(kb.originalPage or ""))
|
||||
logger.debug(debugMsg)
|
||||
# Unlike HEAD/Range, 'skip-read' leaves the body unread and must close the
|
||||
# connection (an unread body cannot be reused), paying a fresh TCP/TLS handshake
|
||||
# per request. That only outweighs the avoided body transfer for large responses;
|
||||
# for small ones it is a net slowdown, so it is gated by the response size here
|
||||
elif length >= NULL_CONNECTION_SKIP_READ_MIN_LENGTH:
|
||||
kb.nullConnection = NULLCONNECTION.SKIP_READ
|
||||
|
||||
infoMsg = "NULL connection is supported with 'skip-read' method"
|
||||
logger.info(infoMsg)
|
||||
else:
|
||||
debugMsg = "'skip-read' NULL connection method is available but skipped because the "
|
||||
debugMsg += "response (%d B) is too small for it to outweigh the per-request reconnect cost" % length
|
||||
logger.debug(debugMsg)
|
||||
|
||||
except SqlmapConnectionException:
|
||||
pass
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ from lib.core.enums import OS
|
|||
from thirdparty import six
|
||||
|
||||
# sqlmap version (<major>.<minor>.<month>.<monthly commit>)
|
||||
VERSION = "1.10.6.200"
|
||||
VERSION = "1.10.6.201"
|
||||
TYPE = "dev" if VERSION.count('.') > 2 and VERSION.split('.')[-1] != '0' else "stable"
|
||||
TYPE_COLORS = {"dev": 33, "stable": 90, "pip": 34}
|
||||
VERSION_STRING = "sqlmap/%s#%s" % ('.'.join(VERSION.split('.')[:-1]) if VERSION.count('.') > 2 and VERSION.split('.')[-1] == '0' else VERSION, TYPE)
|
||||
|
|
@ -194,6 +194,15 @@ STRUCTURAL_ID_REGEX = r"""(?si)\bid\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s"'<>]+))""
|
|||
# is a net slowdown, so it is gated by this size
|
||||
NULL_CONNECTION_SKIP_READ_MIN_LENGTH = 256 * 1024
|
||||
|
||||
# Coarse plausibility band for a NULL connection method's reported length, relative to the known
|
||||
# original page length (len(kb.originalPage)). A method is accepted only if its length falls within
|
||||
# it; this rejects a method whose length does not track the real GET response (e.g. HEAD returning
|
||||
# 'Content-Length: 0', HEAD served from a different code path, or sneaked-in compression). The band
|
||||
# is deliberately generous (byte-vs-character size and moderate page dynamism are expected, and a
|
||||
# false reject merely forgoes the optimization, which is safe) - it only catches gross mismatches
|
||||
NULL_CONNECTION_LENGTH_TOLERANCE_LOW = 0.5
|
||||
NULL_CONNECTION_LENGTH_TOLERANCE_HIGH = 4.0
|
||||
|
||||
# Regular expression used for recognition of IP addresses
|
||||
IP_ADDRESS_REGEX = r"\b(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\b"
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue