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

Skip to content

Commit fa0507a

Browse files
committed
Minor enhancement to fingerprint the back-end DBMS operating system (type,
version, release, distribution, codename and service pack) by parsing the DBMS banner value when both -f and -b are provided: adapted the code and added XML files defining regular expressions for matching. Example of the -f -b output now on MySQL 5.0.67 running on latest Ubuntu: --8<-- back-end DBMS: active fingerprint: MySQL >= 5.0.38 and < 5.1.2 comment injection fingerprint: MySQL 5.0.67 banner parsing fingerprint: MySQL 5.0.67 html error message fingerprint: MySQL back-end DBMS operating system: Linux Ubuntu 8.10 (Intrepid) --8<--
1 parent 84cbc60 commit fa0507a

15 files changed

Lines changed: 372 additions & 69 deletions

File tree

doc/ChangeLog

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
sqlmap (0.6.3-1) stable; urgency=low
22

3+
* Major bug fix to correctly handle httplib.BadStatusLine exception;
34
* Minor enhancement to support stacked queries which will be used
45
sometimes by takeover functionality and time based blind SQL injection
56
technique;
67
* Minor enhancement to be able to specify the number of seconds to wait
78
between each HTTP request;
89
* Minor enhancement to be able to enumerate table columns and dump table
9-
entries also if the database name is not provided by using the current
10-
database on MySQL and MSSQL, the 'public' scheme on PostgreSQL and the
11-
'USERS' TABLESPACE_NAME on Oracle;
10+
entries, also when the database name is not provided, by using the
11+
current database on MySQL and Microsoft SQL Server, the 'public'
12+
scheme on PostgreSQL and the 'USERS' TABLESPACE_NAME on Oracle;
13+
* Minor improvement to set by default in all HTTP requests the standard
14+
HTTP headers (Accept, Accept-Encoding, etc);
1215
* Minor improvements to sqlmap Debian package files: sqlmap uploaded
1316
to official Debian project repository;
1417
* Minor bug fix to handle session.error and session.timeout in HTTP

lib/controller/handler.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ def setHandler():
5555

5656
for dbmsAliases, dbmsEntry in dbmsMap:
5757
if conf.dbms and conf.dbms not in dbmsAliases:
58-
debugMsg = "skipping to test for %s" % dbmsNames[count]
58+
debugMsg = "skipping test for %s" % dbmsNames[count]
5959
logger.debug(debugMsg)
6060
count += 1
6161
continue

lib/core/common.py

Lines changed: 59 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ def paramToDict(place, parameters=None):
112112
return testableParameters
113113

114114

115-
def formatFingerprint(versions=None):
115+
def formatDBMSfp(versions=None):
116116
"""
117117
This function format the back-end DBMS fingerprint value and return its
118118
values formatted as a human readable string.
@@ -130,6 +130,47 @@ def formatFingerprint(versions=None):
130130
return "%s %s" % (kb.dbms, " and ".join([version for version in versions]))
131131

132132

133+
def formatOSfp(info):
134+
"""
135+
This function format the back-end operating system fingerprint value
136+
and return its values formatted as a human readable string.
137+
138+
@return: detected back-end operating system based upon fingerprint
139+
techniques.
140+
@rtype: C{str}
141+
"""
142+
143+
infoStr = ""
144+
145+
# Example of 'info' dictionary:
146+
# {
147+
# 'distrib': 'Ubuntu',
148+
# 'release': '8.10',
149+
# 'codename': 'Intrepid',
150+
# 'version': '5.0.67',
151+
# 'type': 'Linux'
152+
# }
153+
154+
if not info or 'type' not in info:
155+
return infoStr
156+
elif info['type'] != "None":
157+
infoStr += "back-end DBMS operating system: %s" % info['type']
158+
159+
if 'distrib' in info and info['distrib'] != "None":
160+
infoStr += " %s" % info['distrib']
161+
162+
if 'release' in info and info['release'] != "None":
163+
infoStr += " %s" % info['release']
164+
165+
if 'sp' in info and info['sp'] != "None":
166+
infoStr += " %s" % info['sp']
167+
168+
if 'codename' in info and info['codename'] != "None":
169+
infoStr += " (%s)" % info['codename']
170+
171+
return infoStr
172+
173+
133174
def getHtmlErrorFp():
134175
"""
135176
This function parses the knowledge base htmlFp list and return its
@@ -442,20 +483,25 @@ def cleanQuery(query):
442483

