mirror of
https://github.com/sqlmapproject/sqlmap.git
synced 2026-09-27 13:34:33 +00:00
91 lines
3.8 KiB
Python
91 lines
3.8 KiB
Python
#!/usr/bin/env python
|
|
|
|
"""
|
|
Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org)
|
|
See the file 'LICENSE' for copying permission
|
|
|
|
Invariants of the random markers in kb.chars (lib/core/option.py).
|
|
|
|
These markers are drawn at random once per run, so a collision between two of them is a
|
|
heisenbug: it corrupts extraction only in the fraction of runs that happen to draw the same
|
|
value twice. Both collisions below were observed and are pinned here.
|
|
|
|
start/stop wrap the delimited output. Equal values make the output ambiguous
|
|
(a two-row result then carries four identical markers instead of
|
|
two pairs), so the parsing returns garbage or nothing at all.
|
|
at/space/dollar/hash_ stand in for '@', ' ', '$' and '#' inside a single chained REPLACE()
|
|
(see the Oracle XMLType vectors in error_based.xml). Two equal
|
|
markers make _errorReplaceChars() restore whichever character it
|
|
tries first, so e.g. an '@' in an e-mail column comes back as ' '.
|
|
|
|
The loop count is what matters: the smaller alphabet collides once every ~512 draws and the
|
|
single-letter one every ~5, so a single sample proves nothing.
|
|
|
|
stdlib unittest only (no pytest / no pip); works on Python 2.7 and 3.x.
|
|
"""
|
|
|
|
import os
|
|
import sys
|
|
import unittest
|
|
|
|
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
|
from _testutils import bootstrap
|
|
bootstrap()
|
|
|
|
from lib.core.data import kb
|
|
from lib.core.option import _setKnowledgeBaseAttributes
|
|
|
|
ROUNDS = 3000
|
|
|
|
MARKERS = ("start", "stop", "at", "space", "dollar", "hash_")
|
|
|
|
|
|
class KbCharsDistinctTest(unittest.TestCase):
|
|
"""Re-draws the markers many times. kb is global, so it is snapshotted and put back -
|
|
leaking a fresh kb into the rest of the suite is how this bug got noticed in the first place."""
|
|
|
|
def setUp(self):
|
|
self._saved = dict(kb)
|
|
|
|
def tearDown(self):
|
|
kb.clear()
|
|
kb.update(self._saved)
|
|
|
|
def test_markers_never_collide(self):
|
|
for i in range(ROUNDS):
|
|
_setKnowledgeBaseAttributes()
|
|
drawn = [getattr(kb.chars, _) for _ in MARKERS]
|
|
self.assertEqual(len(set(drawn)), len(MARKERS),
|
|
msg="colliding kb.chars markers on round %d: %s" % (i, dict(zip(MARKERS, drawn))))
|
|
|
|
def test_markers_never_contain_each_other(self):
|
|
# whole-string distinctness is not enough: the boundary character wrapping every marker used
|
|
# to be drawn for the inner letters as well, so a start marker could render as 'qzqxq', which
|
|
# carries the perfectly legal replacement marker 'qzq' (and 'qxq') inside it
|
|
for i in range(ROUNDS):
|
|
_setKnowledgeBaseAttributes()
|
|
drawn = [getattr(kb.chars, _) for _ in MARKERS]
|
|
for one in drawn:
|
|
for other in drawn:
|
|
if one is not other:
|
|
self.assertNotIn(other, one,
|
|
msg="kb.chars marker %r contains %r on round %d" % (one, other, i))
|
|
|
|
def test_markers_keep_their_shape(self):
|
|
# the fix must not change the on-the-wire length of a payload
|
|
for _ in range(ROUNDS // 100):
|
|
_setKnowledgeBaseAttributes()
|
|
self.assertEqual(len(kb.chars.start), 5)
|
|
self.assertEqual(len(kb.chars.stop), 5)
|
|
for name in ("at", "space", "dollar", "hash_"):
|
|
self.assertEqual(len(getattr(kb.chars, name)), 3, msg=name)
|
|
self.assertEqual(len(kb.chars.delimiter), 6)
|
|
|
|
def test_kb_is_restored(self):
|
|
# guards the snapshot above, so a later failure elsewhere is never blamed on this file
|
|
_setKnowledgeBaseAttributes()
|
|
self.assertNotEqual(dict(kb).get("chars"), self._saved.get("chars"))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main(verbosity=2)
|