diff --git a/doc/ChangeLog b/doc/ChangeLog
index 314f13316..cd0c4edd5 100644
--- a/doc/ChangeLog
+++ b/doc/ChangeLog
@@ -3,7 +3,7 @@ sqlmap (0.6.2-1) stable; urgency=low
* Major bug fix to correctly dump tables entries when --stop is not
specified;
* Major bug fix so that the users' privileges enumeration now works
- properly also on MySQL < 5.0;
+ properly also on both MySQL < 5.0 and MySQL >= 5.0;
* Major bug fix when the request is POST to also send the url parameters
if any have been provided;
* Major improvement to correctly enumerate tables, columns and dump
diff --git a/lib/core/unescaper.py b/lib/core/unescaper.py
index 586406fd7..0dbc9465f 100644
--- a/lib/core/unescaper.py
+++ b/lib/core/unescaper.py
@@ -33,8 +33,8 @@ class Unescaper:
self.__unescaper = unescapeFunction
- def unescape(self, expression):
- return self.__unescaper(expression)
+ def unescape(self, expression, quote=True):
+ return self.__unescaper(expression, quote=quote)
unescaper = Unescaper()
diff --git a/plugins/dbms/mssqlserver.py b/plugins/dbms/mssqlserver.py
index 32c229043..45f124417 100644
--- a/plugins/dbms/mssqlserver.py
+++ b/plugins/dbms/mssqlserver.py
@@ -67,30 +67,33 @@ class MSSQLServerMap(Fingerprint, Enumeration, Filesystem, Takeover):
@staticmethod
- def unescape(expression):
- while True:
- index = expression.find("'")
- if index == -1:
- break
+ def unescape(expression, quote=True):
+ if quote:
+ while True:
+ index = expression.find("'")
+ if index == -1:
+ break
- firstIndex = index + 1
- index = expression[firstIndex:].find("'")
+ firstIndex = index + 1
+ index = expression[firstIndex:].find("'")
- if index == -1:
- raise sqlmapSyntaxException, "Unenclosed ' in '%s'" % expression
+ if index == -1:
+ raise sqlmapSyntaxException, "Unenclosed ' in '%s'" % expression
- lastIndex = firstIndex + index
- old = "'%s'" % expression[firstIndex:lastIndex]
- #unescaped = ""
- unescaped = "("
+ lastIndex = firstIndex + index
+ old = "'%s'" % expression[firstIndex:lastIndex]
+ #unescaped = "("
+ unescaped = ""
- for i in range(firstIndex, lastIndex):
- unescaped += "CHAR(%d)" % (ord(expression[i]))
- if i < lastIndex - 1:
- unescaped += "+"
+ for i in range(firstIndex, lastIndex):
+ unescaped += "CHAR(%d)" % (ord(expression[i]))
+ if i < lastIndex - 1:
+ unescaped += "+"
- unescaped += ")"
- expression = expression.replace(old, unescaped)
+ #unescaped += ")"
+ expression = expression.replace(old, unescaped)
+ else:
+ expression = "+".join("CHAR(%d)" % ord(c) for c in expression)
return expression
diff --git a/plugins/dbms/mysql.py b/plugins/dbms/mysql.py
index 77129ebfd..81452b1da 100644
--- a/plugins/dbms/mysql.py
+++ b/plugins/dbms/mysql.py
@@ -66,28 +66,35 @@ class MySQLMap(Fingerprint, Enumeration, Filesystem, Takeover):
@staticmethod
- def unescape(expression):
- while True:
- index = expression.find("'")
- if index == -1:
- break
+ def unescape(expression, quote=True):
+ if quote:
+ while True:
+ index = expression.find("'")
+ if index == -1:
+ break
- firstIndex = index + 1
- index = expression[firstIndex:].find("'")
+ firstIndex = index + 1
+ index = expression[firstIndex:].find("'")
- if index == -1:
- raise sqlmapSyntaxException, "Unenclosed ' in '%s'" % expression
+ if index == -1:
+ raise sqlmapSyntaxException, "Unenclosed ' in '%s'" % expression
- lastIndex = firstIndex + index
- old = "'%s'" % expression[firstIndex:lastIndex]
- unescaped = ""
+ lastIndex = firstIndex + index
+ old = "'%s'" % expression[firstIndex:lastIndex]
+ unescaped = ""
- for i in range(firstIndex, lastIndex):
- unescaped += "%d" % (ord(expression[i]))
- if i < lastIndex - 1:
- unescaped += ","
+ for i in range(firstIndex, lastIndex):
+ unescaped += "%d" % (ord(expression[i]))
+ if i < lastIndex - 1:
+ unescaped += ","
- expression = expression.replace(old, "CHAR(%s)" % unescaped)
+ expression = expression.replace(old, "CHAR(%s)" % unescaped)
+ else:
+ unescaped = "CHAR("
+ unescaped += ",".join("%d" % ord(c) for c in expression)
+ unescaped += ")"
+
+ expression = unescaped
return expression
diff --git a/plugins/dbms/oracle.py b/plugins/dbms/oracle.py
index 57ee93dff..ac98f2a37 100644
--- a/plugins/dbms/oracle.py
+++ b/plugins/dbms/oracle.py
@@ -59,30 +59,33 @@ class OracleMap(Fingerprint, Enumeration, Filesystem, Takeover):
@staticmethod
- def unescape(expression):
- while True:
- index = expression.find("'")
- if index == -1:
- break
+ def unescape(expression, quote=True):
+ if quote:
+ while True:
+ index = expression.find("'")
+ if index == -1:
+ break
- firstIndex = index + 1
- index = expression[firstIndex:].find("'")
+ firstIndex = index + 1
+ index = expression[firstIndex:].find("'")
- if index == -1:
- raise sqlmapSyntaxException, "Unenclosed ' in '%s'" % expression
+ if index == -1:
+ raise sqlmapSyntaxException, "Unenclosed ' in '%s'" % expression
- lastIndex = firstIndex + index
- old = "'%s'" % expression[firstIndex:lastIndex]
- #unescaped = ""
- unescaped = "("
+ lastIndex = firstIndex + index
+ old = "'%s'" % expression[firstIndex:lastIndex]
+ #unescaped = "("
+ unescaped = ""
- for i in range(firstIndex, lastIndex):
- unescaped += "CHR(%d)" % (ord(expression[i]))
- if i < lastIndex - 1:
- unescaped += "||"
+ for i in range(firstIndex, lastIndex):
+ unescaped += "CHR(%d)" % (ord(expression[i]))
+ if i < lastIndex - 1:
+ unescaped += "||"
- unescaped += ")"
- expression = expression.replace(old, unescaped)
+ #unescaped += ")"
+ expression = expression.replace(old, unescaped)
+ else:
+ expression = "||".join("CHR(%d)" % ord(c) for c in expression)
return expression
diff --git a/plugins/dbms/postgresql.py b/plugins/dbms/postgresql.py
index 0164507c6..ca1ad85f0 100644
--- a/plugins/dbms/postgresql.py
+++ b/plugins/dbms/postgresql.py
@@ -59,29 +59,33 @@ class PostgreSQLMap(Fingerprint, Enumeration, Filesystem, Takeover):
@staticmethod
- def unescape(expression):
- while True:
- index = expression.find("'")
- if index == -1:
- break
+ def unescape(expression, quote=True):
+ if quote:
+ while True:
+ index = expression.find("'")
+ if index == -1:
+ break
- firstIndex = index + 1
- index = expression[firstIndex:].find("'")
+ firstIndex = index + 1
+ index = expression[firstIndex:].find("'")
- if index == -1:
- raise sqlmapSyntaxException, "Unenclosed ' in '%s'" % expression
+ if index == -1:
+ raise sqlmapSyntaxException, "Unenclosed ' in '%s'" % expression
- lastIndex = firstIndex + index
- old = "'%s'" % expression[firstIndex:lastIndex]
- unescaped = "("
+ lastIndex = firstIndex + index
+ old = "'%s'" % expression[firstIndex:lastIndex]
+ #unescaped = "("
+ unescaped = ""
- for i in range(firstIndex, lastIndex):
- unescaped += "CHR(%d)" % (ord(expression[i]))
- if i < lastIndex - 1:
- unescaped += "||"
+ for i in range(firstIndex, lastIndex):
+ unescaped += "CHR(%d)" % (ord(expression[i]))
+ if i < lastIndex - 1:
+ unescaped += "||"
- unescaped += ")"
- expression = expression.replace(old, unescaped)
+ #unescaped += ")"
+ expression = expression.replace(old, unescaped)
+ else:
+ expression = "||".join("CHR(%d)" % ord(c) for c in expression)
return expression
diff --git a/plugins/generic/enumeration.py b/plugins/generic/enumeration.py
index 823f0676e..4c021e8b7 100644
--- a/plugins/generic/enumeration.py
+++ b/plugins/generic/enumeration.py
@@ -40,6 +40,7 @@ from lib.core.exception import sqlmapNoneDataException
from lib.core.exception import sqlmapUndefinedMethod
from lib.core.exception import sqlmapUnsupportedFeatureException
from lib.core.shell import autoCompletion
+from lib.core.unescaper import unescaper
from lib.request import inject
from lib.request.connect import Connect as Request
@@ -346,19 +347,19 @@ class Enumeration:
if "," in conf.user:
users = conf.user.split(",")
query += " WHERE "
- # NOTE: we need this here only for MySQL 5.0 because
- # of a known issue explained in queries.xml
+ # NOTE: I assume that the user provided is not in
+ # MySQL >= 5.0 syntax 'user'@'host'
if kb.dbms == "MySQL" and self.has_information_schema:
- likeUser = "%" + conf.user + "%"
+ queryUser = "%" + conf.user + "%"
query += " OR ".join("%s LIKE '%s'" % (condition, "%" + user + "%") for user in users)
else:
query += " OR ".join("%s = '%s'" % (condition, user) for user in users)
else:
- # NOTE: we need this here only for MySQL 5.0 because
- # of a known issue explained in queries.xml
+ # NOTE: I assume that the user provided is not in
+ # MySQL >= 5.0 syntax 'user'@'host'
if kb.dbms == "MySQL" and self.has_information_schema:
- likeUser = "%" + conf.user + "%"
- query += " WHERE %s LIKE '%s'" % (condition, likeUser)
+ queryUser = "%" + conf.user + "%"
+ query += " WHERE %s LIKE '%s'" % (condition, queryUser)
else:
query += " WHERE %s = '%s'" % (condition, conf.user)
@@ -406,11 +407,25 @@ class Enumeration:
self.cachedUsersPrivileges[user] = list(privileges)
if not self.cachedUsersPrivileges:
+ conditionChar = "="
+
if conf.user:
- if "," in conf.user:
+ if kb.dbms == "MySQL" and self.has_information_schema:
+ conditionChar = " LIKE "
+
+ if "," in conf.user:
+ users = set()
+ for user in conf.user.split(","):
+ users.add("%" + user + "%")
+ else:
+ users = [ "%" + conf.user + "%" ]
+
+ elif "," in conf.user:
users = conf.user.split(",")
+
else:
- users = [conf.user]
+ users = [ conf.user ]
+
else:
if not len(self.cachedUsers):
users = self.getUsers()
@@ -420,11 +435,10 @@ class Enumeration:
retrievedUsers = set()
for user in users:
- if kb.dbms == "MySQL":
- parsedUser = re.search("\047(.*?)\047@'", user)
+ unescapedUser = None
- if parsedUser:
- user = parsedUser.groups()[0].replace("'", "")
+ if kb.dbms == "MySQL" and self.has_information_schema:
+ unescapedUser = unescaper.unescape(user, quote=False)
if user in retrievedUsers:
continue
@@ -433,24 +447,26 @@ class Enumeration:
logMsg += "for user '%s'" % user
logger.info(logMsg)
- if kb.dbms == "MySQL" and self.has_information_schema:
- likeUser = "%" + user + "%"
+ if unescapedUser:
+ queryUser = unescapedUser
else:
- likeUser = user
+ queryUser = user
if kb.dbms == "MySQL" and not self.has_information_schema:
- query = rootQuery["blind"]["count2"] % likeUser
+ query = rootQuery["blind"]["count2"] % queryUser
+ elif kb.dbms == "MySQL" and self.has_information_schema:
+ query = rootQuery["blind"]["count"] % (conditionChar, queryUser)
else:
- query = rootQuery["blind"]["count"] % likeUser
+ query = rootQuery["blind"]["count"] % queryUser
count = inject.getValue(query, inband=False)
if not len(count) or count == "0":
warnMsg = "unable to retrieve the number of "
- warnMsg += "privileges for user '%s'" % likeUser
+ warnMsg += "privileges for user '%s'" % user
logger.warn(warnMsg)
continue
- logMsg = "fetching privileges for user '%s'" % likeUser
+ logMsg = "fetching privileges for user '%s'" % user
logger.info(logMsg)
privileges = set()
@@ -458,13 +474,15 @@ class Enumeration:
for index in indexRange:
if kb.dbms == "MySQL" and not self.has_information_schema:
- query = rootQuery["blind"]["query2"] % (likeUser, index)
+ query = rootQuery["blind"]["query2"] % (queryUser, index)
+ elif kb.dbms == "MySQL" and self.has_information_schema:
+ query = rootQuery["blind"]["query"] % (conditionChar, queryUser, index)
else:
- query = rootQuery["blind"]["query"] % (likeUser, index)
+ query = rootQuery["blind"]["query"] % (queryUser, index)
privilege = inject.getValue(query, inband=False)
- # In PostgreSQL we return 1 if the privilege
- # if True, otherwise 0
+ # In PostgreSQL we get 1 if the privilege is True,
+ # 0 otherwise
if kb.dbms == "PostgreSQL" and ", " in privilege:
privilege = privilege.replace(", ", ",")
privs = privilege.split(",")
@@ -501,6 +519,12 @@ class Enumeration:
if self.__isAdminFromPrivileges(privileges):
areAdmins.add(user)
+ # In MySQL < 5.0 we break the cycle after the first
+ # time we get the user's privileges otherwise we
+ # duplicate the same query
+ if kb.dbms == "MySQL" and not self.has_information_schema:
+ break
+
if privileges:
self.cachedUsersPrivileges[user] = list(privileges)
else:
diff --git a/xml/queries.xml b/xml/queries.xml
index ad2a64123..0e987b35c 100644
--- a/xml/queries.xml
+++ b/xml/queries.xml
@@ -29,8 +29,7 @@
-
-
+
@@ -177,10 +176,12 @@
+
+