443484
def setPaths():
444485
# sqlmap paths
445-
paths.SQLMAP_SHELL_PATH = "%s/shell" % paths.SQLMAP_ROOT_PATH
446-
paths.SQLMAP_TXT_PATH = "%s/txt" % paths.SQLMAP_ROOT_PATH
447-
paths.SQLMAP_XML_PATH = "%s/xml" % paths.SQLMAP_ROOT_PATH
448-
paths.SQLMAP_OUTPUT_PATH = "%s/output" % paths.SQLMAP_ROOT_PATH
449-
paths.SQLMAP_DUMP_PATH = paths.SQLMAP_OUTPUT_PATH + "/%s/dump"
450-
paths.SQLMAP_FILES_PATH = paths.SQLMAP_OUTPUT_PATH + "/%s/files"
486+
paths.SQLMAP_SHELL_PATH = "%s/shell" % paths.SQLMAP_ROOT_PATH
487+
paths.SQLMAP_TXT_PATH = "%s/txt" % paths.SQLMAP_ROOT_PATH
488+
paths.SQLMAP_XML_PATH = "%s/xml" % paths.SQLMAP_ROOT_PATH
489+
paths.SQLMAP_XML_BANNER_PATH = "%s/banner" % paths.SQLMAP_XML_PATH
490+
paths.SQLMAP_OUTPUT_PATH = "%s/output" % paths.SQLMAP_ROOT_PATH
491+
paths.SQLMAP_DUMP_PATH = paths.SQLMAP_OUTPUT_PATH + "/%s/dump"
492+
paths.SQLMAP_FILES_PATH = paths.SQLMAP_OUTPUT_PATH + "/%s/files"
451493

452494
# sqlmap files
453-
paths.SQLMAP_HISTORY = "%s/.sqlmap_history" % paths.SQLMAP_ROOT_PATH
454-
paths.SQLMAP_CONFIG = "%s/sqlmap-%s.conf" % (paths.SQLMAP_ROOT_PATH, randomStr())
455-
paths.FUZZ_VECTORS = "%s/fuzz_vectors.txt" % paths.SQLMAP_TXT_PATH
456-
paths.ERRORS_XML = "%s/errors.xml" % paths.SQLMAP_XML_PATH
457-
paths.MSSQL_XML = "%s/mssql.xml" % paths.SQLMAP_XML_PATH
458-
paths.QUERIES_XML = "%s/queries.xml" % paths.SQLMAP_XML_PATH
495+
paths.SQLMAP_HISTORY = "%s/.sqlmap_history" % paths.SQLMAP_ROOT_PATH
496+
paths.SQLMAP_CONFIG = "%s/sqlmap-%s.conf" % (paths.SQLMAP_ROOT_PATH, randomStr())
497+
paths.FUZZ_VECTORS = "%s/fuzz_vectors.txt" % paths.SQLMAP_TXT_PATH
498+
paths.ERRORS_XML = "%s/errors.xml" % paths.SQLMAP_XML_PATH
499+
paths.QUERIES_XML = "%s/queries.xml" % paths.SQLMAP_XML_PATH
500+
paths.GENERIC_XML = "%s/generic.xml" % paths.SQLMAP_XML_BANNER_PATH
501+
paths.MSSQL_XML = "%s/mssql.xml" % paths.SQLMAP_XML_BANNER_PATH
502+
paths.MYSQL_XML = "%s/mysql.xml" % paths.SQLMAP_XML_BANNER_PATH
503+
paths.ORACLE_XML = "%s/oracle.xml" % paths.SQLMAP_XML_BANNER_PATH
504+
paths.PGSQL_XML = "%s/postgresql.xml" % paths.SQLMAP_XML_BANNER_PATH
459505

460506

461507
def weAreFrozen():

lib/parse/banner.py

Lines changed: 86 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -31,25 +31,67 @@
3131

3232
from lib.core.common import checkFile
3333
from lib.core.common import sanitizeStr
34+
from lib.core.data import kb
35+
from lib.core.data import paths
3436

3537

36-
class bannerHandler(ContentHandler):
38+
class BannerHandler(ContentHandler):
3739
"""
3840
This class defines methods to parse and extract information from
3941
the given DBMS banner based upon the data in XML file
4042
"""
4143

