diff --git a/lib/core/agent.py b/lib/core/agent.py index 001a66a3e..e11afe200 100644 --- a/lib/core/agent.py +++ b/lib/core/agent.py @@ -183,8 +183,12 @@ class Agent(object): newValue = self.adjustLateValues(newValue) # TODO: support for POST_HINT - newValue = encodeBase64(newValue, binary=False, encoding=conf.encoding or UNICODE_ENCODING) - origValue = encodeBase64(origValue, binary=False, encoding=conf.encoding or UNICODE_ENCODING) + newValue = encodeBase64(newValue, binary=False, encoding=conf.encoding or UNICODE_ENCODING, safe=conf.base64Safe) + + if parameter in kb.base64Originals: + origValue = kb.base64Originals[parameter] + else: + 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 684a9e29f..4e2169bcc 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -631,7 +631,7 @@ def paramToDict(place, parameters=None): if parameter in (conf.base64Parameter or []): try: - oldValue = value + kb.base64Originals[parameter] = oldValue = value 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: diff --git a/lib/core/convert.py b/lib/core/convert.py index 1feae196e..11197b537 100644 --- a/lib/core/convert.py +++ b/lib/core/convert.py @@ -198,7 +198,7 @@ def decodeBase64(value, binary=True, encoding=None): True >>> decodeBase64("MTIz", binary=False) '123' - >>> decodeBase64("A-B_CD") == decodeBase64("A+B/CD") + >>> decodeBase64("A-B_CDE") == decodeBase64("A+B/CDE") True >>> decodeBase64(b"MTIzNA") == b"1234" True @@ -231,7 +231,7 @@ def decodeBase64(value, binary=True, encoding=None): return retVal -def encodeBase64(value, binary=True, encoding=None, padding=True): +def encodeBase64(value, binary=True, encoding=None, padding=True, safe=False): """ Returns a decoded representation of provided Base64 value @@ -241,6 +241,8 @@ def encodeBase64(value, binary=True, encoding=None, padding=True): 'MTIzNA==' >>> encodeBase64(u"1234", binary=False, padding=False) 'MTIzNA' + >>> encodeBase64(decodeBase64("A-B_CDE"), binary=False, safe=True) + 'A-B_CDE' """ if value is None: @@ -254,6 +256,16 @@ def encodeBase64(value, binary=True, encoding=None, padding=True): if not binary: retVal = getText(retVal, encoding) + if safe: + padding = False + + # Reference: https://en.wikipedia.org/wiki/Base64#URL_applications + # Reference: https://perldoc.perl.org/MIME/Base64.html + if isinstance(retVal, bytes): + retVal = retVal.replace(b'+', b'-').replace(b'/', b'_') + else: + retVal = retVal.replace('+', '-').replace('/', '_') + if not padding: retVal = retVal.rstrip(b'=' if isinstance(retVal, bytes) else '=') diff --git a/lib/core/option.py b/lib/core/option.py index 59924fb43..7e78ce032 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -1856,6 +1856,7 @@ def _setKnowledgeBaseAttributes(flushAll=True): kb.arch = None kb.authHeader = None kb.bannerFp = AttribDict() + kb.base64Originals = {} kb.binaryField = False kb.browserVerification = None diff --git a/lib/core/optiondict.py b/lib/core/optiondict.py index 434754d93..57a28d3ef 100644 --- a/lib/core/optiondict.py +++ b/lib/core/optiondict.py @@ -203,6 +203,7 @@ optDict = { "answers": "string", "batch": "boolean", "base64Parameter": "string", + "base64Safe": "boolean", "binaryFields": "string", "charset": "string", "checkInternet": "boolean", diff --git a/lib/core/settings.py b/lib/core/settings.py index 9e789fe64..7fb330c53 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from lib.core.enums import OS from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.4.8.6" +VERSION = "1.4.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/parse/cmdline.py b/lib/parse/cmdline.py index a8e7e1f7b..9869199f3 100644 --- a/lib/parse/cmdline.py +++ b/lib/parse/cmdline.py @@ -622,6 +622,9 @@ def cmdLineParser(argv=None): general.add_argument("--base64", dest="base64Parameter", help="Parameter(s) containing Base64 encoded data") + general.add_argument("--base64-safe", dest="base64Safe", action="store_true", + help="Use URL and filename safe Base64 alphabet") + general.add_argument("--batch", dest="batch", action="store_true", help="Never ask for user input, use the default behavior") diff --git a/sqlmap.conf b/sqlmap.conf index fa4389f1d..3d46fb6ba 100644 --- a/sqlmap.conf +++ b/sqlmap.conf @@ -699,6 +699,10 @@ answers = # Parameter(s) containing Base64 encoded data base64Parameter = +# Use URL and filename safe Base64 alphabet (Reference: https://en.wikipedia.org/wiki/Base64#URL_applications). +# Valid: True or False +base64Safe = False + # Never ask for user input, use the default behaviour. # Valid: True or False batch = False