From 8a458fc8d068ba842dc629b75ba7462a4fa17bcf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miroslav=20=C5=A0tampar?= Date: Thu, 18 Jun 2026 13:14:15 +0200 Subject: [PATCH 1/4] Removal of duplicate boundary --- data/txt/sha256sums.txt | 4 ++-- data/xml/boundaries.xml | 8 -------- lib/core/settings.py | 2 +- 3 files changed, 3 insertions(+), 11 deletions(-) diff --git a/data/txt/sha256sums.txt b/data/txt/sha256sums.txt index f6a88d4c87e..ab376f2e64c 100644 --- a/data/txt/sha256sums.txt +++ b/data/txt/sha256sums.txt @@ -76,7 +76,7 @@ c5b9d622aca6da735e7ed9906e28c7e061e97c223ef92ba1a5d5028ecbb16962 data/udf/postg a7eb4d1bcbdfd155383dcd35396e2d9dd40c2e89ce9d5a02e63a95a94f0ab4ea data/xml/banner/sharepoint.xml e2febc92f9686eacf17a0054f175917b783cc6638ca570435a5203b03245fc18 data/xml/banner/x-aspnet-version.xml 3a440fbbf8adffbe6f570978e96657da2750c76043f8e88a2c269fe9a190778c data/xml/banner/x-powered-by.xml -0223157364ea212de98190e7c6f46f9d2ee20cf3d17916d1af16e857bb5dc575 data/xml/boundaries.xml +a32fc8796082d2e45cfc969f0b45ad476bf87a8515d67b2fed77c5058df5a0f5 data/xml/boundaries.xml 0baf0fade74d4ad294ee88ef306743da0c6a4631b8d640708809103ef9cf63ed data/xml/errors.xml d0b094a110bccec97d50037cc51445191561c0722ec53bf2cebe1521786e2451 data/xml/payloads/boolean_blind.xml 53d0f29459f37248c320d5cb9960d432f46889696d27ae30cc3a3309fd6e026c data/xml/payloads/error_based.xml @@ -189,7 +189,7 @@ ccc4a717e887652b1fcce073d9409d9c59a3b28548c703a9e453d15845f90cd7 lib/core/patch 48797d6c34dd9bb8a53f7f3794c85f4288d82a9a1d6be7fcf317d388cb20d4b3 lib/core/replication.py 0b8c38a01bb01f843d94a6c5f2075ee47520d0c4aa799cecea9c3e2c5a4a23a6 lib/core/revision.py 888daba83fd4a34e9503fe21f01fef4cc730e5cde871b1d40e15d4cbc847d56c lib/core/session.py -997888bab1d98fb9bc2550f3ab99df966d37f38719a41a8fb767e2cd79db6c4f lib/core/settings.py +c5bd1d6e862412495961bee8513e9e2f78a8f90ca15fdc53a87f221b84f9ab70 lib/core/settings.py cd5a66deee8963ba8e7e9af3dd36eb5e8127d4d68698811c29e789655f507f82 lib/core/shell.py bcb5d8090d5e3e0ef2a586ba09ba80eef0c6d51feb0f611ed25299fbb254f725 lib/core/subprocessng.py 70ea3768f1b3062b22d20644df41c86238157ec80dd43da40545c620714273c6 lib/core/target.py diff --git a/data/xml/boundaries.xml b/data/xml/boundaries.xml index ccf93177a58..cea5457cdd6 100644 --- a/data/xml/boundaries.xml +++ b/data/xml/boundaries.xml @@ -441,14 +441,6 @@ Formats: )+ - - 5 - 9 - 1 - 2 - '+(SELECT '[RANDSTR]' WHERE [RANDNUM]=[RANDNUM] - )+' - diff --git a/lib/core/settings.py b/lib/core/settings.py index 6a295c57f59..faf1d11db58 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -20,7 +20,7 @@ from thirdparty import six # sqlmap version (...) -VERSION = "1.10.6.121" +VERSION = "1.10.6.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) From 8de9c5899d6e2222e29252a4552d1e2eda008c12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miroslav=20=C5=A0tampar?= Date: Thu, 18 Jun 2026 17:46:40 +0200 Subject: [PATCH 2/4] Couple of improvements --- data/txt/sha256sums.txt | 34 +++++++++---------- data/xml/payloads/error_based.xml | 4 +-- data/xml/payloads/inline_query.xml | 2 +- lib/controller/checks.py | 13 ++++++++ lib/controller/controller.py | 2 +- lib/core/agent.py | 2 +- lib/core/bigarray.py | 4 +-- lib/core/common.py | 4 +-- lib/core/option.py | 6 ++-- lib/core/optiondict.py | 2 +- lib/core/settings.py | 7 +++- lib/core/testing.py | 2 +- lib/parse/cmdline.py | 2 +- lib/request/inject.py | 12 ++++--- lib/techniques/error/use.py | 6 ++-- lib/techniques/union/use.py | 6 ++-- lib/utils/prove.py | 49 +++++++++++++++++++++++----- plugins/dbms/mssqlserver/takeover.py | 2 +- 18 files changed, 108 insertions(+), 51 deletions(-) diff --git a/data/txt/sha256sums.txt b/data/txt/sha256sums.txt index ab376f2e64c..18bc616082f 100644 --- a/data/txt/sha256sums.txt +++ b/data/txt/sha256sums.txt @@ -79,8 +79,8 @@ e2febc92f9686eacf17a0054f175917b783cc6638ca570435a5203b03245fc18 data/xml/banne a32fc8796082d2e45cfc969f0b45ad476bf87a8515d67b2fed77c5058df5a0f5 data/xml/boundaries.xml 0baf0fade74d4ad294ee88ef306743da0c6a4631b8d640708809103ef9cf63ed data/xml/errors.xml d0b094a110bccec97d50037cc51445191561c0722ec53bf2cebe1521786e2451 data/xml/payloads/boolean_blind.xml -53d0f29459f37248c320d5cb9960d432f46889696d27ae30cc3a3309fd6e026c data/xml/payloads/error_based.xml -b0f434f64105bd61ab0f6867b3f681b97fa02b4fb809ac538db382d031f0e609 data/xml/payloads/inline_query.xml +6ebf0da74b18c95aee4fd4fc2874bda4b3780dc4254806f3968b953fa01bdca1 data/xml/payloads/error_based.xml +516a2ff314bba3ecf65d0371bf8c2654ad79b09c0737b1fe0f178d7885a9508d data/xml/payloads/inline_query.xml 0648264166455010921df1ec431e4c973809f37ef12cbfea75f95029222eb689 data/xml/payloads/stacked_queries.xml 997556b6170964a64474a2e053abe33cf2cf029fb1acec660d4651cc67a3c7e1 data/xml/payloads/time_blind.xml 40a4878669f318568097719d07dc906a19b8520bc742be3583321fc1e8176089 data/xml/payloads/union_query.xml @@ -162,13 +162,13 @@ df768bcb9838dc6c46dab9b4a877056cb4742bd6cfaaf438c4a3712c5cc0d264 extra/shutils/ 1966ca704961fb987ab757f0a4afddbf841d1a880631b701487c75cef63d60c3 extra/vulnserver/__init__.py 63657c00a046ca0fb28fd069407ab6305bd7b95c42f26a96ed083fd05b152252 extra/vulnserver/vulnserver.py 3abecaec1a9c59645a4821463a2d761235f7a4f763a491f188a41a083bbddd98 lib/controller/action.py -72707b5bdfc757c4e5271e156178919292b991a6e7337d3dcdeffea9df6db3ea lib/controller/checks.py -dcd4adcd7a2447a624ca7927541941d25767a4581af2d762c3197dc93790f4df lib/controller/controller.py +9387fb775b694156a71b336a2a9638ef24c577aa38746f391ac040ff05306d95 lib/controller/checks.py +96463b969312bd4fd29452b5fc739f33e5a73f81fdc1ef80ac27debbe9926e42 lib/controller/controller.py d69e84f1648cdb907f5d2dd454f03874a4613752b07867510145d51d84b3c56f lib/controller/handler.py 1966ca704961fb987ab757f0a4afddbf841d1a880631b701487c75cef63d60c3 lib/controller/__init__.py -b36b085ff1b5797e375c1e2ca3b12c7ab4204f48acd1a1efb075cff8302d9750 lib/core/agent.py -ca3e5ce56cb1cae0a8e815425ab6810068004bffe8861d1037c7c87c0ae02477 lib/core/bigarray.py -734a00fd87c67cde48d9ab9b5cdfa8b064300939898c4de2636e91d16a4223ba lib/core/common.py +1d7ed24bc41b9b73d7483a41c8b9162e95c7c027b1d07e52904c75fcad42fcfd lib/core/agent.py +12d0f1f28796b6fbf5629a3fd335b4098eac0583f832d1aa650efa22bf52e782 lib/core/bigarray.py +f3725380a33c370c263516863d8e2bf3582f0ea6e37d45df8c176aa62ade19a1 lib/core/common.py 8f1272487e1adfcc8c755a2f56f0c6d21eac5e685a73a9a159482f9dc9142bc5 lib/core/compat.py 742bce10b97034966021ec60c7ac294db4af4fe7893613d63172a02c29f009f8 lib/core/convert.py c03dc585f89642cfd81b087ac2723e3e1bb3bfa8c60e6f5fe58ef3b0113ebfe6 lib/core/data.py @@ -181,26 +181,26 @@ e4f92e09737ff0dda7ec30e0db1912570e252853b3af9b8f2b9f68ad33cf09fe lib/core/enums 5387168e5dfedd94ae22af7bb255f27d6baaca50b24179c6b98f4f325f5cc7b4 lib/core/exception.py 1966ca704961fb987ab757f0a4afddbf841d1a880631b701487c75cef63d60c3 lib/core/__init__.py 914a13ee21fd610a6153a37cbe50830fcbd1324c7ebc1e7fc206d5e598b0f7ad lib/core/log.py -06651cff25422dcb84c159f80faf8dc377d82ddd451b5910f12c4c6a3ebe1e94 lib/core/optiondict.py -e3a3729a24306b7ecace614fe27a8123c0becb0c5283ca519e5bcf376af2c711 lib/core/option.py +96d54c79a2982709ba5639bc997c76db700e85c7fdcb474cedb490132f7ae5ad lib/core/optiondict.py +8084a0efe82bf3d3ff98f988bc6227d72ca015ac665afee9a8afc09afac2be52 lib/core/option.py ccc4a717e887652b1fcce073d9409d9c59a3b28548c703a9e453d15845f90cd7 lib/core/patch.py 49c0fa7e3814dfda610d665ee02b12df299b28bc0b6773815b4395514ddf8dec lib/core/profiling.py 03db48f02c3d07a047ddb8fe33a757b6238867352d8ddda2a83e4fec09a98d04 lib/core/readlineng.py 48797d6c34dd9bb8a53f7f3794c85f4288d82a9a1d6be7fcf317d388cb20d4b3 lib/core/replication.py 0b8c38a01bb01f843d94a6c5f2075ee47520d0c4aa799cecea9c3e2c5a4a23a6 lib/core/revision.py 888daba83fd4a34e9503fe21f01fef4cc730e5cde871b1d40e15d4cbc847d56c lib/core/session.py -c5bd1d6e862412495961bee8513e9e2f78a8f90ca15fdc53a87f221b84f9ab70 lib/core/settings.py +5edba86522bc49aa6caf80118fc560610e76cc7f35a3c3c09a8052747a3b97ef lib/core/settings.py cd5a66deee8963ba8e7e9af3dd36eb5e8127d4d68698811c29e789655f507f82 lib/core/shell.py bcb5d8090d5e3e0ef2a586ba09ba80eef0c6d51feb0f611ed25299fbb254f725 lib/core/subprocessng.py 70ea3768f1b3062b22d20644df41c86238157ec80dd43da40545c620714273c6 lib/core/target.py -c39dae0602b356d42f55df369c05614bbfb00c2abf2f0419fefe2ae781aa3098 lib/core/testing.py +a43da1fc59cbc48698db34dc3516967910c84ef5945cf5423c2954acd54fe898 lib/core/testing.py e3e653364d08d04d7492aa40a2bd29c6a28f4d78fecdd6c10f21f6cb28b98b4c lib/core/threads.py b9aacb840310173202f79c2ba125b0243003ee6b44c92eca50424f2bdfc83c02 lib/core/unescaper.py 53e396902cb2546eaa09e77073fcba8be8827ee9ce055cfc899e81b0e6ad4d6d lib/core/update.py 2400e465fa4d13e4c32795910878c71ff212e4361b46428d57ce43983f5e997c lib/core/wordlist.py 1966ca704961fb987ab757f0a4afddbf841d1a880631b701487c75cef63d60c3 lib/__init__.py 54bfd31ebded3ffa5848df1c644f196eb704116517c7a3d860b5d081e984d821 lib/parse/banner.py -14b2fcfa2d6c3a155e3b85f093929c6129893ad191d1988a717daa1ffbb422e7 lib/parse/cmdline.py +8c18ec0dc54dd313033408d7f55556d2068dbbafd9dd92a759d770843a680fd9 lib/parse/cmdline.py 02d82e4069bd98c52755417f8b8e306d79945672656ac24f1a45e7a6eff4b158 lib/parse/configfile.py c5b258be7485089fac9d9cd179960e774fbd85e62836dc67cce76cc028bb6aeb lib/parse/handler.py 5c9a9caee948843d5537745640cc7b98d70a0412cc0949f59d4ebe8b2907c06c lib/parse/headers.py @@ -217,7 +217,7 @@ ec14b5139cd6b03aa167a7b91fab913baf042d4370471390c13eed325eeb245f lib/request/co cf019248253a5d7edb7bc474aa020b9e8625d73008a463c56ba2b539d7f2d8ec lib/request/dns.py 92c81cc31ff4a396723242058fb2152c9e9745f8412d01ea74480b048a53af6c lib/request/httpshandler.py 1966ca704961fb987ab757f0a4afddbf841d1a880631b701487c75cef63d60c3 lib/request/__init__.py -aeeeb5f0148078e30d52208184042efc3618d3f2e840d7221897aae34315824e lib/request/inject.py +7a0ac2522213e756348fd871a7af74cc963bdc82f9d7ade57be5de42b5bf7cab lib/request/inject.py ada4d305d6ce441f79e52ec3f2fc23869ee2fa87c017723e8f3ed0dfa61cdab4 lib/request/methodrequest.py 43a7fdf64e7ba63c6b2d641c9f999a63c12ac23b43b64fedfce4e05b863de568 lib/request/pkihandler.py b90feeb16e89a844427df42373b0139eb6f6cf3c48ccec32b3e3a3f540c2451e lib/request/rangehandler.py @@ -237,11 +237,11 @@ f522436fbd14bdab090a1d305fcac0361800cb8e36c8cbcb47933298376a71e0 lib/takeover/r 3df9839fb92a81d46b6194d7adacb43f391efb78b071783c132e8d596ecbfaf1 lib/techniques/dns/test.py 2934514a60cbcd48675053a73f785b4c7bfe606b51c34ae81a86818362ec4672 lib/techniques/dns/use.py 1966ca704961fb987ab757f0a4afddbf841d1a880631b701487c75cef63d60c3 lib/techniques/error/__init__.py -ee63b978154b0cb9a385fe51926ef6dc6f425b07f62b0d17208e82b4ac020f5c lib/techniques/error/use.py +5bbef46c16e34fd80e3f9f0e9aa255ce2e39be0d0e57479e25890b041c7efc7d lib/techniques/error/use.py 1966ca704961fb987ab757f0a4afddbf841d1a880631b701487c75cef63d60c3 lib/techniques/__init__.py 1966ca704961fb987ab757f0a4afddbf841d1a880631b701487c75cef63d60c3 lib/techniques/union/__init__.py 30cae858e2a5a75b40854399f65ad074e6bb808d56d5ee66b94d4002dc6e101b lib/techniques/union/test.py -5b49f5bca4e35362fa7d83896e0769fdb01ad152f30059aafd8ce0f093400a3f lib/techniques/union/use.py +0a9d884d95734986a628e5846ed85c985a96534fb0c56f9d7042a89377801bc2 lib/techniques/union/use.py aeefb42ea0c68f72744bc1bfd7194ec1bc06480d8a7e23f4b8d3d23fbba2b014 lib/utils/api.py 442555ab85277aff7c9e0cf465ea5b0d28395c326f68363449b2d3941f4b6de2 lib/utils/brute.py da5bcbcda3f667582adf5db8c1b5d511b469ac61b55d387cec66de35720ed718 lib/utils/crawler.py @@ -255,7 +255,7 @@ a94958be0ec3e9d28d8171813a6a90655a9ad7e6aa33c661e8d8ebbfcf208dbb lib/utils/deps 1966ca704961fb987ab757f0a4afddbf841d1a880631b701487c75cef63d60c3 lib/utils/__init__.py e7d31de0e268c129ee11c590eb618f73a85e1022c08b8ed1f77753043c949214 lib/utils/pivotdumptable.py c1dfc3bed0fed9b181f612d1d747955dd2b506dbe99bc9fd481495602371473a lib/utils/progress.py -0aeb890fb6b0783f25df7c1ba7c9d0098325b4f7a677ff0151e411be24760f04 lib/utils/prove.py +c442e9ef8324fd6fdf7bc334d765f0a6ce4037397eb3d79d59b5ce3e9a043855 lib/utils/prove.py 2cd84db16edef8c9948e197a51d870cf1c338f4a89037b4d422de990f4a45237 lib/utils/purge.py f635872093a12cd63a72d77adf88e8f8cd4084a5cc64384f12966cd75a499bdf lib/utils/safe2bin.py de4be7e291db0962cd59f9c04b3f7259f846e315df1fd9b323954f89fae0b2db lib/utils/search.py @@ -400,7 +400,7 @@ ba04af3683b9a6e29e8fa6b3bf436a57e59435cebb042414f2df82018d91599e plugins/dbms/m 38ade085f9f1b227eda8c89f78e3ce869e8f430c98bef0cc7cbd2c7dcd60c24e plugins/dbms/mssqlserver/fingerprint.py 1ecde09e80d7b709a710281f4983a6831bc02ca3458ae0b97b28446d6db241b4 plugins/dbms/mssqlserver/__init__.py a89074020253365b6c95a4fa53e41fb0dc16f26a209b31f28e65910f26b81d21 plugins/dbms/mssqlserver/syntax.py -57f263084438e9b2ec2e62909fc51871e9eefb1a9156bbe87908592c5274b639 plugins/dbms/mssqlserver/takeover.py +099f17ba54181e0dc4da721db6a2ef52f6b8e57adeaf69248500754f4ecf398d plugins/dbms/mssqlserver/takeover.py 275ffb2a63c179a5b1673866fcd4020d7f30a68e6d7736e7e21094e2a3234578 plugins/dbms/mysql/connector.py 51590c30177adf8c435e4d6d4be070f6708d81793f70577d9317daa4ef2485ba plugins/dbms/mysql/enumeration.py 5114ca85e5aac6eaebf2ca2cf6b944250329d2d5c36a36015ac34599c9437838 plugins/dbms/mysql/filesystem.py diff --git a/data/xml/payloads/error_based.xml b/data/xml/payloads/error_based.xml index 90bed48b231..1e237c9f649 100644 --- a/data/xml/payloads/error_based.xml +++ b/data/xml/payloads/error_based.xml @@ -880,7 +880,7 @@ 1 1,2,3,9 1 - AND [RANDNUM]=('[DELIMITER_START]'||CAST(([QUERY]) AS String)||'[DELIMITER_STOP]') + AND [RANDNUM]=('[DELIMITER_START]'||CAST(([QUERY]) AS Nullable(String))||'[DELIMITER_STOP]') AND [RANDNUM]=('[DELIMITER_START]'||(CASE WHEN ([RANDNUM]=[RANDNUM]) THEN '1' ELSE '0' END)||'[DELIMITER_STOP]') @@ -899,7 +899,7 @@ 3 1,2,3,9 1 - OR [RANDNUM]=('[DELIMITER_START]'||CAST(([QUERY]) AS String)||'[DELIMITER_STOP]') + OR [RANDNUM]=('[DELIMITER_START]'||CAST(([QUERY]) AS Nullable(String))||'[DELIMITER_STOP]') OR [RANDNUM]=('[DELIMITER_START]'||(CASE WHEN ([RANDNUM]=[RANDNUM]) THEN '1' ELSE '0' END)||'[DELIMITER_STOP]') diff --git a/data/xml/payloads/inline_query.xml b/data/xml/payloads/inline_query.xml index 7269be695c4..5b28c05a80d 100644 --- a/data/xml/payloads/inline_query.xml +++ b/data/xml/payloads/inline_query.xml @@ -141,7 +141,7 @@ 1 1,2,3,8 3 - ('[DELIMITER_START]'||CAST(([QUERY]) AS String)||'[DELIMITER_STOP]') + ('[DELIMITER_START]'||CAST(([QUERY]) AS Nullable(String))||'[DELIMITER_STOP]') ('[DELIMITER_START]'||(CASE WHEN ([RANDNUM]=[RANDNUM]) THEN '1' ELSE '0' END)||'[DELIMITER_STOP]') diff --git a/lib/controller/checks.py b/lib/controller/checks.py index 71d86f054ab..c450aa1d7f3 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -94,6 +94,7 @@ from lib.core.settings import SUPPORTED_DBMS from lib.core.settings import UPPER_RATIO_BOUND from lib.core.settings import URI_HTTP_HEADER +from lib.core.settings import WAF_BLOCK_HTTP_CODES from lib.core.threads import getCurrentThreadData from lib.core.unescaper import unescaper from lib.request.connect import Connect as Request @@ -588,6 +589,18 @@ def genCmpPayload(): break if injectable: + # WAF/IPS block-artifact guard: a TRUE condition (the always-true payload that + # mimics a legitimate request) coming back with a blocked HTTP status (e.g. 403) + # while the FALSE condition passes (2xx) is the WAF answering, not the database. + # A real boolean injection's TRUE condition reproduces the normal page, so this + # status-code asymmetry is the classic false positive - refuse it here. + if not kb.negativeLogic and trueCode in WAF_BLOCK_HTTP_CODES and (falseCode or 0) < 400 and (kb.heuristicCode or 200) < 400: + warnMsg = "%sparameter '%s' TRUE/FALSE responses differ only by a blocked HTTP %d vs %d status, " % ("%s " % paramType if paramType != parameter else "", parameter, trueCode, falseCode) + warnMsg += "which is characteristic of a WAF/IPS block rather than a SQL injection; skipping as a likely false positive" + logger.warning(warnMsg) + injectable = False + continue + if kb.pageStable and not any((conf.string, conf.notString, conf.regexp, conf.code, conf.titles, kb.nullConnection)): if all((falseCode, trueCode)) and falseCode != trueCode and trueCode != kb.heuristicCode: suggestion = conf.code = trueCode diff --git a/lib/controller/controller.py b/lib/controller/controller.py index afe65d9d7f4..bd3418d35a5 100644 --- a/lib/controller/controller.py +++ b/lib/controller/controller.py @@ -817,7 +817,7 @@ def start(): try: action() finally: - if conf.prove: + if conf.proof: from lib.utils.prove import proveExploitation proveExploitation() diff --git a/lib/core/agent.py b/lib/core/agent.py index ea0f206b7a0..67b5d885762 100644 --- a/lib/core/agent.py +++ b/lib/core/agent.py @@ -827,7 +827,7 @@ def concatQuery(self, query, unpack=True): def forgeUnionQuery(self, query, position, count, comment, prefix, suffix, char, where, multipleUnions=None, limited=False, fromTable=None): """ - Take in input an query (pseudo query) string and return its + Take in input a query (pseudo query) string and return its processed UNION ALL SELECT query. Examples: diff --git a/lib/core/bigarray.py b/lib/core/bigarray.py index 7b8bb595bce..0f2b50b142e 100644 --- a/lib/core/bigarray.py +++ b/lib/core/bigarray.py @@ -210,9 +210,9 @@ def _dump(self, chunk): except (OSError, IOError) as ex: errMsg = "exception occurred while storing data " errMsg += "to a temporary file ('%s'). Please " % ex - errMsg += "make sure that there is enough disk space left. If problem persists, " + errMsg += "make sure that there is enough disk space left. If the problem persists, " errMsg += "try to set environment variable 'TEMP' to a location " - errMsg += "writeable by the current user" + errMsg += "writable by the current user" raise SqlmapSystemException(errMsg) def _checkcache(self, index): diff --git a/lib/core/common.py b/lib/core/common.py index 6ec8a9572c5..2d4c7bc51d5 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -2761,7 +2761,7 @@ def getPartRun(alias=True): def longestCommonPrefix(*sequences): """ - Returns longest common prefix occuring in given sequences + Returns longest common prefix occurring in given sequences # Reference: http://boredzo.org/blog/archives/2007-01-06/longest-common-prefix-in-python-2 @@ -3158,7 +3158,7 @@ def getPublicTypeMembers(type_, onlyValues=False): def enumValueToNameLookup(type_, value_): """ - Returns name of a enum member with a given value + Returns name of an enum member with a given value >>> enumValueToNameLookup(SORT_ORDER, 100) 'LAST' diff --git a/lib/core/option.py b/lib/core/option.py index 118ba15aef8..5cb69d297d2 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -608,7 +608,7 @@ def _setMetasploit(): else: warnMsg = "the provided Metasploit Framework path " warnMsg += "'%s' is not valid. The cause could " % conf.msfPath - warnMsg += "be that the path does not exists or that one " + warnMsg += "be that the path does not exist or that one " warnMsg += "or more of the needed Metasploit executables " warnMsg += "within msfcli, msfconsole, msfencode and " warnMsg += "msfpayload do not exist" @@ -1675,9 +1675,9 @@ def _createTemporaryDirectory(): 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, " + warnMsg += "make sure that there is enough disk space left. If the problem persists, " warnMsg += "try to set environment variable 'TEMP' to a location " - warnMsg += "writeable by the current user" + warnMsg += "writable by the current user" logger.warning(warnMsg) if "sqlmap" not in (tempfile.tempdir or "") or conf.tmpDir and tempfile.tempdir == conf.tmpDir: diff --git a/lib/core/optiondict.py b/lib/core/optiondict.py index 1631bd0517e..af5c5ab6b84 100644 --- a/lib/core/optiondict.py +++ b/lib/core/optiondict.py @@ -100,7 +100,7 @@ "prefix": "string", "suffix": "string", "tamper": "string", - "prove": "boolean", + "proof": "boolean", }, "Detection": { diff --git a/lib/core/settings.py b/lib/core/settings.py index faf1d11db58..dfc4af21049 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -20,7 +20,7 @@ from thirdparty import six # sqlmap version (...) -VERSION = "1.10.6.122" +VERSION = "1.10.6.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) @@ -54,6 +54,11 @@ # Timeout used in heuristic check for WAF/IPS protected targets IPS_WAF_CHECK_TIMEOUT = 10 +# HTTP status codes a WAF/IPS typically returns when it blocks a request. Used to reject a boolean +# "injection" whose only TRUE/FALSE difference is the always-true payload being blocked (a status-code +# false positive) rather than the back-end actually answering. +WAF_BLOCK_HTTP_CODES = (403, 406, 429, 451, 501, 503) + # Candidate tamper scripts for automatic WAF-bypass, ordered by empirical WAF-bypass value # (structural token-substitution first, camouflage last; per identYwaf data). The back-end DBMS # is not pre-filtered here: semantics-preservation is verified at runtime by re-running detection diff --git a/lib/core/testing.py b/lib/core/testing.py index 8ab1aaa1aa2..e082dce197b 100644 --- a/lib/core/testing.py +++ b/lib/core/testing.py @@ -61,7 +61,7 @@ def vulnTest(): ("-u --data=\"security_level=3\" -p id --flush-session --technique=B", ("bypassed the WAF/IPS by using tamper script", "Type: boolean-based blind")), # automatic WAF-bypass: SQL-tamper dimension at a stricter signature threshold ("-u --data=\"security_level=4\" -p id --flush-session --technique=B --banner", ("random (non-scanner) User-Agent and browser-like headers to bypass the WAF/IPS", "Type: boolean-based blind", "banner: '3.")), # automatic WAF-bypass against a libinjection-class WAF: tampers cannot help, only the non-scanner User-Agent does ("-u --data=\"security_level=5\" -p id --flush-session --technique=B", ("unable to automatically bypass the WAF/IPS", "does not seem to be injectable")), # automatic WAF-bypass honest bail: a libinjection-class WAF that no User-Agent or tamper can defeat - ("-u -p id --flush-session --prove", ("sqlmap proved exploitation of the following injection point", "Parameter: id (GET)", "Technique: boolean-based blind", "TRUE (5/5)", "repeatably", "Retrieved: back-end DBMS banner '3.")), # --prove: report-grade proof in the injection-point style - forces the boolean technique (so a multi-technique point still proves), and actively reads a value out as the strongest proof + ("-u -p id --flush-session --proof", ("sqlmap proved exploitation of the following injection point", "Parameter: id (GET)", "Technique: boolean-based blind", "TRUE (5/5)", "repeatably", "Retrieved: back-end DBMS banner '3.")), # --proof: report-grade proof in the injection-point style - forces the boolean technique (so a multi-technique point still proves), and actively reads a value out as the strongest proof ("-r --flush-session -v 5 --test-skip=\"heavy\" --save=", ("CloudFlare", "web application technology: Express", "possible DBMS: 'SQLite'", "User-Agent: foobar", "~Type: time-based blind", "saved command line options to the configuration file")), ("-c ", ("CloudFlare", "possible DBMS: 'SQLite'", "User-Agent: foobar", "~Type: time-based blind")), ("-l --flush-session --keep-alive --skip-waf -vvvvv --technique=U --union-from=users --banner --parse-errors", ("banner: '3.", "ORDER BY term out of range", "~xp_cmdshell", "Connection: keep-alive")), diff --git a/lib/parse/cmdline.py b/lib/parse/cmdline.py index f3e99ecf433..1ef639ed6b0 100644 --- a/lib/parse/cmdline.py +++ b/lib/parse/cmdline.py @@ -375,7 +375,7 @@ def cmdLineParser(argv=None): injection.add_argument("--tamper", dest="tamper", help="Use given script(s) for tampering injection data") - injection.add_argument("--prove", dest="prove", action="store_true", + injection.add_argument("--proof", dest="proof", action="store_true", help="Prove exploitation of the detected injection point(s)") # Detection options diff --git a/lib/request/inject.py b/lib/request/inject.py index 2bb641acad8..417b638d786 100644 --- a/lib/request/inject.py +++ b/lib/request/inject.py @@ -162,8 +162,8 @@ def _goInferenceFields(expression, expressionFields, expressionFieldsList, paylo def _goInferenceProxy(expression, fromUser=False, batch=False, unpack=True, charsetType=None, firstChar=None, lastChar=None, dump=False): """ - Retrieve the output of a SQL query characted by character taking - advantage of an blind SQL injection vulnerability on the affected + Retrieve the output of a SQL query character by character taking + advantage of a blind SQL injection vulnerability on the affected parameter through a bisection algorithm. """ @@ -209,9 +209,11 @@ def _goInferenceProxy(expression, fromUser=False, batch=False, unpack=True, char test = False if test: - # Count the number of SQL query entries output - countFirstField = queries[Backend.getIdentifiedDbms()].count.query % expressionFieldsList[0] - countedExpression = expression.replace(expressionFields, countFirstField, 1) + # Count the number of SQL query entries output. NOTE: COUNT(*) (row count), not + # COUNT() - the latter excludes NULLs and would drop NULL-valued rows from + # the dump (e.g. dumping a single column whose value is NULL on some rows). + countField = queries[Backend.getIdentifiedDbms()].count.query % '*' + countedExpression = expression.replace(expressionFields, countField, 1) if " ORDER BY " in countedExpression.upper(): _ = countedExpression.upper().rindex(" ORDER BY ") diff --git a/lib/techniques/error/use.py b/lib/techniques/error/use.py index 2eb38c1c46e..4b5a645c51e 100644 --- a/lib/techniques/error/use.py +++ b/lib/techniques/error/use.py @@ -326,8 +326,10 @@ def errorUse(expression, dump=False): expression, limitCond, topLimit, startLimit, stopLimit = agent.limitCondition(expression, dump) if limitCond: - # Count the number of SQL query entries output - countedExpression = expression.replace(expressionFields, queries[Backend.getIdentifiedDbms()].count.query % ('*' if len(expressionFieldsList) > 1 else expressionFields), 1) + # Count the number of SQL query entries output. NOTE: always COUNT(*) (row count); a single + # field must NOT use COUNT(field) as that excludes NULLs and would drop NULL-valued rows from + # the dump (e.g. a column whose value is NULL on some rows). + countedExpression = expression.replace(expressionFields, queries[Backend.getIdentifiedDbms()].count.query % '*', 1) if " ORDER BY " in countedExpression.upper(): _ = countedExpression.upper().rindex(" ORDER BY ") diff --git a/lib/techniques/union/use.py b/lib/techniques/union/use.py index 59ce5de670c..bb008579feb 100644 --- a/lib/techniques/union/use.py +++ b/lib/techniques/union/use.py @@ -295,8 +295,10 @@ def unionUse(expression, unpack=True, dump=False): expression, limitCond, topLimit, startLimit, stopLimit = agent.limitCondition(expression, dump) if limitCond: - # Count the number of SQL query entries output - countedExpression = expression.replace(expressionFields, queries[Backend.getIdentifiedDbms()].count.query % ('*' if len(expressionFieldsList) > 1 else expressionFields), 1) + # Count the number of SQL query entries output. NOTE: always COUNT(*) (row count); a single + # field must NOT use COUNT(field) as that excludes NULLs and would drop NULL-valued rows from + # the dump (e.g. a column whose value is NULL on some rows). + countedExpression = expression.replace(expressionFields, queries[Backend.getIdentifiedDbms()].count.query % '*', 1) if " ORDER BY " in countedExpression.upper(): _ = countedExpression.upper().rindex(" ORDER BY ") diff --git a/lib/utils/prove.py b/lib/utils/prove.py index f435e6b371b..af11306c930 100644 --- a/lib/utils/prove.py +++ b/lib/utils/prove.py @@ -104,12 +104,16 @@ def _signalArtifacts(expression): return None, None -def _proveBoolean(injection): +def _proveBoolean(injection, signal=None): """ Demonstrates deterministic boolean control, rendered with the distinguishing signal sqlmap already auto-selected (--string / --code / --title), repeated to show it is stable (not a fluke). The signal line quotes the actual distinguishing artifact: the matched string, the two HTTP codes, or the two page titles - so a reader sees exactly what tells TRUE from FALSE. + + When a mutable 'signal' dict is supplied it is filled with the distinguishing artifact (code-based? + and the TRUE/FALSE HTTP codes) so the caller can tell a genuine signal from a blocked-response (WAF) + artifact - a TRUE condition that yields an HTTP 4xx is a block, not a database answer. """ retVal = [] @@ -128,6 +132,10 @@ def _proveBoolean(injection): trueCode, trueTitle = _signalArtifacts("%d=%d" % (n, n)) falseCode, falseTitle = _signalArtifacts("%d=%d" % (n, n + 1)) + if signal is not None: + signal["codeBased"] = bool(injection.conf.code) + signal["trueCode"], signal["falseCode"] = trueCode, falseCode + if injection.conf.string: retVal.append("the response contains %s only when the condition is TRUE" % repr(injection.conf.string).lstrip('u')) elif injection.conf.notString: @@ -277,7 +285,7 @@ def _retrieveProof(): def proveExploitation(): """ Renders a report-grade, best-effort demonstration of exploitation for the confirmed injection point - (option '--prove'), in the same style as sqlmap's injection-point summary so it reads naturally: the + (option '--proof'), in the same style as sqlmap's injection-point summary so it reads naturally: the target URL and the confirmed injection point (parameter / type / title / payload), then the strongest proof first - an actual value read out of the back-end (drilling from the plain read to a more evasive one so a WAF/IPS does not stop it) - backed by a deterministic boolean differential (rendered with the @@ -290,11 +298,12 @@ def proveExploitation(): injection = kb.injection if getattr(kb.injection, "place", None) else kb.injections[0] + signal = {} saved = _activateInjection(injection) try: if PAYLOAD.TECHNIQUE.BOOLEAN in injection.data: stype = PAYLOAD.TECHNIQUE.BOOLEAN - proof = _proveBoolean(injection) + proof = _proveBoolean(injection, signal) elif PAYLOAD.TECHNIQUE.TIME in injection.data or PAYLOAD.TECHNIQUE.STACKED in injection.data: stype = PAYLOAD.TECHNIQUE.TIME if PAYLOAD.TECHNIQUE.TIME in injection.data else PAYLOAD.TECHNIQUE.STACKED proof = _proveTime(injection) @@ -330,16 +339,40 @@ def proveExploitation(): if sdata.payload: payload = urldecode(agent.adjustLateValues(sdata.payload), unsafe="&", spaceplus=(injection.place != PLACE.GET and kb.postSpaceToPlus)) fields.append(_field("Payload", payload)) - if proof: - fields.append(_field("Proof", proof)) - if rungs: + # Reading a value back out of the back-end is the GATE, not a bonus: it is the only thing that + # distinguishes a real injection from a differential that merely correlates with the payload. A + # WAF/IPS that answers blocked payloads with a distinct HTTP status (e.g. 403 when TRUE, 200 when + # FALSE) reproduces a perfect, repeatable boolean differential WITHOUT any SQL ever executing - so + # the differential alone is exactly the signal detection already (mis)read. If nothing could be read + # back, exploitation is NOT proven; say so plainly instead of echoing the detection verdict. + proven = bool(rungs) + + if proven: + if proof: + fields.append(_field("Proof", proof)) for label, text in rungs: fields.append(_field(label, text)) + header = "sqlmap proved exploitation of the following injection point" else: - fields.append(_field("Retrieved", "(no value could be read back; the proof above still confirms exploitation)")) + if proof: + fields.append(_field("Observed", proof)) # the differential is observed, but unconfirmed + suspectWaf = bool(signal.get("codeBased")) and (signal.get("trueCode") or 0) >= 400 + wafInterfering = suspectWaf or kb.droppingRequests or bool(kb.identifiedWafs) + verdict = ["no value could be read back through the injection (tried a random arithmetic product and the DBMS banner)"] + if suspectWaf: + verdict.append("the TRUE/FALSE difference is only an HTTP %s (blocked) response - characteristic of a WAF/IPS, not a database answer" % signal.get("trueCode")) + if wafInterfering: + # behind a WAF, an unconfirmed read-back is ambiguous: a genuine injection whose data-retrieval + # payloads are being blocked looks the same as a pure WAF artifact - so don't assert "false + # positive", point the user at the way to disambiguate instead + verdict.append("a WAF/IPS is interfering: this may be a real injection whose data-retrieval is blocked, or a false positive") + verdict.append("=> exploitation is NOT proven; re-test directly (no WAF) or with --tamper, then re-prove") + else: + verdict.append("=> exploitation is NOT proven; the reported injection is likely a FALSE POSITIVE") + fields.append(_field("Verdict", verdict)) + header = "sqlmap could NOT prove exploitation of the reported injection point" data = "\n".join(fields) - header = "sqlmap proved exploitation of the following injection point" conf.dumper.string(header, data) try: diff --git a/plugins/dbms/mssqlserver/takeover.py b/plugins/dbms/mssqlserver/takeover.py index 53c1b078720..23a10b318a4 100644 --- a/plugins/dbms/mssqlserver/takeover.py +++ b/plugins/dbms/mssqlserver/takeover.py @@ -61,7 +61,7 @@ def spHeapOverflow(self): break if not addrs: - errMsg = "sqlmap can not exploit the stored procedure buffer " + errMsg = "sqlmap cannot exploit the stored procedure buffer " errMsg += "overflow because it does not have a valid return " errMsg += "code for the underlying operating system (Windows " errMsg += "%s Service Pack %d)" % (Backend.getOsVersion(), Backend.getOsServicePack()) From a2d44a7a16060a93e873164e980d0783ef713538 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miroslav=20=C5=A0tampar?= Date: Fri, 19 Jun 2026 00:55:11 +0200 Subject: [PATCH 3/4] Minor patching --- data/txt/sha256sums.txt | 14 +++++------ data/xml/payloads/error_based.xml | 38 +++++++++++++++++++++++++++++ data/xml/queries.xml | 6 ++--- lib/core/enums.py | 1 + lib/core/settings.py | 2 +- plugins/dbms/monetdb/fingerprint.py | 2 +- plugins/dbms/presto/enumeration.py | 11 ++------- plugins/dbms/presto/fingerprint.py | 21 ++++++++++++++++ 8 files changed, 74 insertions(+), 21 deletions(-) diff --git a/data/txt/sha256sums.txt b/data/txt/sha256sums.txt index 18bc616082f..993c89b1dc2 100644 --- a/data/txt/sha256sums.txt +++ b/data/txt/sha256sums.txt @@ -79,12 +79,12 @@ e2febc92f9686eacf17a0054f175917b783cc6638ca570435a5203b03245fc18 data/xml/banne a32fc8796082d2e45cfc969f0b45ad476bf87a8515d67b2fed77c5058df5a0f5 data/xml/boundaries.xml 0baf0fade74d4ad294ee88ef306743da0c6a4631b8d640708809103ef9cf63ed data/xml/errors.xml d0b094a110bccec97d50037cc51445191561c0722ec53bf2cebe1521786e2451 data/xml/payloads/boolean_blind.xml -6ebf0da74b18c95aee4fd4fc2874bda4b3780dc4254806f3968b953fa01bdca1 data/xml/payloads/error_based.xml +2da9159c066c66b47767f66e8c46ed94394f9511940c32e6adf454126197443b data/xml/payloads/error_based.xml 516a2ff314bba3ecf65d0371bf8c2654ad79b09c0737b1fe0f178d7885a9508d data/xml/payloads/inline_query.xml 0648264166455010921df1ec431e4c973809f37ef12cbfea75f95029222eb689 data/xml/payloads/stacked_queries.xml 997556b6170964a64474a2e053abe33cf2cf029fb1acec660d4651cc67a3c7e1 data/xml/payloads/time_blind.xml 40a4878669f318568097719d07dc906a19b8520bc742be3583321fc1e8176089 data/xml/payloads/union_query.xml -38882b6ceb8bca59ce8ed927abe3b8840394c56b3881371c2103e229b8795040 data/xml/queries.xml +f01093d5a1ff6a58653e7058a93e15801d9446f1f2c5de5b5d1054f17dd1ad44 data/xml/queries.xml e043101194219a2e4c8bc352f0d3a04b87e1c28b1bcd6c13f6d5d1c9e260b653 doc/ARCHITECTURE.md 0f5a9c84cb57809be8759f483c7d05f54847115e715521ac0ecf390c0aa68465 doc/AUTHORS ce20a4b452f24a97fde7ec9ed816feee12ac148e1fde5f1722772cc866b12740 doc/CHANGELOG.md @@ -177,7 +177,7 @@ c03dc585f89642cfd81b087ac2723e3e1bb3bfa8c60e6f5fe58ef3b0113ebfe6 lib/core/data. 147823c37596bd6a56d677697781f34b8d1d1671d5a2518fbc9468d623c6d07d lib/core/defaults.py 2f44a1bfe6f18aafe64147b99e69aa93cf438c0e7befe59f4e2aee9065c8b7b6 lib/core/dicts.py 2592b0fd38c272c0b0d49878f4449437eb8ba8ff7536bb39b2ac9a2511010f7c lib/core/dump.py -e4f92e09737ff0dda7ec30e0db1912570e252853b3af9b8f2b9f68ad33cf09fe lib/core/enums.py +6b6514202c6ca2d29069176bccf10492927d83e6ede06c9f4b4fcc6164e61856 lib/core/enums.py 5387168e5dfedd94ae22af7bb255f27d6baaca50b24179c6b98f4f325f5cc7b4 lib/core/exception.py 1966ca704961fb987ab757f0a4afddbf841d1a880631b701487c75cef63d60c3 lib/core/__init__.py 914a13ee21fd610a6153a37cbe50830fcbd1324c7ebc1e7fc206d5e598b0f7ad lib/core/log.py @@ -189,7 +189,7 @@ ccc4a717e887652b1fcce073d9409d9c59a3b28548c703a9e453d15845f90cd7 lib/core/patch 48797d6c34dd9bb8a53f7f3794c85f4288d82a9a1d6be7fcf317d388cb20d4b3 lib/core/replication.py 0b8c38a01bb01f843d94a6c5f2075ee47520d0c4aa799cecea9c3e2c5a4a23a6 lib/core/revision.py 888daba83fd4a34e9503fe21f01fef4cc730e5cde871b1d40e15d4cbc847d56c lib/core/session.py -5edba86522bc49aa6caf80118fc560610e76cc7f35a3c3c09a8052747a3b97ef lib/core/settings.py +25506d477075d1a33849a4db1058e1fb0cc98100e714c1afa0e7e98cad2f2901 lib/core/settings.py cd5a66deee8963ba8e7e9af3dd36eb5e8127d4d68698811c29e789655f507f82 lib/core/shell.py bcb5d8090d5e3e0ef2a586ba09ba80eef0c6d51feb0f611ed25299fbb254f725 lib/core/subprocessng.py 70ea3768f1b3062b22d20644df41c86238157ec80dd43da40545c620714273c6 lib/core/target.py @@ -390,7 +390,7 @@ e9ef99b83542121ac4489526ecb90def4bba9ec62a0dd990bb39d7db387c5ff6 plugins/dbms/m 8a9d30546e3e96295b59bb5e53b352d039f785e0fa8ae19b2073083f1555f45b plugins/dbms/monetdb/connector.py ba04af3683b9a6e29e8fa6b3bf436a57e59435cebb042414f2df82018d91599e plugins/dbms/monetdb/enumeration.py 672dc9b3d291aa4f5d6c4cbe364e92b92e19ee6de86f6d9b9a4dda7d5611b409 plugins/dbms/monetdb/filesystem.py -5fd3a9eb6210c32395e025e327bfeb24fd18f0cc7da554be526c7f2ae9af3f7d plugins/dbms/monetdb/fingerprint.py +7188530754349b765b9842ad8f416766fd7035f131ad6444156ae0de45efc8fe plugins/dbms/monetdb/fingerprint.py 05dc581f0fbed20030200e5c7bd45a971ad4e910c6502ad02cc6c26fd5937003 plugins/dbms/monetdb/__init__.py 78f1ff4b82fd4af50e1fbdb81539862f1c31258cda212b39f4a8501960f1b95e plugins/dbms/monetdb/syntax.py 236fd244f0bbc3976b389429a8176feda6c243267564c2a0eff6fc2458c1b3f9 plugins/dbms/monetdb/takeover.py @@ -423,9 +423,9 @@ bdb13225f822227c32051a296918b3ed423a0644ce0c962db13a0dc0e9636395 plugins/dbms/p 4fce63dd766a35b7273351df2de706c37a0392479578705853b4333c119f2270 plugins/dbms/postgresql/syntax.py d3cb1ebaf594b30cebddd16a8dcf6cf33a3536c3da4caf7e4b9d8c910288eb8d plugins/dbms/postgresql/takeover.py 9a63ef08407c1f4686679343e733bfc124d287ebadf747db5ecbc3abed694462 plugins/dbms/presto/connector.py -23e2fb4fc9c6b84d7503986f311da9c3a9c6eb261433f80be1e854144ebb15b4 plugins/dbms/presto/enumeration.py +1c966d62ce361cf681202be88d839a9bd2677b1444e6998778151ab27647199e plugins/dbms/presto/enumeration.py 874532c0a1a09e2c3d6ea5f4b9e12552ce18ae04a8d13a9f8e099071760f4a73 plugins/dbms/presto/filesystem.py -acd58559efbce9f94683260c45619286b5bb015ff5dbf39b9e8c9b286f34fbe8 plugins/dbms/presto/fingerprint.py +338fbc37ae85f293f07461127dd1465a3ad6bc6bedcdb025ffac35df8bfc8949 plugins/dbms/presto/fingerprint.py 5c104b3ee2e86bf29a8f446d7779470b42d173e87b672c43257289b0d798d2b1 plugins/dbms/presto/__init__.py 859cc5b9be496fe35f2782743f8e573ff9d823de7e99b0d32dbc250c361c653e plugins/dbms/presto/syntax.py 98e28b754352529381b5cffdc701a1c08158d7e7466764310627280d51f744ba plugins/dbms/presto/takeover.py diff --git a/data/xml/payloads/error_based.xml b/data/xml/payloads/error_based.xml index 1e237c9f649..a6ad852cdbd 100644 --- a/data/xml/payloads/error_based.xml +++ b/data/xml/payloads/error_based.xml @@ -911,6 +911,44 @@ + + H2 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (CAST) + 2 + 1 + 1 + 1,2,3,9 + 1 + AND [RANDNUM]=CAST('[DELIMITER_START]'||([QUERY])||'[DELIMITER_STOP]' AS INT) + + AND [RANDNUM]=CAST('[DELIMITER_START]'||(SELECT CASE WHEN ([RANDNUM]=[RANDNUM]) THEN '1' ELSE '0' END)||'[DELIMITER_STOP]' AS INT) + + + [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] + +
+ H2 +
+
+ + + H2 OR error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (CAST) + 2 + 4 + 3 + 1,2,3,9 + 1 + OR [RANDNUM]=CAST('[DELIMITER_START]'||([QUERY])||'[DELIMITER_STOP]' AS INT) + + OR [RANDNUM]=CAST('[DELIMITER_START]'||(SELECT CASE WHEN ([RANDNUM]=[RANDNUM]) THEN '1' ELSE '0' END)||'[DELIMITER_STOP]' AS INT) + + + [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] + +
+ H2 +
+
+ Spanner AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause 2 diff --git a/data/xml/queries.xml b/data/xml/queries.xml index a7f0dd452fb..cc26298eada 100644 --- a/data/xml/queries.xml +++ b/data/xml/queries.xml @@ -1136,9 +1136,9 @@ /> - + - + @@ -1424,7 +1424,7 @@ - + diff --git a/lib/core/enums.py b/lib/core/enums.py index ed3325025da..b96312b9a23 100644 --- a/lib/core/enums.py +++ b/lib/core/enums.py @@ -114,6 +114,7 @@ class FORK(object): DM8 = "DM8" DORIS = "Doris" STARROCKS = "StarRocks" + TRINO = "Trino" class CUSTOM_LOGGING(object): PAYLOAD = 9 diff --git a/lib/core/settings.py b/lib/core/settings.py index dfc4af21049..85bb4610728 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -20,7 +20,7 @@ from thirdparty import six # sqlmap version (...) -VERSION = "1.10.6.123" +VERSION = "1.10.6.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) diff --git a/plugins/dbms/monetdb/fingerprint.py b/plugins/dbms/monetdb/fingerprint.py index 83c065d18b4..e429a9315bd 100644 --- a/plugins/dbms/monetdb/fingerprint.py +++ b/plugins/dbms/monetdb/fingerprint.py @@ -68,7 +68,7 @@ def checkDbms(self): infoMsg = "testing %s" % DBMS.MONETDB logger.info(infoMsg) - result = inject.checkBooleanExpression("isaurl(NULL)=false") + result = inject.checkBooleanExpression("isaurl(NULL) IS NULL") if result: infoMsg = "confirming %s" % DBMS.MONETDB diff --git a/plugins/dbms/presto/enumeration.py b/plugins/dbms/presto/enumeration.py index aad5d4bcad5..5843d9e521a 100644 --- a/plugins/dbms/presto/enumeration.py +++ b/plugins/dbms/presto/enumeration.py @@ -9,15 +9,8 @@ from plugins.generic.enumeration import Enumeration as GenericEnumeration class Enumeration(GenericEnumeration): - def getBanner(self): - warnMsg = "on Presto it is not possible to get the banner" - logger.warning(warnMsg) - - return None - - def getCurrentDb(self): - warnMsg = "on Presto it is not possible to get name of the current database (schema)" - logger.warning(warnMsg) + # NOTE: getBanner()/getCurrentDb() are intentionally NOT overridden - modern Presto/Trino expose + # version() and current_schema (wired in queries.xml), so the generic implementations work. def isDba(self, user=None): warnMsg = "on Presto it is not possible to test if current user is DBA" diff --git a/plugins/dbms/presto/fingerprint.py b/plugins/dbms/presto/fingerprint.py index fdc5b7968a6..4b6cd9e8b39 100644 --- a/plugins/dbms/presto/fingerprint.py +++ b/plugins/dbms/presto/fingerprint.py @@ -7,10 +7,14 @@ from lib.core.common import Backend from lib.core.common import Format +from lib.core.common import hashDBRetrieve +from lib.core.common import hashDBWrite 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 FORK +from lib.core.enums import HASHDB_KEYS from lib.core.session import setDbms from lib.core.settings import PRESTO_ALIASES from lib.request import inject @@ -21,6 +25,18 @@ def __init__(self): GenericFingerprint.__init__(self, DBMS.PRESTO) def getFingerprint(self): + fork = hashDBRetrieve(HASHDB_KEYS.DBMS_FORK) + + if fork is None: + # Trino (the PrestoSQL fork) exposes functions PrestoDB never added (e.g. SOUNDEX), + # so a NULL-based probe on one of them distinguishes the fork from the original. + if inject.checkBooleanExpression("SOUNDEX(NULL) IS NULL"): + fork = FORK.TRINO + else: + fork = "" + + hashDBWrite(HASHDB_KEYS.DBMS_FORK, fork) + value = "" wsOsFp = Format.getOs("web server", kb.headersFp) @@ -37,6 +53,8 @@ def getFingerprint(self): if not conf.extensiveFp: value += DBMS.PRESTO + if fork: + value += " (%s fork)" % fork return value actVer = Format.getDbms() @@ -55,6 +73,9 @@ def getFingerprint(self): if htmlErrorFp: value += "\n%shtml error message fingerprint: %s" % (blank, htmlErrorFp) + if fork: + value += "\n%sfork fingerprint: %s" % (blank, fork) + return value def checkDbms(self): From d5d6fac58d785281cef9482b29d2ae4bf777cd4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miroslav=20=C5=A0tampar?= Date: Fri, 19 Jun 2026 09:45:25 +0200 Subject: [PATCH 4/4] Minor patch --- data/txt/sha256sums.txt | 4 ++-- lib/core/settings.py | 2 +- plugins/generic/databases.py | 5 +++++ 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/data/txt/sha256sums.txt b/data/txt/sha256sums.txt index 993c89b1dc2..031f8173272 100644 --- a/data/txt/sha256sums.txt +++ b/data/txt/sha256sums.txt @@ -189,7 +189,7 @@ ccc4a717e887652b1fcce073d9409d9c59a3b28548c703a9e453d15845f90cd7 lib/core/patch 48797d6c34dd9bb8a53f7f3794c85f4288d82a9a1d6be7fcf317d388cb20d4b3 lib/core/replication.py 0b8c38a01bb01f843d94a6c5f2075ee47520d0c4aa799cecea9c3e2c5a4a23a6 lib/core/revision.py 888daba83fd4a34e9503fe21f01fef4cc730e5cde871b1d40e15d4cbc847d56c lib/core/session.py -25506d477075d1a33849a4db1058e1fb0cc98100e714c1afa0e7e98cad2f2901 lib/core/settings.py +6931bc15cb35d138913beb2ecca2432821b7d128e95f0db58ab99e32e24259dc lib/core/settings.py cd5a66deee8963ba8e7e9af3dd36eb5e8127d4d68698811c29e789655f507f82 lib/core/shell.py bcb5d8090d5e3e0ef2a586ba09ba80eef0c6d51feb0f611ed25299fbb254f725 lib/core/subprocessng.py 70ea3768f1b3062b22d20644df41c86238157ec80dd43da40545c620714273c6 lib/core/target.py @@ -480,7 +480,7 @@ e2e20e4707abe9ed8b6208837332d2daa4eaca282f847412063f2484dcca8fbd plugins/dbms/v 2b2dad6ba1d344215cad11b629546eb9f259d7c996c202edf3de5ab22418787e plugins/dbms/virtuoso/takeover.py 51c44048e4b335b306f8ed1323fd78ad6935a8c0d6e9d6efe195a9a5a24e46dc plugins/generic/connector.py a967f4ebd101c68a5dcc10ff18c882a8f44a5c3bf06613d951a739ecc3abb9b3 plugins/generic/custom.py -6d037861acbbabec529e10c50840820ca7b876c29c69310a571b519c3f3b72fa plugins/generic/databases.py +020f0f828121fe03704fdef241364ffd33c5dce1e5d04028bc7375b4563c3696 plugins/generic/databases.py 36b7319ac00f8fe1a33496364a76ff165ea2e66db0150f5366a45135366369ca plugins/generic/entries.py d2de7fc135cf0db3eb4ac4a509c23ebec5250a5d8043face7f8c546a09f301b5 plugins/generic/enumeration.py a02ac4ebc1cc488a2aa5ae07e6d0c3d5064e99ded7fd529dfa073735692f11df plugins/generic/filesystem.py diff --git a/lib/core/settings.py b/lib/core/settings.py index 85bb4610728..12a42e3ee7e 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -20,7 +20,7 @@ from thirdparty import six # sqlmap version (...) -VERSION = "1.10.6.124" +VERSION = "1.10.6.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) diff --git a/plugins/generic/databases.py b/plugins/generic/databases.py index bae73904c89..d3eef7ea37f 100644 --- a/plugins/generic/databases.py +++ b/plugins/generic/databases.py @@ -1051,6 +1051,11 @@ def getStatements(self): rootQuery = queries[Backend.getIdentifiedDbms()].statements + if "inband" not in rootQuery and "blind" not in rootQuery: + warnMsg = "on %s it is not possible to enumerate the SQL statements" % Backend.getIdentifiedDbms() + logger.warning(warnMsg) + return kb.data.cachedStatements + if any(isTechniqueAvailable(_) for _ in (PAYLOAD.TECHNIQUE.UNION, PAYLOAD.TECHNIQUE.ERROR, PAYLOAD.TECHNIQUE.QUERY)) or conf.direct: if Backend.isDbms(DBMS.MYSQL) and Backend.isFork(FORK.DRIZZLE): query = rootQuery.inband.query2