44+
def __init__(self, banner):
45+
self.__banner = sanitizeStr(banner)
46+
47+
self.__regexp = None
48+
self.__match = None
49+
self.__position = None
50+
51+
self.info = {}
52+
53+
54+
def startElement(self, name, attrs):
55+
if name == "regexp":
56+
self.__regexp = sanitizeStr(attrs.get("value"))
57+
self.__match = re.search(self.__regexp, self.__banner, re.I | re.M)
58+
59+
if name == "info" and self.__match:
60+
self.__position = sanitizeStr(attrs.get("version"))
61+
self.__sp = sanitizeStr(attrs.get("sp"))
62+
63+
self.info['type'] = sanitizeStr(attrs.get("type"))
64+
self.info['distrib'] = sanitizeStr(attrs.get("distrib"))
65+
self.info['release'] = sanitizeStr(attrs.get("release"))
66+
self.info['codename'] = sanitizeStr(attrs.get("codename"))
67+
68+
if self.__position.isdigit():
69+
self.info['version'] = self.__match.group(int(self.__position))
70+
71+
if self.__sp.isdigit():
72+
self.info['sp'] = "Service Pack %s" % self.__match.group(int(self.__sp))
73+
74+
self.__match = None
75+
self.__position = None
76+
77+
78+
class MSSQLBannerHandler(ContentHandler):
79+
"""
80+
This class defines methods to parse and extract information from the
81+
given Microsoft SQL Server banner based upon the data in XML file
82+
"""
83+
4284
def __init__(self, banner):
4385
self.__banner = sanitizeStr(banner)
44-
self.release = None
45-
self.version = None
46-
self.servicePack = None
86+
4787
self.__inVersion = False
4888
self.__inServicePack = False
4989
self.__release = None
5090
self.__version = ""
5191
self.__servicePack = ""
5292

93+
self.info = {}
94+
5395

5496
def startElement(self, name, attrs):
5597
if name == "signatures":
@@ -72,9 +114,9 @@ def characters(self, data):
72114
def endElement(self, name):
73115
if name == "signature":
74116
if re.search(" %s[\.\ ]+" % self.__version, self.__banner):
75-
self.release = self.__release
76-
self.version = self.__version
77-
self.servicePack = self.__servicePack
117+
self.info['dbmsRelease'] = self.__release
118+
self.info['dbmsVersion'] = self.__version
119+
self.info['dbmsServicePack'] = self.__servicePack
78120

79121
self.__version = ""
80122
self.__servicePack = ""
@@ -89,16 +131,47 @@ def endElement(self, name):
89131
self.__servicePack = self.__servicePack.replace(" ", "")
90132

91133

92-
93-
def bannerParser(banner, xmlfile):
134+
def bannerParser(banner):
94135
"""
95136
This function calls a class to extract information from the given
96137
DBMS banner based upon the data in XML file
97138
"""
98139

99-
checkFile(xmlfile)
100140
banner = sanitizeStr(banner)
101-
handler = bannerHandler(banner)
102-
parse(xmlfile, handler)
141+
info = {}
142+
143+
if kb.dbms == "Microsoft SQL Server":
144+
xmlfile = paths.MSSQL_XML
145+
elif kb.dbms == "MySQL":
146+
xmlfile = paths.MYSQL_XML
147+
elif kb.dbms == "Oracle":
148+
xmlfile = paths.ORACLE_XML
149+
elif kb.dbms == "PostgreSQL":
150+
xmlfile = paths.PGSQL_XML
151+
152+
checkFile(xmlfile)
153+
154+
if kb.dbms == "Microsoft SQL Server":
155+
handler = MSSQLBannerHandler(banner)
156+
parse(xmlfile, handler)
157+
info = handler.info
158+
159+
handler = BannerHandler(banner)
160+
parse(paths.GENERIC_XML, handler)
161+
162+
for title, value in handler.info.items():
163+
info[title] = value
164+
else:
165+
handler = BannerHandler(banner)
166+
parse(xmlfile, handler)
167+
info = handler.info
168+
169+
if "type" not in info or info["type"] == "None":
170+
parse(paths.GENERIC_XML, handler)
171+
info["type"] = handler.info["type"]
172+
173+
if "distrib" not in info or info["distrib"] == "None":
174+
parse(paths.GENERIC_XML, handler)
175+
info["distrib"] = handler.info["distrib"]
103176

104-
return handler.release, handler.version, handler.servicePack
177+
return info

lib/parse/cmdline.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ def cmdLineParser():
129129

130130
fingerprint.add_option("-f", "--fingerprint", dest="extensiveFp",
131131
action="store_true",
132-
help="Perform an extensive database fingerprint")
132+
help="Perform an extensive DBMS version fingerprint")
133133

