diff --git a/lib/takeover/abstraction.py b/lib/takeover/abstraction.py index c1ec683bc..51fab9854 100644 --- a/lib/takeover/abstraction.py +++ b/lib/takeover/abstraction.py @@ -15,6 +15,7 @@ from lib.core.data import conf from lib.core.data import logger from lib.core.enums import DBMS from lib.core.enums import PAYLOAD +from lib.core.exception import SqlmapFilePathException from lib.core.exception import SqlmapUnsupportedFeatureException from lib.core.shell import autoCompletion from lib.request import inject @@ -195,7 +196,11 @@ class Abstraction(Web, UDF, Xp_cmdshell): logger.warn(warnMsg) if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL): - self.udfInjectSys() + success = self.udfInjectSys() + + if success is not True: + msg = "unable to mount the operating system takeover" + raise SqlmapFilePathException(msg) elif Backend.isDbms(DBMS.MSSQL): if mandatory: self.xpCmdshellInit() diff --git a/lib/takeover/udf.py b/lib/takeover/udf.py index 6f4868efd..8375f2a4d 100644 --- a/lib/takeover/udf.py +++ b/lib/takeover/udf.py @@ -137,6 +137,8 @@ class UDF: raise SqlmapUnsupportedFeatureException(errMsg) def udfInjectCore(self, udfDict): + written = False + for udf in udfDict.keys(): if udf in self.createdUdf: continue @@ -145,7 +147,22 @@ class UDF: if len(self.udfToCreate) > 0: self.udfSetRemotePath() - self.writeFile(self.udfLocalFile, self.udfRemoteFile, "binary") + written = self.writeFile(self.udfLocalFile, self.udfRemoteFile, "binary", forceCheck=True) + + if written is not True: + errMsg = "there has been a problem uploading the shared library, " + errMsg += "it looks like the binary file has not been written " + errMsg += "on the database underlying file system" + logger.error(errMsg) + + message = "do you want to proceed anyway? Beware that the " + message += "operating system takeover will fail [y/N] " + choice = readInput(message, default="N") + + if choice and choice.lower() == "y": + written = True + else: + return False for udf, inpRet in udfDict.items(): if udf in self.udfToCreate and udf not in self.createdUdf: @@ -158,10 +175,12 @@ class UDF: self.udfCreateSupportTbl(supportTblType) + return written + def udfInjectSys(self): self.udfSetLocalPaths() self.udfCheckNeeded() - self.udfInjectCore(self.sysUdfs) + return self.udfInjectCore(self.sysUdfs) def udfInjectCustom(self): if Backend.getIdentifiedDbms() not in (DBMS.MYSQL, DBMS.PGSQL): @@ -297,7 +316,11 @@ class UDF: self.udfs[udfName]["return"] = retType break - self.udfInjectCore(self.udfs) + success = self.udfInjectCore(self.udfs) + + if success is False: + self.cleanup(udfDict=self.udfs) + return False msg = "do you want to call your injected user-defined " msg += "functions now? [Y/n/q] " diff --git a/plugins/dbms/mssqlserver/filesystem.py b/plugins/dbms/mssqlserver/filesystem.py index 585d005b8..076874ac7 100644 --- a/plugins/dbms/mssqlserver/filesystem.py +++ b/plugins/dbms/mssqlserver/filesystem.py @@ -326,7 +326,7 @@ class Filesystem(GenericFilesystem): self.execCmd(complComm) - def stackedWriteFile(self, wFile, dFile, fileType): + def stackedWriteFile(self, wFile, dFile, fileType, forceCheck=False): # NOTE: this is needed here because we use xp_cmdshell extended # procedure to write a file on the back-end Microsoft SQL Server # file system @@ -341,9 +341,9 @@ class Filesystem(GenericFilesystem): self._stackedWriteFileVbs(tmpPath, wFileContent, dFile, fileType) - sameFile = self.askCheckWrittenFile(wFile, dFile) + written = self.askCheckWrittenFile(wFile, dFile) - if sameFile is False: + if written is False: message = "do you want to try to upload the file with " message += "another technique? [Y/n] " choice = readInput(message, default="Y") @@ -351,4 +351,6 @@ class Filesystem(GenericFilesystem): if not choice or choice.lower() == "y": self._stackedWriteFileDebugExe(tmpPath, wFile, wFileContent, dFile, fileType) #self._stackedWriteFilePS(tmpPath, wFileContent, dFile, fileType) - self.askCheckWrittenFile(wFile, dFile) + written = self.askCheckWrittenFile(wFile, dFile, forceCheck) + + return written diff --git a/plugins/dbms/mysql/filesystem.py b/plugins/dbms/mysql/filesystem.py index 7a57ed09b..08f3ebc63 100644 --- a/plugins/dbms/mysql/filesystem.py +++ b/plugins/dbms/mysql/filesystem.py @@ -104,7 +104,7 @@ class Filesystem(GenericFilesystem): warnMsg += "file as a leftover from UNION query" singleTimeWarnMessage(warnMsg) - def stackedWriteFile(self, wFile, dFile, fileType): + def stackedWriteFile(self, wFile, dFile, fileType, forceCheck=False): debugMsg = "creating a support table to write the hexadecimal " debugMsg += "encoded file to" logger.debug(debugMsg) @@ -131,4 +131,4 @@ class Filesystem(GenericFilesystem): # Reference: http://dev.mysql.com/doc/refman/5.1/en/select.html inject.goStacked("SELECT %s FROM %s INTO DUMPFILE '%s'" % (self.tblField, self.fileTblName, dFile), silent=True) - self.askCheckWrittenFile(wFile, dFile) + return self.askCheckWrittenFile(wFile, dFile, forceCheck) diff --git a/plugins/dbms/postgresql/filesystem.py b/plugins/dbms/postgresql/filesystem.py index c638559a5..6c3e8e385 100644 --- a/plugins/dbms/postgresql/filesystem.py +++ b/plugins/dbms/postgresql/filesystem.py @@ -33,7 +33,7 @@ class Filesystem(GenericFilesystem): errMsg += "query SQL injection technique" raise SqlmapUnsupportedFeatureException(errMsg) - def stackedWriteFile(self, wFile, dFile, fileType): + def stackedWriteFile(self, wFile, dFile, fileType, forceCheck=False): wFileSize = os.path.getsize(wFile) if wFileSize > 8192: @@ -110,6 +110,8 @@ class Filesystem(GenericFilesystem): # (pg_largeobject 'data' field) inject.goStacked("SELECT lo_export(%d, '%s')" % (self.oid, dFile), silent=True) - self.askCheckWrittenFile(wFile, dFile) + written = self.askCheckWrittenFile(wFile, dFile, forceCheck) inject.goStacked("SELECT lo_unlink(%d)" % self.oid) + + return written diff --git a/plugins/generic/filesystem.py b/plugins/generic/filesystem.py index 1f155c46d..6af3cb2e8 100644 --- a/plugins/generic/filesystem.py +++ b/plugins/generic/filesystem.py @@ -135,13 +135,17 @@ class Filesystem: return retVal - def askCheckWrittenFile(self, localFile, remoteFile): - message = "do you want confirmation that the local file '%s' " % localFile - message += "has been successfully written on the back-end DBMS " - message += "file system (%s)? [Y/n] " % remoteFile - output = readInput(message, default="Y") + def askCheckWrittenFile(self, localFile, remoteFile, forceCheck=False): + output = None + if forceCheck is not True: + message = "do you want confirmation that the local file '%s' " % localFile + message += "has been successfully written on the back-end DBMS " + message += "file system (%s)? [Y/n] " % remoteFile + output = readInput(message, default="Y") - if not output or output in ("y", "Y"): + readInput("press ENTER to continue :)") + + if forceCheck or (not output or output in ("y", "Y")): return self._checkFileLength(localFile, remoteFile) return True @@ -249,7 +253,9 @@ class Filesystem: return localFilePaths - def writeFile(self, localFile, remoteFile, fileType=None): + def writeFile(self, localFile, remoteFile, fileType=None, forceCheck=False): + written = False + self.checkDbmsOs() if localFile.endswith('_'): @@ -261,7 +267,7 @@ class Filesystem: debugMsg += "stacked query SQL injection technique" logger.debug(debugMsg) - self.stackedWriteFile(localFile, remoteFile, fileType) + written = self.stackedWriteFile(localFile, remoteFile, fileType, forceCheck) self.cleanup(onlyFileTbl=True) elif isTechniqueAvailable(PAYLOAD.TECHNIQUE.UNION) and Backend.isDbms(DBMS.MYSQL): debugMsg = "going to upload the %s file with " % fileType @@ -276,3 +282,5 @@ class Filesystem: logger.error(errMsg) return None + + return written