Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion extra/dbwire/firebird.py
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
2 changes: 1 addition & 1 deletion lib/core/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
from thirdparty import six

# sqlmap version (<major>.<minor>.<month>.<monthly commit>)
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)
Expand Down
3 changes: 2 additions & 1 deletion lib/utils/jwt.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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}
Expand Down
15 changes: 14 additions & 1 deletion plugins/dbms/mssqlserver/syntax.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
5 changes: 5 additions & 0 deletions plugins/generic/filesystem.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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)
Expand Down
24 changes: 24 additions & 0 deletions tests/test_hql.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
7 changes: 7 additions & 0 deletions tests/test_jwt.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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("."))
Expand Down