Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 09ca578

Browse files
committed
Major bug fix so that the users' privileges enumeration now works properly also on both MySQL < 5.0 and MySQL >= 5.0 also if the user has provided one or more users with -U option;
1 parent 91a4724 commit 09ca578

8 files changed

Lines changed: 159 additions & 117 deletions

File tree

doc/ChangeLog

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ sqlmap (0.6.2-1) stable; urgency=low
33
* Major bug fix to correctly dump tables entries when --stop is not
44
specified;
55
* Major bug fix so that the users' privileges enumeration now works
6-
properly also on MySQL < 5.0;
6+
properly also on both MySQL < 5.0 and MySQL >= 5.0;
77
* Major bug fix when the request is POST to also send the url parameters
88
if any have been provided;
99
* Major improvement to correctly enumerate tables, columns and dump

lib/core/unescaper.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,8 @@ def setUnescape(self, unescapeFunction):
3333
self.__unescaper = unescapeFunction
3434

3535

36-
def unescape(self, expression):
37-
return self.__unescaper(expression)
36+
def unescape(self, expression, quote=True):
37+
return self.__unescaper(expression, quote=quote)
3838

3939

4040
unescaper = Unescaper()

plugins/dbms/mssqlserver.py

Lines changed: 27 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -67,30 +67,33 @@ def __init__(self):
6767

6868

6969
@staticmethod
70-
def unescape(expression):
71-
while True:
72-
index = expression.find("'")
73-
if index == -1:
74-
break
75-
76-
firstIndex = index + 1
77-
index = expression[firstIndex:].find("'")
78-
79-
if index == -1:
80-
raise sqlmapSyntaxException, "Unenclosed ' in '%s'" % expression
81-
82-
lastIndex = firstIndex + index
83-
old = "'%s'" % expression[firstIndex:lastIndex]
84-
#unescaped = ""
85-
unescaped = "("
86-
87-
for i in range(firstIndex, lastIndex):
88-
unescaped += "CHAR(%d)" % (ord(expression[i]))
89-
if i < lastIndex - 1:
90-
unescaped += "+"
91-
92-
unescaped += ")"
93-
expression = expression.replace(old, unescaped)
70+
def unescape(expression, quote=True):
71+
if quote:
72+
while True:
73+
index = expression.find("'")
74+
if index == -1:
75+
break
76+
77+
firstIndex = index + 1
78+
index = expression[firstIndex:].find("'")
79+
80+
if index == -1:
81+
raise sqlmapSyntaxException, "Unenclosed ' in '%s'" % expression
82+
83+
lastIndex = firstIndex + index
84+
old = "'%s'" % expression[firstIndex:lastIndex]
85+
#unescaped = "("
86+
unescaped = ""
87+
88+
for i in range(firstIndex, lastIndex):
89+
unescaped += "CHAR(%d)" % (ord(expression[i]))
90+
if i < lastIndex - 1:
91+
unescaped += "+"
92+
93+
#unescaped += ")"
94+
expression = expression.replace(old, unescaped)
95+
else:
96+
expression = "+".join("CHAR(%d)" % ord(c) for c in expression)
9497

9598
return expression
9699

plugins/dbms/mysql.py

Lines changed: 24 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -66,28 +66,35 @@ def __init__(self):
6666

6767

6868
@staticmethod
69-
def unescape(expression):
70-
while True:
71-
index = expression.find("'")
72-
if index == -1:
73-
break
69+
def unescape(expression, quote=True):
70+
if quote:
71+
while True:
72+
index = expression.find("'")
73+
if index == -1:
74+
break
7475

75-
firstIndex = index + 1
76-
index = expression[firstIndex:].find("'")
76+
firstIndex = index + 1
77+
index = expression[firstIndex:].find("'")
7778

78-
if index == -1:
79-
raise sqlmapSyntaxException, "Unenclosed ' in '%s'" % expression
79+
if index == -1:
80+
raise sqlmapSyntaxException, "Unenclosed ' in '%s'" % expression
8081

81-
lastIndex = firstIndex + index
82-
old = "'%s'" % expression[firstIndex:lastIndex]
83-
unescaped = ""
82+
lastIndex = firstIndex + index
83+
old = "'%s'" % expression[firstIndex:lastIndex]
84+
unescaped = ""
8485

85-
for i in range(firstIndex, lastIndex):
86-
unescaped += "%d" % (ord(expression[i]))
87-
if i < lastIndex - 1:
88-
unescaped += ","
86+
for i in range(firstIndex, lastIndex):
87+
unescaped += "%d" % (ord(expression[i]))
88+
if i < lastIndex - 1:
89+
unescaped += ","
90+
91+
expression = expression.replace(old, "CHAR(%s)" % unescaped)
92+
else:
93+
unescaped = "CHAR("
94+
unescaped += ",".join("%d" % ord(c) for c in expression)
95+
unescaped += ")"
8996

90-
expression = expression.replace(old, "CHAR(%s)" % unescaped)
97+
expression = unescaped
9198

9299
return expression
93100

plugins/dbms/oracle.py

