diff --git a/extra/dbwire/firebird.py b/extra/dbwire/firebird.py index 0e1c8ec5afb..647070723d0 100644 --- a/extra/dbwire/firebird.py +++ b/extra/dbwire/firebird.py @@ -341,7 +341,18 @@ def _b2i_signed(b): return n def _scaled(n, scale): - # integer n represents n * 10**scale (scale <= 0); render as an exact decimal string + """ + integer n represents n * 10**scale (scale <= 0); render as an exact decimal string + + >>> _scaled(1234, -2) + '12.34' + >>> _scaled(-5, -2) + '-0.05' + >>> _scaled(5, -4) + '0.0005' + >>> _scaled(7, 0) + '7' + """ if scale >= 0: return str(n * (10 ** scale)) digits = "%0*d" % (-scale + 1, abs(n)) diff --git a/lib/core/settings.py b/lib/core/settings.py index ee2ac49de4a..70cf154e89a 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -20,7 +20,7 @@ from thirdparty import six # sqlmap version (...) -VERSION = "1.10.8.1" +VERSION = "1.10.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/utils/jwt.py b/lib/utils/jwt.py index 358e62b6cf9..88b5490c92c 100644 --- a/lib/utils/jwt.py +++ b/lib/utils/jwt.py @@ -14,6 +14,7 @@ from lib.core.convert import encodeBase64 from lib.core.convert import getBytes from lib.core.convert import getText +from thirdparty import six # a compact JSON Web Token: base64url(header).base64url(payload).base64url(signature); a header always starts # with '{"' which base64url-encodes to the literal prefix 'eyJ', so this matches JWTs embedded in a larger value @@ -46,7 +47,7 @@ def parseJWT(token): except Exception: return None - if not isinstance(header, dict) or "alg" not in header: + if not isinstance(header, dict) or not isinstance(header.get("alg"), six.string_types): return None return {"header": header, "payload": payload, "signature": signature, "signingInput": token.rsplit('.', 1)[0], "raw": token} diff --git a/plugins/dbms/mssqlserver/syntax.py b/plugins/dbms/mssqlserver/syntax.py index 183ce9462c9..c60441855dd 100644 --- a/plugins/dbms/mssqlserver/syntax.py +++ b/plugins/dbms/mssqlserver/syntax.py @@ -16,9 +16,22 @@ def escape(expression, quote=True): True >>> Syntax.escape(u"SELECT 'abcd\xebfgh' FROM foobar") == "SELECT CHAR(97)+CHAR(98)+CHAR(99)+CHAR(100)+NCHAR(235)+CHAR(102)+CHAR(103)+CHAR(104) FROM foobar" True + >>> Syntax.escape(u"SELECT '\U0001f600' FROM foobar") == "SELECT NCHAR(55357)+NCHAR(56832) FROM foobar" + True """ def escaper(value): - return "+".join("%s(%d)" % ("CHAR" if _ < 128 else "NCHAR", _) for _ in getOrds(value)) + chars = [] + + for _ in getOrds(value): + if _ < 128: + chars.append("CHAR(%d)" % _) + elif _ < 0x10000: + chars.append("NCHAR(%d)" % _) + else: + _ -= 0x10000 + chars.append("NCHAR(%d)+NCHAR(%d)" % (0xd800 + (_ >> 10), 0xdc00 + (_ & 0x3ff))) # SQL Server's NCHAR() only accepts BMP values without SC collation, so split into a surrogate pair + + return "+".join(chars) return Syntax._escape(expression, quote, escaper) diff --git a/plugins/generic/filesystem.py b/plugins/generic/filesystem.py index 69ceebb9f55..be6fbd30d12 100644 --- a/plugins/generic/filesystem.py +++ b/plugins/generic/filesystem.py @@ -48,6 +48,8 @@ def __init__(self): self.tblField = "data" def _checkFileLength(self, localFile, remoteFile, fileRead=False): + lengthQuery = None + if Backend.isDbms(DBMS.MYSQL): lengthQuery = "LENGTH(LOAD_FILE('%s'))" % remoteFile @@ -70,6 +72,9 @@ def _checkFileLength(self, localFile, remoteFile, fileRead=False): if fileRead and Backend.isDbms(DBMS.PGSQL): logger.info("length of read file '%s' cannot be checked on PostgreSQL" % remoteFile) sameFile = True + elif lengthQuery is None: + logger.info("length of the %s file '%s' cannot be checked on %s" % ("read" if fileRead else "written", remoteFile, Backend.getDbms())) + sameFile = True else: logger.debug("checking the length of the remote file '%s'" % remoteFile) remoteFileSize = inject.getValue(lengthQuery, resumeValue=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS) diff --git a/tests/test_hql.py b/tests/test_hql.py index 0712b5ce04e..113c7ac3858 100644 --- a/tests/test_hql.py +++ b/tests/test_hql.py @@ -48,6 +48,30 @@ def test_short_entity(self): self.assertEqual(hql._shortEntity("User"), "User") +class TestOriginalValue(unittest.TestCase): + def setUp(self): + self.originalParameters = hql.conf.parameters + self.originalParamDict = hql.conf.paramDict + + def tearDown(self): + hql.conf.parameters = self.originalParameters + hql.conf.paramDict = self.originalParamDict + + def test_original_value_parsed_from_raw_query_string(self): + hql.conf.parameters = {"GET": "id=1&name=alice"} + self.assertEqual(hql._originalValue("GET", "name"), "alice") + + def test_original_value_falls_back_to_param_dict(self): + hql.conf.parameters = {} + hql.conf.paramDict = {"GET": {"name": "bob"}} + self.assertEqual(hql._originalValue("GET", "name"), "bob") + + def test_original_value_missing_returns_empty(self): + hql.conf.parameters = {} + hql.conf.paramDict = {} + self.assertEqual(hql._originalValue("GET", "nope"), "") + + class TestBoundary(unittest.TestCase): def test_wrap_string(self): b = hql.Boundary("' OR ", " OR '1'='2", True) diff --git a/tests/test_jwt.py b/tests/test_jwt.py index 31d0cf66859..b1ff857d045 100644 --- a/tests/test_jwt.py +++ b/tests/test_jwt.py @@ -24,6 +24,7 @@ from lib.core.enums import PLACE from lib.utils.jwt import auditJWT from lib.utils.jwt import crackHMAC +from lib.utils.jwt import encodeSegment from lib.utils.jwt import findJWTs from lib.utils.jwt import forgeJWT from lib.utils.jwt import parseJWT @@ -41,6 +42,12 @@ def test_parse_rejects_non_jwt(self): for value in ("", "a.b", "a.b.c.d", "not.a.jwt", "eyJx.eyJx"): self.assertIsNone(parseJWT(value)) + def test_parse_rejects_non_string_alg(self): + # RFC 7515: "alg" MUST be a string; a crafted token with e.g. an integer "alg" must not parse + # (a permissive gate here would let a non-string "alg" reach auditJWT's alg.strip() and crash) + token = "%s.%s." % (encodeSegment({"alg": 123}), encodeSegment({})) + self.assertIsNone(parseJWT(token)) + def test_forge_none_is_unsigned(self): token = forgeJWT({"alg": "none"}, {"user": "admin"}) self.assertTrue(token.endswith("."))