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

Skip to content

Commit 7d07248

Browse files
committed
Major enhancement to the engine to parse XML files and matches on DBMS banner
and HTTP response headers. Initial web application technology fingerprint (for the moment based only on X-Powered-By HTTP response header and not shown yet to the user). Minor layout adjustments.
1 parent 66fb3c3 commit 7d07248

13 files changed

Lines changed: 222 additions & 133 deletions

File tree

lib/core/common.py

Lines changed: 32 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,11 @@ def formatDBMSfp(versions=None):
130130
return "%s %s" % (kb.dbms, " and ".join([version for version in versions]))
131131

132132

133-
def formatOSfp(info):
133+
def __formatOSfpString(values):
134+
return " or ".join([v for v in values])
135+
136+
137+
def formatOSfp():
134138
"""
135139
This function format the back-end operating system fingerprint value
136140
and return its values formatted as a human readable string.
@@ -142,31 +146,40 @@ def formatOSfp(info):
142146

143147
infoStr = ""
144148

145-
# Example of 'info' dictionary:
149+
# Examples of kb.bannerFp dictionary:
150+
#
146151
# {
147-
# 'distrib': 'Ubuntu',
148-
# 'release': '8.10',
149-
# 'codename': 'Intrepid',
150-
# 'version': '5.0.67',
151-
# 'type': 'Linux'
152+
# "distrib": set(["2000"]),
153+
# "dbmsVersion": "8.00.194",
154+
# "dbmsRelease": "2000",
155+
# "dbmsServicePack": "0",
156+
# "type": set(["Windows"])
157+
# }
158+
#
159+
# {
160+
# "distrib": set(["Ubuntu"]),
161+
# "release": set(["8.10"]),
162+
# "codename": set(["Intrepid"]),
163+
# "version": "5.0.67",
164+
# "type": set(["Linux"])
152165
# }
153166

154-
if not info or 'type' not in info:
167+
if not kb.bannerFp or "type" not in kb.bannerFp:
155168
return infoStr
156-
elif info['type'] != "None":
157-
infoStr += "back-end DBMS operating system: %s" % info['type']
169+
else:
170+
infoStr += "back-end DBMS operating system: %s" % __formatOSfpString(kb.bannerFp["type"])
158171

159-
if 'distrib' in info and info['distrib'] != "None":
160-
infoStr += " %s" % info['distrib']
172+
if "distrib" in kb.bannerFp:
173+
infoStr += " %s" % __formatOSfpString(kb.bannerFp["distrib"])
161174

162-
if 'release' in info and info['release'] != "None":
163-
infoStr += " %s" % info['release']
175+
if "release" in kb.bannerFp:
176+
infoStr += " %s" % __formatOSfpString(kb.bannerFp["release"])
164177

165-
if 'sp' in info and info['sp'] != "None":
166-
infoStr += " %s" % info['sp']
178+
if "sp" in kb.bannerFp:
179+
infoStr += " %s" % __formatOSfpString(kb.bannerFp["sp"])
167180

168-
if 'codename' in info and info['codename'] != "None":
169-
infoStr += " (%s)" % info['codename']
181+
if "codename" in kb.bannerFp:
182+
infoStr += " (%s)" % __formatOSfpString(kb.bannerFp["codename"])
170183

171184
return infoStr
172185

@@ -248,7 +261,7 @@ def getDirectories():
248261
if kb.docRoot:
249262
directories.add(kb.docRoot)
250263

251-
pagePath = re.search('^/(.*)/', conf.path)
264+
pagePath = re.search("^/(.*)/", conf.path)
252265

253266
if kb.docRoot and pagePath:
254267
pagePath = pagePath.groups()[0]

lib/core/option.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -453,6 +453,7 @@ def __setKnowledgeBaseAttributes():
453453
kb.dbms = None
454454
kb.dbmsDetected = False
455455
kb.dbmsVersion = None
456+
kb.bannerFp = {}
456457
kb.headersFp = {}
457458
kb.htmlFp = []
458459
kb.injParameter = None

lib/parse/banner.py

Lines changed: 41 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -44,11 +44,24 @@ class BannerHandler(ContentHandler):
4444
def __init__(self, banner):
4545
self.__banner = sanitizeStr(banner)
4646

47-
self.__regexp = None
48-
self.__match = None
49-
self.__position = None
47+
self.__regexp = None
48+
self.__match = None
49+
self.__version = None
5050

51-
self.info = {}
51+
52+
def __feedInfo(self, key, value):
53+
value = sanitizeStr(value)
54+
55+
if value in ( None, "None" ):
56+
return
57+
58+
if key == "version":
59+
kb.bannerFp[key] = value
60+
else:
61+
if key not in kb.bannerFp.keys():
62+
kb.bannerFp[key] = set()
63+
64+
kb.bannerFp[key].add(value)
5265

5366

5467
def startElement(self, name, attrs):
@@ -57,22 +70,23 @@ def startElement(self, name, attrs):
5770
self.__match = re.search(self.__regexp, self.__banner, re.I | re.M)
5871

5972
if name == "info" and self.__match:
60-
self.__position = sanitizeStr(attrs.get("version"))
61-
self.__sp = sanitizeStr(attrs.get("sp"))
73+
self.__feedInfo("type", attrs.get("type"))
74+
self.__feedInfo("distrib", attrs.get("distrib"))
75+
self.__feedInfo("release", attrs.get("release"))
76+
self.__feedInfo("codename", attrs.get("codename"))
6277

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"))
78+
self.__version = sanitizeStr(attrs.get("version"))
79+
self.__sp = sanitizeStr(attrs.get("sp"))
6780

68-
if self.__position.isdigit():
69-
self.info['version'] = self.__match.group(int(self.__position))
81+
if self.__version.isdigit():
82+
self.__feedInfo("version", self.__match.group(int(self.__version)))
7083

7184
if self.__sp.isdigit():
72-
self.info['sp'] = "Service Pack %s" % self.__match.group(int(self.__sp))
85+
self.__feedInfo("sp", "Service Pack %s" % self.__match.group(int(self.__sp)))
7386

74-
self.__match = None
75-
self.__position = None
87+
self.__regexp = None
88+
self.__match = None
89+
self.__version = None
7690

7791

7892
class MSSQLBannerHandler(ContentHandler):
@@ -90,7 +104,14 @@ def __init__(self, banner):
90104
self.__version = ""
91105
self.__servicePack = ""
92106

93-
self.info = {}
107+
108+
def __feedInfo(self, key, value):
109+
value = sanitizeStr(value)
110+
111+
if value in ( None, "None" ):
112+
return
113+
114+
kb.bannerFp[key] = value
94115

95116

96117
def startElement(self, name, attrs):
@@ -114,9 +135,9 @@ def characters(self, data):
114135
def endElement(self, name):
115136
if name == "signature":
116137
if re.search(" %s[\.\ ]+" % self.__version, self.__banner):
117-
self.info['dbmsRelease'] = self.__release
118-
self.info['dbmsVersion'] = self.__version
119-
self.info['dbmsServicePack'] = self.__servicePack
138+
self.__feedInfo("dbmsRelease", self.__release)
139+
self.__feedInfo("dbmsVersion", self.__version)
140+
self.__feedInfo("dbmsServicePack", self.__servicePack)
120141

121142
self.__version = ""
122143
self.__servicePack = ""
@@ -137,9 +158,6 @@ def bannerParser(banner):
137158
DBMS banner based upon the data in XML file
138159
"""
139160

