Skip to content

Commit 516f559

Browse files
committed
Implements #4973
1 parent b208262 commit 516f559

3 files changed

Lines changed: 79 additions & 4 deletions

File tree

‎lib/core/settings.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
from thirdparty import six
2121

2222
# sqlmap version (<major>.<minor>.<month>.<monthly commit>)
23-
VERSION = "1.10.10.5"
23+
VERSION = "1.10.10.6"
2424
TYPE = "dev" if VERSION.count('.') > 2 and VERSION.split('.')[-1] != '0' else "stable"
2525
TYPE_COLORS = {"dev": 33, "stable": 90, "pip": 34}
2626
VERSION_STRING = "sqlmap/%s#%s" % ('.'.join(VERSION.split('.')[:-1]) if VERSION.count('.') > 2 and VERSION.split('.')[-1] == '0' else VERSION, TYPE)

‎lib/techniques/dns/use.py‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,10 @@ def dnsUse(payload, expression):
7474

7575
if Backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.PGSQL):
7676
query = agent.prefixQuery("; %s" % expressionUnescaped)
77-
query = "%s%s" % (query, queries[Backend.getIdentifiedDbms()].comment.query)
77+
if conf.suffix is not None:
78+
query = agent.suffixQuery(query, comment="", suffix=conf.suffix)
79+
else:
80+
query = "%s%s" % (query, queries[Backend.getIdentifiedDbms()].comment.query)
7881
forgedPayload = agent.payload(newValue=query)
7982
else:
8083
forgedPayload = safeStringFormat(payload, (expressionUnescaped, randomInt(1), randomInt(3)))

‎tests/test_dns_engine.py‎

Lines changed: 74 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,13 @@
4343

4444
from lib.core.agent import agent
4545
from lib.core.common import Backend
46-
from lib.core.data import conf, kb
46+
from lib.core.data import conf, kb, queries
47+
from lib.core.datatype import AttribDict
48+
from lib.core.datatype import InjectionDict
4749
from lib.core.threads import getCurrentThreadData
4850
from lib.core.enums import DBMS
51+
from lib.core.enums import PAYLOAD
52+
from lib.core.enums import PLACE
4953
from lib.core.exception import SqlmapNotVulnerableException
5054
from lib.core.settings import DNS_BOUNDARIES_ALPHABET
5155
from lib.core.settings import MAX_DNS_LABEL
@@ -80,7 +84,8 @@ def close(self):
8084
except socket.error:
8185
pass
8286

83-
_CONF = {"dnsDomain": "exfil.test", "hexConvert": False, "api": False, "verbose": 0, "forceDns": False}
87+
_CONF = {"dnsDomain": "exfil.test", "hexConvert": False, "api": False, "verbose": 0, "forceDns": False,
88+
"suffix": None, "direct": False}
8489
_KB = {"dnsTest": True, "dnsMode": False, "bruteMode": False, "safeCharEncode": False}
8590

8691

@@ -252,6 +257,73 @@ class TestDnsExfilEngineMssql(TestDnsExfilEngine):
252257
DBMS_NAME = "Microsoft SQL Server"
253258

254259

260+
class TestDnsStackedSuffix(_DnsCase):
261+
def setUp(self):
262+
_DnsCase.setUp(self)
263+
restore_attrs(self._s_agent)
264+
self._saved_target = {k: conf.get(k) for k in ("parameters", "paramDict", "prefix")}
265+
self._saved_injection = {k: kb.get(k) for k in ("injection", "technique", "forceWhere")}
266+
conf.parameters = {PLACE.GET: "id=1"}
267+
conf.paramDict = {PLACE.GET: {"id": "1"}}
268+
conf.prefix = " /* custom prefix */"
269+
kb.technique = PAYLOAD.TECHNIQUE.BOOLEAN
270+
kb.forceWhere = None
271+
kb.injection = InjectionDict()
272+
kb.injection.place = PLACE.GET
273+
kb.injection.parameter = "id"
274+
kb.injection.prefix = conf.prefix
275+
kb.injection.suffix = "; SELECT 999-- inferred suffix"
276+
kb.injection.clause = [1]
277+
kb.injection.data[PAYLOAD.TECHNIQUE.BOOLEAN] = AttribDict({"where": PAYLOAD.WHERE.ORIGINAL, "comment": "-- inferred comment"})
278+
279+
def tearDown(self):
280+
for k, v in self._saved_target.items():
281+
conf[k] = v
282+
for k, v in self._saved_injection.items():
283+
kb[k] = v
284+
_DnsCase.tearDown(self)
285+
286+
def _extract_with_suffix(self, suffix):
287+
secret = "The quick brown fox jumps over the lazy dog 0123456789"
288+
conf.suffix = suffix
289+
290+
for dbms in (DBMS.MSSQL, DBMS.PGSQL):
291+
set_dbms(dbms)
292+
self._install_oracle(secret)
293+
oracle = Connect.queryPage
294+
payloads = []
295+
296+
def require_suffix(payload, **kwargs):
297+
sql = agent.removePayloadDelimiters(payload)
298+
payloads.append(sql)
299+
# Model an endpoint that accepts the DNS payload only with its required ending.
300+
if suffix and not sql.endswith(suffix):
301+
return None
302+
return oracle(payload, **kwargs)
303+
304+
Connect.queryPage = staticmethod(require_suffix)
305+
self.assertEqual(dnsmod.dnsUse("unused", "SELECT secret"), secret, dbms)
306+
self.assertGreater(len(payloads), 1, dbms)
307+
for payload in payloads:
308+
self.assertTrue(payload.startswith("id=1%s; " % conf.prefix), dbms)
309+
self.assertNotIn("-- inferred", payload)
310+
if suffix is None:
311+
self.assertTrue(payload.endswith(queries[dbms].comment.query), dbms)
312+
elif suffix:
313+
self.assertTrue(payload.endswith(suffix), dbms)
314+
else:
315+
self.assertFalse(payload.endswith(queries[dbms].comment.query), dbms)
316+
317+
def test_explicit_suffix_survives_every_chunk(self):
318+
self._extract_with_suffix("; SELECT 4973-- required suffix")
319+
320+
def test_default_comment_without_explicit_suffix(self):
321+
self._extract_with_suffix(None)
322+
323+
def test_explicit_empty_suffix_does_not_add_comment(self):
324+
self._extract_with_suffix("")
325+
326+
255327
class TestDnsLabelInvariant(_DnsCase):
256328
"""The exfil chunk is hex-encoded into ONE DNS label, so the label dnsUse emits must never
257329
exceed the 63-octet DNS label limit - otherwise the query carries an invalid (over-long) label

0 commit comments

Comments
 (0)