diff --git a/doc/ChangeLog b/doc/ChangeLog
index 827b7d7ed..2420943dc 100644
--- a/doc/ChangeLog
+++ b/doc/ChangeLog
@@ -1,14 +1,17 @@
sqlmap (0.6.3-1) stable; urgency=low
+ * Major bug fix to correctly handle httplib.BadStatusLine exception;
* Minor enhancement to support stacked queries which will be used
sometimes by takeover functionality and time based blind SQL injection
technique;
* Minor enhancement to be able to specify the number of seconds to wait
between each HTTP request;
* Minor enhancement to be able to enumerate table columns and dump table
- entries also if the database name is not provided by using the current
- database on MySQL and MSSQL, the 'public' scheme on PostgreSQL and the
- 'USERS' TABLESPACE_NAME on Oracle;
+ entries, also when the database name is not provided, by using the
+ current database on MySQL and Microsoft SQL Server, the 'public'
+ scheme on PostgreSQL and the 'USERS' TABLESPACE_NAME on Oracle;
+ * Minor improvement to set by default in all HTTP requests the standard
+ HTTP headers (Accept, Accept-Encoding, etc);
* Minor improvements to sqlmap Debian package files: sqlmap uploaded
to official Debian project repository;
* Minor bug fix to handle session.error and session.timeout in HTTP
diff --git a/lib/controller/handler.py b/lib/controller/handler.py
index 25d7dea1c..5ee86fa25 100644
--- a/lib/controller/handler.py
+++ b/lib/controller/handler.py
@@ -55,7 +55,7 @@ def setHandler():
for dbmsAliases, dbmsEntry in dbmsMap:
if conf.dbms and conf.dbms not in dbmsAliases:
- debugMsg = "skipping to test for %s" % dbmsNames[count]
+ debugMsg = "skipping test for %s" % dbmsNames[count]
logger.debug(debugMsg)
count += 1
continue
diff --git a/lib/core/common.py b/lib/core/common.py
index 736b20ea8..82f2c5dfb 100644
--- a/lib/core/common.py
+++ b/lib/core/common.py
@@ -112,7 +112,7 @@ def paramToDict(place, parameters=None):
return testableParameters
-def formatFingerprint(versions=None):
+def formatDBMSfp(versions=None):
"""
This function format the back-end DBMS fingerprint value and return its
values formatted as a human readable string.
@@ -130,6 +130,47 @@ def formatFingerprint(versions=None):
return "%s %s" % (kb.dbms, " and ".join([version for version in versions]))
+def formatOSfp(info):
+ """
+ This function format the back-end operating system fingerprint value
+ and return its values formatted as a human readable string.
+
+ @return: detected back-end operating system based upon fingerprint
+ techniques.
+ @rtype: C{str}
+ """
+
+ infoStr = ""
+
+ # Example of 'info' dictionary:
+ # {
+ # 'distrib': 'Ubuntu',
+ # 'release': '8.10',
+ # 'codename': 'Intrepid',
+ # 'version': '5.0.67',
+ # 'type': 'Linux'
+ # }
+
+ if not info or 'type' not in info:
+ return infoStr
+ elif info['type'] != "None":
+ infoStr += "back-end DBMS operating system: %s" % info['type']
+
+ if 'distrib' in info and info['distrib'] != "None":
+ infoStr += " %s" % info['distrib']
+
+ if 'release' in info and info['release'] != "None":
+ infoStr += " %s" % info['release']
+
+ if 'sp' in info and info['sp'] != "None":
+ infoStr += " %s" % info['sp']
+
+ if 'codename' in info and info['codename'] != "None":
+ infoStr += " (%s)" % info['codename']
+
+ return infoStr
+
+
def getHtmlErrorFp():
"""
This function parses the knowledge base htmlFp list and return its
@@ -442,20 +483,25 @@ def cleanQuery(query):
def setPaths():
# sqlmap paths
- paths.SQLMAP_SHELL_PATH = "%s/shell" % paths.SQLMAP_ROOT_PATH
- paths.SQLMAP_TXT_PATH = "%s/txt" % paths.SQLMAP_ROOT_PATH
- paths.SQLMAP_XML_PATH = "%s/xml" % paths.SQLMAP_ROOT_PATH
- paths.SQLMAP_OUTPUT_PATH = "%s/output" % paths.SQLMAP_ROOT_PATH
- paths.SQLMAP_DUMP_PATH = paths.SQLMAP_OUTPUT_PATH + "/%s/dump"
- paths.SQLMAP_FILES_PATH = paths.SQLMAP_OUTPUT_PATH + "/%s/files"
+ paths.SQLMAP_SHELL_PATH = "%s/shell" % paths.SQLMAP_ROOT_PATH
+ paths.SQLMAP_TXT_PATH = "%s/txt" % paths.SQLMAP_ROOT_PATH
+ paths.SQLMAP_XML_PATH = "%s/xml" % paths.SQLMAP_ROOT_PATH
+ paths.SQLMAP_XML_BANNER_PATH = "%s/banner" % paths.SQLMAP_XML_PATH
+ paths.SQLMAP_OUTPUT_PATH = "%s/output" % paths.SQLMAP_ROOT_PATH
+ paths.SQLMAP_DUMP_PATH = paths.SQLMAP_OUTPUT_PATH + "/%s/dump"
+ paths.SQLMAP_FILES_PATH = paths.SQLMAP_OUTPUT_PATH + "/%s/files"
# sqlmap files
- paths.SQLMAP_HISTORY = "%s/.sqlmap_history" % paths.SQLMAP_ROOT_PATH
- paths.SQLMAP_CONFIG = "%s/sqlmap-%s.conf" % (paths.SQLMAP_ROOT_PATH, randomStr())
- paths.FUZZ_VECTORS = "%s/fuzz_vectors.txt" % paths.SQLMAP_TXT_PATH
- paths.ERRORS_XML = "%s/errors.xml" % paths.SQLMAP_XML_PATH
- paths.MSSQL_XML = "%s/mssql.xml" % paths.SQLMAP_XML_PATH
- paths.QUERIES_XML = "%s/queries.xml" % paths.SQLMAP_XML_PATH
+ paths.SQLMAP_HISTORY = "%s/.sqlmap_history" % paths.SQLMAP_ROOT_PATH
+ paths.SQLMAP_CONFIG = "%s/sqlmap-%s.conf" % (paths.SQLMAP_ROOT_PATH, randomStr())
+ paths.FUZZ_VECTORS = "%s/fuzz_vectors.txt" % paths.SQLMAP_TXT_PATH
+ paths.ERRORS_XML = "%s/errors.xml" % paths.SQLMAP_XML_PATH
+ paths.QUERIES_XML = "%s/queries.xml" % paths.SQLMAP_XML_PATH
+ paths.GENERIC_XML = "%s/generic.xml" % paths.SQLMAP_XML_BANNER_PATH
+ paths.MSSQL_XML = "%s/mssql.xml" % paths.SQLMAP_XML_BANNER_PATH
+ paths.MYSQL_XML = "%s/mysql.xml" % paths.SQLMAP_XML_BANNER_PATH
+ paths.ORACLE_XML = "%s/oracle.xml" % paths.SQLMAP_XML_BANNER_PATH
+ paths.PGSQL_XML = "%s/postgresql.xml" % paths.SQLMAP_XML_BANNER_PATH
def weAreFrozen():
diff --git a/lib/parse/banner.py b/lib/parse/banner.py
index 04b7dcef8..71fc1da9b 100644
--- a/lib/parse/banner.py
+++ b/lib/parse/banner.py
@@ -31,25 +31,67 @@ from xml.sax.handler import ContentHandler
from lib.core.common import checkFile
from lib.core.common import sanitizeStr
+from lib.core.data import kb
+from lib.core.data import paths
-class bannerHandler(ContentHandler):
+class BannerHandler(ContentHandler):
"""
This class defines methods to parse and extract information from
the given DBMS banner based upon the data in XML file
"""
+ def __init__(self, banner):
+ self.__banner = sanitizeStr(banner)
+
+ self.__regexp = None
+ self.__match = None
+ self.__position = None
+
+ self.info = {}
+
+
+ def startElement(self, name, attrs):
+ if name == "regexp":
+ self.__regexp = sanitizeStr(attrs.get("value"))
+ self.__match = re.search(self.__regexp, self.__banner, re.I | re.M)
+
+ if name == "info" and self.__match:
+ self.__position = sanitizeStr(attrs.get("version"))
+ self.__sp = sanitizeStr(attrs.get("sp"))
+
+ self.info['type'] = sanitizeStr(attrs.get("type"))
+ self.info['distrib'] = sanitizeStr(attrs.get("distrib"))
+ self.info['release'] = sanitizeStr(attrs.get("release"))
+ self.info['codename'] = sanitizeStr(attrs.get("codename"))
+
+ if self.__position.isdigit():
+ self.info['version'] = self.__match.group(int(self.__position))
+
+ if self.__sp.isdigit():
+ self.info['sp'] = "Service Pack %s" % self.__match.group(int(self.__sp))
+
+ self.__match = None
+ self.__position = None
+
+
+class MSSQLBannerHandler(ContentHandler):
+ """
+ This class defines methods to parse and extract information from the
+ given Microsoft SQL Server banner based upon the data in XML file
+ """
+
def __init__(self, banner):
self.__banner = sanitizeStr(banner)
- self.release = None
- self.version = None
- self.servicePack = None
+
self.__inVersion = False
self.__inServicePack = False
self.__release = None
self.__version = ""
self.__servicePack = ""
+ self.info = {}
+
def startElement(self, name, attrs):
if name == "signatures":
@@ -72,9 +114,9 @@ class bannerHandler(ContentHandler):
def endElement(self, name):
if name == "signature":
if re.search(" %s[\.\ ]+" % self.__version, self.__banner):
- self.release = self.__release
- self.version = self.__version
- self.servicePack = self.__servicePack
+ self.info['dbmsRelease'] = self.__release
+ self.info['dbmsVersion'] = self.__version
+ self.info['dbmsServicePack'] = self.__servicePack
self.__version = ""
self.__servicePack = ""
@@ -89,16 +131,47 @@ class bannerHandler(ContentHandler):
self.__servicePack = self.__servicePack.replace(" ", "")
-
-def bannerParser(banner, xmlfile):
+def bannerParser(banner):
"""
This function calls a class to extract information from the given
DBMS banner based upon the data in XML file
"""
- checkFile(xmlfile)
banner = sanitizeStr(banner)
- handler = bannerHandler(banner)
- parse(xmlfile, handler)
+ info = {}
- return handler.release, handler.version, handler.servicePack
+ if kb.dbms == "Microsoft SQL Server":
+ xmlfile = paths.MSSQL_XML
+ elif kb.dbms == "MySQL":
+ xmlfile = paths.MYSQL_XML
+ elif kb.dbms == "Oracle":
+ xmlfile = paths.ORACLE_XML
+ elif kb.dbms == "PostgreSQL":
+ xmlfile = paths.PGSQL_XML
+
+ checkFile(xmlfile)
+
+ if kb.dbms == "Microsoft SQL Server":
+ handler = MSSQLBannerHandler(banner)
+ parse(xmlfile, handler)
+ info = handler.info
+
+ handler = BannerHandler(banner)
+ parse(paths.GENERIC_XML, handler)
+
+ for title, value in handler.info.items():
+ info[title] = value
+ else:
+ handler = BannerHandler(banner)
+ parse(xmlfile, handler)
+ info = handler.info
+
+ if "type" not in info or info["type"] == "None":
+ parse(paths.GENERIC_XML, handler)
+ info["type"] = handler.info["type"]
+
+ if "distrib" not in info or info["distrib"] == "None":
+ parse(paths.GENERIC_XML, handler)
+ info["distrib"] = handler.info["distrib"]
+
+ return info
diff --git a/lib/parse/cmdline.py b/lib/parse/cmdline.py
index b759efac9..24b4120c7 100644
--- a/lib/parse/cmdline.py
+++ b/lib/parse/cmdline.py
@@ -129,7 +129,7 @@ def cmdLineParser():
fingerprint.add_option("-f", "--fingerprint", dest="extensiveFp",
action="store_true",
- help="Perform an extensive database fingerprint")
+ help="Perform an extensive DBMS version fingerprint")
# Enumeration options
enumeration = OptionGroup(parser, "Enumeration", "These options can "
diff --git a/plugins/dbms/mssqlserver.py b/plugins/dbms/mssqlserver.py
index 524c2efb8..cff2cfd88 100644
--- a/plugins/dbms/mssqlserver.py
+++ b/plugins/dbms/mssqlserver.py
@@ -28,14 +28,14 @@ import time
from lib.core.agent import agent
from lib.core.common import dataToStdout
-from lib.core.common import formatFingerprint
+from lib.core.common import formatDBMSfp
+from lib.core.common import formatOSfp
from lib.core.common import getHtmlErrorFp
from lib.core.common import randomInt
from lib.core.common import readInput
from lib.core.data import conf
from lib.core.data import kb
from lib.core.data import logger
-from lib.core.data import paths
from lib.core.data import queries
from lib.core.exception import sqlmapNoneDataException
from lib.core.exception import sqlmapSyntaxException
@@ -124,16 +124,21 @@ class MSSQLServerMap(Fingerprint, Enumeration, Filesystem, Takeover):
def getFingerprint(self):
- actVer = formatFingerprint()
+ actVer = formatDBMSfp()
if not conf.extensiveFp:
return actVer
- blank = " " * 16
- value = "active fingerprint: %s" % actVer
+ blank = " " * 16
+ formatInfo = None
+ value = "active fingerprint: %s" % actVer
if self.banner:
- release, version, servicepack = bannerParser(self.banner, paths.MSSQL_XML)
+ info = bannerParser(self.banner)
+ release = info["dbmsRelease"]
+ version = info["dbmsVersion"]
+ servicepack = info["dbmsServicePack"]
+ formatInfo = formatOSfp(info)
if release and version and servicepack:
banVer = "Microsoft SQL Server %s " % release
@@ -148,6 +153,9 @@ class MSSQLServerMap(Fingerprint, Enumeration, Filesystem, Takeover):
if htmlParsed:
value += "\n%shtml error message fingerprint: %s" % (blank, htmlParsed)
+ if formatInfo:
+ value += "\n%s" % formatInfo
+
return value
diff --git a/plugins/dbms/mysql.py b/plugins/dbms/mysql.py
index ecfa1fa54..004b100eb 100644
--- a/plugins/dbms/mysql.py
+++ b/plugins/dbms/mysql.py
@@ -28,7 +28,8 @@ import re
from lib.core.agent import agent
from lib.core.common import fileToStr
-from lib.core.common import formatFingerprint
+from lib.core.common import formatDBMSfp
+from lib.core.common import formatOSfp
from lib.core.common import getDirectories
from lib.core.common import getHtmlErrorFp
from lib.core.common import randomInt
@@ -43,6 +44,7 @@ from lib.core.settings import MYSQL_ALIASES
from lib.core.settings import MYSQL_SYSTEM_DBS
from lib.core.shell import autoCompletion
from lib.core.unescaper import unescaper
+from lib.parse.banner import bannerParser
from lib.request import inject
from lib.request.connect import Connect as Request
#from lib.utils.fuzzer import passiveFuzzing
@@ -180,26 +182,28 @@ class MySQLMap(Fingerprint, Enumeration, Filesystem, Takeover):
def getFingerprint(self):
- actVer = formatFingerprint()
+ actVer = formatDBMSfp()
if not conf.extensiveFp:
return actVer
- blank = " " * 16
- value = "active fingerprint: %s" % actVer
- comVer = self.__commentCheck()
+ comVer = self.__commentCheck()
+ blank = " " * 16
+ formatInfo = None
+ value = "active fingerprint: %s" % actVer
if comVer:
- comVer = formatFingerprint([comVer])
+ comVer = formatDBMSfp([comVer])
value += "\n%scomment injection fingerprint: %s" % (blank, comVer)
if self.banner:
- banVer = re.search("^([\d\.]+)", self.banner)
- banVer = banVer.groups()[0]
+ info = bannerParser(self.banner)
+ formatInfo = formatOSfp(info)
+
+ banVer = info['version']
if re.search("-log$", self.banner):
banVer += ", logging enabled"
- banVer = formatFingerprint([banVer])
-
+ banVer = formatDBMSfp([banVer])
value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer)
#passiveFuzzing()
@@ -208,6 +212,9 @@ class MySQLMap(Fingerprint, Enumeration, Filesystem, Takeover):
if htmlParsed:
value += "\n%shtml error message fingerprint: %s" % (blank, htmlParsed)
+ if formatInfo:
+ value += "\n%s" % formatInfo
+
return value
diff --git a/plugins/dbms/oracle.py b/plugins/dbms/oracle.py
index fc134af0f..4922f606d 100644
--- a/plugins/dbms/oracle.py
+++ b/plugins/dbms/oracle.py
@@ -26,7 +26,8 @@ Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
import re
-from lib.core.common import formatFingerprint
+from lib.core.common import formatDBMSfp
+from lib.core.common import formatOSfp
from lib.core.common import getHtmlErrorFp
from lib.core.data import conf
from lib.core.data import kb
@@ -36,6 +37,7 @@ from lib.core.session import setDbms
from lib.core.settings import ORACLE_ALIASES
from lib.core.settings import ORACLE_SYSTEM_DBS
from lib.core.unescaper import unescaper
+from lib.parse.banner import bannerParser
from lib.request import inject
#from lib.utils.fuzzer import passiveFuzzing
@@ -119,19 +121,19 @@ class OracleMap(Fingerprint, Enumeration, Filesystem, Takeover):
if not conf.extensiveFp:
return "Oracle"
- actVer = formatFingerprint()
+ actVer = formatDBMSfp()
- blank = " " * 16
- value = "active fingerprint: %s" % actVer
+ blank = " " * 16
+ formatInfo = None
+ value = "active fingerprint: %s" % actVer
if self.banner:
- banVer = re.search("^Oracle .*Release ([\d\.]+) ", self.banner)
+ info = bannerParser(self.banner)
+ formatInfo = formatOSfp(info)
- if banVer:
- banVer = banVer.groups()[0]
- banVer = formatFingerprint([banVer])
-
- value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer)
+ banVer = info['version']
+ banVer = formatDBMSfp([banVer])
+ value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer)
#passiveFuzzing()
htmlParsed = getHtmlErrorFp()
@@ -139,6 +141,9 @@ class OracleMap(Fingerprint, Enumeration, Filesystem, Takeover):
if htmlParsed:
value += "\n%shtml error message fingerprint: %s" % (blank, htmlParsed)
+ if formatInfo:
+ value += "\n%s" % formatInfo
+
return value
@@ -159,7 +164,7 @@ class OracleMap(Fingerprint, Enumeration, Filesystem, Takeover):
logMsg = "confirming Oracle"
logger.info(logMsg)
- query = "SELECT VERSION FROM SYS.PRODUCT_COMPONENT_VERSION WHERE ROWNUM=1"
+ query = "SELECT SUBSTR((VERSION), 1, 2) FROM SYS.PRODUCT_COMPONENT_VERSION WHERE ROWNUM=1"
version = inject.getValue(query)
if not version:
@@ -173,13 +178,13 @@ class OracleMap(Fingerprint, Enumeration, Filesystem, Takeover):
if not conf.extensiveFp:
return True
- if re.search("^11\.", version):
+ if re.search("^11", version):
kb.dbmsVersion = ["11i"]
- elif re.search("^10\.", version):
+ elif re.search("^10", version):
kb.dbmsVersion = ["10g"]
- elif re.search("^9\.", version):
+ elif re.search("^9", version):
kb.dbmsVersion = ["9i"]
- elif re.search("^8\.", version):
+ elif re.search("^8", version):
kb.dbmsVersion = ["8i"]
if conf.getBanner:
diff --git a/plugins/dbms/postgresql.py b/plugins/dbms/postgresql.py
index ca1ad85f0..dfe1a11be 100644
--- a/plugins/dbms/postgresql.py
+++ b/plugins/dbms/postgresql.py
@@ -26,7 +26,8 @@ Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
import re
-from lib.core.common import formatFingerprint
+from lib.core.common import formatDBMSfp
+from lib.core.common import formatOSfp
from lib.core.common import getHtmlErrorFp
from lib.core.common import randomInt
from lib.core.data import conf
@@ -37,6 +38,7 @@ from lib.core.session import setDbms
from lib.core.settings import PGSQL_ALIASES
from lib.core.settings import PGSQL_SYSTEM_DBS
from lib.core.unescaper import unescaper
+from lib.parse.banner import bannerParser
from lib.request import inject
#from lib.utils.fuzzer import passiveFuzzing
@@ -119,16 +121,18 @@ class PostgreSQLMap(Fingerprint, Enumeration, Filesystem, Takeover):
if not conf.extensiveFp:
return "PostgreSQL"
- actVer = formatFingerprint()
+ actVer = formatDBMSfp()
- blank = " " * 16
- value = "active fingerprint: %s" % actVer
+ blank = " " * 16
+ formatInfo = None
+ value = "active fingerprint: %s" % actVer
if self.banner:
- banVer = re.search("^PostgreSQL ([\d\.]+)", self.banner)
- banVer = banVer.groups()[0]
- banVer = formatFingerprint([banVer])
+ info = bannerParser(self.banner)
+ formatInfo = formatOSfp(info)
+ banVer = info['version']
+ banVer = formatDBMSfp([banVer])
value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer)
#passiveFuzzing()
@@ -137,6 +141,9 @@ class PostgreSQLMap(Fingerprint, Enumeration, Filesystem, Takeover):
if htmlParsed:
value += "\n%shtml error message fingerprint: %s" % (blank, htmlParsed)
+ if formatInfo:
+ value += "\n%s" % formatInfo
+
return value
diff --git a/xml/banner/generic.xml b/xml/banner/generic.xml
new file mode 100644
index 000000000..afc762df9
--- /dev/null
+++ b/xml/banner/generic.xml
@@ -0,0 +1,86 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/xml/mssql.xml b/xml/banner/mssql.xml
similarity index 100%
rename from xml/mssql.xml
rename to xml/banner/mssql.xml
diff --git a/xml/banner/mysql.xml b/xml/banner/mysql.xml
new file mode 100644
index 000000000..1c21ec89c
--- /dev/null
+++ b/xml/banner/mysql.xml
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/xml/banner/oracle.xml b/xml/banner/oracle.xml
new file mode 100644
index 000000000..ad8bc36d9
--- /dev/null
+++ b/xml/banner/oracle.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/xml/banner/postgresql.xml b/xml/banner/postgresql.xml
new file mode 100644
index 000000000..79cde445b
--- /dev/null
+++ b/xml/banner/postgresql.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/xml/queries.xml b/xml/queries.xml
index 14d766a8c..84d7ba66f 100644
--- a/xml/queries.xml
+++ b/xml/queries.xml
@@ -75,6 +75,10 @@
+