140-
banner = sanitizeStr(banner)
141-
info = {}
142-
143161
if kb.dbms == "Microsoft SQL Server":
144162
xmlfile = paths.MSSQL_XML
145163
elif kb.dbms == "MySQL":
@@ -154,24 +172,9 @@ def bannerParser(banner):
154172
if kb.dbms == "Microsoft SQL Server":
155173
handler = MSSQLBannerHandler(banner)
156174
parse(xmlfile, handler)
157-
info = handler.info
158-
159175
handler = BannerHandler(banner)
160176
parse(paths.GENERIC_XML, handler)
161-
162-
for title, value in handler.info.items():
163-
info[title] = value
164177
else:
165178
handler = BannerHandler(banner)
166179
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"]
176-
177-
return info
180+
parse(paths.GENERIC_XML, handler)

lib/parse/headers.py

Lines changed: 77 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,68 @@
2626

2727
import re
2828

29+
from xml.sax import parse
30+
from xml.sax.handler import ContentHandler
31+
2932
from lib.core.common import checkFile
33+
from lib.core.common import sanitizeStr
3034
from lib.core.data import kb
3135
from lib.core.data import paths
32-
from lib.parse.banner import BannerHandler
36+
37+
38+
class HeadersHandler(ContentHandler):
39+
"""
40+
This class defines methods to parse and extract information from
41+
the given HTTP header based upon the data in XML file
42+
"""
43+
44+
def __init__(self, header):
45+
self.__header = sanitizeStr(header)
46+
47+
self.__regexp = None
48+
self.__match = None
49+
self.__techVersion = None
50+
51+
52+
def __feedInfo(self, key, value):
53+
value = sanitizeStr(value)
54+
55+
if value in ( None, "None" ):
56+
return
57+
58+
if key == "techVersion":
59+
kb.headersFp[key] = value
60+
else:
61+
if key not in kb.headersFp.keys():
62+
kb.headersFp[key] = set()
63+
64+
kb.headersFp[key].add(value)
65+
66+
67+
def startElement(self, name, attrs):
68+
if name == "regexp":
69+
self.__regexp = sanitizeStr(attrs.get("value"))
70+
self.__match = re.search(self.__regexp, self.__header, re.I | re.M)
71+
72+
if name == "info" and self.__match:
73+
self.__feedInfo("type", attrs.get("type"))
74+
self.__feedInfo("distrib", attrs.get("distrib"))
75+
self.__feedInfo("release", attrs.get("release"))
76+
self.__feedInfo("codename", attrs.get("codename"))
77+
self.__feedInfo("technology", attrs.get("codename"))
78+
79+
self.__techVersion = sanitizeStr(attrs.get("tech_version"))
80+
self.__sp = sanitizeStr(attrs.get("sp"))
81+
82+
if self.__techVersion.isdigit():
83+
self.__feedInfo("techVersion", self.__match.group(int(self.__techVersion)))
84+
85+
if self.__sp.isdigit():
86+
self.__feedInfo("sp", "Service Pack %s" % self.__match.group(int(self.__sp)))
87+
88+
self.__regexp = None
89+
self.__match = None
90+
self.__techVersion = None
3391