134134
# Enumeration options
135135
enumeration = OptionGroup(parser, "Enumeration", "These options can "

plugins/dbms/mssqlserver.py

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,14 +28,14 @@
2828

2929
from lib.core.agent import agent
3030
from lib.core.common import dataToStdout
31-
from lib.core.common import formatFingerprint
31+
from lib.core.common import formatDBMSfp
32+
from lib.core.common import formatOSfp
3233
from lib.core.common import getHtmlErrorFp
3334
from lib.core.common import randomInt
3435
from lib.core.common import readInput
3536
from lib.core.data import conf
3637
from lib.core.data import kb
3738
from lib.core.data import logger
38-
from lib.core.data import paths
3939
from lib.core.data import queries
4040
from lib.core.exception import sqlmapNoneDataException
4141
from lib.core.exception import sqlmapSyntaxException
@@ -124,16 +124,21 @@ def escape(expression):
124124

125125

126126
def getFingerprint(self):
127-
actVer = formatFingerprint()
127+
actVer = formatDBMSfp()
128128

129129
if not conf.extensiveFp:
130130
return actVer
131131

132-
blank = " " * 16
133-
value = "active fingerprint: %s" % actVer
132+
blank = " " * 16
133+
formatInfo = None
134+
value = "active fingerprint: %s" % actVer
134135

135136
if self.banner:
136-
release, version, servicepack = bannerParser(self.banner, paths.MSSQL_XML)
137+
info = bannerParser(self.banner)
138+
release = info["dbmsRelease"]
139+
version = info["dbmsVersion"]
140+
servicepack = info["dbmsServicePack"]
141+
formatInfo = formatOSfp(info)
137142

138143
if release and version and servicepack:
139144
banVer = "Microsoft SQL Server %s " % release
@@ -148,6 +153,9 @@ def getFingerprint(self):
148153
if htmlParsed:
149154
value += "\n%shtml error message fingerprint: %s" % (blank, htmlParsed)
150155

156+
if formatInfo:
157+
value += "\n%s" % formatInfo
158+
151159
return value
152160

153161

plugins/dbms/mysql.py

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@
2828

2929
from lib.core.agent import agent
3030
from lib.core.common import fileToStr
31-
from lib.core.common import formatFingerprint
31+
from lib.core.common import formatDBMSfp
32+
from lib.core.common import formatOSfp
3233
from lib.core.common import getDirectories
3334
from lib.core.common import getHtmlErrorFp
3435
from lib.core.common import randomInt
@@ -43,6 +44,7 @@
4344
from lib.core.settings import MYSQL_SYSTEM_DBS
4445
from lib.core.shell import autoCompletion
4546
from lib.core.unescaper import unescaper
47+
from lib.parse.banner import bannerParser
4648
from lib.request import inject
4749
from lib.request.connect import Connect as Request
4850
#from lib.utils.fuzzer import passiveFuzzing
@@ -180,26 +182,28 @@ def __commentCheck(self):
180182

181183

182184
def getFingerprint(self):
183-
actVer = formatFingerprint()
185+
actVer = formatDBMSfp()
184186

185187
if not conf.extensiveFp:
186188
return actVer
187189

188-
blank = " " * 16
189-
value = "active fingerprint: %s" % actVer
190-
comVer = self.__commentCheck()
190+
comVer = self.__commentCheck()
191+
blank = " " * 16
192+
formatInfo = None
193+
value = "active fingerprint: %s" % actVer
191194

192195
if comVer:
193-
comVer = formatFingerprint([comVer])
196+
comVer = formatDBMSfp([comVer])
194197
value += "\n%scomment injection fingerprint: %s" % (blank, comVer)
195198

196199
if self.banner:
197-
banVer = re.search("^([\d\.]+)", self.banner)
198-
banVer = banVer.groups()[0]
200+
info = bannerParser(self.banner)
201+
formatInfo = formatOSfp(info)
202+
203+
banVer = info['version']
199204
if re.search("-log$", self.banner):
200205
banVer += ", logging enabled"
201-
banVer = formatFingerprint([banVer])
202-
206+
banVer = formatDBMSfp([banVer])
203207
value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer)
204208

205209
#passiveFuzzing()
@@ -208,6 +212,9 @@ def getFingerprint(self):
208212
if htmlParsed:
209213
value += "\n%shtml error message fingerprint: %s" % (blank, htmlParsed)
210214

215+
if formatInfo:
216+
value += "\n%s" % formatInfo
217+
211218
return value
212219

213220

0 commit comments

Comments
 (0)