PageRenderTime 68ms CodeModel.GetById 27ms RepoModel.GetById 1ms app.codeStats 1ms

/nselib/mssql.lua

https://github.com/prakashgamit/nmap
Lua | 3173 lines | 1968 code | 518 blank | 687 comment | 313 complexity | 4f8391b5a235ef7c0a2568f454d42a87 MD5 | raw file
Possible License(s): BSD-3-Clause, GPL-2.0, LGPL-2.0, LGPL-2.1

Large files files are truncated, but you can click here to view the full file

  1. ---
  2. -- MSSQL Library supporting a very limited subset of operations.
  3. --
  4. -- The library was designed and tested against Microsoft SQL Server 2005.
  5. -- However, it should work with versions 7.0, 2000, 2005, 2008 and 2012.
  6. -- Only a minimal amount of parsers have been added for tokens, column types
  7. -- and column data in order to support the first scripts.
  8. --
  9. -- The code has been implemented based on traffic analysis and the following
  10. -- documentation:
  11. -- * SSRP Protocol Specification: http://msdn.microsoft.com/en-us/library/cc219703.aspx
  12. -- * TDS Protocol Specification: http://msdn.microsoft.com/en-us/library/dd304523.aspx
  13. -- * TDS Protocol Documentation: http://www.freetds.org/tds.html.
  14. -- * The JTDS source code: http://jtds.sourceforge.net/index.html.
  15. --
  16. -- * SSRP: Class that handles communication over the SQL Server Resolution Protocol, used for identifying instances on a host.
  17. -- * ColumnInfo: Class containing parsers for column types which are present before the row data in all query response packets. The column information contains information relevant to the data type used to hold the data eg. precision, character sets, size etc.
  18. -- * ColumnData: Class containing parsers for the actual column information.
  19. -- * Token: Class containing parsers for tokens returned in all TDS responses. A server response may hold one or more tokens with information from the server. Each token has a type which has a number of type specific fields.
  20. -- * QueryPacket: Class used to hold a query and convert it to a string suitable for transmission over a socket.
  21. -- * LoginPacket: Class used to hold login specific data which can easily be converted to a string suitable for transmission over a socket.
  22. -- * PreLoginPacket: Class used to (partially) implement the TDS PreLogin packet
  23. -- * TDSStream: Class that handles communication over the Tabular Data Stream protocol used by SQL serve. It is used to transmit the the Query- and Login-packets to the server.
  24. -- * Helper: Class which facilitates the use of the library by through action oriented functions with descriptive names.
  25. -- * Util: A "static" class containing mostly character and type conversion functions.
  26. --
  27. -- The following sample code illustrates how scripts can use the Helper class
  28. -- to interface the library:
  29. --
  30. -- <code>
  31. -- local helper = mssql.Helper:new()
  32. -- status, result = helper:Connect( host, port )
  33. -- status, result = helper:Login( username, password, "temdpb", host.ip )
  34. -- status, result = helper:Query( "SELECT name FROM master..syslogins" )
  35. -- helper:Disconnect()
  36. -- </code>
  37. --
  38. -- The following sample code illustrates how scripts can use the Helper class
  39. -- with pre-discovered instances (e.g. by <code>ms-sql-discover</code> or <code>broadcast-ms-sql-discover</code>):
  40. --
  41. -- <code>
  42. -- local instance = mssql.Helper.GetDiscoveredInstances( host, port )
  43. -- if ( instance ) then
  44. -- local helper = mssql.Helper:new()
  45. -- status, result = helper:ConnectEx( instance )
  46. -- status, result = helper:LoginEx( instance )
  47. -- status, result = helper:Query( "SELECT name FROM master..syslogins" )
  48. -- helper:Disconnect()
  49. -- end
  50. -- </code>
  51. --
  52. -- Known limitations:
  53. -- * The library does not support SSL. The foremost reason being the akward choice of implementation where the SSL handshake is performed within the TDS data block. By default, servers support connections over non SSL connections though.
  54. -- * Version 7 and ONLY version 7 of the protocol is supported. This should cover Microsoft SQL Server 7.0 and later.
  55. -- * TDS Responses contain one or more response tokens which are parsed based on their type. The supported tokens are listed in the <code>TokenType</code> table and their respective parsers can be found in the <code>Token</code> class. Note that some token parsers are not fully implemented and simply move the offset the right number of bytes to continue processing of the response.
  56. -- * The library only supports a limited subsets of datatypes and will abort execution and return an error if it detects an unsupported type. The supported data types are listed in the <code>DataTypes</code> table. In order to add additional data types a parser function has to be added to both the <code>ColumnInfo</code> and <code>ColumnData</code> class.
  57. -- * No functionality for languages, localization or characted codepages has been considered or implemented.
  58. -- * The library does database authentication only. No OS authentication or use of the integrated security model is supported.
  59. -- * Queries using SELECT, INSERT, DELETE and EXEC of procedures have been tested while developing scripts.
  60. --
  61. -- @copyright Same as Nmap--See http://nmap.org/book/man-legal.html
  62. --
  63. -- @author "Patrik Karlsson <patrik@cqure.net>, Chris Woodbury"
  64. --
  65. -- @args mssql.username The username to use to connect to SQL Server instances.
  66. -- This username is used by scripts taking actions that require
  67. -- authentication (e.g. <code>ms-sql-query</code>) This username (and its
  68. -- associated password) takes precedence over any credentials discovered
  69. -- by the <code>ms-sql-brute</code> and <code>ms-sql-empty-password</code>
  70. -- scripts.
  71. --
  72. -- @args mssql.password The password for <code>mssql.username</code>. If this
  73. -- argument is not given but <code>mssql.username</code>, a blank password
  74. -- is used.
  75. --
  76. -- @args mssql.instance-name The name of the instance to connect to.
  77. --
  78. -- @args mssql.instance-port The port of the instance to connect to.
  79. --
  80. -- @args mssql.instance-all Targets all SQL server instances discovered
  81. -- throught the browser service.
  82. --
  83. -- @args mssql.domain The domain against which to perform integrated
  84. -- authentication. When set, the scripts assume integrated authentication
  85. -- should be performed, rather than the default sql login.
  86. --
  87. -- @args mssql.protocol The protocol to use to connect to the instance. The
  88. -- protocol may be either <code>NP</code>,<code>Named Pipes</code> or
  89. -- <code>TCP</code>.
  90. --
  91. -- @args mssql.timeout How long to wait for SQL responses. This is a number
  92. -- followed by <code>ms</code> for milliseconds, <code>s</code> for
  93. -- seconds, <code>m</code> for minutes, or <code>h</code> for hours.
  94. -- Default: <code>30s</code>.
  95. --
  96. -- @args mssql.scanned-ports-only If set, the script will only connect
  97. -- to ports that were included in the Nmap scan. This may result in
  98. -- instances not being discovered, particularly if UDP port 1434 is not
  99. -- included. Additionally, instances that are found to be running on
  100. -- ports that were not scanned (e.g. if 1434/udp is in the scan and the
  101. -- SQL Server Browser service on that port reports an instance
  102. -- listening on 43210/tcp, which was not scanned) will be reported but
  103. -- will not be stored for use by other ms-sql-* scripts.
  104. local bin = require "bin"
  105. local bit = require "bit"
  106. local math = require "math"
  107. local match = require "match"
  108. local nmap = require "nmap"
  109. local os = require "os"
  110. local shortport = require "shortport"
  111. local smb = require "smb"
  112. local smbauth = require "smbauth"
  113. local stdnse = require "stdnse"
  114. local strbuf = require "strbuf"
  115. local string = require "string"
  116. local table = require "table"
  117. _ENV = stdnse.module("mssql", stdnse.seeall)
  118. -- Created 01/17/2010 - v0.1 - created by Patrik Karlsson <patrik@cqure.net>
  119. -- Revised 03/28/2010 - v0.2 - fixed incorrect token types. added 30 seconds timeout
  120. -- Revised 01/23/2011 - v0.3 - fixed parsing error in discovery code with patch
  121. -- from Chris Woodbury
  122. -- Revised 02/01/2011 - v0.4 - numerous changes and additions to support new
  123. -- functionality in ms-sql- scripts and to be more
  124. -- robust in parsing and handling data. (Chris Woodbury)
  125. -- Revised 02/19/2011 - v0.5 - numerous changes in script, library behaviour
  126. -- * huge improvements in version detection
  127. -- * added support for named pipes
  128. -- * added support for integrated NTLMv1 authentication
  129. --
  130. -- (Patrik Karlsson, Chris Woodbury)
  131. -- Revised 08/19/2012 - v0.6 - added multiple data types
  132. -- * added detection and handling of null values when processing query responses from the server
  133. -- * added DoneProc response token support
  134. --
  135. -- (Tom Sellers)
  136. -- Updated 10/01/2012 - v0.7 - added support for 2012 and later service packs for 2005, 2008 and 2008 R2 (Rob Nicholls)
  137. local HAVE_SSL, openssl = pcall(require, "openssl")
  138. do
  139. namedpipes = smb.namedpipes
  140. local arg = stdnse.get_script_args( "mssql.timeout" ) or "30s"
  141. local timeout, err = stdnse.parse_timespec(arg)
  142. if not timeout then
  143. error(err)
  144. end
  145. MSSQL_TIMEOUT = timeout
  146. SCANNED_PORTS_ONLY = false
  147. if ( stdnse.get_script_args( "mssql.scanned-ports-only" ) ) then
  148. SCANNED_PORTS_ONLY = true
  149. end
  150. end
  151. -- *************************************
  152. -- Informational Classes
  153. -- *************************************
  154. --- SqlServerInstanceInfo class
  155. SqlServerInstanceInfo =
  156. {
  157. instanceName = nil,
  158. version = nil,
  159. serverName = nil,
  160. isClustered = nil,
  161. host = nil,
  162. port = nil,
  163. pipeName = nil,
  164. new = function(self,o)
  165. o = o or {}
  166. setmetatable(o, self)
  167. self.__index = self
  168. return o
  169. end,
  170. -- Compares two SqlServerInstanceInfo objects and determines whether they
  171. -- refer to the same SQL Server instance, judging by a combination of host,
  172. -- port, named pipe information and instance name.
  173. __eq = function( self, other )
  174. local areEqual
  175. if ( not (self.host and other.host) ) then
  176. -- if they don't both have host information, we certainly can't say
  177. -- whether they're the same
  178. areEqual = false
  179. else
  180. areEqual = (self.host.ip == other.host.ip)
  181. end
  182. if (self.port and other.port) then
  183. areEqual = areEqual and ( other.port.number == self.port.number and
  184. other.port.protocol == self.port.protocol )
  185. elseif (self.pipeName and other.pipeName) then
  186. areEqual = areEqual and (self.pipeName == other.pipeName)
  187. elseif (self.instanceName and other.instanceName) then
  188. areEqual = areEqual and (self.instanceName == other.instanceName)
  189. else
  190. -- if we have neither port nor named pipe info nor instance names,
  191. -- we can't say whether they're the same
  192. areEqual = false
  193. end
  194. return areEqual
  195. end,
  196. --- Merges the data from one SqlServerInstanceInfo object into another. Each
  197. -- field in the first object is populated with the data from that field in
  198. -- second object if the first object's field is nil OR if <code>overwrite</code>
  199. -- is set to true. A special case is made for the <code>version</code> field,
  200. -- which is only overwritten in the second object has more reliable version
  201. -- information. The second object is not modified.
  202. Merge = function( self, other, overwrite )
  203. local mergeFields = { "host", "port", "instanceName", "version", "isClustered", "pipeName" }
  204. for _, fieldname in ipairs( mergeFields ) do
  205. -- Add values from other only if self doesn't have a value, or if overwrite is true
  206. if ( other[ fieldname ] ~= nil and (overwrite or self[ fieldname ] == nil) ) then
  207. self[ fieldname ] = other[ fieldname ]
  208. end
  209. end
  210. if (self.version and self.version.source == "SSRP" and
  211. other.version and other.version.Source == "SSNetLib") then
  212. self.version = other.version
  213. end
  214. end,
  215. --- Returns a name for the instance, based on the available information. This
  216. -- may take one of the following forms:
  217. -- * HOST\INSTANCENAME
  218. -- * PIPENAME
  219. -- * HOST:PORT
  220. GetName = function( self )
  221. if (self.instanceName) then
  222. return string.format( "%s\\%s", self.host.ip or self.serverName or "[nil]", self.instanceName or "[nil]" )
  223. elseif (self.pipeName) then
  224. return string.format( "%s", self.pipeName )
  225. else
  226. return string.format( "%s:%s", self.host.ip or self.serverName or "[nil]", (self.port and self.port.number) or "[nil]" )
  227. end
  228. end,
  229. --- Sets whether the instance is in a cluster
  230. --
  231. -- @param self
  232. -- @param isClustered Boolean true or the string "Yes" are interpreted as true;
  233. -- all other values are interpreted as false.
  234. SetIsClustered = function( self, isClustered )
  235. self.isClustered = (isClustered == true) or (isClustered == "Yes")
  236. end,
  237. --- Indicates whether this instance has networking protocols enabled, such
  238. -- that scripts could attempt to connect to it.
  239. HasNetworkProtocols = function( self )
  240. return (self.pipeName ~= nil) or (self.port and self.port.number)
  241. end,
  242. }
  243. --- SqlServerVersionInfo class
  244. SqlServerVersionInfo =
  245. {
  246. versionNumber = "", -- The full version string (e.g. "9.00.2047.00")
  247. major = nil, -- The major version (e.g. 9)
  248. minor = nil, -- The minor version (e.g. 0)
  249. build = nil, -- The build number (e.g. 2047)
  250. subBuild = nil, -- The sub-build number (e.g. 0)
  251. productName = nil, -- The prodcut name (e.g. "SQL Server 2005")
  252. brandedVersion = nil, -- The branded version of the product (e.g. "2005")
  253. servicePackLevel = nil, -- The service pack leve (e.g. "SP1")
  254. patched = nil, -- Whether patches have been applied since SP installation (true/false/nil)
  255. source = nil, -- The source of the version info (e.g. "SSRP", "SSNetLib")
  256. new = function(self,o)
  257. o = o or {}
  258. setmetatable(o, self)
  259. self.__index = self
  260. return o
  261. end,
  262. --- Sets the version using a version number string.
  263. --
  264. -- @param versionNumber a version number string (e.g. "9.00.1399.00")
  265. -- @param source a string indicating the source of the version info (e.g. "SSRP", "SSNetLib")
  266. SetVersionNumber = function(self, versionNumber, source)
  267. local major, minor, revision, subBuild
  268. if versionNumber:match( "^%d+%.%d+%.%d+.%d+" ) then
  269. major, minor, revision, subBuild = versionNumber:match( "^(%d+)%.(%d+)%.(%d+)" )
  270. elseif versionNumber:match( "^%d+%.%d+%.%d+" ) then
  271. major, minor, revision = versionNumber:match( "^(%d+)%.(%d+)%.(%d+)" )
  272. else
  273. stdnse.print_debug( 1, "%s: SetVersionNumber: versionNumber is not in correct format: %s", "MSSQL", versionNumber or "nil" )
  274. end
  275. self:SetVersion( major, minor, revision, subBuild, source )
  276. end,
  277. --- Sets the version using the individual numeric components of the version
  278. -- number.
  279. --
  280. -- @param source a string indicating the source of the version info (e.g. "SSRP", "SSNetLib")
  281. SetVersion = function(self, major, minor, build, subBuild, source)
  282. self.source = source
  283. -- make sure our version numbers all end up as valid numbers
  284. self.major, self.minor, self.build, self.subBuild =
  285. tonumber( major or 0 ), tonumber( minor or 0 ), tonumber( build or 0 ), tonumber( subBuild or 0 )
  286. self.versionNumber = string.format( "%u.%02u.%u.%02u", self.major, self.minor, self.build, self.subBuild )
  287. self:_ParseVersionInfo()
  288. end,
  289. --- Using the version number, determines the product version
  290. _InferProductVersion = function(self)
  291. local VERSION_LOOKUP_TABLE = {
  292. ["^6%.0"] = "6.0", ["^6%.5"] = "6.5", ["^7%.0"] = "7.0",
  293. ["^8%.0"] = "2000", ["^9%.0"] = "2005", ["^10%.0"] = "2008",
  294. ["^10%.50"] = "2008 R2", ["^11%.0"] = "2012",
  295. }
  296. local product = ""
  297. for m, v in pairs(VERSION_LOOKUP_TABLE) do
  298. if ( self.versionNumber:match(m) ) then
  299. product = v
  300. self.brandedVersion = product
  301. break
  302. end
  303. end
  304. self.productName = ("Microsoft SQL Server %s"):format(product)
  305. end,
  306. --- Returns a lookup table that maps revision numbers to service pack levels for
  307. -- the applicable SQL Server version (e.g. { {1600, "RTM"}, {2531, "SP1"} }).
  308. _GetSpLookupTable = function(self)
  309. -- Service pack lookup tables:
  310. -- For instances where a revised service pack was released (e.g. 2000 SP3a), we will include the
  311. -- build number for the original SP and the build number for the revision. However, leaving it
  312. -- like this would make it appear that subsequent builds were a patched version of the revision
  313. -- (e.g. a patch applied to 2000 SP3 that increased the build number to 780 would get displayed
  314. -- as "SP3a+", when it was actually SP3+). To avoid this, we will include an additional fake build
  315. -- number that combines the two.
  316. local SP_LOOKUP_TABLE_6_5 = { {201, "RTM"}, {213, "SP1"}, {240, "SP2"}, {258, "SP3"}, {281, "SP4"},
  317. {415, "SP5"}, {416, "SP5a"}, {417, "SP5/SP5a"}, }
  318. local SP_LOOKUP_TABLE_7 = { {623, "RTM"}, {699, "SP1"}, {842, "SP2"}, {961, "SP3"}, {1063, "SP4"}, }
  319. local SP_LOOKUP_TABLE_2000 = { {194, "RTM"}, {384, "SP1"}, {532, "SP2"}, {534, "SP2"}, {760, "SP3"},
  320. {766, "SP3a"}, {767, "SP3/SP3a"}, {2039, "SP4"}, }
  321. local SP_LOOKUP_TABLE_2005 = { {1399, "RTM"}, {2047, "SP1"}, {3042, "SP2"}, {4035, "SP3"}, {5000, "SP4"}, }
  322. local SP_LOOKUP_TABLE_2008 = { {1600, "RTM"}, {2531, "SP1"}, {4000, "SP2"}, {5500, "SP3"}, }
  323. local SP_LOOKUP_TABLE_2008R2 = { {1600, "RTM"}, {2500, "SP1"}, {4000, "SP2"}, }
  324. local SP_LOOKUP_TABLE_2012 = { {2100, "RTM"}, }
  325. if ( not self.brandedVersion ) then
  326. self:_InferProductVersion()
  327. end
  328. local spLookupTable
  329. if self.brandedVersion == "6.5" then spLookupTable = SP_LOOKUP_TABLE_6_5
  330. elseif self.brandedVersion == "7.0" then spLookupTable = SP_LOOKUP_TABLE_7
  331. elseif self.brandedVersion == "2000" then spLookupTable = SP_LOOKUP_TABLE_2000
  332. elseif self.brandedVersion == "2005" then spLookupTable = SP_LOOKUP_TABLE_2005
  333. elseif self.brandedVersion == "2008" then spLookupTable = SP_LOOKUP_TABLE_2008
  334. elseif self.brandedVersion == "2008 R2" then spLookupTable = SP_LOOKUP_TABLE_2008R2
  335. elseif self.brandedVersion == "2012" then spLookupTable = SP_LOOKUP_TABLE_2012
  336. end
  337. return spLookupTable
  338. end,
  339. --- Processes version data to determine (if possible) the product version,
  340. -- service pack level and patch status.
  341. _ParseVersionInfo = function(self)
  342. local spLookupTable = self:_GetSpLookupTable()
  343. if spLookupTable then
  344. local spLookupItr = 0
  345. -- Loop through the service pack levels until we find one whose revision
  346. -- number is the same as or lower than our revision number.
  347. while spLookupItr < #spLookupTable do
  348. spLookupItr = spLookupItr + 1
  349. if (spLookupTable[ spLookupItr ][1] == self.build ) then
  350. spLookupItr = spLookupItr
  351. break
  352. elseif (spLookupTable[ spLookupItr ][1] > self.build ) then
  353. -- The target revision number is lower than the first release
  354. if spLookupItr == 1 then
  355. self.servicePackLevel = "Pre-RTM"
  356. else
  357. -- we went too far - it's the previous SP, but with patches applied
  358. spLookupItr = spLookupItr - 1
  359. end
  360. break
  361. end
  362. end
  363. -- Now that we've identified the proper service pack level:
  364. if self.servicePackLevel ~= "Pre-RTM" then
  365. self.servicePackLevel = spLookupTable[ spLookupItr ][2]
  366. if ( spLookupTable[ spLookupItr ][1] == self.build ) then
  367. self.patched = false
  368. else
  369. self.patched = true
  370. end
  371. end
  372. -- Clean up some of our inferences. If the source of our revision number
  373. -- was the SSRP (SQL Server Browser) response, we need to recognize its
  374. -- limitations:
  375. -- * Versions of SQL Server prior to 2005 are reported with the RTM build
  376. -- number, regardless of the actual version (e.g. SQL Server 2000 is
  377. -- always 8.00.194).
  378. -- * Versions of SQL Server starting with 2005 (and going through at least
  379. -- 2008) do better but are still only reported with the build number as
  380. -- of the last service pack (e.g. SQL Server 2005 SP3 with patches is
  381. -- still reported as 9.00.4035.00).
  382. if ( self.source == "SSRP" ) then
  383. self.patched = nil
  384. if ( self.major <= 8 ) then
  385. self.servicePackLevel = nil
  386. end
  387. end
  388. end
  389. return true
  390. end,
  391. ---
  392. ToString = function(self)
  393. local friendlyVersion = strbuf.new()
  394. if self.productName then
  395. friendlyVersion:concatbuf( self.productName )
  396. if self.servicePackLevel then
  397. friendlyVersion:concatbuf( " " )
  398. friendlyVersion:concatbuf( self.servicePackLevel )
  399. end
  400. if self.patched then
  401. friendlyVersion:concatbuf( "+" )
  402. end
  403. end
  404. return friendlyVersion:dump()
  405. end,
  406. --- Uses the information in this SqlServerVersionInformation object to
  407. -- populate the version information in an Nmap port table for a SQL Server
  408. -- TCP listener.
  409. --
  410. -- @param self A SqlServerVersionInformation object
  411. -- @param port An Nmap port table corresponding to the instance
  412. PopulateNmapPortVersion = function(self, port)
  413. port.service = "ms-sql-s"
  414. port.version = port.version or {}
  415. port.version.name = "ms-sql-s"
  416. port.version.product = self.productName
  417. local versionString = strbuf.new()
  418. if self.source ~= "SSRP" then
  419. versionString:concatbuf( self.versionNumber )
  420. if self.servicePackLevel then
  421. versionString:concatbuf( "; " )
  422. versionString:concatbuf( self.servicePackLevel )
  423. end
  424. if self.patched then
  425. versionString:concatbuf( "+" )
  426. end
  427. port.version.version = versionString:dump()
  428. end
  429. return port
  430. end,
  431. }
  432. -- *************************************
  433. -- SSRP (SQL Server Resolution Protocol)
  434. -- *************************************
  435. SSRP =
  436. {
  437. PORT = { number = 1434, protocol = "udp" },
  438. DEBUG_ID = "MSSQL-SSRP",
  439. MESSAGE_TYPE =
  440. {
  441. ClientBroadcast = 0x02,
  442. ClientUnicast = 0x03,
  443. ClientUnicastInstance = 0x04,
  444. ClientUnicastDAC = 0x0F,
  445. ServerResponse = 0x05,
  446. },
  447. --- Parses an SSRP string and returns a table containing one or more
  448. -- SqlServerInstanceInfo objects created from the parsed string.
  449. _ParseSsrpString = function( host, ssrpString )
  450. -- It would seem easier to just capture (.-;;) repeateadly, since
  451. -- each instance ends with ";;", but ";;" can also occur within the
  452. -- data, signifying an empty field (e.g. "...bv;;@COMPNAME;;tcp;1433;;...").
  453. -- So, instead, we'll split up the string ahead of time.
  454. -- See the SSRP specification for more details.
  455. local instanceStrings = {}
  456. local firstInstanceEnd, instanceString
  457. repeat
  458. firstInstanceEnd = ssrpString:find( ";ServerName;(.-);InstanceName;(.-);IsClustered;(.-);" )
  459. if firstInstanceEnd then
  460. instanceString = ssrpString:sub( 1, firstInstanceEnd )
  461. ssrpString = ssrpString:sub( firstInstanceEnd + 1 )
  462. else
  463. instanceString = ssrpString
  464. end
  465. table.insert( instanceStrings, instanceString )
  466. until (not firstInstanceEnd)
  467. stdnse.print_debug( 2, "%s: SSRP Substrings:\n %s", SSRP.DEBUG_ID, stdnse.strjoin( "\n ", instanceStrings ) )
  468. local instances = {}
  469. for _, instanceString in ipairs( instanceStrings ) do
  470. local instance = SqlServerInstanceInfo:new()
  471. local version = SqlServerVersionInfo:new()
  472. instance.version = version
  473. instance.host = host
  474. instance.serverName = instanceString:match( "ServerName;(.-);")
  475. instance.instanceName = instanceString:match( "InstanceName;(.-);")
  476. instance:SetIsClustered( instanceString:match( "IsClustered;(.-);") )
  477. version:SetVersionNumber( instanceString:match( "Version;(.-);"), "SSRP" )
  478. local tcpPort = tonumber( instanceString:match( ";tcp;(.-);") )
  479. if tcpPort then instance.port = {number = tcpPort, protocol = "tcp"} end
  480. local pipeName = instanceString:match( ";np;(.-);")
  481. local status, pipeSubPath = namedpipes.get_pipe_subpath( pipeName )
  482. if status then
  483. pipeName = namedpipes.make_pipe_name( host.ip, pipeSubPath )
  484. elseif pipeName ~= nil then
  485. stdnse.print_debug( 1, "%s: Invalid pipe name:\n%s", SSRP.DEBUG_ID, pipeName )
  486. end
  487. instance.pipeName = pipeName
  488. table.insert( instances, instance )
  489. end
  490. return instances
  491. end,
  492. ---
  493. _ProcessResponse = function( host, responseData )
  494. local instances
  495. local pos, messageType, dataLength = 1, nil, nil
  496. pos, messageType, dataLength = bin.unpack("<CS", responseData, 1)
  497. -- extract the response data (i.e. everything after the 3-byte header)
  498. responseData = responseData:sub(4)
  499. stdnse.print_debug( 2, "%s: SSRP Data: %s", SSRP.DEBUG_ID, responseData )
  500. if ( messageType ~= SSRP.MESSAGE_TYPE.ServerResponse or
  501. dataLength ~= responseData:len() ) then
  502. stdnse.print_debug( 2, "%s: Invalid SSRP response. Type: 0x%02x, Length: %d, Actual length: %d",
  503. SSRP.DEBUG_ID, messageType, dataLength, responseData:len() )
  504. else
  505. instances = SSRP._ParseSsrpString( host, responseData )
  506. end
  507. return instances
  508. end,
  509. --- Attempts to retrieve information about SQL Server instances by querying
  510. -- the SQL Server Browser service on a host.
  511. --
  512. -- @param host A host table for the target host
  513. -- @param port (Optional) A port table for the target SQL Server Browser service
  514. -- @return (status, result) If status is true, result is a table of
  515. -- SqlServerInstanceInfo objects. If status is false, result is an
  516. -- error message.
  517. DiscoverInstances = function( host, port )
  518. port = port or SSRP.PORT
  519. if ( SCANNED_PORTS_ONLY and nmap.get_port_state( host, port ) == nil ) then
  520. stdnse.print_debug( 2, "%s: Discovery disallowed: scanned-ports-only is set and port %d was not scanned", SSRP.DEBUG_ID, port.number )
  521. return false, "Discovery disallowed: scanned-ports-only"
  522. end
  523. local socket = nmap.new_socket("udp")
  524. socket:set_timeout(5000)
  525. if ( port.number ~= SSRP.PORT.number ) then
  526. stdnse.print_debug( 1, "%s: DiscoverInstances() called with non-standard port (%d)", SSRP.DEBUG_ID, port.number )
  527. end
  528. local status, err = socket:connect( host, port )
  529. if ( not(status) ) then return false, err end
  530. status, err = socket:send( bin.pack( "C", SSRP.MESSAGE_TYPE.ClientUnicast ) )
  531. if ( not(status) ) then return false, err end
  532. local responseData, instances_host
  533. status, responseData = socket:receive()
  534. if ( not(status) ) then return false, responseData
  535. else
  536. instances_host = SSRP._ProcessResponse( host, responseData )
  537. end
  538. socket:close()
  539. return status, instances_host
  540. end,
  541. --- Attempts to retrieve information about SQL Server instances by querying
  542. -- the SQL Server Browser service on a broadcast domain.
  543. --
  544. -- @param host A host table for the broadcast specification
  545. -- @param port (Optional) A port table for the target SQL Server Browser service
  546. -- @return (status, result) If status is true, result is a table of
  547. -- tables containing SqlServerInstanceInfo objects. The top-level table
  548. -- is indexed by IP address. If status is false, result is an
  549. -- error message.
  550. DiscoverInstances_Broadcast = function( host, port )
  551. port = port or SSRP.PORT
  552. local socket = nmap.new_socket("udp")
  553. socket:set_timeout(5000)
  554. local instances_all = {}
  555. if ( port.number ~= SSRP.PORT.number ) then
  556. stdnse.print_debug( 1, "%S: DiscoverInstances_Broadcast() called with non-standard port (%d)", SSRP.DEBUG_ID, port.number )
  557. end
  558. local status, err = socket:sendto(host, port, bin.pack( "C", SSRP.MESSAGE_TYPE.ClientBroadcast ))
  559. if ( not(status) ) then return false, err end
  560. while ( status ) do
  561. local responseData
  562. status, responseData = socket:receive()
  563. if ( status ) then
  564. local remoteIp, _
  565. status, _, _, remoteIp, _ = socket:get_info()
  566. local instances_host = SSRP._ProcessResponse( {ip = remoteIp, name = ""}, responseData )
  567. instances_all[ remoteIp ] = instances_host
  568. end
  569. end
  570. socket:close()
  571. return true, instances_all
  572. end,
  573. }
  574. -- *************************
  575. -- TDS (Tabular Data Stream)
  576. -- *************************
  577. -- TDS packet types
  578. PacketType =
  579. {
  580. Query = 0x01,
  581. Response = 0x04,
  582. Login = 0x10,
  583. NTAuthentication = 0x11,
  584. PreLogin = 0x12,
  585. }
  586. -- TDS response token types
  587. TokenType =
  588. {
  589. ReturnStatus = 0x79,
  590. TDS7Results = 0x81,
  591. ErrorMessage = 0xAA,
  592. InformationMessage = 0xAB,
  593. LoginAcknowledgement = 0xAD,
  594. Row = 0xD1,
  595. OrderBy = 0xA9,
  596. EnvironmentChange = 0xE3,
  597. NTLMSSP_CHALLENGE = 0xed,
  598. Done = 0xFD,
  599. DoneProc = 0xFE,
  600. DoneInProc = 0xFF,
  601. }
  602. -- SQL Server/Sybase data types
  603. DataTypes =
  604. {
  605. SQLTEXT = 0x23,
  606. GUIDTYPE = 0x24,
  607. SYBINTN = 0x26,
  608. SYBINT2 = 0x34,
  609. SYBINT4 = 0x38,
  610. SYBDATETIME = 0x3D,
  611. NTEXTTYPE = 0x63,
  612. BITNTYPE = 0x68,
  613. DECIMALNTYPE = 0x6A,
  614. NUMERICNTYPE = 0x6C,
  615. FLTNTYPE = 0x6D,
  616. MONEYNTYPE = 0x6E,
  617. SYBDATETIMN = 0x6F,
  618. XSYBVARBINARY = 0xA5,
  619. XSYBVARCHAR = 0xA7,
  620. BIGBINARYTYPE = 0xAD,
  621. BIGCHARTYPE = 0xAF,
  622. XSYBNVARCHAR = 0xE7,
  623. SQLNCHAR = 0xEF,
  624. }
  625. -- SQL Server login error codes
  626. -- See http://msdn.microsoft.com/en-us/library/ms131024.aspx
  627. LoginErrorType =
  628. {
  629. AccountLockedOut = 15113,
  630. NotAssociatedWithTrustedConnection = 18452, -- This probably means that the server is set for Windows authentication only
  631. InvalidUsernameOrPassword = 18456,
  632. PasswordChangeFailed_PasswordNotAllowed = 18463,
  633. PasswordChangeFailed_PasswordTooShort = 18464,
  634. PasswordChangeFailed_PasswordTooLong = 18465,
  635. PasswordChangeFailed_PasswordNotComplex = 18466,
  636. PasswordChangeFailed_PasswordFilter = 18467,
  637. PasswordChangeFailed_UnexpectedError = 18468,
  638. PasswordExpired = 18487,
  639. PasswordMustChange = 18488,
  640. }
  641. LoginErrorMessage = {}
  642. for i, v in pairs(LoginErrorType) do
  643. LoginErrorMessage[v] = i
  644. end
  645. -- "static" ColumInfo parser class
  646. ColumnInfo =
  647. {
  648. Parse =
  649. {
  650. [DataTypes.SQLTEXT] = function( data, pos )
  651. local colinfo = {}
  652. local tmp
  653. pos, colinfo.unknown, colinfo.codepage, colinfo.flags, colinfo.charset = bin.unpack("<ISSC", data, pos )
  654. pos, colinfo.tablenamelen = bin.unpack("s", data, pos )
  655. pos, colinfo.tablename = bin.unpack("A" .. (colinfo.tablenamelen * 2), data, pos)
  656. pos, colinfo.msglen = bin.unpack("<C", data, pos )
  657. pos, tmp = bin.unpack("A" .. (colinfo.msglen * 2), data, pos)
  658. colinfo.text = Util.FromWideChar(tmp)
  659. return pos, colinfo
  660. end,
  661. [DataTypes.GUIDTYPE] = function( data, pos )
  662. return ColumnInfo.Parse[DataTypes.SYBINTN](data, pos)
  663. end,
  664. [DataTypes.SYBINTN] = function( data, pos )
  665. local colinfo = {}
  666. local tmp
  667. pos, colinfo.unknown, colinfo.msglen = bin.unpack("<CC", data, pos)
  668. pos, tmp = bin.unpack("A" .. (colinfo.msglen * 2), data, pos )
  669. colinfo.text = Util.FromWideChar(tmp)
  670. return pos, colinfo
  671. end,
  672. [DataTypes.SYBINT2] = function( data, pos )
  673. return ColumnInfo.Parse[DataTypes.SYBDATETIME](data, pos)
  674. end,
  675. [DataTypes.SYBINT4] = function( data, pos )
  676. return ColumnInfo.Parse[DataTypes.SYBDATETIME](data, pos)
  677. end,
  678. [DataTypes.SYBDATETIME] = function( data, pos )
  679. local colinfo = {}
  680. local tmp
  681. pos, colinfo.msglen = bin.unpack("C", data, pos)
  682. pos, tmp = bin.unpack("A" .. (colinfo.msglen * 2), data, pos )
  683. colinfo.text = Util.FromWideChar(tmp)
  684. return pos, colinfo
  685. end,
  686. [DataTypes.NTEXTTYPE] = function( data, pos )
  687. return ColumnInfo.Parse[DataTypes.SQLTEXT](data, pos)
  688. end,
  689. [DataTypes.BITNTYPE] = function( data, pos )
  690. return ColumnInfo.Parse[DataTypes.SYBINTN](data, pos)
  691. end,
  692. [DataTypes.DECIMALNTYPE] = function( data, pos )
  693. local colinfo = {}
  694. local tmp
  695. pos, colinfo.unknown, colinfo.precision, colinfo.scale = bin.unpack("<CCC", data, pos)
  696. pos, colinfo.msglen = bin.unpack("<C",data,pos)
  697. pos, tmp = bin.unpack("A" .. (colinfo.msglen * 2), data, pos )
  698. colinfo.text = Util.FromWideChar(tmp)
  699. return pos, colinfo
  700. end,
  701. [DataTypes.NUMERICNTYPE] = function( data, pos )
  702. return ColumnInfo.Parse[DataTypes.DECIMALNTYPE](data, pos)
  703. end,
  704. [DataTypes.FLTNTYPE] = function( data, pos )
  705. return ColumnInfo.Parse[DataTypes.SYBINTN](data, pos)
  706. end,
  707. [DataTypes.MONEYNTYPE] = function( data, pos )
  708. return ColumnInfo.Parse[DataTypes.SYBINTN](data, pos)
  709. end,
  710. [DataTypes.SYBDATETIMN] = function( data, pos )
  711. return ColumnInfo.Parse[DataTypes.SYBINTN](data, pos)
  712. end,
  713. [DataTypes.XSYBVARBINARY] = function( data, pos )
  714. local colinfo = {}
  715. local tmp
  716. pos, colinfo.lts, colinfo.msglen = bin.unpack("<SC", data, pos)
  717. pos, tmp = bin.unpack("A" .. (colinfo.msglen * 2), data, pos )
  718. colinfo.text = Util.FromWideChar(tmp)
  719. return pos, colinfo
  720. end,
  721. [DataTypes.XSYBVARCHAR] = function( data, pos )
  722. return ColumnInfo.Parse[DataTypes.XSYBNVARCHAR](data, pos)
  723. end,
  724. [DataTypes.BIGBINARYTYPE] = function( data, pos )
  725. return ColumnInfo.Parse[DataTypes.XSYBVARBINARY](data, pos)
  726. end,
  727. [DataTypes.BIGCHARTYPE] = function( data, pos )
  728. return ColumnInfo.Parse[DataTypes.XSYBNVARCHAR](data, pos)
  729. end,
  730. [DataTypes.XSYBNVARCHAR] = function( data, pos )
  731. local colinfo = {}
  732. local tmp
  733. pos, colinfo.lts, colinfo.codepage, colinfo.flags, colinfo.charset,
  734. colinfo.msglen = bin.unpack("<SSSCC", data, pos )
  735. pos, tmp = bin.unpack("A" .. (colinfo.msglen * 2), data, pos)
  736. colinfo.text = Util.FromWideChar(tmp)
  737. return pos, colinfo
  738. end,
  739. [DataTypes.SQLNCHAR] = function( data, pos )
  740. return ColumnInfo.Parse[DataTypes.XSYBNVARCHAR](data, pos)
  741. end,
  742. }
  743. }
  744. -- "static" ColumData parser class
  745. ColumnData =
  746. {
  747. Parse = {
  748. [DataTypes.SQLTEXT] = function( data, pos )
  749. local len, coldata
  750. -- The first len value is the size of the meta data block
  751. -- for non-null values this seems to be 0x10 / 16 bytes
  752. pos, len = bin.unpack( "<C", data, pos )
  753. if ( len == 0 ) then
  754. return pos, 'Null'
  755. end
  756. -- Skip over the text update time and date values, we don't need them
  757. -- We may come back add parsing for this information.
  758. pos = pos + len
  759. -- skip a label, should be 'dummyTS'
  760. pos = pos + 8
  761. -- extract the actual data
  762. pos, len = bin.unpack( "<I", data, pos )
  763. pos, coldata = bin.unpack( "A"..len, data, pos )
  764. return pos, coldata
  765. end,
  766. [DataTypes.GUIDTYPE] = function( data, pos )
  767. local len, coldata, index, nextdata
  768. local hex = {}
  769. pos, len = bin.unpack("C", data, pos)
  770. if ( len == 0 ) then
  771. return pos, 'Null'
  772. elseif ( len == 16 ) then
  773. -- Return the first 8 bytes
  774. for index=1, 8 do
  775. pos, hex[index] = bin.unpack("H", data, pos)
  776. end
  777. -- reorder the bytes
  778. coldata = hex[4] .. hex[3] .. hex[2] .. hex[1]
  779. coldata = coldata .. '-' .. hex[6] .. hex[5]
  780. coldata = coldata .. '-' .. hex[8] .. hex[7]
  781. pos, nextdata = bin.unpack("H2", data, pos)
  782. coldata = coldata .. '-' .. nextdata
  783. pos, nextdata = bin.unpack("H6", data, pos)
  784. coldata = coldata .. '-' .. nextdata
  785. else
  786. stdnse.print_debug("Unhandled length (%d) for GUIDTYPE", len)
  787. return pos + len, 'Unsupported Data'
  788. end
  789. return pos, coldata
  790. end,
  791. [DataTypes.SYBINTN] = function( data, pos )
  792. local len, num
  793. pos, len = bin.unpack("C", data, pos)
  794. if ( len == 0 ) then
  795. return pos, 'Null'
  796. elseif ( len == 1 ) then
  797. return bin.unpack("C", data, pos)
  798. elseif ( len == 2 ) then
  799. return bin.unpack("<s", data, pos)
  800. elseif ( len == 4 ) then
  801. return bin.unpack("<i", data, pos)
  802. elseif ( len == 8 ) then
  803. return bin.unpack("<l", data, pos)
  804. else
  805. return -1, ("Unhandled length (%d) for SYBINTN"):format(len)
  806. end
  807. return -1, "Error"
  808. end,
  809. [DataTypes.SYBINT2] = function( data, pos )
  810. local num
  811. pos, num = bin.unpack("<S", data, pos)
  812. return pos, num
  813. end,
  814. [DataTypes.SYBINT4] = function( data, pos )
  815. local num
  816. pos, num = bin.unpack("<I", data, pos)
  817. return pos, num
  818. end,
  819. [DataTypes.SYBDATETIME] = function( data, pos )
  820. local hi, lo, result_seconds, result
  821. local tds_epoch, system_epoch, tds_offset_seconds
  822. pos, hi, lo = bin.unpack("<iI", data, pos)
  823. tds_epoch = os.time( {year = 1900, month = 1, day = 1, hour = 00, min = 00, sec = 00, isdst = nil} )
  824. -- determine the offset between the tds_epoch and the local system epoch
  825. system_epoch = os.time( os.date("*t", 0))
  826. tds_offset_seconds = os.difftime(tds_epoch,system_epoch)
  827. result_seconds = (hi*24*60*60) + (lo/300)
  828. result = os.date("!%b %d, %Y %H:%M:%S", tds_offset_seconds + result_seconds )
  829. return pos, result
  830. end,
  831. [DataTypes.NTEXTTYPE] = function( data, pos )
  832. local len, coldata
  833. -- The first len value is the size of the meta data block
  834. pos, len = bin.unpack( "<C", data, pos )
  835. if ( len == 0 ) then
  836. return pos, 'Null'
  837. end
  838. -- Skip over the text update time and date values, we don't need them
  839. -- We may come back add parsing for this information.
  840. pos = pos + len
  841. -- skip a label, should be 'dummyTS'
  842. pos = pos + 8
  843. -- extract the actual data
  844. pos, len = bin.unpack( "<I", data, pos )
  845. pos, coldata = bin.unpack( "A"..len, data, pos )
  846. return pos, Util.FromWideChar(coldata)
  847. end,
  848. [DataTypes.BITNTYPE] = function( data, pos )
  849. return ColumnData.Parse[DataTypes.SYBINTN](data, pos)
  850. end,
  851. [DataTypes.DECIMALNTYPE] = function( precision, scale, data, pos )
  852. local len, sign, format_string, coldata
  853. pos, len = bin.unpack("<C", data, pos)
  854. if ( len == 0 ) then
  855. return pos, 'Null'
  856. end
  857. pos, sign = bin.unpack("<C", data, pos)
  858. -- subtract 1 from data len to account for sign byte
  859. len = len - 1
  860. if ( len == 2 ) then
  861. pos, coldata = bin.unpack("<S", data, pos)
  862. elseif ( len == 4 ) then
  863. pos, coldata = bin.unpack("<I", data, pos)
  864. elseif ( len == 8 ) then
  865. pos, coldata = bin.unpack("<L", data, pos)
  866. else
  867. stdnse.print_debug("Unhandled length (%d) for DECIMALNTYPE", len)
  868. return pos + len, 'Unsupported Data'
  869. end
  870. if ( sign == 0 ) then
  871. coldata = coldata * -1
  872. end
  873. coldata = coldata * (10^-scale)
  874. -- format the return information to reduce truncation by lua
  875. format_string = string.format("%%.%if", scale)
  876. coldata = string.format(format_string,coldata)
  877. return pos, coldata
  878. end,
  879. [DataTypes.NUMERICNTYPE] = function( precision, scale, data, pos )
  880. return ColumnData.Parse[DataTypes.DECIMALNTYPE]( precision, scale, data, pos )
  881. end,
  882. [DataTypes.SYBDATETIME] = function( data, pos )
  883. local hi, lo, result_seconds, result
  884. local tds_epoch, system_epoch, tds_offset_seconds
  885. pos, hi, lo = bin.unpack("<iI", data, pos)
  886. tds_epoch = os.time( {year = 1900, month = 1, day = 1, hour = 00, min = 00, sec = 00, isdst = nil} )
  887. -- determine the offset between the tds_epoch and the local system epoch
  888. system_epoch = os.time( os.date("*t", 0))
  889. tds_offset_seconds = os.difftime(tds_epoch,system_epoch)
  890. result_seconds = (hi*24*60*60) + (lo/300)
  891. result = os.date("!%b %d, %Y %H:%M:%S", tds_offset_seconds + result_seconds )
  892. return pos, result
  893. end,
  894. [DataTypes.BITNTYPE] = function( data, pos )
  895. return ColumnData.Parse[DataTypes.SYBINTN](data, pos)
  896. end,
  897. [DataTypes.NTEXTTYPE] = function( data, pos )
  898. local len, coldata
  899. -- The first len value is the size of the meta data block
  900. pos, len = bin.unpack( "<C", data, pos )
  901. if ( len == 0 ) then
  902. return pos, 'Null'
  903. end
  904. -- Skip over the text update time and date values, we don't need them
  905. -- We may come back add parsing for this information.
  906. pos = pos + len
  907. -- skip a label, should be 'dummyTS'
  908. pos = pos + 8
  909. -- extract the actual data
  910. pos, len = bin.unpack( "<I", data, pos )
  911. pos, coldata = bin.unpack( "A"..len, data, pos )
  912. return pos, Util.FromWideChar(coldata)
  913. end,
  914. [DataTypes.FLTNTYPE] = function( data, pos )
  915. local len, coldata
  916. pos, len = bin.unpack("<C", data, pos)
  917. if ( len == 0 ) then
  918. return pos, 'Null'
  919. elseif ( len == 4 ) then
  920. pos, coldata = bin.unpack("f", data, pos)
  921. elseif ( len == 8 ) then
  922. pos, coldata = bin.unpack("<d", data, pos)
  923. end
  924. return pos, coldata
  925. end,
  926. [DataTypes.MONEYNTYPE] = function( data, pos )
  927. local len, value, coldata, hi, lo
  928. pos, len = bin.unpack("C", data, pos)
  929. if ( len == 0 ) then
  930. return pos, 'Null'
  931. elseif ( len == 4 ) then
  932. --type smallmoney
  933. pos, value = bin.unpack("<i", data, pos)
  934. elseif ( len == 8 ) then
  935. -- type money
  936. pos, hi,lo = bin.unpack("<II", data, pos)
  937. value = ( hi * 4294967296 ) + lo
  938. else
  939. return -1, ("Unhandled length (%d) for MONEYNTYPE"):format(len)
  940. end
  941. -- the datatype allows for 4 decimal places after the period to support various currency types.
  942. -- forcing to string to avoid truncation
  943. coldata = string.format("%.4f",value/10000)
  944. return pos, coldata
  945. end,
  946. [DataTypes.SYBDATETIMN] = function( data, pos )
  947. local len, coldata
  948. pos, len = bin.unpack( "<C", data, pos )
  949. if ( len == 0 ) then
  950. return pos, 'Null'
  951. elseif ( len == 4 ) then
  952. -- format is smalldatetime
  953. local days, mins
  954. pos, days, mins = bin.unpack("<SS", data, pos)
  955. local tds_epoch = os.time( {year = 1900, month = 1, day = 1, hour = 00, min = 00, sec = 00, isdst = nil} )
  956. -- determine the offset between the tds_epoch and the local system epoch
  957. local system_epoch = os.time( os.date("*t", 0))
  958. local tds_offset_seconds = os.difftime(tds_epoch,system_epoch)
  959. local result_seconds = (days*24*60*60) + (mins*60)
  960. coldata = os.date("!%b %d, %Y %H:%M:%S", tds_offset_seconds + result_seconds )
  961. return pos,coldata
  962. elseif ( len == 8 ) then
  963. -- format is datetime
  964. return ColumnData.Parse[DataTypes.SYBDATETIME](data, pos)
  965. else
  966. return -1, ("Unhandled length (%d) for SYBDATETIMN"):format(len)
  967. end
  968. end,
  969. [DataTypes.XSYBVARBINARY] = function( data, pos )
  970. local len, coldata
  971. pos, len = bin.unpack( "<S", data, pos )
  972. if ( len == 65535 ) then
  973. return pos, 'Null'
  974. else
  975. pos, coldata = bin.unpack( "A"..len, data, pos )
  976. return pos, "0x" .. select(2, bin.unpack("H"..coldata:len(), coldata ) )
  977. end
  978. return -1, "Error"
  979. end,
  980. [DataTypes.XSYBVARCHAR] = function( data, pos )
  981. local len, coldata
  982. pos, len = bin.unpack( "<S", data, pos )
  983. if ( len == 65535 ) then
  984. return pos, 'Null'
  985. end
  986. pos, coldata = bin.unpack( "A"..len, data, pos )
  987. return pos, coldata
  988. end,
  989. [DataTypes.BIGBINARYTYPE] = function( data, pos )
  990. return ColumnData.Parse[DataTypes.XSYBVARBINARY](data, pos)
  991. end,
  992. [DataTypes.BIGCHARTYPE] = function( data, pos )
  993. return ColumnData.Parse[DataTypes.XSYBVARCHAR](data, pos)
  994. end,
  995. [DataTypes.XSYBNVARCHAR] = function( data, pos )
  996. local len, coldata
  997. pos, len = bin.unpack( "<S", data, pos )
  998. if ( len == 65535 ) then
  999. return pos, 'Null'
  1000. end
  1001. pos, coldata = bin.unpack( "A"..len, data, pos )
  1002. return pos, Util.FromWideChar(coldata)
  1003. end,
  1004. [DataTypes.SQLNCHAR] = function( data, pos )
  1005. return ColumnData.Parse[DataTypes.XSYBNVARCHAR](data, pos)
  1006. end,
  1007. }
  1008. }
  1009. -- "static" Token parser class
  1010. Token =
  1011. {
  1012. Parse = {
  1013. --- Parse error message tokens
  1014. --
  1015. -- @param data string containing "raw" data
  1016. -- @param pos number containing offset into data
  1017. -- @return pos number containing new offset after parse
  1018. -- @return token table containing token specific fields
  1019. [TokenType.ErrorMessage] = function( data, pos )
  1020. local token = {}
  1021. local tmp
  1022. token.type = TokenType.ErrorMessage
  1023. pos, token.size, token.errno, token.state, token.severity, token.errlen = bin.unpack( "<SICCS", data, pos )
  1024. pos, tmp = bin.unpack("A" .. (token.errlen * 2), data, pos )
  1025. token.error = Util.FromWideChar(tmp)
  1026. pos, token.srvlen = bin.unpack("C", data, pos)
  1027. pos, tmp = bin.unpack("A" .. (token.srvlen * 2), data, pos )
  1028. token.server = Util.FromWideChar(tmp)
  1029. pos, token.proclen = bin.unpack("C", data, pos)
  1030. pos, tmp = bin.unpack("A" .. (token.proclen * 2), data, pos )
  1031. token.proc = Util.FromWideChar(tmp)
  1032. pos, token.lineno = bin.unpack("<S", data, pos)
  1033. return pos, token
  1034. end,
  1035. --- Parse environment change tokens
  1036. -- (This function is not implemented and simply moves the pos offset)
  1037. --
  1038. -- @param data string containing "raw" data
  1039. -- @param pos number containing offset into data
  1040. -- @return pos number containing new offset after parse
  1041. -- @return token table containing token specific fields
  1042. [TokenType.EnvironmentChange] = function( data, pos )
  1043. local token = {}
  1044. local tmp
  1045. token.type = TokenType.EnvironmentChange
  1046. pos, token.size = bin.unpack("<S", data, pos)
  1047. return pos + token.size, token
  1048. end,
  1049. --- Parse information message tokens
  1050. --
  1051. -- @param data string containing "raw" data
  1052. -- @param pos number containing offset into data
  1053. -- @return pos number containing new offset after parse
  1054. -- @return token table containing token specific fields
  1055. [TokenType.InformationMessage] = function( data, pos )
  1056. local pos, token = Token.Parse[TokenType.ErrorMessage]( data, pos )
  1057. token.type = TokenType.InformationMessage
  1058. return pos, token
  1059. end,
  1060. --- Parse login acknowledgment tokens
  1061. --
  1062. -- @param data string containing "raw" data
  1063. -- @param pos number containing offset into data
  1064. -- @return pos number containing new offset after parse
  1065. -- @return token table containing token specific fields
  1066. [TokenType.LoginAcknowledgement] = function( data, pos )
  1067. local token = {}
  1068. local _
  1069. token.type = TokenType.LoginAcknowledgement
  1070. pos, token.size, _, _, _, _, token.textlen = bin.unpack( "<SCCCSC", data, pos )
  1071. pos, token.text = bin.unpack("A" .. token.textlen * 2, data, pos)
  1072. pos, token.version = bin.unpack("<I", data, pos )
  1073. return pos, token
  1074. end,
  1075. --- Parse done tokens
  1076. --
  1077. -- @param data string containing "raw" data
  1078. -- @param pos number containing offset into data
  1079. -- @return pos number containing new offset after parse
  1080. -- @return token table containing token specific fields
  1081. [TokenType.Done] = function( data, pos )
  1082. local token = {}
  1083. token.type = TokenType.Done
  1084. pos, token.flags, token.operation, token.rowcount = bin.unpack( "<SSI", data, pos )
  1085. return pos, token
  1086. end,
  1087. --- Parses a DoneProc token recieved after executing a SP
  1088. --
  1089. -- @param data string containing "raw" data
  1090. -- @param pos number containing offset into data
  1091. -- @return pos number containing new offset after parse
  1092. -- @return token table containing token specific fields
  1093. [TokenType.DoneProc] = function( data, pos )
  1094. local token
  1095. pos, token = Token.Parse[TokenType.Done]( data, pos )
  1096. token.type = TokenType.DoneProc
  1097. return pos, token
  1098. end,
  1099. --- Parses a DoneInProc token recieved after executing a SP
  1100. --
  1101. -- @param data string containing "raw" data
  1102. -- @param pos number containing offset into data
  1103. -- @return pos number containing new offset after parse
  1104. -- @return token table containing token specific fields
  1105. [TokenType.DoneInProc] = function( data, pos )
  1106. local token
  1107. pos, token = Token.Parse[TokenType.Done]( data, pos )
  1108. token.type = TokenType.DoneInProc
  1109. return pos, token
  1110. end,
  1111. --- Parses a ReturnStatus token
  1112. --
  1113. -- @param data string containing "raw" data
  1114. -- @param pos number containing offset into data
  1115. -- @return pos number containing new offset after parse
  1116. -- @return token table containing token specific fields
  1117. [TokenType.ReturnStatus] = function( data, pos )
  1118. local token = {}
  1119. pos, token.value = bin.unpack("<i", data, pos)
  1120. token.type = TokenType.ReturnStatus
  1121. return pos, token
  1122. end,
  1123. --- Parses a OrderBy token
  1124. --
  1125. -- @param data string containing "raw" data
  1126. -- @param pos number containing offset into data
  1127. -- @return pos number containing new offset after parse
  1128. -- @return token table containing token specific fields
  1129. [TokenType.OrderBy] = function( data, pos )
  1130. local token = {}
  1131. pos, token.size = bin.unpack("<S", data, pos)
  1132. token.type = TokenType.OrderBy
  1133. return pos + token.size, token
  1134. end,
  1135. --- Parse TDS result tokens
  1136. --
  1137. -- @param data string containing "raw" data
  1138. -- @param pos number containing offset into data
  1139. -- @return pos number containing new offset after parse
  1140. -- @return token table containing token specific fields
  1141. [TokenType.TDS7Results] = function( data, pos )
  1142. local token = {}
  1143. local _
  1144. token.type = TokenType.TDS7Results
  1145. pos, token.count = bin.unpack( "<S", data, pos )
  1146. token.colinfo = {}
  1147. for i=1, token.count do
  1148. local colinfo = {}
  1149. local usertype, flags, ttype
  1150. pos, usertype, flags, ttype = bin.unpack("<SSC", data, pos )
  1151. if ( not(ColumnInfo.Parse[ttype]) ) then
  1152. return -1, ("Unhandled data type: 0x%X"):format(ttype)
  1153. end
  1154. pos, colinfo = ColumnInfo.Parse[ttype]( data, pos )
  1155. colinfo.usertype = usertype
  1156. colinfo.flags = flags
  1157. colinfo.type = ttype
  1158. table.insert( token.colinfo, colinfo )
  1159. end
  1160. return pos, token
  1161. end,
  1162. [TokenType.NTLMSSP_CHALLENGE] = function(data, pos)
  1163. local pos, len, ntlmssp, msgtype = bin.unpack("<SA8I", data, pos)
  1164. local NTLMSSP_CHALLENGE = 2
  1165. if ( ntlmssp ~= "NTLMSSP\0" or msgtype ~= NTLMSSP_CHALLENGE ) then
  1166. return -1, "Failed to process NTLMSSP Challenge"
  1167. end
  1168. local ntlm_challenge = data:sub( 28, 35 )
  1169. pos = pos + len - 13
  1170. return pos, ntlm_challenge
  1171. end,
  1172. },
  1173. --- Parses the first token at positions pos
  1174. --
  1175. -- @param data string containing "raw" data
  1176. -- @param pos number containing offset into data
  1177. -- @return pos number containing new offset after parse or -1 on error
  1178. -- @return token table containing token specific fields or error message on error
  1179. ParseToken = function( data, pos )
  1180. local ttype
  1181. pos, ttype = bin.unpack("C", data, pos)
  1182. if ( not(Token.Parse[ttype]) ) then
  1183. stdnse.print_debug( 1, "%s: No parser for token type 0x%X", "MSSQL", ttype )
  1184. return -1, ("No parser for token type: 0x%X"):format( ttype )
  1185. end
  1186. return Token.Parse[ttype](data, pos)
  1187. end,
  1188. }
  1189. --- QueryPacket class
  1190. QueryPacket =
  1191. {
  1192. new = function(self,o)
  1193. o = o or {}
  1194. setmetatable(o, self)
  1195. self.__index = self
  1196. return o
  1197. end,
  1198. SetQuery = function( self, query )
  1199. self.query = query
  1200. end,
  1201. --- Returns the query packet as string
  1202. --
  1203. -- @return string containing the authentication packet
  1204. ToString = function( self )
  1205. return PacketType.Query, Util.ToWideChar( self.query )
  1206. end,
  1207. }
  1208. --- PreLoginPacket class
  1209. PreLoginPacket =
  1210. {
  1211. -- TDS pre-login option types
  1212. OPTION_TYPE = {
  1213. Version = 0x00,

Large files files are truncated, but you can click here to view the full file