3492

3593
def headersParser(headers):
@@ -39,17 +97,23 @@ def headersParser(headers):
3997
and the web application technology
4098
"""
4199

42-
topHeaders = (
43-
"cookie",
44-
"microsoftsharepointteamservices",
45-
"server",
46-
"servlet-engine",
47-
"www-authenticate",
48-
"x-aspnet-version",
49-
"x-powered-by",
50-
)
100+
# TODO: ahead here
101+
topHeaders = {
102+
#"cookie": "%s/cookie.xml" % paths.SQLMAP_XML_BANNER_PATH,
103+
#"microsoftsharepointteamservices": "%s/microsoftsharepointteamservices.xml" % paths.SQLMAP_XML_BANNER_PATH,
104+
#"server": "%s/server.xml" % paths.SQLMAP_XML_BANNER_PATH,
105+
#"servlet-engine": "%s/servlet-engine.xml" % paths.SQLMAP_XML_BANNER_PATH,
106+
#"set-cookie": "%s/cookie.xml" % paths.SQLMAP_XML_BANNER_PATH,
107+
#"www-authenticate": "%s/www-authenticate.xml" % paths.SQLMAP_XML_BANNER_PATH,
108+
#"x-aspnet-version": "%s/x-aspnet-version.xml" % paths.SQLMAP_XML_BANNER_PATH,
109+
"x-powered-by": "%s/x-powered-by.xml" % paths.SQLMAP_XML_BANNER_PATH,
110+
}
51111

52112
for header in headers:
53-
if header in topHeaders:
54-
# TODO: fill me
55-
pass
113+
if header in topHeaders.keys():
114+
value = headers[header]
115+
xmlfile = topHeaders[header]
116+
checkFile(xmlfile)
117+
handler = HeadersHandler(value)
118+
parse(xmlfile, handler)
119+
parse(paths.GENERIC_XML, handler)

plugins/dbms/mssqlserver.py

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -124,15 +124,13 @@ def escape(expression):
124124

125125
def getFingerprint(self):
126126
value = ""
127-
info = None
128127
formatInfo = None
129128

130129
if self.banner:
131-
info = bannerParser(self.banner)
132-
formatInfo = formatOSfp(info)
130+
formatInfo = formatOSfp()
133131

134-
if formatInfo:
135-
value += "%s\n" % formatInfo
132+
if formatInfo:
133+
value += "%s\n" % formatInfo
136134

137135
value += "back-end DBMS: "
138136
actVer = formatDBMSfp()
@@ -145,10 +143,10 @@ def getFingerprint(self):
145143
formatInfo = None
146144
value += "active fingerprint: %s" % actVer
147145

148-
if info:
149-
release = info["dbmsRelease"]
150-
version = info["dbmsVersion"]
151-
servicepack = info["dbmsServicePack"]
146+
if kb.bannerFp:
147+
release = kb.bannerFp["dbmsRelease"]
148+
version = kb.bannerFp["dbmsVersion"]
149+
servicepack = kb.bannerFp["dbmsServicePack"]
152150

153151
if release and version and servicepack:
154152
banVer = "Microsoft SQL Server %s " % release
@@ -169,8 +167,7 @@ def checkDbms(self):
169167
if conf.dbms in MSSQL_ALIASES and kb.dbmsVersion and kb.dbmsVersion[0].isdigit():
170168
setDbms("Microsoft SQL Server %s" % kb.dbmsVersion[0])
171169

172-
if conf.getBanner:
173-
self.banner = inject.getValue("@@VERSION")
170+
self.getPrematureBanner("@@VERSION")
174171

175172
if not conf.extensiveFp:
176173
return True
@@ -197,8 +194,7 @@ def checkDbms(self):
197194
else:
198195
setDbms("Microsoft SQL Server")
199196

200-
if conf.getBanner:
201-
self.banner = inject.getValue("@@VERSION")
197+
self.getPrematureBanner("@@VERSION")
202198

203199
return True
204200
else:

0 commit comments

Comments
 (0)