Module: Metasploit::Framework::MSSQL::Base
- Included in:
- Client, Msf::Exploit::Remote::MSSQL
- Defined in:
- lib/metasploit/framework/mssql/base.rb
Overview
A base mixin of useful mssql methods for parsing structures etc
Constant Summary collapse
- ENCRYPT_OFF =
Encryption
0x00
- ENCRYPT_ON =
Encryption is available but off.
0x01
- ENCRYPT_NOT_SUP =
Encryption is available and on.
0x02
- ENCRYPT_REQ =
Encryption is not available.
0x03
- TYPE_SQL_BATCH =
Packet Type
1
- TYPE_PRE_TDS7_LOGIN =
(Client) SQL command
2
- TYPE_RPC =
(Client) Pre-login with version < 7 (unused)
3
- TYPE_TABLE_RESPONSE =
(Client) RPC
4
- TYPE_ATTENTION_SIGNAL =
Request Completion, Error and Info Messages, Attention Acknowledgement
6
- TYPE_BULK_LOAD =
(Client) Attention
7
- TYPE_TRANSACTION_MANAGER_REQUEST =
(Client) SQL Command with binary data
14
- TYPE_TDS7_LOGIN =
(Client) Transaction request manager
16
- TYPE_SSPI_MESSAGE =
(Client) Login
17
- TYPE_PRE_LOGIN_MESSAGE =
(Client) Login
18
- STATUS_NORMAL =
Status
0x00
- STATUS_END_OF_MESSAGE =
0x01
- STATUS_IGNORE_EVENT =
0x02
- STATUS_RESETCONNECTION =
TDS 7.1+
0x08
- STATUS_RESETCONNECTIONSKIPTRAN =
TDS 7.3+
0x10
Instance Method Summary collapse
-
#mssql_parse_done(data, info) ⇒ Object
Parse a “done” TDS token.
-
#mssql_parse_env(data, info) ⇒ Object
Parse an “environment change” TDS token.
-
#mssql_parse_error(data, info) ⇒ Object
Parse an “error” TDS token.
-
#mssql_parse_info(data, info) ⇒ Object
Parse an “information” TDS token.
-
#mssql_parse_login_ack(data, info) ⇒ Object
Parse a “login ack” TDS token.
-
#mssql_parse_reply(data, info) ⇒ Object
Parse individual tokens from a TDS reply.
-
#mssql_parse_ret(data, info) ⇒ Object
Parse a “ret” TDS token.
-
#mssql_parse_tds_reply(data, info) ⇒ Object
Parse a raw TDS reply from the server.
-
#mssql_parse_tds_row(data, info) ⇒ Object
Parse a single row of a TDS reply.
-
#mssql_send_recv(req, timeout = 15, check_status = true) ⇒ Object
Send and receive using TDS.
-
#mssql_tds_encrypt(pass) ⇒ Object
Encrypt a password according to the TDS protocol (encode).
Instance Method Details
#mssql_parse_done(data, info) ⇒ Object
Parse a “done” TDS token
285 286 287 288 289 |
# File 'lib/metasploit/framework/mssql/base.rb', line 285 def mssql_parse_done(data, info) status, cmd, rows = data.slice!(0, 8).unpack('vvV') info[:done] = { :status => status, :cmd => cmd, :rows => rows } info end |
#mssql_parse_env(data, info) ⇒ Object
Parse an “environment change” TDS token
309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 |
# File 'lib/metasploit/framework/mssql/base.rb', line 309 def mssql_parse_env(data, info) len = data.slice!(0, 2).unpack('v')[0] buff = data.slice!(0, len) type = buff.slice!(0, 1).unpack('C')[0] nval = '' nlen = buff.slice!(0, 1).unpack('C')[0] || 0 nval = buff.slice!(0, nlen * 2).gsub("\x00", '') if nlen > 0 oval = '' olen = buff.slice!(0, 1).unpack('C')[0] || 0 oval = buff.slice!(0, olen * 2).gsub("\x00", '') if olen > 0 info[:envs] ||= [] info[:envs] << { :type => type, :old => oval, :new => nval } info end |
#mssql_parse_error(data, info) ⇒ Object
Parse an “error” TDS token
294 295 296 297 298 299 300 301 302 303 304 |
# File 'lib/metasploit/framework/mssql/base.rb', line 294 def mssql_parse_error(data, info) len = data.slice!(0, 2).unpack('v')[0] buff = data.slice!(0, len) errno, state, sev, elen = buff.slice!(0, 8).unpack('VCCv') emsg = buff.slice!(0, elen * 2) emsg.gsub!("\x00", '') info[:errors] << "SQL Server Error ##{errno} (State:#{state} Severity:#{sev}): #{emsg}" info end |
#mssql_parse_info(data, info) ⇒ Object
Parse an “information” TDS token
330 331 332 333 334 335 336 337 338 339 340 341 |
# File 'lib/metasploit/framework/mssql/base.rb', line 330 def mssql_parse_info(data, info) len = data.slice!(0, 2).unpack('v')[0] buff = data.slice!(0, len) errno, state, sev, elen = buff.slice!(0, 8).unpack('VCCv') emsg = buff.slice!(0, elen * 2) emsg.gsub!("\x00", '') info[:infos] ||= [] info[:infos] << "SQL Server Info ##{errno} (State:#{state} Severity:#{sev}): #{emsg}" info end |
#mssql_parse_login_ack(data, info) ⇒ Object
Parse a “login ack” TDS token
346 347 348 349 350 |
# File 'lib/metasploit/framework/mssql/base.rb', line 346 def mssql_parse_login_ack(data, info) len = data.slice!(0, 2).unpack('v')[0] _buff = data.slice!(0, len) info[:login_ack] = true end |
#mssql_parse_reply(data, info) ⇒ Object
Parse individual tokens from a TDS reply
158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 |
# File 'lib/metasploit/framework/mssql/base.rb', line 158 def mssql_parse_reply(data, info) info[:errors] = [] return if not data until data.empty? token = data.slice!(0, 1).unpack('C')[0] case token when 0x81 mssql_parse_tds_reply(data, info) when 0xd1 mssql_parse_tds_row(data, info) when 0xe3 mssql_parse_env(data, info) when 0x79 mssql_parse_ret(data, info) when 0xfd, 0xfe, 0xff mssql_parse_done(data, info) when 0xad mssql_parse_login_ack(data, info) when 0xab mssql_parse_info(data, info) when 0xaa mssql_parse_error(data, info) when nil break else info[:errors] << "unsupported token: #{token}" end end info end |
#mssql_parse_ret(data, info) ⇒ Object
Parse a “ret” TDS token
276 277 278 279 280 |
# File 'lib/metasploit/framework/mssql/base.rb', line 276 def mssql_parse_ret(data, info) ret = data.slice!(0, 4).unpack('N')[0] info[:ret] = ret info end |
#mssql_parse_tds_reply(data, info) ⇒ Object
Parse a raw TDS reply from the server
83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 |
# File 'lib/metasploit/framework/mssql/base.rb', line 83 def mssql_parse_tds_reply(data, info) info[:errors] ||= [] info[:colinfos] ||= [] info[:colnames] ||= [] # Parse out the columns cols = data.slice!(0, 2).unpack('v')[0] 0.upto(cols-1) do |col_idx| col = {} info[:colinfos][col_idx] = col col[:utype] = data.slice!(0, 2).unpack('v')[0] col[:flags] = data.slice!(0, 2).unpack('v')[0] col[:type] = data.slice!(0, 1).unpack('C')[0] case col[:type] when 48 col[:id] = :tinyint when 52 col[:id] = :smallint when 56 col[:id] = :rawint when 61 col[:id] = :datetime when 34 col[:id] = :image col[:max_size] = data.slice!(0, 4).unpack('V')[0] col[:value_length] = data.slice!(0, 2).unpack('v')[0] col[:value] = data.slice!(0, col[:value_length] * 2).gsub("\x00", '') when 36 col[:id] = :string when 38 col[:id] = :int col[:int_size] = data.slice!(0, 1).unpack('C')[0] when 127 col[:id] = :bigint when 165 col[:id] = :hex col[:max_size] = data.slice!(0, 2).unpack('v')[0] when 173 col[:id] = :hex # binary(2) col[:max_size] = data.slice!(0, 2).unpack('v')[0] when 231, 175, 167, 239 col[:id] = :string col[:max_size] = data.slice!(0, 2).unpack('v')[0] col[:codepage] = data.slice!(0, 2).unpack('v')[0] col[:cflags] = data.slice!(0, 2).unpack('v')[0] col[:charset_id] = data.slice!(0, 1).unpack('C')[0] else col[:id] = :unknown end col[:msg_len] = data.slice!(0, 1).unpack('C')[0] if col[:msg_len] && col[:msg_len] > 0 col[:name] = data.slice!(0, col[:msg_len] * 2).gsub("\x00", '') end info[:colnames] << (col[:name] || 'NULL') end end |
#mssql_parse_tds_row(data, info) ⇒ Object
Parse a single row of a TDS reply
192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 |
# File 'lib/metasploit/framework/mssql/base.rb', line 192 def mssql_parse_tds_row(data, info) info[:rows] ||= [] row = [] info[:colinfos].each do |col| if(data.length == 0) row << "<EMPTY>" next end case col[:id] when :hex str = "" len = data.slice!(0, 2).unpack('v')[0] if len > 0 && len < 65535 str << data.slice!(0, len) end row << str.unpack("H*")[0] when :string str = "" len = data.slice!(0, 2).unpack('v')[0] if len > 0 && len < 65535 str << data.slice!(0, len) end row << str.gsub("\x00", '') when :datetime row << data.slice!(0, 8).unpack("H*")[0] when :rawint row << data.slice!(0, 4).unpack('V')[0] when :bigint row << data.slice!(0, 8).unpack("H*")[0] when :smallint row << data.slice!(0, 2).unpack("v")[0] when :smallint3 row << [data.slice!(0, 3)].pack("Z4").unpack("V")[0] when :tinyint row << data.slice!(0, 1).unpack("C")[0] when :image str = '' len = data.slice!(0, 1).unpack('C')[0] str = data.slice!(0, len) if len && len > 0 row << str.unpack("H*")[0] when :int len = data.slice!(0, 1).unpack("C")[0] raw = data.slice!(0, len) if len && len > 0 case len when 0, 255 row << '' when 1 row << raw.unpack("C")[0] when 2 row << raw.unpack('v')[0] when 4 row << raw.unpack('V')[0] when 5 row << raw.unpack('V')[0] # XXX: missing high byte when 8 row << raw.unpack('VV')[0] # XXX: missing high dword else info[:errors] << "invalid integer size: #{len} #{data[0, 16].unpack("H*")[0]}" end else info[:errors] << "unknown column type: #{col.inspect}" end end info[:rows] << row info end |
#mssql_send_recv(req, timeout = 15, check_status = true) ⇒ Object
Send and receive using TDS
37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
# File 'lib/metasploit/framework/mssql/base.rb', line 37 def mssql_send_recv(req, timeout=15, check_status = true) sock.put(req) # Read the 8 byte header to get the length and status # Read the length to get the data # If the status is 0, read another header and more data done = false resp = "" while(not done) head = sock.get_once(8, timeout) if !(head && head.length == 8) return false end # Is this the last buffer? if head[1, 1] == "\x01" || !check_status done = true end # Grab this block's length rlen = head[2, 2].unpack('n')[0] - 8 while(rlen > 0) buff = sock.get_once(rlen, timeout) return if not buff resp << buff rlen -= buff.length end end resp end |
#mssql_tds_encrypt(pass) ⇒ Object
Encrypt a password according to the TDS protocol (encode)
75 76 77 78 |
# File 'lib/metasploit/framework/mssql/base.rb', line 75 def mssql_tds_encrypt(pass) # Convert to unicode, swap 4 bits both ways, xor with 0xa5 Rex::Text.to_unicode(pass).unpack('C*').map {|c| (((c & 0x0f) << 4) + ((c & 0xf0) >> 4)) ^ 0xa5 }.pack("C*") end |