From 654aecedfef57a7b115d754d4d043c57794a3f41 Mon Sep 17 00:00:00 2001 From: Bernardo Damele Date: Mon, 17 Nov 2008 00:00:54 +0000 Subject: [PATCH] Minor layout adjustments, minor fixes and updated changelog --- doc/ChangeLog | 4 ++- lib/controller/action.py | 2 +- lib/core/common.py | 2 +- lib/core/option.py | 1 + lib/parse/headers.py | 57 +++++++++++++++++++++++++++++++++++++ lib/parse/html.py | 18 ++++++++---- lib/request/basic.py | 27 +++++++++--------- lib/request/connect.py | 4 +-- plugins/dbms/mssqlserver.py | 16 ++++++----- plugins/dbms/mysql.py | 18 ++++++------ plugins/dbms/oracle.py | 20 +++++++------ plugins/dbms/postgresql.py | 20 +++++++------ xml/banner/generic.xml | 2 +- 13 files changed, 133 insertions(+), 58 deletions(-) create mode 100644 lib/parse/headers.py diff --git a/doc/ChangeLog b/doc/ChangeLog index 2420943dc..4ecc84a71 100644 --- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -4,8 +4,10 @@ sqlmap (0.6.3-1) stable; urgency=low * Minor enhancement to support stacked queries which will be used sometimes by takeover functionality and time based blind SQL injection technique; + * Minor enhancement to fingerprint the back-end DBMS operating system by + parsing the DBMS banner value when both -f and -b are provided; * Minor enhancement to be able to specify the number of seconds to wait - between each HTTP request; + between each HTTP request providing option --delay #; * Minor enhancement to be able to enumerate table columns and dump table entries, also when the database name is not provided, by using the current database on MySQL and Microsoft SQL Server, the 'public' diff --git a/lib/controller/action.py b/lib/controller/action.py index c94e4a54c..f2936dd53 100644 --- a/lib/controller/action.py +++ b/lib/controller/action.py @@ -67,7 +67,7 @@ def action(): raise sqlmapUnsupportedDBMSException, errMsg - print "back-end DBMS:\t%s\n" % conf.dbmsHandler.getFingerprint() + print "%s\n" % conf.dbmsHandler.getFingerprint() # Techniques options if conf.timeTest: diff --git a/lib/core/common.py b/lib/core/common.py index 82f2c5dfb..68f127800 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -190,7 +190,7 @@ def getHtmlErrorFp(): htmlVer = kb.htmlFp[0] htmlParsed = htmlVer elif len(kb.htmlFp) > 1: - htmlParsed = "or ".join([htmlFp for htmlFp in kb.htmlFp]) + htmlParsed = " or ".join([htmlFp for htmlFp in kb.htmlFp]) return htmlParsed diff --git a/lib/core/option.py b/lib/core/option.py index a15cfdbcd..89b992868 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -453,6 +453,7 @@ def __setKnowledgeBaseAttributes(): kb.dbms = None kb.dbmsDetected = False kb.dbmsVersion = None + kb.headersFp = {} kb.htmlFp = [] kb.injParameter = None kb.injPlace = None diff --git a/lib/parse/headers.py b/lib/parse/headers.py new file mode 100644 index 000000000..2a768eb0b --- /dev/null +++ b/lib/parse/headers.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python + +""" +$Id$ + +This file is part of the sqlmap project, http://sqlmap.sourceforge.net. + +Copyright (c) 2006-2008 Bernardo Damele A. G. + and Daniele Bellucci + +sqlmap is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation version 2 of the License. + +sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +details. + +You should have received a copy of the GNU General Public License along +with sqlmap; if not, write to the Free Software Foundation, Inc., 51 +Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +""" + + + +import re + +from xml.sax import parse +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 +from lib.parse.banner import BannerHandler + +def headersParser(headers): + """ + This function calls a class that parses the input HTTP headers to + fingerprint the back-end database management system operating system + and web application technology + """ + + topHeaders = { + "cookie", + "microsoftsharepointteamservices", + "server", + "servlet-engine", + "www-authenticate", + "x-aspnet-version", + "x-powered-by", + } + + for header in headers: + if header in topHeaders: + pass diff --git a/lib/parse/html.py b/lib/parse/html.py index ac81e67e8..237f6596f 100644 --- a/lib/parse/html.py +++ b/lib/parse/html.py @@ -31,6 +31,8 @@ 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 htmlHandler(ContentHandler): @@ -40,12 +42,12 @@ class htmlHandler(ContentHandler): """ def __init__(self, page): - self.__dbms = None - self.__page = page + self.__dbms = None + self.__page = page self.__regexp = None - self.__match = None + self.__match = None - self.dbms = None + self.dbms = None def startElement(self, name, attrs): @@ -61,15 +63,21 @@ class htmlHandler(ContentHandler): self.__match = None -def htmlParser(page, xmlfile): +def htmlParser(page, xmlfile=None): """ This function calls a class that parses the input HTML page to fingerprint the back-end database management system """ + if not xmlfile: + xmlfile = paths.ERRORS_XML + checkFile(xmlfile) page = sanitizeStr(page) handler = htmlHandler(page) parse(xmlfile, handler) + if handler.dbms and handler.dbms not in kb.htmlFp: + kb.htmlFp.append(handler.dbms) + return handler.dbms diff --git a/lib/request/basic.py b/lib/request/basic.py index ffe3b4558..1704ae8fa 100644 --- a/lib/request/basic.py +++ b/lib/request/basic.py @@ -29,6 +29,7 @@ import re from lib.core.data import conf from lib.core.data import kb from lib.core.data import paths +from lib.parse.headers import headersParser from lib.parse.html import htmlParser @@ -51,7 +52,7 @@ def forgeHeaders(cookie, ua): return headers -def parsePage(page): +def parseResponse(page, headers): """ @param page: the page to parse to feed the knowledge base htmlFp (back-end DBMS fingerprint based upon DBMS error messages return @@ -63,19 +64,17 @@ def parsePage(page): like for DBMS error messages (ERRORS_XML), see above. """ - if not page: - return + if headers: + headersParser(headers) - htmlParsed = htmlParser(page, paths.ERRORS_XML) + if page: + htmlParser(page) - if htmlParsed and htmlParsed not in kb.htmlFp: - kb.htmlFp.append(htmlParsed) + # Detect injectable page absolute system path + # NOTE: this regular expression works if the remote web application + # is written in PHP and debug/error messages are enabled. + absFilePaths = re.findall(" in (.*?) on line", page, re.I) - # Detect injectable page absolute system path - # NOTE: this regular expression works if the remote web application - # is written in PHP and debug/error messages are enabled. - absFilePaths = re.findall(" in (.*?) on line", page, re.I) - - for absFilePath in absFilePaths: - if absFilePath not in kb.absFilePaths: - kb.absFilePaths.add(absFilePath) + for absFilePath in absFilePaths: + if absFilePath not in kb.absFilePaths: + kb.absFilePaths.add(absFilePath) diff --git a/lib/request/connect.py b/lib/request/connect.py index 90c2c84a7..b3c301c22 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -39,7 +39,7 @@ from lib.core.data import kb from lib.core.data import logger from lib.core.exception import sqlmapConnectionException from lib.request.basic import forgeHeaders -from lib.request.basic import parsePage +from lib.request.basic import parseResponse @@ -196,7 +196,7 @@ class Connect: else: raise sqlmapConnectionException, warnMsg - parsePage(page) + parseResponse(page, responseHeaders) responseMsg += "(%s - %d):\n" % (status, code) if conf.verbose <= 4: diff --git a/plugins/dbms/mssqlserver.py b/plugins/dbms/mssqlserver.py index cff2cfd88..4b52f3455 100644 --- a/plugins/dbms/mssqlserver.py +++ b/plugins/dbms/mssqlserver.py @@ -124,14 +124,16 @@ class MSSQLServerMap(Fingerprint, Enumeration, Filesystem, Takeover): def getFingerprint(self): + value = "back-end DBMS: " actVer = formatDBMSfp() if not conf.extensiveFp: - return actVer + value += actVer + return value - blank = " " * 16 - formatInfo = None - value = "active fingerprint: %s" % actVer + blank = " " * 15 + formatInfo = None + value += "active fingerprint: %s" % actVer if self.banner: info = bannerParser(self.banner) @@ -148,10 +150,10 @@ class MSSQLServerMap(Fingerprint, Enumeration, Filesystem, Takeover): value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer) #passiveFuzzing() - htmlParsed = getHtmlErrorFp() + htmlErrorFp = getHtmlErrorFp() - if htmlParsed: - value += "\n%shtml error message fingerprint: %s" % (blank, htmlParsed) + if htmlErrorFp: + value += "\n%shtml error message fingerprint: %s" % (blank, htmlErrorFp) if formatInfo: value += "\n%s" % formatInfo diff --git a/plugins/dbms/mysql.py b/plugins/dbms/mysql.py index 004b100eb..b9ba41e7e 100644 --- a/plugins/dbms/mysql.py +++ b/plugins/dbms/mysql.py @@ -182,15 +182,17 @@ class MySQLMap(Fingerprint, Enumeration, Filesystem, Takeover): def getFingerprint(self): + value = "back-end DBMS: " actVer = formatDBMSfp() if not conf.extensiveFp: - return actVer + value += actVer + return value - comVer = self.__commentCheck() - blank = " " * 16 - formatInfo = None - value = "active fingerprint: %s" % actVer + comVer = self.__commentCheck() + blank = " " * 15 + formatInfo = None + value += "active fingerprint: %s" % actVer if comVer: comVer = formatDBMSfp([comVer]) @@ -207,10 +209,10 @@ class MySQLMap(Fingerprint, Enumeration, Filesystem, Takeover): value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer) #passiveFuzzing() - htmlParsed = getHtmlErrorFp() + htmlErrorFp = getHtmlErrorFp() - if htmlParsed: - value += "\n%shtml error message fingerprint: %s" % (blank, htmlParsed) + if htmlErrorFp: + value += "\n%shtml error message fingerprint: %s" % (blank, htmlErrorFp) if formatInfo: value += "\n%s" % formatInfo diff --git a/plugins/dbms/oracle.py b/plugins/dbms/oracle.py index 4922f606d..6a9a7c773 100644 --- a/plugins/dbms/oracle.py +++ b/plugins/dbms/oracle.py @@ -118,14 +118,16 @@ class OracleMap(Fingerprint, Enumeration, Filesystem, Takeover): def getFingerprint(self): + value = "back-end DBMS: " + if not conf.extensiveFp: - return "Oracle" + value += "Oracle" + return value - actVer = formatDBMSfp() - - blank = " " * 16 - formatInfo = None - value = "active fingerprint: %s" % actVer + actVer = formatDBMSfp() + blank = " " * 15 + formatInfo = None + value += "active fingerprint: %s" % actVer if self.banner: info = bannerParser(self.banner) @@ -136,10 +138,10 @@ class OracleMap(Fingerprint, Enumeration, Filesystem, Takeover): value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer) #passiveFuzzing() - htmlParsed = getHtmlErrorFp() + htmlErrorFp = getHtmlErrorFp() - if htmlParsed: - value += "\n%shtml error message fingerprint: %s" % (blank, htmlParsed) + if htmlErrorFp: + value += "\n%shtml error message fingerprint: %s" % (blank, htmlErrorFp) if formatInfo: value += "\n%s" % formatInfo diff --git a/plugins/dbms/postgresql.py b/plugins/dbms/postgresql.py index dfe1a11be..2343a1cd4 100644 --- a/plugins/dbms/postgresql.py +++ b/plugins/dbms/postgresql.py @@ -118,14 +118,16 @@ class PostgreSQLMap(Fingerprint, Enumeration, Filesystem, Takeover): def getFingerprint(self): + value = "back-end DBMS: " + if not conf.extensiveFp: - return "PostgreSQL" + value += "PostgreSQL" + return value - actVer = formatDBMSfp() - - blank = " " * 16 - formatInfo = None - value = "active fingerprint: %s" % actVer + actVer = formatDBMSfp() + blank = " " * 15 + formatInfo = None + value += "active fingerprint: %s" % actVer if self.banner: info = bannerParser(self.banner) @@ -136,10 +138,10 @@ class PostgreSQLMap(Fingerprint, Enumeration, Filesystem, Takeover): value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer) #passiveFuzzing() - htmlParsed = getHtmlErrorFp() + htmlErrorFp = getHtmlErrorFp() - if htmlParsed: - value += "\n%shtml error message fingerprint: %s" % (blank, htmlParsed) + if htmlErrorFp: + value += "\n%shtml error message fingerprint: %s" % (blank, htmlErrorFp) if formatInfo: value += "\n%s" % formatInfo diff --git a/xml/banner/generic.xml b/xml/banner/generic.xml index afc762df9..cf460474f 100644 --- a/xml/banner/generic.xml +++ b/xml/banner/generic.xml @@ -2,7 +2,7 @@ - +