Lines changed: 27 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -59,30 +59,33 @@ def __init__(self):
5959

6060

6161
@staticmethod
62-
def unescape(expression):
63-
while True:
64-
index = expression.find("'")
65-
if index == -1:
66-
break
67-
68-
firstIndex = index + 1
69-
index = expression[firstIndex:].find("'")
70-
71-
if index == -1:
72-
raise sqlmapSyntaxException, "Unenclosed ' in '%s'" % expression
73-
74-
lastIndex = firstIndex + index
75-
old = "'%s'" % expression[firstIndex:lastIndex]
76-
#unescaped = ""
77-
unescaped = "("
78-
79-
for i in range(firstIndex, lastIndex):
80-
unescaped += "CHR(%d)" % (ord(expression[i]))
81-
if i < lastIndex - 1:
82-
unescaped += "||"
83-
84-
unescaped += ")"
85-
expression = expression.replace(old, unescaped)
62+
def unescape(expression, quote=True):
63+
if quote:
64+
while True:
65+
index = expression.find("'")
66+
if index == -1:
67+
break
68+
69+
firstIndex = index + 1
70+
index = expression[firstIndex:].find("'")
71+
72+
if index == -1:
73+
raise sqlmapSyntaxException, "Unenclosed ' in '%s'" % expression
74+
75+
lastIndex = firstIndex + index
76+
old = "'%s'" % expression[firstIndex:lastIndex]
77+
#unescaped = "("
78+
unescaped = ""
79+
80+
for i in range(firstIndex, lastIndex):
81+
unescaped += "CHR(%d)" % (ord(expression[i]))
82+
if i < lastIndex - 1:
83+
unescaped += "||"
84+
85+
#unescaped += ")"
86+
expression = expression.replace(old, unescaped)
87+
else:
88+
expression = "||".join("CHR(%d)" % ord(c) for c in expression)
8689

8790
return expression
8891

plugins/dbms/postgresql.py

Lines changed: 27 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -59,29 +59,33 @@ def __init__(self):
5959

6060

6161
@staticmethod
62-
def unescape(expression):
63-
while True:
64-
index = expression.find("'")
65-
if index == -1:
66-
break
67-
68-
firstIndex = index + 1
69-
index = expression[firstIndex:].find("'")
70-
71-
if index == -1:
72-
raise sqlmapSyntaxException, "Unenclosed ' in '%s'" % expression
73-
74-
lastIndex = firstIndex + index
75-
old = "'%s'" % expression[firstIndex:lastIndex]
76-
unescaped = "("
77-
78-
for i in range(firstIndex, lastIndex):
79-
unescaped += "CHR(%d)" % (ord(expression[i]))
80-
if i < lastIndex - 1:
81-
unescaped += "||"
82-
83-
unescaped += ")"
84-
expression = expression.replace(old, unescaped)
62+
def unescape(expression, quote=True):
63+
if quote:
64+
while True:
65+
index = expression.find("'")
66+
if index == -1:
67+
break
68+
69+
firstIndex = index + 1
70+
index = expression[firstIndex:].find("'")
71+
72+
if index == -1:
73+
raise sqlmapSyntaxException, "Unenclosed ' in '%s'" % expression
74+
75+
lastIndex = firstIndex + index
76+
old = "'%s'" % expression[firstIndex:lastIndex]
77+
#unescaped = "("
78+
unescaped = ""
79+
80+
for i in range(firstIndex, lastIndex):
81+
unescaped += "CHR(%d)" % (ord(expression[i]))
82+
if i < lastIndex - 1:
83+
unescaped += "||"
84+
85+
#unescaped += ")"
86+
expression = expression.replace(old, unescaped)
87+
else:
88+
expression = "||".join("CHR(%d)" % ord(c) for c in expression)
8589

8690
return expression
8791

plugins/generic/enumeration.py

Lines changed: 48 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
from lib.core.exception import sqlmapUndefinedMethod
4141
from lib.core.exception import sqlmapUnsupportedFeatureException
4242
from lib.core.shell import autoCompletion
43+
from lib.core.unescaper import unescaper
4344
from lib.request import inject
4445
from lib.request.connect import Connect as Request
4546

@@ -346,19 +347,19 @@ def getPrivileges(self):
346347
if "," in conf.user:
347348
users = conf.user.split(",")
348349
query += " WHERE "
349-
# NOTE: we need this here only for MySQL 5.0 because
350-
# of a known issue explained in queries.xml
350+
# NOTE: I assume that the user provided is not in
351+
# MySQL >= 5.0 syntax 'user'@'host'
351352
if kb.dbms == "MySQL" and self.has_information_schema:
352-
likeUser = "%" + conf.user + "%"
353+
queryUser = "%" + conf.user + "%"
353354
query += " OR ".join("%s LIKE '%s'" % (condition, "%" + user + "%") for user in users)
354355
else:
355356
query += " OR ".join("%s = '%s'" % (condition, user) for user in users)
356357
else:
357-
# NOTE: we need this here only for MySQL 5.0 because
358-
# of a known issue explained in queries.xml
358+
# NOTE: I assume that the user provided is not in
359+
# MySQL >= 5.0 syntax 'user'@'host'
359360
if kb.dbms == "MySQL" and self.has_information_schema:
360-
likeUser = "%" + conf.user + "%"
361-
query += " WHERE %s LIKE '%s'" % (condition, likeUser)
361+
queryUser = "%" + conf.user + "%"
362+
query += " WHERE %s LIKE '%s'" % (condition, queryUser)
362363
else:
363364
query += " WHERE %s = '%s'" % (condition, conf.user)
364365

