diff --git a/lib/core/common.py b/lib/core/common.py index 0b1904eed..982241b4b 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -1809,7 +1809,17 @@ def parseTargetUrl(): errMsg += "in the hostname part" raise SqlmapGenericException(errMsg) - 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"))) + netloc = urlSplit.netloc + + # Note: strip any URL userinfo ('user:pass@') so it is not mistaken for host:port (as the proxy + # parser already does). Credentials embedded in the URL are NOT applied - '--auth-cred' is the + # supported way to pass HTTP authentication - so warn rather than silently dropping them. + if '@' in netloc: + if not any((conf.get("authType"), conf.get("authCred"), conf.get("authFile"))): + singleTimeWarnMessage("credentials in the target URL are ignored. Use '--auth-cred' for HTTP authentication") + netloc = netloc.rsplit('@', 1)[-1] + + hostnamePort = netloc.split(":") if not re.search(r"\[.+\]", netloc) else filterNone((re.search(r"\[.+\]", netloc).group(0), re.search(r"\](:(?P\d+))?", netloc).group("port"))) conf.scheme = (urlSplit.scheme.strip().lower() or "http") conf.path = urlSplit.path.strip() diff --git a/lib/core/settings.py b/lib/core/settings.py index 583661971..acf6fdf3f 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -20,7 +20,7 @@ from lib.core.enums import OS from thirdparty import six # sqlmap version (...) -VERSION = "1.10.7.133" +VERSION = "1.10.7.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/tests/test_targeturl.py b/tests/test_targeturl.py index 74c14c071..6db349a85 100644 --- a/tests/test_targeturl.py +++ b/tests/test_targeturl.py @@ -11,8 +11,10 @@ conf.scheme / conf.path - the values every subsequent request is built from. A wrong default port or dropped scheme here misdirects the entire scan, so the scheme/default-port/explicit-port/path cases are pinned. -(Inline URL credentials user:pw@host are intentionally not covered - sqlmap -uses --auth-cred for that and does not parse them out of conf.url.) +Inline URL credentials (user:pw@host) are stripped so the host/port parse +correctly - previously the userinfo was mistaken for the host (user:pass@host -> +hostname 'user'). The credentials are still NOT used for authentication (sqlmap +warns and expects --auth-cred); only the host-misparse is fixed. """ import os @@ -80,5 +82,24 @@ class TestPath(unittest.TestCase): self.assertEqual(_parse("http://host/some/path?q=1")[3], "/some/path") +class TestInlineCredentials(unittest.TestCase): + """Userinfo (user:pw@) must be stripped from the host, not mistaken for it.""" + + def test_user_pass_with_port(self): + host, port, _, _ = _parse("http://user:pass@host:8080/?id=1") + self.assertEqual((host, port), ("host", 8080)) + + def test_user_pass_default_port(self): + host, port, _, _ = _parse("http://user:pass@host/?id=1") + self.assertEqual((host, port), ("host", 80)) + + def test_user_only(self): + self.assertEqual(_parse("http://user@host/?id=1")[0], "host") + + def test_credentials_with_ipv6(self): + host, port, _, _ = _parse("http://user:pass@[::1]:8443/?id=1") + self.assertEqual((host, port), ("::1", 8443)) + + if __name__ == "__main__": unittest.main(verbosity=2)