diff --git a/lib/core/common.py b/lib/core/common.py index 7478bf242..6a8afd535 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -160,6 +160,7 @@ from lib.core.settings import REFLECTIVE_MISS_THRESHOLD from lib.core.settings import SAFE_VARIABLE_MARKER from lib.core.settings import SENSITIVE_DATA_REGEX from lib.core.settings import SENSITIVE_OPTIONS +from lib.core.settings import STDIN_PIPE_DASH from lib.core.settings import SUPPORTED_DBMS from lib.core.settings import TEXT_TAG_REGEX from lib.core.settings import TIME_STDEV_COEFF @@ -1165,6 +1166,14 @@ def getHeader(headers, key): break return retVal +def checkPipedInput(): + """ + Checks whether input to program has been provided via standard input (e.g. cat /tmp/req.txt | python sqlmap.py -r -) + # Reference: https://stackoverflow.com/a/33873570 + """ + + return not os.isatty(sys.stdin.fileno()) + def checkFile(filename, raiseOnError=True): """ Checks for file existence and readability @@ -1178,19 +1187,22 @@ def checkFile(filename, raiseOnError=True): if filename: filename = filename.strip('"\'') - try: - if filename is None or not os.path.isfile(filename): - valid = False - except: - valid = False - - if valid: + if filename == STDIN_PIPE_DASH: + return checkPipedInput() + else: try: - with open(filename, "rb"): - pass + if filename is None or not os.path.isfile(filename): + valid = False except: valid = False + if valid: + try: + with open(filename, "rb"): + pass + except: + valid = False + if not valid and raiseOnError: raise SqlmapSystemException("unable to read file '%s'" % filename) @@ -3305,13 +3317,19 @@ def openFile(filename, mode='r', encoding=UNICODE_ENCODING, errors="replace", bu Returns file handle of a given filename """ - try: - return codecs.open(filename, mode, encoding, errors, buffering) - except IOError: - errMsg = "there has been a file opening error for filename '%s'. " % filename - errMsg += "Please check %s permissions on a file " % ("write" if mode and ('w' in mode or 'a' in mode or '+' in mode) else "read") - errMsg += "and that it's not locked by another process." - raise SqlmapSystemException(errMsg) + if filename == STDIN_PIPE_DASH: + if filename not in kb.cache.content: + kb.cache.content[filename] = sys.stdin.read() + + return contextlib.closing(StringIO(readCachedFileContent(filename))) + else: + try: + return codecs.open(filename, mode, encoding, errors, buffering) + except IOError: + errMsg = "there has been a file opening error for filename '%s'. " % filename + errMsg += "Please check %s permissions on a file " % ("write" if mode and ('w' in mode or 'a' in mode or '+' in mode) else "read") + errMsg += "and that it's not locked by another process." + raise SqlmapSystemException(errMsg) def decodeIntToUnicode(value): """ @@ -4797,14 +4815,7 @@ def parseRequestFile(reqFile, checkParams=True): if not(conf.scope and not re.search(conf.scope, url, re.I)): yield (url, conf.method or method, data, cookie, tuple(headers)) - checkFile(reqFile) - try: - with openFile(reqFile, "rb") as f: - content = f.read() - except (IOError, OSError, MemoryError) as ex: - errMsg = "something went wrong while trying " - errMsg += "to read the content of file '%s' ('%s')" % (reqFile, getSafeExString(ex)) - raise SqlmapSystemException(errMsg) + content = readCachedFileContent(reqFile) if conf.scope: logger.info("using regular expression '%s' for filtering targets" % conf.scope) diff --git a/lib/core/option.py b/lib/core/option.py index 9309e0353..4204de6db 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -226,7 +226,7 @@ def _setMultipleTargets(): errMsg = "the specified list of targets does not exist" raise SqlmapFilePathException(errMsg) - if os.path.isfile(conf.logFile): + if checkFile(conf.logFile, False): for target in parseRequestFile(conf.logFile): url, _, data, _, _ = target key = re.sub(r"(\w+=)[^%s ]*" % (conf.paramDel or DEFAULT_GET_POST_DELIMITER), r"\g<1>", "%s %s" % (url, data)) @@ -292,7 +292,7 @@ def _setRequestFromFile(): conf.requestFile = safeExpandUser(conf.requestFile) seen = set() - if not os.path.isfile(conf.requestFile): + if not checkFile(conf.requestFile, False): errMsg = "specified HTTP request file '%s' " % conf.requestFile errMsg += "does not exist" raise SqlmapFilePathException(errMsg) @@ -309,7 +309,7 @@ def _setRequestFromFile(): if conf.secondReq: conf.secondReq = safeExpandUser(conf.secondReq) - if not os.path.isfile(conf.secondReq): + if not checkFile(conf.secondReq, False): errMsg = "specified second-order HTTP request file '%s' " % conf.secondReq errMsg += "does not exist" raise SqlmapFilePathException(errMsg) @@ -411,7 +411,7 @@ def _setBulkMultipleTargets(): infoMsg = "parsing multiple targets list from '%s'" % conf.bulkFile logger.info(infoMsg) - if not os.path.isfile(conf.bulkFile): + if not checkFile(conf.bulkFile, False): errMsg = "the specified bulk file " errMsg += "does not exist" raise SqlmapFilePathException(errMsg) diff --git a/lib/core/settings.py b/lib/core/settings.py index 6a9be7b74..a638906af 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import DBMS_DIRECTORY_NAME from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.3.29" +VERSION = "1.3.3.30" TYPE = "dev" if VERSION.count('.') > 2 and VERSION.split('.')[-1] != '0' else "stable" TYPE_COLORS = {"dev": 33, "stable": 90, "pip": 34} VERSION_STRING = "sqlmap/%s#%s" % ('.'.join(VERSION.split('.')[:-1]) if VERSION.count('.') > 2 and VERSION.split('.')[-1] == '0' else VERSION, TYPE) @@ -222,6 +222,9 @@ try: except LookupError: DEFAULT_PAGE_ENCODING = "utf8" +# Marker for program piped input +STDIN_PIPE_DASH = '-' + # URL used in dummy runs DUMMY_URL = "http://foo/bar?id=1" diff --git a/sqlmap.py b/sqlmap.py index ce3b4cbf8..22821ed84 100755 --- a/sqlmap.py +++ b/sqlmap.py @@ -40,6 +40,7 @@ try: from lib.core.common import banner from lib.core.common import checkIntegrity + from lib.core.common import checkPipedInput from lib.core.common import createGithubIssue from lib.core.common import dataToStdout from lib.core.common import getSafeExString @@ -131,6 +132,9 @@ def main(): cmdLineOptions.update(cmdLineParser().__dict__) initOptions(cmdLineOptions) + if checkPipedInput(): + conf.batch = True + if conf.get("api"): # heavy imports from lib.utils.api import StdDbOut diff --git a/txt/checksum.md5 b/txt/checksum.md5 index a93570f4b..e1f6e65cb 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -30,7 +30,7 @@ c1da277517c7ec4c23e953a51b51e203 lib/controller/handler.py fb6be55d21a70765e35549af2484f762 lib/controller/__init__.py ed7874be0d2d3802f3d20184f2b280d5 lib/core/agent.py a932126e7d80e545c5d44af178d0bc0c lib/core/bigarray.py -c775aa0369c9eb044efb5065f60a25cd lib/core/common.py +b096680d917729fd9658f9b75d44bb3b lib/core/common.py de8d27ae6241163ff9e97aa9e7c51a18 lib/core/convert.py abcb1121eb56d3401839d14e8ed06b6e lib/core/data.py f89512ef3ebea85611c5dde6c891b657 lib/core/datatype.py @@ -43,14 +43,14 @@ f89512ef3ebea85611c5dde6c891b657 lib/core/datatype.py fb6be55d21a70765e35549af2484f762 lib/core/__init__.py 18c896b157b03af716542e5fe9233ef9 lib/core/log.py 947f41084e551ff3b7ef7dda2f25ef20 lib/core/optiondict.py -63c5c71af3fd05cd0a1c97dbbe7216b5 lib/core/option.py +94679a06c134ca5c1db1e435e1cb9fb1 lib/core/option.py fe370021c6bc99daf44b2bfc0d1effb3 lib/core/patch.py 4b12aa67fbf6c973d12e54cf9cb54ea0 lib/core/profiling.py d5ef43fe3cdd6c2602d7db45651f9ceb lib/core/readlineng.py 7d8a22c582ad201f65b73225e4456170 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -96b86cf1a93128720caa88a22f604ea1 lib/core/settings.py +2d4b155258f2ae85c7f287c58742e1fb lib/core/settings.py 4483b4a5b601d8f1c4281071dff21ecc lib/core/shell.py 10fd19b0716ed261e6d04f311f6f527c lib/core/subprocessng.py 10d7e4f7ba2502cce5cf69223c52eddc lib/core/target.py @@ -236,7 +236,7 @@ ec2ba8c757ac96425dcd2b97970edd3a shell/stagers/stager.asp_ 0c48ddb1feb7e38a951ef05a0d48e032 shell/stagers/stager.jsp_ 2f9e459a4cf6a58680978cdce5ff7971 shell/stagers/stager.php_ 41522f8ad02ac133ca0aeaab374c36a8 sqlmapapi.py -a436dd078b06bf664637b27f42889a35 sqlmap.py +05fe39a2fbd1cff87cb43c5c36e64365 sqlmap.py 772fb3dd15edc9d4055ab9f9dee0c203 tamper/0x2char.py 3d89a5c4c33d4d1d9303f5e3bd11f0ae tamper/apostrophemask.py 1fd0eec63970728c1e6628b2e4c21d81 tamper/apostrophenullencode.py