@@ -406,11 +407,25 @@ def getPrivileges(self):
406407
self.cachedUsersPrivileges[user] = list(privileges)
407408

408409
if not self.cachedUsersPrivileges:
410+
conditionChar = "="
411+
409412
if conf.user:
410-
if "," in conf.user:
413+
if kb.dbms == "MySQL" and self.has_information_schema:
414+
conditionChar = " LIKE "
415+
416+
if "," in conf.user:
417+
users = set()
418+
for user in conf.user.split(","):
419+
users.add("%" + user + "%")
420+
else:
421+
users = [ "%" + conf.user + "%" ]
422+
423+
elif "," in conf.user:
411424
users = conf.user.split(",")
425+
412426
else:
413-
users = [conf.user]
427+
users = [ conf.user ]
428+
414429
else:
415430
if not len(self.cachedUsers):
416431
users = self.getUsers()
@@ -420,11 +435,10 @@ def getPrivileges(self):
420435
retrievedUsers = set()
421436

422437
for user in users:
423-
if kb.dbms == "MySQL":
424-
parsedUser = re.search("\047(.*?)\047@'", user)
438+
unescapedUser = None
425439

426-
if parsedUser:
427-
user = parsedUser.groups()[0].replace("'", "")
440+
if kb.dbms == "MySQL" and self.has_information_schema:
441+
unescapedUser = unescaper.unescape(user, quote=False)
428442

429443
if user in retrievedUsers:
430444
continue
@@ -433,38 +447,42 @@ def getPrivileges(self):
433447
logMsg += "for user '%s'" % user
434448
logger.info(logMsg)
435449

436-
if kb.dbms == "MySQL" and self.has_information_schema:
437-
likeUser = "%" + user + "%"
450+
if unescapedUser:
451+
queryUser = unescapedUser
438452
else:
439-
likeUser = user
453+
queryUser = user
440454

441455
if kb.dbms == "MySQL" and not self.has_information_schema:
442-
query = rootQuery["blind"]["count2"] % likeUser
456+
query = rootQuery["blind"]["count2"] % queryUser
457+
elif kb.dbms == "MySQL" and self.has_information_schema:
458+
query = rootQuery["blind"]["count"] % (conditionChar, queryUser)
443459
else:
444-
query = rootQuery["blind"]["count"] % likeUser
460+
query = rootQuery["blind"]["count"] % queryUser
445461
count = inject.getValue(query, inband=False)
446462

447463
if not len(count) or count == "0":
448464
warnMsg = "unable to retrieve the number of "
449-
warnMsg += "privileges for user '%s'" % likeUser
465+
warnMsg += "privileges for user '%s'" % user
450466
logger.warn(warnMsg)
451467
continue
452468

453-
logMsg = "fetching privileges for user '%s'" % likeUser
469+
logMsg = "fetching privileges for user '%s'" % user
454470
logger.info(logMsg)
455471

456472
privileges = set()
457473
indexRange = getRange(count)
458474

459475
for index in indexRange:
460476
if kb.dbms == "MySQL" and not self.has_information_schema:
461-
query = rootQuery["blind"]["query2"] % (likeUser, index)
477+
query = rootQuery["blind"]["query2"] % (queryUser, index)
478+
elif kb.dbms == "MySQL" and self.has_information_schema:
479+
query = rootQuery["blind"]["query"] % (conditionChar, queryUser, index)
462480
else:
463-
query = rootQuery["blind"]["query"] % (likeUser, index)
481+
query = rootQuery["blind"]["query"] % (queryUser, index)
464482
privilege = inject.getValue(query, inband=False)
465483

466-
# In PostgreSQL we return 1 if the privilege
467-
# if True, otherwise 0
484+
# In PostgreSQL we get 1 if the privilege is True,
485+
# 0 otherwise
468486
if kb.dbms == "PostgreSQL" and ", " in privilege:
469487
privilege = privilege.replace(", ", ",")
470488
privs = privilege.split(",")
@@ -501,6 +519,12 @@ def getPrivileges(self):
501519
if self.__isAdminFromPrivileges(privileges):
502520
areAdmins.add(user)
503521

522+
# In MySQL < 5.0 we break the cycle after the first
523+
# time we get the user's privileges otherwise we
524+
# duplicate the same query
525+
if kb.dbms == "MySQL" and not self.has_information_schema:
526+
break
527+
504528
if privileges:
505529
self.cachedUsersPrivileges[user] = list(privileges)
506530
else:

0 commit comments

Comments
 (0)